//-----------------------------------------------------------------------------
// 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;
}
}