Extending the Torque3D Material System

by Pat Wilson

The material and shader generation system in Torque3D is a powerful, and flexible tool for providing artists with a set of features they can use to bring their creations to life. A system, such as this, is of limited use to a team if it can not be modified and extended easily. This document will describe how the Torque3D material code constructs HLSL and GLSL shaders, explain how to add additional material shader features, and provide the programmer with a simple, re-usable feature template.

This document assumes familiarity with shading languages, and 3D API concepts.

Adding a Simple Shader Feature

You can get a quick overview at how a material property goes from script to shader code on this page. Now lets start with shader code, and work back to script by implementing a simple shader feature which multiplies the output of the material it is part of by a constant value.

First declare the feature type in ‘Engine/source/materials/materialFeatureTypes.h’

Next, the corresponding implementation in ‘Engine/source/materials/materialFeatureTypes.cpp’

This specifies that the FeatureType, MFT_ConstantMult, should execute in the MaterialFeatureGroup MFG_PostProcess, the last group to execute (see MaterialFeatureGroup struct) and it should run at order 999.0f in that group, effectively making it the last thing to process the fragment before it is returned by the pixel shader.

Now that we have an entry point into the material system which will allow us to run code where we want: right before output, we need to write that shader code. Create a new file, ‘Engine/source/shaderGen/constantMult.cpp’. Note that this document demonstrates adding a feature differently than is demonstrated by other features in the engine. This is done, not only to make the code more concise, but to demonstrate an additional, more encapsulated way to extend the functionality in the ShaderGen system.

We are going to skip the ShaderFeatureHLSL and ShaderFeatureGLSL classes and implement a general feature for both shading languages (it is recommended that you look over the existing shader features as reference material).

This shader feature can be used for both GLSL and HLSL, since its function is so simple. The more complex shader features which exist in Torque3D use different implementations for OpenGL and Direct3D, which are based off of ShaderFeatureGLSL and ShaderFeatureHLSL. This method of implementing shader features is useful and you should be familiar with it even though this document does not discuss it.

The ConstantMultFeature::processPix() method uses a variable, called ‘multConst’. This is a value that we want to expose to the Material definition in script. We will get to that later. It takes this value and multiplies it by the output color.

Exposing Data to the Shader Feature

The next step in this process is to assign data the shader constant that the above feature uses, ‘multConst’. The central location for assignment of values to shader constants is located in ProcessedShaderMaterial::_setShaderConstants(). Add this code in that method.

This checks to see if a shader constant handle called ‘mMultConstSC’ is valid. A handle will only be valid if the shader code uses that shader constant. If that handle is valid, it will assign the value specifed in the Material to the shader constant. Next, add that constant handle member to the ShaderConstHandles class in ‘Engine/source/materials/processedShaderMaterial.h’.

Next, add the code which will assign a handle to that member in ShaderConstHandles::init().

Note that the name must begin with the ‘$’ character, and that the name assigned in the previous code must match this name.

(From above code block, provided for reference)

Now this handle will get assigned a value if the shader which is being used requests the ‘multConst’ constant. This will be true if the ShaderFeature that we assigned to MFT_ConstantMult gets added during shader generation. This will only happen if the MaterialFeatureData structure specifies that MFT_ConstantMult is enabled by the material, so let’s take a look at ProcessedShaderMaterial::_determineFeatures(), and add the following code.

This will enable the MFT_ConstantMult feature on the generated shader if the value assigned to the constant has an Alpha value greater than -1.0. If it does, the shader code which we wrote in the ConstantMultFeature will get added to the generated shader. This will cause the //‘mMultConstSC’ handle that we added to be valid for Material definitions which specify a value for ‘multConst’. That member still does not exist yet, so let’s go to the Material class, and add that member variable.

And also initialization in the Material constructor.

This initializes the value of Alpha to -1.0, which will cause the feature to be disabled by default. Next we need to allow this value to be adjusted from the script definition of a Material, so expose it as a field in Material::initPersistFields().

Wrapping it all up

Robot turned red using the new shader feature

Robot turned red using the new shader feature

We have now worked backwards, from shader code, all the way back to script. To test this new material feature, adjust one of the Material definitions in your game project to look something like this:

singleton Material(skin)
{
   mapTo = "skin";
   baseTex[0] = "skin";
   bumpTex[0] = "skin_n";
   pixelSpecular[0] = true;
   specular[0] = "0.6 0.7 0.6 0.7";
   specularPower[0] = 32.0;
 
   // Add this to your material
   multConstant = "1.0 0.0 0.0 1.0"; 
};

You should now have a red version of the existing material.

This document should provide you with a good overview of how to add a ShaderFeature to Torque3D. The concepts introduced here form the foundation of the lighting and material system for Torque. A topic as diverse as material functionality can’t possibly be covered in one document, or with example code alone. The material features included in Torque3D provide a wide range of functionality examples for creating more complicated features.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License