meshOpNode.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 "meshOpNode.h"

// Function Sets
//
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnMeshData.h>
#include <maya/MFnComponentListData.h>
#include <maya/MFnSingleIndexedComponent.h>
#include <maya/MFnEnumAttribute.h>

// General Includes
//
#include <maya/MGlobal.h>
#include <maya/MPlug.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MIOStream.h>

// Macros
//
#define MCheckStatus(status,message)    \
    if( MStatus::kSuccess != status ) { \
        cerr << message << "\n";        \
        return status;                  \
    }


// Unique Node TypeId
MTypeId     meshOpNode::id( 0x00085000 );

// Node attributes
// (in addition to inMesh and outMesh defined by polyModifierNode)
//
MObject     meshOpNode::cpList;
MObject     meshOpNode::opType;

meshOpNode::meshOpNode()
{}

meshOpNode::~meshOpNode()
{}

MStatus meshOpNode::compute( const MPlug& plug, MDataBlock& data )
//
//  Description:
//      This method computes the value of the given output plug based
//      on the values of the input attributes.
//
//  Arguments:
//      plug - the plug to compute
//      data - object that provides access to the attributes for this node
//
{
    MStatus status = MS::kSuccess;
 
    MDataHandle stateData = data.outputValue( state, &status );
    MCheckStatus( status, "ERROR getting state" );

    // Check for the HasNoEffect/PassThrough flag on the node.
    //
    // (stateData is an enumeration standard in all depend nodes)
    // 
    // (0 = Normal)
    // (1 = HasNoEffect/PassThrough)
    // (2 = Blocking)
    // ...
    //
    if( stateData.asShort() == 1 )
    {
        MDataHandle inputData = data.inputValue( inMesh, &status );
        MCheckStatus(status,"ERROR getting inMesh");

        MDataHandle outputData = data.outputValue( outMesh, &status );
        MCheckStatus(status,"ERROR getting outMesh");

        // Simply redirect the inMesh to the outMesh for the PassThrough effect
        //
        outputData.set(inputData.asMesh());
    }
    else
    {
        // Check which output attribute we have been asked to 
        // compute. If this node doesn't know how to compute it, 
        // we must return MS::kUnknownParameter
        // 
        if (plug == outMesh)
        {
            MDataHandle inputData = data.inputValue( inMesh, &status );
            MCheckStatus(status,"ERROR getting inMesh");

            MDataHandle outputData = data.outputValue( outMesh, &status );
            MCheckStatus(status,"ERROR getting outMesh"); 

            // Now, we get the value of the component list and the operation
            // type and use it to perform the mesh operation on this mesh
            //
            MDataHandle inputIDs = data.inputValue( cpList, &status);
            MCheckStatus(status,"ERROR getting componentList"); 
            
            MDataHandle opTypeData = data.inputValue( opType, &status);
            MCheckStatus(status,"ERROR getting opType"); 

            // Copy the inMesh to the outMesh, so you can
            // perform operations directly on outMesh
            //
            outputData.set(inputData.asMesh());
            MObject mesh = outputData.asMesh();

            // Retrieve the ID list from the component list.
            //
            // Note, we use a component list to store the components
            // because it is more compact memory wise. (ie. comp[81:85]
            // is smaller than comp[81], comp[82],...,comp[85])
            //
            MObject compList = inputIDs.data();
            MFnComponentListData compListFn( compList );

            // Get what operation is requested and 
            // what type of component is expected for this operation.
            MeshOperation operationType = (MeshOperation) opTypeData.asShort();
            MFn::Type componentType =
                meshOpFty::getExpectedComponentType(operationType);

            unsigned i;
            int j;
            MIntArray cpIds;

            for( i = 0; i < compListFn.length(); i++ )
            {
                MObject comp = compListFn[i];
                if( comp.apiType() == componentType )
                {
                    MFnSingleIndexedComponent siComp( comp );
                    for( j = 0; j < siComp.elementCount(); j++ )
                        cpIds.append( siComp.element(j) );
                }
            }

            // Set the mesh object and component List on the factory
            //
            fmeshOpFactory.setMesh( mesh );
            fmeshOpFactory.setComponentList( compList );
            fmeshOpFactory.setComponentIDs( cpIds );
            fmeshOpFactory.setMeshOperation( operationType );

            // Now, perform the meshOp
            //
            status = fmeshOpFactory.doIt();

            // Mark the output mesh as clean
            //
            outputData.setClean();
        }
        else
        {
            status = MS::kUnknownParameter;
        }
    }

    return status;
}

void* meshOpNode::creator()
//
//  Description:
//      this method exists to give Maya a way to create new objects
//      of this type. 
//
//  Return Value:
//      a new object of this type
//
{
    return new meshOpNode();
}

MStatus meshOpNode::initialize()
//
//  Description:
//      This method is called to create and initialize all of the attributes
//      and attribute dependencies for this node type.  This is only called 
//      once when the node type is registered with Maya.
//
//  Return Values:
//      MS::kSuccess
//      MS::kFailure
//      
{
    MStatus             status;

    MFnTypedAttribute attrFn;
    MFnEnumAttribute enumFn;

    cpList = attrFn.create("inputComponents", "ics",
        MFnComponentListData::kComponentList);
    attrFn.setStorable(true);   // To be stored during file-save

    opType = enumFn.create("operationType", "oprt", 0, &status);
    enumFn.addField("subd_edges", 0);
    enumFn.addField("subd_faces", 1);
    enumFn.setHidden(false);
    enumFn.setKeyable(true);
    enumFn.setStorable(true);   // To be stored during file-save

    inMesh = attrFn.create("inMesh", "im", MFnMeshData::kMesh);
    attrFn.setStorable(true);   // To be stored during file-save

    // Attribute is read-only because it is an output attribute
    //
    outMesh = attrFn.create("outMesh", "om", MFnMeshData::kMesh);
    attrFn.setStorable(false);
    attrFn.setWritable(false);

    // Add the attributes we have created to the node
    //
    status = addAttribute( cpList );
        if (!status)
        {
            status.perror("addAttribute");
            return status;
        }
    status = addAttribute( opType );
        if (!status)
        {
            status.perror("addAttribute");
            return status;
        }
    status = addAttribute( inMesh );
        if (!status)
        {
            status.perror("addAttribute");
            return status;
        }
    status = addAttribute( outMesh);
        if (!status)
        {
            status.perror("addAttribute");
            return status;
        }

    // Set up a dependency between the input and the output.  This will cause
    // the output to be marked dirty when the input changes.  The output will
    // then be recomputed the next time the value of the output is requested.
    //
    status = attributeAffects( inMesh, outMesh );
        if (!status)
        {
            status.perror("attributeAffects");
            return status;
        }

    status = attributeAffects( cpList, outMesh );
        if (!status)
        {
            status.perror("attributeAffects");
            return status;
        }

    status = attributeAffects( opType, outMesh );
        if (!status)
        {
            status.perror("attributeAffects");
            return status;
        }

    return MS::kSuccess;

}