VHLT's "func_detail"

MS:C community

Old Skool Apostle
Alpha Tester
Jul 7, 2011
485
95
Hello everybody,

I've just been experimenting a bit with an entity that's exclusive to vluzacn's modified version of Zoner's Half-Life Tools v3.4, also known as ZHLT v3.4 VL30 or "VHLT" in short. This entity is called func_detail and it is the best entity ever made. Ever.

What is it? A func_detail is a brush-based entity that does not cut into the BSP structure (= less wpoly = higher r_speeds = better map performance) but it does block light/cast shadow -- like you'd generally want solid objects to do.
Big deal, you can use a func_wall and play around with the "ZHLT Lightflags" Attribute to achieve just those effects, right?

But here's the thing that makes func_detail the best entity ever: It's actually not an entity! Upon completion of the map compile, they're not counted as entities, and if you view your map in BSP Viewer, func_detail doesn't even show up in the entity list. They render fine (and infact still render even if you disable entity rendering) and, like I said, block light yet don't cut worldbrushes. They do cut each other (like func_wall entities cut each other) but that's no big deal really.

I'd like to quote vluzacn here:
vluzacn in the ZHLT v3.4 VL25 changelog said:
Add 'func_detail' entity, which is similar in function to the func_detail in Source, though I probably implement it in a rather different way.

What is func_detail:
(Brushes that are not in any entities are called world brushes, and brushes in func_details are called detail brushes.)
Detail brushes are almost same to world brushes. They cannot be distinguished from world brushes once the compile has finished. For example, when viewing the bsp with 'BSP Viewer', func_details don't disappear after unchecking 'render entities'.
What makes detail brushes different from world brushes is that they don't participate in visibility calculation, which means they won't slow down hlvis.
A better description can be found here: http://developer.valvesoftware.com/wiki/Func_detail

Differences between detail brushes, world brushes and func_walls:
Compared to world brushes, detail brushes don't block vis or slow down vis process, and don't chop faces from world brushes, and don't cause the 'Ambiguous leafnode content' warning.
Note that detail brushes cannot be used to seal a map, and must not be made of water or sky (but SKIP and CLIP can be used in func_details).
Compared to func_wall, func_detail don't consume engine's model precache slots (which is 512 max), and will give player mdl (and any other mdl) correct brightness when he steps over it.
In addition, there is an option that allows a func_detail to chop faces of world brushes in order to reduce face area and produce better lighting.
But unlike func_wall, the faces in func_detail will be cut if it happens to span across a splitting plane of the BSP structure generated from world brushes and func_details, thus increasing bsp file size and wpoly.

Usage:
Convert any brushes, that are not parts of the basic structure of the map, to func_detail. They cannot be water brushes or sky brushes.
You can leave 'Detail level' to 1. For tiny objects, you might set it to 2, so that they won't chop the faces of other func_details.
For large shapes such as terrain and walls, you can set 'Lower its level to cut others' to no less than its detail level, so that they can chop adjacent world brushes like world brushes.
Before compiling the map, hide all entities and func_details, make sure there are no leaks and the structure is good enough for vis calculation.

Further explanations:
To add the feature of detail brushes, I introduced a concept called 'detail level', which is a number assigned to each brush, which can be 0, 1, 2, 3,... .
--I will refer to all brushes that have the same detail level as a 'layer':
All world brushes are in the layer of detail level 0.
All brushes in a func_detail go to the layer corresponding to the func_detail's 'detail level' which is default to 1.
Brushes in brush entities such as func_wall are not in consideration here, because they are in seperated entities.
--In the CSG process:
Every layer will not be affected by any more detailed layers (with only one exception).
Every layer will be chopped by all less detailed layers. That is, if a brush in this layer is embedded into less detailed layers, then the embedded part will be chopped off.
Brushes in the same layer always chop each other.
The exception is, if you want a brush from a more detailed layer to chop the faces of a brush from a less detailed layer, you can either set the former's 'Lower its level to cut others' or the latter's 'Raise its level to get cut' to no less than the difference of their detail level.
--In the BSP process:
First, the surfaces of the layer of detail level 0 split the space into large leafs, which are also portal leafs that are used in vis calculation.
And the layer of detail level 0 is not allowed to have leaks, so detail brushes cannot be used to 'seal' a map.
Then the surfaces the layer of detail level 1 split every leaf into smaller leafs, but they no longer act as portal leafs.
Then does layer of detail level 2, 3,... .
Note that number of visleafs reported in the bsp statistics chart is the number of final leafs generated by all layers.
--In the VIS process:
The visibility data is calculated only according to portal leafs generated by the layer of detail level 0.
So, func_details will not increase the time of VIS process significantly. But they may have some impact on the VIS process, because when the BSP program is processing the layer of detail level 0, it has to also try to optimize the total number of faces, nodes and leafs.
After the calculation, the vis data between portal leafs is then converted to the vis data between final leafs. So adding func_details will increase the total size of vis data.

