volumeLightCmd.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 <math.h>

#include <maya/MIOStream.h>
#include <maya/MSimple.h>
#include <maya/MFnNurbsCurve.h>
#include <maya/MPointArray.h>
#include <maya/MDoubleArray.h>
#include <maya/MPoint.h>

#include <maya/MRampAttribute.h>
#include <maya/MFnVolumeLight.h>
#include <maya/MFloatArray.h>
#include <maya/MIntArray.h>
#include <maya/MColorArray.h>
#include <maya/MColor.h>

DeclareSimpleCommand( volumeLight, PLUGIN_COMPANY, "5.0");
#define EQUAL(a,b) ( (((a-b) > -0.00001) && ((a-b) < 0.00001)) ? 1 : 0 )

MStatus volumeLight::doIt( const MArgList& args )
{
    MStatus stat;

    double arc = 180.0f;
    double coneEndRadius = 0.0f;
    MFnVolumeLight::MLightDirection volumeLightDirection = MFnVolumeLight::kOutward;
    MFnVolumeLight::MLightShape lightShape = MFnVolumeLight::kConeVolume;
    bool emitAmbient = true;

    unsigned    i;

    // Parse the arguments.
    for ( i = 0; i < args.length(); i++ )
    {
        if ( MString( "-a" ) == args.asString( i, &stat )
                && MS::kSuccess == stat)
        {
            double tmp = args.asDouble( ++i, &stat );
            if ( MS::kSuccess == stat )
                arc = tmp;
        }
        else if ( MString( "-c" ) == args.asString( i, &stat )
                && MS::kSuccess == stat)
        {
            double tmp = args.asDouble( ++i, &stat );
            if ( MS::kSuccess == stat )
                coneEndRadius = tmp;
        }
        else if ( MString( "-e" ) == args.asString( i, &stat )
                && MS::kSuccess == stat)
        {
            bool tmp = args.asBool( ++i, &stat );
            if ( MS::kSuccess == stat )
                emitAmbient = tmp;
        }

    }

    MFnVolumeLight light;

    light.create( true, &stat);

    cout<<"What's up?";

    if ( MS::kSuccess != stat )
    {
        cout<<"Error creating light."<<endl;
        return stat;
    }
    
    stat = light.setArc ((float)arc);
    if ( MS::kSuccess != stat )
    {
        cout<<"Error setting \"arc\" attribute."<<endl;
        return stat;
    }

    stat = light.setVolumeLightDirection (volumeLightDirection);
    if ( MS::kSuccess != stat )
    {
        cout<<"Error setting \"volumeLightDirection\" attribute."<<endl;
        return stat;
    }

    stat = light.setConeEndRadius ((float)coneEndRadius);
    if ( MS::kSuccess != stat )
    {
        cout<<"Error setting \"coneEndRadius\" attribute."<<endl;
        return stat;
    }

    stat = light.setEmitAmbient (emitAmbient);
    if ( MS::kSuccess != stat )
    {
        cout<<"Error setting \"emitAmbient\" attribute."<<endl;
        return stat;
    }

    stat = light.setLightShape (lightShape);
    if ( MS::kSuccess != stat )
    {
        cout<<"Error setting \"lightShape\" attribute."<<endl;
        return stat;
    }

    double arcGet = light.arc (&stat);
    if ( MS::kSuccess != stat || arcGet != arc)
    {
        cout<<"Error getting \"arc\" attribute."<<endl;
        return stat;
    }

    MFnVolumeLight::MLightDirection volumeLightDirectionGet = light.volumeLightDirection (&stat);
    if ( MS::kSuccess != stat || volumeLightDirectionGet != volumeLightDirection)
    {
        cout<<"Error getting \"volumeLightDirection\" attribute."<<endl;
        return stat;
    }

    double coneEndRadiusGet = light.coneEndRadius (&stat);
    if ( MS::kSuccess != stat || coneEndRadiusGet != coneEndRadius)
    {
        cout<<"Error getting \"coneEndRadius\" attribute."<<endl;
        return stat;
    }

    bool emitAmbientGet = light.emitAmbient (&stat);
    if ( MS::kSuccess != stat || emitAmbientGet != emitAmbient)
    {
        cout<<"Error getting \"emitAmbient\" attribute."<<endl;
        return stat;
    }

    MFnVolumeLight::MLightShape lightShapeGet = light.lightShape (&stat);
    if ( MS::kSuccess != stat || lightShapeGet != lightShape)
    {
        cout<<"Error getting \"lightShape\" attribute."<<endl;
        return stat;
    }

    // Get reference to the penumbra ramp.
    MRampAttribute ramp = light.penumbraRamp (&stat);
    if ( MS::kSuccess != stat )
    {
        cout<<"Error getting \"penumbraRamp\" attribute."<<endl;
        return stat;
    }

    MFloatArray a, b;
    MIntArray c,d;

    // Get the entries in the ramp
    ramp.getEntries (d, a, b, c, &stat);
    if ( MS::kSuccess != stat )
    {
        cout<<"Error getting entries from \"penumbraRamp\" attribute."<<endl;
        return stat;
    }

    // There should be 2 entries by default.
    if (d.length() != 2)
    {
        cout<<"Invalid number of entries in \"penumbraRamp\" attribute."<<endl;
        return stat;
    }

    MFloatArray a1, b1;
    MIntArray c1;

    // Prepare an array of entries to add.
    // In this case we are just adding 1 more entry
    // at position 0.5 with a curve value of 0.25 and a linear interpolation.
    a1.append (0.5f);
    b1.append (0.25f);
    c1.append (MRampAttribute::kLinear);

    // Add it to the curve ramp
    ramp.addEntries (a1, b1, c1, &stat);
    if ( MS::kSuccess != stat)
    {
        cout<<"Error adding entries to \"penumbraRamp\" attribute."<<endl;
        return stat;
    }


    // Get the entries to make sure that the above add actually worked.
    MFloatArray a2, b2;
    MIntArray c2,d2;
    ramp.getEntries (d2, a2, b2, c2, &stat);
    if ( MS::kSuccess != stat )
    {
        cout<<"Error getting entries from \"penumbraRamp\" attribute."<<endl;
        return stat;
    }
    if ( a.length() + a1.length() != a2.length())
    {
        cout<<"Invalid number of entries in \"penumbraRamp\" attribute."<<endl;
        return stat;
    }

    // Now try to interpolate the value at a point
    float newVal = -1;
    ramp.getValueAtPosition(.3f, newVal, &stat);

    if ( MS::kSuccess != stat )
    {
        cout<<"Error interpolating value from \"penumbraRamp\" attribute."<<endl;
        return stat;
    }
    if ( !EQUAL(newVal, .15f))
    {
        cout<<"Invalid interpolation in  \"penumbraRamp\" expected .15 got "<<newVal
            <<" ."<<endl;
    }

    // Try to delete an entry in an incorrect manner. 
    // This delete will work because there is an entry at 0, 
    // However we should never do it this way, because the entries
    // array can become sparse, so trying to delete an entry without 
    // checking whether an entry exists at that index can cause a failure.
    MIntArray entriesToDelete;
    entriesToDelete.append (0);
    ramp.deleteEntries (entriesToDelete, &stat);
    if ( MS::kSuccess != stat )
    {
        cout<<"Error deleting entries from \"penumbraRamp\" attribute."<<endl;
        return stat;
    }

    // Check to see whether the above delete worked.
    // As mentioned earlier it did work, but we shouldn't do it this way.
    // To illustrate why we shouldn't do it this way, we'll try to delete
    // entry at index 0 ( this no longer exists)
    ramp.getEntries (d2, a2, b2, c2, &stat);

    if ( a2.length() != 2)
    {
        cout<<"Invalid number of entries in \"penumbraRamp\" attribute."<<endl;
        return stat;
    }
    // Trying to delete entry at 0.
    entriesToDelete.clear();
    entriesToDelete.append (0);
    ramp.deleteEntries (entriesToDelete, &stat);

    // It will fail because no entry exists.
    if ( MS::kSuccess == stat)
    {
        cout<<"Error deleting entries from \"penumbraRamp\" attribute."<<endl;
        return stat;
    }
    if ( a2.length() != 2)
    {
        cout<<"Invalid number of entries in \"penumbraRamp\" attribute."<<endl;
        return stat;
    }

    // The proper way to delete is to retrieve the index by calling "getEntries"
    ramp.getEntries (d2, a2, b2, c2, &stat);
    entriesToDelete.clear();
    entriesToDelete.append (d2[0]);

    // Delete the first logical entry in the entry array.
    ramp.deleteEntries (entriesToDelete, &stat);
    if ( MS::kSuccess != stat)
    {
        cout<<"Error deleting entries from \"penumbraRamp\" attribute."<<endl;
        return stat;
    }

    // There should be only 1 entry left.
    ramp.getEntries (d2, a2, b2, c2, &stat);
    if ( MS::kSuccess != stat)
    {
        cout<<"Error getting entries from \"penumbraRamp\" attribute."<<endl;
        return stat;
    }
    entriesToDelete.clear();
    entriesToDelete.append (d2[0]);

    // Can't delete the last entry, should return failure.
    ramp.deleteEntries (entriesToDelete, &stat);
    if ( MS::kSuccess == stat)
    {
        cout<<"Error deleting entries from \"penumbraRamp\" attribute."<<endl;
        return stat;
    }

    ramp.setPositionAtIndex (0.0f, d2[0], &stat);
    if ( MS::kSuccess != stat)
    {
        printf("Error setting position at index: %d, of \"penumbraRamp\" attribute.\n", d2[0]);
        return stat;
    }

    ramp.setValueAtIndex (1.0f, d2[0], &stat);
    if ( MS::kSuccess != stat)
    {
        printf("Error setting value at index: %d, of \"penumbraRamp\" attribute.\n", d2[0]);
        return stat;
    }

    ramp.setInterpolationAtIndex (MRampAttribute::kNone, d2[0], &stat);
    if ( MS::kSuccess != stat)
    {
        printf("Error setting interpolation at index: %d, of \"penumbraRamp\" attribute.\n", d2[0]);
        return stat;
    }

    MRampAttribute ramp2 = light.colorRamp (&stat);
    if ( MS::kSuccess != stat)
    {
        cout<<"Error getting  \"colorRamp\" attribute."<<endl;
        return stat;
    }   
    MFloatArray a3;
    MColorArray b3;
    MIntArray c3,d3;

    // Get the entries in the ramp
    ramp2.getEntries (d3, a3, b3, c3, &stat);
    if ( MS::kSuccess != stat)
    {
        cout<<"Error getting entries from \"colorRamp\" attribute."<<endl;
        return stat;
    }
    // There should be 2 entries by default.
    if ( d3.length() != 2)
    {
        cout<<"Invalid number of entries in \"colorRamp\" attribute."<<endl;
        return stat;
    }

    MFloatArray a4;
    MColorArray b4;
    MIntArray c4;

    // Prepare an array of entries to add.
    // In this case we are just adding 1 more entry
    // at position 0.5 withe curve value of 0.5 and a linear interpolation.
    a4.append (0.5f);
    b4.append (MColor (0.0f, 0.0f, 0.75f));
    c4.append (MRampAttribute::kLinear);

    // Add it to the curve ramp
    ramp2.addEntries (a4, b4, c4, &stat);
    if ( MS::kSuccess != stat)
    {
        cout<<"Error adding entries to \"colorRamp\" attribute."<<endl;
        return stat;
    }
    // Get the entries to make sure that the above add actually worked.
    MFloatArray a5;
    MColorArray b5;
    MIntArray c5,d5;
    ramp2.getEntries (d5, a5, b5, c5, &stat);
    if ( MS::kSuccess != stat)
    {
        cout<<"Error getting entries from \"colorRamp\" attribute."<<endl;
        return stat;
    }
    if (  a3.length() + a4.length() != a5.length())
    {
        cout<<"Invalid number of entries in \"colorRamp\" attribute."<<endl;
        return stat;
    }

    // Now try to interpolate the color at a point
    MColor newCol(0.0, 0.0, 0.0);
    ramp2.getColorAtPosition(.3f, newCol, &stat);

    if ( MS::kSuccess != stat )
    {
        cout<<"Error interpolating color from \"penumbraRamp\" attribute."<<endl;
        return stat;
    }
    if ( !EQUAL(newCol[2], .45))
    {
        cout<<"Invalid color interpolation in  \"colorRamp\" expected .45 got "<<newCol[2]<<endl;
    }

    MColor clr (0.5, 0.5, 0.0);
    ramp2.setColorAtIndex (clr, d5[0], &stat);
    if ( MS::kSuccess != stat)
    {
        cout<<"Error setting color at index: "<<d5[0]
            <<", of \"colorRamp\" attribute."<<endl;
        return stat;
    }

    ramp2.setInterpolationAtIndex (MRampAttribute::kSpline, d5[1], &stat);
    if ( MS::kSuccess != stat)
    {
        cout<<"Error setting interpolation at index: "<<d5[1]
            <<", of \"colorRamp\" attribute."<<endl;
        return stat;
    }

    return stat;
}