stgatilov Posted November 19, 2024 Report Posted November 19, 2024 Every material consists of global keywords and stages. Stages are classified into ambient stages and interaction stages (diffuse, specular, bump, parallax). Interaction stages are partitioned into interaction groups. Each group is rendered as single draw call with interaction shader, its stages supply various settings (e.g. diffuse texture is taken from diffuse stage, specular texture from specular stage, etc.). Ambient stages are all extracted into separate list and rendered in their respective order. But this always happens after all interactions are rendered. In 2.12 and earlier, interaction groups were detected dynamically during rendering. There was some preprocessing code which was static (sorting stages, adding implicit bumpmap) and some additional detection in rendering code which was dynamic because it checked conditions. In the recent TDM version (hopefully in 2.13 too), the old approach is replaced with two new ways to detect interaction groups: implicit and explicit. Both ways are fully static and implemented straight during material parsing. Implicit detection. We go through stages in their definition order and add each new stage into the last interaction group. If the last interaction group already contains a stage of same type, then we finish that group and start a new one with the current stage. For instance, if we see interaction stages DSBBDDSBSD (abbreviated by first letters), it will get split into [DSB][BD][DSB][SD] groups. This should work fine for simple materials with one stage of each kind. It should also work fine for vertex-blended materials because they should have each group starting from bump stage (and hopefully they don't have duplicate diffuse stages). Explicit detection. There is new material keyword: interactionSeparator. If at least one such keyword is present in material, then explicit detection is used. Each interaction group consists of all the stages located between two consecutive interaction separators. The stages before first separator are put into a group too (if any), same applies to the stages after the last separator. Note that disabled stages are ignored during runtime, so it is possible to have several stages of the same kind in an interaction group, as long as at most one of them is enabled at any time. Explicit approach can be used for non-trivial cases, especially when stages are enabled by condition (like e.g. animated textures). Quote
stgatilov Posted November 19, 2024 Author Report Posted November 19, 2024 Let's consider the case from: Here is how it looks with explicit interaction groups: textures/darkmod/map_specific/grass4_dirt_01_blend { qer_editorimage textures/darkmod/nature/grass/grass4_ed surftype15 description "grass" // interactionSeparator --- the one at start is optional { blend diffusemap map textures/darkmod/nature/grass/grass4 vertexColor } { blend bumpmap map textures/darkmod/nature/grass/grass4_local vertexColor } interactionSeparator { blend diffusemap map textures/darkmod/nature/dirt/dry_earth_muddy inverseVertexColor } { blend bumpmap map textures/darkmod/nature/dirt/dry_earth_muddy_local inverseVertexColor } // interactionSeparator --- the one at end is optional } The separator in the middle is important here, since it splits the stages into two groups. Note that this specific case should work fine now even without any separators (i.e. with implicit detection). The implicit partitioning will break first group when it detects second diffuse stage. 1 Quote
stgatilov Posted November 19, 2024 Author Report Posted November 19, 2024 Another example from: Should now work fine with explicit groups: Spoiler water_animatedA { ... interactionSeparator { blend specularmap map _white rgb 0.15 } { if ( (time * 30) % 59 == 0 ) vertexProgram heatHazeWithAlphaDepth.vfp vertexParm 0 time * 0 , time * 0 // scroll vertexParm 1 (( parm6 + 2.5) - (( parm6 || 0) * 2.5)) //magnitude fragmentMap 0 _currentRender fragmentMap 1 textures/water_source/water_animatedA/0001.png fragmentMap 2 textures/water_source/vp_water fragmentMap 3 _currentDepth } ... { if ( (time * 30) % 59 == 59 ) vertexProgram heatHazeWithAlphaDepth.vfp vertexParm 0 time * 0 , time * 0 // scroll vertexParm 1 (( parm6 + 2.5) - (( parm6 || 0) * 2.5)) //magnitude fragmentMap 0 _currentRender fragmentMap 1 textures/water_source/water_animatedA/0060.png fragmentMap 2 textures/water_source/vp_water fragmentMap 3 _currentDepth } { blend diffusemap map textures/water_source/water_cyan colored RGB 1 } { if ( (time * 30) % 59 == 0 ) blend bumpmap map textures/water_source/water_animatedA/0001.png } ... { if ( (time * 30) % 59 == 59 ) blend bumpmap map textures/water_source/water_animatedA/0060.png } interactionSeparator { forceHighQuality blend gl_dst_alpha, gl_one CubeMap env/gen1 texgen reflect RGB .15 } } The sequence of stages here is SDBBB...BBB (ignoring ambient stages). Implicit groups detection will see many bump stages and partition as [SDB][B][B][B]...[B][B][B]. While interaction separators at material start/end are optional in terms of splitting the groups, they do switch from implicit detection to explicit detection --- that's all we need. Since separators are only at start/end, all the stages will get into single group with many bump stages. As long as conditions are correct, we will get exactly one bump stage active at each moment. 1 Quote
stgatilov Posted January 6 Author Report Posted January 6 Moved this topic from development forums, since it covers a potentially important behavior change in 2.13. Luckily, its important is countered by the rarity of such complicated materials. I hope that this change has not broken existing materials. And even if it had broken any, we will be able to fix them manually... Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.