Post news Report RSS Hard Duty: Atmospheric Demo - An inside view to resolving issues

Hard Duty: Atmospheric Demo entered beta testing not long ago, see what has been learned through this phase.

Posted by on

Howdy!

We've been in beta testing for about a week now, and there are a couple key points that I would like to share that I have learned during this phase. This post is mainly intended to help you learn as well from my particular experience. I will be posting a second article shortly after talking more about the things you need to know about Hard Duty, such as new features, fixes, etc. There are a lot of things there too.

Overall, beta testing has shown no major issues -- no crashes, no major softlocks, no major game breaking bugs. That said, there has been quality feedback and I would like to start...

...on the subject of optimization

Hezus, one of the beta testers, and I had a very in-depth talk about optimization, mainly around func_detail and func_wall. This conversation has led to the update of his TWHL tutorial on optimization. See section 3.1.2 and 5.1.

For the longest time, I have only known what the detail level on func_detail is at the surface level. Detail level tells the game whether a face from a func_detail will cut into another func_detail's face. Let's take a look at an example. The screenshot below shows a pipe (tied to a func_detail) touching a metal plate (which is another func_detail). Both of those brushes have a detail level (zhlt_detaillevel) set to 1.

Face Cuts

Whenever 2 func_detail brushes of equal detail level connect like that, it creates unnecessary additional face cuts, which causes additional wpolies (more on wpolies in a second). This is a problem, because, despite the fact it looks ugly in wireframe mode (gl_wireframe 2), this can cause terrible optimization issues when a large number of func_detail brushes do that, driving wpolies up, thereby causing FPS to tank. The way Hezus explained this to me in order to properly understand detail level on func_detail is to think of faces as layers. In short, the smaller the face is, the higher the detail level it should be. In our example above, the face of the pipe touching the metal plate is smaller, therefore, I am going to set the entire pipe to a detail level of 2.

Detail Level

This one simple change allows us to fix those ugly cuts, and we managed to reduce the wpolies a bit.

Bad Face Cuts Fixed

The problem is that I had done the entire level design by that point and had many, many extraneous face cuts caused by my lack of understanding of this basic concept. This is perhaps partly due to the fact that I am a long time Source engine designer and this problem ultimately doesn't exist. Still, I went through all of the maps and fixed all the func_detail brushes. The results pretty much speak for themselves (check the wpolies number on the top hand left corner):

(Please note that gl_wireframe 2 causes FPS to tank, at least, on my system. You will not be suffering from FPS loss under normal gameplay circumstances.)

Before:

Func_detail hunt down

After:

Func_detail hunt down


Before:

Func_detail hunt down

After: Func_detail hunt down


Before:

Func_detail hunt down

After:

Func_detail hunt down

As you can see, I managed to save a massive amount of wpolies with this very simple fix.

However, we run into another issue when curved pipes are involved. Take a look at this:

Extra face cuts despite func_detail

Despite proper detail level used on those pipes, we still run into the issue of them causing additional face cuts for no good reason. This is because GoldSrc just does not like anything that is not square and on the grid. Half-Life does not really have any of this type of complex geometry anywhere. Only place such complex brushwork can be found is the dome in Uplink.

Sadly, there is no real long term good solution here. The fix here was to turn each brush to func_wall individually (I cannot stress the importance of this last word! More on this in a second). This, however, comes at a cost. As explained in Hezus's optimization tutorial, func_wall is treated like a model, and there is limited space in classic GoldSrc for models (512!), which means if we do this too often, we are quickly going to run out of resources. If that was not bad enough, it also raises clipnode levels up (we do have some control over this, but this is beyond the scope of this article). Therefore, you should use func_wall very conservatively for such cases. You are more likely going to use func_wall for transparent textures anyway, so keep this in mind.

By using func_wall on those pipes, we managed to fix all the bad face cuts.

Bad face cuts fixed with func_wall

Why tie each individual brush to func_wall, though? There are two mains reasons. First, if we group all the pipes and tie them to one func_wall, this is not going to help much in terms of visibility optimization, because as soon as one bit of the pipes is visible, the entire thing gets rendered, even parts that are out of our FOV. Secondly, this prevents some faces from getting deleted. Since we are dealing with complex brushwork, we prevent face errors from happening from the get go.

