vertex position node

Cross api support for vertex positions
matrixmultiplyop added for multiplying matrix with other types
automatically expands or shortens vec types to suit the matrix
Added identity to the matrix initialization op.
This commit is contained in:
marauder2k7 2025-11-29 08:15:55 +00:00
parent cff7a8060d
commit a92ea1ffab
5 changed files with 418 additions and 56 deletions

View file

@ -15,11 +15,13 @@
#include "materials/materialFeatureTypes.h"
ImplementFeatureType(SNF_VertexPosition, U32(-1), -1, false);
ImplementFeatureType(SNF_DefaultTexCoord, U32(-1), -1, false);
ImplementFeatureType(SNF_TextureFeature, U32(-1), -1, false);
ImplementFeatureType(SNF_NormalMapFeature, U32(-1), -1, false);
ImplementEnumType(ShaderNodeFeature_enum, "Shader node features. Each of thes relates to a specific node for generating a shader.\n\n")
{ ShaderNodeFeature_enum::eSNF_VertexPosition, "SNF_VertexPosition", "Setup vertex position." },
{ ShaderNodeFeature_enum::eSNF_DefaultTexCoord, "SNF_DefaultTexCoord", "Setup the default texcoord." },
{ ShaderNodeFeature_enum::eSNF_TextureFeature, "SNF_TextureFeature", "Sample a Texture - Params: (string,string,GFXSamplerStateData,bool)." },
{ ShaderNodeFeature_enum::eSNF_NormalMapFeature,"SNF_NormalMapFeature", "Convert a texture to a normalmap - Params: (string,float,bool,bool)." },
@ -29,6 +31,7 @@ namespace
{
void register_node_features(GFXAdapterType type)
{
FEATUREMGR->registerFeature(SNF_VertexPosition, new NodeVertexPositionFeature);
FEATUREMGR->registerFeature(SNF_DefaultTexCoord, new DefaultTexcoordFeature);
FEATUREMGR->registerFeature(SNF_TextureFeature, new TextureFeature, TextureFeature::createFunction);
FEATUREMGR->registerFeature(SNF_NormalMapFeature, new NormalMapFeature, NormalMapFeature::createFunction);
@ -51,6 +54,125 @@ MODULE_INIT
MODULE_END;
Var* ShaderFeatureNode::getObjTrans(Vector<ShaderComponent*>& componentList,
bool useInstancing,
MultiLine* meta)
{
Var* objTrans = (Var*)LangElement::find("objTrans");
if (objTrans)
return objTrans;
if (useInstancing)
{
ShaderConnector* vertStruct = dynamic_cast<ShaderConnector*>(componentList[C_VERT_STRUCT]);
Var* instObjTrans = vertStruct->getElement(RT_TEXCOORD, 4, 4);
instObjTrans->setStructName("IN");
instObjTrans->setName("inst_objectTrans");
mInstancingFormat->addElement("objTrans", GFXDeclType_Float4, instObjTrans->constNum + 0);
mInstancingFormat->addElement("objTrans", GFXDeclType_Float4, instObjTrans->constNum + 1);
mInstancingFormat->addElement("objTrans", GFXDeclType_Float4, instObjTrans->constNum + 2);
mInstancingFormat->addElement("objTrans", GFXDeclType_Float4, instObjTrans->constNum + 3);
objTrans = new Var;
objTrans->setType(GFXSCT_Float4x4);
objTrans->setName("objTrans");
Vector<LangElement*> matrixVars;
matrixVars.push_back(instObjTrans);
meta->addStatement(new GenOp(" @ = @;", new DecOp(objTrans), new MatrixInitializeOp(objTrans, matrixVars)));
}
else
{
objTrans = new Var;
objTrans->setType(GFXSCT_Float4x4);
objTrans->setName("objTrans");
objTrans->uniform = true;
objTrans->constSortPos = cspPrimitive;
}
return objTrans;
}
Var* ShaderFeatureNode::getModelView( Vector<ShaderComponent*>& componentList,
bool useInstancing,
MultiLine* meta)
{
Var* modelview = (Var*)LangElement::find("modelview");
if (modelview)
return modelview;
if (useInstancing)
{
Var* objTrans = getObjTrans(componentList, useInstancing, meta);
Var* viewProj = (Var*)LangElement::find("viewProj");
if (!viewProj)
{
viewProj = new Var;
viewProj->setType(GFXSCT_Float4x4);
viewProj->setName("viewProj");
viewProj->uniform = true;
viewProj->constSortPos = cspPass;
}
modelview = new Var;
modelview->setType(GFXSCT_Float4x4);
modelview->setName("modelview");
meta->addStatement(new GenOp(" @ = @; // Instancing!\r\n", new DecOp(modelview), new MatrixMultiplyOp(viewProj, objTrans)));
}
else
{
modelview = new Var;
modelview->setType(GFXSCT_Float4x4);
modelview->setName("modelview");
modelview->uniform = true;
modelview->constSortPos = cspPrimitive;
}
return modelview;
}
Var* ShaderFeatureNode::getWorldView( Vector<ShaderComponent*>& componentList,
bool useInstancing,
MultiLine* meta)
{
Var* worldView = (Var*)LangElement::find("worldViewOnly");
if (worldView)
return worldView;
if (useInstancing)
{
Var* objTrans = getObjTrans(componentList, useInstancing, meta);
Var* worldToCamera = (Var*)LangElement::find("worldToCamera");
if (!worldToCamera)
{
worldToCamera = new Var;
worldToCamera->setType(GFXSCT_Float4x4);
worldToCamera->setName("worldToCamera");
worldToCamera->uniform = true;
worldToCamera->constSortPos = cspPass;
}
worldView = new Var;
worldView->setType(GFXSCT_Float4x4);
worldView->setName("worldViewOnly");
meta->addStatement(new GenOp(" @ = @; // Instancing!\r\n", new DecOp(worldView), new MatrixMultiplyOp(worldToCamera, objTrans) ));
}
else
{
worldView = new Var;
worldView->setType(GFXSCT_Float4x4);
worldView->setName("worldViewOnly");
worldView->uniform = true;
worldView->constSortPos = cspPrimitive;
}
return worldView;
}
void ShaderFeatureNode::setupTextureSample( const String& samplerName,
GFXShaderConstType samplerType,
Vector<ShaderComponent*>& componentList,
@ -69,43 +191,7 @@ void ShaderFeatureNode::setupTextureSample( const String& samplerName,
Var* samplerVar = dynamic_cast<Var*>(LangElement::find(sampVarName));
Var* textureVar;
if (!isGL) // HLSL requires both Texture + SamplerState
{
if (!samplerVar)
{
samplerVar = new Var;
samplerVar->setType(isComparison ? "SamplerComparisonState" : "SamplerState");
samplerVar->setName(sampVarName);
samplerVar->uniform = true;
samplerVar->sampler = true;
samplerVar->constNum = Var::getTexUnitNum();
}
textureVar = dynamic_cast<Var*>(LangElement::find(texVarName));
if (!textureVar)
{
textureVar = new Var;
textureVar->setType(LangElement::samplerTypeToString(samplerType)); // Texture2D, TextureCube, etc.
textureVar->setName(texVarName);
textureVar->uniform = true;
textureVar->texture = true;
textureVar->constNum = samplerVar->constNum;
}
}
else
{
if (!samplerVar)
{
samplerVar = new Var;
samplerVar->setType(LangElement::samplerTypeToString(samplerType));
samplerVar->setName(sampVarName);
samplerVar->uniform = true;
samplerVar->sampler = true;
samplerVar->constNum = Var::getTexUnitNum();
}
}
Var* textureVar = NULL;
// The sampled color variable (e.g. "samplerName_col") should always be new but just in case
Var* sampledColor = (Var*)LangElement::find(resultVarName);
@ -123,6 +209,16 @@ void ShaderFeatureNode::setupTextureSample( const String& samplerName,
if (isGL)
{
// ---------------- GLSL Sampling ----------------
if (!samplerVar)
{
samplerVar = new Var;
samplerVar->setType(LangElement::constTypeToString(samplerType, true));
samplerVar->setName(sampVarName);
samplerVar->uniform = true;
samplerVar->sampler = true;
samplerVar->constNum = Var::getTexUnitNum();
}
if (isComparison)
{
if (useGather)
@ -149,6 +245,27 @@ void ShaderFeatureNode::setupTextureSample( const String& samplerName,
else
{
// ---------------- HLSL Sampling ----------------
if (!samplerVar)
{
samplerVar = new Var;
samplerVar->setType(isComparison ? "SamplerComparisonState" : "SamplerState");
samplerVar->setName(sampVarName);
samplerVar->uniform = true;
samplerVar->sampler = true;
samplerVar->constNum = Var::getTexUnitNum();
}
textureVar = dynamic_cast<Var*>(LangElement::find(texVarName));
if (!textureVar)
{
textureVar = new Var;
textureVar->setType(LangElement::constTypeToString(samplerType, true)); // Texture2D, TextureCube, etc.
textureVar->setName(texVarName);
textureVar->uniform = true;
textureVar->texture = true;
textureVar->constNum = samplerVar->constNum;
}
if (isComparison)
{
if (useGather)
@ -192,8 +309,7 @@ Var* ShaderFeatureNode::getOutTexCoord(const char* name, GFXShaderConstType type
// Statement allows for casting of different types which
// eliminates vector truncation problems.
String statement = String::ToString(" @ = (%s)@;\r\n", type);
meta->addStatement(new GenOp(statement, texCoord, inTex));
meta->addStatement(new GenOp(" @ = (@)@;\r\n", texCoord, new TypeOp(type), inTex));
}
@ -265,6 +381,57 @@ LangElement* ShaderFeatureNode::assignColor(LangElement* elem, Material::BlendOp
return NULL;
}
//--------------------------------------------------------
// Vertex position.
//--------------------------------------------------------
void NodeVertexPositionFeature::processVert(Vector<ShaderComponent*>& componentList, const MaterialFeatureData& fd)
{
// First check for an input position from a previous feature
// then look for the default vertex position.
Var* inPosition = (Var*)LangElement::find("inPosition");
if (!inPosition)
inPosition = (Var*)LangElement::find("position");
const bool glsl = (GFX->getAdapterType() == OpenGL);
// grab connector position
ShaderConnector* connectComp = dynamic_cast<ShaderConnector*>(componentList[C_CONNECTOR]);
Var* outPosition = connectComp->getElement( glsl ? RT_POSITION : RT_SVPOSITION);
if (glsl)
{
outPosition->setName("gl_Position");
}
else
{
outPosition->setName("hpos");
outPosition->setStructName("OUT");
}
MultiLine* meta = new MultiLine;
Var* modelview = getModelView(componentList, fd.features[MFT_UseInstancing], meta);
meta->addStatement(new GenOp(" @ = @;\r\n", outPosition, new MatrixMultiplyOp( modelview, new CastOp(inPosition, GFXSCT_Float4, "x;y;z"))));
output = meta;
}
void NodeVertexPositionFeature::processPix(Vector<ShaderComponent*>& componentList, const MaterialFeatureData& fd)
{
const bool glsl = (GFX->getAdapterType() == OpenGL);
if (!glsl)
{
// grab connector position
ShaderConnector* connectComp = dynamic_cast<ShaderConnector*>(componentList[C_CONNECTOR]);
Var* outPosition = connectComp->getElement(RT_SVPOSITION);
outPosition->setName("vpos");
outPosition->setStructName("IN");
}
}
//--------------------------------------------------------
// Setup the default texcoord
//--------------------------------------------------------
@ -352,10 +519,10 @@ void NormalMapFeature::processPix(Vector<ShaderComponent*>& componentList, const
meta->addStatement(expandNormalMap(sampledColor, tempNormDecl, tempNorm, fd));
meta->addStatement(
new GenOp(" @.xy *= float2(@, @);\r\n", tempNorm, params->flipX ? -1.0f : 1.0f, params->flipY ? -1.0f : 1.0f));
new GenOp(" @.xy *= @(@, @);\r\n", tempNorm, new TypeOp(GFXSCT_Float2), params->flipX ? -1.0f : 1.0f, params->flipY ? -1.0f : 1.0f));
meta->addStatement(new GenOp(" @.xyz = normalize( float3( @.xy * @, @.z ) );\r\n",
tempNorm, tempNorm, params->strength, tempNorm));
meta->addStatement(new GenOp(" @.xyz = normalize( @( @.xy * @, @.z ) );\r\n",
tempNorm, new TypeOp(GFXSCT_Float3), tempNorm, params->strength, tempNorm));
// write back into our known variable.
meta->addStatement(new GenOp(" @ = @;\r\n", sampledColor, tempNorm));
@ -363,3 +530,4 @@ void NormalMapFeature::processPix(Vector<ShaderComponent*>& componentList, const
output = meta;
}

View file

@ -20,6 +20,7 @@
#include "gfx/gfxAPI.h"
#endif
DeclareFeatureType(SNF_VertexPosition);
DeclareFeatureType(SNF_DefaultTexCoord);
DeclareFeatureType(SNF_TextureFeature);
DeclareFeatureType(SNF_NormalMapFeature);
@ -29,6 +30,7 @@ DeclareFeatureType(SNF_NormalMapFeature);
/// </summary>
enum ShaderNodeFeature_enum
{
eSNF_VertexPosition,
eSNF_DefaultTexCoord,
eSNF_TextureFeature,
eSNF_NormalMapFeature,
@ -39,6 +41,18 @@ DefineEnumType(ShaderNodeFeature_enum);
class ShaderFeatureNode : public ShaderFeature
{
public:
///
Var* getObjTrans( Vector<ShaderComponent*>& componentList,
bool useInstancing,
MultiLine* meta);
Var* getModelView(Vector<ShaderComponent*>& componentList,
bool useInstancing,
MultiLine* meta);
Var* getWorldView(Vector<ShaderComponent*>& componentList, bool useInstancing, MultiLine* meta);
void setupTextureSample(const String& samplerName,
GFXShaderConstType samplerType,
Vector<ShaderComponent*>& componentList,
@ -58,6 +72,19 @@ public:
LangElement* assignColor(LangElement* elem, Material::BlendOp blend, LangElement* lerpElem = NULL, ShaderFeature::OutputTarget outputTarget = ShaderFeature::DefaultTarget) override;
};
class NodeVertexPositionFeature : public ShaderFeatureNode
{
void processVert(Vector<ShaderComponent*>& componentList,
const MaterialFeatureData& fd) override;
void processPix(Vector<ShaderComponent*>& componentList,
const MaterialFeatureData& fd) override;
String getName() override
{
return "NodeVertexPositionFeature";
}
};
class DefaultTexcoordFeature : public ShaderFeatureNode
{

View file

@ -38,6 +38,73 @@ void ShaderFeature::addDependency( const ShaderDependency *dependsOn )
mDependencies.push_back( dependsOn );
}
Var* ShaderFeature::getVertTexCoord(const String& name)
{
Var* inTex = NULL;
for (U32 i = 0; i < LangElement::elementList.size(); i++)
{
if (!String::compare((char*)LangElement::elementList[i]->name, name.c_str()))
{
inTex = dynamic_cast<Var*>(LangElement::elementList[i]);
if (inTex)
{
// NOTE: This used to do this check...
//
// String::compare( (char*)inTex->structName, "IN" )
//
// ... to ensure that the var was from the input
// vertex structure, but this kept some features
// ( ie. imposter vert ) from decoding their own
// coords for other features to use.
//
// If we run into issues with collisions between
// IN vars and local vars we may need to revise.
break;
}
}
}
return inTex;
}
LangElement* ShaderFeature::expandNormalMap(LangElement* sampleNormalOp, LangElement* normalDecl, LangElement* normalVar, const MaterialFeatureData& fd)
{
MultiLine* meta = new MultiLine;
const bool hasBc3 = fd.features.hasFeature(MFT_IsBC3nm, getProcessIndex());
const bool hasBc5 = fd.features.hasFeature(MFT_IsBC5nm, getProcessIndex());
if (hasBc3 || hasBc5)
{
if (fd.features[MFT_ImposterVert])
{
// The imposter system uses object space normals and
// encodes them with the z axis in the alpha component.
meta->addStatement(new GenOp(" @ = @( normalize( @.xyw * 2.0 - 1.0 ), 0.0 ); // Obj DXTnm\r\n", normalDecl, new TypeOp(GFXSCT_Float4), sampleNormalOp));
}
else if (hasBc3)
{
// BC3 Swizzle trick
meta->addStatement(new GenOp(" @ = @( @.ag * 2.0 - 1.0, 0.0, 0.0 ); // DXTnm\r\n", normalDecl, new TypeOp(GFXSCT_Float4), sampleNormalOp));
meta->addStatement(new GenOp(" @.z = sqrt( 1.0 - dot( @.xy, @.xy ) ); // DXTnm\r\n", normalVar, normalVar, normalVar));
}
else if (hasBc5)
{
// BC5
meta->addStatement(new GenOp(" @ = @( @.gr * 2.0 - 1.0, 0.0, 0.0 ); // bc5nm\r\n", normalDecl, new TypeOp(GFXSCT_Float4), sampleNormalOp));
meta->addStatement(new GenOp(" @.z = sqrt( 1.0 - dot( @.xy, @.xy ) ); // bc5nm\r\n", normalVar, normalVar, normalVar));
}
}
else
{
meta->addStatement(new GenOp(" @ = @;\r\n", normalDecl, sampleNormalOp));
meta->addStatement(new GenOp(" @.xyz = @.xyz * 2.0 - 1.0;\r\n", normalVar, normalVar));
}
return meta;
}
ShaderFeature::Resources ShaderFeature::getResources( const MaterialFeatureData &fd )
{
Resources temp;

View file

@ -26,7 +26,7 @@
#include "shaderOp.h"
bool resolveSourceType(LangElement* elem, Var*& outVar, const ShaderTypeInfo*& outInfo)
bool ShaderOp::resolveSourceType(LangElement* elem, Var*& outVar, const ShaderTypeInfo*& outInfo)
{
outVar = nullptr;
outInfo = nullptr;
@ -35,18 +35,18 @@ bool resolveSourceType(LangElement* elem, Var*& outVar, const ShaderTypeInfo*& o
if (Var* v = dynamic_cast<Var*>(elem))
{
outVar = v;
outInfo = LangElement::getTypeInfo(LangElement::stringToConstType((const char*)v->type));
outInfo = getTypeInfo(stringToConstType((const char*)v->type));
return outInfo != nullptr;
}
// INDEX OP: arrVar[index]
if (IndexOp* idx = dynamic_cast<IndexOp*>(elem))
{
Var* arr = idx->arrVar;
Var* arr = dynamic_cast<Var*>(idx->mInput[0]);
if (!arr)
return false;
const ShaderTypeInfo* arrInfo = LangElement::getTypeInfo(LangElement::stringToConstType((const char*)arr->type));
const ShaderTypeInfo* arrInfo = getTypeInfo(stringToConstType((const char*)arr->type));
if (!arrInfo)
return false;
@ -56,10 +56,23 @@ bool resolveSourceType(LangElement* elem, Var*& outVar, const ShaderTypeInfo*& o
return true;
}
if (CastOp* cast = dynamic_cast<CastOp*>(elem))
{
Var* castVar = dynamic_cast<Var*>(cast->mInput[0]);
if (!castVar)
return false;
const ShaderTypeInfo* castInfo = getTypeInfo(cast->mTargetType);// get the casts target type.
if (!castInfo)
return false;
outVar = castVar;
outInfo = castInfo;
}
return false;
}
//**************************************************************************
// Shader Operations
//**************************************************************************
@ -117,9 +130,8 @@ void EchoOp::print( Stream &stream )
//**************************************************************************
// Index operation
//**************************************************************************
IndexOp::IndexOp( Var* var, U32 index ) : Parent( NULL, NULL )
IndexOp::IndexOp( Var* var, U32 index ) : Parent(var, NULL )
{
arrVar = var; // need to keep hold of it for casts.
mInput[0] = var;
mIndex = index;
}
@ -266,13 +278,13 @@ void CastOp::print(Stream& stream)
const ShaderTypeInfo* dstInfo = getTypeInfo(mTargetType);
// no info? types match? nothing to do.
if (!srcInfo || !dstInfo || (srcInfo->type == dstInfo->type))
if (!srcInfo || !dstInfo)
{
srcElem->print(stream); // print something....
return;
}
bool glsl = (GFX->getAdapterType() == OpenGL);
const bool glsl = (GFX->getAdapterType() == OpenGL);
const char* dstName = glsl ? dstInfo->glslName : dstInfo->hlslName;
U32 srcSize = srcInfo->cols;
@ -322,7 +334,7 @@ void CastOp::print(Stream& stream)
}
// vector -> vector widening
if (srcSize < dstSize)
if (srcSize <= dstSize)
{
WRITESTR(dstName);
WRITESTR("(");
@ -452,7 +464,18 @@ void MatrixInitializeOp::print(Stream& stream)
// If not enough elements → pad with zeros
while (count < matSize)
{
WRITESTR("0");
U32 row = count / cols;
U32 col = count % cols;
if (row == col)
{
WRITESTR("1");
}
else
{
WRITESTR("0");
}
count++;
if (count < matSize)
@ -472,3 +495,71 @@ void MatrixInitializeOp::print(Stream& stream)
}
}
MatrixMultiplyOp::MatrixMultiplyOp(LangElement* left, LangElement* right) : Parent(left, right)
{
mInput[0] = left;
mInput[1] = right;
}
void MatrixMultiplyOp::print(Stream& stream)
{
LangElement* leftElem = mInput[0];
Var* leftVar = nullptr;
const ShaderTypeInfo* leftInfo = nullptr;
if (!resolveSourceType(leftElem, leftVar, leftInfo))
{
return;
}
LangElement* rightElem = mInput[0];
Var* rightVar = nullptr;
const ShaderTypeInfo* rightInfo = nullptr;
if (!resolveSourceType(rightElem, rightVar, rightInfo))
{
return;
}
if (leftInfo->isMatrix() && rightInfo->isVector())
{
if (rightInfo->cols != leftInfo->cols)
{
rightElem = new CastOp(rightVar, (GFXShaderConstType)(GFXSCT_Float + (leftInfo->cols - 1)));
}
}
if (leftInfo->isVector() && rightInfo->isMatrix())
{
if (rightInfo->cols != leftInfo->cols)
{
leftElem = new CastOp(leftVar, (GFXShaderConstType)(GFXSCT_Float + (rightInfo->cols - 1)));
}
}
else if (leftInfo->isMatrix() && rightInfo->isMatrix())
{
if (leftInfo->cols != rightInfo->rows)
Con::warnf("MatrixMultiplyOp: incompatible matrices: (%dx%d) × (%dx%d)",
leftInfo->rows, leftInfo->cols,
rightInfo->rows, rightInfo->cols);
}
const bool glsl = (GFX->getAdapterType() == OpenGL);
if (!glsl)
{
WRITESTR("mul(");
leftElem->print(stream);
WRITESTR(", ");
rightElem->print(stream);
WRITESTR(")");
}
else
{
leftElem->print(stream);
WRITESTR(" * ");
rightElem->print(stream);
}
}

View file

@ -45,7 +45,7 @@
*/
//**************************************************************************
bool resolveSourceType(LangElement* elem, Var*& outVar, const ShaderTypeInfo*& outInfo);
///**************************************************************************
/// Shader operation base class
@ -56,6 +56,7 @@ protected:
LangElement * mInput[2];
public:
bool resolveSourceType(LangElement* elem, Var*& outVar, const ShaderTypeInfo*& outInfo);
ShaderOp( LangElement *in1, LangElement *in2 );
};
@ -118,7 +119,6 @@ class IndexOp : public ShaderOp
typedef ShaderOp Parent;
U32 mIndex;
public:
Var* arrVar;
IndexOp( Var* var, U32 index );
void print( Stream &stream ) override;
};
@ -185,10 +185,10 @@ public:
class CastOp : public ShaderOp
{
typedef ShaderOp Parent;
GFXShaderConstType mTargetType;
Vector<String> mSwizzle; // "x", "y", "z", "w"
Vector<String> mFillValues; // "0", "0", "0", "1"
public:
GFXShaderConstType mTargetType;
CastOp( LangElement* srcVar,
GFXShaderConstType type,
const char* swizzleStr = "x;y;z;w",
@ -243,4 +243,13 @@ public:
*/
//----------------------------------------------------------------------------
class MatrixMultiplyOp : public ShaderOp
{
typedef ShaderOp Parent;
public:
MatrixMultiplyOp(LangElement* left, LangElement* right);
void print(Stream& stream) override;
};
#endif // _SHADEROP_H_