geometrySurfaceConstraint/geometrySurfaceConstraint.cpp
 
 
 
geometrySurfaceConstraint/geometrySurfaceConstraint.cpp
//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+

#include <geometrySurfaceConstraint.h>

//
//      Example: geometrySurfaceConstraint
//      This example demonstrates how to use the MPxConstraint
//      and MPxConstraintCommand classes to create a 
//      geometry constraint.  This type of constraint will
//      keep the constrained object attached to the target 
//      as the target is moved.
//      The constrained object can be constrained to one of
//      mutliple targets.  You can choose to constrain to
//      the target of the highest or lowest weight.
//

/*
loadPlugin geometrySurfaceConstraint;

// 1. cylinder constrained to plane
file -f -new;
polyPlane -w 1 -h 1 -sx 10 -sy 10 -ax 0 1 0 -cuv 2 -ch 1;
scale -r 15 15 15;
polyCylinder -r 1 -h 2 -sx 20 -sy 1 -sz 1 -ax 0 1 0 -rcp 0 -cuv 3 -ch 1;
select -cl;
select -r pPlane1 pCylinder1;
geometrySurfaceConstraint -weight 1;

// 2. cylinder constrained to one of two planes
// depending on plane weight
file -f -new;
polyPlane -w 1 -h 1 -sx 10 -sy 10 -ax 0 1 0 -cuv 2 -ch 1;
scale -r 10 10 10;
polyPlane -w 1 -h 1 -sx 10 -sy 10 -ax 0 1 0 -cuv 2 -ch 1;
scale -r 15 15 15;
polyCylinder -r 1 -h 2 -sx 20 -sy 1 -sz 1 -ax 0 1 0 -rcp 0 -cuv 3 -ch 1;
select -cl;
select -r pPlane1 pPlane2 pCylinder1;
geometryConstraint -weight 1.0;
// change plane weight to move constrained object
geometryConstraint -e -w 10.0 pPlane2 pCylinder1;
*/

//
//      Node implementation
//

MTypeId     geometrySurfaceConstraint::id( 0x8103F );
MObject     geometrySurfaceConstraint::compoundTarget;        
MObject     geometrySurfaceConstraint::targetGeometry;       
MObject     geometrySurfaceConstraint::targetWeight;       
MObject     geometrySurfaceConstraint::constraintParentInverseMatrix;       
MObject     geometrySurfaceConstraint::constraintGeometry;       


geometrySurfaceConstraint::geometrySurfaceConstraint() 
{
        weightType = geometrySurfaceConstraintCommand::kLargestWeight;
}

geometrySurfaceConstraint::~geometrySurfaceConstraint() 
{
}

void geometrySurfaceConstraint::postConstructor()
{
}

MStatus geometrySurfaceConstraint::compute( const MPlug& plug, MDataBlock& block )
{       
        MStatus returnStatus;
 
        if ( plug == geometrySurfaceConstraint::constraintGeometry )
        {
                //
                block.inputValue(constraintParentInverseMatrix);
                //
                MArrayDataHandle targetArray = block.inputArrayValue( compoundTarget );
                unsigned int targetArrayCount = targetArray.elementCount();
                double weight,selectedWeight = 0;
                if ( weightType == geometrySurfaceConstraintCommand::kSmallestWeight )
                        selectedWeight = FLT_MAX;
                MObject selectedMesh;
                unsigned int i;
                for ( i = 0; i < targetArrayCount; i++ )
                {
                        MDataHandle targetElement = targetArray.inputValue();
                        weight = targetElement.child(targetWeight).asDouble();
                        if ( !equivalent(weight,0.0))
                        {
                                if ( weightType == geometrySurfaceConstraintCommand::kLargestWeight )
                                {
                                        if ( weight > selectedWeight )
                                        {
                                                MObject mesh = targetElement.child(targetGeometry).asMesh();
                                                if ( !mesh.isNull() )
                                                {
                                                        selectedMesh = mesh;
                                                        selectedWeight =  weight;
                                                }
                                        }
                                }
                                else
                                {
                                        if  ( weight < selectedWeight )
                                        {
                                                MObject mesh = targetElement.child(targetGeometry).asMesh();
                                                if ( !mesh.isNull() )
                                                {
                                                        selectedMesh = mesh;
                                                        selectedWeight =  weight;
                                                }
                                        }
                                }
                        }
                        targetArray.next();
                }
                //
                if ( selectedMesh.isNull() )
                {
                        block.setClean(plug);
                }
                else
                {
                        // The transform node via the geometry attribute will take care of
                        // updating the location of the constrained geometry.
                        MDataHandle outputConstraintGeometryHandle = block.outputValue(constraintGeometry);
                        outputConstraintGeometryHandle.setMObject(selectedMesh);
                }
        } 
        else 
        {
                return MS::kUnknownParameter;
        }

        return MS::kSuccess;
}

