My first level of any consequence lagged badly and I didn't understand why. I posted on a few well known and authoritative sites, read all I could on the subject and ended up being more confused than when I started.
So not to be left in the dark, I did some research and read all the articles written by the "Experts" on r_speeds, unfortunately and a lot of what I read contradicted itself, so I decided to test all the theories.
What are 'r_speeds'?
First let's have a look at what r_speeds are. As the wiki page says, r_speeds
stands for Render Speed
- the speed at which the game engine draws the frames you see to the screen. If you open the example map and bring up the console by typing
(tilde key) and then type
you will be presented with 4 lines of numbers in the top left hand corner of the screen.
If your Half-Life graphics options are set to use OpenGL, they should look something like this:
- 60fps: the number of frames being drawn to the screen per second. The game should appear fluid at above about 25fps.
- 0ms: the time in milliseconds that it takes to execute one game cycle (or the time taken to draw this frame).
- 112 wpoly: the World Polygon count. This is the figure that people often talk about when they are describing r_speeds. This number is a result of the engine dividing the areas of the map that require a texture into polygons based on the scale of the texture to be rendered. It is not the number of brushes in the map.
- 0 epoly is the Entity Polygon count, but not the entities you're thinking of. It is the number of Model polygons (xxx.mdl) being rendered. Visible point-based entities like monsters, suits, weapons, shell casings, gibs are all models. An item_suit, for example, will increase this figure to 960.
Epolys do have an affect on the fps and ms counts, so keep this in mind when placing models, monsters and items. You can use up to 500 non-visible entities, such as path_corner
in in your map and the epoly count (and the wpoly count) will not be affected. But beware of possible entity overflow errors.
If on the other hand you have your HL video properties set to software mode, the figures will look like this: And they mean.....
- 20.1ms: the number of milliseconds it takes to execute one game cycle.
- 205: the total number of polygon surfaces in view.
- 180: the number of polygon surfaces in view that are drawn.
- 155: total visible polygons which aren't drawn.
- 0 surf: the number of dynamically lit surfaces.
The interesting thing about using software mode to check your r_speeds is that you can see how many polygons the software engine is trying to draw. The important figure is the 'polygon surfaces in view' (180) - this is the figure that you need to keep down. I did read somewhere that the software engine stops drawing polygons after it has rendered 800, so if you are designing a map that will be played on a machine that can only render in Software mode, this is worth remembering.
The other thing of interest is the surf reading. Basically this is the number of surfaces that have a changing light characteristic, such as a surface affected by a flashing light. Throw a few pulsating lights into your level and you will notice the difference. Muzzleflash from weapons also effects the surf reading, but not as drastically as Dynamic Lights.
You must limit the number of Dynamic lights (Lights that have a custom or appearance property) to approximately 4 in one given viewable area or you will receive the error "Too many light faces" - but once again there are exceptions to this rule.
So now you know what the figures represent...
What is 'within limits'?
There are so many figures suggested in other forums, web sites and tutorials that it hard see what they are referring to. The reason for this is that it depends on what your map will be used for and what computer it will be run on. If it's a multi-player map, 'what is the average speed of the connection to the server?' Questions like this, and their response will determine what you think is acceptable. [After all, it is up to you...]
As a really general rule of thumb, single-player maps can get away with a wpoly count of 600 to 800 without any appreciable lag. Having said this, if you were to run the same map on a 486DX66 with a 1Mb graphics card in software mode, it wouldn't run.... So the thing to remember here is what machine the map will eventually be used on. If you are distributing an SP Mod, then spare a thought for the guy who doesn't own a supercomputer.
A multi-player map or bot-filled map should be built with a wpoly count of 400 to 600 or less. Don't forget that the epoly count will increase rapidly as players (entity models) arrive in the same area (this is also seen in SP maps that have a large number of visible entity models in the same space, such as a lot of monsters or an Osprey). This will cause lag, but the reason I have not mentioned an acceptable figure here is that I really don't know. Some suggestions are for 10,000 max with the inclusion of all possible player entities. Personally I think this is a bit high, and some tutorials actually limit the areas by model count and max player count. Just remember that a high epoly count does increase lag. So the placing of static models is crucial. When everyone runs to grab the grenades, the epoly count will go through the roof. So in these areas, limit the number of wpolys belonging to static models such as cars, tanks, etc. and limit epolys belonging to any entities rendered visible that might be functional or not.
If you want to be very safe, it is still good practice to limit your r_speeds to 800 in normal traffic areas of your map. However, since even the most basic modern computers are much more powerful today than in 2005, you can go quite a bit past that if you're careful.
Building a huge box will not increase r_speeds until you add a visible texture. So build a box, 3008x256x3008 and add the crete3_wall01
texture to all faces with a scale of X
. Add an info_player_start
and a light
entity. Now compile it. You hate me don't you? Is your machine still trying to compile the map?...
When your map finally compiles, bring up the console and enter r_speeds 1
. We are going to have a look at how the engine divides your map into sections for texturing, so enter gl_wireframe 2
(this will only work in OpenGL mode). You should have a wpoly figure of about 506. Also of interest is that the millisecond refresh speed may have jumped. The gl_wireframe mode will show you a set of white lines. Lots of them aren't there?
Now go back to the texture properties and increase the scale of the texture to 10
in both axes and recompile the map (faster this time wasn't it?) Enter r_speeds 1
and gl_wireframe 2
and you will see that the wpoly count has been reduced to 16
; there are also a lot fewer white lines. In fact there are 16 boxes outlined by the white lines.Argghhh!
Has the penny dropped?
This is the big theory folks. The size of a visible texture rendered directly affects the wpoly count. The game engine divides the visible map into sections called world polygons based on the scale of the textures applied to brushes.
So if the scale of your texture on a single surface is high the wpoly count will be low (above X:Y:1
, but less than X:Y:3
I will get to this a bit later...).
Multiple textures on multiple brushes will also increase the wpoly count, but the wpoly count created by a single texture applied to multiple brushes that form a solid will be dependent on the scale of the texture chosen and not on the number of brushes in the solid.
Have I lost anyone yet? I think I have lost myself...
It is important
to mention at this stage, that the rescaling of the texture to a value of X:Y:10
was only an example. It is not good practice to stretch your textures beyond 2
, and I have noticed that the savings gained in the reduction of r_speeds seems to reverse when you resize above a scale of 2
. Stretching beyond 10 times the normal texture size might cause compiling errors, depending on your graphics setup.
If you do follow this concept then great, but unfortunately no-one builds maps with a single texture, or without anything in them. So let's look at some ways to reduce r_speeds in a normal map. In the example map I have added boxes of similar construction. Basic blocks, hollowed and then a corridor carved into some of them. There are columns in most of the boxes. The first one is a normal cylinder, and the rest are vertex manipulated columns. Also included is a teleporter between boxes for the first few, while you get used to the gl_wireframe picture. Print out the 'Tour.txt'
that is included with the download to guide you through the example map.
Corners, Mitering and molding
I said in the original Tutorial: In the Beginning Part 1
that it was a good idea to mold the corners. Doing that actually doesn't impact r_speeds - it is purely aesthetic. It doesn't effect the wpoly count of the map at all. Blending and Mitering of corners has no affect on how the engine draws the visible surface. Blending of corners has no effect on r_speeds, and sometimes leads to strange texture placement.
However having said that, mitering corners is a really good idea if you want textures to flow around the corner. Actually it's the only way I know of to make a texture look as though it belongs to the previous wall and avoids those funny black or white lines that sometimes occur.
Mitering is another form of Vertex manipulation (VM). VM of solids can sometimes increase the wpoly count, because you are adding additional faces to be textured but it should only be by about 2 or so, if your reshaped solid is conventional(ie: block). As mentioned in Atom's tutorial on VM, it also takes care of those brushes that don't quite meet. There are a number of error codes associated with VM, If you haven't already read Atom's tutorial on Vertex Manipulation, I recommend that you do it now!
Sky Textures and Boxes
Sky texture is one of the few textures that doesn't require Scaling. You will get the same wpoly count using SKY texture with a Scale X:1
and Y: 1
as you would with X: 10
and Y: 10
. The only brush that affects the r_speeds when sky is rendered is any other visible textured brush in the map. So if you had a massive box as in the example map and added sky to the walls and the ceiling and added a light_environment then compiled it, you should get a wpoly count based on the scale of the floor texture. (12 wpoly in the example map because the walls are textured at X: 10 Y: 10
It is important to understand the boundaries of your map. Building a huge skybox around it is not a good idea, because the engine will render texture on brushes underneath your map, and on the sides even though you will never see them. This will increase r_speeds. I use the "build the map add the sky technique" and end up filling in the gaps. This method basically caps off the sides of the map, and then adds the top.
Brushes touching other brushes and general brush building
If a brush that is textured touches another brush that is textured, the engine carves up one of the brushes into additional world polygons to correctly display the texture. This increases the wpoly count. You can work around this in two ways. First is to leave a 1 unit gap between the brushes, although there are areas where this isn't possible. The second way is to turn one of the brushes into a func_wall
entity (actually any entity that will render texture). An entity brush that touches a solid textured brush will not affect the way the solid brush is rendered, and this is why it is recommended that you use entity brushes for light fittings, and small objects. Don't forget that the entity brush will increase the wpoly count, because it is a textured brush, so try to have the light texture at or above Scale X:1Y:1
or build the brush to fit the scale of the light texture.
Entity Brushes also have an effect on the white lines in gl_wireframe view. It looks as though the entity brush is blocking the lines behind it, that might lead you to believe that the engine isn't rendering textures or brushes behind the entity brush. The truth is that Entity brushes dont block VIS, just the same as light will shine through them. Zoners Compile tools came up with a fix for this, but it doesn't change the property of the entity brush, as far as the engine is concerned it is see through and everything behind the entity brush will be rendered whether you can see it or not. SlayerA and I discovered this in a little test map with an arm chair behind a func_wall
and an armchair in the other same sized and textured room. When the map ran, it looked like a blank wall, but the r_speeds were similar in both rooms. The engine was rendering both chairs, but we could only see one.NEW INFO:
If you stand behind a func_xxx entity in a map and have gl_wireframe 2 selected and look through the brush, it will look as though the wireframe lines have disapeared from view, this optical illusion indicates that those brushes are not being rendered, but the truth is that they are. The gl_wireframe method of indicating which brushes are being rendered and which are not is only a guide. Don't be tricked into thinking that if the lines disapear, then everything is Ok. It might be that you are looking through a func_xxx entity brush. Consider this when you read about VIS Blocker below, and remember that entity brushes do not block visibility.
Clipping, Carving and more VM
If you build 4 cubes and piece them together to build a square, Select all of them, add texture and they will be rendered as 1 cube. Reduction in wpoly count, you bet. However you have to apply the texture to the blocks when they are all selected (Grouped
). The reason the blocks don't act as 4 separate cubes is because you have textured them together. This is where good texture Scaling and placement comes in. So if you can, Select the separate brushes and texture them as one, why can't you do that with brushes that touch each other? Because the cube becomes a solid for rendering purposes and the unseen areas are stripped out, where as the touching brushes form a "T" junction and require rendering differently.
Lot's of tutorials tell you not to Clip or Carve solids because it increases r_speeds, I haven't found any evidence of that unless the clipping is on a plane that is not square. So the trick is to stick to VM. If you are going to Carve, just be mindful that there can be problems. Test carving until you get used to it, try carving and then run the map to see if there have been any changes. Avoid carving with anything other than solid blocks.
In the example map, as you move between the boxes, you will come to a box with a corridor. You should see the wireframe extending into the next box, because the engine is rendering some of the textures in that box in advance. What you think you can see and what the engine thinks you should see are two different things. This is where vis blocking comes in. Entities do not block vis as you have probably found out (and I mentioned above). Lights on one side of a door will shine through a func_door
entity. So they cannot be used to stop the rendering of textures in another room. The best way to do this is with Hint and Skip Brushes, and that is another tutorial in itself. But a quick fix way is by building a blocking brush between boxes. In the example you will see that the Vis blocking brushes are actually touching the wall. This creates fractures of the wall for texture rendering purposes, as did the columns, and increases the wpoly count. But you have to weigh up the benefits, try placing the brushes 1 unit away from the walls and floor, run the map and the try it again. If the 1 unit gap works then go with it. I tried this example with the brushes 1 unit away from the walls, and didn't get the reduction in the wpoly count that I had hoped for. By letting the brushes touch the walls I got the best reduction in wpoly count. The difference was about 30
What have we learned?
r_speeds increase in direct proportion to the amount of solid textured brushes in your map and their interaction with other solid visible textured brushes. The Scale of the texture drawn on those brushes can increase or decrease the wpoly count. Models, such as Monsters, other players, gib's and anything ending in ".mdl" will increase the epoly count. High epoly counts (above about 10,000) will increase overall r_speeds and refresh rates creating Lag. Entities that are not models do not affect the epoly count unless they are textured.
If you're having trouble, run your map in OpenGL with r_speeds 1
and gl_wireframe 2
selected. Areas of high r_speeds will be visible then by the amount of white gl_wireframe lines and High woply counts as you move around. Initial Design can save you some hassle but don't get too hung up on some of the old myths. It is basically all about how many visible brush faces the engine has to render...