#include "cgfxShaderCommon.h"
#include "cgfxShaderCmd.h"
#include "cgfxShaderNode.h"
#include "cgfxFindImage.h"
#include <maya/MArgDatabase.h>
#include <maya/MCommandResult.h>
#include <maya/MDagPath.h>
#include <maya/MFeedbackLine.h>
#include <maya/MFnDagNode.h>
#include <maya/MGlobal.h>
#include <Cg/cg.h>
#if defined(WIN32) || defined(LINUX)
#include <GL/gl.h>
#else
#if !OSMac_X86_64_
#include <AGL/agl.h>
#else
#include <OpenGL/gl.h>
#endif
#endif
#include <GL/glext.h>
#define kMaxTexCoordsFlag "-mtc"
#define kMaxTexCoordsFlagLong "-maxTexCoords"
#define kPluginPathFlag "-pp"
#define kPluginPathFlagLong "-pluginPath"
#define kFxFlag "-fx"
#define kFxFlagLong "-fxFile"
#define kFxTechniqueFlag "-t"
#define kFxTechniqueFlagLong "-technique"
#define kNameFlag "-n"
#define kNameFlagLong "-name"
#define kListTechniquesFlag "-lt"
#define kListTechniquesFlagLong "-listTechniques"
#define kListParametersFlag "-lp"
#define kListParametersFlagLong "-listParameters"
#define kParameterFlag "-p"
#define kParameterFlagLong "-parameter"
#define kTexCoordSourceFlag "-tcs"
#define kTexCoordSourceFlagLong "-texCoordSource"
#if MAYA_API_VERSION >= 700
#define kColorSourceFlag "-cs"
#define kColorSourceFlagLong "-colorSource"
#endif
#define kEmptyUVFlag "-euv"
#define kEmptyUVFlagLong "-emptyUV"
#define kEmptyUVShapesFlag "-eus"
#define kEmptyUVShapesFlagLong "-emptyUVShapes"
#define kCaseInsensitiveFlag "-ci"
#define kCaseInsensitiveFlagLong "-caseInsensitive"
#define kDescriptionFlag "-des"
#define kDescriptionFlagLong "-description"
MString cgfxShaderCmd::sPluginPath;
MStatus
cgfxShaderCmd::doIt( const MArgList& args )
{
MStatus stat;
try
{
stat = doCmd( args );
}
catch ( cgfxShaderCommon::InternalError* e )
{
reportInternalError( __FILE__, (size_t)e );
stat = MS::kFailure;
}
catch ( ... )
{
reportInternalError( __FILE__, __LINE__ );
stat = MS::kFailure;
}
return stat;
}
MStatus
cgfxShaderCmd::redoIt()
{
#ifdef KH_DEBUG
MString ss = " .. Redo ";
ss += fArgString;
ss += "\n";
::OutputDebugString( ss.asChar() );
#endif
MStatus stat;
try
{
MObject oNode;
stat = fNodeSelection.getDependNode( 0, oNode );
M_CHECK( stat );
MFnDependencyNode fnNode( oNode, &stat );
M_CHECK( stat && fnNode.typeId() == cgfxShaderNode::sId );
cgfxShaderNode* pNode = (cgfxShaderNode*)fnNode.userNode();
M_CHECK( pNode );
stat = redoCmd( oNode, fnNode, pNode );
}
catch ( cgfxShaderCommon::InternalError* e )
{
reportInternalError( __FILE__, (size_t)e );
stat = MS::kFailure;
}
catch ( ... )
{
reportInternalError( __FILE__, __LINE__ );
stat = MS::kFailure;
}
#ifdef KH_DEBUG
ss = " .. redone\n";
::OutputDebugString( ss.asChar() );
#endif
return stat;
}
MStatus
cgfxShaderCmd::undoIt()
{
#ifdef KH_DEBUG
MString ss = " .. Undo ";
ss += fArgString;
ss += "\n";
::OutputDebugString( ss.asChar() );
#endif
MStatus stat;
try
{
stat = undoCmd();
}
catch ( cgfxShaderCommon::InternalError* e )
{
reportInternalError( __FILE__, (size_t)e );
stat = MS::kFailure;
}
catch ( ... )
{
reportInternalError( __FILE__, __LINE__ );
stat = MS::kFailure;
}
#ifdef KH_DEBUG
ss = " .. undone\n";
::OutputDebugString( ss.asChar() );
#endif
return stat;
}
MStatus
cgfxShaderCmd::doCmd(const MArgList& args)
{
#if defined(_WIN32) && defined(CGFX_DEBUG_MEMORY)
if (tmpFlag == -1)
{
tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF;
tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
_CrtSetDbgFlag( tmpFlag );
}
#endif
MStatus status;
MSelectionList selList;
MObject oNode;
MString sResult;
MStringArray saResult;
MString sFeedback;
MString sTemp;
MString sWho = "cgfxShader";
status = parseArgs(args, selList);
if (!status)
{
return status;
}
if ( fPluginPath )
{
setResult( sPluginPath );
return MS::kSuccess;
}
if ( fMaxTexCoords )
{
GLint mtc = 0;
M3dView vw = M3dView::active3dView( &status );
if ( status &&
vw.beginGL() )
{
glGetIntegerv( GL_MAX_TEXTURE_COORDS_ARB, &mtc );
GLint mic = 0;
glGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &mic );
if (mic < mtc)
mtc = mic;
if ( mtc < 1 )
mtc = 1;
else if ( mtc > CGFXSHADERNODE_GL_TEXTURE_MAX )
mtc = CGFXSHADERNODE_GL_TEXTURE_MAX;
vw.endGL();
}
setResult( (int)mtc );
return MS::kSuccess;
}
MFnDependencyNode fnNode;
cgfxShaderNode* pNode = NULL;
if ( fIsEdit || fIsQuery )
{
if (selList.length() != 1)
{
status = MS::kNotFound;
return status;
}
MStringArray tmpList;
selList.getSelectionStrings(tmpList);
fNodeName = tmpList[0];
if ( fNodeName.length() )
{
sWho += " \"";
sWho += fNodeName;
sWho += "\"";
}
status = selList.getDependNode(0, oNode);
if (!status)
{
return status;
}
status = fnNode.setObject( oNode );
if (!status)
{
status.perror("cgfxShader");
return status;
}
if (fnNode.typeId() != cgfxShaderNode::sId)
{
status = MS::kInvalidParameter;
status.perror("cgfxShader");
return status;
}
pNode = (cgfxShaderNode*)fnNode.userNode();
if (!pNode)
{
status = MS::kInvalidParameter;
status.perror("cgfxShader");
return status;
}
}
if ( fListTechniques )
{
setResult( pNode->getTechniqueList() );
return status;
}
if ( fListParameters )
{
cgfxAttrDefList* list = cgfxAttrDef::attrsFromNode( oNode );
for ( cgfxAttrDefList::iterator it = list; it; ++it )
{
cgfxAttrDef* aDef = *it;
if ( fDescription )
{
sResult = aDef->fName.length() ? aDef->fName : " ";
sResult += "\t";
sTemp = aDef->typeName();
sResult += sTemp.length() ? sTemp : " ";
sResult += "\t";
sResult += aDef->fSemantic.length() ? aDef->fSemantic : " ";
sResult += "\t";
sResult += aDef->fDescription.length() ? aDef->fDescription : " ";
sResult += "\t";
const char* suffix = aDef->getExtraAttrSuffix();
sResult += suffix ? suffix : " ";
}
else
sResult = aDef->fName;
saResult.append( sResult );
}
setResult( saResult );
return status;
}
if ( fParameterName.length() > 0 )
{
cgfxAttrDefList* list = cgfxAttrDef::attrsFromNode( oNode );
cgfxAttrDefList::iterator it;
if ( fCaseInsensitive )
it = list->findInsensitive( fParameterName );
else
it = list->find( fParameterName );
if ( fDescription )
{
if ( it )
{
cgfxAttrDef* aDef = *it;
saResult.append( aDef->fName );
saResult.append( aDef->typeName() );
saResult.append( aDef->fSemantic );
saResult.append( aDef->fDescription );
const char* suffix = aDef->getExtraAttrSuffix();
saResult.append( suffix ? suffix : "" );
}
setResult( saResult );
}
else
{
if ( it )
sResult = (*it)->typeName();
setResult( sResult );
}
return status;
}
if ( fEmptyUV )
{
setResult( pNode->getEmptyUVSets() );
return MS::kSuccess;
}
if ( fEmptyUVShapes )
{
const MObjectArray& oaShapes = pNode->getEmptyUVSetShapes();
MFnDagNode fnDagNode;
MDagPath dpShape;
for ( unsigned iShape = 0; iShape < oaShapes.length(); ++iShape )
{
fnDagNode.setObject( oaShapes[ iShape ] );
fnDagNode.getPath( dpShape );
saResult.append( dpShape.partialPathName() );
}
setResult( saResult );
return MS::kSuccess;
}
if ( fTexCoordSource )
{
setResult( pNode->getTexCoordSource() );
return MS::kSuccess;
}
#if MAYA_API_VERSION >= 700
if ( fColorSource )
{
setResult( pNode->getColorSource() );
return MS::kSuccess;
}
#endif
if ( fIsQuery )
return MS::kInvalidParameter;
if (fNewFxFile.length() > 0)
{
const char* errors = 0;
MString file = cgfxFindFile(fNewFxFile);
MString projectFile = cgfxFindFile(fNewFxFile, true);
pNode->setShaderFxFileChanged( true );
MStringArray fileOptions;
cgfxGetFxIncludePath( file, fileOptions );
const char *opts[_CGFX_PLUGIN_MAX_COMPILER_ARGS_];
unsigned int numOpts = fileOptions.length();
if (numOpts)
{
numOpts = (numOpts > _CGFX_PLUGIN_MAX_COMPILER_ARGS_) ? _CGFX_PLUGIN_MAX_COMPILER_ARGS_ : numOpts;
for (unsigned int i=0; i<numOpts; i++)
{
opts[i] = fileOptions[i].asChar();
}
opts[numOpts] = NULL;
}
fNewEffect = cgCreateEffectFromFile(cgfxShaderNode::sCgContext, file.asChar(), opts);
if (fNewEffect)
{
const MGlobal::MMayaState mayaState = MGlobal::mayaState(&status);
if ( !status ) return status;
if ( mayaState == MGlobal::kBatch ) return MS::kSuccess;
fNewFxFile = projectFile;
M3dView view = M3dView::active3dView();
if (!view.beginGL())
{
MString es = "There is no active view to bind " + sWho + " to.";
MGlobal::displayWarning( es );
return MS::kSuccess;
}
view.endGL();
}
if (fNewEffect)
{
sFeedback = sWho;
sFeedback += " loaded effect \"";
sFeedback += file;
sFeedback += "\"";
MGlobal::displayInfo( sFeedback );
}
else
{
if ( errors )
MGlobal::displayError( errors );
sFeedback = sWho;
sFeedback += " unable to load effect \"";
sFeedback += file.length() ? file : fNewFxFile;
sFeedback += "\"";
MGlobal::displayError( sFeedback );
return MS::kFailure;
}
}
if (fNewTechnique.length() == 0 && pNode)
fNewTechnique = pNode->getTechnique();
fDagMod = new MDGModifier;
if ( !fIsEdit )
{
oNode = fDagMod->createNode(cgfxShaderNode::sId, &status);
M_CHECK( status );
if ( fNodeName.length() > 0 )
{
status = fDagMod->renameNode(oNode, fNodeName);
M_CHECK( status );
}
status = fnNode.setObject( oNode );
M_CHECK( status && fnNode.typeId() == cgfxShaderNode::sId );
pNode = (cgfxShaderNode*)fnNode.userNode();
M_CHECK( pNode );
status = MGlobal::getActiveSelectionList( fOldSelection );
M_CHECK( status );
}
cgfxAttrDef::updateNode( fNewEffect,
pNode,
fDagMod,
fNewAttrDefList,
fNewAttributeList );
status = fNodeSelection.add( oNode );
M_CHECK( status );
fOldFxFile = pNode->shaderFxFile();
fOldTechnique = pNode->getTechnique();
pNode->getAttributeList( fOldAttributeList );
fOldEffect = pNode->effect();
fOldAttrDefList = pNode->attrDefList();
if ( fOldAttrDefList )
fOldAttrDefList->addRef();
return redoCmd( oNode, fnNode, pNode );
}
MStatus
cgfxShaderCmd::redoCmd( MObject& oNode,
MFnDependencyNode& fnNode,
cgfxShaderNode* pNode )
{
MStatus status;
pNode->setAttrDefList( NULL );
status = fDagMod->doIt();
M_CHECK( status );
pNode->setAttributeList(fNewAttributeList);
pNode->setAttrDefList(fNewAttrDefList);
pNode->setEffect(fNewEffect);
cgfxAttrDef::initializeAttributes( oNode, fNewAttrDefList, false, fDagMod);
fnNode.findPlug( pNode->sShader ).setValue( fNewFxFile );
fnNode.findPlug( pNode->sTechnique ).setValue( fNewTechnique );
fNewTechnique = pNode->getTechnique();
if ( !fIsEdit )
{
fNodeName = fnNode.name();
MSelectionList selList;
selList.add(oNode);
MGlobal::setActiveSelectionList(selList);
}
return MS::kSuccess;
}
MStatus
cgfxShaderCmd::undoCmd()
{
MStatus status;
MObject oNode;
status = fNodeSelection.getDependNode(0, oNode);
M_CHECK( status );
MFnDependencyNode fnNode( oNode, &status );
M_CHECK( status && fnNode.typeId() == cgfxShaderNode::sId );
cgfxShaderNode* pNode = (cgfxShaderNode*)fnNode.userNode();
M_CHECK( pNode );
pNode->setAttrDefList( NULL );
status = fDagMod->undoIt();
M_CHECK( status );
if ( fIsEdit )
{
pNode->setEffect( fOldEffect );
pNode->setAttrDefList( fOldAttrDefList );
pNode->setAttributeList( fOldAttributeList );
cgfxAttrDef::initializeAttributes( oNode, fOldAttrDefList, true, fDagMod);
fnNode.findPlug( pNode->sShader ).setValue( fOldFxFile );
fnNode.findPlug( pNode->sTechnique ).setValue( fOldTechnique );
}
else
{
MGlobal::setActiveSelectionList( fOldSelection );
}
return MS::kSuccess;
}
MSyntax cgfxShaderCmd::newSyntax()
{
MSyntax syntax;
syntax.enableEdit();
syntax.enableQuery();
syntax.addFlag( kPluginPathFlag, kPluginPathFlagLong );
syntax.addFlag( kMaxTexCoordsFlag, kMaxTexCoordsFlagLong );
syntax.addFlag(kFxFlag, kFxFlagLong, MSyntax::kString);
syntax.addFlag(kFxTechniqueFlag, kFxTechniqueFlagLong, MSyntax::kString);
syntax.addFlag( kListTechniquesFlag, kListTechniquesFlagLong );
syntax.addFlag(kNameFlag, kNameFlagLong, MSyntax::kString);
syntax.addFlag(kListParametersFlag, kListParametersFlagLong);
syntax.addFlag(kParameterFlag, kParameterFlagLong, MSyntax::kString);
syntax.addFlag( kEmptyUVFlag, kEmptyUVFlagLong );
syntax.addFlag( kEmptyUVShapesFlag, kEmptyUVShapesFlagLong );
syntax.addFlag( kTexCoordSourceFlag, kTexCoordSourceFlagLong );
#if MAYA_API_VERSION >= 700
syntax.addFlag( kColorSourceFlag, kColorSourceFlagLong );
#endif
syntax.addFlag( kCaseInsensitiveFlag, kCaseInsensitiveFlagLong );
syntax.addFlag( kDescriptionFlag, kDescriptionFlagLong );
syntax.setObjectType(MSyntax::kSelectionList, 0, 1);
return syntax;
}
void* cgfxShaderCmd::creator()
{
return new cgfxShaderCmd();
}
cgfxShaderCmd::cgfxShaderCmd()
: fIsEdit( false )
, fIsQuery( false )
, fMaxTexCoords( false )
, fPluginPath( false )
, fEmptyUV( false )
, fEmptyUVShapes( false )
, fListParameters(false)
, fListTechniques( false )
, fTexCoordSource( false )
#if MAYA_API_VERSION >= 700
, fColorSource( false )
#endif
, fCaseInsensitive( false )
, fDescription( false )
, fOldEffect(0)
, fOldAttrDefList(0)
, fNewEffect(0)
, fNewAttrDefList(0)
, fDagMod(0)
{ }
cgfxShaderCmd::~cgfxShaderCmd()
{
try
{
#ifdef KH_DEBUG
if ( !fIsQuery )
{
MString ss = " .. ~cmd ";
ss += fArgString;
ss += "\n";
::OutputDebugString( ss.asChar() );
}
#endif
if (fOldAttrDefList)
{
fOldAttrDefList->release();
fOldAttrDefList = 0;
}
if (fNewAttrDefList)
{
fNewAttrDefList->release();
fNewAttrDefList = 0;
}
delete fDagMod;
}
catch ( cgfxShaderCommon::InternalError* e )
{
reportInternalError( __FILE__, (size_t)e );
}
catch ( ... )
{
reportInternalError( __FILE__, __LINE__ );
}
}
bool cgfxShaderCmd::isUndoable() const
{
return !fIsQuery;
}
MStatus cgfxShaderCmd::parseArgs(const MArgList& args, MSelectionList& selList)
{
MStatus status;
MString sMsg;
selList.clear();
fArgString.clear();
for ( unsigned iArg = 0; iArg < args.length(); ++iArg )
{
if ( iArg > 0 )
fArgString += " ";
fArgString += args.asString( iArg );
}
#ifdef KH_DEBUG
MString ss = " .. Cmd ";
ss += fArgString;
ss += "\n";
::OutputDebugString( ss.asChar() );
#endif
MArgDatabase argData( syntax(), args, &status );
if ( !status )
return status;
bool bCgfxShaderNodeRequired = true;
fIsEdit = argData.isEdit();
fIsQuery = argData.isQuery();
if ( argData.isFlagSet( kMaxTexCoordsFlag ) )
{
bCgfxShaderNodeRequired = false;
fMaxTexCoords = true;
fIsQuery = true;
}
if ( argData.isFlagSet( kPluginPathFlag ) )
{
bCgfxShaderNodeRequired = false;
fPluginPath = true;
fIsQuery = true;
}
if ( argData.isFlagSet( kEmptyUVFlag ) )
{
fEmptyUV = true;
fIsQuery = true;
}
if ( argData.isFlagSet( kEmptyUVShapesFlag ) )
{
fEmptyUVShapes = true;
fIsQuery = true;
}
if ( argData.isFlagSet( kTexCoordSourceFlag ) )
{
fTexCoordSource = true;
fIsQuery = true;
}
#if MAYA_API_VERSION >= 700
if ( argData.isFlagSet( kColorSourceFlag ) )
{
fColorSource = true;
fIsQuery = true;
}
#endif
if (argData.isFlagSet(kFxFlag))
{
argData.getFlagArgument(kFxFlag, 0, fNewFxFile);
}
if (argData.isFlagSet(kFxTechniqueFlag))
{
argData.getFlagArgument( kFxFlag, 0, fNewTechnique );
}
if (argData.isFlagSet(kNameFlag))
{
argData.getFlagArgument(kNameFlag, 0, fNodeName);
}
if (argData.isFlagSet(kListParametersFlag))
{
fListParameters = true;
fIsQuery = true;
}
if ( argData.isFlagSet( kListTechniquesFlag ) )
{
fListTechniques = true;
fIsQuery = true;
}
if (argData.isFlagSet(kParameterFlag))
{
argData.getFlagArgument(kParameterFlag, 0, fParameterName);
fIsQuery = true;
}
if ( argData.isFlagSet( kCaseInsensitiveFlag ) )
{
fCaseInsensitive = true;
fIsQuery = true;
}
if ( argData.isFlagSet( kDescriptionFlag ) )
{
fDescription = true;
fIsQuery = true;
}
if ( fIsQuery &&
fIsEdit )
{
MString es = "cgfxShader: invalid use of -e/-edit flag";
MGlobal::displayError( es );
return MS::kInvalidParameter;
}
if ( bCgfxShaderNodeRequired )
{
argData.getObjects(selList);
if ( selList.length() == 0 )
MGlobal::getActiveSelectionList( selList );
if ( selList.length() != 1 )
{
sMsg = "Exactly one node must be specified or selected for command: cgfxShader ";
sMsg += fArgString;
MGlobal::displayError( sMsg );
status = MS::kInvalidParameter;
}
}
return status;
}
void
cgfxShaderCmd::reportInternalError( const char* sFile, size_t errcode )
{
MString es = "cgfxShader internal error ";
es += (int)errcode;
if ( this &&
fArgString.length() > 0 )
{
es += " with args: ";
es += fArgString;
}
#ifdef _WINDOWS
::OutputDebugString( es.asChar() );
::OutputDebugString( "\n" );
#endif
MGlobal::displayError( es );
}