const MObject geometrySurfaceConstraint::weightAttribute() const
{
        return geometrySurfaceConstraint::targetWeight;
}

const MObject geometrySurfaceConstraint::targetAttribute() const
{
        return geometrySurfaceConstraint::compoundTarget;
}

void geometrySurfaceConstraint::getOutputAttributes(MObjectArray& attributeArray)
{
        attributeArray.clear();
        attributeArray.append( geometrySurfaceConstraint::constraintGeometry );
}

void* geometrySurfaceConstraint::creator()
{
        return new geometrySurfaceConstraint();
}

MStatus geometrySurfaceConstraint::initialize()
{
        MFnNumericAttribute nAttr;
        MStatus                         status;

        // constraint attributes

        {       // Geometry: mesh, readable, not writable, delete on disconnect
                MFnTypedAttribute typedAttrNotWritable;
                geometrySurfaceConstraint::constraintGeometry =
                        typedAttrNotWritable.create( "constraintGeometry", "cg", MFnData::kMesh, &status );     
                if (!status) { status.perror("typedAttrNotWritable.create:cgeom"); return status;}
                status = typedAttrNotWritable.setReadable(true);
                if (!status) { status.perror("typedAttrNotWritable.setReadable:cgeom"); return status;}
                status = typedAttrNotWritable.setWritable(false);
                if (!status) { status.perror("typedAttrNotWritable.setWritable:cgeom"); return status;}
                status = typedAttrNotWritable.setDisconnectBehavior(MFnAttribute::kDelete);
                if (!status) { status.perror("typedAttrNotWritable.setDisconnectBehavior:cgeom"); return status;}
        }
        {       // Parent inverse matrix: delete on disconnect
                MFnTypedAttribute typedAttr;
                geometrySurfaceConstraint::constraintParentInverseMatrix =
                        typedAttr.create( "constraintPim", "ci", MFnData::kMatrix, &status );   
                if (!status) { status.perror("typedAttr.create:matrix"); return status;}
                status = typedAttr.setDisconnectBehavior(MFnAttribute::kDelete);
                if (!status) { status.perror("typedAttr.setDisconnectBehavior:cgeom"); return status;}

                // Target geometry: mesh, delete on disconnect
                geometrySurfaceConstraint::targetGeometry =
                        typedAttr.create( "targetGeometry", "tg", MFnData::kMesh, &status );    
                if (!status) { status.perror("typedAttr.create:tgeom"); return status;}
                status = typedAttr.setDisconnectBehavior(MFnAttribute::kDelete);
                if (!status) { status.perror("typedAttr.setDisconnectBehavior:cgeom"); return status;}
        }
        {       // Target weight: double, min 0, default 1.0, keyable, delete on disconnect
                MFnNumericAttribute typedAttrKeyable;
                geometrySurfaceConstraint::targetWeight 
                        = typedAttrKeyable.create( "weight", "wt", MFnNumericData::kDouble, 1.0, &status );
                if (!status) { status.perror("typedAttrKeyable.create:weight"); return status;}
                status = typedAttrKeyable.setMin( (double) 0 );
                if (!status) { status.perror("typedAttrKeyable.setMin"); return status;}
                status = typedAttrKeyable.setKeyable( true );
                if (!status) { status.perror("typedAttrKeyable.setKeyable"); return status;}
                status = typedAttrKeyable.setDisconnectBehavior(MFnAttribute::kDelete);
                if (!status) { status.perror("typedAttrKeyable.setDisconnectBehavior:cgeom"); return status;}
        }
        {       // Compound target(geometry,weight): array, delete on disconnect
                MFnCompoundAttribute compoundAttr;
                geometrySurfaceConstraint::compoundTarget = 
                        compoundAttr.create( "target", "tgt",&status );
                if (!status) { status.perror("compoundAttr.create"); return status;}
                status = compoundAttr.addChild( geometrySurfaceConstraint::targetGeometry );
                if (!status) { status.perror("compoundAttr.addChild"); return status;}
                status = compoundAttr.addChild( geometrySurfaceConstraint::targetWeight );
                if (!status) { status.perror("compoundAttr.addChild"); return status;}
                status = compoundAttr.setArray( true );
                if (!status) { status.perror("compoundAttr.setArray"); return status;}
                status = compoundAttr.setDisconnectBehavior(MFnAttribute::kDelete);
                if (!status) { status.perror("typedAttrKeyable.setDisconnectBehavior:cgeom"); return status;}
        }

        status = addAttribute( geometrySurfaceConstraint::constraintParentInverseMatrix );
        if (!status) { status.perror("addAttribute"); return status;}
        status = addAttribute( geometrySurfaceConstraint::constraintGeometry );
        if (!status) { status.perror("addAttribute"); return status;}
        status = addAttribute( geometrySurfaceConstraint::compoundTarget );
        if (!status) { status.perror("addAttribute"); return status;}

        status = attributeAffects( compoundTarget, constraintGeometry );
        if (!status) { status.perror("attributeAffects"); return status;}
        status = attributeAffects( targetGeometry, constraintGeometry );
        if (!status) { status.perror("attributeAffects"); return status;}
        status = attributeAffects( targetWeight, constraintGeometry );
        if (!status) { status.perror("attributeAffects"); return status;}
        status = attributeAffects( constraintParentInverseMatrix, constraintGeometry );
        if (!status) { status.perror("attributeAffects"); return status;}

        return MS::kSuccess;
}

