cgFx/cgfxAttrDef.cpp

cgFx/cgfxAttrDef.cpp
//
// Copyright (C) 2002-2004 NVIDIA
//
// File: cgfxAttrDef.cpp
//
// Author: Jim Atkinson
//
// cgfxAttrDef holds the "definition" of an attribute on a cgfxShader
// node. This definition includes all the Maya attributes plus the
// CGeffect parameter index.
//
// Changes:
// 12/2003 Kurt Harriman - www.octopusgraphics.com +1-415-893-1023
// - Shader parameter descriptions can be queried via the
// "-des/description" flag of cgfxShader command, together
// with "-lp/listParameters" or "-p/parameter <name>"
// - "-ci/caseInsensitive" option for "-p/parameter <name>"
// - "uimin"/"uimax" annotations set numeric slider bounds.
// - "uiname" annotation is used as parameter description
// if there is no "desc" annotation.
// - Attribute bounds and initial values are updated when
// effect is changed or reloaded.
// - When creating dynamic attributes for shader parameters,
// make them keyable if type is bool, int, float or color.
// Vector types other than colors are made keyable if
// they have a "desc" or "uiname" annotation.
// - Dangling references to deleted dynamic attributes
// caused exceptions in MObject destructor, terminating
// the Maya process. This has been fixed.
// - Fixed some undo/redo bugs that caused crashes and
// incorrect rendering. Fixed some memory leaks.
// - Improved error handling. Use M_CHECK for internal errors.
//
//-
// ==========================================================================
// Copyright 2015 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+
#include "cgfxShaderCommon.h"
#include <float.h> // FLT_MAX
#include <limits.h> // INT_MAX, INT_MIN
#include <string.h>
#include <maya/MIOStream.h>
#include <maya/MGlobal.h>
#include <maya/MString.h>
#include <maya/MStringArray.h>
#include <maya/MFnStringData.h>
#include <maya/MFnStringArrayData.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnCompoundAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnMatrixAttribute.h>
#include <maya/MDGModifier.h>
#include <maya/MFnMatrixData.h>
#include <maya/MPlugArray.h>
#include <maya/MItDependencyGraph.h>
#include <maya/MSelectionList.h>
#include "cgfxAttrDef.h"
#include "cgfxShaderNode.h"
#include "cgfxFindImage.h"
#include "cgfxTextureCache.h"
//
// Defines
//
#define N_MAX_STRING_LENGTH 1024
#ifdef _WIN32
#else
# define stricmp strcasecmp
# define strnicmp strncasecmp
#endif
// Decide the symbol we use
const char cgfxAttrDef::fSymbol = '_';
// Constructor
cgfxAttrDef::cgfxAttrDef(CGparameter cgParameter)
: fType( kAttrTypeUnknown )
, fSize( 0 )
, fHint( kVectorHintNone )
, fNumericMin( NULL )
, fNumericMax( NULL )
, fNumericSoftMin( NULL )
, fNumericSoftMax( NULL )
, fUnits( MDistance::kInvalid )
, fNumericDef( NULL )
, fTextureMonitor(kNullCallback)
// , fParameterIndex( (LPCSTR)(-1) )
, fParameterHandle(0)
, fInvertMatrix( false )
, fTransposeMatrix( false )
, fTweaked( false )
, fInitOnUndo( false )
, fIsConvertedToInternal( false )
{
fName = cgGetParameterName(cgParameter);
fType = cgfxAttrDef::kAttrTypeOther;
fSize = cgGetParameterRows(cgParameter) * cgGetParameterColumns(cgParameter);
fParameterHandle = cgParameter;
fSemantic = cgGetParameterSemantic(cgParameter);
CGtype cgParameterType = cgGetParameterType(cgParameter);
switch (cgParameterType)
{
case CG_BOOL :
if (stricmp(fSemantic.asChar() , "HardwareFogEnabled") == 0)
fType = cgfxAttrDef::kAttrTypeHardwareFogEnabled;
else
fType = cgfxAttrDef::kAttrTypeBool;
break ;
case CG_INT :
if (stricmp(fSemantic.asChar() , "HardwareFogMode") == 0)
fType = cgfxAttrDef::kAttrTypeHardwareFogMode;
else
fType = cgfxAttrDef::kAttrTypeInt;
break ;
case CG_HALF :
case CG_FLOAT :
if (stricmp(fSemantic.asChar() , "HardwareFogStart") == 0)
fType = cgfxAttrDef::kAttrTypeHardwareFogStart;
else if (stricmp(fSemantic.asChar() , "HardwareFogEnd") == 0)
fType = cgfxAttrDef::kAttrTypeHardwareFogEnd;
else if (stricmp(fSemantic.asChar() , "HardwareFogDensity") == 0)
fType = cgfxAttrDef::kAttrTypeHardwareFogDensity;
#ifdef _WIN32
else if (stricmp(fSemantic.asChar() , "Time")==0)
fType = cgfxAttrDef::kAttrTypeTime;
#endif
else
fType = cgfxAttrDef::kAttrTypeFloat;
break;
case CG_HALF2 :
case CG_FLOAT2 :
fType = cgfxAttrDef::kAttrTypeVector2;
break ;
case CG_HALF3 :
case CG_FLOAT3 :
fType = cgfxAttrDef::kAttrTypeVector3;
break ;
case CG_HALF4 :
case CG_FLOAT4 :
if (stricmp(fSemantic.asChar() , "HardwareFogColor") == 0)
fType = cgfxAttrDef::kAttrTypeHardwareFogColor;
else
fType = cgfxAttrDef::kAttrTypeVector4;
break ;
case CG_HALF4x4 :
case CG_FLOAT4x4 :
setMatrixType(cgParameter);
break ;
case CG_STRING :
fType = cgfxAttrDef::kAttrTypeString;
break ;
case CG_TEXTURE :
// handled by setSamplerType()
break ;
case CG_SAMPLER1D :
case CG_SAMPLER2D :
case CG_SAMPLER3D :
case CG_SAMPLERRECT :
case CG_SAMPLERCUBE :
setSamplerType(cgParameter);
break ;
case CG_ARRAY :
case CG_STRUCT :
break ;
default :
MString msg = cgGetTypeString(cgParameterType);
msg += " not yet supported";
M_CHECK( false );
}
if (fType == cgfxAttrDef::kAttrTypeVector3 ||
fType == cgfxAttrDef::kAttrTypeVector4)
{
// Set the specific vector type
//
setVectorType(cgParameter);
}
// Now that we know something about this attribute, walk through
// the annotations on the parameter and see if there is any
// additional information we can find.
//
MString sUIName;
CGannotation cgAnnotation = cgGetFirstParameterAnnotation(cgParameter);
while (cgAnnotation)
{
const char* annotationName = cgGetAnnotationName(cgAnnotation);
const char* annotationValue = cgGetStringAnnotationValue(cgAnnotation);
CGtype cgAnnotationType = cgGetAnnotationType(cgAnnotation);
if (stricmp(annotationName, "uihelp") == 0)
{
fDescription = MString(annotationValue);
}
else if (stricmp(annotationName, "uiname" ) == 0 )
{
sUIName = MString(annotationValue);
}
else if( stricmp( annotationName, "units") == 0)
{
// Make sure units are converted to internal or not
// _XXX is a unit converted to internal
if ( fSymbol == annotationValue[0] )
{
fIsConvertedToInternal = true;
annotationValue++;
}
else
{
fIsConvertedToInternal = false;
}
if( stricmp( annotationValue, "inches") == 0)
{
}
else if( stricmp( annotationValue, "millimetres") == 0 || stricmp( annotationValue, "millimeters") == 0 || stricmp( annotationValue, "mm") == 0)
{
}
else if( stricmp( annotationValue, "centimetres") == 0 || stricmp( annotationValue, "centimeters") == 0 || stricmp( annotationValue, "cm") == 0)
{
}
else if( stricmp( annotationValue, "metres") == 0 || stricmp( annotationValue, "meters") == 0 || stricmp( annotationValue, "m") == 0)
{
}
else if( stricmp( annotationValue, "kilometres") == 0 || stricmp( annotationValue, "kilometers") == 0 || stricmp( annotationValue, "km") == 0)
{
}
else if( stricmp( annotationValue, "feet") == 0)
{
fUnits = MDistance::kFeet;
}
}
else if ((fType >= cgfxAttrDef::kAttrTypeFirstTexture &&
fType <= cgfxAttrDef::kAttrTypeLastTexture))
{
if (stricmp(annotationName, "resourcetype") == 0)
{
if (stricmp(annotationValue, "1d") == 0)
{
fType = cgfxAttrDef::kAttrTypeColor1DTexture;
}
else if (stricmp(annotationValue, "2d") == 0)
{
fType = cgfxAttrDef::kAttrTypeColor2DTexture;
}
else if (stricmp(annotationValue, "3d") == 0)
{
fType = cgfxAttrDef::kAttrTypeColor3DTexture;
}
else if (stricmp(annotationValue, "cube") == 0)
{
fType = cgfxAttrDef::kAttrTypeCubeTexture;
}
else if (stricmp(annotationValue, "rect") == 0)
{
fType = cgfxAttrDef::kAttrTypeColor2DRectTexture;
}
}
else if (stricmp(annotationName, "resourcename") == 0)
{
// Store the texture file to load as the
// string default argument. (I know, its kind
// of a kludge; but if the texture attributes
// were string values, it would be exactly
// correct.
//
fStringDef = annotationValue;
}
}
else if (cgAnnotationType == CG_BOOL )
{} // no min/max for bool
else if (stricmp(annotationName, "min") == 0 ||
stricmp(annotationName, "max") == 0 ||
stricmp(annotationName, "uimin") == 0 ||
stricmp(annotationName, "uimax") == 0 )
{
double * tmp = new double [fSize];
switch (cgAnnotationType)
{
case CG_INT:
{
// int* val = (int*) adesc.Value;
// for (int k = 0; k < fSize; ++k)
// {
// tmp[k] = val[k];
// }
int nValues;
const int* annotationValues = cgGetIntAnnotationValues(cgAnnotation, &nValues);
for (int iValue = 0; iValue < nValues; ++iValue)
tmp[iValue] = static_cast<double>(annotationValues[iValue]);
}
break;
case CG_FLOAT:
{
// float* val = (float*) adesc.Value;
// for (int k = 0; k < fSize; ++k)
// {
// tmp[k] = val[k];
// }
int nValues;
const float* annotationValues = cgGetFloatAnnotationValues(cgAnnotation, &nValues);
for (int iValue = 0; iValue < nValues; ++iValue)
tmp[iValue] = static_cast<double>(annotationValues[iValue]);
}
break;
default:
// This is not a numeric attribute, reset tmp to NULL
delete [] tmp;
tmp = 0;
break;
}
if (stricmp(annotationName, "min") == 0)
{
fNumericMin = tmp;
}
else if (stricmp(annotationName, "max") == 0)
{
fNumericMax = tmp;
}
else if (stricmp(annotationName, "uimin") == 0)
{
fNumericSoftMin = tmp;
}
else
{
fNumericSoftMax = tmp;
}
} // end of if (adesc.Name == "min"|"max")
cgAnnotation = cgGetNextAnnotation(cgAnnotation);
}
// Enforce limits on colors if they do not already have them.
if ( fType == cgfxAttrDef::kAttrTypeColor3 ||
fType == cgfxAttrDef::kAttrTypeColor4 )
{
if ( !fNumericMin )
{
fNumericMin = new double[4];
fNumericMin[0] = 0.0;
fNumericMin[1] = 0.0;
fNumericMin[2] = 0.0;
fNumericMin[3] = 0.0;
}
if ( !fNumericMax )
{
fNumericMax = new double[4];
fNumericMax[0] = 1.0;
fNumericMax[1] = 1.0;
fNumericMax[2] = 1.0;
fNumericMax[3] = 1.0;
}
}
// If no description, use UIName.
if ( !fDescription.length() )
fDescription = sUIName;
// Now get the default values
//
double* tmp = new double [fSize];
CGtype cgParameterBaseType = cgGetParameterBaseType(cgParameter);
switch (cgParameterBaseType)
{
case CG_BOOL:
{
int val;
if (cgGetParameterValueic(cgParameter, 1, &val) != 1)
{
delete [] tmp;
tmp = 0;
break;
}
for (int k = 0; k < fSize; ++k)
{
tmp[k] = val ? 1 : 0;
}
}
break;
case CG_INT:
{
int val;
if (cgGetParameterValueic(cgParameter, 1, &val) != 1)
{
delete [] tmp;
tmp = 0;
break;
}
for (int k = 0; k < fSize; ++k)
{
tmp[k] = val;
}
}
break;
case CG_FLOAT:
{
if (fSize == 1)
{
float val;
if (cgGetParameterValuefc(cgParameter, 1, &val) != 1)
{
delete [] tmp;
tmp = 0;
break;
}
// If the value is a converted value, we need to convert it to one with unit
if( fIsConvertedToInternal)
{
val = (float)MDistance( val, MDistance::internalUnit()).as(fUnits);
}
tmp[0] = val;
}
else if (fSize <= 4 || fType == cgfxAttrDef::kAttrTypeMatrix)
{
float val[16];
if (fType == kAttrTypeMatrix)
{
cgGetMatrixParameterfc(cgParameter, val);
}
else
{
unsigned int vecSize = fSize;
cgGetParameterValuefc(cgParameter, vecSize, val);
}
/*if (result != S_OK)
{
delete [] tmp;
tmp = 0;
break;
}*/
for (int k = 0; k < fSize; ++k)
{
// If the value is a converted value, we need to convert it to one with unit
if( fIsConvertedToInternal)
{
val[k] = (float)MDistance( val[k], MDistance::internalUnit()).as(fUnits);
}
tmp[k] = val[k];
}
}
}
break;
case CG_STRING:
{
#ifdef _WIN32
LPCSTR val;
#else
const char* val = NULL;
#endif
val = cgGetStringParameterValue(cgParameter);
fStringDef = val;
}
// Fall through into the default case to destroy the
// numeric default value.
//
default:
// We don't know what to do but there is no point in
// keeping tmp around.
//
delete [] tmp;
tmp = 0;
}
// Don't save initial value if it is zero (or identity matrix).
if ( tmp )
{
int k;
if ( fSize == 16 )
{
const double* d = &MMatrix::identity[0][0];
for ( k = 0; k < fSize; ++k )
if ( tmp[ k ] != d[ k ] )
break;
}
else
{
for ( k = 0; k < fSize; ++k )
if ( tmp[ k ] != 0.0 )
break;
}
if ( k == fSize )
{
delete [] tmp;
tmp = 0;
}
}
fNumericDef = tmp;
};
cgfxAttrDef::cgfxAttrDef(
const MString& sAttrName,
const cgfxAttrType eAttrType,
const MString& sDescription,
const MString& sSemantic,
MObject obNode,
MObject obAttr
)
: fType( kAttrTypeUnknown )
, fSize( 0 )
, fHint( kVectorHintNone )
, fNumericMin( NULL )
, fNumericMax( NULL )
, fNumericSoftMin( NULL )
, fNumericSoftMax( NULL )
, fUnits( MDistance::kInvalid )
, fNumericDef( NULL )
, fTextureMonitor(kNullCallback)
// , fParameterIndex( (LPCSTR)(-1) )
, fParameterHandle(0)
, fInvertMatrix( false )
, fTransposeMatrix( false )
, fTweaked( false )
, fInitOnUndo( false )
, fIsConvertedToInternal(false)
{
MStatus status;
fName = sAttrName;
fType = eAttrType;
fDescription = sDescription;
fSemantic = sSemantic;
fAttr = obAttr;
MPlug plug( obNode, obAttr );
double numericMin[4];
double numericMax[4];
double numericValue[4];
bool hasMin = false;
bool hasMax = false;
bool isNumeric = false;
// If compound attribute, get value and bounds of each element.
if ( fnCompound.setObject( obAttr ) )
{
hasMin = true;
hasMax = true;
isNumeric = true;
MObject obChild;
MStringArray saChild;
int iChild;
int nChild = fnCompound.numChildren();
M_CHECK( nChild >= 2 && nChild <= 3 );
for ( iChild = 0; iChild < nChild; ++iChild )
{
// Get child attribute.
if ( iChild < 3 )
{
obChild = fnCompound.child( iChild, &status );
M_CHECK( status );
}
status = fnNumeric.setObject( obChild );
M_CHECK( status );
// Min
if ( fnNumeric.hasMin() )
fnNumeric.getMin( numericMin[ iChild ] );
else
hasMin = false;
// Max
if ( fnNumeric.hasMax() )
fnNumeric.getMax( numericMax[ iChild ] );
else
hasMax = false;
// Value
MPlug plChild( obNode, obChild );
status = plChild.getValue( numericValue[ iChild ] );
M_CHECK( status );
// Check for 4-element vector.
saChild.append( fnNumeric.name() );
if ( iChild == 2 )
{
const char* suffix = NULL;
if ( saChild[0] == sAttrName + "X" &&
saChild[1] == sAttrName + "Y" &&
saChild[2] == sAttrName + "Z" )
suffix = "W";
else if ( saChild[0] == sAttrName + "R" &&
saChild[1] == sAttrName + "G" &&
saChild[2] == sAttrName + "B" )
suffix = "Alpha";
if ( suffix )
{
MString sName2 = sAttrName + suffix;
MFnDependencyNode fnNode(obNode);
obChild = fnNode.attribute( sName2, &status );
MFnNumericData::Type ndt = fnNumeric.unitType();
if ( status &&
fnNumeric.setObject( obChild ) &&
fnNumeric.unitType() == ndt )
{
fAttr2 = obChild;
nChild = 4; // loop again to get extra attr
}
}
}
} // loop over children
fSize = nChild;
} // compound
// Simple numeric attribute?
else if ( fnNumeric.setObject( obAttr ) )
{
MFnNumericAttribute fnNumeric( obAttr, &status );
M_CHECK( status );
fSize = 1;
isNumeric = true;
// Get min and max.
if ( fnNumeric.hasMin() )
{
fnNumeric.getMin( numericMin[0] );
hasMin = true;
}
if ( fnNumeric.hasMax() )
{
fnNumeric.getMax( numericMax[0] );
hasMax = true;
}
// Get slider bounds.
if ( fnNumeric.hasSoftMin() )
{
fNumericSoftMin = new double[1];
fnNumeric.getSoftMin( fNumericSoftMin[0] );
}
if ( fnNumeric.hasSoftMax() )
{
fNumericSoftMax = new double[1];
fnNumeric.getSoftMax( fNumericSoftMax[0] );
}
// Value
status = plug.getValue( numericValue[0] );
M_CHECK( status );
} // simple numeric
// String attribute?
else if (fnTyped.setObject( obAttr ) &&
fnTyped.attrType() == MFnData::kString)
{
fSize = 1;
status = plug.getValue( fStringDef );
M_CHECK( status );
} // string
// Matrix attribute?
else if (fnMatrix.setObject( obAttr ))
{
MObject obData;
status = plug.getValue( obData );
M_CHECK( status );
MFnMatrixData fnMatrixData( obData, &status );
M_CHECK( status );
const MMatrix& mat = fnMatrixData.matrix( &status );
M_CHECK( status );
fSize = 16;
fNumericDef = new double[ fSize ];
const double* p = &(mat.matrix[0][0]);
for ( int i = 0; i < 16; ++i )
fNumericDef[ i ] = p[ i ];
M_CHECK( status );
} // matrix
// Mystified...
else
M_CHECK( false );
// Store numeric value, min and max.
if ( isNumeric )
{
fNumericDef = new double[ fSize ];
memcpy( fNumericDef, numericValue, fSize * sizeof( numericValue[0] ) );
if ( hasMin )
{
fNumericMin = new double[ fSize ];
memcpy( fNumericMin, numericMin, fSize * sizeof( numericMin[0] ) );
}
if ( hasMax )
{
fNumericMax = new double[ fSize ];
memcpy( fNumericMax, numericMax, fSize * sizeof( numericMax[0] ) );
}
}
// set attribute flags
setAttributeFlags();
}
// Destructor
cgfxAttrDef::~cgfxAttrDef()
{
release();
delete [] fNumericMin;
delete [] fNumericMax;
delete [] fNumericSoftMin;
delete [] fNumericSoftMax;
delete [] fNumericDef;
};
// Release any associated resources
void cgfxAttrDef::release()
{
releaseTexture();
releaseCallback();
}
void cgfxAttrDef::releaseTexture()
{
fTexture = cgfxRCPtr<cgfxTextureCacheEntry>();
}
void cgfxAttrDef::releaseCallback()
{
if( fTextureMonitor != kNullCallback)
{
MMessage::removeCallback( fTextureMonitor);
fTextureMonitor = kNullCallback;
}
}
// ================ cgfxAttrDef::setTextureType ================
// This method looks at the parameter data type, semantic, and
// annotation and determines
void cgfxAttrDef::setTextureType(CGparameter cgParameter)
{
fType = kAttrTypeColor2DTexture;
const char* semantic = cgGetParameterSemantic(cgParameter);
if (!semantic) return;
// We have to go thru semantics and annotations to find the type of the texture
if (semantic)
{
if (stricmp(semantic, "normal") == 0)
{
fType = kAttrTypeNormalTexture;
}
else if (stricmp(semantic, "height") == 0)
{
fType = kAttrTypeBumpTexture;
}
else if (stricmp(semantic, "environment") == 0)
{
fType = kAttrTypeEnvTexture;
}
else if (stricmp(semantic, "environmentnormal") == 0)
{
fType = kAttrTypeNormalizationTexture;
}
}
// Now browse through the annotations to see if there is anything
// interesting there too.
CGannotation cgAnnotation = cgGetFirstParameterAnnotation(cgParameter);
while (cgAnnotation)
{
const char* annotationName = cgGetAnnotationName(cgAnnotation);
const char* annotationValue = cgGetStringAnnotationValue(cgAnnotation);
if (stricmp(annotationName, "resourcetype") == 0)
{
if (stricmp(annotationValue, "1d") == 0)
{
fType = kAttrTypeColor1DTexture;
}
else if (stricmp(annotationValue, "2d") == 0)
{
fType = kAttrTypeColor2DTexture;
}
else if (stricmp(annotationValue, "rect") == 0)
{
fType = kAttrTypeColor2DRectTexture;
}
else if (stricmp(annotationValue, "3d") == 0)
{
fType = kAttrTypeColor3DTexture;
}
else if (stricmp(annotationValue, "cube") == 0)
{
fType = kAttrTypeCubeTexture;
}
}
else if (stricmp(annotationName, "resourcename") == 0)
{
// Store the texture file to load as the
// string default argument. (I know, its kind
// of a kludge; but if the texture attributes
// were string values, it would be exactly
// correct.
//
fStringDef = annotationValue;
}
else if (stricmp(annotationName, "uvlink") == 0)
{
// Store the variable name of the input UV
// set that this texture is to be linked against.
fTextureUVLink = annotationValue;
}
cgAnnotation = cgGetNextAnnotation(cgAnnotation);
}
}
void cgfxAttrDef::setSamplerType(CGparameter cgParameter)
{
CGstateassignment cgStateAssignment = cgGetNamedSamplerStateAssignment(cgParameter, "texture");
setTextureType(cgGetTextureStateAssignmentValue(cgStateAssignment));
}
void cgfxAttrDef::setMatrixType(CGparameter cgParameter)
{
fType = kAttrTypeMatrix;
const char* semantic = cgGetParameterSemantic(cgParameter);
if (!semantic)
return;
if (stricmp(semantic, "world") == 0)
{
fType = kAttrTypeWorldMatrix;
fInvertMatrix = false;
fTransposeMatrix = false;
}
else if (stricmp(semantic, "worldinverse") == 0)
{
fType = kAttrTypeWorldMatrix;
fInvertMatrix = true;
fTransposeMatrix = false;
}
else if (stricmp(semantic, "worldtranspose") == 0)
{
fType = kAttrTypeWorldMatrix;
fInvertMatrix = false;
fTransposeMatrix = true;
}
else if (stricmp(semantic, "worldinversetranspose") == 0)
{
fType = kAttrTypeWorldMatrix;
fInvertMatrix = true;
fTransposeMatrix = true;
}
else if (stricmp(semantic, "worldview") == 0)
{
fType = kAttrTypeWorldViewMatrix;
fInvertMatrix = false;
fTransposeMatrix = false;
}
else if (stricmp(semantic, "worldviewtranspose") == 0)
{
fType = kAttrTypeWorldViewMatrix;
fInvertMatrix = false;
fTransposeMatrix = true;
}
else if (stricmp(semantic, "worldviewinverse") == 0)
{
fType = kAttrTypeWorldViewMatrix;
fInvertMatrix = true;
fTransposeMatrix = false;
}
else if (stricmp(semantic, "worldviewinversetranspose") == 0)
{
fType = kAttrTypeWorldViewMatrix;
fInvertMatrix = true;
fTransposeMatrix = true;
}
else if (stricmp(semantic, "worldviewprojection") == 0)
{
fType = kAttrTypeWorldViewProjectionMatrix;
fInvertMatrix = false;
fTransposeMatrix = false;
}
else if (stricmp(semantic, "worldviewprojectiontranspose") == 0)
{
fType = kAttrTypeWorldViewProjectionMatrix;
fInvertMatrix = false;
fTransposeMatrix = true;
}
else if (stricmp(semantic, "worldviewprojectioninverse") == 0)
{
fType = kAttrTypeWorldViewProjectionMatrix;
fInvertMatrix = true;
fTransposeMatrix = false;
}
else if (stricmp(semantic, "worldviewprojectioninversetranspose") == 0)
{
fType = kAttrTypeWorldViewProjectionMatrix;
fInvertMatrix = true;
fTransposeMatrix = true;
}
else if (stricmp(semantic, "view") == 0)
{
fType = kAttrTypeViewMatrix;
fInvertMatrix = false;
fTransposeMatrix = false;
}
else if (stricmp(semantic, "viewinverse") == 0)
{
fType = kAttrTypeViewMatrix;
fInvertMatrix = true;
fTransposeMatrix = false;
}
else if (stricmp(semantic, "viewtranspose") == 0)
{
fType = kAttrTypeViewMatrix;
fInvertMatrix = false;
fTransposeMatrix = true;
}
else if (stricmp(semantic, "viewinversetranspose") == 0)
{
fType = kAttrTypeViewMatrix;
fInvertMatrix = true;
fTransposeMatrix = true;
}
else if (stricmp(semantic, "projection") == 0)
{
fType = kAttrTypeProjectionMatrix;
fInvertMatrix = false;
fTransposeMatrix = false;
}
else if (stricmp(semantic, "projectioninverse") == 0)
{
fType = kAttrTypeProjectionMatrix;
fInvertMatrix = true;
fTransposeMatrix = false;
}
else if (stricmp(semantic, "projectiontranspose") == 0)
{
fType = kAttrTypeProjectionMatrix;
fInvertMatrix = false;
fTransposeMatrix = true;
}
else if (stricmp(semantic, "projectioninversetranspose") == 0)
{
fType = kAttrTypeProjectionMatrix;
fInvertMatrix = true;
fTransposeMatrix = true;
}
}
// This routine returns true if the semantic value is known to refer
// to a color value instead of a positional value. We determine that
// this is a color value because it uses one of the known names or it
// contains the string "color" or "colour" in it.
//
void cgfxAttrDef::setVectorType(CGparameter cgParameter)
{
#if 0
// This variable is not used
static char* colorList[] =
{
"diffuse",
"specular",
"ambient",
"emissive",
};
#endif
const char* semantic = cgGetParameterSemantic(cgParameter);
if ((semantic == NULL || strcmp(semantic,"") == 0) == false)
{
// Check the semantic value to see if this is a color
//
if (stricmp(semantic, "diffuse") == 0 ||
stricmp(semantic, "specular") == 0 ||
stricmp(semantic, "ambient") == 0 ||
stricmp(semantic, "emissive") == 0)
{
fType = (fSize == 3) ? kAttrTypeColor3 : kAttrTypeColor4;
}
else if (stricmp(semantic, "direction") == 0)
{
fType = kAttrTypeFirstDir;
}
else if (stricmp(semantic, "position") == 0)
{
fType = kAttrTypeFirstPos;
}
}
CGannotation cgAnnotation = cgGetFirstParameterAnnotation(cgParameter);
while (cgAnnotation)
{
const char* annotationName = cgGetAnnotationName(cgAnnotation);
const char* annotationValue = cgGetStringAnnotationValue(cgAnnotation);
if (stricmp(annotationName, "type") == 0)
{
if (strlen(annotationValue) != 0)
{
if (stricmp(annotationValue, "color") == 0)
{
fType = (fSize == 3) ? kAttrTypeColor3 : kAttrTypeColor4;
}
}
}
else if (stricmp(annotationName, "space") == 0)
{
// we assume the attribute to be a direction if it doesn't have semantic to determine it.
// display a warning at the same time
if(fType != kAttrTypeFirstDir && fType != kAttrTypeFirstPos)
{
const MString warnMsg = fName + " has space annotation but doesn't have semantic to determine if it is a direction or position! Assuming it to be a direction. You'd better add a semantic for it!";
fType = kAttrTypeFirstDir;
}
if (stricmp(annotationValue, "world") == 0)
{
fType = (cgfxAttrType)(fType + kAttrTypeWorldDir - kAttrTypeFirstDir);
}
else if (stricmp(annotationValue, "view") == 0 ||
stricmp(annotationValue, "devicelightspace") == 0)
{
fType = (cgfxAttrType)(fType + kAttrTypeViewDir - kAttrTypeFirstDir);
}
else if (stricmp(annotationValue, "projection") == 0)
{
fType = (cgfxAttrType)(fType + kAttrTypeProjectionDir - kAttrTypeFirstDir);
}
else if (stricmp(annotationValue, "screen") == 0)
{
fType = (cgfxAttrType)(fType + kAttrTypeScreenDir - kAttrTypeFirstDir);
}
}
else if (stricmp(annotationName, "object") == 0)
{
fHint = kVectorHintNone;
if (stricmp(annotationValue, "dirlight") == 0)
{
fHint = kVectorHintDirLight;
}
else if (stricmp(annotationValue, "spotlight") == 0)
{
fHint = kVectorHintSpotLight;
}
else if (stricmp(annotationValue, "pointlight") == 0)
{
fHint = kVectorHintPointLight;
}
else if (stricmp(annotationValue, "camera") == 0 || stricmp(annotationValue, "eye") == 0)
{
fHint = kVectorHintEye;
}
}
cgAnnotation = cgGetNextAnnotation(cgAnnotation);
}
return;
}
// ========== cgfxAttrDef::attrsFromNode ==========
//
// This function simply returns the parses through the dynamic
// attributes on an effect and builds a list of cgfxAttrDef objects.
// The cgfxAttrDef objects in the list are incomplete but they are
// only used to determine which attributes on the object need to be
// created, destroyed, or left alone. Ultimately, the cgfxAttrDefList
// that is constructed from the effect itself will be the one held by
// the node.
//
/* static */
cgfxRCPtr<cgfxAttrDefList> cgfxAttrDef::attrsFromNode(MObject& oNode)
{
MStatus status;
MFnDependencyNode fnNode(oNode, &status);
M_CHECK( status );
M_CHECK( fnNode.typeId() == cgfxShaderNode::sId );
cgfxShaderNode* pNode = (cgfxShaderNode *) fnNode.userNode();
M_CHECK( pNode );
cgfxRCPtr<cgfxAttrDefList> list = pNode->attrDefList();
// The list has not been initialized. Create it and try again.
//
if (list.isNull())
{
buildAttrDefList(oNode);
list = pNode->attrDefList();
}
return list;
}
// ========== cgfxAttrDef::buildAttrDefList ==========
//
// This routine reconstructs the attrDefList from stringArray value in
// the attributeList attribute. The reconstructed list is incomplete
// but it is good enough to compare to the list generated by
// attrsFromEffect to see if the connections are still valid.
//
void cgfxAttrDef::buildAttrDefList(MObject& oNode)
{
MStatus status;
MFnDependencyNode fnNode(oNode, &status);
M_CHECK( status &&
fnNode.typeId() == cgfxShaderNode::sId );
cgfxShaderNode* pNode = (cgfxShaderNode *) fnNode.userNode();
M_CHECK( pNode && pNode->attrDefList().isNull() );
cgfxRCPtr<cgfxAttrDefList> list(new cgfxAttrDefList);
MStringArray saList;
pNode->getAttributeList(saList);
// MStatus status;
// // Get the value of the attributeList attribute
// //
// MPlug plug(oNode, cgfxShaderNode::sAttributeList);
// MObject saDataObject;
// status = plug.getValue(saDataObject);
// if (!status)
// {
// sprintf(errorMsg, "%s(%d): failed to get attributeList value: %s!",
// __FILE__, __LINE__, status.errorString().asChar());
// throw errorMsg;
// }
// MFnStringArrayData fnSaData(saDataObject, &status);
// if (!status)
// {
// sprintf(errorMsg,
// "%s(%d): failed to construct attributeList function set: %s!",
// __FILE__, __LINE__, status.errorString().asChar());
// throw errorMsg;
// }
// fnSaData.copyTo(saList);
// Ok, we succeeded, saList is now an array of "top level"
// dynamic attribute names along with some minimal type
// information. Parse through it and reconstruct the
// cgfxAttrDefList.
//
unsigned int i;
for (i = 0; i < saList.length(); ++i)
{
MString item = saList[i];
MStringArray splitItem;
MObject oAttr;
item.split('\t', splitItem);
cgfxAttrDef* attrDef = attrFromNode( fnNode,
splitItem[0],
(cgfxAttrType)(splitItem[1].asInt()),
splitItem[2],
splitItem[3]);
if ( attrDef )
{
list->add( attrDef );
}
}
pNode->setAttrDefList(list);
}
/* static */
cgfxAttrDef*
cgfxAttrDef::attrFromNode( const MFnDependencyNode& fnNode,
const MString& sAttrName,
const cgfxAttrType eAttrType,
const MString& sDescription,
const MString& sSemantic)
{
MStatus status;
cgfxAttrDef* attrDef = NULL;
try
{
MObject obNode = fnNode.object();
MObject obAttr = fnNode.attribute( sAttrName, &status );
if ( !status ) // if node doesn't have this attr
return NULL; // skip it
attrDef = new cgfxAttrDef(
sAttrName, eAttrType, sDescription, sSemantic,
obNode, obAttr
);
}
catch ( cgfxShaderCommon::InternalError* e )
{
size_t ee = (size_t)e;
MString sMsg = "(";
sMsg += (int)ee;
sMsg += ") cgfxShader node \"";
sMsg += fnNode.name();
sMsg += "\" has invalid attribute \"";
sMsg += sAttrName;
sMsg += "\" - ignored";
delete attrDef;
attrDef = NULL;
}
catch (...)
{
delete attrDef;
M_CHECK( false );
}
return attrDef;
} // cgfxAttrDef::attrFromNode
bool
cgfxAttrDef::createAttribute( const MObject& oNode, MDGModifier* mod, cgfxShaderNode* pNode)
{
MFnDependencyNode fnNode( oNode );
// Return if node already has an attribute with the specified name.
// (Shader var name could conflict with a predefined static attr.)
MObject obExistingAttr = fnNode.attribute( fName );
if ( !obExistingAttr.isNull() )
{
return false;
}
try
{
MStatus status;
MObject oAttr, oAttr2;
MObject oSrcNode, oDstNode;
MObject oSrcAttr, oDstAttr;
bool doConnection = false;
switch (fType)
{
case kAttrTypeBool:
oAttr = nAttr.create( fName, fName, MFnNumericData::kBoolean,
0.0, &status );
M_CHECK( status );
nAttr.setKeyable( true );
nAttr.setAffectsAppearance( true );
// Add to category so we know we don't want to export as a user property (MAYA-79473)
nAttr.addToCategory( "HW_shader_parameter" );
break;
case kAttrTypeInt:
oAttr = nAttr.create( fName, fName, MFnNumericData::kInt,
0.0, &status );
M_CHECK( status );
nAttr.setKeyable( true );
nAttr.setAffectsAppearance( true );
// Add to category so we know we don't want to export as a user property (MAYA-79473)
nAttr.addToCategory( "HW_shader_parameter" );
break;
case kAttrTypeFloat:
oAttr = nAttr.create( fName, fName, MFnNumericData::kFloat,
0.0, &status );
M_CHECK( status );
nAttr.setKeyable( true );
nAttr.setAffectsAppearance( true );
// Add to category so we know we don't want to export as a user property (MAYA-79473)
nAttr.addToCategory( "HW_shader_parameter" );
break;
case kAttrTypeString:
oAttr = tAttr.create(fName, fName, MFnData::kString,
MObject::kNullObj, &status );
tAttr.setAffectsAppearance( true );
M_CHECK( status );
// Add to category so we know we don't want to export as a user property (MAYA-79473)
tAttr.addToCategory( "HW_shader_parameter" );
break;
case kAttrTypeVector2:
case kAttrTypeVector3:
case kAttrTypeVector4:
case kAttrTypeColor3:
case kAttrTypeColor4:
case kAttrTypeObjectDir:
case kAttrTypeWorldDir:
case kAttrTypeViewDir:
case kAttrTypeProjectionDir:
case kAttrTypeScreenDir:
case kAttrTypeObjectPos:
case kAttrTypeWorldPos:
case kAttrTypeViewPos:
case kAttrTypeProjectionPos:
case kAttrTypeScreenPos:
{
const char** suffixes = compoundAttrSuffixes( fType );
MString sChild;
MObject oaChildren[4];
M_CHECK( fSize <= 4 );
for ( int iChild = 0; iChild < fSize; ++iChild )
{
const char* suffix = suffixes[ iChild ];
sChild = fName + suffix;
oaChildren[ iChild ] = nAttr.create( sChild,
sChild,
0.0,
&status );
M_CHECK( status );
}
if ( fSize == 4 )
{
oAttr2 = oaChildren[3];
if ( fType == kAttrTypeColor3 ||
fType == kAttrTypeColor4 ||
fDescription.length() > 0 )
nAttr.setKeyable( true );
// Add to category so we know we don't want to export as a user property (MAYA-79473)
nAttr.addToCategory( "HW_shader_parameter" );
}
oAttr = nAttr.create( fName,
fName,
oaChildren[0],
oaChildren[1],
oaChildren[2],
&status );
M_CHECK( status );
if ( fType == kAttrTypeColor3 ||
fType == kAttrTypeColor4 )
{
nAttr.setKeyable( true );
nAttr.setUsedAsColor( true );
}
else if ( fDescription.length() > 0 )
nAttr.setKeyable( true );
nAttr.setAffectsAppearance( true );
// Add to category so we know we don't want to export as a user property (MAYA-79473)
nAttr.addToCategory( "HW_shader_parameter" );
break;
}
case kAttrTypeMatrix:
// Create a generic matrix
//
oAttr = mAttr.create(fName, fName,
M_CHECK( status );
mAttr.setAffectsAppearance( true );
// Add to category so we know we don't want to export as a user property (MAYA-79473)
mAttr.addToCategory( "HW_shader_parameter" );
break;
case kAttrTypeWorldMatrix:
case kAttrTypeViewMatrix:
case kAttrTypeProjectionMatrix:
case kAttrTypeWorldViewMatrix:
case kAttrTypeWorldViewProjectionMatrix:
// These matricies are handled internally and have no attribute.
//
break;
case kAttrTypeColor1DTexture:
case kAttrTypeColor2DTexture:
case kAttrTypeColor3DTexture:
case kAttrTypeColor2DRectTexture:
case kAttrTypeNormalTexture:
case kAttrTypeBumpTexture:
case kAttrTypeCubeTexture:
case kAttrTypeEnvTexture:
case kAttrTypeNormalizationTexture:
if( pNode->getTexturesByName())
{
oAttr = tAttr.create(fName, fName, MFnData::kString,
MObject::kNullObj, &status );
M_CHECK( status );
tAttr.setAffectsAppearance( true );
// Add to category so we know we don't want to export as a user property (MAYA-79473)
tAttr.addToCategory( "HW_shader_parameter" );
}
else
{
const char* suffix1 = "R";
const char* suffix2 = "G";
const char* suffix3 = "B";
MObject oChild1 = nAttr.create(fName + suffix1,
fName + suffix1,
0.0, &status);
MObject oChild2 = nAttr.create(fName + suffix2,
fName + suffix2,
0.0, &status);
MObject oChild3 = nAttr.create(fName + suffix3,
fName + suffix3,
0.0, &status);
oAttr = nAttr.create( fName,
fName,
oChild1,
oChild2,
oChild3,
&status );
M_CHECK( status );
// Although it's not strictly necessary, set this attribute
// to be a color so the user can will at least get the
// texture assignment button in the AE if for some reason
// our AE template is missing
nAttr.setUsedAsColor( true );
nAttr.setAffectsAppearance( true );
// Add to category so we know we don't want to export as a user property (MAYA-79473)
nAttr.addToCategory( "HW_shader_parameter" );
}
break;
#ifdef _WIN32
case kAttrTypeTime:
#endif
case kAttrTypeHardwareFogEnabled:
case kAttrTypeHardwareFogMode:
case kAttrTypeHardwareFogStart:
case kAttrTypeHardwareFogEnd:
case kAttrTypeHardwareFogDensity:
case kAttrTypeHardwareFogColor:
case kAttrTypeOther:
break;
default:
M_CHECK( false );
}
if (oAttr.isNull())
{
// There is no attribute for this parameter
//
return false;
}
// Add the attribute to the node
//
status = mod->addAttribute( oNode, oAttr );
M_CHECK( status );
if (!oAttr2.isNull())
{
status = mod->addAttribute( oNode, oAttr2 );
M_CHECK( status );
}
// Hold onto a copy of the attribute for easy access later.
fAttr = oAttr;
fAttr2 = oAttr2;
// If we need to connect this node to some other node, do so.
//
if (doConnection)
{
status = mod->connect(oSrcNode, oSrcAttr, oDstNode, oDstAttr);
M_CHECK( status );
}
return true; // success
}
catch ( cgfxShaderCommon::InternalError* e )
{
size_t ee = (size_t)e;
fType = kAttrTypeUnknown;
MString sMsg = "(";
sMsg += (int)ee;
sMsg += ") cgfxShader node \"";
sMsg += fnNode.name();
sMsg += "\": unable to add attribute \"";
sMsg += fName;
sMsg += "\"";
return false; // failure
}
catch (...)
{
M_CHECK( false );
}
return true;
} // cgfxAttrDef::createAttribute
bool
cgfxAttrDef::destroyAttribute( MObject& oNode, MDGModifier* dgMod)
{
MStatus status;
// If this is a texture node, clear the value (which will destroy
// any attached textures)
if( fType >= kAttrTypeFirstTexture && fType <= kAttrTypeLastTexture)
setTexture( oNode, "", dgMod);
// New effect won't need this old attr anymore.
status = dgMod->removeAttribute( oNode, fAttr );
// If there is a secondary attribute, remove that too.
if ( !fAttr2.isNull() )
status = dgMod->removeAttribute( oNode, fAttr2 );
// Don't leave dangling references to deleted attributes.
fAttr2 = MObject::kNullObj;
return status == MStatus::kSuccess;
} // cgfxAttrDef::destroyAttribute
// ========== cgfxAttrDef::updateNode ==========
//
// This routine takes a node and an effect and ensures that all those
// and only those attributes that should be on the node, are on the
// node.
//
// The output cgfxAttrDefList and its elements are newly
// allocated.
//
/* static */
void
cgfxAttrDef::updateNode(
const cgfxRCPtr<const cgfxEffect>& effect, // IN
cgfxShaderNode* pNode, // IN
MDGModifier* dgMod, // UPD
cgfxRCPtr<cgfxAttrDefList>& effectList, // OUT
MStringArray& attributeList ) // OUT
{
MStatus status;
effectList = cgfxRCPtr<cgfxAttrDefList>();
try
{
MObject oNode = pNode->thisMObject();
MFnDependencyNode fnNode( oNode );
MFnAttribute fnAttr;
effectList = effect->attrsFromEffect(); // caller will own this list
cgfxRCPtr<cgfxAttrDefList> nodeList = attrsFromNode( oNode ); // oNode owns this one
cgfxAttrDefList::iterator emIt;
cgfxAttrDefList::iterator nmIt;
cgfxAttrDef* adef;
// Walk through the nodeList. Delete each attribute that is not
// also found in the effect list.
//
for (nmIt = nodeList->begin(); nmIt; ++nmIt)
{ // loop over nodeList
adef = (*nmIt);
// Skip if node doesn't have this attribute.
if ( adef->fAttr.isNull() )
continue;
// Look for a matching attribute in the effect
emIt = effectList->find( adef->fName );
// Drop Maya attribute from node if shader var
// was declared in old effect, but not declared
// in new effect, or data type is not the same.
if ( !emIt ||
(*emIt)->fType != adef->fType )
{
adef->destroyAttribute( oNode, dgMod);
}
// If this is a texture and it has a non-default
// value, we should switch to this mode of texture
// definition (names or nodes)
else if( emIt &&
(*emIt)->fType >= kAttrTypeFirstTexture &&
(*emIt)->fType <= kAttrTypeLastTexture)
{
// Get the attribute type of the existing attribute
// and ensure our node is setup to digest the correct
// type of textures
MFnTypedAttribute typedFn( adef->fAttr);
bool usesName = typedFn.attrType() == MFnData::kString;
pNode->setTexturesByName( usesName);
// Finally, if this texture uses fileTexture nodes then
// mark the value as "tweaked" to prevent the default value
// code from trying to attach a default fileTexture node.
// This is crucial to being able to load shaders back in. The
// Maya file will create our node using the following steps:
// 1) Create an empty cgfxShader node
// 2) Create all the dynamic attributes (like this texture)
// 3) Set the effect attribute (which calls this code)
// 4) Create DG connections to file texture nodes
// So, if we did setup a default file texture node, the load
// would be unable to connect the real file texture.
//
if( !usesName)
(*emIt)->fTweaked = true;
}
} // loop over nodeList
// Delete any unnecessary attributes before starting to add new ones
// (in case we're deleting and re-creating a property with a different
// type)
//
dgMod->doIt();
// Walk through the effectList. Add each item that is not also
// found in the node list.
//
for (emIt = effectList->begin(); emIt; ++emIt)
{ // loop over effectList
adef = (*emIt);
// Note: nodeList::find will work with a null this pointer
// so nodeList->find() will work even if nodeList is NULL.
//
nmIt = nodeList->find( adef->fName );
// Double check that the attr still exists. Get current value.
cgfxAttrDef* cdef = NULL;
if ( nmIt &&
!(*nmIt)->fAttr.isNull() )
cdef = attrFromNode( fnNode,
(*nmIt)->fName,
(*nmIt)->fType,
(*nmIt)->fDescription,
(*nmIt)->fSemantic);
// Add new Maya attribute.
if ( !cdef )
adef->createAttribute( oNode, dgMod, pNode );
// Go on with existing attribute.
else
{ // use existing attribute
// Copy the attribute handles to the new effect's cgfxAttrDef.
adef->fAttr = (*nmIt)->fAttr;
adef->fAttr2 = (*nmIt)->fAttr2;
// Did we already notice that the user has set this attr?
if ( (*nmIt)->fTweaked )
adef->fTweaked = true;
// If no old effect, then the current values were
// loaded from the scene file, and should override
// the new effect's defaults if they are different.
else if ( pNode->effect().isNull() )
{
if ( !adef->isInitialValueEqual( *cdef ) )
adef->fTweaked = true;
}
// If current value is not the same as old effect's
// default, then user has adjusted it. Current value
// takes precedence over the new effect's default.
else if ( !(*nmIt)->isInitialValueEqual( *cdef ) )
adef->fTweaked = true;
// User hasn't changed this value. New effect's
// default takes precedence. Since we are going to
// change the value, remember to change it back
// to the old effect's default in case of undo.
// Among other things, this logic allows the UI shader
// description to update as different effects are chosen.
else
(*nmIt)->fInitOnUndo = true;
delete cdef;
} // use existing attribute
} // loop over effectList
// Now rebuild the attributeList attribute value. This is an array
// of strings of the format "attrName<TAB>type<TAB>Description<TAB>Semantic".
//
MString tmpStr;
attributeList.clear();
cgfxAttrDefList::iterator it(effectList);
while (it)
{
cgfxAttrDef* aDef = *it;
tmpStr = aDef->fName;
tmpStr += "\t";
tmpStr += (int)aDef->fType;
tmpStr += "\t";
tmpStr += aDef->fDescription;
tmpStr += "\t";
tmpStr += aDef->fSemantic;
// Drop trailing tabs.
const char* bp = tmpStr.asChar();
const char* ep;
for ( ep = bp + tmpStr.length(); bp < ep; --ep )
if ( ep[-1] != '\t' )
break;
attributeList.append( MString( bp, (int)(ep - bp) ) );
++it;
}
}
catch ( cgfxShaderCommon::InternalError* )
{
throw;
}
catch (...)
{
M_CHECK( false );
}
} // cgfxAttrDef::updateNode
// Return true if initial value of 'this' is same as 'that'.
bool
cgfxAttrDef::isInitialValueEqual( const cgfxAttrDef& that ) const
{
if ( fStringDef != that.fStringDef )
return false;
const double* thisNumericDef = fNumericDef;
const double* thatNumericDef = that.fNumericDef;
if ( thisNumericDef == thatNumericDef )
return true;
// Make sure we don't proceed to test default colour values for
// texture attributes as the colour itself is meaningless!
//
if( fType == that.fType && fType >= kAttrTypeFirstTexture && fType <= kAttrTypeLastTexture)
return true;
if ( !thisNumericDef )
{
thisNumericDef = thatNumericDef;
thatNumericDef = fNumericDef;
}
if ( !thatNumericDef )
{
if ( fType == kAttrTypeMatrix )
{
thatNumericDef = &MMatrix::identity.matrix[0][0];
M_CHECK( fSize == 16 && that.fSize == 16 );
}
else
{
static const double d0[4] = {0.0, 0.0, 0.0, 0.0};
thatNumericDef = d0;
M_CHECK( fSize <= sizeof(d0)/sizeof(d0[0]) );
if ( fSize != that.fSize )
MGlobal::displayWarning( "CgFX attribute size mismatch" );
}
}
double eps = 0.0001;
int i;
for ( i = 0; i < fSize; ++i )
if ( thisNumericDef[ i ] + eps < thatNumericDef[ i ] ||
thatNumericDef[ i ] + eps < thisNumericDef[ i ] )
return false;
return true;
} // cgfxAttrDef::isInitialValueEqual
// Copy initial value from given attribute.
void
cgfxAttrDef::setInitialValue( const cgfxAttrDef& from )
{
if ( from.fNumericDef )
{
M_CHECK( fSize == from.fSize );
if ( !fNumericDef )
fNumericDef = new double[ fSize ];
memcpy( fNumericDef, from.fNumericDef, fSize * sizeof( *fNumericDef ) );
}
else
{
delete fNumericDef;
fStringDef = from.fStringDef;
}
} // cgfxAttrDef::setInitialValue
// Set attribute flag
void cgfxAttrDef::setAttributeFlags()
{
MFnAttribute attribute;
if(!attribute.setObject(fAttr))
return ;
switch (fType)
{
case kAttrTypeColor3:
case kAttrTypeColor4:
attribute.setKeyable( true );
attribute.setUsedAsColor( true );
attribute.setAffectsAppearance( true );
break;
case kAttrTypeBool:
case kAttrTypeInt:
case kAttrTypeFloat:
attribute.setKeyable( true );
attribute.setAffectsAppearance( true );
break;
case kAttrTypeVector2:
case kAttrTypeVector3:
case kAttrTypeVector4:
case kAttrTypeObjectDir:
case kAttrTypeWorldDir:
case kAttrTypeViewDir:
case kAttrTypeProjectionDir:
case kAttrTypeScreenDir:
case kAttrTypeObjectPos:
case kAttrTypeWorldPos:
case kAttrTypeViewPos:
case kAttrTypeProjectionPos:
case kAttrTypeScreenPos:
if( fDescription.length() > 0)
attribute.setKeyable( true );
attribute.setAffectsAppearance( true );
break;
case kAttrTypeString:
case kAttrTypeMatrix: // Create a generic matrix
attribute.setAffectsAppearance( true );
break;
case kAttrTypeWorldMatrix:
case kAttrTypeViewMatrix:
case kAttrTypeProjectionMatrix:
case kAttrTypeWorldViewMatrix:
case kAttrTypeWorldViewProjectionMatrix:
// These matricies are handled internally and have no attribute.
//
break;
case kAttrTypeColor1DTexture:
case kAttrTypeColor2DTexture:
case kAttrTypeColor3DTexture:
case kAttrTypeColor2DRectTexture:
case kAttrTypeNormalTexture:
case kAttrTypeBumpTexture:
case kAttrTypeCubeTexture:
case kAttrTypeEnvTexture:
case kAttrTypeNormalizationTexture:
/*if(!getTexturesByName())
// Although it's not strictly necessary, set this attribute
// to be a color so the user can will at least get the
// texture assignment button in the AE if for some reason
// our AE template is missing
attribute.setUsedAsColor( true );*/
attribute.setAffectsAppearance( true );
break;
#ifdef _WIN32
case kAttrTypeTime:
#endif
case kAttrTypeHardwareFogEnabled:
case kAttrTypeHardwareFogMode:
case kAttrTypeHardwareFogStart:
case kAttrTypeHardwareFogEnd:
case kAttrTypeHardwareFogDensity:
case kAttrTypeHardwareFogColor:
case kAttrTypeOther:
break;
default:
M_CHECK( false );
}
}
// Set Maya attributes to their initial values.
/* static */
void
cgfxAttrDef::initializeAttributes(
MObject& oNode,
const cgfxRCPtr<cgfxAttrDefList>& list,
bool bUndoing,
MDGModifier* dgMod)
{
MStatus status;
for ( cgfxAttrDefList::iterator it( list ); it; ++it )
{ // loop over cgfxAttrDefList
cgfxAttrDef* aDef = (*it);
if ( aDef->fAttr.isNull() ) // if no Maya attr
continue; // try next
bool bSetValue = bUndoing ? aDef->fInitOnUndo
: !aDef->fTweaked;
try
{
// Boolean
if ( aDef->fType == kAttrTypeBool )
{
if ( bSetValue )
aDef->setValue( oNode, aDef->fNumericDef && aDef->fNumericDef[0] );
}
// Texture node: must be check before both numeric (as a
// texture could be a float3 colour) and string (as it
// could also be a string)
else if( aDef->fType >= kAttrTypeFirstTexture &&
aDef->fType <= kAttrTypeLastTexture)
{
if ( bSetValue )
aDef->setTexture( oNode, aDef->fStringDef, dgMod);
}
// Numeric
else if ( fnNumeric.setObject( aDef->fAttr ) )
{ // numeric attr
// Constants for removing old bounds...
double vMin = -FLT_MAX;
double vMax = FLT_MAX;
if ( aDef->fType == kAttrTypeInt )
{
vMin = INT_MIN;
vMax = INT_MAX;
}
// Set or remove bounds.
switch ( aDef->fSize )
{ // switch to set/remove bounds
case 1:
if ( aDef->fNumericMin )
fnNumeric.setMin( aDef->fNumericMin[0] );
else if ( fnNumeric.hasMin() )
fnNumeric.setMin( vMin );
if ( aDef->fNumericMax )
fnNumeric.setMax( aDef->fNumericMax[0] );
else if ( fnNumeric.hasMax() )
fnNumeric.setMax( vMax );
if ( aDef->fNumericSoftMin )
fnNumeric.setSoftMin( aDef->fNumericSoftMin[0] );
else if ( fnNumeric.hasSoftMin() )
fnNumeric.setSoftMin( vMin );
if ( aDef->fNumericSoftMax )
fnNumeric.setSoftMax( aDef->fNumericSoftMax[0] );
else if ( fnNumeric.hasSoftMax() )
fnNumeric.setSoftMax( vMax );
break;
case 2:
if ( aDef->fNumericMin )
fnNumeric.setMin( aDef->fNumericMin[0],
aDef->fNumericMin[1] );
else if ( fnNumeric.hasMin() )
fnNumeric.setMin( vMin, vMin );
if ( aDef->fNumericMax )
fnNumeric.setMax( aDef->fNumericMax[0],
aDef->fNumericMax[1] );
else if ( fnNumeric.hasMax() )
fnNumeric.setMax( vMax, vMax );
break;
case 3:
case 4:
if ( aDef->fNumericMin )
fnNumeric.setMin( aDef->fNumericMin[0],
aDef->fNumericMin[1],
aDef->fNumericMin[2] );
else if ( fnNumeric.hasMin() )
fnNumeric.setMin( vMin, vMin, vMin );
if ( aDef->fNumericMax )
fnNumeric.setMax( aDef->fNumericMax[0],
aDef->fNumericMax[1],
aDef->fNumericMax[2] );
else if ( fnNumeric.hasMax() )
fnNumeric.setMax( vMax, vMax, vMax );
break;
default:
M_CHECK( false );
} // switch to set/remove bounds
// Set initial value.
// Use 0 if no initial value specified in .fx file.
if ( bSetValue )
{ // set numeric initial value
static const double d0[4] = {0.0, 0.0, 0.0, 0.0};
const double* pNumericDef = aDef->fNumericDef ? aDef->fNumericDef
: d0;
switch ( aDef->fSize )
{
case 1:
if ( aDef->fType == kAttrTypeInt )
aDef->setValue( oNode, (int)pNumericDef[0] );
else
aDef->setValue( oNode, (float)pNumericDef[0] );
break;
case 2:
aDef->setValue( oNode,
(float)pNumericDef[0],
(float)pNumericDef[1] );
break;
case 3:
aDef->setValue( oNode,
(float)pNumericDef[0],
(float)pNumericDef[1],
(float)pNumericDef[2] );
break;
case 4:
status = fnNumeric.setObject( aDef->fAttr2 );
M_CHECK( status );
if ( aDef->fNumericMin )
fnNumeric.setMin( aDef->fNumericMin[3] );
else if ( fnNumeric.hasMin() )
fnNumeric.setMin( vMin );
if ( aDef->fNumericMax )
fnNumeric.setMax( aDef->fNumericMax[3] );
else if ( fnNumeric.hasMax() )
fnNumeric.setMax( vMax );
aDef->setValue( oNode,
(float)pNumericDef[0],
(float)pNumericDef[1],
(float)pNumericDef[2],
(float)pNumericDef[3] );
break;
default:
M_CHECK( false );
} // switch ( aDef->fSize )
} // set numeric initial value
} // numeric attr
// String
else if ( fnTyped.setObject( aDef->fAttr ) &&
fnTyped.attrType() == MFnData::kString )
{
if ( bSetValue )
aDef->setValue( oNode, aDef->fStringDef );
}
// Matrix
else if ( fnMatrix.setObject( aDef->fAttr ) )
{
if ( !bSetValue )
{}
else if ( aDef->fNumericDef )
{
double* p = &m.matrix[0][0];
for ( int k = 0; k < 16; ++k )
p[ k ] = aDef->fNumericDef[ k ];
aDef->setValue( oNode, m );
}
else
aDef->setValue( oNode, MMatrix::identity );
}
}
catch ( cgfxShaderCommon::InternalError* e )
{
size_t ee = (size_t)e;
MFnDependencyNode fnNode( oNode );
MString sMsg = "(";
sMsg += (int)ee;
sMsg += ") cgfxShader node \"";
sMsg += fnNode.name();
sMsg += "\": unable to initialize attribute \"";
sMsg += aDef->fName;
sMsg += "\"";
}
} // loop over cgfxAttrDefList
} // cgfxAttrDef::initializeAttributes
// Clear all Maya attribute references in a cgfxAttrDefList.
// This should be called whenever the list is detached from
// the cgfxShader node, to avert an eventual exception in
// MObject::~MObject() in case the referenced Maya attribute
// happens to be deleted while the cgfxAttrDefList is in
// suspense on Maya's undo queue.
//
/* static */
void cgfxAttrDef::purgeMObjectCache(const cgfxRCPtr<cgfxAttrDefList>& list)
{
cgfxAttrDefList::iterator it( list );
for ( ; it; ++it )
{
cgfxAttrDef* aDef = (*it);
aDef->fAttr = MObject::kNullObj;
aDef->fAttr2 = MObject::kNullObj;
}
} // cgfxAttrDef::purgeMObjectCache
// Refresh Maya attribute references in a cgfxAttrDefList.
// This should be called whenever a saved list is re-attached
// to the cgfxShader node in the course of undo or redo.
//
/* static */
void cgfxAttrDef::validateMObjectCache(
const MObject& obCgfxShader,
const cgfxRCPtr<cgfxAttrDefList>& list)
{
MStatus status;
MString sName2;
MFnDependencyNode fnNode( obCgfxShader, &status );
cgfxAttrDefList::iterator it( list );
for ( ; it; ++it )
{
cgfxAttrDef* aDef = (*it);
aDef->fAttr = fnNode.attribute( aDef->fName, &status );
// 4-element vectors use an extra attribute for the 4th element.
const char* suffix = aDef->getExtraAttrSuffix();
if ( suffix )
aDef->fAttr2 = fnNode.attribute( aDef->fName + suffix, &status );
}
} // cgfxAttrDef::validateMObjectCache
// Return suffix for Color4/Vector4 extra attribute, or NULL.
const char*
cgfxAttrDef::getExtraAttrSuffix() const
{
if ( fSize == 4 )
return compoundAttrSuffixes( fType )[ 3 ];
return NULL;
} // cgfxAttrDef::getExtraAttrSuffix
// Get a string representation of a cgfxAttrType
//
/* static */
const char* cgfxAttrDef::typeName( cgfxAttrType type )
{
#define CASE(name) case kAttrType##name: return #name
switch (type)
{
default: // Fall through into case unknown
CASE(Unknown);
CASE(Bool);
CASE(Int);
CASE(Float);
CASE(String);
CASE(Vector2);
CASE(Vector3);
CASE(Vector4);
CASE(ObjectDir);
CASE(WorldDir);
CASE(ViewDir);
CASE(ProjectionDir);
CASE(ScreenDir);
CASE(ObjectPos);
CASE(WorldPos);
CASE(ViewPos);
CASE(ProjectionPos);
CASE(ScreenPos);
CASE(Color3);
CASE(Color4);
CASE(Matrix);
CASE(WorldMatrix);
CASE(ViewMatrix);
CASE(ProjectionMatrix);
CASE(WorldViewMatrix);
CASE(WorldViewProjectionMatrix);
CASE(Color1DTexture);
CASE(Color2DTexture);
CASE(Color3DTexture);
CASE(Color2DRectTexture);
CASE(NormalTexture);
CASE(BumpTexture);
CASE(CubeTexture);
CASE(EnvTexture);
CASE(NormalizationTexture);
#ifdef _WIN32
CASE(Time);
#endif
CASE(HardwareFogEnabled);
CASE(HardwareFogMode);
CASE(HardwareFogStart);
CASE(HardwareFogEnd);
CASE(HardwareFogDensity);
CASE(HardwareFogColor);
CASE(Other);
}
}
/* static */
const char**
cgfxAttrDef::compoundAttrSuffixes( cgfxAttrType eAttrType )
{
static const char* simple[] = { NULL, NULL, NULL, NULL, NULL };
static const char* vector[] = { "X", "Y", "Z", "W", NULL };
static const char* color[] = { "R", "G", "B", "Alpha", NULL };
const char** p;
switch ( eAttrType )
{
case kAttrTypeVector2:
case kAttrTypeVector3:
case kAttrTypeVector4:
case kAttrTypeObjectDir:
case kAttrTypeWorldDir:
case kAttrTypeViewDir:
case kAttrTypeProjectionDir:
case kAttrTypeScreenDir:
case kAttrTypeObjectPos:
case kAttrTypeWorldPos:
case kAttrTypeViewPos:
case kAttrTypeProjectionPos:
case kAttrTypeScreenPos:
p = vector;
break;
case kAttrTypeColor3:
case kAttrTypeColor4:
p = color;
break;
default:
p = simple;
break;
}
return p;
} // cgfxAttrDef::compoundAttrSuffixes
// Methods to get attribute values
//
void
cgfxAttrDef::getValue( MObject& oNode, bool& value ) const
{
MStatus status;
MPlug plug(oNode, fAttr);
status = plug.getValue(value);
M_CHECK( status );
}
void
cgfxAttrDef::getValue( MObject& oNode, int& value ) const
{
MStatus status;
MPlug plug(oNode, fAttr);
status = plug.getValue(value);
M_CHECK( status );
}
void
cgfxAttrDef::getValue( MObject& oNode, float& value ) const
{
MStatus status;
MPlug plug(oNode, fAttr);
status = plug.getValue(value);
if( fUnits != MDistance::kInvalid)
{
value = (float)MDistance( value, fUnits).as( MDistance::internalUnit());
}
M_CHECK( status );
}
void
cgfxAttrDef::getValue( MObject& oNode, MString& value ) const
{
MStatus status;
MPlug plug(oNode, fAttr);
status = plug.getValue(value);
M_CHECK( status );
}
void
cgfxAttrDef::getValue( MObject& oNode, float& v1, float& v2 ) const
{
MStatus status;
MPlug plug(oNode, fAttr);
MObject oData;
status = plug.getValue(oData);
M_CHECK( status );
MFnNumericData fnData(oData, &status);
M_CHECK( status );
status = fnData.getData(v1, v2);
M_CHECK( status );
if( fUnits != MDistance::kInvalid)
{
v1 = (float)MDistance( v1, fUnits).as( MDistance::internalUnit());
v2 = (float)MDistance( v2, fUnits).as( MDistance::internalUnit());
}
}
void
cgfxAttrDef::getValue( MObject& oNode,
float& v1, float& v2, float& v3 ) const
{
MStatus status;
MPlug plug(oNode, fAttr);
MObject oData;
status = plug.getValue(oData);
M_CHECK( status );
MFnNumericData fnData(oData, &status);
M_CHECK( status );
status = fnData.getData(v1, v2, v3);
M_CHECK( status );
if( fUnits != MDistance::kInvalid)
{
v1 = (float)MDistance( v1, fUnits).as( MDistance::internalUnit());
v2 = (float)MDistance( v2, fUnits).as( MDistance::internalUnit());
v3 = (float)MDistance( v3, fUnits).as( MDistance::internalUnit());
}
}
void
cgfxAttrDef::getValue( MObject& oNode,
float& v1, float& v2, float& v3, float& v4 ) const
{
MStatus status;
MPlug plug(oNode, fAttr);
MPlug plug2(oNode, fAttr2);
MObject oData;
status = plug.getValue(oData);
M_CHECK( status );
MFnNumericData fnData(oData, &status);
M_CHECK( status );
status = fnData.getData(v1, v2, v3);
M_CHECK( status );
// Get the 4th value from the extra attribute.
//
status = plug2.getValue(v4);
M_CHECK( status );
if( fUnits != MDistance::kInvalid)
{
v1 = (float)MDistance( v1, fUnits).as( MDistance::internalUnit());
v2 = (float)MDistance( v2, fUnits).as( MDistance::internalUnit());
v3 = (float)MDistance( v3, fUnits).as( MDistance::internalUnit());
v4 = (float)MDistance( v4, fUnits).as( MDistance::internalUnit());
}
}
void
cgfxAttrDef::getValue( MObject& oNode, MMatrix& value ) const
{
MStatus status;
MPlug plug(oNode, fAttr);
MObject oData;
status = plug.getValue(oData);
M_CHECK( status );
MFnMatrixData fnData(oData, &status);
M_CHECK( status );
value = fnData.matrix(&status);
M_CHECK( status );
}
void
cgfxAttrDef::getValue( MObject& oNode, MImage& value ) const
{
MStatus status = MS::kFailure;
MPlug plug(oNode, fAttr);
if (fType >= kAttrTypeFirstTexture &&
fType <= kAttrTypeLastTexture)
{
MPlugArray plugArray;
plug.connectedTo(plugArray, true, false, &status);
M_CHECK( status );
if (plugArray.length() != 1)
{
M_CHECK( status );
}
MPlug srcPlug = plugArray[0];
MObject oSrcNode = srcPlug.node();
// OutputDebugStrings("Source texture object = ", oSrcNode.apiTypeStr());
value.release();
status = value.readFromTextureNode(oSrcNode);
M_CHECK( status );
}
M_CHECK( status );
}
// Get the source of an attribute value
//
void
cgfxAttrDef::getSource( MObject& oNode, MPlug& src) const
{
MStatus status = MS::kFailure;
MPlug plug(oNode, fAttr);
MPlugArray plugArray;
plug.connectedTo(plugArray, true, false, &status);
M_CHECK( status && plugArray.length() <= 1);
if (plugArray.length() == 1)
src = plugArray[0];
}
// Methods to set attribute values
//
void
cgfxAttrDef::setValue( MObject& oNode, bool value )
{
MStatus status;
MPlug plug(oNode, fAttr);
status = plug.setValue(value);
M_CHECK( status );
}
void
cgfxAttrDef::setValue( MObject& oNode, int value )
{
MStatus status;
MPlug plug(oNode, fAttr);
status = plug.setValue(value);
M_CHECK( status );
}
void
cgfxAttrDef::setValue( MObject& oNode, float value )
{
MStatus status;
MPlug plug(oNode, fAttr);
status = plug.setValue(value);
M_CHECK( status );
}
void
cgfxAttrDef::setValue( MObject& oNode, const MString& value )
{
MStatus status;
MPlug plug(oNode, fAttr);
status = plug.setValue((MString &)value);
M_CHECK( status );
}
void
cgfxAttrDef::setValue( MObject& oNode, float v1, float v2 )
{
MStatus status;
MPlug plug(oNode, fAttr);
MObject oData = fnData.create(MFnNumericData::k2Float, &status);
M_CHECK( status );
fnData.setData(v1, v2);
status = plug.setValue(oData);
M_CHECK( status );
}
void
cgfxAttrDef::setValue( MObject& oNode, float v1, float v2, float v3 )
{
MStatus status;
MPlug plug(oNode, fAttr);
MObject oData = fnData.create(MFnNumericData::k3Float, &status);
M_CHECK( status );
fnData.setData(v1, v2, v3);
status = plug.setValue(oData);
M_CHECK( status );
}
void
cgfxAttrDef::setValue( MObject& oNode, float v1, float v2, float v3, float v4 )
{
MStatus status;
MPlug plug(oNode, fAttr);
MPlug plug2(oNode, fAttr2);
MObject oData = fnData.create(MFnNumericData::k3Float, &status);
M_CHECK( status );
fnData.setData(v1, v2, v3);
status = plug.setValue(oData);
M_CHECK( status );
status = plug2.setValue(v4);
M_CHECK( status );
}
void
cgfxAttrDef::setValue( MObject& oNode, const MMatrix& v )
{
MStatus status;
MFnMatrixData fnData;
MObject oData = fnData.create( v, &status );
M_CHECK( status );
MPlug plug( oNode, fAttr );
status = plug.setValue( oData );
M_CHECK( status );
}
// Utility to check if a node is used by any nodes other than us
//
bool isUsedElsewhere( MObject node, MObject user)
{
for( MItDependencyGraph iter( node); !iter.isDone(); iter.next())
{
// If there is a downstream connection to something other than our shader ...
//
if( iter.thisNode() != node)
{
if( iter.thisNode() != user)
{
// And that connection uses anything other than the message attribute
// of the texture (which is used to connect the texture to the
// global texture list)
//
iter.thisPlug().connectedTo( src, true, false);
if( src.length() == 1 && src[ 0].partialName() != "msg")
{
// Finally, check this isn't just the swatch renderer taking
// a quick look at this node
//
MFnDependencyNode dgFn( iter.thisNode());
if( dgFn.name() != "swatchShadingGroup")
{
// Then we're not the only user of this node!
//
//cout<<"Not removing node due to connection<<src[0].name().asChar()<<" to "<<iter.thisPlug().name().asChar()<<endl;
return true;
}
}
}
// If this downstream connection is to another shader, or a
// message connection, don't follow the connection any further
//
iter.prune();
}
}
return false;
}
void
cgfxAttrDef::setTexture( MObject& oNode, const MString& value, MDGModifier* dgMod)
{
MStatus status;
MPlug plug(oNode, fAttr);
// Is this a node or name based texture?
//
MFnAttribute attrFn( fAttr);
if( attrFn.isUsedAsColor() )
{
// Node based texture.
// Remove any existing texture
//
if( plug.isConnected())
{
plug.connectedTo( src, true, false, &status);
M_CHECK( status );
if( src.length() > 0)
{
MObject textureNode = src[ 0].node();
// If no other nodes use this texture, we can remove it to
// avoid cluttering up the scene with unused texture nodes
//
if( !isUsedElsewhere( textureNode, oNode))
{
// We are the only user of this texture node so we
// can delete it. Before we do that though, are
// we the only user of the placement node too?
//
MFnDependencyNode textureFn( textureNode);
MPlug uvPlug = textureFn.findPlug( "uv", &status);
if( status == MS::kSuccess)
{
MPlugArray placementNode;
uvPlug.connectedTo( placementNode, true, false, &status);
M_CHECK( status );
// If we are the only user of the placement node, delete
// it as well
//
if( placementNode.length() > 0 &&
!isUsedElsewhere( placementNode[ 0].node(), textureNode))
{
M_CHECK( dgMod->deleteNode( placementNode[ 0].node()) );
}
}
// Delete the texture node
//
M_CHECK( dgMod->deleteNode( textureNode) );
}
else
{
// We're not deleting the texture, so just disconnect it
//
M_CHECK( dgMod->disconnect( src[ 0], plug) );
}
}
}
// Do we have a (default) value to set?
//
if( value.length() > 0)
{
// Resolve the texture value as either an absolute or
// project relative path. Even though we re-resolve paths
// when loading textures ourselves, if we want the Maya
// texture swatch to display, Maya needs to be able to
// find the texture as well.
//
MString relativePath = cgfxFindFile( value, true );
// If we didn't find it, just leave the original path (even
// though it wont work)
//
if( relativePath.length() == 0) relativePath = value;
// Create a new file texture and placement node
// Use the MEL commands (as opposed to dgMod.createNode) as these
// correctly hook up the rendering message connections so our
// nodes show up in the hypershade etc.
//
MObject textureNode, placementNode;
MSelectionList originalSelection, newlyCreatedNode;
MGlobal::getActiveSelectionList( originalSelection);
dgMod->commandToExecute( "shadingNode -asTexture file" );
// the next operation getActiveSelectionList needs the node indeed created, so force doIt.
dgMod->doIt();
MGlobal::getActiveSelectionList( newlyCreatedNode);
M_CHECK( newlyCreatedNode.length() > 0 && newlyCreatedNode.getDependNode( 0, textureNode));
dgMod->commandToExecute( "shadingNode -asUtility place2dTexture" );
// the next operation getActiveSelectionList needs the node indeed created, so force doIt.
dgMod->doIt();
MGlobal::getActiveSelectionList( newlyCreatedNode);
M_CHECK( newlyCreatedNode.length() > 0 && newlyCreatedNode.getDependNode( 0, placementNode));
MGlobal::setActiveSelectionList( originalSelection);
MFnDependencyNode fileTextureFn( textureNode, &status);
M_CHECK( status );
MFnDependencyNode placementFn( placementNode, &status);
M_CHECK( status );
// Connect the placement node to the file texture node
//
M_CHECK( dgMod->connect( placementFn.findPlug( "coverage"), fileTextureFn.findPlug( "coverage")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "translateFrame"), fileTextureFn.findPlug( "translateFrame")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "rotateFrame"), fileTextureFn.findPlug( "rotateFrame")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "mirrorU"), fileTextureFn.findPlug( "mirrorU")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "mirrorV"), fileTextureFn.findPlug( "mirrorV")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "stagger"), fileTextureFn.findPlug( "stagger")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "wrapU"), fileTextureFn.findPlug( "wrapU")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "wrapV"), fileTextureFn.findPlug( "wrapV")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "repeatUV"), fileTextureFn.findPlug( "repeatUV")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "offset"), fileTextureFn.findPlug( "offset")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "rotateUV"), fileTextureFn.findPlug( "rotateUV")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "noiseUV"), fileTextureFn.findPlug( "noiseUV")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "vertexUvOne"), fileTextureFn.findPlug( "vertexUvOne")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "vertexUvTwo"), fileTextureFn.findPlug( "vertexUvTwo")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "vertexUvThree"), fileTextureFn.findPlug( "vertexUvThree")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "vertexCameraOne"), fileTextureFn.findPlug( "vertexCameraOne")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "outUV"), fileTextureFn.findPlug( "uv")) );
M_CHECK( dgMod->connect( placementFn.findPlug( "outUvFilterSize"), fileTextureFn.findPlug( "uvFilterSize")) );
// Connect our file texture node to our shader attribute, then set the texture
//
M_CHECK( dgMod->connect( fileTextureFn.findPlug( "outColor"), plug) );
status = fileTextureFn.findPlug( "fileTextureName").setValue( relativePath);
M_CHECK( status );
}
M_CHECK( dgMod->doIt() );
}
else
{
// Simple String attribute - but do a safety check to be sure
//
if( fnTyped.setObject( fAttr ) &&
fnTyped.attrType() == MFnData::kString )
{
status = plug.setValue((MString &)value);
M_CHECK( status );
}
}
}
void cgfxAttrDef::setUnitsToInternal( CGparameter& cgParameter )
{
// If the units are converted to internal, return
if (fIsConvertedToInternal) return;
CGannotation cgAnnotation = cgGetFirstParameterAnnotation(cgParameter);
while (cgAnnotation)
{
const char* annotationName = cgGetAnnotationName(cgAnnotation);
const char* annotationValue = cgGetStringAnnotationValue(cgAnnotation);
if( stricmp( annotationName, "units" ) == 0)
{
MString unit(&fSymbol);
unit += annotationValue;
// Notice: _XXX is a internal unit
cgSetStringAnnotation(cgAnnotation, unit.asChar());
// Make sure if the bind is called more than one time, no need to set to internal again
fIsConvertedToInternal = true;
return;
}
cgAnnotation = cgGetNextAnnotation(cgAnnotation);
}
}
//--------------------------------------------------------------------//
// cgfxAttrDefList //
//--------------------------------------------------------------------//
cgfxAttrDefList::iterator
cgfxAttrDefList::findInsensitive( const MString& name )
{
const char* pName = name.asChar();
unsigned lName = name.length();
iterator it( *this );
for ( ; it; ++it )
if ( lName == (*it)->fName.length() &&
0 == stricmp( pName, (*it)->fName.asChar() ) )
break;
return it;
}; // cgfxAttrDefList::findInsensitive
void cgfxAttrDefList::release()
{
--refcount;
if (refcount <= 0)
{
M_CHECK( refcount == 0 );
delete this;
}
};
void cgfxAttrDefList::releaseTextures()
{
iterator it(*this);
while (it)
{
(*it)->release();
++it;
}
}
void cgfxAttrDefList::dump(const char* name)
{
fprintf(stderr, "Dumping cgfxAttrDefList %s : \n", name);
for (cgfxAttrDefList::iterator it = begin(); it; ++it) {
cgfxAttrDef* aDef = (*it);
fprintf(
stderr,
" name=%s, type=%s, size=%d, attr=%s, hint=%d, attr2=%s, def=%s, desc=%s, semantic=%s, CGParameter=0x%p\n",
aDef->fName.asChar(),
cgfxAttrDef::typeName(aDef->fType),
aDef->fSize,
aDef->fAttr.apiTypeStr(),
aDef->fHint,
aDef->fAttr2.apiTypeStr(),
aDef->fStringDef.asChar(),
aDef->fDescription.asChar(),
aDef->fSemantic.asChar(),
aDef->fParameterHandle
);
}
}