POP/POP_RadialBirth.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 <GEO/GEO_PrimPart.h>
#include <GEO/GEO_Point.h>
#include <GU/GU_Detail.h>
#include <PRM/PRM_Include.h>
#include <OP/OP_Operator.h>
#include <OP/OP_OperatorTable.h>
#include "POP_RadialBirth.h"

using namespace HDK_Sample;

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

static PRM_Name         names[] =
{
    PRM_Name("center",          "Center"),
    PRM_Name("distance",        "Distance"),
    PRM_Name("speed",           "Speed"),
    PRM_Name("birthrate",       "Birth Rate"),
    PRM_Name("group",           "Birth Group"),
    PRM_Name(0)
};

static PRM_Range        birthRange(PRM_RANGE_RESTRICTED, 0, PRM_RANGE_UI, 10);

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

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

PRM_Template
POP_RadialBirth::myTemplateList[] =
{
    PRM_Template(PRM_FLT_J,     1, &POPactivateName, PRMoneDefaults, 0,
                 &PRMunitRange),
    PRM_Template(PRM_XYZ_J,     3, &names[0]),
    PRM_Template(PRM_FLT_J,     2, &names[1], distanceDefaults),
    PRM_Template(PRM_FLT_J,     2, &names[2], speedDefaults),
    PRM_Template(PRM_INT_J,     1, &names[3], PRMfourDefaults, 0, &birthRange),
    PRM_Template(PRM_STRING,    1, &names[4]),
    PRM_Template(PRM_FLT_J,     1, &POPlifeName, &POPlifeDefault),
    PRM_Template(PRM_FLT_J,     1, &POPlifevarName),
    PRM_Template(PRM_INT_J,     1, &POPoriginindexName),
    PRM_Template()
};

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

void
newPopOperator (OP_OperatorTable* table)
{
    table->addOperator(
        new OP_Operator("hdk_radialbirth",               // Name
                        "Radial Birth",                  // English
                        POP_RadialBirth::myConstructor,  // "Constructor"
                        POP_RadialBirth::myTemplateList, // simple parms
                        0,                               // MinSources
                        0,                               // MaxSources
                        0,                               // variable pair
                        OP_FLAG_GENERATOR));             // op flags
}

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

unsigned
POP_RadialBirth::disableParms (void)
{
    return 0;
}

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

int *POP_RadialBirth::myIndirect = 0;

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

    myLife        = 100;
    myLifeVar     = 0;
    myOriginIndex = 0;
    myCenter.assign(0, 0, 0);
    mySpeedMin    = 0;
    mySpeedMax    = 1;
}

POP_RadialBirth::~POP_RadialBirth (void) 
{
}

OP_ERROR
POP_RadialBirth::cookPop (OP_Context& context)
{
    POP_ContextData*    data = (POP_ContextData*) context.getData();
    float               t = context.getTime();
    GEO_PrimParticle*   part;
    GEO_ParticleVertex* pvtx;
    UT_String           birthName;
    GB_PointGroup*      birthGroup = NULL;
    int                 born;
    UT_Vector4          birthpos;
    UT_Vector3          pos;
    float               distmin;
    float               distmax;
    float               tmp;


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

    // The myParticleList variable determines which particle primitives
    // this POP processes. Since this is a generator, it only includes
    // a single primitive, the one that it creates. The getPrimPart()
    // function will create a new particle primitive in the detail if
    // it hasn't been created yet.
    part = data->getPrimPart(this);
    initParticleList(context, part);

    // Check if the cook is being requested just because we need an
    // update of the guide geometry. If so, don't need to do anything else.
    if (data->isGuideOnly())
        goto done;

    // Group that will contain this frame's birthed particles.
    GROUP(birthName);
    if (birthName.isstring())
    {
        forceValidGroupName(birthName, UT_ERROR_WARNING);
        birthGroup = data->getDetail()->newPointGroup((const char*) birthName);
    }

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

    // Update parameter values for this frame.
    myLife      = LIFE(t);
    myLifeVar   = LIFEVAR(t);
    mySpeedMin  = SPEEDMIN(t);
    mySpeedMax  = SPEEDMAX(t);
    if (mySpeedMax < mySpeedMin)
    {
        tmp = mySpeedMin;
        mySpeedMin = mySpeedMax;
        mySpeedMax = tmp;
    }

    born        = BIRTHRATE(t);
    distmin     = DISTANCEMIN(t);
    distmax     = DISTANCEMAX(t);
    if (distmax < distmin)
    {
        tmp = distmin;
        distmin = distmax;
        distmax = tmp;
    }
    myOriginIndex = ORIGININDEX();
    myCenter.assign(CENTERX(t), CENTERY(t), CENTERZ(t));

    // Birth required number of particles.
    while (born > 0)
    {
        // Using the random() function in the POP_ContextData will 
        // guarantee deterministic behavior when POPs are recooked.
        pos.assign(data->random() - 0.5f, 
                   data->random() - 0.5f, 
                   data->random() - 0.5f);
        pos.normalize();
        pos *= (distmin + data->random() * (distmax - distmin));
        pos += myCenter;

        // Birth a new particle into the particle primitive. The
        // initial position is given. The velocity attribute is
        // set in setAtrtib. The 3 NULLs in the call represent the
        // birth map, general attribute inheritance and parental
        // attribute inheritance. Since we are birthing particles on
        // their own and not inheriting from anything, they are all
        // set to NULL so birthed particles will contain default
        // attribute values.
        birthpos = pos;
        pvtx = 
            birthParticle(data, part, birthpos, NULL, NULL, NULL,
                          (POP_IntFunc) &POP_RadialBirth::getOriginIndex,
                          0, POP_ORIGIN_INDEX, 
                          (POP_FloatFunc) &POP_RadialBirth::getLifetime, 
                          (POP_BirthAttribFunc) &POP_RadialBirth::setAttrib);
        if (!pvtx)
            break;

        if (birthGroup)
            birthGroup->add(pvtx->getPt());

        born--;
    }

done:

    unlockInputs();

    return error();
}

float
POP_RadialBirth::getLifetime (POP_ContextData* data)
{
    return myLife + 2.0f * (data->random() - 0.5f) * myLifeVar;
}

int
POP_RadialBirth::getOriginIndex (POP_ContextData* /*data*/)
{
    return myOriginIndex;
}

void
POP_RadialBirth::setAttrib (POP_ContextData* data, GEO_Point* ppt, 
                            UT_Vector3* vel, void*)
{
    // Choose a random velocity that goes radially from the center.
    *vel = ppt->getPos();
    *vel -= myCenter;
    vel->normalize();
    *vel *= (mySpeedMin + data->random() * (mySpeedMax - mySpeedMin));
}

void
POP_RadialBirth::addAttrib (void* userdata)
{
    POP_ContextData*            data = (POP_ContextData*) userdata;

    // Add the "origin" attribute.
    addOriginAttrib(data);
}

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