With that said, you should always rely on func_detail as much as possible first for most of your detail brushwork. There is one drawback to func_detail, though. It counts toward the worldleaves (but this limit is much higher!), which means that, if you are like me, and like filling your levels with a lot of detail brushwork, you are going to hit that limit real fast. Take a look at this:

Curved Geometry

The entire brushwork you see here is made of func_detail. The problem is that, despite the fact it looks cool, this kind of brushwork drives the worldleaves to insane levels, and that is not even talking about the compilers trying to compute such a thing. I have been very lucky that it compiled fine, but this means you need to pay attention to your brushwork. Therefore, you need to make a choice. You can either make big levels, but pull back on the detail level, or make massively detailed levels, but you will have to resort to smaller levels (and therefore, more level transitions). You have all the cards. Use them wisely.

On the subject of func_tank

This entity has also caused me some trouble. Here is something you need to know. A mounted gun can either be controlled by a monster, or controlled by the player, but not both at the same time on the same entity. When I say controlled by a monster, this is to simplify the problem. Technically, a non controlled func_tank has a mind of its own and will shoot the player on sight. In Opposing Force, there are two sequences where a Black Ops monster is behind a 50 cal and once dead, you can control the gun. In reality, what is happening is that there are two func_tank brush entities. One is visible and shooting at you and the one you control is invisible until the Black Ops monsters are dead, which will then kill the first func_tank and render the second one for you to use. This was my original setup as well.

Take a look at this:

Tank

In this level, there is the use of a func_tank by a grunt. The issue we had is that any time you would specifically gib him, the game would crash. I could not figure out what was going on so I turned to the brogrammers who investigated the issue and found out that the issue was client side (so not a level design error). The crash itself was never fixed, but a solution was implemented so that the crash never happens again:

Tank Control

This is an entirely new entity. What this does is eliminate the hack of using a second func_tank and allows a level designer to simply have one func_tank "controlled" by a monster and whenever that monster dies, you can take control of the gun. Pretty neat!

On the subject of level transitions (trigger_changelevel)

This one took me a while to understand as well. See here:

Transition

This area is a transition zone. A change level happens if you will. This transition area used to be a lot more simple than what it is now (I extended this area). This is important to note, because when I was working on this area, I modified the trigger_changelevel brush to fit the entire area, naturally. The problem is that I had my game running in the background, as many of you probably are doing too when jumping back and forth between Hammer and the game, but this is absolutely bad whenever trigger_changelevel gets involved. For whatever reason, if you touch trigger_changelevel in any way and try to test it in-game, the transition will not work properly anymore, despite the fact that it was working perfectly previously and you have not changed any keyvalues. I could not for the love of me figure out why this was happening until I restarted my game and it started working properly again. I asked Hezus if he ever had the same problem and confirmed he also did. He was kind enough to provide me with an explanation that he got himself from Solokiller:

the restart command doesn't pass a startspot name which is used for landmark-based changelevels, and the engine doesn't have the last landmark offset set by the first map's changelevel so the landmark offset points to the world origin
[7:17 PM]
startspot is supposed to spawn you at the landmark, the offset just moves you to the location you're supposed to be at
[7:18 PM]
what you can do is this: changelevel mapname landmarkname
[7:18 PM]
specifying the landmark name as the second parameter sets spartspot, so you'll spawn there
[7:19 PM]
c1a4e for instance has no spawn points at all, so without this you spawn in the void
[7:19 PM]
but you can't use changelevel if not already in a map
[7:20 PM]
it is possible to rework spawn point logic to use landmarks before using the world origin so it's definitely fixable

Therefore, keep this in mind whenever you work with trigger_changelevel. Quit the game or restart it before testing your changes.

Post comment Comments
Barney/msn.com
Barney/msn.com - - 17 comments

Ah, good news!

Reply Good karma Bad karma+1 vote
SovCounselor
SovCounselor - - 33 comments

Very not bad.

Reply Good karma Bad karma+1 vote
fresco
fresco - - 140 comments

Yup, trigger_changelevel can be nasty. Great article!

Reply Good karma Bad karma+1 vote
Dr.pper
Dr.pper - - 269 comments

I thought this mod was dead!

Reply Good karma Bad karma+1 vote
Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: