Game Development Community

Turn shader on/off at runtime

by Bryan Stroebel · in Torque Game Engine Advanced · 05/19/2006 (2:41 pm) · 2 replies

If you want to toggle shaders on/off at runtime while in the game here's what you need to do:

First we need to modify material.h . Add this right after DECLARE_CONOBJECT(Material);
bool shaderON;//shader on/off
   bool tsSwitch;//toggle shader switch. needed for normal materials

Next we need to modify material.cpp. add this in the Material::Material() constructor function, at the end:
//BCS - Set defaults for toggleshader() and ChangeMaterial()     
	shaderON = true;  
	tsSwitch = false;

next add this to the end of material.cpp:
//BCS - toggle shader off/on
ConsoleMethod(Material,toggleshader,void,2,2,"toggleshader()")
{
	object->tsSwitch = true;
	object->shaderON = !object->shaderON;
}

next we need to modify customMaterial.cpp. find CustomMaterial::setupSubPass( SceneGraphData &sgData ) and replace this:
if( mShaderData && mShaderData->shader)
with this:
if( mShaderData && mShaderData->shader && shaderON)

Now open matInstance.h and put the following right under void construct();
//BCS - toggle shader off/on
   void disableShaderFeatures( U32 stageNum, U32 maxstages, GFXShaderFeatureData &fd );
   void enableStageFeatures(U32 stageNum, U32 maxstages);
   Vector<bool> vMatboolSet;	//holds all original material bool settings

now open matInstance.cpp and find the MatInstance::processMaterial() function. and change this:
for( U32 i=0; i<mMaxStages; i++ )
   {
      GFXShaderFeatureData fd;
      determineFeatures( i, fd );

      if( fd.codify() )
      {
         createPasses( fd, i );
      }
   }
to this:
for( U32 i=0; i<mMaxStages; i++ )
   {
      GFXShaderFeatureData fd;
		//BCS - added support for toggle shader on/off
		if(mMaterial->shaderON)
		{	
			if(mMaterial->tsSwitch)
			{
				enableStageFeatures(i,mMaxStages);
			}
			determineFeatures( i, fd );
		}
		else
		{
			disableShaderFeatures( i, mMaxStages, fd );
		}
		//********************************************
      if( fd.codify() )
      {
         createPasses( fd, i );
      }
   }
	if(mMaterial->shaderON){vMatboolSet.clear();}

next find MatInstance::setupPass( SceneGraphData &sgData ) and add the following right after
CustomMaterial *custMat = dynamic_cast(mMaterial);
//BCS - reinit material if toggleing shader ********************
	if(mMaterial->tsSwitch)
	{
		reInit();
		mMaterial->tsSwitch = false;
	}

#1
05/19/2006 (2:42 pm)
Continuing....

now add these two functions to the end of the matInstance.cpp file:
//BCS - disables all shader features for a material when toggleshader()
//turns the shader off. shaderON = false
void MatInstance::disableShaderFeatures( U32 stageNum, U32 maxstages, GFXShaderFeatureData &fd )
{
	//before we disable all the shader features and values we need
	//to place the original values in a vector incase we want to turn
	//the shader back on.
	if(stageNum == 0) 
	{
		// on the first stage save the non-stage data to the vector
		vMatboolSet.push_back(mMaterial->castsShadow);
		vMatboolSet.push_back(mMaterial->breakable);
		vMatboolSet.push_back(mMaterial->doubleSided);
		vMatboolSet.push_back(mMaterial->dynamicCubemap);
		vMatboolSet.push_back(mMaterial->translucent);
		vMatboolSet.push_back(mMaterial->translucentZWrite);
		vMatboolSet.push_back(mMaterial->planarReflection);
		// and disable the non-stage data on the material.
		mMaterial->castsShadow = false;
		mMaterial->breakable = false;
		mMaterial->doubleSided = false;
		mMaterial->dynamicCubemap = false;
		mMaterial->translucent = false;
		mMaterial->translucentZWrite = false;
		mMaterial->planarReflection = false;		
	}
	//for each stage save the stage data to the vector	
	vMatboolSet.push_back(mMaterial->pixelSpecular[stageNum]);
	vMatboolSet.push_back(mMaterial->vertexSpecular[stageNum]);
	vMatboolSet.push_back(mMaterial->glow[stageNum]);
	vMatboolSet.push_back(mMaterial->emissive[stageNum]);

	//disable all shader features per pass except for the base texture
	// we still want the base texture to show.
	for( U32 i=0; i<GFXShaderFeatureData::NumFeatures; i++ )
	{
		if( i == GFXShaderFeatureData::BaseTex)
		{
			fd.features[i] = true;
		}
		else
		{
			fd.features[i] = false;
		}
	}
	//disable stage data for each stage
	mMaterial->pixelSpecular[stageNum] = false;
	mMaterial->vertexSpecular[stageNum] = false;
	mMaterial->glow[stageNum] = false;
	mMaterial->emissive[stageNum] = false;
}
//BCS - enables the original shader boolean values when 
//toggleshader() turns the shader back on. shaderON = true
void MatInstance::enableStageFeatures(U32 stageNum,U32 maxstages)
{
	U32 i = stageNum * 4;
	//on the first stage set the materials non-stage values
	//from the vector.
	if(stageNum == 0) 
	{
		mMaterial->castsShadow = vMatboolSet[stageNum];
		mMaterial->breakable = vMatboolSet[stageNum+1];
		mMaterial->doubleSided = vMatboolSet[stageNum+2];
		mMaterial->dynamicCubemap = vMatboolSet[stageNum+3];
		mMaterial->translucent = vMatboolSet[stageNum+4];
		mMaterial->translucentZWrite = vMatboolSet[stageNum+5];
		mMaterial->planarReflection = vMatboolSet[stageNum+6];
	}
	//set the original stage values, for each stage, from the vector
	mMaterial->pixelSpecular[stageNum] = vMatboolSet[i+7];
	mMaterial->vertexSpecular[stageNum] = vMatboolSet[i+8];
	mMaterial->glow[stageNum] = vMatboolSet[i+9];
	mMaterial->emissive[stageNum] = vMatboolSet[i+10];
}

What happens is, when we turn the shader off we save the origianl shader values in a vector so we can reassign them when we turn the shader back on.

Now compile and run TSE. Use the following to toggle the shader off and on:
material.toggleshader();

for example: to toggle the orcskin shader off and on you would type:
orckskin.toggleshader();
orckskin.toggleshader();

Right now this turns all shading features off but you could look at the disableShaderFeatures function and only turn off what you want.

Hope this helps.
#2
05/19/2006 (8:18 pm)
This is very useful.