POP/POP_CircleForce.C

/*
 * Copyright (c) 2009
 *      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 <PRM/PRM_SpareData.h>
#include <OP/OP_Operator.h>
#include <OP/OP_OperatorTable.h>
#include "POP_CircleForce.h"

using namespace HDK_Sample;

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

static PRM_Name         names[] =
{
    PRM_Name("speed",         "Speed"),
    PRM_Name("falloff",         "Fall Off"),
    PRM_Name(0),
};

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


PRM_Template
POP_CircleForce::myTemplateList[] =
{
    PRM_Template(PRM_FLT_J,     1, &POPactivateName, PRMoneDefaults, 0,
                 &PRMunitRange),
    PRM_Template(PRM_STRING,    1, &POPsourceName, 0, 
                 &POP_Node::pointGroupMenu),
    PRM_Template(PRM_ORD,       1, &POPusecontextgeoName, 0,
                                    &POPusecontextgeoMenu),

    PRM_Template(PRM_STRING, PRM_TYPE_DYNAMIC_PATH, 1, &POPfullsopName,
                                0, 0, 0, 0, &PRM_SpareData::sopPath),

    PRM_Template(PRM_TOGGLE,    1, &POPignorexformName),
    PRM_Template(PRM_FLT,     1, &names[0], PRMoneDefaults),
    PRM_Template(PRM_FLT,     1, &names[1], PRMoneDefaults),

    PRM_Template()
};

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

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

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

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

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

unsigned
POP_CircleForce::disableParms (void)
{
    unsigned            changed = 0;

    int                 usecontextgeo = (USECONTEXTGEO() != 0);

    changed += enableParm("soppath", !usecontextgeo);

    return changed;
}

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

int *POP_CircleForce::myIndirect = 0;

POP_CircleForce::POP_CircleForce (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_CircleForce::~POP_CircleForce (void) 
{
}

OP_ERROR
POP_CircleForce::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;
    POP_FParam          speed;
    POP_FParam          falloff;
    UT_String           sourceName;
    const GB_PointGroup *sourceGroup = NULL;
    GU_Detail           gdpcopy;
    GU_Detail           *source;
    UT_Interrupt*       boss = UTgetInterrupt();

    GEO_PointTree               pttree;
    UT_PtrArray<UT_PtrArray<GEO_Point *> *> ring;
    GEO_AttributeHandle         sourcevel_gah;

    // 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;

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

    // Import our context geometry.
    source = getGeo(NULL,       // Use different geo path
                    data,       // Context data for context geos
                    t,  // Time to cooksops at 
                    1,  // Add extra inputs so we recook if SOP does 
                    IGNOREXFORM(), // Ignore object level transforms
                    &gdpcopy,   // Where to store result if we need
                                // to transform
                    0 /*&myPrevSop*/, // Tracks last SOP so we can detect changes
                    0 /*&doXform*/,   // If transform was needed
                    0 /*&xform*/,     // What the transform was
                    0 /*&myNeedResetAttractor*/ // Did the SOP change?
                    );
    if (!source)
    {
        addError(POP_NO_SOP_DETAIL);
        goto done;
    }

    // Import our context geo as guide geometry
    if (doUpdateViewport(data))
    {
        myGuide->merge(*source, 0 /*prim group*/, 0 /*merge prim groups*/);
    }

    // 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(speed, SPEED, getSpeed, mySpeed, POP_CircleForce);
    POP_FCACHE(falloff, FALLOFF, getFallOff, myFallOff, POP_CircleForce);

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

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

    sourcevel_gah = source->getPointAttribute("v");

    // Build a mapping from each point to all points connected
    // by an edge, ignoring duplicate connections.

    source->buildRingZeroPoints(ring, 0);

    // Build a search tree for all points
    pttree.build(source, 0 /*ptgrp*/);

    // 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, source, sourcevel_gah, ring, pttree, speed, falloff);
            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, source, sourcevel_gah, ring, pttree, speed, falloff);
                myCurrIter++;
            }
        }
    }

done:

    for (int i = 0; i < ring.entries(); i++)
    {
        delete ring[i];
    }

    cleanupDynamicVars();

    unlockInputs();

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

    return error();
}

void
POP_CircleForce::changePoint (GEO_Point* ppt, POP_ContextData* data, float t,
                            GU_Detail *source,
                            GEO_AttributeHandle &sourcevel_gah,
                            UT_PtrArray<UT_PtrArray<GEO_Point *> *> &ring,
                            GEO_PointTree &pttree,
                             POP_FParam speed,
                             POP_FParam falloff)
{
    int                 state;
    float               thespeed, thefalloff;
    float               dist;
    GEO_Point           *srcpt;
    UT_Vector3          pos, newv;

    if (data->isGuideOnly())
        return;

    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;

    pos = ppt->getPos();
    // Find nearest point on curve set.
    srcpt = pttree.findNearestPt(pos);

    if (!srcpt)
        return;

    // New velocity is the velocity of the closest point
    // plus a tangential spin component
    newv = 0;
    if (sourcevel_gah.isAttributeValid())
    {
        sourcevel_gah.setElement(srcpt);
        newv = sourcevel_gah.getV3();
    }

    UT_PtrArray<GEO_Point *>    *r;

    r = ring(srcpt->getNum());

    if (r && r->entries())
    {
        // Find closest segment
        float           mindist = -1, dist;
        float           minsign = 1;
        UT_Vector3      proj, a, b, minb, minproj;
        a = srcpt->getPos();
        for (int i = 0; i < r->entries(); i++)
        {
            b = (*r)(i)->getPos();
            proj = pos.projectOnSegment(a, b);
            dist = distance2(pos, proj);
            if (mindist < 0 || dist < mindist)
            {
                minb = b;
                mindist = dist;
                // This is a hack to determine a direction for the line,
                // ideally you'd build this from the polygon.
                if ((*r)(i)->getNum() < srcpt->getNum())
                    minsign = -1;
                else
                    minsign = 1;
                minproj = proj;
            }
        }
        thespeed = POP_PEVAL(speed);
        thefalloff = POP_PEVAL(falloff);

        if (dist < thefalloff*thefalloff)
        {
            // Simple fall off function
            float       weight;
            weight = 1.0 - (mindist / (thefalloff*thefalloff));
            weight *= weight;

            thespeed *= weight;

            // Now determine our direction perpendicular to a - minb
            UT_Vector3          tangent, toline, orbit;

            tangent = a - minb;
            tangent.normalize();
            tangent *= minsign;

            toline = pos - minproj;
            toline.normalize();
            
            orbit = cross(tangent, toline);
            orbit *= thespeed;

            newv += orbit;
        }
    }
    

    ppt->setValue<UT_Vector3>(data->getVelocityOffset(), newv);
}

Generated on Mon Jan 28 00:45:44 2013 for HDK by  doxygen 1.5.9