5. Reducing r_speeds
even further
Next to steps I've demonstrated with the test map, there are a few more misc. tricks and tips you can use to improve the performance.
5.1 func_detail
vs func_wall
Earlier in this tutorial I've explained the benefits of using
func_detail
and
func_wall
for wpoly improvement and I've also explained the way the renderer works. With this knowledge I will try to explain wheter you should use
func_detail
or
func_wall
.
The main difference between
func_detail
and
func_wall
is that
func_detail
is technically NOT an entity. It's a trick used in the compile process to add additional layers to the world geometry so they do not interfere with one another.
func_wall
is an entity and is processed as a model by the engine. This means it's treated differently by the renderer and I'll try to show you with this example. The numbers on the box will be referenced later.
I'm spawning my player with the box behind him. The box in this case is a
func_wall
. Each wall of the room is 1 wpoly, so that means with 6 wpoly on the counter, the box is not being added to the wpoly. It's not being rendered.
Now I'm going to slowly turn to the right until 1 face of the box comes into my Field of Vision (FOV). Even with 1 face visible, the wpoly jumps to 9, adding 3 wpoly. This is because the
func_wall
is considered to be a model and thus all visible faces from my position (#1,2 and 3) are being rendered at once. All the backfaces of the box (not visible from my position) will not be rendered.
Now let's turn the box into a
func_detail
.
You can see that, even without having the box in my view, 2 additional wpoly is added. These come from faces #1 and 2, that would be visible from my position if I had eyes in my back. Everything that is world geometry (including
func_detail
) will still be rendered, even if it's outside of my view and within the same VIS sector (visleaf).
But if we bring the box into view, the wpoly stay the same.
It is not regarded as a model, but as independent faces and thus it will not add 3 wpoly like the
func_detail
did. It will only render what would be perceivable from my position (only faces #1 and 2).
So, in short:
func_detail
s are meanies, because always render behind your back.
func_wall
s only show up when you are actually looking at them. So if you want to keep your wpoly as low as possible, use
func_wall
. Make sure that you do not group multiple objects into 1 big
func_wall
, because if one of them is being rendered, then all of them are (even outside your FOV).
Even when looking directly at them,
func_wall
can have a slightly better wpoly than
func_detail
. Here are some examples of a complicated lamp and its wpoly. Both fully yellow lamps are tied into 1 entity and very poorly optimized. The multi-coloured lamps have separate entities and (in case of the
func_detail
) correct detail levels.
The
func_wall
lamp will perform slightly better than the
func_detail
lamp. However, this comes at a cost.
func_wall
is an entity and the amount of entities you can have is limited. If you're planning to make a large map but rely on
func_wall
alone, you will quickly find yourself running out of resources. In this case, using
func_detail
is the better option.
If you're planning on making a very complicated map with a lot of complex details, you might run into the
worldleaves
limit. All geometry that is tied to
func_detail
will count towards this limit, because it is technically part of the world geometry. Turning objects and geometry to
func_wall
will free up some of those
worldleaves
.
Lastly, because
func_detail
is part of the world geometry, the compiler can sometimes interpret the faces wrong. This happens especially on curved or slanted surfaces or when you've rotated complex objects. In this case,
func_wall
can give you better results.
5.2 SOLIDHINT
Just as regular
HINT faces help VIS to make more efficient cuts,
SOLIDHINTS can help the engine to make better face subdivisions. On straight and simple brushes the face subdivisions are quite optimal but on complex shapes and especially triangle terrain the BSP compiler can have trouble finding the right place to cut a face. I've highlighted an example in the following screenshot, but you can probably find many more.
To help BSP find the edges of the triangle shape and thus make a correct cut, we're going to place
SOLIDHINT textures on all invisible faces. The easiest way to so this is to select all terrain, replace all with SOLIDHINT and then retexture the visible faces. This saves a lot of time and you're sure you don't miss any inside faces.
Now recompile and see the result. Most of the bad cuts are gone and we've won 10 wpolies!
5.3 Models
If you can, you can consider swapping some of your brushwork for models. This will take some load of the wpoly but will add some to the epoly. The engine can deal with a lot more epolies than wpolies, so mostly swapping a brush-object for a model can be a good trade-off. However, the Half-Life engine doesn't do lighting very well when it comes to models. Each model will only get 1 type of lighting grabbed from it's origin point. So, it's advised to use this technique on smaller objects and in well-lit environments, so the lack of shadows on the model will not be very noticeable.
5.4 Sprites
Another way to add detail to your map is the usage of sprites. Just like models, sprites don't add to the wpoly and are relatively cheap to render. They are mostly used for special effects like glow or teleports, but you can pretty much turn every texture into a sprite. This means you can swap content you would normally make out of brushes for sprites, like these trees in the original They Hunger:
By default sprites will always face the player (and thus rotate). To make sure they don't do this (like the trees above), you can set the sprite to other modes like PARRALEL_UPRIGHT or ORIENTED. Since sprites are simple 2D planes, they will always be flat and will probably not look great up close. The tree sprites in the shot above were swapped with models in the SC version of They Hunger, but you could probably still use the sprites in the background, where players won't be able to go. Using sprites is definitely cheaper than using models or brushwork, so are a potential polysaver when applied right.
See also:
env_sprite
which has some more information about sprite types and orientation.
5.5 Decals
Decals are small overlay textures, commonly used for bullet holes or blood spatters. However, you can also add other details onto textures such as numbers or stripes. So instead of having separate textures with the numbers/stripes on it, you can just paste a decal onto the surface. Decals don't cause face subdivisions, so in some cases it might help you save wpolies.
However, the decals aren't very versatile. They can't be resized and can only be used in the decals.wad file. This special wad file only has one specific colour palette, so the decals can either be grayscale or only have 1 main colour. If you work for a multiplayer mod, you can't add your own custom decals to this wad file, because that could potentially cause conflicts with other maps doing the same, so you'd have to work with the default HL decals. All of these drawbacks do not make decals ideal tools to with structurally build your map but it can help in certain cases.
5.6 Wpoly/Epoly balance
Having a high wpoly doesn't necessarily mean you'll notice a lot of frame drops. There are levels out there with 8000 wpolies which run fine on modern video cards, but you'll need to find the right balance between wpoly and epoly to make sure there are no frame drops. Even on high-end video cards, the half-life engine can still completely crap out, because of the old render techniques.
It also comes down what the epoly is made up from. 50,000 epolies of static models will be rendered a lot better than 50,000 epolies of enemies, since you'll have AI calculations and lots of effects to deal with. You must also factor in other players if you create a multiplayer map. Their models and effects also contribute to the the scene's load.
The maximum amounts of wpoly and epoly have always been a point of debate but, generally, the lower the wpoly is, the more epoly you can have and vice versa. For singleplayer maps, you could go higher, but for multiplayer maps I would advice the following:
wpoly |
epoly |
500 |
100,000 |
750 |
75,000 |
1000 |
50,000 |
1250 |
25,000 |
1500 |
10,000 |
Note that these guidelines are pinpointed on a single player testing the map, so in reality there are going to be more epolies once other players get into the scene. Personally, I'd like to keep my areas under 1000 wpolies.
5.7 Skybox
A rather cheap way of making your map look better is using the
skybox. Instead of mapping out hills and rocks in the distance, let the skybox do the work for you, as demonstrated in Half-Life 'Surface Tension':
The skybox doesn't add to the wpoly and is a 256x256 texture by default, so hardly takes the engine effort to render. But the default quality is low.
5.8 Level of Detail (LOD)
You can use a LOD system for your artwork to save some wpolies. Not everything needs to be high poly to make a scene look good. Especially details in the distance or areas where players won't be able to go, do not necessarily need high-poly objects. So make low-def version of these objects. Translucent textures can be a great help with that:
The same goes for ornaments or details on walls. For distant use, make a screenshot of the artwork and turn it into a texture.
5.9 env_render
In some cases certain complex brushes or map parts can't be hidden by VIS because your layout doesn't allow a visblock without the intended gameplay being broken. An out-of-the-box solution could be the
env_render
. This entity can render and unrender
func_wall
s and thus stop them from being counted in the total wpoly upon trigger. Make sure your
func_wall
has 'Solid' as Render Mode and '0' as Render FX. This will make it invisible. Then once the player can have visible contact with the brush, make sure he touches a trigger that then triggers an
env_render
to set the brush to Render FX '255'.
This is quite easy to use in single player but quite harder to do in multiplayer, because you'll have multiple players in multiple locations. However, I used this technique on my Suspension map. I made a giant bridge and cut it into segments. The players' view into the other segments is always obstructed by objects. Players battle in the first segment until they've won the fight. This then triggers a series of
env_render
s that render the second segment and a way opens up for the players to get into the second segment. Once all players are in the second segment, the way closes and the first segment becomes unrendered. This way you can create the illusion of a massive map but keeping the wpoly low in all directions.
5.10 Detail textures
Detail textures are high-res overlays that are being rendered on top of textures, so they'll bring more detail to the blurry texture underneath, like so:
While this is a great looking effect, the feature is generally not known as a wpoly saver. But by using grey lowpoly textures and overlaying them with high poly detail textures you can have great visuals with very low wpoly. Here are some examples:
Regular 1024x1024 texture on 0,125 scale (288 wpoly)
Regular 16x16 texture on 1,0 scale with 1024x1024 detail overlay (16 wpoly)
In this shot I was able to use a very high detailed texture with only 16 wpoly. Then why not use this everywhere? Well, the detail textures do not count towards the wpoly count but that doesn't mean they do not cause any engine load. Technically speaking, their rendering load is quite low but it's not possible to see their effect in numbers like
r_speeds
command does. Also, this rendering feature is very well supported on Nvidia graphics cards but not so much on ATI/AMD or Intel cards. Users of the latter might experience FPS problems with these textures enabled. Another problem with detail textures is that they can be disabled by user command, so you're not in full control of how players experience your map.
So, do the pro's outweigh the con's? Since it's hard to measure the effects of detail textures and it affects graphics cards very differently it's quite difficult to say whether this method is good or not. In general, Half-Life clearly wasn't made for high resolution textures, so if you want to work with this engine, I'd advice you use it with respect for its limits. This is probably not worth your trouble if you stay lower than 256px textures.
But if you really want to push this old geezer of an engine onto the race track and see if it can keep up with 512px or even 1024px textures, then this method might be the way to do it. So here how it's done:
- Create a 16x16 texture on a grayscale. The darker the gray, the darker your texture will be in game. Put it into a WAD (calling it
detail_wall1
).
- Make sure the hi-res texture you want to use, is a 24bit TGA. Put it in ../gfx/detail/mapname/ (calling it wall1.tga).
- Put the detail_wall1 texture on a wall with scale 1.0 and compile the map.
- Create a text file in ../maps called 'mapname_detail.txt' (where 'mapname' is the name of your BSP).
- In that text file put:
detail_wall1 detail/mapname/wall1 0.125 0.125
- First name is the grey texture in the WAD. Second is the path of the overlay detail texture and third are the X and Y scales of the detail texture.
- Load up the map and see if looks good. You can make the gray texture darker or lighter or change the scale in the text file.
And there you have it, high resolution textures with very low wpoly. It works, but you have to take some extra steps and be very aware of the limitations mentioned above.
5.11 TRANSLUCENT & func_detail
level 0
The
TRANSLUCENT texture type is an engine feature that was never really used in Half-Life but somehow remained within the code. Other than the name suggests, this texture is not transparent but any brush with this texture type on it will become non-solid. Doesn't seem like this could be useful, but the interesting thing about it, is that it also blocks VIS.
You can turn any texture into a TRANSLUCENT type texture by adding a '@' in front of it. I've created a special test texture (@test) for this purpose. Just add a regular world brush somewhere and make sure all sides have the @-texture on it.
Behind this TRANSLUCENT wall are several barrels that are not being rendered because of the VIS-blocking properties of this texture. This is what it looks like inside the brush:
Inside, you'll end up within a VIS sector that is disconnected from all the other sectors. As you can see, it will render the backface of the @test texture, except where it meets with other world geometry, like the floor, ceiling and backwall. The size of the brush pretty much becomes it's own VIS sector in which you can place entities. I've placed a light entity inside, since it will block light coming from the outside of the brush.
You can do something similar with a
func_detail
brush (with a regular texture) if you set it's detail level to 0. 0 is the level of regular world brushes, so it will also block VIS. However, you can also make this non-solid by setting the 'Passable' key to 'Yes'. It will now also work like the TRANSPARENT texture type but only with one difference:
Once you walk inside the
func_detail
brush, you'll end up in the void. From here, everything in the world will render (the opposite of what the TRANSLUCENT brush did). It will also discard anything that was inside the brush like entities or other brushes. This doesn't make it very useful and you're probably better off using the TRANSLUCENT method, if you want to use this feature.
How to make use of the TRANSLUCENT texture type? You'd need to use very thin (1 unit) brushes or else the players will notice that they are inside the brush, so this limits its potential. You can also only use 1 texture across the entire brush or it'll give you a compile error. An idea is to use it as curtains inside window panes. It will completely block whats inside the building and yet, players can access the place by jumping through the curtain.
As you can see it works, but as said, this trick only works for very specific purposes.
5.12 AAATRIGGER vs NULL
When creating a trigger brush (
trigger_once
,
trigger_multiple
, etc), traditionally you'll use the
AAATRIGGER texture. It's useful, because you can easily recognize where trigger fields are and some editors (like JACK) will automatically make them transparent for better visibility. However, I sometimes see mappers use the
NULL face on triggers, because they think it will save them some engine resources. This is not the case. In VHLT,
AAATRIGGER texture (just like the
NULL texture) is automatically removed by the compiler and will
not count towards the face limit.
Technically, you can put any texture you want on a trigger brush, but any non-tool texture (anything other than
AAATRIGGER,
NULL,
SKIP,
HINT, etc)
will count towards the face limit, even when they are invisible in-game. Use
zhlt_invisible
to treat an entire brush entity as if it were textured with
NULL.
5.13 NULL vs SKIP
The
SKIP texture will tell the compiler to skip the face where this texture was applied on. That might make it seem like it is similar to what the
NULL texture does, but there are some fundamental differences.
The
NULL texture only removes the face of the brush it is applied to. The compiler will still create a plane and clipnodes along the brush. The
SKIP texture, however, will tell the compiler to completely skip this face and there will be no planes or clipping information at all. So if you use
SKIP on a world brush, it will cause a LEAK error, since it will treat this brush as if it was never there. A
func_wall
with a
SKIP texture on one of the sides, will become fully non-solid, because clipnodes were not generated.
Even if the non-solid nature isn't a problem for your brush, using the
SKIP texture will also mean that the compiler doesn't interpret your brush correctly. So if you use
SKIP on a complex, multi-brush
func_wall
, it'll render some of the interior faces, which NULL would normally hide. So, only use
SKIP for your
HINT brushes (see #4.2).
6.0 Other engine limits
The VHLT compile tools have increased many of the original engine and compile limits so you'll be able to make much larger maps than ever before. The compile log gives you a lot of information on this topic. Look for the BSP process and you'll find a list:
Object names Objects/Maxobjs Memory / Maxmem Fullness
------------ --------------- --------------- --------
models 1/4096 64/262144 ( 0.0%)
planes 20/32768 400/655360 ( 0.1%)
vertexes 25/65535 300/786420 ( 0.0%)
nodes 6/32768 144/786432 ( 0.0%)
texinfos 3/32767 120/1310680 ( 0.0%)
faces 16/65535 320/1310700 ( 0.0%)
* worldfaces 16/65535 0/0 ( 0.0%)
clipnodes 18/32768 144/262144 ( 0.1%)
leaves 2/65536 56/1835008 ( 0.0%)
* worldleaves 1/65536 0/0 ( 0.0%)
marksurfaces 16/65535 32/131070 ( 0.0%)
surfedges 68/512000 272/2048000 ( 0.0%)
edges 41/256000 164/1024000 ( 0.0%)
texdata [variable] 92/61440000 ( 0.0%)
lightdata [variable] 0/67108864 ( 0.0%)
visdata [variable] 0/67108864 ( 0.0%)
entdata [variable] 505/2097152 ( 0.0%)
* AllocBlock 1/64 0/0 ( 1.6%)
You can see how full the engine limits are. Here are some pointers and tricks to squeeze as much out of the engine as possible.
6.1 General mapping practice
A lot of performance and issues with engine limits can be prevented if you learn to use a clean mapping style. While a lot is possible in the GoldSrc engine, it heavily favours square blocks. Stuff like wedges, cylinders or even spheres work, but will cost you a lot more engine resources. As long as you keep things square and simple, you can build massive maps without hitting many of the engine limits. So, when designing an area, try to build the general outlines as straight and square as possible.
It's also a really good idea to make use of the power of 2 rule you often see while working with this engine: (2, 4), 8, 16, 32, 64, 128, 512, 1024 etc. These are magic numbers, which will make your textures easily fit your environments but also helps the engine figure out how to compile and render your map. It also gives you better control over where the face subdivisions and VIS cuts will be. The editor's grid uses these increments (on the smallest grid each block is 8x8 units), so if you keep to them as much as possible you'll also have a way better overview of your map. Look at the following example:
6.1 Leaves
The 'leaves' mentioned in the compile log are the sectors generated by VIS mentioned in chapter 4.0. You'll probably won't hit this limit quick but if you get close, you can easily increase the
-maxnodesize
(see 4.3). Especially in larger rooms or outside areas, it will remove many unnecessary sectors, because in open areas there isn't much VISblock to begin with and all sectors can be seen anyhow.
6.2 Clipnodes
6.2.1 Works and Quirks
Clipnodes are used to calculate how the engine handles collision. To understand how that works, you need to know about hulls. Each monster/NPC/player has a hull around it to indicate its size. You can see the player hull (indicated by the box around him) in the picture below:
Shown in red is a clipnode. This node covers the entire straight wall (also called a
plane) and each flat plane will have its own clipnode. If the players' hull touches this clipnode, the engine will tell it to stop moving in that direction. This is how collision works in GoldSource.
There are different types of hulls: There is one for a standing player (72x32x32), one for a crouching player (36x32x32) and one for large monsters (64x64x64). There is also a hull for point entities (like bullet impacts) but they don't require clipnodes for their collisions. The other 3 hulls are also used by all monsters/NPCs in the game.
While compiling your map, the compiler will generate a clipnode for each used hull size. So every plane will (mostly) generate 3 clipnodes. So if you have a square room (4 walls, 1 floor, 1 ceiling) you'll have 6 planes and therefore 18 clipnodes.
The compiler is actually very clever about where it's generating clipnode planes. Take the case in the image below for example:
I've added 2 pillars with a gap of 32 units in front of this wall. You might expect that the compiler would just create a clipnode plane along each face of the pillars. However, it recognizes that the space in between isn't large enough for the 3 hulls (which are 32, 32 and 64 units wide) to fit in anyway. So instead it just creates clipnode planes in front of the pillars and at the side where the hulls can fit (red line).
In the same fashion it will also determine if a space is large enough for only 1 or 2 of the hull types. Inside an air vent for instance. Only the crawling player hull size (36x32x32) will fit here, so it will not add clipnodes for the other hull sizes. So the sides of our ventilation shaft only create 1 clipnode per side:
Another example to illustrate this, is this floating crate (40 units off the ground). Only a crawling player hull will fit under there, so it'll only create 1 clipnode for that specific type on the bottom of the crate.
So far this is all rather straight forward but things get weird with complex shapes. Let's imagine a 4 sided pyramid. Since it has 4 sides, it will generate 4 clipnode planes. Let's put it somewhere where all 3 hulls can touch it, so 4 times 3 equals 12 clipnodes. However, if you compile this you'll end up with 27 clipnodes! What gives? The answer is illustrated in the image below:
Whenever the compiler finds a sharp edge, it'll generate another clipnode plane on that point. This is probably to remedy clipping errors where players get stuck on these points. Our pyramid has 5 of these edges, times 3 hulls equals another 15 clipnodes. Combined with the clipnodes from the faces, it adds up to 27 clipnodes.
So what is a 'sharp' edge exactly? The tip of the pyramid seems clear but, technically, the bottom of the pyramid has perfect 90 degree angles. Our box earlier had a 90 degree edge and didn't need an extra clipnode plane. Then how does the compiler determine when to add that extra plane? Take a look at the next image.
This is the same pyramid as before but rotated. This one only generates 15 clipnodes: 12 from the 4 faces and 3 from the tip. The 4 sharp points on the bottom no longer receive extra clipnodes. So the compiler also makes a judgement based on the orientation of the brush. If the 90 degree angle is oriented on a straight X and Y axis, it'll not create extra clipnodes on the edges. The same goes for X/Z and Y/Z axis.
Does this mean you should only make straight brushes? No. The extra clipnodes at edges are only generated when a point is sticking out at a 90 degree or higher angle. In all other cases, you'll be perfectly fine. This image shows some shape examples:
In conclusion, the general rule of thumb when it comes to clipnodes is: no pointy stuff. However, you can still create pointy brushes and get away with it using the CLIP tool texture.
6.2.2 CLIP
The CLIP texture is a special tool texture introduced in the ZHLT compile tools. An area that is covered with this texture will be stripped of its clipnodes and generate clipnodes along the shape of the CLIP brush instead.
Let's take the pyramid from earlier as an example. Because of the extra nodes generated by its sharp edges, it will cause 27 clipnodes. Now put a CLIP brush box on top of it and it'll use the CLIP brush to generate the clipnodes.
The box has 5 approachable sides, which results in 15 clipnodes. A lot better than our initial 27!
You can also put CLIP brushes in areas where players and monsters won't be able to go. The small room generated 6 clipnode planes and was big enough for all 3 hulls, so CLIPPING this room saved me 18 clipnodes.
So, knowing this, it seems like a good idea to put CLIP brushes into every crevice you can find. This high ceiling for instance. Players won't be able to jump that high, so let's put a CLIP brush there.
This setup still generates 6 planes and thus 18 clipnodes. You are only moving the ceiling plane a bit lower, but not removing any planes and thus not saving any clipnodes. Only if you have solid brushes up there (like beams) CLIPPING this area will make sense, since you'll remove the clipnodes on the planes running along the beams. In this case, I'd advice you to set the beams to non-solid (more on this in 6.2.4) and not put a CLIP brush there at all. This way you can still use this space for airborne monsters or other stuff.
The same goes for gaps and crevices that are 32 units wide or smaller. As we've determined, the compiler will ignore those anyhow because none of the hulls can fit in there. So putting a CLIP brush between these pillars will not save us any clipnodes:
So don't go around CLIPPING everything but pick your battles! It might not even help and the compiler will do a lot by itself anyway. Also, the clipnode generation can vary from time to time, especially if your brushwork gets more complicated. As described in 6.1, it's a good idea to maintain a clean mapping style, which is especially true for clipnode generation. Observe the following examples:
In theory, it shouldn't matter that I've extruded that wall because the compiler removes everything outside the level. However, this unexpected design confuses the clipnode generator enough to raise itself from 45 to 59 clipnodes. It added 5 extra unnecessary planes to make up for my sloppy design! So, make sure only the edges of your brushes touch as in the first example to get the best clipnode results.
There are a few compile options to improve the clipnodes, which I'd like to review in the next paragraph.
6.2.3 -cliptype
The CSG compiler will generate clipnodes for your map but (by default) only does a moderate job at it. Newer compile tools have added parameters called
-cliptype
, to improve the amount of clipnodes used. Here is an overview:
-cliptype legacy
(this is the default HL cliptype)
-cliptype normalized
(reduces clips by 3%, minimal improvements)
-cliptype smallest
(reduces clips by 15%, but doesn't do a good job at it, weird clipping errors)
-cliptype precise
(reduces clips by 18%, and with good quality).
-cliptype simple
(reduces clips by 20%, good quality but players float slightly on sloped brushes).
I'd advise the
precise cliptype for general use.
6.2.4 zhlt_noclip
All world geometry will cause clipnodes to form, but also brush-based entities like:
func_wall
,
func_detail
,
func_train
,
func_door
, etc. But you can also win back some clipnodes on all
func_*
brushes. Every one of these entities have
zhlt_noclip
(ZHLT Disable Clipping / Passable). If players don't have to collide with a certain object then set this value to
1
(Always non-solid / Yes). I use this for small details like signs, lights, ceiling lamps, etc. If players don't have to stand on it and won't notice it's actually non-solid, then use this setting!
Also set
zhlt_noclip 1
on non-solid brush entities like
func_illusionary
and entities using Non-solid/Passable flags. Even though they are non-solid, they will still generate clipnodes on default.
6.2.5 -nohull2
& cliphull2
Another compile parameter you can use on BSP is
-nohull2
. This will stop the compiler from calculation hull collisions on the 2nd hull type, which is the largest (64x64x64). This hull is used by large monsters (
gargantuas,
bullsquids,
gonarchs, etc.) and large pushables, so if your map doesn't feature those, you can use this compile command and save a lot of clipnodes.
If you still want to use large monsters, you can confine them to certain areas only and then use the tool texture
cliphull2
from zhlt.wad. Cover all areas where the large creatures can't go with a brush containing this texture and all clipnode information for hull2 will be stripped. You can do the same thing with cliphull1 and cliphull3 but note that these hulls are used by players and all other monsters, so the uses for this are scarce. This would only work for a space where only a large monster can roam.
6.2.6 -nobrink
VHLT uses an option to fix 'brink' bugs, where players get stuck on the edge of a 45 degree angle. The compiler adds a few extra clipnodes wherever these angles are to remedy the bug, however this also increases the amount of clipnodes. If your map doesn't feature any of these sloped angles, then you can the
-nobrink
command to BSP to disable the fix and win back some clipnodes. You'd have to test your map extensively to make sure this option doesn't lead to any problems and the gain is relatively low, so you'd have to use this as a last resort.
6.2.7 BEVEL
BEVEL is a special
tool texture feature that was introduced with the ZHLT compile tools. It can be used to remedy clipping errors or where the compile generates clipnodes where they don't have to be (like around exterior corners). Judging from some older forum posts and tutorials, people believed that using BEVEL on unseen brush sides (instead of
NULL) would reduce clipnodes and some mappers even adopted this as general practice. This is NOT the case. BEVEL is a very experimental feature that uses a different approach to collision than regular brushes. To quote Vluzacn (creator of VHLT tools):
It changes the clipnode of a brush in such a way that any player approaching the brush will not be blocked by the brush until his origin reached the plane that the BEVEL face lies on.
As for clipnode usage, BEVEL has no advantage over
NULL. Sometimes BEVEL even use 150% as much clipnodes as NULL.
The BEVEL feature isn't even recommended by the original author of ZHLT because using
-cliptype precise
(see 6.2.1) will eliminate the clipping problems BEVEL can remedy.
6.3 zhlt_usemodel
This feature can help you reduce engine resources by copying a template brush model you plan to use a lot. The copied version will not be counted towards the engine limits, since they are only loaded on map start.
Create a template brush (any
func_*
that can have a targetname) and give it a name and an origin brush in its center. Now set up a square box with NULL textures on each side and also an origin brush. Turn it into a
func_wall
and in 'zhlt template model target' set the name of the template brush. You'd end up with something like this:
There are some problems with this setup. Since it's a copy of the original, it will use the lighting properties of the original and also copy any decals that might affect the original, so you'll have to take this into account while using this feature.
6.4 Compile settings
Another way to increase performance or quality of your map is to tweak the settings of the compile programs. I'll review a few of them that I find very useful.
CSG
-cliptype precise
(this will optimize the clipnodes)
-onlyents
(this will update all entity information. Really handy if you have a big map and you've only adjusted a few values in the entities. Then you don't have to run BSP/VIS/RAD again)
BSP
-viewportal
(creates a portal point file, so you can see the VIS portal cuts in the editor)
VIS
-fast
(for quick compile to test your map)
-full
(better and more complex calculations, for final compile)
RAD
-bounce #
(light bounces off surfaces to light up the surroundings, creating better and more realistic environments. I recommend value 8)
-extra
(gives extra oversampling for your lights, making it look better)
-smooth #
(smooths out light across curves and adjacent brushes, use 120 or 240)
-smooth2 #
(smooths out light for adjacent brushes with different textures, use 120 or 240)
-scale 1
(the old compile tools had a bug where final lighting was calculated twice, which lead to brighter maps. This bug was fixed in VHLT, but to compensate for the expected lighting the
-scale
command was set to 2. Setting this back to 1 will create the most accurate lighting effects, but you might have to revisit all your brightness settings. Easy to do if you're using
texlights.
-sparse
(bypasses the MAX_PATCHES limit).
General parameters for all tools
-texdata #
(You can increase the amount of bytes allocated for textures. If you use many textures, you might need to set this to 16384 (16mb) or more)
-low
(This will set the CPU priority for compiling to low, so you can still use your computer for other tasks)
-chart
(This will give you a summary with detailed compile statistics)
-verbose
(Verbose will give you extra information on small errors, warnings and extra compile information)
-estimate
(Gives you a time estimation on how long the current compile process is going to take)