- Main Page
- Related Pages
- Modules
- Classes
- Examples

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