//
//      Command implementation
//

geometrySurfaceConstraintCommand::geometrySurfaceConstraintCommand() {}
geometrySurfaceConstraintCommand::~geometrySurfaceConstraintCommand() {}

void* geometrySurfaceConstraintCommand::creator()
{
        return new geometrySurfaceConstraintCommand();
}

void geometrySurfaceConstraintCommand::createdConstraint(MPxConstraint *constraint)
{
        if ( constraint )
        {
                geometrySurfaceConstraint *c = (geometrySurfaceConstraint*) constraint;
                c->weightType = weightType;
        }
        else
        {
                MGlobal::displayError("Failed to get created constraint.");
        }
}


MStatus geometrySurfaceConstraintCommand::parseArgs(const MArgList &argList)
{
        MStatus                 ReturnStatus;
        MArgDatabase    argData(syntax(), argList, &ReturnStatus);

        if ( ReturnStatus.error() )
                return MS::kFailure;

        // Settings only work at creation time. Would need an
        // attribute on the node in order to push this state
        // into the node at any time.
        ConstraintType typ;
        if (argData.isFlagSet(kConstrainToLargestWeightFlag))
                typ = geometrySurfaceConstraintCommand::kLargestWeight;
        else if (argData.isFlagSet(kConstrainToSmallestWeightFlag))
                typ = geometrySurfaceConstraintCommand::kSmallestWeight;
        else
                typ = geometrySurfaceConstraintCommand::kLargestWeight;
        weightType = typ;

        // Need parent to process
        return MS::kUnknownParameter;
}

MStatus geometrySurfaceConstraintCommand::doIt(const MArgList &argList)
{
        MStatus ReturnStatus;

        if ( MS::kFailure == parseArgs(argList) )
                return MS::kFailure;

        return MS::kUnknownParameter;
}

MStatus geometrySurfaceConstraintCommand::connectTarget(void *opaqueTarget, int index)
{
        MStatus status = connectTargetAttribute( 
                        opaqueTarget, index, geometrySurfaceConstraint::targetGeometry );
        if (!status) { status.perror("connectTargetGeometry"); return status;}
        return MS::kSuccess;
}

