Working Example of a Colorpicker

From TDN

This Follows Pure research Example For changing Speculatiry on the fly , and is a slightly more usefull use for essentially the same process in a different subsystem: To reitterate a few points:
Just as with datablocks, there's times you'll want to change stock behavior from per type-of-asset, to per-asset, and that's where this
comes in.

Now then, for clarity, we'll be re-starting our code-journey at the custommaterial end, instead of the material end this time, and work our way back from there with the following setup:

Engine:
CustomMaterial.cc/cpp

void CustomMaterial::setShaderConstants( const SceneGraphData &sgData,
                                         U32 stageNum )
{
...
   if (mOverrideColor) GFX->setPixelShaderConstF( PC_USERDEF1, (float*)&sgData.mObjectColor, 1 );
);
...
}

Note the use of the PC_USERDEF1 register, instead of the old embeded one. There are 3 of these set aside stock in the shdrConsts.h file

Since we are not using a stock material anymore, we need to expand the script to the following: Script:


<pre>
new CustomMaterial( buggy_body001a )
{
   texture[0] = "./microt"; //refferences register(S0) in the hlsl file
   texture[1] = "./microtb"; //refferences register(S1) in the hlsl file

   shader = ColorPickerShader;
   version = 1.1;
   OverrideColor = true;
  mapto = "microt";
};

Refferences the custom Shader Hook.

new ShaderData( ColorPickerShader )
{
   DXVertexShaderFile   = "client/shaders/ColorPickerV.hlsl";
   DXPixelShaderFile    = "client/shaders/ColorPickerP.hlsl";
   pixVersion = 1.1;
};

Defines the custom Shader Hook.

And for the last exposed bit before we delve back into the engine to finish on up, the new Shader set (yoinked from diffBump for the interested)

ColorPickerP.hlsl:

#define IN_HLSL
#include "shdrConsts.h"
//-----------------------------------------------------------------------------
// Structures                                                                  
//-----------------------------------------------------------------------------
struct ConnectData
{
   float2 texCoord        : TEXCOORD0;
   float2 bumpCoord       : TEXCOORD1;
   float3 lightVec        : TEXCOORD2;
};


struct Fragout
{
   float4 col : COLOR0;
};

//-----------------------------------------------------------------------------
// Main                                                                        
//-----------------------------------------------------------------------------
Fragout main( ConnectData IN,
              uniform sampler2D diffuseMap      : register(S0),
              uniform sampler2D bumpMap         : register(S1),
              uniform float4    ambient         : register(PC_DIFF_COLOR),
              uniform float4    colorpicker     : register(PC_USERDEF1)
)
{
   Fragout OUT;
   float4 diffuseColor = tex2D(diffuseMap, IN.texCoord);
   OUT.col = diffuseColor;
   float4 bumpNormal = tex2D(bumpMap, IN.bumpCoord);

   IN.lightVec = IN.lightVec * 2.0 - 1.0;
   float4 bumpDot = saturate( dot(bumpNormal.xyz * 2.0 - 1.0, IN.lightVec.xyz) );
   OUT.col *= bumpDot + ambient;

  //check the alphachannel of the diffuse color (register(S0) above / texture[0] = in the material def and if it's greater than 90%, blend it
  //(this is so you have both that, and the normal multiplication used
   if (diffuseColor.a > 0.9) OUT.col *= colorpicker; 


   return OUT;
}

Note the usage of the uniform float4 colorpicker  : register(PC_USERDEF1) line. This is what's telling the shader to use the register were feeding it from the engine for the data for the colorpicker variable in it's calculations.

ColorPickerV.hlsl:

//-----------------------------------------------------------------------------
// Structures                                                                  
//-----------------------------------------------------------------------------
struct VertData
{
   float4 texCoord        : TEXCOORD0;
   float2 lmCoord         : TEXCOORD1;
   float3 T               : TEXCOORD2;
   float3 B               : TEXCOORD3;
   float3 N               : TEXCOORD4;
   float3 normal          : NORMAL;
   float4 position        : POSITION;
};


struct ConnectData
{
   float4 hpos            : POSITION;
   float2 outTexCoord     : TEXCOORD0;
   float2 bumpCoord       : TEXCOORD1;
   float3 outLightVec     : TEXCOORD2;
};


//-----------------------------------------------------------------------------
// Main                                                                        
//-----------------------------------------------------------------------------
ConnectData main( VertData IN,
                  uniform float4x4 modelview       : register(C0),
                  uniform float4x4 texMat          : register(C4),
                  uniform float3   inLightVec      : register(C24),
                  uniform float3   eyePos          : register(C20),
                  uniform float4x4 objTrans        : register(C12)
)
{
   ConnectData OUT;

   OUT.hpos = mul(modelview, IN.position);
   OUT.outTexCoord = mul(texMat, IN.texCoord);
   OUT.bumpCoord = OUT.outTexCoord;

   float3x3 objToTangentSpace;
   objToTangentSpace[0] = IN.T;
   objToTangentSpace[1] = IN.B;
   objToTangentSpace[2] = IN.N;

   OUT.outLightVec.xyz = -inLightVec;
   OUT.outLightVec.xyz = mul(objToTangentSpace, OUT.outLightVec);
   OUT.outLightVec = OUT.outLightVec / 2.0 + 0.5;

   return OUT;
}

now sgData get's it's info from struct RenderInst, so we parse in a ColorF mObjectColor; into renderInstMgr.h

In order to have a point of reffernece for that RenderInst, we go into
tsMesh.cc/cpp

void TSMesh::render(S32 frame, S32 matFrame, TSMaterialList * materials)
{
   if( vertsPerFrame <= 0 ) return;

   RenderInst *coreRI = gRenderInstManager.allocInst();
   coreRI->type = RenderInstManager::RIT_Mesh;
   coreRI->calcSortPoint( smObject, smSceneState->getCameraPosition() );

   if (smObject) coreRI->mObjectColor = smObject->mObjectColor; //goes fatal if not assigned...
...
}

smObject refferences a sceneObject so we need to add yet another mObjectColor to that definition,
as well as add in:

void SceneObject::initPersistFields()
{
...
   addField("ObjectColor", TypeColorF, Offset(mObjectColor, SceneObject));
...
}

so that we can dynamicly update our values.

Wich brings us to the highly unoptimised quick-hack finish of the lil example:

U32 GameBase::packUpdate(NetConnection *, U32 mask, BitStream *stream)
{
...
   stream->writeFloat(mObjectColor.red, 7);
   stream->writeFloat(mObjectColor.green, 7);
   stream->writeFloat(mObjectColor.blue, 7);
   stream->writeFloat(mObjectColor.alpha, 7);
...
}

void GameBase::unpackUpdate(NetConnection *con, BitStream *stream)
{
...
   mObjectColor.red = stream->readFloat(7);
   mObjectColor.green = stream->readFloat(7);
   mObjectColor.blue = stream->readFloat(7);
   mObjectColor.alpha = stream->readFloat(7);
...
}