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

//
//      Translate selected objects
//
#include <maya/MIOStream.h>
#include <stdio.h>
#include <stdlib.h>

#include <maya/MPxCommand.h>
#include <maya/MFnPlugin.h>
#include <maya/MArgList.h>
#include <maya/MGlobal.h>
#include <maya/M3dView.h>

#include <maya/MDagPath.h>
#include <maya/MMatrix.h>
#include <maya/MFloatMatrix.h>

#include <maya/MRenderUtil.h>
#include <maya/MFloatPoint.h>
#include <maya/MFloatPointArray.h>
#include <maya/MFloatArray.h>
#include <maya/MFloatVectorArray.h>
#include <math.h>

#ifndef M_PI
#define M_PI        3.14159265358979323846  /* pi */
#endif

class sampleParticles : public MPxCommand
{
public:
                    sampleParticles() {};
    virtual         ~sampleParticles(); 

    MStatus         doIt( const MArgList& args );
    static void*    creator();
};

sampleParticles::~sampleParticles() {}

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

/*

    emits particles with color sampled from specified
    shading node/shading engine

*/
MStatus sampleParticles::doIt( const MArgList& args )
{
    unsigned int i;
    bool shadow = 0;
    bool reuse = 0;

    for ( i = 0; i < args.length(); i++ )
        if ( args.asString(i) == MString("-shadow") || 
            args.asString(i) == MString("-s") )
            shadow = 1;
        else if ( args.asString(i) == MString("-reuse") || 
            args.asString(i) == MString("-r") )
            reuse = 1;
        else
            break;
    if ( args.length() - i < 5 )
    {
        displayError( "Usage: sampleParticles [-shadow|-reuse] particleName <shadingEngine|shadingNode.plug> resX resY scale\n"
            "  Example: sampleParticles -shadow particle1 phong1SG 64 64 10;\n"
            "  Example: sampleParticles particle1 file1.outColor 128 128 5;\n" );
        return MS::kFailure;
    }
    if ( reuse && !shadow ) // can only reuse if shadow is turned on
        reuse = 0;

    MString particleName = args.asString( i );
    MString node = args.asString( i+1 );
    int resX = args.asInt( i+2 );
    int resY = args.asInt( i+3 );
    double scale = args.asDouble( i+4 );

    if ( scale <= 0.0 )
        scale = 1.0;

    MFloatArray uCoord, vCoord;
    MFloatPointArray points;
    MFloatVectorArray normals, tanUs, tanVs;

    if ( resX <= 0 )
        resX = 1;
    if ( resY <= 0 )
        resY = 1;

    MString command( "emit -o " );
    command += particleName;
    char tmp[2048];

    float stepU = (float) (1.0 / resX);
    float stepV = (float) (1.0 / resY);

    // stuff sample data by iterating over grid
    // Y is set to arch along the X axis

    int x, y;
    for ( y = 0; y < resY; y++ )
        for ( x = 0; x < resX; x++ )
        {
            uCoord.append( stepU * x );
            vCoord.append( stepV * y );

            float curY = (float) (sin( stepU * (x) * M_PI )*2.0);

            MFloatPoint curPt(
                (float) (stepU * x * scale),
                curY,
                (float) (stepV * y * scale ));

            MFloatPoint uPt(
                (float) (stepU * (x+1) * scale),
                (float) (sin( stepU * (x+1) * M_PI )*2.0),
                (float) (stepV * y * scale ));

            MFloatPoint vPt(
                (float) (stepU * (x) * scale),
                curY,
                (float) (stepV * (y+1) * scale ));

            MFloatVector du, dv, n;
            du = uPt-curPt;
            dv = vPt-curPt;

            n = dv^du;  // normal is based on dU x dV
            n = n.normal();
            normals.append( n );

            du.normal();
            dv.normal();
            tanUs.append( du );
            tanVs.append( dv );

            points.append( curPt );
        }

    // get current camera's world matrix

    MDagPath cameraPath;
    M3dView::active3dView().getCamera( cameraPath );
    MMatrix mat = cameraPath.inclusiveMatrix();
    MFloatMatrix cameraMat( mat.matrix );

    MFloatVectorArray colors, transps;
    if ( MS::kSuccess == MRenderUtil::sampleShadingNetwork( 
            node, 
            points.length(),
            shadow,
            reuse,

            cameraMat,

            &points,
            &uCoord,
            &vCoord,
            &normals,
            &points,
            &tanUs,
            &tanVs,
            NULL,   // don't need filterSize

            colors,
            transps ) )
    {
        fprintf( stderr, "%u points sampled...\n", points.length() );
        for ( i = 0; i < uCoord.length(); i++ )
        {
            sprintf( tmp, " -pos %g %g %g -at velocity -vv %g %g %g -at rgbPP -vv %g %g %g",
                points[i].x,
                points[i].y,
                points[i].z,

                normals[i].x,
                normals[i].y,
                normals[i].z,

                colors[i].x,
                colors[i].y,
                colors[i].z );

            command += MString( tmp );

            // execute emit command once every 512 samples
            if ( i % 512 == 0 )
            {
                fprintf( stderr, "%u...\n", i );
                MGlobal::executeCommand( command, false, false );
                command = MString( "emit -o " );
                command += particleName;
            }
        }

        if ( i % 512 )
            MGlobal::executeCommand( command, true, true );
    }
    else
    {
        displayError( node + MString(" is not a shading engine!  Specify node.attr or shading group node." ) );
    }

    return MS::kSuccess;
}


//
// The following routines are used to register/unregister
// the command we are creating within Maya
//
MStatus initializePlugin( MObject obj )
{
    MStatus   status;
    MFnPlugin plugin( obj, PLUGIN_COMPANY, "3.0", "Any");

    status = plugin.registerCommand( "sampleParticles", sampleParticles::creator );
    if (!status) {
        status.perror("registerCommand");
        return status;
    }

    return status;
}

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

    status = plugin.deregisterCommand( "sampleParticles" );
    if (!status) {
        status.perror("deregisterCommand");
        return status;
    }

    return status;
}