Other notes:
The detail layers uses an advanced method to decide leafnode content, so detail brushes will not cause "Ambiguous leafnode contents" warnings and related problems.
Usually merely using detail level 0, 1 and 2 is enough. Using too many detail levels may make the BSP process inefficient and increase the bsp file size. For the same reason, 'Detail level of cliphulls' should always be 0 or 1 unless you are trying to solve some cliphull errors by adjusting this value.
Changing the 'Detail level of cliphulls' to 0 will reduce the number of clipnodes, but the cliphull of this func_detail will not use the new leafnode content method.
vluzacn said:
Too many small world brushes will slow down the VIS part in map compiling a lot and often increase face number since they cut intersecting faces. Typically, you solve this problem by converting them into func_walls. But converting to func_walls also has some negative effects, including consuming model slots (you will get PF_precache_model_I error if you have too many brush based entities), less effective VIS optimization (which means that the whole func_wall will be rendered if any part of it is visible to the player), and sometimes incorrect color of player model when a player steps on it. Func_details are good substitute for this kind of func_walls because they don't have these issues.
What I want to emphasize is: func_detail is amazing. It's the best entity of all time. I really am running out of superlatives to describe how incredible this thing is.
A map like bloodrose could probably save over 60 or even over 70 entities by converting (most) of the func_wall to func_detail.

I'll upload comparison pictures later.

P.S.: Mappers who examine the zhlt.fgd that comes with ZHLT v3.4 VL30 will probably feel like kids walking into candy stores (lots of awesome entities in there). Try it!
 

MS:C community

Old Skool Apostle
Alpha Tester
Jul 7, 2011
485
95
Now with pictures!

I hacked together a quick test map that features two spot lights (actually three, but the pictures only show two of them), one blue and one yellow, aiming at a staircase. I compiled the map three times:
  • comd_wb: the staircase is a worldbrush
  • comd_fw: the staircase is a func_wall (with "ZHLT Lightflags" = "Opaque")
  • comd_fd: the staircase is a func_detail
'comd' stands for 'comparison: detail'

The testmap isn't all that good (a bit too dark) but it serves its purpose and that's enough. Pictures! Click to enlarge.

The staircase as a collection of worldbrushes: You can clearly see the VIS cuts (some horizontal, others vertical) on the walls. Ew... 218 world polygons for an area this underdetailed, yuck.
XXkczCp.png

The staircase is one big func_wall. Now as you can see, there are no more horrendous VIS cuts on the walls due to the staircase, but still 116 world polygons (floor, wall, ceiling) and 99 entity polygons (staircase) are being rendered. That's a total of 215. This is because the entire staircase is being rendered, ie. also the part behind the player. This could be solved by turning the staircase into two separate func_wall entities, but there's a better way...
v9GQs77.png

The staircase is one big func_detail. Again there's no boatloads of VIS cuts on the walls but this time the staircase is not a 'real' entity! As you can see, it doesn't even show up in the entity list on the left-hand side of the screen! And only 153 total brush polygons are being rendered! That's about 25% less than in the other two maps! SWEET JESUS!
6mzFMMh.png

In-game pics of sh*tty testmap here and here.

You can also download a ZIP archive with all three map sources, all three map BSPs and the above pictures. See the wizardry for yourself!
 

Thothie

Administrator
Staff member
Administrator
Moderator
Lead MSC Developer
MSC Developer
Apr 8, 2005
16,225
272
lost
Fair warning, after extensive experience with this entity - it does not always work as advertised - be prepared for odd issues, especially with alloc blocks.

VHLT itself also introduces a lot of lighting issues and bugs, especially when trying to light water, as well as having several bugs involved in detecting alignment of brushes with different textures on each face - and really wild differences in lighting consistency. Converting a map that wasn't designed with VHLT in mind, that uses light textures, is always an epic adventure...