MStatus geometrySurfaceConstraintCommand::connectObjectAndConstraint( MDGModifier& modifier )
{
        MObject transform = transformObject();
        if ( transform.isNull() )
        {
                MGlobal::displayError("Failed to get transformObject()");
                return MS::kFailure;
        }

        MStatus status;
        MFnTransform transformFn( transform );
        MVector translate = transformFn.getTranslation(MSpace::kTransform,&status);
        if (!status) { status.perror(" transformFn.getTranslation"); return status;}

        MPlug translatePlug = transformFn.findPlug( "translate", &status );
        if (!status) { status.perror(" transformFn.findPlug"); return status;}

        if ( MPlug::kFreeToChange == translatePlug.isFreeToChange() )
        {
                MFnNumericData nd;
                MObject translateData = nd.create( MFnNumericData::k3Double, &status );
                status = nd.setData3Double( translate.x,translate.y,translate.z);
                if (!status) { status.perror("nd.setData3Double"); return status;}
                status = modifier.newPlugValue( translatePlug, translateData );
                if (!status) { status.perror("modifier.newPlugValue"); return status;}

                status = connectObjectAttribute( 
                        MPxTransform::geometry, 
                                        geometrySurfaceConstraint::constraintGeometry, false );
                if (!status) { status.perror("connectObjectAttribute"); return status;}
        }

        status = connectObjectAttribute( 
                MPxTransform::parentInverseMatrix,
                        geometrySurfaceConstraint::constraintParentInverseMatrix, true, true );
        if (!status) { status.perror("connectObjectAttribute"); return status;}

        return MS::kSuccess;
}

const MObject& geometrySurfaceConstraintCommand::constraintInstancedAttribute() const
{
        return geometrySurfaceConstraint::constraintParentInverseMatrix;
}

const MObject& geometrySurfaceConstraintCommand::constraintOutputAttribute() const
{
        return geometrySurfaceConstraint::constraintGeometry;
}

const MObject& geometrySurfaceConstraintCommand::constraintTargetInstancedAttribute() const
{
        return geometrySurfaceConstraint::targetGeometry;
}

const MObject& geometrySurfaceConstraintCommand::constraintTargetAttribute() const
{
        return geometrySurfaceConstraint::compoundTarget;
}

const MObject& geometrySurfaceConstraintCommand::constraintTargetWeightAttribute() const
{
        return geometrySurfaceConstraint::targetWeight;
}

const MObject& geometrySurfaceConstraintCommand::objectAttribute() const
{
        return MPxTransform::geometry;
}

MTypeId geometrySurfaceConstraintCommand::constraintTypeId() const
{
        return geometrySurfaceConstraint::id;
}

MPxConstraintCommand::TargetType geometrySurfaceConstraintCommand::targetType() const
{
        return MPxConstraintCommand::kGeometryShape;
}

MStatus geometrySurfaceConstraintCommand::appendSyntax()
{
        MStatus ReturnStatus;

        MSyntax theSyntax = syntax(&ReturnStatus);
        if (MS::kSuccess != ReturnStatus) {
                MGlobal::displayError("Could not get the parent's syntax");
                return ReturnStatus;
        }

        // Add our command flags
        theSyntax.addFlag( kConstrainToLargestWeightFlag, kConstrainToLargestWeightFlagLong );
        theSyntax.addFlag( kConstrainToSmallestWeightFlag, kConstrainToSmallestWeightFlagLong );

        return ReturnStatus;
}

//
//      Entry points
//

MStatus initializePlugin( MObject obj )
{ 
        MStatus   status;
        MFnPlugin plugin( obj, PLUGIN_COMPANY, "9.0", "Any");

        status = plugin.registerNode( "geometrySurfaceConstraint", geometrySurfaceConstraint::id, geometrySurfaceConstraint::creator,
                geometrySurfaceConstraint::initialize, MPxNode::kConstraintNode );
        if (!status) {
                status.perror("registerNode");
                return status;
        }

        status = plugin.registerConstraintCommand( "geometrySurfaceConstraint", geometrySurfaceConstraintCommand::creator );
        if (!status) {
                status.perror("registerConstraintCommand");
                return status;
        }

        return status;
}

MStatus uninitializePlugin( MObject obj)
{
        MStatus   status;
        MFnPlugin plugin( obj );

        status = plugin.deregisterNode( geometrySurfaceConstraint::id );
        if (!status) {
                status.perror("deregisterNode");
                return status;
        }

        status = plugin.deregisterConstraintCommand( "geometrySurfaceConstraint" );
        if (!status) {
                status.perror("deregisterNode");
                return status;
        }

        return status;
}