#include <GB/GB_AttributeRef.h>
#include <UT/UT_DSOVersion.h>
#include <UT/UT_Math.h>
#include <UT/UT_Interrupt.h>
#include <UT/UT_Vector3.h>
#include <UT/UT_Vector4.h>
#include <GEO/GEO_PrimPart.h>
#include <GU/GU_Detail.h>
#include <GU/GU_RayIntersect.h>
#include <PRM/PRM_Include.h>
#include <OP/OP_Director.h>
#include <OP/OP_Operator.h>
#include <OP/OP_OperatorTable.h>
#include "SOP_SParticle.h"
using namespace HDK_Sample;
void
newSopOperator(OP_OperatorTable *table)
{
table->addOperator(new OP_Operator("hdk_sparticle",
"Simple Particle",
SOP_SParticle::myConstructor,
SOP_SParticle::myTemplateList,
1,
2,
0));
}
static PRM_Name names[] = {
PRM_Name("reset", "Reset Frame"),
PRM_Name("birth", "Birth Rate"),
PRM_Name("force", "Force"),
};
static PRM_Default birthRate(10);
PRM_Template
SOP_SParticle::myTemplateList[] = {
PRM_Template(PRM_INT, 1, &names[0], PRMoneDefaults),
PRM_Template(PRM_INT_J, 1, &names[1], &birthRate),
PRM_Template(PRM_XYZ_J, 3, &names[2]),
PRM_Template(),
};
int *
SOP_SParticle::myOffsets = 0;
OP_Node *
SOP_SParticle::myConstructor(OP_Network *net, const char *name, OP_Operator *op)
{
return new SOP_SParticle(net, name, op);
}
SOP_SParticle::SOP_SParticle(OP_Network *net, const char *name, OP_Operator *op)
: SOP_Node(net, name, op)
{
if (!myOffsets)
myOffsets = allocIndirect(32);
GBclearAttributeRef(myVelocity);
}
SOP_SParticle::~SOP_SParticle() {}
unsigned
SOP_SParticle::disableParms()
{
return 0;
}
void
SOP_SParticle::birthParticle()
{
const GEO_Point *src;
GEO_Point *ppt;
GEO_ParticleVertex *pvtx;
float *life;
src = 0;
pvtx = mySystem->giveBirth();
if (mySource)
{
if (mySourceNum >= mySource->points().entries())
mySourceNum = 0;
if (mySource->points().entries() > 0)
src = mySource->points()(mySourceNum);
mySourceNum++;
}
if (src)
{
ppt = pvtx->getPt();
ppt->getPos() = src->getPos();
if (GBisAttributeRefValid(mySourceVel))
{
ppt->setValue<UT_Vector3>(myVelocity,
src->getValue<UT_Vector3>(mySourceVel));
}
else
ppt->setValue<UT_Vector3>(myVelocity, UT_Vector3(0, 0, 0));
}
else
{
ppt = pvtx->getPt();
ppt->getPos().assign(drand48()-.5, drand48()-.5, drand48()-.5, 1);
ppt->setValue<UT_Vector3>(myVelocity, UT_Vector3(0, 0, 0));
}
ppt->setValue<float>(myLife, 0, 0);
ppt->setValue<float>(myLife, 30+30*drand48(), 1);
}
int
SOP_SParticle::moveParticle(GEO_ParticleVertex *pvtx, const UT_Vector3 &force)
{
float life, death;
float tinc;
UT_Vector3 vel, dir;
life = pvtx->getPt()->getValue<float>(myLife, 0);
death = pvtx->getPt()->getValue<float>(myLife, 1);
life += 1;
pvtx->getPt()->setValue<float>(myLife, life, 0);
if (life >= death)
return 0;
tinc = 1./30.;
vel = pvtx->getPt()->getValue<UT_Vector3>(myVelocity);
vel.x() += tinc*force.x();
vel.y() += tinc*force.y();
vel.z() += tinc*force.z();
pvtx->getPt()->setValue<UT_Vector3>(myVelocity, vel);
if (myCollision)
{
dir = vel * tinc;
GU_RayInfo info(dir.normalize());
UT_Vector3 start;
start = pvtx->getPos();
if (myCollision->sendRay(start, dir, info) > 0)
return 0;
}
pvtx->getPos().x() += tinc*vel.x();
pvtx->getPos().y() += tinc*vel.y();
pvtx->getPos().z() += tinc*vel.z();
return 1;
}
void
SOP_SParticle::timeStep(float now)
{
int i, nbirth;
GEO_ParticleVertex *pvtx, *next = 0;
UT_Vector3 force;
force.assign(FX(now), FY(now), FZ(now));
nbirth = BIRTH(now);
if (error() >= UT_ERROR_ABORT)
return;
for (i = nbirth; i >= 0; i--)
birthParticle();
for (pvtx = mySystem->iterateInit(); pvtx; pvtx = next)
{
next = mySystem->iterateFastNext(pvtx);
if (!moveParticle(pvtx, force))
mySystem->deadParticle(pvtx);
}
}
void
SOP_SParticle::initSystem()
{
if (!gdp) gdp = new GU_Detail;
if (gdp->points().entries() > 0 || GBisAttributeRefInvalid(myVelocity))
{
mySourceNum = 0;
gdp->clearAndDestroy();
mySystem = (GEO_PrimParticle *)gdp->appendPrimitive(GEOPRIMPART);
mySystem->clearAndDestroy();
myVelocity = gdp->addPointAttrib("v", sizeof(UT_Vector3),
GB_ATTRIB_VECTOR, 0);
myLife = gdp->addPointAttrib("life", sizeof(float)*2,
GB_ATTRIB_FLOAT, 0);
}
}
OP_ERROR
SOP_SParticle::cookMySop(OP_Context &context)
{
double reset, currframe;
CH_Manager *chman;
const GU_Detail *collision;
if (lockInputs(context) >= UT_ERROR_ABORT)
return error();
OP_Node::flags().timeDep = 1;
chman = OPgetDirector()->getChannelManager();
currframe = chman->getSample(context.getTime());
reset = RESET();
if (currframe <= reset)
{
myLastCookTime = reset;
initSystem();
}
else
{
collision = inputGeo(1, context);
if (collision)
{
myCollision = new GU_RayIntersect;
myCollision->init(collision);
}
else myCollision = 0;
mySource = inputGeo(0, context);
if (mySource)
{
mySourceVel = mySource->findPointAttrib("v", sizeof(UT_Vector3),
GB_ATTRIB_VECTOR);
if (GBisAttributeRefInvalid(mySourceVel))
mySourceVel = mySource->findPointAttrib("N", sizeof(UT_Vector3),
GB_ATTRIB_VECTOR);
}
checkInputChanged(0, -1, myDetailGroupPair, (GU_Detail *)mySource, 0);
currframe += 0.05;
while (myLastCookTime < currframe)
{
timeStep(chman->getTime(myLastCookTime));
myLastCookTime += 1;
}
if (myCollision) delete myCollision;
select(GU_SPoint);
}
unlockInputs();
return error();
}
const char *
SOP_SParticle::inputLabel(unsigned inum) const
{
switch (inum)
{
case 0: return "Particle Source Geometry";
case 1: return "Collision Object";
}
return "Unknown source";
}