But it also has a lot of advantages, including the fact that, so far as I know, it's the only compiler that can return your alloc block usage in -chart. It also has a lot of odd lighting features, including transparency casting (see the gates on The Wall), toggle'able shadows, and translucence (though I've not had much luck with that last one). Be nice if it had the syntax lighting of SHLT, and at least an option to use the classic lighting style - or something more consistent with it.
 

MS:C community

Old Skool Apostle
Alpha Tester
Jul 7, 2011
485
95
Could you please be a bit more specific about these problems you encountered?
And you're referring to allocblock:full?

Anyway I removed my "I Xn1XZd7.png func_detail" signature because I don't want to advertise something that might cause problems.

Also, the VHLT RAD "knows" what colour of light a texture should emit (ie. it knows that a red texture should emit red light). Therefore you only need to specify the brightness for texture lighting with VHLT, rather than the colour and brightness like you would when using ZHLT or SHLT. I haven't really worked enough with water to have noticed any weird issues, but as for brushes... They always turned out fine thusfar.

I'll keep your warning in mind though, thanks.

Edit: Incase you're interested: vluzacn uploaded a neat testmap demonstrating his toggle-able shadow effect.
 

Thothie

Administrator
Staff member
Administrator
Moderator
Lead MSC Developer
MSC Developer
Apr 8, 2005
16,225
272
lost
See my edit above regarding other VHLT pros and cons...

But in regard to func_detail specifically, it seems to cause clipping issues if the func_detail is too complex or intersects other objects - sometimes causing nearby floors and walls to lose their clipping (fall through map) - though it also seems to do this to rooms in adjacent vis nodes - eg. across the room, in a brush it's not even touching. And yes, from time to time, with no real explanation, it'll spin the alloc block count out of control (which, yes, lets the map compile, but causes it to crash on load). It also likes to sometimes cause vis extent errors (also only fatal at run time), if you mix faces on it and one or more faces are vised out or nulled. Solid func_details (ie. clipped ones) also tend to have very haphazard clipping issues, especially, again, when intersecting with other brushes, placed near complex ones, or used near other solid entities (eg. doors, walls, breakables). Sometimes it seems the two clip structures kinda merge, developing an invisible wall between them.

Spent a lot of time with it tweaking up Deralia_Sewers and attempting to circumvent Shender_East's alloc block problems. (Which in the end just involved replacing some map structures with models - which is, sadly, why you can't use the stalagmites for cover against the crystal spider lightning ball spam.)

When it does work, it works well - and it's *generally* faster than SHLT (depending on your lighting setup). When starting a map from scratch, it's much easier, but if you've been compiling in ZHLT or SHLT, and already have lighting, I'd suggest sticking to what you're currently using, as it can be quite frustrating to try to fix the lighting issues that crop up. If you come across some bizarre compile issue, however, sometimes it can get around those - though sometimes it'll cause ones of its own (bit hit or miss there - it did, for instance, salvage Dragoon_Caves - but puked up sorc_villa like a bulimic whore).

Therefore you only need to specify the brightness for texture lighting with VHLT, rather than the colour and brightness like you would when using ZHLT or SHLT
Yes... And SHLT has that feature too... But in VHLT, the brightness is several orders of magnitude different, and the color of the texture "mixes" with the color you set, even if you don't use straight 255's, and thus makes it very difficult to get the color you originally intended... This is a real problem, if it's a mostly dim texture, with a light in it - like a glowing eye or gem mounted in dark metal or stone, all in one texture... VHLT looks as those as a dim textures... Thus, when converting The_Wall2 to VHLT - you know all those little Doom demon face plates in the support columns? Suffice to say there was a LOT of trial and error involved in getting them to work right, and the brightness went from 150, to 60000 before it got anywhere near to the original setup (seemed to be just bjorked for the longest time) in addition to all the color combinations I had to go throw at it get what it thought of as "dark brown" anywhere near to the original.

Similarly, and I'm not sure why Vluzacn hasn't fixed this, as he said he would... You cannot use VHLT to cast light through some types of entity brushes - most alarmingly water (or any brush with a !fluid texture) . You have to use Skip brushes to make VHLT ignore the water brush for glowing water to cast light - otherwise, somehow, the water brush blocks its own light... which became quite a task in Deralia Sewers - being lit almost entirely by glowing water.

