diff options
Diffstat (limited to 'tools/globalcode/utility/MExt.cpp')
-rw-r--r-- | tools/globalcode/utility/MExt.cpp | 2030 |
1 files changed, 2030 insertions, 0 deletions
diff --git a/tools/globalcode/utility/MExt.cpp b/tools/globalcode/utility/MExt.cpp new file mode 100644 index 0000000..6523859 --- /dev/null +++ b/tools/globalcode/utility/MExt.cpp @@ -0,0 +1,2030 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// MExt.cpp +// +// Description: Functions that extend the Maya API to perform other common +// tasks. +// +// Modification History: +// + Created Aug 21, 2001 -- bkusy +//----------------------------------------------------------------------------- +#include "precompiled/PCH.h" + +//---------------------------------------- +// System Includes +//---------------------------------------- +//#include <windows.h> + +#include <assert.h> +#include <stdlib.h> +#include <math.h> + +/* Using precompiled headers +#include <maya/M3dView.h> +#include <maya/MArgList.h> +#include <maya/MDagPath.h> +#include <maya/MDoubleArray.h> +#include <maya/MDGModifier.h> +#include <maya/MDoubleArray.h> +#include <maya/MGlobal.h> +#include <maya/MFnDagNode.h> +#include <maya/MFnData.h> +#include <maya/MFnDependencyNode.h> +#include <maya/MFnDoubleArrayData.h> +#include <maya/MFnMatrixData.h> +#include <maya/MFnTransform.h> +#include <maya/MFnTypedAttribute.h> +#include <maya/MItDag.h> +#include <maya/MItSelectionList.h> +#include <maya/MMatrix.h> +#include <maya/MObject.h> +#include <maya/MPlug.h> +#include <maya/MPlugArray.h> +#include <maya/MPoint.h> +#include <maya/MSelectionList.h> +#include <maya/MStatus.h> +#include <maya/MString.h> +#include <maya/MTransformationMatrix.h> +#include <maya/MTypeId.h> +#include <maya/MVector.h> +*/ + +//---------------------------------------- +// Project Includes +//---------------------------------------- +#include "MExt.h" +#include "util.h" + +//---------------------------------------- +// Constants, Typedefs and Statics +//---------------------------------------- +static const double EPSILON = 0.00001; +static const int SCRATCHPAD_SIZE = 256; +static char scratchpad[ SCRATCHPAD_SIZE + 1 ]; + + +//----------------------------------------------------------------------------- +// G e t S c a l e d +// +// Synopsis: Retrieves an attribute that is first scaled by the scale in +// the node's parenting transform. +// +// Parameters: vertex - reference parameter to receive scaled attribute. +// node - the node the attribute is on. +// attr - the attribute to retrieve. +// +// Returns: The status of the request, hopefully MS::kSuccess. +// +// Constraints: This method only applies the scale that is stored in the +// immediate parent of the node. Prior parenting tranforms have +// no effect. +//----------------------------------------------------------------------------- +void MExt::Attr::GetScaled( MPoint* vertex, + const MObject& node, + const MObject& attr + ) +{ + MStatus status; + + // + // Get the nodes parenting transform. + // + MFnDagNode fnNode( node, &status ); + assert( status ); + + MObject transform = fnNode.parent( 0, &status ); + assert( status ); + + MFnTransform fnTransform( transform, &status ); + assert( status ); + + // + // Get the scale in the parenting transform. + // + double scale[3]; + status = fnTransform.getScale( scale ); + assert( status ); + + // + // Get the attribute. + // + Get( vertex, node, attr ); + + // + // Scale the attribute. + // + vertex->x *= scale[0]; + vertex->y *= scale[1]; + vertex->z *= scale[2]; +} + +//----------------------------------------------------------------------------- +// S e t S c a l e d +// +// Synopsis: Sets the attribute after taking into account the scale set in +// the immediate parenting transform. If the attribute initially +// has a value of (10,10,10) and a scaling vector of +// (0.5, 2.0, 0.5) is in the parenting transform. The attribute +// will be stored as (20,5,20) so that it will reflect the +// original value when the attribute is retrieved through the +// transform at a later time. +// +// Parameters: vertex - the vertex values to set the attribute to. +// node - the node to set the attribute on. +// attr - the attribute to set. +// +// Returns: NOTHING +// +// Constraints: Only the scaling in the immediate parenting transform is taken +// into account. +// +//----------------------------------------------------------------------------- +void MExt::Attr::SetScaled( const MPoint& vertex, + MObject& node, + MObject& attr + ) +{ + MStatus status; + + // + // Get the nodes parenting transform. + // + MFnDagNode fnNode( node, &status ); + assert( status ); + + MObject transform = fnNode.parent( 0, &status ); + assert( status ); + + MFnTransform fnTransform( transform, &status ); + assert( status ); + + // + // Get the scale in the parenting transform. + // + double scale[3]; + status = fnTransform.getScale( scale ); + assert( status ); + + // + // Create the "unscaled" vertex. + // + MPoint scaledVertex = vertex; + scaledVertex.x = scaledVertex.x / scale[0]; + scaledVertex.y = scaledVertex.y / scale[1]; + scaledVertex.z = scaledVertex.z / scale[2]; + + Set( scaledVertex, node, attr ); +} + +//----------------------------------------------------------------------------- +// S e t S c a l e d +// +// Synopsis: Sets the attribute after taking into account the scale set in +// the immediate parenting transform. If the attribute initially +// has a value of (10,10,10) and a scaling vector of +// (0.5, 2.0, 0.5) is in the parenting transform. The attribute +// will be stored as (20,5,20) so that it will reflect the +// original value when the attribute is retrieved through the +// transform at a later time. +// +// Parameters: vertex - the vertex values to set the attribute to. +// node - the node to set the attribute on. +// attr - the name of the attribute. +// +// Returns: NOTHING +// +// Constraints: Only the scaling in the immediate parenting transform is taken +// into account. +// +//----------------------------------------------------------------------------- +void MExt::Attr::SetScaled( const MPoint& vertex, + MObject& node, + const MString& attr + ) +{ + MStatus status; + + // + // Get the attribute object that corresponds to the named attribute. + // + MFnDagNode fnNode( node, &status ); + assert( status ); + + MPlug plug = fnNode.findPlug( attr, &status ); + assert( status ); + + SetScaled( vertex, node, plug.attribute() ); +} + +//----------------------------------------------------------------------------- +// O p t i o n P a r s e r : : O p t i o n P a r s e r +// +// Synopsis: Constructor +// +// Parameters: args - the MArgList passed into functions like doIt(); +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +MExt::OptionParser::OptionParser( const char* command, const MArgList& args ) : + m_argc( 0 ), + m_argv( 0 ), + m_opt( 0 ) +{ + assert( command ); + + typedef char* charPointer; + + // + // Create our simultated argument environment. We add one to m_argc because + // we are inserting the command name as well. + // + m_argc = args.length() + 1; + m_argv = new charPointer[ m_argc ]; + assert( m_argv ); + + // + // Copy in the command name. + // + m_argv[0] = new char[ strlen( command ) + 1 ]; + assert( m_argv[0] ); + strcpy( m_argv[0], command ); + + // + // Copy in the arguments from argList. + // + int i; + for ( i = 1; i < m_argc; i++ ) + { + MString arg; + args.get( i - 1, arg ); + m_argv[i] = new char[ strlen( arg.asChar() ) + 1 ]; + assert( m_argv[i] ); + strcpy( m_argv[i], arg.asChar() ); + } + + // + // Initialize the parser. + // + util_getopt_init(); +} + +//----------------------------------------------------------------------------- +// O p t i o n P a r s e r : : ~ O p t i o n P a r s e r +// +// Synopsis: Destructor +// +// Parameters: NONE +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +MExt::OptionParser::~OptionParser() +{ + if ( m_argv ) + { + int i; + for ( i = 0; i < m_argc; i++ ) + { + if ( m_argv[i] ) + { + delete m_argv[i]; + } + } + + delete m_argv; + } + + m_argc = 0; + + if ( m_opt ) + { + delete m_opt; + m_opt = 0; + } +} + +//----------------------------------------------------------------------------- +// O p t i o n P a r s e r : : s e t O p t i o n s +// +// Synopsis: Specify the options that will be parsed. +// +// Parameters: optionSpec - the specification string. eg. "hgu:t:" would +// specify two boolean flags, "h" and "g" ( they +// do not take arguments ), and two argument flags, +// "u" and "t", that take arguments. Flags that +// take arguments must be followed by a ":". +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +void MExt::OptionParser::setOptions( const char* optionSpec ) +{ + int len = strlen( optionSpec ) + 1; + m_opt = new char[ len + 1 ]; + assert( m_opt ); + + strncpy( m_opt, optionSpec, len ); +} + +//----------------------------------------------------------------------------- +// O p t i o n P a r s e r : : n e x t O p t i o n +// +// Synopsis: Get the next option. +// +// Parameters: NONE +// +// Returns: The character flag for the next option or -1 if no more +// options. +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +int MExt::OptionParser::nextOption() +{ + int result = util_getopt( m_argc, m_argv, m_opt ); + return result; +} + +//----------------------------------------------------------------------------- +// O p t i o n P a r s e r : : g e t A r g +// +// Synopsis: +// +// Parameters: NONE +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +MString MExt::OptionParser::getArg() +{ + MString result( util_optarg ); + return result; +} + +//----------------------------------------------------------------------------- +// O p t i o n V a r : : G e t +// +// Synopsis: +// +// Parameters: NONE +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +bool MExt::OptionVar::Get( char* buffer, unsigned int buffer_size, const char* symbol ) +{ + bool doesExist = false; + + MString command = "optionVar -exists "; + command += symbol; + + int exists; + MGlobal::executeCommand( command, exists ); + + if ( exists ) + { + command = "optionVar -query "; + command += symbol; + + MString result; + MGlobal::executeCommand( command, result ); + + assert( result.length() < buffer_size ); + strncpy( buffer, result.asChar(), buffer_size ); + + doesExist = true; + } + + return doesExist; +} + + +//----------------------------------------------------------------------------- +// O p t i o n V a r : : S e t +// +// Synopsis: +// +// Parameters: NONE +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +void MExt::OptionVar::Set( const char* buffer, const char* symbol ) +{ + MString command; + command = "optionVar -stringValue "; + command += symbol; + command += " \""; + command += buffer; + command += "\""; + + MGlobal::executeCommand( command ); +} + +//----------------------------------------------------------------------------- +// A d d C h i l d +// +// Synopsis: Make a locator node the child of another by parenting the transforms. +// +// Parameters: parentLocatorNode - locator node to be the parent +// childLocatorNode - locator node to be the child +// +// Returns: NOTHING +// +// Constraints: Must both have transforms. +// +//----------------------------------------------------------------------------- +void MExt::AddChild( MObject& parentLocatorNode, MObject& childLocatorNode ) +{ +// assert( parentLocatorNode.apiType() == MFn::kLocator ); +// assert( childLocatorNode.apiType() == MFn::kLocator ); + + //Get the transform of the parent node + MFnDagNode fnDag( parentLocatorNode ); + MObject parentTransform = fnDag.parent( 0 ); + + //Get teh transform of the child node. + fnDag.setObject( childLocatorNode ); + MObject childTransform = fnDag.parent( 0 ); + + //Parent the fence to the wall + fnDag.setObject( parentTransform ); + fnDag.addChild( childTransform ); +} + +//----------------------------------------------------------------------------- +// C o n n e c t +// +// Synopsis: Connect two nodes via the specified attributes. +// +// Parameters: node - the source node. +// attr - the source attribute. +// otherNode - the destination node. +// otherAttr - the destination attribute. +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +void MExt::Connect( MObject& node, + MObject& attr, + MObject& otherNode, + MObject& otherAttr + ) +{ + MStatus status; + + MDGModifier modifier; + status = modifier.connect( node, attr, otherNode, otherAttr ); + assert( status ); + + status = modifier.doIt(); + if ( !status ) MGlobal::displayError( status.errorString() ); + assert( status ); +} + +void MExt::Connect( MObject& node, + const char* attr, + MObject& otherNode, + const char* otherAttr + ) +{ + MStatus status; + + MFnDependencyNode fnNode; + + fnNode.setObject( node ); + MPlug nodePlug = fnNode.findPlug( MString( attr ), &status ); + assert( status ); + + + fnNode.setObject( otherNode ); + MPlug otherNodePlug = fnNode.findPlug( MString( otherAttr ), &status ); + assert( status ); + + MDGModifier modifier; + status = modifier.connect( node, nodePlug.attribute(), otherNode, otherNodePlug.attribute() ); + assert( status ); + + status = modifier.doIt(); + if ( !status ) MGlobal::displayError( status.errorString() ); + assert( status ); +} + +//----------------------------------------------------------------------------- +// C r e a t e N o d e +// +// Synopsis: +// +// Parameters: node - reference parameter to receive node MObject. +// transform - reference parameter to receive the parenting +// transform. +// type - the type of node to create. +// name - the name to assign to the node. If NULL default +// name is used. +// group - MObject representing the group under which to +// attach the new node. If NULL then the new node +// is attached at the DAG root. +// +// Returns: the status of the request -- hopefully MS::kSuccess. +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +void MExt::CreateNode( MObject* node, + MObject* transform, + const MString& type, + const MString* name, + const MObject& group + ) +{ + MStatus status; + + // + // Determine names for the nodes. This must be done before the + // nodes are created so as to avoid conflicting with the default + // names that Maya will assign them prior to us renaming them. + // + MString nodeName = type; + if ( name ) nodeName = *name; + MString transformName = ( "TForm" + nodeName ); + + MakeNameUnique( &nodeName, nodeName, group ); + MakeNameUnique( &transformName, transformName, group ); + + // + // Create the transform. + // + MFnTransform fnTransform; + fnTransform.create( MObject::kNullObj, &status ); + assert( status ); + + // + // Create the node under the transform. + // + MFnDagNode fnNode; + fnNode.create( type, fnTransform.object(), &status ); + assert( status ); + + if ( group != MObject::kNullObj ) + { + // + // Place the new node under the group node. + // + MFnTransform fnGroup( group, &status ); + assert( status ); + + status = fnGroup.addChild( fnTransform.object() ); + assert( status ); + } + + // + // Name the nodes. + // + fnTransform.setName( transformName, &status ); + assert( status ); + + fnNode.setName( nodeName, &status ); + assert( status ); + + // + // Return the node and transform objects in the reference parameters. + // + if ( transform ) + { + *transform = fnTransform.object(); + } + + if ( node ) + { + *node = fnNode.object(); + } +} + +//----------------------------------------------------------------------------- +// C r e a t e N o d e +// +// Synopsis: +// +// Parameters: node - reference parameter to receive node MObject. +// transform - reference parameter to receive the parenting +// transform. +// type - the type of node to create. +// name - the name to assign to the node. If NULL default +// name is used. +// group - MObject representing the group under which to +// attach the new node. If NULL then the new node +// is attached at the DAG root. +// +// Returns: the status of the request -- hopefully MS::kSuccess. +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +void MExt::CreateNode( MObject& node, + MObject& transform, + const MString& type, + const MString* name, + const MObject& group + ) +{ + MStatus status; + + // + // Determine names for the nodes. This must be done before the + // nodes are created so as to avoid conflicting with the default + // names that Maya will assign them prior to us renaming them. + // + MString nodeName = type; + if ( name ) nodeName = *name; + MString transformName = ( "TForm" + nodeName ); + + MakeNameUnique( &nodeName, nodeName, group ); + MakeNameUnique( &transformName, transformName, group ); + + // + // Create the transform. + // + MFnTransform fnTransform; + fnTransform.create( MObject::kNullObj, &status ); + assert( status ); + + // + // Create the node under the transform. + // + MFnDagNode fnNode; + fnNode.create( type, fnTransform.object(), &status ); + assert( status ); + + if ( group != MObject::kNullObj ) + { + // + // Place the new node under the group node. + // + MFnTransform fnGroup( group, &status ); + assert( status ); + + status = fnGroup.addChild( fnTransform.object() ); + assert( status ); + } + + // + // Name the nodes. + // + fnTransform.setName( transformName, &status ); + assert( status ); + + fnNode.setName( nodeName, &status ); + assert( status ); + + // + // Return the node and transform objects in the reference parameters. + // + transform = fnTransform.object(); + node = fnNode.object(); +} + +//----------------------------------------------------------------------------- +// C r e a t e V e r t e x A t t r i b u t e +// +// Synopsis: Intended for use within the initialize() method for a node +// class. Creates a vertex attribute using a double array. +// +// Parameters: name - the name of the attribute. +// breif_name - the brief name of the attribute. +// +// Returns: the attribute object. +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +void MExt::CreateVertexAttribute( MObject* attr, + const char* name, + const char* briefName + ) +{ + MStatus status; + + MDoubleArray doubleArray; + MFnDoubleArrayData doubleArrayData; + MObject defaultVertex = doubleArrayData.create( doubleArray, &status ); + assert( status ); + + MFnTypedAttribute fnAttribute; + *attr = fnAttribute.create( name, + briefName, + MFnData::kDoubleArray, + defaultVertex, + &status + ); + assert( status ); +} + +//============================================================================= +// MExt::DeleteNode +//============================================================================= +// Description: Comment +// +// Parameters: ( MObject& node, bool deleteParent ) +// +// Return: void +// +//============================================================================= +void MExt::DeleteNode( MObject& node, bool deleteParent ) +{ + //Get the parent and delete it too if it's a transform and the bool says so. + + MStatus status; + MObject parent; + + if ( deleteParent ) + { + //Get the parent please. + MFnDagNode fnDag( node ); + parent = fnDag.parent( 0, &status ); + assert( status ); + } + + //Delete this node + MGlobal::deleteNode( node ); + + if ( deleteParent ) + { + //Delete the parent. + MGlobal::deleteNode( parent ); + } +} + +//----------------------------------------------------------------------------- +// D i s c o n n e c t A l l +// +// Synopsis: +// +// Parameters: NONE +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +void MExt::DisconnectAll( MObject& node, MObject& attr ) +{ + MStatus status; + + MFnDependencyNode fnNode( node, &status ); + assert( status ); + + // + // Get the plug for the attribute to be disconnected. + // + MPlug plug = fnNode.findPlug( attr, &status ); + assert( status ); + + // + // Find all connections and disconnect them. + // + MDGModifier modifier; + MPlugArray sources; + MPlugArray targets; + MExt::ResolveConnections( &sources, &targets, plug, true, true ); + + unsigned int count = sources.length(); + unsigned int i; + + for ( i = 0; i < count; i++ ) + { + status = modifier.disconnect( sources[i], targets[i] ); + assert( status ); + } + + status = modifier.doIt(); + assert( status ); +} + +void MExt::DisconnectAll( MObject& node, const char* attrName ) +{ + MStatus status; + + MFnDependencyNode fnNode( node, &status ); + assert( status ); + + // + // Get the plug for the attribute to be disconnected. + // + MPlug plug = fnNode.findPlug( MString( attrName ), &status ); + assert( status ); + + // + // Find all connections and disconnect them. + // + MDGModifier modifier; + MPlugArray sources; + MPlugArray targets; + MExt::ResolveConnections( &sources, &targets, plug, true, true ); + + unsigned int count = sources.length(); + unsigned int i; + + for ( i = 0; i < count; i++ ) + { + status = modifier.disconnect( sources[i], targets[i] ); + assert( status ); + } + + status = modifier.doIt(); + assert( status ); +} + +//----------------------------------------------------------------------------- +// D i s p l a y E r r o r +// +// Synopsis: +// +// Parameters: NONE +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +int MExt::DisplayError( const char* fmt, ... ) +{ + va_list argp; + va_start( argp, fmt ); + int size = _vsnprintf( scratchpad, SCRATCHPAD_SIZE, fmt, argp ); + MGlobal::displayError( scratchpad ); + return size; +} + +//----------------------------------------------------------------------------- +// D i s p l a y W a r n i n g +// +// Synopsis: +// +// Parameters: NONE +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +int MExt::DisplayWarning( const char* fmt, ... ) +{ + va_list argp; + va_start( argp, fmt ); + int size = _vsnprintf( scratchpad, SCRATCHPAD_SIZE, fmt, argp ); + MGlobal::displayWarning( scratchpad ); + return size; +} + +//----------------------------------------------------------------------------- +// D i s p l a y I n f o +// +// Synopsis: +// +// Parameters: NONE +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +int MExt::DisplayInfo( const char* fmt, ... ) +{ + va_list argp; + va_start( argp, fmt ); + int size = _vsnprintf( scratchpad, SCRATCHPAD_SIZE, fmt, argp ); + MGlobal::displayInfo( scratchpad ); + return size; +} + + + + +//----------------------------------------------------------------------------- +// F i l t e r S e l e c t i o n L i s t +// +// Synopsis: Filters the given source list for nodes of "typeName" and +// places them in the filtered list. If transforms are in the +// source list all their children are filtered as well. +// +// Parameters: filteredList - reference paremeter to receive the filtered +// list. +// typeName - the type name to check for. +// sourceList - the list to filter. +// +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +void MExt::FilterSelectionList( MSelectionList* filteredList, + const MString& typeName, + const MSelectionList& sourceList + ) +{ + assert( filteredList ); + + MItSelectionList it_source( sourceList ); + while( ! it_source.isDone() ) + { + MObject node; + it_source.getDependNode( node ); + + MFnDependencyNode fnNode; + fnNode.setObject( node ); + + if ( fnNode.typeName() == typeName ) + { + filteredList->add( fnNode.object() ); + } + else if ( strcmp( "transform" , fnNode.typeName().asChar() ) == 0 ) + { + SelectNodesBelowRoot( filteredList, typeName, fnNode.object() ); + } + + it_source.next(); + } +} + +//============================================================================= +// MExt::FindAllSkeletonRoots +//============================================================================= +// Description: Comment +// +// Parameters: ( MObjectArray* objects ) +// +// Return: bool +// +//============================================================================= +bool MExt::FindAllSkeletonRoots( MObjectArray* objects ) +{ + MStatus status; + bool returnVal = false; + + MItDag dagIt( MItDag::kDepthFirst, MFn::kTransform, &status ); + assert( status ); + + while( !dagIt.isDone() ) + { + MFnTransform fnTransform( dagIt.item() ); + + MPlug p3dBoolPlug = fnTransform.findPlug( MString("p3dBooleanAttributes"), &status ); + if ( status ) + { + //This has p3d info. + int value = 0; + p3dBoolPlug.getValue( value ); + + if ( value & 0x0002 ) //This is the skelton root bit... HACK + { + objects->append( fnTransform.object() ); + returnVal = true; + } + } + + dagIt.next(); + } + + return returnVal; +} + +//============================================================================= +// MExt::FindAllTransforms +//============================================================================= +// Description: Comment +// +// Parameters: ( MObjectArray* transforms, const MObject& root ) +// +// Return: bool +// +//============================================================================= +bool MExt::FindAllTransforms( MObjectArray* transforms, const MObject& root ) +{ + bool returnVal = false; + + MItDag dagIt( MItDag::kDepthFirst, MFn::kTransform ); + MDagPath path; + MDagPath::getAPathTo( root, path ); + + dagIt.reset( path, MItDag::kDepthFirst, MFn::kTransform ); + + while ( !dagIt.isDone() ) + { + transforms->append( dagIt.item() ); + returnVal = true; + + dagIt.next(); + } + + return returnVal; +} + +//----------------------------------------------------------------------------- +// F i n d D a g N o d e B y N a m e +// +// Synopsis: Find a node in the DAG using its name as the search key. +// +// Parameters: path - reference object to receive the path of the found +// node. +// name - the name to search for. +// root - only search under this node. +// +// Returns: true if found, false otherwise. +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +bool MExt::FindDagNodeByName( MDagPath* path, + const MString& name, + const MObject& root + ) +{ + MStatus status; + + MItDag it_dag; + if ( root != MObject::kNullObj ) + { + status = it_dag.reset( root ); + if ( MS::kSuccess != status ) return false; + } + + bool found = false; + while ( !found && !it_dag.isDone() ) + { + MFnDependencyNode node( it_dag.item(), &status ); + assert( status ); + if ( name == node.name() ) + { + found = true; + if ( path ) + { + it_dag.getPath( *path ); + } + } + it_dag.next(); + } + + return found; +} + +//----------------------------------------------------------------------------- +// F i n d D a g N o d e B y N a m e +// +// Synopsis: Find a node in the DAG using its name as the search key. +// +// Parameters: path - reference object to receive the path of the found +// node. +// name - the name to search for. +// root - only search under the node named root. +// +// Returns: true if found, false otherwise. +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +bool MExt::FindDagNodeByName( MDagPath* path, + const MString& name, + const MString& root + ) +{ + MDagPath myPath; + bool found = FindDagNodeByName( &myPath, root ); + if ( found ) + { + found = FindDagNodeByName( path, name, myPath.node() ); + } + + return found; +} + +//----------------------------------------------------------------------------- +// G e t W o r l d P o s i t i o n +// +// Synopsis: Retrieves the world position of the given node. +// +// Parameters: wp - reference parameter to receive the world positioin. +// node - the node object to retrieve the world position of. +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +void MExt::GetWorldPosition( MPoint* wp, const MObject& node ) +{ + MStatus status; + + MFnDependencyNode fnNode( node ); + + // + // Attempt to get the world matrix attribute. + // + MObject attrObject = fnNode.attribute( "worldMatrix", &status ); + assert( status ); + + // + // Build the world matrix plug. Use the first element on the plug. + // + MPlug plug( const_cast<MObject&>(node), attrObject ); + plug = plug.elementByLogicalIndex( 0, &status ); + assert( status ); + + // + // Get the world matrix. We have to go through a few Maya layers on this + // one. + // + MObject matrixObject; + status = plug.getValue( matrixObject ); + assert( status ); + + MFnMatrixData matrixData( matrixObject, &status ); + assert( status ); + + MMatrix matrix = matrixData.matrix( &status ); + assert( status ); + + // + // The translation vector of the matrix is our position. + // + wp->x = matrix( 3, 0 ); + wp->y = matrix( 3, 1 ); + wp->z = matrix( 3, 2 ); +} + + +//----------------------------------------------------------------------------- +// G e t W o r l d P o s i t i o n B e t w e e n +// +// Synopsis: Retrieves the world position of a point between the the given nodes. +// +// Parameters: node1 - The first node +// node1 - The second node +// betweenPoint - receives the point between the two nodes +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +MPoint MExt::GetWorldPositionBetween( MObject& node1, MObject& node2 ) +{ + MVector newWPVect; + MPoint node1WP; + MPoint node2WP; + + MExt::GetWorldPosition( &node1WP, node1 ); + MExt::GetWorldPosition( &node2WP, node2 ); + + newWPVect = node2WP - node1WP; + newWPVect /= 2.0f; + newWPVect += node1WP; + + MPoint newPoint( newWPVect ); + return newPoint; +} + +//============================================================================= +// MExt::GetWorldMatrix +//============================================================================= +// Description: Comment +// +// Parameters: ( MObject& node ) +// +// Return: MMatrix +// +//============================================================================= +MMatrix MExt::GetWorldMatrix( MObject& node ) +{ + MStatus status; + + MFnDependencyNode fnNode( node ); + + // + // Attempt to get the world matrix attribute. + // + MObject attrObject = fnNode.attribute( "worldMatrix", &status ); + assert( status ); + + // + // Build the world matrix plug. Use the first element on the plug. + // + MPlug plug( const_cast<MObject&>(node), attrObject ); + plug = plug.elementByLogicalIndex( 0, &status ); + assert( status ); + + // + // Get the world matrix. We have to go through a few Maya layers on this + // one. + // + MObject matrixObject; + status = plug.getValue( matrixObject ); + assert( status ); + + MFnMatrixData matrixData( matrixObject, &status ); + assert( status ); + + MMatrix matrix = matrixData.matrix( &status ); + assert( status ); + + return matrix; +} + +//----------------------------------------------------------------------------- +// MExt : : I s C o n n e c t e d +// +// Synopsis: +// +// Parameters: NONE +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +bool MExt::IsConnected( MObject& node, const char* attr ) +{ + MStatus status; + MFnDependencyNode fnNode; + + + fnNode.setObject( node ); + + MPlug plug = fnNode.findPlug( MString( attr ), &status ); + assert( status ); + + return plug.isConnected(); +} + +bool MExt::IsConnected( MObject& node, MObject& attr ) +{ + MStatus status; + MFnDependencyNode fnNode; + + + fnNode.setObject( node ); + + MPlug plug = fnNode.findPlug( attr, &status ); + assert( status ); + + return plug.isConnected(); +} + +bool MExt::IsConnected( MPlug& plug1, MPlug& plug2 ) +{ + MPlugArray plugArray; + + plug1.connectedTo( plugArray, true, true ); + + unsigned int i; + for ( i = 0; i < plugArray.length(); ++i ) + { + if ( plugArray[i] == plug2 ) + { + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// M a k e N a m e U n i q u e +// +// Synopsis: Append numerical suffixes to a name to make it unique under +// a specified root node. +// +// Parameters: unique - a reference parameter to receive the new unique name. +// name - the original name. +// root - the node under which the name must be unique. +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +void MExt::MakeNameUnique( MString* unique, + const MString& name, + const MObject& root + ) +{ + const int bufferSize = 256; + + MString myName = name; + assert( myName.length() < bufferSize ); + + if ( FindDagNodeByName( 0, myName, root ) ) + { + char buffer[ bufferSize ]; + strncpy( buffer, myName.asChar(), bufferSize ); + + // + // Isolate the base name by removing any numerical suffixes. + // + char* suffix = const_cast<char*>(util_reverseSpan( buffer, "0123456789" )); + if ( suffix ) + { + *suffix = '\0'; + } + + myName = buffer; + int isuffix = 0; + while( FindDagNodeByName( 0, myName, root ) ) + { + isuffix++; + myName = buffer; + myName += isuffix; + } + + } + + *unique = myName; +} + +//----------------------------------------------------------------------------- +// M a k e N a m e U n i q u e +// +// Synopsis: Append numerical suffixes to a name to make it unique under +// a specified root node. +// +// Parameters: unique - a reference parameter to receive the new unique name. +// name - the original name. +// root - name of the node under which the name must be unique. +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +void MExt::MakeNameUnique( MString* unique, + const MString& name, + const MString& root + ) +{ + MObject rootObject = MObject::kNullObj; + MDagPath path; + if ( FindDagNodeByName( &path, root ) ) + { + rootObject = path.node(); + } + + MakeNameUnique( unique, name, rootObject ); +} + +//============================================================================= +// MExt::MeshClickIntersect +//============================================================================= +// Description: Comment +// +// Parameters: ( short xClick, short yClick, MPoint& intersect, bool closest = true ) +// +// Return: bool +// +//============================================================================= +bool MExt::MeshClickIntersect( short xClick, + short yClick, + MPoint& intersect, + bool closest ) +{ + //Get the mesh below the clicked point and find it's y height. + short xStart, xEnd, yStart, yEnd; + + xStart = 0; + xEnd = M3dView::active3dView().portWidth(); + yStart = M3dView::active3dView().portHeight(); + yEnd = 0; + + MGlobal::selectFromScreen( xStart, + yStart, + xEnd, + yEnd, + MGlobal::kReplaceList ); + + MSelectionList selectionList; + + MGlobal::getActiveSelectionList( selectionList ); + + if ( selectionList.length() > 0 ) + { + //Go through each selected object and see if the ray intersects it. + MItSelectionList selectIt( selectionList, MFn::kMesh ); + + MPoint nearClick, farClick; + M3dView activeView = M3dView::active3dView(); + activeView.viewToWorld( xClick, yClick, nearClick, farClick ); + MVector rayDir( MVector( farClick ) - MVector( nearClick ) ); + MPointArray intersectPoints; + MDagPath objDag; + + bool found = false; + + MPoint resultPoint; + + if ( closest ) + { + resultPoint.x = 100000.0; + resultPoint.y = 100000.0; + resultPoint.z = 100000.0; + } + else + { + resultPoint.x = 0; + resultPoint.y = 0; + resultPoint.z = 0; + } + + while ( !selectIt.isDone() ) + { + selectIt.getDagPath( objDag ); + + MFnMesh mesh( objDag ); + + mesh.intersect( nearClick, rayDir, intersectPoints, 0.001f, MSpace::kWorld ); + + unsigned int i; + for ( i = 0; i < intersectPoints.length(); ++i ) + { + //test each point... + if ( closest ) + { + if ( intersectPoints[i].distanceTo(nearClick) < (resultPoint.distanceTo(nearClick) ) ) + { + resultPoint = intersectPoints[i]; + found = true; + } + } + else + { + if ( intersectPoints[i].distanceTo(nearClick) > (resultPoint.distanceTo(nearClick) ) ) + { + resultPoint = intersectPoints[i]; + found = true; + } + } + } + + selectIt.next(); + } + + if ( found ) + { + intersect = resultPoint; + MGlobal::clearSelectionList(); + return true; + } + } + + MGlobal::clearSelectionList(); + return false; +} + +//============================================================================= +// MExt::MeshIntersectAlongVector +//============================================================================= +// Description: Comment +// +// Parameters: ( MPoint from, MPoint direction, MPoint& intersect, bool closest = true ) +// +// Return: bool +// +//============================================================================= +bool MExt::MeshIntersectAlongVector( MPoint from, + MPoint direction, + MPoint& intersect, + bool closest ) +{ + MSelectionList selectionList; + selectionList.clear(); + + MItDag itDag(MItDag::kDepthFirst, MFn::kMesh ); + + while ( !itDag.isDone() ) + { + MDagPath dagPath; + itDag.getPath( dagPath ); + + selectionList.add( dagPath ); + + itDag.next(); + } + + if ( selectionList.length() > 0 ) + { + //Go through each selected object and see if the ray intersects it. + MItSelectionList selectIt( selectionList, MFn::kMesh ); + + MPointArray intersectPoints; + MDagPath objDag; + + bool found = false; + + MPoint resultPoint; + + if ( closest ) + { + resultPoint.x = 100000.0; + resultPoint.y = 100000.0; + resultPoint.z = 100000.0; + } + else + { + resultPoint.x = 0; + resultPoint.y = 0; + resultPoint.z = 0; + } + + while ( !selectIt.isDone() ) + { + selectIt.getDagPath( objDag ); + + MStatus status; + + MFnMesh mesh( objDag, &status ); + assert( status ); + + + const char* name = mesh.name().asChar(); + + mesh.intersect( from, direction, intersectPoints, 0.001f, MSpace::kWorld ); + + unsigned int i; + for ( i = 0; i < intersectPoints.length(); ++i ) + { + //test each point... + if ( closest ) + { + if ( intersectPoints[i].distanceTo(from) < (resultPoint.distanceTo(from) ) ) + { + resultPoint = intersectPoints[i]; + found = true; + } + } + else + { + if ( intersectPoints[i].distanceTo(from) > (resultPoint.distanceTo(from) ) ) + { + resultPoint = intersectPoints[i]; + found = true; + } + } + } + + selectIt.next(); + } + + if ( found ) + { + intersect = resultPoint; + MGlobal::clearSelectionList(); + return true; + } + } + + MGlobal::clearSelectionList(); + return false; +} + +//----------------------------------------------------------------------------- +// P l u g H a s C o n n e c t i o n +// +// Synopsis: Determines if there are any connections on the specified plug. +// +// Parameters: connectedNode - reference parameter to receive the associated +// node for the first connection found. +// plug - the plug to get the connections for. +// asSrc - if true, retrieve connections where "plug" is +// the source of the connection. +// asDst - if true, retrieve connections where "plug" is +// the target of the connection. +// +// Returns: true, if the plug has connections; false, otherwise +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +bool MExt::PlugHasConnection( MObject* connectedNode, + MPlug& plug, + bool asDst, + bool asSrc, + const char* type + ) +{ + bool isOk = false; + + MPlug myPlug; + isOk = PlugHasConnection( &myPlug, plug, asDst, asSrc, type ); + + if ( isOk ) + { + if ( connectedNode ) *connectedNode = myPlug.node(); + } + + return isOk; +} + +//----------------------------------------------------------------------------- +// P l u g H a s C o n n e c t i o n +// +// Synopsis: Determines if there are any connections on the specified plug. +// +// Parameters: connectedPlug - reference parameter to receive the plug of +// the first connection found. +// plug - the plug to get the connections for. +// asSrc - if true, retrieve connections where "plug" is +// the source of the connection. +// asDst - if true, retrieve connections where "plug" is +// the target of the connection. +// +// Returns: true, if the plug has connections; false, otherwise +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +bool MExt::PlugHasConnection( MPlug* connectedPlug, + MPlug& plug, + bool asDst, + bool asSrc, + const char* type + ) +{ + MPlugArray buffer; + + if ( asSrc ) + { + MPlugArray destinations; + ResolveConnections( &buffer, &destinations, plug, false, true ); + + unsigned int i; + for( i = 0; i < destinations.length(); i++ ) + { + bool isOk = true; + if ( type ) + { + MFnDependencyNode fnNode( destinations[i].node() ); + if ( fnNode.typeName() != type ) + { + isOk = false; + } + } + + if ( isOk ) + { + if ( connectedPlug ) *connectedPlug = destinations[i]; + return true; + } + } + } + + if ( asDst ) + { + MPlugArray sources; + ResolveConnections( &sources, &buffer, plug, true, false ); + + unsigned int i; + for( i = 0; i < sources.length(); i++ ) + { + bool isOk = true; + if ( type ) + { + MFnDependencyNode fnNode( sources[i].node() ); + if ( fnNode.typeName() != type ) + { + isOk = false; + } + } + + if ( isOk ) + { + if ( connectedPlug ) *connectedPlug = sources[i]; + return true; + } + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// R e s o l v e C o n n e c t i o n s +// +// Synopsis: Retrieves the plugs that are connected to this plug. This +// will work on plugs that are associated with array as well as +// non-array attributes. +// +// Parameters: sources - reference parameter to recieve list of +// connection sources. +// targets - reference parameter to recieve list of +// connection targets. +// plug - the plug to get the connections for. +// asSrc - if true, retrieve connections where "plug" is +// the source of the connection. +// asDst - if true, retrieve connections where "plug" is +// the target of the connection. +// +// Returns: true, if the plug has connections; false, otherwise +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +void MExt::ResolveConnections( MPlugArray* sources, + MPlugArray* targets, + MPlug& plug, + bool asDst, + bool asSrc + ) +{ + assert( sources ); + assert( targets ); + + MStatus status; + MPlugArray myPlugs; + unsigned int count = 0; + unsigned int i = 0; + + if ( plug.isArray() ) + { + count = plug.numElements( &status ); + assert( status ); + + for ( i = 0; i < count; i++ ) + { + MPlug element = plug.elementByPhysicalIndex( i, &status ); + assert( status ); + + if ( element.isConnected() ) + { + + MString name = element.name(); + const char* dbg_name = name.asChar(); + + myPlugs.append( element ); + } + } + } + else + { + myPlugs.append( plug ); + } + + sources->clear(); + targets->clear(); + count = myPlugs.length(); + for ( i = 0; i < count; i++ ) + { + MPlugArray connectedPlugs; + + if ( asDst ) + { + myPlugs[i].connectedTo( connectedPlugs, true, false, &status ); + assert( status ); + + if ( connectedPlugs.length() > 0 ) + { + sources->append( connectedPlugs[0] ); + targets->append( myPlugs[i] ); + } + } + + if ( asSrc ) + { + myPlugs[i].connectedTo( connectedPlugs, false, true, &status ); + assert( status ); + + if ( connectedPlugs.length() > 0 ) + { + sources->append( myPlugs[i] ); + targets->append( connectedPlugs[0] ); + } + } + } + + assert( sources->length() == targets->length() ); +} + +//----------------------------------------------------------------------------- +// S e l e c t N o d e s B e l o w R o o t +// +// Synopsis: Select nodes meeting the specified criteria and place them +// in the provided selection list. +// +// Parameters: list - the list to receive the nodes. +// typeId - the typeId of the node type to select. +// root - the root of the subsection of the DAG to search. +// +// Returns: NOTHING +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +void MExt::SelectNodesBelowRoot( MSelectionList* list, + const MString& typeName, + const MObject& root, + MSelectionList* intersectionList + ) +{ + assert( list ); + + MStatus status; + + bool mergeWithExisting = true; + + MItDag itor; + + if ( root != MObject::kNullObj ) + { + itor.reset( root ); + } + + while( ! itor.isDone() ) + { + MFnDependencyNode fnNode; + fnNode.setObject( itor.item() ); + + if ( fnNode.typeName() == typeName ) + { + bool doAdd = true; + + if ( intersectionList ) + { + MDagPath path; + status = MDagPath::getAPathTo( fnNode.object(), path ); + + // + // We only add the item if it is in the intersectionList. + // + if ( ! intersectionList->hasItem( path ) ) + { + doAdd = false; + } + } + + if ( doAdd ) + { + list->add( fnNode.object(), mergeWithExisting ); + } + } + + itor.next(); + } +} + +//----------------------------------------------------------------------------- +// S e t W o r l d P o s i t i o n +// +// Synopsis: Sets the world position of the given node. +// +// Parameters: wp - the new world position. +// node - the node for which to set the position. +// +// Returns: Hopefully MS::kSuccess, but other MS::????? errors will occur +// if the object is not a suitable type of node. +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +void MExt::SetWorldPosition( const MPoint& wp, const MObject& node ) +{ + MStatus status; + + MFnDependencyNode fnNode( node ); + + // + // Attempt to get the world matrix attribute. + // + MObject attrObject = fnNode.attribute( "worldMatrix", &status ); + assert( status ); + + // + // Build the world matrix plug. Use the first element on the plug. + // + MPlug plug( const_cast<MObject&>(node), attrObject ); + plug = plug.elementByLogicalIndex( 0, &status ); + assert( status ); + + // + // Get the world matrix. We have to go through a few Maya layers on this + // one. + // + MObject matrixObject; + status = plug.getValue( matrixObject ); + assert( status ); + + MFnMatrixData matrixData( matrixObject, &status ); + assert( status ); + + // + // Create a world tranformation matrix. + // + MTransformationMatrix matrix( matrixData.matrix( &status ) ); + assert( status ); + + // + // Get the world translation vector. + // + MVector worldTranslation = matrix.translation( MSpace::kWorld, &status ); + + + // + // Get the nodes immediate transform and create a function wrapper for it. + // + MDagPath nodePath; + status = MDagPath::getAPathTo( node, nodePath ); + assert( status ); + + MObject transformObject = nodePath.transform( &status ); + assert( status ); + + MFnTransform fnTransform( transformObject, &status ); + assert( status ); + + // + // Get the node translation vector. + // + MVector nodeTranslation = fnTransform.translation( MSpace::kTransform, &status ); + + // + // The exclusive translation vector is that vector which reflect the + // amount of translation the node undergoes as a result of transforms + // exclusive of its immediate parent. + // + MVector exclusiveTranslation = worldTranslation - nodeTranslation; + + // + // Set the nodeTranslation to that or our desired world position less the + // exclusiveTranslation vector. + // + MVector position( wp ); + nodeTranslation = position - exclusiveTranslation; + + // + // Push the result back into the transform and we are done. + // + status = fnTransform.setTranslation( nodeTranslation, MSpace::kTransform ); + assert( status ); + +} + +//----------------------------------------------------------------------------- +// v i e w T o W o r l d A t Y +// +// Synopsis: Convert the specified view coordinates to world coordinates on +// the specified y plane. +// +// Parameters: world - reference parameter to recieve the world coordinates. +// view - the view position to be converted. +// y - the y plane to translate to. +// +// Returns: The status of the request. Will return failure if the the +// view plane is perpendicular to the y-plane. +// +// Constraints: NONE +// +//----------------------------------------------------------------------------- +void MExt::ViewToWorldAtY( MPoint* wc, MPoint& vc, double y ) +{ + assert( wc ); + + MStatus status = MS::kFailure; + + M3dView view = M3dView::active3dView(); + MPoint rayOrigin; + MVector rayVector; + status = view.viewToWorld( static_cast<short>(vc.x), + static_cast<short>(vc.y), + rayOrigin, + rayVector + ); + assert( status ); + + MPoint result; + if ( fabs(rayVector.y) > EPSILON ) + { + // + // The following formulas for x and z use the point slope formula in + // the form + // x = ( y - y0 ) / M + x0 + // = ( y - y0 ) / ( dy / dx ) + x0 + // = ( ( y - y0 ) / dy ) * dx + x0 + // + double coeff = ( y - rayOrigin.y ) / rayVector.y; + wc->x = ( coeff * rayVector.x ) + rayOrigin.x; + wc->y = y; + wc->z = ( coeff * rayVector.z ) + rayOrigin.z; + } +} |