#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)
, mySystem(NULL)
{
if (!myOffsets)
myOffsets = allocIndirect(32);
myVelocity.clear();
}
SOP_SParticle::~SOP_SParticle() {}
unsigned
SOP_SParticle::disableParms()
{
return 0;
}
void
SOP_SParticle::birthParticle()
{
GA_Offset vtxoff;
GEO_Point src(gdp->getPointMap(), GA_INVALID_OFFSET);
vtxoff = mySystem->giveBirth();
if (mySource)
{
if (mySourceNum >= mySource->getPointMap().indexSize())
mySourceNum = 0;
if (mySource->getPointMap().indexSize() > 0)
src = GEO_Point(mySource->getPointMap(),
mySource->pointOffset(mySourceNum));
mySourceNum++;
}
GEO_Point ppt(gdp->getPointMap(), GA_INVALID_OFFSET);
if (src.isGAValid())
{
ppt = GEO_Point(gdp->getPointMap(), gdp->vertexPoint(vtxoff));
ppt.setPos(src.getPos());
if (mySourceVel.isValid())
{
ppt.setValue<UT_Vector3>(myVelocity,
src.getValue<UT_Vector3>(mySourceVel));
}
else
ppt.setValue<UT_Vector3>(myVelocity, UT_Vector3(0, 0, 0));
}
else
{
ppt = GEO_Point(gdp->getPointMap(), gdp->vertexPoint(vtxoff));
ppt.setPos(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_Point *ppt, const UT_Vector3 &force)
{
float life, death;
float tinc;
UT_Vector3 vel, dir;
life = ppt->getValue<float>(myLife, 0);
death = ppt->getValue<float>(myLife, 1);
life += 1;
ppt->setValue<float>(myLife, life, 0);
if (life >= death)
return 0;
tinc = 1./30.;
vel = ppt->getValue<UT_Vector3>(myVelocity);
vel.x() += tinc*force.x();
vel.y() += tinc*force.y();
vel.z() += tinc*force.z();
ppt->setValue<UT_Vector3>(myVelocity, vel);
if (myCollision)
{
dir = vel * tinc;
GU_RayInfo info(dir.normalize());
UT_Vector3 start;
start = ppt->getPos();
if (myCollision->sendRay(start, dir, info) > 0)
return 0;
}
UT_Vector4 pos = ppt->getPos();
pos.x() += tinc*vel.x();
pos.y() += tinc*vel.y();
pos.z() += tinc*vel.z();
ppt->setPos(pos);
return 1;
}
void
SOP_SParticle::timeStep(fpreal now)
{
int i, nbirth;
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 (GA_Index i = 0; i < mySystem->getNumParticles(); i++)
{
if (!moveParticle(gdp->getGEOPoint(mySystem->vertexPoint(i)), force))
mySystem->deadParticle(i);
}
mySystem->deleteDead();
}
void
SOP_SParticle::initSystem()
{
if (!gdp) gdp = new GU_Detail;
if (gdp->getPointMap().indexSize() > 0 || myVelocity.isInvalid())
{
mySourceNum = 0;
gdp->clearAndDestroy();
mySystem = (GEO_PrimParticle *)gdp->appendPrimitive(GEO_PRIMPART);
mySystem->clearAndDestroy();
myVelocity = gdp->addFloatTuple(GA_ATTRIB_POINT, "v", 3);
if (myVelocity.isValid())
myVelocity.getAttribute()->setTypeInfo(GA_TYPE_VECTOR);
myLife = gdp->addFloatTuple(GA_ATTRIB_POINT, "life", 2);
}
}
OP_ERROR
SOP_SParticle::cookMySop(OP_Context &context)
{
fpreal 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 || !mySystem)
{
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->findFloatTuple(GA_ATTRIB_POINT, "v", 3);
if (mySourceVel.isInvalid())
mySourceVel = mySource->findFloatTuple(GA_ATTRIB_POINT, "N", 3);
}
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";
}