Edit: Incase you're interested: vluzacn uploaded a neat testmap demonstrating his toggle-able shadow effect.
It's the translucency that kinda ran us into a func_wall, sort-to-speak, rather, before we said eff it (dun think Crow ever worked it out either). Both The Wall and Deralia Sewers use shadow toggles successfully.

If you're interested in some of the issues we came across, there's some ancient threads where we bashed heads together working around them:
viewtopic.php?f=8&t=6643&hilit=vhlt&start=165
Also on the svencoop forums: http://forums.svencoop.com/showthread.p ... 59&page=10
 

MS:C community

Old Skool Apostle
Alpha Tester
Jul 7, 2011
485
95
Thothie said:
Similarly, and I'm not sure why Vluzacn hasn't fixed this, as he said he would... You cannot use VHLT to cast light through some types of entity brushes - most alarmingly water (or any brush with a !fluid texture) . You have to use Skip brushes to make VHLT ignore the water brush for glowing water to cast light - otherwise, somehow, the water brush blocks its own light... which became quite a task in Deralia Sewers - being lit almost entirely by glowing water.
I noticed deraliasewers was compiled with ZHLT v3.4 VL23. I'm fairly sure the oddity with func_water blocking its own light no longer exists in the latest version (VL30). I made a quick testmap and assigned a light_surface entity to the !dswtr texture (as used in deraliasewers). I tried different brightness values for this entity, namely 40, 80, 500, 1000, 1500, 2000 and 3000. The value 1500 seems to work best. The wandering skeleton was lit up properly in each case, as was the player model.

There is a little spotlight in the map (like in deraliasewers) but its brightness (which comes from a light_environment entity and of course a sky brush) is only 50 and not 1500 like in deraliasewers, so the spotlight doesn't really influence the map's lighting a lot.

Anyway, the reason you'd need brightness values this big is because the VHLT RAD knows what colour of light a texture should emit -- ie. the (average) colour of the texture. The !dswtr texture is very dark so that's why you need a very high value for the brightness to have it light up properly. There's an option in light_surface to have a texture emit a custom colour (as if the texture actually were in that custom colour) but I haven't tried it yet. You did mention this though:
Thothie said:
and the color of the texture "mixes" with the color you set
My suggestion: Don't use custom colours (only set brightness). I can understand this must be frustrating for the demon face plate things you mentioned... I haven't looked for a workaround yet.

As for func_detail... I just hope splitting up some of my bigger func_detail into several smaller ones will avoid them becoming too complex.

I've yet to try out the translucency feature but as far as I know, brushes which you wish to be translucent must be no more than two units thick and I *think* they can't be brush-based entities either, or at least not brush-based entities with special rendering properties. You just use an info_translucent to specify translucency values for textures, like you'd use an info_texlights to specify texture lighting values (actually light_surface has more options than info_texlights but that's not the point here).

Also, the main reason I use VHLT is this:
vluzacn in the ZHLT v3.4 VL20 changelog said:
Fixed a bug that causes "Warning: Leaf portal saw into leaf" and wrong vis.
And I can tell you, this is where VHLT most definitely delivers!
 

MS:C community

Old Skool Apostle
Alpha Tester
Jul 7, 2011
485
95
MS:C community said:
My suggestion: Don't use custom colours (only set brightness). I can understand this must be frustrating for the demon face plate things you mentioned... I haven't looked for a workaround yet.
Well, cancel that. This is actually much easier than I expected it to be.

I just assigned a light_surface entity to plaq_53 (the demon face plate texture) and set the brightness to 500, but this time I changed the "Color" Attribute to 235 159 39 (this is the second brightest shade of orange I could find in the texture). And it looks great to me.

I also tested translucency and it works as intended. The test area is a pitch black room (to get rid of light reflections) with a metal frame (func_detail) and eleven orange panels (all grouped into one func_detail). Every orange panel has a differently named copy of the same texture (transl00 through transl10). This allows me to test eleven different translucency values. After testing some values, these are the most interesting results I got: front of setup, back of setup

The orange "crystal strip" is illuminated due to, you guessed it, a light_surface (brightness 750, no custom colour assigned). I also tested with a blue-ish crystal strip (same light_surface values). Results: front of alternate setup, back of alternate setup

I also found these two pictures on the internet (added the red text myself though, after reading the accompanying description).
So it works fine, but I must admit it's not as spectacular as I thought it would be.

Edit: Turning both func_detail into worldbrushes yielded the same effect! That's pretty impressive.
 

