closestPointCmd.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.
// ==========================================================================
//+


// MAYA HEADERS
#include <maya/MFnPlugin.h>
#include <maya/MDagPath.h>
#include <maya/MFnMesh.h>
#include <maya/MMeshIntersector.h>
#include <maya/MItSelectionList.h>
#include <maya/MItMeshPolygon.h>
#include <maya/MObject.h>
#include <maya/MSelectionList.h>
#include <maya/MArgList.h>
#include <maya/MFloatPoint.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnPointLight.h>
#include <maya/MFloatPointArray.h>
#include <maya/MPxCommand.h>
#include <maya/MDGModifier.h>
#include <maya/MFnDagNode.h>
#include <maya/MDagPath.h>
#include <maya/MGlobal.h>
#include <maya/MIOStream.h>


#define MERR_CHK(status,msg) if ( !status ) { MGlobal::displayError(msg); } // cerr << msg << endl; }

//
//  PLUGIN DESCRIPTION
//  
//  This is an example of the closest point between a point light and a mesh.
//  
//
//  PLUGIN INSTRUCTIONS
//
//  - create a point light and a poly plane (mesh)
//  - run the command as such: closestPointCmd <pointLightName> <planeName>
//  - a sphere will be created at the closest point
//  
//  Use the following script to automatically display closest point on the mesh (assuming 
//  a point light and a mesh with the names used here exist):
//
//  global proc closestPointExample()
//  {
//      closestPointCmd pointLight1 pPlane1;
//      select -r pointLight1;
//  }
//
//  scriptJob -ac "pointLight1.tx" closestPointExample;
//  scriptJob -ac "pointLight1.ty" closestPointExample;
//  scriptJob -ac "pointLight1.tz" closestPointExample;
//
//  scriptJob -ac "pointLight1.rx" closestPointExample;
//  scriptJob -ac "pointLight1.ry" closestPointExample;
//  scriptJob -ac "pointLight1.rz" closestPointExample;
//

/*

// Alternatively use the following code to demonstrate
// the closest point
//

    loadPlugin closestPointCmd;

    file -f -new;
    defaultPointLight(1, 1,1,1, 0, 0, 0,0,0, 1);
    move -r 0 5 0 ;
    polyPlane -w 1 -h 1 -sx 10 -sy 10 -ax 0 1 0 -cuv 2 -ch 1;
    scale -r 10 10 10 ;
    closestPointCmd pointLight1 pPlane1;

    file -f -new;
    defaultPointLight(1, 1,1,1, 0, 0, 0,0,0, 1);
    move -r 2 5 0 ;
    polyPlane -w 1 -h 1 -sx 10 -sy 10 -ax 0 1 0 -cuv 2 -ch 1;
    scale -r 10 10 10 ;
    closestPointCmd pointLight1 pPlane1;
    select -cl;

    unloadPlugin closestPointCmd ;

*/




// MAIN CLASS FOR THE closestPointCmd COMMAND:
class closestPointCmd : public MPxCommand
{
    public:
        closestPointCmd();
        virtual ~closestPointCmd();
        static void* creator();
        bool isUndoable() const;
        MStatus doIt(const MArgList&);
        MStatus undoIt();

        void createDisplay( MPointOnMesh& info, MMatrix& matrix);
};



// CONSTRUCTOR:
closestPointCmd::closestPointCmd()
{
}



// DESTRUCTOR:
closestPointCmd::~closestPointCmd()
{
}



// FOR CREATING AN INSTANCE OF THIS COMMAND:
void* closestPointCmd::creator()
{
   return new closestPointCmd;
}



// MAKE THIS COMMAND NOT UNDOABLE:
bool closestPointCmd::isUndoable() const
{
   return false;
}

