Thread: [Tutorial] Total Map Optimisation

Results 1 to 16 of 16
  1. #1
    Administrator Hezus's Avatar  
    Join Date
    Aug 2001
    The Netherlands

    [Tutorial] Total Map Optimisation

    This version of the guide focusses on the workings of Sven Co-op. For a vanilla Half-Life specific version, go here.

    This tutorial helps you understand how the engine renders your map and how to improve the performance. Many tutorials have been written about this in the past, but some of that information is outdated or incomplete. Also, this guide aims to help you through the process of improving the performance by optimising an example map, rather than just give you generic information. Really good for newbies, but also for veteran mappers as it might give you some new insights.

    For this guide we're going to use vluzacn's v34 compile tools and his version of zhlt.fgd. It's got many extra features to optimise the map. Get it here or use the compile tools from the SvenCo-op SDK (based on vhlt). Also make sure you have zhlt.wad loaded.

    I've created a very basic and very badly optimised example map. We're going through some steps to fix it, which will become gradually more advanced. You can skip paragraphs you're already familiar with but if you encounter some terminology you don't understand, look for explanation in earlier paragraphs.

    1. Checking the performance
    In the main menu, open the console and enter these commands:
    developer 1
    maxplayers 1
    sv_cheats 1 (or 'sv_cheats 255 in SC)
    r_speeds 1 (or 'cl_showfps 3' in SC)
    map yournamename

    In the top left corner, you'll notice some values:
    59 FPS: Current amount of frames/sec.
    0 ms: The current latency (lag).
    363 wpoly (World Polygons): the amount of brushes (walls and such) being rendered.
    53338 epoly (Entity Polygons): the amount of polygons in models being rendered.
    Let's start with the optimisation of the epolies.

    2. Epolies
    There are quite a lot of epolies at work here. The pistol takes up 2.359 of those epolies. You could use a lower detailed pistol and save some epolies but I don't want to sacrifice detail for that. Instead, I'm going to look at my map and see what I can do.

    Behind the door are 12 monster_human_grunts. Those grunts are currently doing nothing but are being rendered by the engine for 50.979 polies. I only want them to pop up once it's necessary. So I'm going to change them all to squadmakers and name them. Then I'll place a trigger_once in front of the door and target the squadmakers. I'm also going to reduce the amount of grunts to 6 and let others spawn in a few seconds later from behind the crates. This way there will only be 6 grunts rendered at the same time at the most and I only use 25.489 polies max.

    Next to the eploy reduction, it will also increase performance since your computers CPU doesn't have to calculate effects and AI, and it will also improve the server load in multiplayer.

    3. Wpolies
    The wpolies is where you, as a mapper, can make a difference. There are several techniques to improve them but lets first try to understand how the rendering works.

    Go into the console and type:
    gl_wireframe 1
    gl_wireframecolor 255 0 0
    (gl_wireframecolor only available in SC 5.17)

    You can see how the map is being cut up into squares and sometimes triangles. This is called face subdivision and it's the way the engine works. Each square/triangle is a face and the more faces, the more polies. This is how the engine sees your map, but that's not the full story. Go into the console and type:

    gl_wireframe 2

    A lot of extra faces appeared in the room behind the door. Now this is how the engine actualy renders your map. You might not see that room, but the engine does and adds the wpolies from that room to the total amount. We're going to try to bring that number down by using some tricks. We'll start with the easy tricks and move down the list to the more advanced measures to optimise our map as much as possible.

    3.1 func_wall & func_detail
    Whenever a brush touches another brush it will generate more faces, as you can see from the following screenshot.

    The crates touch the floor and generate extra faces there and the pipes cut really bad faces into the ceiling. To stop this from happening, we're going to turn them into func_wall.

    As you can see, the extra faces are gone and we've nearly saves 100 wpolies here! I'm also going to use this for all the other things in the map, except for the outer walls, since that would cause a leak. Everything in purple is now a func_wall.

    Everything fine, except for this part:

    Since I considered them as 1 object, I tied the pipe and the panel into 1 func_wall. This however, makes them cut into eachother. Turn the pipe and the panel both into seperate func_walls and the problem is gone.

    When you are using a func_train or func_tank it will be difficult to seperate brushes because then the movement of the individual parts might be screwed up. With a func_breakable however, you can set up the individual parts to target the other parts once they break. I mostly set up small details to break only by trigger and only have the larger part of the breakable take the damage.

    Instead of func_wall, you can also use func_detail. I do not want brushes to cut into eachother, so every pillar and beam is a seperate func_detail. However, I still end up with this:

    What's going on? If you look into the properties of the func_detail, you'll notice an entry called 'Detail level' or 'zhlt_detaillevel'. This controls the way the compiler treats your brushes and the face subdivision. Brushes with the same detail level number will cut into eachother, so if one func_detail brush touches another func_detail brush then make sure they have different level numbers. Note: even if you tie 2 brushes into 1 func_detail, it's still going to cut into eachother, so always make a seperate func_detail for each individual brush.

    I'm going to use level 1 on the pillars and level 2 on the beams and the extra cuts are gone! Note that using a lot of different detail levels will slow down the compile process, so try to keep it to 1 or 2. Also, do not use 0, because that is the detail level of all normal world brushes.

    The benefit of using func_detail is that they do not count towards the model limit, as func_walls do. However, using func_detail can lead to higher wpoly because they will render outside of your Field of View (FOV). More on this in paragraph 5.1.

    3.2 NULL
    As wireframe mode shows us, the engine renders far more than we can see. Also some faces that players are never going to see. Luckily you can manually remove these by putting the NULL texture (from zhlt.wad) onto that face. Like the backsides of these crates: players will not be going there so I'm going to put the NULL texture on there, so those faces will not be drawn. This way I'm saving some more wpolies.

    But you don't have to NULL all and every backside. The compiler already takes care of that for you, but not always. Go into the console and type:
    cl_noclip 1
    When I fly around my map I see many faces already removed by the compiler. The compiler will remove all backfaces on world geometry and all func_detail brushes. It doesn't do so on brush entities, like this func_door:

    All brush entities (like func_wall, func_door, func_rotating, func_train, etc) will keep all their faces. You're going to have to manually use the NULL texture on the backfaces of these entities. Because many of those backfaces aren't lit, it is sometimes hard to see if a face is there or not. Get really close to the face and then activate the flashlight to see it better.

    3.3 Texture scaling
    Another way to improve the wpoly is to scale the textures up. The textures will become a bit blurry because of the scaling, so I'm going to use it on a texture you won't see up close: the ceiling. I'm using a value of 2.0 for the scale.

    Let's compare the results. The top half is the ceiling on scale 1.0 and the lower one 2.0. On scale 1.0 there are 4 horizontal faces and on 2.0 only 2, thus there is a reduction of 30 wpolies. It's good to know this trick, but just scaling up isn't always going to help or even necessary. It's better to know exactly why this happens, so you'll have to know a bit more about face subdivision.

    3.4 Face subdivision
    NOTE: The subdivision limit has been increased since version 5.21 from 240 to 528. More on that here. The basics of the system remain the same, so where it says 240 the default is now 528 units.

    The wireframe mode shows you quite clearly where the faces are being made but how does the engine determine those? The engine creates a new face every 240 pixels when a texture is on scale 1.0. Scaling the texture up will also increase that pixel limit. So on 2.0 the faces are being cut at 480 pixels, and on 0.5 on 120 pixels.

    Knowing this, you can experiment to find the optimal scale rate. If you look closely at the 2.0 scaled ceiling you can see that the faces on the left are smaller than the faces on the right. That means that this scale is not fully efficient. So I'm going to scale it to 1.75 and compile again:

    As you can see, the faces fit the ceiling perfectly at this scale and we didn't lose a single wpoly. Yet, the texture looks a little bit better! The only way to improve the wpoly now is to scale it up to 3.5 (creating 1 large face across) but that would make it look horrible. So, some guides might tell you to scale up your textures to redicilous amounts but now you see that it's not useful to do that.

    3.5 256x textures
    Now you know that faces are being made every 240 pixels, which means that 256 units high walls with 256x (or larger) textures will always make an additional cut, as you can see here:

    So, I'm going to grab the texture (C3A2A_W3) from the hl.wad with Wally and export it to a TGA.

    Now in Photoshop (or any other editor) I'm going to reduce the size of the texture from 256x96 to 240x96. Make sure you always use a power of 16 or Wally won't accept it when you upload it into your own WAD file. I'm going to rename the texture to C3A2A_NEW, so it doesn't conflict with the original. Then I'm swapping the new texture with the old and setting the scale to 1.067. This will make the 240x texture fit perfectly on the 256 units high wall and will also be the perfect scale for the face subdivision. Let's compile and see!

    The faces now fit perfectly and I've already won 20 wpolies in this corner! There is a tiny loss in quality (it has 16 pixels less) but it's hardly noticable on this texture and there is quite some gain. Since the face subdivision is 240, 240x240 textures are the perfect size. However you scale them up or down, it will always give you the most ideal quality/wpoly balance.

    Some guides might frown upon 240x textures, because older version of the engine scaled the textures down to 128x first and then back up to 240x resulting in a very blurry texture. As of SvenCo-op 5.17 this problem has been removed, so 240x240 textures are as sharp as they are meant to be! More info on that topic here.

    3.6 Texture alignment
    Faces are also affected by the alignment of a texture. Like this example: sometimes grass or sand can look better if you rotate the texture, but that also means the faces are being rotated.

    Using no rotation means less faces are being generated in this area. So if you really want a better effect you're better off editing the texture itself.

    The alignment also matters, especially on sloped surfaces. This wedge uses the FACE alignment which makes it shrink a bit horizontally and thus generating another face cut.

    Setting the texture to WORLD alignment fixes this problem and gives us back 1 wpoly.

    Texture alignment is also very important whereever 2 brushes meet, like in this corner:

    If you look closely, you can see that these textures are not properly aligned to eachother.

    And above you can see the result of it ingame, it created an extra face. To fix this, align the textures by selecting one of them, and then RIGHT CLICK on the other (Lift+Select mode should be on by default). As you can see, the problem was solved:

    It's easy to miss those faces, so a general good practise is to cut corners with a diagional line. This way there won't be a potentional extra face and I was also able to create a way better looking effect on the top of the brush. Some guides claim that this practise will always improve wpoly, but as you can see it actually comes down to the alignment of the textures in adjacent brushes.

    3.7 Texture merge
    Very often, you see mappers use multiple textures on walls to make maps look interesting. Many texture packs even come with these 'border'-textures. They are smaller (32x or 64x) textures with the same visual style, which can accompany a larger texture (128x or 256x) to create a nice effect. Here is an example from the hl.wad:

    The lower border texture is 64px high and the higher main texture is 128px high. And since they're seperate textures, additional faces will be created. It looks like I'm going to use this combination a lot, so a little math tells me that merging these two textures will result in a single 192px high texture. This is well within the 240px face subdivision cut-off, so definatly worth the effort. I'm exporting both textures from the hl.wad, combining them into a new texture and load it into game.

    And there you have it. Looks exactly the same and we've saved half the wpoly! Even if you're combining two 128px textures into one 256px texture then simply rescale that texture to 240px and you're back within the face limits.

    3.8 -subdivide
    So, we've read about the face subdivision limit, which is 240px. However, there is a compile command that can override this limit and create larger faces and thus less wpoly. I'm going to add '-subdivide 256' to the BSP option and here is the result.

    Only 182 wpolies, which is quite a gain in performance! So why haven't we used this before? Well, there is a very likely chance this option will cause your compile process to crash once it reached RAD. Overscaling the face subdivision can interfere with the way the engine handles the lightmap. If your map is simple and small enough (like mine), you might try to give this a shot. But as said, it's very likely this won't work for your map, so I'm not going to include this technique in my final optimisation results.

    3.9 Results
    So, back to our starting area. With these simple tricks we've managed to bring down the wpoly from 363 to 205 from this point of view.

    This is a great improvement, especially considering we haven't touched the main design of the map. Yet, there are more ways to make this map perform even better. Let's dig deeper into the workings of VIS.
    Last edited by Hezus; 14-03-2020 at 10:15 AM.

  2. #2
    Administrator Hezus's Avatar  
    Join Date
    Aug 2001
    The Netherlands

    Re: [Tutorial] Total Map Optimisation

    4.0 VIS
    VIS is a part of the compiler which handles the visibility, or in other words what the engine can 'see'. We've already determined by use of 'gl_wireframe 2`, that the engine sees more than the player does. I was able to see the room being rendered behind the door, since brush entities (like func_door, func_detail, etc) do not block engine visibily. Only solid world geometry can function as a visibility block, often refered to as a 'visblock'.

    To handle the visibility, the compiler cuts your parts into sectors (called visleafs). Here's a topview of a small example map.

    Since this is a very simple map, VIS did a good job and created the sectors exactly where the rooms and hallways are. The more complicated your brushwork, the harder it's going to be to determine where the sectors are. You can't highlight the sectors in-game, but you can see them pop in and out of existence with 'gl_wireframe 2', when you move around.

    These shots show pretty well how VIS works. The player is standing in sector #1. The engine can draw direct lines from sector #2 and #3 to sector #1. If that is the case, then these sectors will be rendered. There is no straight connecting possible from sector #1 to #4 or #5, so these won't be rendered.

    The player walks into sector #2 and the system updates. There are now straight connections to #1, #3 and #4, which are being rendered. Sector #5 still can't be 'seen' by the engine from sector #2 and remains unrendered.

    4.1 VISblock
    Back to our map. Since the door doesn't block VIS, the engine can look straight into the second room and it will always be rendered. To remedy this, one can make a 'visblock'. This is a world brush, strategicly placed to break the straight visibles lines we discussed above. I'm going to put it straight in front of the door.

    As you can see, this clearly worked! The visblock brush caused VIS to make extra sectors (#3, #4, #5) and there no longer is a straight line from sector #1 to #2 or even from the newly created sector #3! In result, the wpoly has gone down to 100. Quite an improvement from the original 363!

    4.2 HINT brushes
    Next to VISblocks, you can use another trick to manipulate the sectors created by VIS: HINT brushes!

    Before we go there, we need to get some more insights on where VIS cuts the map into sectors. You can't see it in game, but there is an option for the compiler. Use the `-viewportal` command on BSP and compile the map. Now in the editor, go to Map > Load Pointfile and find 'mapname_portal.pts'.

    I've created an example map for this purpose and you can clearly see where the cuts (called visportals) are made:

    Let's see how the engine approaches this situation:

    When the player is in sector #2, you can draw straight lines to all sectors, even #4! Let's try to visualise what sector #4 can see by imagining it's a flashlight and it casts light into the other sectors. This way we can see how far it's influence goes.

    As you can see it only touches sector #2 a little, but it still affects the entire sector. Time to put our thinking caps on! We can't affect the range of sector #4, but we can manualy create another sector in between #2 and #3! This is where we use HINT brushes!

    Create a new block and make sure all 6 sides have the SKIP texture on it. On the face where you want the cut to be, place the HINT texture. The cuts are always two-dimensional, so that's why you only need 1 straight face to use for the cut. The compiler is going to ignore the faces with SKIP on it. Now position the brush so that it cuts diagonally across the corner, like so:

    Let's see what it did. First a visual representation:

    As you can see, a new sector was created (#5) and this causes the straight connection between sector #4 (and even #3!) to break. This is what that looks like in-game:

    Everything around the corner is now unrendered. A huge improvement! HINT is a very good technique to use but as you can see, it takes a bunch of logical thinking and imagination to get it right. Using the portal file and the flashlight visualisation helps a lot but it can still be very tricky to get it right. And in some cases it might not even help or make the wpoly even worse.

    Let's see how this would affect our map when using the flashlight representation. I've removed the visblock wall we created earlier.

    Sector #1, affects #3 (the door) and nearly all of #2, except for a very tiny corner. If you created another sector there with the help of HINTs you'd be able to hide a really tiny crate there, but that doesn't seem all that effective. That means HINT isn't going to help us in this case and the visblock wall is our best option.

    When putting back the VISblock wall, I've noticed that VIS didn't pick the most optimal place to cut the sector.

    As you can see, the line between sector #2 and #4 is cut in a way that it creates a direct connection into sector #1. Therefore, when the player is in sector #1, everything in sector #4 will be rendered. Below, at sector #3, the cut has been make in the right way. There is no connection between #4 and #1 there. We're going to manually change the cut by placing a HINT brush:

    As you can see, the HINT brush helped the compiler to put the cut in the right spot. Now sector #4 will no longer be rendered. So, we think we did good, right? Let's put the player into the far corner and see what happens:

    The entire map is rendered! When we look back at our representation, you can clearly see why:

    There is a straight connection between sector #6 and #3, which also renders #4 and #5. We need to break those connections by creating additional sectors like so:

    Which then creates the following representation, in which you can clearly see that the connection to sector #6 has been cut.

    And many wpolies were saved in the process, from 215 to 104.

    I can even take it a little bit further by dividing the other room into 3 sectors:

    When standing in sector #5, there is no connection to #1, so that'll stay unrendered, saving a few more polies. And the same happens on the opposite side: when standing in #6, #3 isn't rendered.

    4.3 Player FOV and VIS
    If you've read up until this point, you'll probably have an understanding of the workings of VIS and its sectors. Now there is another factor that comes into play, which is the players Field Of Vision (or FOV). In Half-Life and Sven Co-op the 'default_fov' is '90', which means the player has a visual range of 90 degrees. In HL, at an aspect ratio of 4:3, the field of vision is actually 90 degrees, but in widescreen modes the FOV becomes a little larger. Because of some code changes, the FOV in SvenCo-op is even larger: 'default_fov 90' is actually closer to 110 degrees.

    So why is this important for our level's performance? Everything within the players' view will be rendered, even through certain walls as we've determined earlier in this tutorial. Objects and geometry out of the players FOV will be or won't be rendered depending on what they are exactly. I'll try to explain what does what:

    4.3.1 Brush entities (func_xxx)
    The most simple thing to unrender beyond the player FOV are brush entities (func_xxx). They are treated as entities and have an invisble bounding box around them. As soon as this box comes within the players view, the object will be rendered. So it is always good to turn as much geometry into brush entities (like func_wall) as you can, to make sure it will not be rendered outside the players view. Note that func_detail is technically NOT a brush entity. It is part of the world geometry.

    4.3.2 World geometry
    Regular world brushes (non-entity brushes) will be rendered according to the current VIS sector the player is in and the VIS sectors that are within his view. Look at the graphic below:

    The barrels behind the player are world brushes (non-entity). This room consists out of 1 VIS sector, so the player is constantly looking at this sector. Everything behind him is also part of this sector and thus will be rendered behind his back.

    Now I'm going to use HINT brushes (blue lines) to cut this room into 9 VIS sectors:

    As you can see, from our position in sector 5, sector 1, 2, 3, 4 and 6 are also within the player's FOV. Everything in these sectors will be rendered. However, the barrels behind the player in sector 7, 8 and 9 will now be unrendered since they are inside a sector outside of the player's FOV. This way it will improve your wpoly while moving into a a room since everything behind you gets unrendered.

    So, cutting up your rooms into a raster of HINT brushes seems like a good idea but it comes with a few problems. Firstly, it will create a lot more vis sectors (which are also limited) and it increases the compile time. However, these are only minor problems. The real problem is that it can actually increase your wpoly because whenever a HINT brush cuts through other geometry or objects it will create more faces:

    In an empty square test room the HINT raster technique might work great, but once your map has different shapes and is filled with objects, there is a very likely chance that your HINT brushes will cut through geometry and objects and leave you with more wpoly than you initially had. Especially because your HINT brush has to run from wall to wall and floor to ceiling to create a closed off VIS sector.

    So only if you are being careful enough not to cut through existing geometry you can benefit from this feature. Looking at our test map again, we could add these HINTS without cutting through anything:

    4.4 -maxnodesize
    The BSP compiler has an option called 'maxnodesize' which is on 1024 by default. This is the maximum size of a generated VIS sector. As you can see above, sometimes it helps to create smaller sectors, so you can let the compiler do this job for you. At its best, it can improve wpoly if it places extra sectors to stop direct connections between other sectors (like we did when we used HINTs). At its worst, it can create extra wpoly if the sectors arn't cut in an optimal way and it will generate a lot of extra sectors. This increases compile times and there's a limit of how many sectors the compiler can handle. It will also increase file size of your BSP. And as we've seen above, the new sectors can also cut through brush entities and create a lot of extra faces, making the wpoly worse.

    A possible way to work with this, is to set the -maxnodesize to a lower value, like 512 (64 is the lowest possible) and see what effect it has. If it improves wpoly in certain areas it means you've found an optimisation opportunity! Load up the portal file and see where this extra cut was made and how it affects the map. If it's beneficial, then recreate the cut with a HINT brush. Go around your map and repeat this step. Once you've found them all, you can set the -maxnodesize back to it's default. This way you've optimised the map fully and you're not creating unnecesarry sectors. I would recommend leaving the maxnodesize on 1024 on a final compile, since it generally gives you the best wpoly results once you've manually places all the best HINT brushes.

    4.5 Results
    So, by understanding the workings of VIS, implentation of a VISblock and the use of HINT brushes, we've greatly optimised our map. With all techniques learned from the guide so far, we've brought down the wpoly by a factor of 3, without changing too much about the layout or the quality of the map. This is the maximum of optimisation I could squeeze out of my small test map.

    In the next chapters I'm going to show you some misc. optimisation tricks and will also delve deeper into the other aspects and limits of the engine and how to optimise them!
    Last edited by Hezus; 07-01-2020 at 07:45 AM.

  3. #3
    Administrator Hezus's Avatar  
    Join Date
    Aug 2001
    The Netherlands

    Re: [Tutorial] Total Map Optimisation

    5.0 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 oneanother. 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_details are meanies, because always render behind your back. Func_walls 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).

    There is one drawback for the func_wall, though: it counts towards the model limit. In the current SC version, this has been pushed to 16384, so you will not be likely to hit it quick, but with large maps that use a lot of models it is possible. If you do, consider changing some objects into func_detail to free up some room.

    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 adviced to use this technique on smaller objects and in well-lit environments, so the lack of shadows on the model will not be very noticable.

    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 relativly 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 definatly cheaper than using models or brushwork, so are a potentional polysaver when applied right.

    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 seperate 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 arn'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. In SC, you can't add your own custom decals to this wad file, because that could potentionally cause conflicts, 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 neccesarily mean you'll notice a lot of framedrops. There are levels out there with 8000 wpolies which run fine on modern videocards, but you'll need to find the right balance between wpoly and epoly to make sure there are no framedrops. Even on high-end videocards, 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. And with Sven Co-op, you must always factor in the extra players. If the map runs fine on your machine while testing in single player, imagine adding another 16 players to it, with the extra epoly of their models, weapons, effects, etc.

    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 single player maps you could go higher, but for multiplayer and especially Sven Co-op I'd advice the following guidelines.

    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. Going above 1500 wpolies or 100,000 epolies isn't a very good idea for Sven Co-op. Personally, I'd like to keep my areas around the 800 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. However, as of SC 5.17, 1024x1024 textures are possible, which can bring even more sharp details!

    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. Translucant 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 certrain 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_walls 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 a 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 techinque 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_renders 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 outweight the con's? Since it's hard to measure the effects of detailtextures and it affects graphics cards very differently it's quite difficult to say wether 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:

    1. Create a 16x16 texure on a grayscale. The darker the gray, the darker your texture will be in game. Put it into a WAD (calling it detail_wall1).
    2. Make sure the hi-res texture you want to use, is a 24bit TGA. Put it in ../gfx/detail/mapname/ (calling it wall1.tga).
    3. Put the detail_wall1 texture on a wall with scale 1.0 and compile the map.
    4. Create a text file in ../maps called 'mapname_detail.txt' (where 'mapname' is the name of your BSP).
    5. 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.
    6. 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 usefull, 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 VISblocking 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 simular 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 usefull 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 potentional. 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.


    In some cases you want to have total darkness at the end of a dark tunnel where players can't get or a very deep drop where they fall to their deaths. In the default HL.wad there is a 'black' texture you can use but this texture will be prone to face subdivision and thus add to the wpoly. Within zhlt.wad there is a special texture called BLACK_HIDDEN, which is black but behaves as a NULL texture and will not be rendered. A regular NULL texture would do the same thing but using BLACK_HIDDEN might make it clearer in the editor what you are using it for.

    Note that this will only work properly in the Sven Co-op. In regular Half-Life, you will see the hall-of-mirrors effect on these faces.

    When creating a trigger brush (trigger_once, trigger_multiple, etc), traditionally you'll use the AAATRIGGER texture. It's useful, because you can easily recognise where trigger fields are and some editors (like JACK) will automaticly make them transparant 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. The AAATRIGGER texture (just like the NULL texture) is automaticly 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.

    5.14 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 practise
    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 uncessary 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 recognises 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 wierd 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 improvments)
    -cliptype smallest (reduces clips by 15%, but doesn't do a good job at it, wierd 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_xxx 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, gonarch, 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 tooltexture "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 extensivly to make sure this option doesn't lead to any problems and the gain is relativly 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 practise. 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_xxx 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.

    -cliptype precise (this will optimise 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)

    -viewportal (creates a portal point file, so you can see the VIS portal cuts in the editor)

    -fast (for quick compile to test your map)
    -full (better and complexer calculations, for final compile)

    -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.
    -vismatrix 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)
    Last edited by Hezus; 20-02-2020 at 09:38 AM.

  4. #4
    trigger_delay & func_lazy TrEmPlEr's Avatar  
    Join Date
    Jul 2002
    Valve Hammer Editor 3.5 beta

    Re: [Tutorial] Total Map Optimisation

    Oh my gosh <3 nice guide so far, can´t wait for more !

    Modstatus: Unknown

  5. #5
    Advanced Leveldesigner SourceSkyBoxer's Avatar
    Join Date
    Apr 2011

    Re: [Tutorial] Total Map Optimisation

    Nice @Hezus thanks for explanation! I will fix my ragemap2018

    PS: Where is example of func_detail if it happens who mapper doesn't know like who made func_detail outside of hollow room like I show you example hallway with curvy surface or spaceship-like wall than it will avoid for leak right?

    Example it is wrong:

    It is correct:

    It is really important for func_detail brushes with closed hollowed brushes.

    Thanks for avoiding of leaks.

    // EDIT What does it happen with gl_wireframecolor command?

    For me nothing found:
    Last edited by SourceSkyBoxer; 09-08-2018 at 04:12 PM.
    Hello guys, Svencoop. I am sorry Please respect me! I am deaf. Thanks, Sven-Coop Forum!

    Please do not share to shit dropbox! Please share only!
    Muhahaha, Facebook crashed!

  6. #6
    Administrator Hezus's Avatar  
    Join Date
    Aug 2001
    The Netherlands

    Re: [Tutorial] Total Map Optimisation

    The wireframecolor command is only available in 5.17. Forgot to add that. It'll be released soon

    As for your example: The guide is for optimising the map, not for general mapping errors. If you want to optimise your map, I automaticly presume that you've managed to make a map without leaks

  7. #7
    Administrator Hezus's Avatar  
    Join Date
    Aug 2001
    The Netherlands

    Re: [Tutorial] Total Map Optimisation

    Made various updates to the guide lately, so be sure to check it out again if you already read it once

    If anyone has corrections, additions or new topics/techniques that I haven't covered, I'd love to hear about them.

  8. #8
    Administrator Hezus's Avatar  
    Join Date
    Aug 2001
    The Netherlands

    Re: [Tutorial] Total Map Optimisation

    Updated the guide some more! New part about TRANSPARENT textures and their VISblocking properties.

  9. #9
    REE3 The303's Avatar  
    Join Date
    Jul 2015

    Re: [Tutorial] Total Map Optimisation

    God-tier work here Hezus.

    Ill also add, you can also make good use of sprites for example, using a parallel_upright mode for some standing details so you can just use a masked picture instead of a model like the stopsign on the examples here:

    Also in part 3 you can fake out wall decals without needing to make new wad textures that can use rendermodes too.

    As for models, you can fudge some lighting errors with zhlt_copylight keyvalue to an info_target. As mentioned before with the part 3 sprite there is an example of doing a fake shadow too.

  10. #10
    func_vehicle enthusiaist w00tguy123's Avatar
    Join Date
    Dec 2006
    U.S. West

    Re: [Tutorial] Total Map Optimisation

    I thought I knew all the tricks but that SOLIDHINT section saved me dozens of polies in the Rust map.

    This guide is so huge it should be a set of wiki pages or something.

  11. #11
    snarks snarks snarks snarks snarks snarks snarks snarks Green Astronauts's Avatar
    Join Date
    Mar 2013
    Nijmegen, the Netherlands

    Re: [Tutorial] Total Map Optimisation

    Quote Originally Posted by Hezus View Post
    Updated the guide some more! New part about TRANSPARENT textures and their VISblocking properties.
    Really really interesting and very concisely written. Thanks Hezus!

    Edit: Quick note/question about a different part of the guide: isn't it zhlt_usemodel and not zhlt_copybrush? I recall there being a key called phlt_copybrush (from SHLT) that behaves similarly, but I don't think it exists in VHLT. Been a while since I mapped though.

  12. #12
    Administrator Hezus's Avatar  
    Join Date
    Aug 2001
    The Netherlands

    Re: [Tutorial] Total Map Optimisation

    Thanks for all the comments

    @303: Totally forgot about sprites! I've added them to the guide!

    @Green Astronauts: You are right, it's zhlt_usemodel. It's a derivative of the old phlt feature, so I probably mixed up the names. Fixed!

  13. #13
    Administrator Hezus's Avatar  
    Join Date
    Aug 2001
    The Netherlands

    Re: [Tutorial] Total Map Optimisation

    Added a new section about player FOV and VIS (4.3)! I've also adjusted some info here and there to make it more accurate or add to it.

    During my test I've also discovered some wierd properties (or issues) of the func_detail. As soon as I have it figured out, I'll probably write a bit about that too!

  14. #14
    Administrator Hezus's Avatar  
    Join Date
    Aug 2001
    The Netherlands

    Re: [Tutorial] Total Map Optimisation

    I've greatly expanded the topic on clipnodes (6.2) with a more in-depth look at the workings of collision in GoldSource. I've come to quite a lot of new insights that I've never seen in any other tutorial, so make sure to give it a read. I also wrote a bit on the BEVEL texture, which some mappers seem to use as general practice (spoiler: don't do it!).

  15. #15
    Administrator Hezus's Avatar  
    Join Date
    Aug 2001
    The Netherlands

    Re: [Tutorial] Total Map Optimisation

    I've rewritten the first part of the tutorial in favour of the use of func_wall.

    Refer to 5.1 to the specifics on this! Spoiler: func_wall is actually the better entity for wpoly reduction!

    Also added a note on the increased subdivision limit. I've not completely rewritten the guide since a lot of vanilla HL users refer to it too and the base principles remain the same.

  16. #16
    Administrator Hezus's Avatar  
    Join Date
    Aug 2001
    The Netherlands

    Re: [Tutorial] Total Map Optimisation

    Added some more to the guide about the use of AAATRIGGER vs NULL (5.13) and NULL vs SKIP (5.14).

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts