#include "SIM_SolverHair.h"
#include <UT/UT_DSOVersion.h>
#include <GU/GU_PrimPoly.h>
#include <PRM/PRM_Include.h>
#include <SIM/SIM_DopDescription.h>
#include <SIM/SIM_GeometryCopy.h>
#include <SIM/SIM_DataFilter.h>
#include <SIM/SIM_Object.h>
#include <SIM/SIM_ObjectArray.h>
#include <SIM/SIM_Engine.h>
#include <SIM/SIM_Force.h>
using namespace HDK_Sample;
void
initializeSIM(void *)
{
IMPLEMENT_DATAFACTORY(SIM_SolverHair);
}
namespace HDK_Sample {
class SIM_HairForceCallback : public SIM_PointForceCallback,
public SIM_EachDataCallback
{
public:
SIM_HairForceCallback(GU_Detail &gdp,
const SIM_Object &object,
const UT_DMatrix4 &xform,
fpreal timestep,
const GB_AttributeRef &massoffset,
const GB_AttributeRef &veloffset);
virtual ~SIM_HairForceCallback();
virtual void callbackConst(const SIM_Data *data,
const char *name);
virtual void forceCallback(int ptnum,
const UT_Vector3 &force,
const UT_Vector3 &torque);
private:
GU_Detail &myGdp;
const SIM_Object &myObject;
const UT_DMatrix4 myTransform;
fpreal myTimestep;
GB_AttributeRef myMassOffset;
GB_AttributeRef myVelOffset;
};
}
SIM_HairForceCallback::SIM_HairForceCallback(GU_Detail &gdp,
const SIM_Object &object,
const UT_DMatrix4 &xform,
fpreal timestep,
const GB_AttributeRef &massoffset,
const GB_AttributeRef &veloffset)
: myGdp(gdp),
myObject(object),
myTransform(xform),
myTimestep(timestep),
myMassOffset(massoffset),
myVelOffset(veloffset)
{
}
SIM_HairForceCallback::~SIM_HairForceCallback()
{
}
void
SIM_HairForceCallback::callbackConst(const SIM_Data *data, const char *)
{
const SIM_Force *force = SIM_DATA_CASTCONST(data, SIM_Force);
GU_DetailHandle gdh;
gdh.allocateAndSet(&myGdp, false);
force->getPointForces(*this, myObject, gdh, myTransform, 0, 0, false);
}
void
SIM_HairForceCallback::forceCallback(int ptnum,
const UT_Vector3 &force,
const UT_Vector3 &)
{
GEO_Point *ppt = myGdp.points()(ptnum);
float mass;
mass = ppt->getValue<float>(myMassOffset);
ppt->setValue<UT_Vector3>(myVelOffset,
ppt->getValue<UT_Vector3>(myVelOffset) + (force/mass)*myTimestep);
}
SIM_SolverHair::SIM_SolverHair(const SIM_DataFactory *factory)
: BaseClass(factory),
SIM_OptionsUser(this)
{
}
SIM_SolverHair::~SIM_SolverHair()
{
}
const SIM_DopDescription *
SIM_SolverHair::getSolverHairDopDescription()
{
static PRM_Template theTemplates[] = {
PRM_Template()
};
static SIM_DopDescription theDopDescription(true,
"hdk_hairsolver",
"Hair Solver",
SIM_SOLVER_DATANAME,
classname(),
theTemplates);
return &theDopDescription;
}
void
SIM_SolverHair::solveHair(SIM_GeometryCopy &hairgeo,
const SIM_ObjectArray &srcobjs,
const SIM_Object &object,
const SIM_Time ×tep) const
{
GU_DetailHandleAutoWriteLock gdl(hairgeo.lockGeometry());
GU_Detail &gdp = *gdl.getGdp();
UT_DMatrix4 xform;
UT_Vector4 startpos, midline;
UT_Vector3 dp, dv;
const UT_Vector3 zero(0.0, 0.0, 0.0);
const float one = 1.0;
GEO_Primitive *hairpoly;
GEO_Point *hairpt0, *hairpt1, *hairpt2;
GB_AttributeRef massoffset, veloffset;
int primnum, ptnum, geonum, numhairprims;
hairgeo.getTransform(xform);
primnum = 0;
numhairprims = gdp.primitives().entries();
for( geonum = 0; geonum < srcobjs.entries(); geonum++ )
{
const SIM_Object *sourceobj = srcobjs(geonum);
const SIM_Geometry *sourcegeo = sourceobj->getGeometry();
if(!sourcegeo || sourcegeo->getGeometry().isNull())
continue;
GU_DetailHandleAutoReadLock sourcegdl(sourcegeo->getGeometry());
const GU_Detail *sourcegdp = sourcegdl.getGdp();
const GEO_Point *sourcept;
UT_DMatrix4 sourcexform;
SIMgetGeometryTransform(sourcexform, *sourceobj);
massoffset = gdp.addPointAttrib(
gdp.getStdAttributeName(GEO_ATTRIBUTE_MASS),
sizeof(float), GB_ATTRIB_FLOAT, &one);
veloffset = gdp.addPointAttrib(
gdp.getStdAttributeName(GEO_ATTRIBUTE_VELOCITY),
sizeof(UT_Vector3), GB_ATTRIB_VECTOR, &zero);
SIM_HairForceCallback callback(gdp, object, xform, timestep,
massoffset, veloffset);
object.forEachConstSubData(callback,
SIM_DataFilterByType("SIM_Force"),
SIM_FORCES_DATANAME,
SIM_DataFilterNone());
FOR_ALL_GPOINTS((&gdp), hairpt0)
{
dp = hairpt0->getValue<UT_Vector3>(veloffset);
dp *= timestep;
hairpt0->getPos() = hairpt0->getPos() + dp;
}
FOR_ALL_GPOINTS(sourcegdp, sourcept)
{
if( primnum >= numhairprims )
break;
startpos = sourcept->getPos();
startpos *= sourcexform;
hairpoly = gdp.primitives()(primnum);
if( hairpoly->getPrimitiveId() == GEOPRIMPOLY &&
hairpoly->getVertexCount() == 10 )
{
UT_Vector3 vel;
hairpt0 = hairpoly->getVertex(0).getPt();
hairpt1 = hairpoly->getVertex(1).getPt();
dp = startpos - hairpt0->getPos();
dv = dp / timestep;
vel = hairpt0->getValue<UT_Vector3>(veloffset);
vel += dv;
vel *= 0.8;
hairpt0->setValue<UT_Vector3>(veloffset, vel);
hairpt0->getPos() = hairpt0->getPos() + dp;
for( ptnum = 1; ptnum < 10; ptnum++ )
{
if( ptnum < 9 )
hairpt2 = hairpoly->getVertex(ptnum + 1).getPt();
midline = hairpt2->getPos() - hairpt0->getPos();
dp = midline * (0.1 / midline.length()) +
hairpt0->getPos() - hairpt1->getPos();
dv = dp / timestep;
vel = hairpt1->getValue<UT_Vector3>(veloffset);
vel += dv;
vel *= 0.8;
hairpt1->setValue<UT_Vector3>(veloffset, vel);
hairpt1->getPos() = hairpt1->getPos() + dp;
hairpt0 = hairpt1;
hairpt1 = hairpt2;
}
}
primnum++;
}
}
hairgeo.releaseGeometry();
}
void
SIM_SolverHair::createHairFromSource(SIM_GeometryCopy &hairgeo,
const SIM_ObjectArray &srcobjs) const
{
GU_DetailHandleAutoWriteLock gdl(hairgeo.lockGeometry());
GU_Detail &gdp = *gdl.getGdp();
UT_Vector4 startpos;
GEO_Point *newpt;
GEO_Face *newpoly;
int i, geonum;
for( geonum = 0; geonum < srcobjs.entries(); geonum++ )
{
const SIM_Object *sourceobj = srcobjs(geonum);
const SIM_Geometry *sourcegeo = sourceobj->getGeometry();
if(!sourcegeo || sourcegeo->getGeometry().isNull())
continue;
GU_DetailHandleAutoReadLock sourcegdl(sourcegeo->getGeometry());
const GU_Detail *sourcegdp = sourcegdl.getGdp();
const GEO_Point *sourcept;
UT_DMatrix4 sourcexform;
SIMgetGeometryTransform(sourcexform, *sourceobj);
FOR_ALL_GPOINTS(sourcegdp, sourcept)
{
startpos = sourcept->getPos();
startpos *= sourcexform;
newpoly = GU_PrimPoly::build(&gdp, 0, GU_POLY_OPEN, 0);
for( i = 0; i < 10; i++ )
{
newpt = gdp.appendPoint();
newpt->getPos().assign(startpos.x(),
startpos.y() + (fpreal)i * 0.1,
startpos.z(),
1.0);
newpoly->appendVertex(newpt);
}
}
}
hairgeo.releaseGeometry();
}
SIM_Solver::SIM_Result
SIM_SolverHair::solveSingleObjectSubclass(SIM_Engine & ,
SIM_Object &object,
SIM_ObjectArray &,
const SIM_Time ×tep,
bool newobject)
{
SIM_GeometryCopy *hairgeo = 0;
SIM_ObjectArray sourceobjects;
if( newobject )
{
if( !SIM_DATA_GET(object, SIM_GEOMETRY_DATANAME, SIM_GeometryCopy) )
{
hairgeo = SIM_DATA_CREATE(object, SIM_GEOMETRY_DATANAME,
SIM_GeometryCopy, 0);
object.getAffectors(sourceobjects, "SIM_RelationshipSource");
if( hairgeo && sourceobjects.entries() > 0 )
createHairFromSource(*hairgeo, sourceobjects);
}
}
else
{
hairgeo = SIM_DATA_GET(object, SIM_GEOMETRY_DATANAME, SIM_GeometryCopy);
if( hairgeo )
{
object.getAffectors(sourceobjects, "SIM_RelationshipSource");
if( hairgeo && sourceobjects.entries() > 0 )
solveHair(*hairgeo, sourceobjects, object, timestep);
}
}
return hairgeo ? SIM_SOLVER_SUCCESS : SIM_SOLVER_FAIL;
}