Thothie

Administrator
Staff member
Administrator
Moderator
Lead MSC Developer
MSC Developer
Apr 8, 2005
16,225
272
lost
MS:C community said:
[Vluzacn fixed water glow]
Well that's promising, at least - dunno if he fixed it for other types of brushes as well. For awhile, he considered it a bug that the previous *HLT's allowed it, which was part of the problem where VHLT's development was going, vs. where SHLT's was - that things should be done a whole new way, as opposed to just using the older, more intuitively predictable system, and optimizing it while adding new features, but retaining compatibility. This philosophy extends to many other areas, making it very difficult to convert older maps to VHLT builds, without a great deal of work.

Sadly, SHLT is no longer under development... But it's also true, no new rendering features have been added to the engine itself, so there's only so much a new compiler can do for you.

MS:C community said:
Anyway, the reason you'd need brightness values this big is because the VHLT RAD knows what colour of light a texture should emit
The core problem is that it doesn't "know" - it guesses, and it often guesses badly. If you have an eye or a crystal in a mount, stones with glowing specs in them, or even, as many default Half-Life ~lights do, a light with a grill over it, its guesses will be disastrously wrong, and very difficult to correct (and pretty much impossible to correct accurately).

To make matters worse, you can't compensate for it by placing a point light, because it makes these same guesses with bounce lights. Now classic HLRAD and SHLT do this to a lesser degree - but in addition to the fact that the bounce light contamination was only ~10%, they also used the classical visible light JPEG spectrum into account ie. favoring the same colors the human eye does. For some reason, VHLT didn't port this, and just goes with straight, averaged, RGB. This often leads to very odd looking color distortions, as described yonder, and the fact that it's radically changing the bounce light color with little regard to the original light source, will often lead to very pixely, kinda 4-bit looking color distortions where more than one light bounces, with very unpredictable colors. Lights mounted in corners with dark walls are to be avoided in VHLT for this reason.

Sometimes you can use -texreflectscale to help compensate, but that causes other issues.

I've also not tried it, but I suspect if the light texture is a {solid type, it might pick up the blue and average the light accordingly. Isn't an issue with bounced light though (as it passes through such textures).

There really needs to be a flag to disable or mitigate this behavior - and there may be one, as it's been awhile since I discussed it with Vluzacn, but meantime, I've just taking to avoiding VHLT on certain map types.

MS:C community said:
I've yet to try out the translucency feature but as far as I know, brushes which you wish to be translucent must be no more than two units thick and I *think* they can't be brush-based entities either, or at least not brush-based entities with special rendering properties. You just use an info_translucent to specify translucency values for textures, like you'd use an info_texlights to specify texture lighting values (actually light_surface has more options than info_texlights but that's not the point here).
Yeah, fiddled with it a bit for some effects in the swamp area of Deralia Sewers, and for the glass pane on the ceiling on Kfortress (which, in the end, just stuck with the SHLT build for other reasons) - but I didn't hack at it very long. Crow was trying to do some stuff with it on his maps to no avail. Never seen it successfully used to any great effect.

vluzacn in the ZHLT v3.4 VL20 changelog said:
Fixed a bug that causes "Warning: Leaf portal saw into leaf" and wrong vis.
It doesn't eliminate the error, it just makes it less likely to pick it up falsely - similar to how using -full on HLVIS reduces them, only further. But there's a bunch of new errors that it does pick up falsely, such as "Ambiguous leafnode content"... And it took me the longest time to convince Vluzacn that this was the case. He kept blaming it on bad vertex manipulation - so I sent him the map source... Which he apparently ignored, so I sent him the snippet of the map code for the brush VHLT was saying was in error, proving that every vertex was perfectly aligned on a simple cube to the last decimal place... It was only then he started looking into ways to mitigate this spew of false errors that VHLT would generate for most maps. He adopted the FixBrinks tool to help, and the newer versions generate far fewer of these false leafnode errors, but there's still far more than there should be.

Now, I'm not saying not to use VHLT - I'm just saying it's not very good for certain types of map builds, and very difficult to adapt to an existing map that already has extensive lighting. When starting from scratch, you may as well go with VHLT. It's easy enough to fall back to more conventional compilers, if it causes you problems - simply converting all your func_details to walls/illusionaries as appropriate. You can sing of its triumphs while it works, but if it breaks your heart later on, don't say you weren't warned. ;)
 
Top