MStatus closestPointCmd::doIt(const MArgList& args)
// Description:
//      Determine the closest point between the point
//      light and the mesh.
//      If it does, it displays the point.
{
    MStatus status = MStatus::kSuccess;

    if (args.length() != 2) 
    {
        MGlobal::displayError("Need 2 items!");
        return MStatus::kFailure;
    }

    MSelectionList activeList;
    int i;
    for ( i = 0; i < 2; i++)
    {
        MString strCurrSelection;
        status = args.get(i, strCurrSelection);
        if (MStatus::kSuccess == status) activeList.add(strCurrSelection);
    }


    MItSelectionList iter(activeList);
    MFnPointLight fnLight;  
    MFnDagNode dagNod;
    MFnDependencyNode fnDN;
    MDagPath pathToMesh;

    float fX = 0;
    float fY = 0;
    float fZ = 0;

    for ( ; !iter.isDone(); iter.next() )
    {
        MObject tempObjectParent, tempObjectChild;
        iter.getDependNode(tempObjectParent);

        if (tempObjectParent.apiType() == MFn::kTransform)
        {
            dagNod.setObject(tempObjectParent);
            tempObjectChild = dagNod.child(0, &status);
        }

        // check what type of object is selected
        if (tempObjectChild.apiType() == MFn::kPointLight)
        {
            MDagPath pathToLight;
            MERR_CHK(MDagPath::getAPathTo(tempObjectParent, pathToLight), 
                    "Couldn't get a path to the pointlight");
            MERR_CHK(fnLight.setObject(pathToLight), "Failure on assigning light");

            status = fnDN.setObject(tempObjectParent);

            MPlug pTempPlug = fnDN.findPlug("translateX", &status);
            if (MStatus::kSuccess == status)
            {
                pTempPlug.getValue(fX);
            }

            pTempPlug = fnDN.findPlug("translateY", &status);
            if (MStatus::kSuccess == status)
            {
                pTempPlug.getValue(fY);
            }

            pTempPlug = fnDN.findPlug("translateZ", &status);
            if (MStatus::kSuccess == status)
            {
                pTempPlug.getValue(fZ);
            }   
        }
        else if (tempObjectChild.apiType() == MFn::kMesh)
        {
            MERR_CHK(MDagPath::getAPathTo(tempObjectChild, pathToMesh), 
                "Couldn't get a path to the pointlight");
        }
        else
        {
            MGlobal::displayError("Need a pointlight and a mesh");
            return MStatus::kFailure;
        }
    }

    MMeshIntersector intersector;
    MMatrix matrix = pathToMesh.inclusiveMatrix();
    MObject node = pathToMesh.node();
    status = intersector.create( node, matrix );
    if ( status )
    {
        MPoint point(fX,fY,fZ);
        MPointOnMesh pointInfo;
        cout << "Using point: " << point << endl;
        status = intersector.getClosestPoint( point, pointInfo );
        if ( status )
        {
            createDisplay( pointInfo, matrix );
        }
        else
        {
            MGlobal::displayError("Failed to get closest point");
        }
    }
    else
    {
        MGlobal::displayError("Failed to create intersector");
    }

    return status;
}

// UNDO THE COMMAND
MStatus closestPointCmd::undoIt()
{
    MStatus status;
    // undo not implemented
    return status;
}

void closestPointCmd::createDisplay( MPointOnMesh& info, MMatrix& matrix )
{
    MPoint worldPoint( info.getPoint() );
    worldPoint = worldPoint * matrix;
    MVector worldNormal( info.getNormal() );
    worldNormal = worldNormal * matrix;
    worldNormal.normalize();

    MString strCommandString = "string $strBall[] = `polySphere -r 0.5`;";
    strCommandString += "$strBallName = $strBall[0];";
    strCommandString += "setAttr ($strBallName + \".tx\") ";
    strCommandString += worldPoint.x;
    strCommandString += ";";
    strCommandString += "setAttr ($strBallName + \".ty\") ";
    strCommandString += worldPoint.y;
    strCommandString += ";";
    strCommandString += "setAttr ($strBallName + \".tz\") ";
    strCommandString += worldPoint.z;
    strCommandString += ";";

    MGlobal::executeCommand(strCommandString);

    // Other info
    cout << "Normal: " << worldNormal << " face id: " << info.faceIndex() << 
        " triangle id: " << info.triangleIndex() << endl;
}

// INITIALIZE THE PLUGIN:
MStatus initializePlugin(MObject obj)
{
    MStatus status;
    MFnPlugin plugin(obj, PLUGIN_COMPANY, "9.0", "Any");
    status = plugin.registerCommand("closestPointCmd", closestPointCmd::creator);
    return status;
}

// UNINITIALIZE THE PLUGIN:
MStatus uninitializePlugin(MObject obj)
{
    MStatus status;
    MFnPlugin plugin(obj);
    plugin.deregisterCommand("closestPointCmd");
    return status;
}