Performance Optimization


This article needs to be wikified. Please help us by adding relevant links, correcting formatting, or improving the article's structure.


If you find that your Age is 'lagging' or having various performance issues, it may be the time to learn some optimizations tricks. Performance optimisation is a vast topic, but an important one. Especially for large Ages who are more prone to performance drops than smaller Ages. But for new Writers even small Ages can lead to some unsuspected performance troubles. This article covers the most common and most useful tricks.

Note: This is a ‘work in progress’ article.

Things to always keep in mind

As much as possible it is better to have in mind performance issues before starting work on an Age. If you need to optimize an Age after it is mostly completed you will find yourself retouching, or even losing, weeks of work; and that is not an enjoyable thing to do. Therefore here are a couple things to always have in mind:

Top things to avoid:

Decent performance can usually be accomplished in most Ages by avoiding in a couple key issues. Here are the top performance killers; keep them in mind at all times. In no specific order:

  • Polygon counts : ”Yay! my Age has more polygons than Riven! oh.. wait.."
  • Texture sizes : Plasma can handle texture sizes up to 1024*1024. That doesn't mean that all textures should be that size.
  • Age size / design : An open Age will always be more difficult to optimise. You need to think of ways to block the view at times.
  • Excessive shaders : Fancy effects and multi texturing are nice, but only up to a certain point. Do not over-do them.
  • Too many trees : Many people like forests, but trees are the death of performance: Polygons, transparency... Use them sparingly.
  • Collisions everywhere : Never enable collisions on all your meshes. Hand-made collisions meshes are a necessity.

Do not be excessive: not too many polygons/textures/objects etc.. It is perfectly possible to have an Age that is both 'reasonnable' and nice looking.

Measuring Performance

The first step to enhancing performance is to measure it. First of all: know you computer. Is it old? Is it new? Is it above Uru's Minimum System Requirements? Depending on your computer Uru Ages will perform differently. Once you know that download and install FRAPS. This tool will allow you to measure the framerate in Uru. It is free, small, and easy to configure. Your target is to keep the framerate above 30FPS on all computers. Uru was released in late 2003, so computers of this time are our baseline. Computer power tend to double every two years (roughly); if your Age runs at 120 FPS on your computer but you bought it in 2009 then divide that number by 5... (This is not an exact formula, but it will give you an estimation). Once you start making some good progress on your Age it is a good idea to send it to people with different computers, so they can test the performance.

Design your Age with 'view blockers'

This cannot be stressed enough: Ages must be designed with performance in mind. It is possible to have a large Age perform correctly as long as it is designed correctly. Among other things that means you need to keep in mind Visibility issues. Cut up your Age in various sections so that you can never see the whole Age at a time. For instance, the City is a large Age but it is cut in different sections, with tunnels and small paths joining them. But try to use the FlyMode to see the whole City at a time and you'll notice the performance difference... This can also be used to make smaller Ages more detailed. For instance Eder Kemo is cut in small sections but they are very detailed. Blocking the view with large objects (walls, mountains, tunnels) at strategical spots is critical.
>> See visibility management section

Using the Debug Client to test your Age

Now that Uru has gone open source Cyan's internal debug client can be used. This particular version of Uru includes a console that is loaded with features to test Ages and measure performance.
Note: this client can only load MOUL ages; ages exported for Uru:CC cannot be used with this client.
There is no article explaining how to compile and use the debug client yet.
<This section needs to be expanded>

First things to take care of

Polygon count

When actually modelling the Age, the number of polygons you are going to use is the first step to decent performance. Using your best judgement, along with Vis regions (see bellow), try to keep the number of polygons in view bellow 150.000 at all times. You can't directly measure polycounts in Uru, so make sure to always keeps an eye on those numbers in Blender (or Max). In fact the number of polygons that the game must calculate depends on where you are looking at; but to make it simple you should determine a specific polygon budget for each area of your Age and use VisRegions. On a computer than was bought after 2003 usually Uru can easily handle 150.000 to 200.000 polygons. That includes the Age, but also the avatar(s) and other minor things like GUIs or particles. So you should stay bellow 150.000 polygons. Use that number for your whole Age if you do not use visRegions; or use it for each separate visRegions if you use them. Remember however that Uru was released in 2003; and since then graphics cards have become much more powerful. So if you want to increase that number, you can. But try and stay reasonnable, and think of the people who do not have recent computers. ;)

As a comparison: the Relto age has a bit over 120.000 polygons, and it is a very detailed Age. The City uses over 550.000 polygons, but it uses visRegions extensively.

Judging the 'correct' polygon count for each object is not always easy; and would deserve a full article. But to get a good idea, import and study the Cyan Ages. (use PyPRP importer)

Texture budget

Measuring the total weight of texture files for your Age is not too difficult. In theory it works very much like the polygon budget (see above): you should have a texture budget either for your whole Age or for each visRegion. If you do not use visRegions this leaves you with a pretty simple solution: look at the weight of your Age's textures PRP file. Cyan's PRP textures files are almost always smaller than 50 megabytes. This is what you should aim for. Again, just like for the polygon budget you can use a larger budget if you want, but stay reasonnable or performance might suffer. If you use visRegions you will need to manually count the total texture weight for each visRegion. To know the size of each exported texture, look into your Age's TexCache folder created in your Uru/dat directory. If you use JPGs files for your textures that size doesn't matter because PyPRP will recompress them anyway. Only the size of the final exported texture is important.

Judging which texture size to use for which object is not too difficult usually: the larger an object appears to the camera, the larger the texture needed. A small book on a desk does not need to have 1024x1024 texture.(unless it can come very close to the camera, like the Books in Relto). There are some tricks to save up textures though:

  • Re-use textures : Don't create a unique textures for each object -- do you really need 20 different 'wood' textures in your Age?
  • Use multi-texturing or stencils for large objects (terrains) : To avoid repettitive textures on large surfaces use multitexturing.
  • Use vertex painting or materials for color variety : If you have a texture that you want to re-use with different colors just color your object with vertex painting, or material diffuse color.


Collisions can be the worst performance hit, but they are pretty easy to take care of. Never enable collision on all your meshes; especially if you have complex meshes. Only enable collisions for objects you can walk on or touch in the game. For complex meshes it is a better idea to build low-polygons custom collider meshes. Have a look a Cyan's Ages (use the PyPRP importer version) to see how they handle those. For many objects it is not even necessary to model a custom collider, often a simple automatic 'box', 'hull', or 'cylinder' collision setting (depending on the mesh) will do the trick. For instance on a tree a 'cylinder' collision setting might do a decent approximation. 'Static triangle mesh' setting should be avoided unless absolutely necessary (for instance on a complex terrain mesh).
>> See: How_To_Make_Your_Objects_Collidable

Shadow buffers

By default Blender enable dynamic shadows for all new materials; this is the 'ShadBuf' button (in the 'Material' button, 'Links and Pipeline' panel). It uses up tons of performance so make sure to disable it for most objects materials except for some rare dynamic objects (animated objects/kickables). For static objects shadows can (and should) be done with lightmaps or approximated with vertex painting.

Visibility Management

Vis regions

On large Ages this is the most important performance optimization. Visregions allow you to 'remove' objects from the scene depending on the camera position. If you are in one part of a large Age you may not see the rest of the Age but it is still loaded and it takes up memory. A vis region can be used to unload that. For instance if you are inside a tunnel you do not need to have the rest of the Age loaded; use a vis region here. Used with a good Age design and visibility blockers, this will save a lot of performance and allow you to make detailed Ages. Designing 'visblockers' is often simple, for instance if you have a tunnel joining two sections of an Age don't make it straight or you will see through the other side. (again Kemo and the City are good examples of that.) For instance, if you have a twisty tunnel going from point A to point B; when you enter the tunnel from region A, region B is not loaded in memory yet, then you take a turn in the tunnel and you can load region B and unload region A and the player won't notice the transition.
>> See: Vis regions


Note: Occluders are not currently available in PyPRP 1.6
An occluder is a special type of mesh that stops object behind it from being rendered. Occluders take time for the engine to calculate, so they should be large, very simple meshes with few faces. Occluders can be single-sided or double-sided. Occluders are not a replacement for visRegions. Use visRegions wisely, and occluders should be for special cases.

Examples of Occluders in Uru:

  • The volcano in the Cleft occludes the terrain behind it.
  • The desert floor occludes the rooms in the Cleft.
  • Zandi's trailer occludes the brushes and rocks.
  • Some giant occluders hide the two main sections of Eder Kemo from each other

Use an occluder if you already have a large solid object that will block viewing of objects behind it. Note that occluders are "all or nothing": you cannot specify which objects to occlude. Everything behind the occluder will not be rendered.

Page swapping

This is a pretty extreme measure, and no Cyan Age uses it.By default all of the Age pages are loaded in memory on the initial linking. Unlike vis regions who can load(unload objects, Page swapping is used to load/unload whole Age pages via Python. This saves up memory, and shortens loading times; but the tradeback is that when you load/unload a page it freezes the game for several seconds. It is also discouraged as it can cause problems online (because not all players have the same objects in memory). This is an 'old' method and has only ever been used on one Age (Ahra Pahts).
There is no wiki article detailling the process yet; search the forums for relevant informations.

Relevance regions

Relevance regions are not currently available in PyPRP 1.6
<This section needs to be expanded>

Misc. things


Kickables are fun, because they are collision-intensive make sure to use custom collider meshes. (alternatively, 'box' 'cylinder' collision settings can work)

Transparency Overdraw reduction

Too many transparent textures on the screen at the same time ('overdraw') can quickly lead to performance drop. If possible when designing an Age, avoid having too many transparent textures on top of each other. (for instances: a glass in front of a tree in front of water etc)

Mesh LODs

For large objects that can be seen from a long distance it is a good idea to create LOD (Level Of Detail) low-polygons versions. For instance this is done by Cyan in the City for most buildings. Those can be displayed/hidden with Vis regions or 'soft distance' softvolumes.
<This section needs to be expanded>

Loading times

PyPRP does not compress vertex data due to floating-point errors that leave visual artefacts. However, you can use libHSPlasma's prc utilities to unpack and repack your DrawableSpans objects. libHSPlasma always compresses vertex data when repacking.
<This section needs to be expanded>