POP/POP_LocalForce.C

/*
 * Copyright (c) 2013
 *      Side Effects Software Inc.  All rights reserved.
 *
 * Redistribution and use of Houdini Development Kit samples in source and
 * binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. The name of Side Effects Software may not be used to endorse or
 *    promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
 * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *----------------------------------------------------------------------------
 */

#include <UT/UT_DSOVersion.h>
#include <UT/UT_Matrix3.h>
#include <UT/UT_Defines.h>
#include <GEO/GEO_PrimPart.h>
#include <GEO/GEO_Point.h>
#include <GU/GU_Detail.h>
#include <GU/GU_PrimTube.h>
#include <PRM/PRM_Include.h>
#include <OP/OP_Operator.h>
#include <OP/OP_OperatorTable.h>
#include "POP_LocalForce.h"

using namespace HDK_Sample;

//-----------------------------------------------------------------------------

static PRM_Name         names[] =
{
    PRM_Name("origin",         "Origin"),
    PRM_Name("dir",            "Direction"),
    PRM_Name("radius",         "Radius"),
    PRM_Name("length",         "Length"),
    PRM_Name("strength",       "Strength"),
    PRM_Name(0),
};

PRM_Range  lengthRange(PRM_RANGE_RESTRICTED, 0.001, PRM_RANGE_UI, 5);

static PRM_Default      dirDefaults[] =
{
    PRM_Default(0), PRM_Default(0), PRM_Default(1)
};

PRM_Template
POP_LocalForce::myTemplateList[] =
{
    PRM_Template(PRM_FLT_J,     1, &POPactivateName, PRMoneDefaults, 0,
                 &PRMunitRange),
    PRM_Template(PRM_STRING,    1, &POPsourceName, 0, 
                 &POP_Node::pointGroupMenu),
    PRM_Template(PRM_XYZ_J,     3, &names[0]),
    PRM_Template(PRM_DIRECTION, 3, &names[1], dirDefaults),
    PRM_Template(PRM_FLT_J,     1, &names[2], PRMoneDefaults),
    PRM_Template(PRM_FLT_J,     1, &names[3], PRMoneDefaults, 0, &lengthRange),
    PRM_Template(PRM_FLT_J,     1, &names[4], PRMoneDefaults),
    PRM_Template(PRM_TOGGLE,    1, &POPignoremassName),
    PRM_Template(PRM_TOGGLE,    1, &POPoverridemassName),
    PRM_Template(PRM_FLT_J,     1, &POPmassName, PRMoneDefaults, 0,
                 &POPmassRange),

    PRM_Template()
};

OP_TemplatePair
POP_LocalForce::myTemplatePair (myTemplateList, &POP_LocalVar::myTemplatePair);

OP_VariablePair
POP_LocalForce::myVariablePair (0, &POP_LocalVar::myVariablePair);

//-----------------------------------------------------------------------------

void
newPopOperator (OP_OperatorTable* table)
{
    table->addOperator(
        new OP_Operator("localforce",                      // Name
                        "LocalForce",                      // English
                        POP_LocalForce::myConstructor,     // "Constructor"
                        &POP_LocalForce::myTemplatePair,   // simple parms
                        1,                                 // MinSources
                        1,                                 // MaxSources
                        &POP_LocalForce::myVariablePair)); // variables
}

//-----------------------------------------------------------------------------

unsigned
POP_LocalForce::disableParms (void)
{
    unsigned            changed = 0;
    int                 ignoremass = IGNOREMASS();
    int                 overridemass = OVERRIDEMASS();


    changed += enableParm("mass", overridemass && !ignoremass);
    changed += enableParm("overridemass", !ignoremass);

    return changed;
}

OP_Node*
POP_LocalForce::myConstructor (OP_Network* net, const char* name, 
                               OP_Operator* entry)
{
    return new POP_LocalForce(net, name, entry);
}

int *POP_LocalForce::myIndirect = 0;

