Thread: [Tutorial] Total Map Optimisation

Results 1 to 12 of 12
  1. #1
    Super moderator Hezus's Avatar
    Join Date
    Aug 2001
    Location
    The Netherlands
    Posts
    9,004

    [Tutorial] Total Map Optimisation

    Introduction
    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
    r_speeds 1
    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.


    4. 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_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_details.



    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_detail.



    Everything fine, except for this part:



    The panel and the pipe are both func_detail, yet they still cut extra faces. 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 panel and level 2 on the pipe and the extra cuts are gone! Another 8 wpolies saved easily. 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 regular (non-entity) world brushes.



    A lot of guides might tell you to use func_walls. Func_walls also stop faces from forming but they also count towards the model limit, which func_details don't. Also, func_details will cast shadows by default. This all makes it the superior entity for this purpose.


    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 faces already been removed by the compiler, even on func_details touching eachother.



    But not on the func_door I'm using.



    Many moving brush entities (like func_doors, func_rotating, func_trains, etc) will keep all their faces. Since they can move and turn, the compiler doesn't automaticly remove faces. You're going to have to manually use the NULL texture on these. More wpolies saved!


    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
    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; 17-08-2018 at 04:25 AM.

  2. #2
    Super moderator Hezus's Avatar
    Join Date
    Aug 2001
    Location
    The Netherlands
    Posts
    9,004

    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 -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.

    A good way to work with this, is to set the -maxnodesize to a lower value, like 256 (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.


    4.4 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; 02-10-2018 at 08:13 AM.

  3. #3
    Super moderator Hezus's Avatar
    Join Date
    Aug 2001
    Location
    The Netherlands
    Posts
    9,004

    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 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.2 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.3 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.4 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.5 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.6 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.7 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.8 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.9 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.10 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.


    5.11 BLACK_HIDDEN

    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.




    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:

    Code:
    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 16 rule you often see while working with this engine: 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, 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
    Clipnodes are used to calculate how the engine handles collision. Each entity has a hull around it and so does all world geometry. There are 4 types of hulls from really small (8x8x8) to large (64x64x64). So for instance, a Gargantua has a large hull (64x) around it, so if it walks into a wall, the hull stops it 64 units from the Garg's origin. This stops the Garg from clipping into the wall. The same goes for func_pushables. Since 64x is the largest hull, you'll notice how very large pushables will clip through the wall when pushed against it. The engine has to make nodes (clip points) for all these 4 types of hulls, which takes up a lot of resources. Here is how to reduce them:

    6.2.1 -cliptype
    The CSG compiler has a parameter called '-cliptype', which has various settings:

    -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.2 CLIP texture
    Zhlt.wad has a special CLIP texture that removes clipnodes from the area where a CLIP block has been placed. Put blocks with the CLIP texture on it in places where players won't be going, such as high ceilings, small holes, narrow paths. And also put CLIPS around complex geometry. So if you have a spike for instance, and players don't have to stand on it or touch it, then put a square box with CLIP textures around it. Even CLIPPING the tiniest hole can help saving clipnodes.



    6.2.3 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.4 -nohull2
    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 and large pushables, so if your map doesn't feature those, you can use this compile command and save a lot of clipnodes.

    6.2.5 -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.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.

    CSG
    -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)

    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 complexer 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)
    -sparse (compresses the vismatrix, saves engine resources)
    -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.

    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; 15-10-2018 at 05:11 AM.

  4. #4
    trigger_delay & func_lazy TrEmPlEr's Avatar  
    Artist
    Join Date
    Jul 2002
    Location
    Valve Hammer Editor 3.5 beta
    Posts
    2,278

    Re: [Tutorial] Total Map Optimisation

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

    here
    Modstatus: Unknown

  5. #5
    Advanced Leveldesigner SourceSkyBoxer's Avatar
    Join Date
    Apr 2011
    Location
    Germany
    Posts
    787

    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 Mega.nz!
    Muhahaha, Facebook crashed!

  6. #6
    Super moderator Hezus's Avatar
    Join Date
    Aug 2001
    Location
    The Netherlands
    Posts
    9,004

    Re: [Tutorial] Total Map Optimisation

    @SSB:
    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
    Super moderator Hezus's Avatar
    Join Date
    Aug 2001
    Location
    The Netherlands
    Posts
    9,004

    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
    Super moderator Hezus's Avatar
    Join Date
    Aug 2001
    Location
    The Netherlands
    Posts
    9,004

    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  
    Contributor
    Join Date
    Jul 2015
    Posts
    391

    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:
    http://www.the303.org/tutorials/gold_sprite.htm

    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
    Location
    U.S. West
    Posts
    1,570

    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.
    Love,
    w00tguy

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

    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.
    In-game name:


    Administrator at the ModRiotGaming servers

  12. #12
    Super moderator Hezus's Avatar
    Join Date
    Aug 2001
    Location
    The Netherlands
    Posts
    9,004

    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!

Posting Permissions

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