POP_LocalForce::POP_LocalForce (OP_Network* net, const char* name, 
                                OP_Operator* entry)
               :POP_LocalVar (net, name, entry) 
{
    if (!myIndirect)
        myIndirect = allocIndirect(sizeof(myTemplateList)/sizeof(PRM_Template));

    // This POP has guide geometry so turn on the template flag by default.
    setTemplate(1);

    // Create the POP_Guide for showing in the viewport, and pass ownership
    // to the guide handle.  The handle permits external parties to have an
    // indirect reference to the POP_Guide object without worrying about it
    // being deleted.  Any references to this handle after this object is
    // destroyed will point to a null guide.
    myGuide = new POP_Guide;
    myGuideHandle.allocateAndSet(myGuide);
}

POP_LocalForce::~POP_LocalForce (void) 
{
}

OP_ERROR
POP_LocalForce::cookPop (OP_Context &context)
{
    float               t = context.getTime();
    int                 thread = context.getThread();
    POP_ContextData*    data = (POP_ContextData*) context.getData();
    GEO_PrimParticle*   part;
    GEO_ParticleVertex* pvtx;
    UT_Matrix4          xform;
    UT_Vector3          origin;
    UT_Vector3          dir;
    float               radius;
    float               length;
    POP_FParam          strength;
    POP_FParam          mass;
    GB_AttributeRef     massOffset;
    UT_String           sourceName;
    const GB_PointGroup *sourceGroup = NULL;
    GU_PrimTubeParms    parms;
    GU_CapOptions       caps;
    UT_Interrupt*       boss = UTgetInterrupt();


    // Add this to the list of guide geometries to show in the POP_ContextData.
    if (doUpdateViewport(data))
    {
        data->appendGuide(this, myGuideHandle);
    }

    if (lockInputs(context) >= UT_ERROR_ABORT)
        return(error());

    setupDynamicVars(data);

    // Build the particle list that this POP needs to process.
    if (buildParticleList(context) >= UT_ERROR_ABORT)
        goto done;

    // The parameters defining the location of the local force cannot
    // be dependent on local variables. Turn them "off" first when
    // evaluating these values.
    allowVar(0);
    origin.assign(ORIGINX(t), ORIGINY(t), ORIGINZ(t));
    dir.assign(DIRX(t), DIRY(t), DIRZ(t));
    dir.normalize();
    radius = RADIUS(t);
    radius = UTabs(radius);
    length = LENGTH(t);
    allowVar(1);

    // Create the guide geometry. Create a tube at the origin and of the 
    // appropriate radius and height. Then instance it at the position
    // of the origin and facing the requested direction.
    if (doUpdateViewport(data))
    {
        parms.taper = 1.0f;
        buildQuadricXform(parms.xform, 0, 0, length / 2, 
                          radius, radius, length, 'x');
        xform.instance(origin, dir, 1.0,
                        0, 0, 0, 0, 0);
        parms.xform *= xform;
        parms.gdp = myGuide;
        GU_PrimTube::build(parms, caps, GEOPRIMTUBE);
    }

    // check activation event
    if (!checkActivation(data, (POP_FParam) &POP_LocalForce::ACTIVATE))
        goto done;

    // Point group to act on.
    SOURCE(sourceName);
    if (sourceName.isstring())
    {
        sourceGroup = parsePointGroups((const char*) sourceName,
                                       data->getDetail());
        if (!sourceGroup)
        {
            addError(POP_BAD_GROUP, sourceName);
            goto done;
        }
    }

    // In order for the local variables to work, we must initialize them.
    setupVars(data, sourceGroup);

    // For efficiency, the parameter values are cached if they do not
    // change per particle (e.g. constants or expressions not dependent
    // on particle variables). Use these macros to cache the values.
    POP_FCACHE(strength, STRENGTH, getStrength, myStrength, POP_LocalForce);

    if (OVERRIDEMASS())
    {
        // When the mass is overridden, we want to evaluate the field for 
        // every particle so cache it like usual.
        massOffset.clear();
        POP_FCACHE(mass, MASS, getMass, myMass, POP_LocalForce);
    }
    else
    {
        // We want to use the mass associated with the particle, so find
        // out the attribute offset. If there isn't any attribute, use
        // a value of 1.0f and stuff it into the cache.
        massOffset = data->getMassOffset();
        if (massOffset.isInvalid())
        {
            myMass = 1.0f;
            mass = (POP_FParam) &POP_LocalForce::getMass;
        }
    }

    if (IGNOREMASS())
    {
        // We want to ignore the mass so just use a value of 1.0f 
        // and stuff it into the cache.
        massOffset.clear();
        myMass = 1.0f;
        mass = (POP_FParam) &POP_LocalForce::getMass;
    }

    if (boss->opInterrupt())
        goto done;

    if (error() >= UT_ERROR_ABORT)
        goto done;

    // Need to set this for the local variables. It represents the
    // $ITER variable. Also need to keep track of myCurrPt which
    // tells the local variables which point to retrieve values from.
    myCurrIter = 0;

    // If a source group is specified, then process only the points in
    // that group.
    if (sourceGroup)
    {
        FOR_ALL_GROUP_POINTS(data->getDetail(), sourceGroup, myCurrPt)
        {
            changePoint(myCurrPt, data, t, origin, dir, radius, length,
                        strength, massOffset, mass);
            myCurrIter++;
        }
    }
    else
    {
        // Process each particle primitive fed into this POP and then
        // each particle within that primitive.
        for (part = myParticleList.iterateInit() ;
             part ; part = myParticleList.iterateNext())
        {
            if (boss->opInterrupt())
                goto done;

            for (pvtx = part->iterateInit() ; pvtx ; pvtx = pvtx->next)
            {
                myCurrPt = pvtx->getPt();
                changePoint(myCurrPt, data, t, origin, dir, radius, length,
                            strength, massOffset, mass);
                myCurrIter++;
            }
        }
    }

done:

    cleanupDynamicVars();

    unlockInputs();

    // Reset this so something is defined for UI dialog updates.
    myCurrPt = NULL;

    return error();
}

void
POP_LocalForce::changePoint (GEO_Point* ppt, POP_ContextData* data, float t,
                             const UT_Vector3& origin, const UT_Vector3& dir, 
                             float radius, float length,
                             POP_FParam strength,
                             const GB_AttributeRef &massOffset,
                             POP_FParam mass)
{
    int                 state;
    UT_Vector3          accel;
    UT_Vector3          D;
    float               s;
    float               m;
    float               dist;


    state = ppt->getValue<int>(data->getStateOffset());

    // Don't need to move stopped or dying points.
    if ((state & PART_STATE_STOPPED) || (state & PART_STATE_DYING))
        return;

    // Find out what mass to use. If the attribute offset was passed in,
    // retrieve it from the point. Otherwise, evaluate the parameter.
    if (massOffset.isValid())
        m = ppt->getValue<float>(massOffset);
    else
        m = POP_PEVAL(mass);

    // Figure out the vector from the point to the origin of the tube.
    D = ppt->getPos();
    D -= origin;
    dist = D.normalize();

    // The dot product of this vector with the normalized tube direction
    // will tell us if the point is within the length of the tube. 
    // If it's negative, the point is behind the tube. 
    t = dot(D, dir);
    if (t < 0 || t * dist > length)
        return;

    // Figure out the distance perpendicular to the axis of the tube.
    D *= dist;
    D -= dir * t;

    // If it's within the radius, apply the acceleration.
    if (D.length() < radius)
    {
        accel = ppt->getValue<UT_Vector3>(data->getAccelOffset());

        // Use the POP_PEVAL maxro to evaluate the parameter. If the value
        // is cached, it retrieves the value from cache otherwise, it evaluates
        // the parameter for this particle.
        s = POP_PEVAL(strength);

        // Don't want a divide by 0.
        if (UTequalZero(m))
            m = 0.01;

        s /= m;

        if (!data->isGuideOnly())
        {
            // Apply the acceleration in the direction of the axis. Note that
            // many POPs can alter a particle's acceleration, so the particle's
            // acceleration should be the sum of all these contributions.
            accel.x() += dir.x() * s; 
            accel.y() += dir.y() * s; 
            accel.z() += dir.z() * s; 
            ppt->setValue<UT_Vector3>(data->getAccelOffset(), accel);
        }

        if (doUpdateViewport(data))
            addAccelGuide(ppt, dir.x() * s, dir.y() * s, dir.z() * s, 
                          data->myTimeInc);
    }
}

Generated on Mon Jan 28 00:26:19 2013 for HDK by  doxygen 1.5.9