Simple Particle Clipping
by Wesley Hopson · 10/03/2014 (9:03 am) · 11 comments
Been a while since I last did a resource. This is a WIP and so far only works with shape base derived classes so far. I am also sure this could use some optimizations but considering what we are doing with particles it is not that bad. As the forums code tags seem to be messing this up each time i edit it and i will prob be editing it alot for the forseeable future i am just gonna nix them too bad it makes the code as ugly as sin.
A few technical notes this does not prevent collisions or from particles entering objects. This tests if the particle is in the object then goes to move it out this is far simpler than doing anything predictive. It also uses a scheme of doing our collision check every X number of milliseconds (stored as pollrate) rather than doing it every position update. The reason for this is one it is much much faster computationaly and second it reduces the number of multiple collision hits on the same object. Naturally lower values of pollrate is faster while higher rates will likely let particles slip through thin meshes and let the particles get farther into objects before being expelled.
How to use:
new values in ParticleData datablocks
PollRate - non zero values makes the particles collide with objects the acctual value to use will depend on experimentation but 500 seems reasonable
CollisionMask - is the mask of object types we $twant to detect as shapebase derrived is all that works right now so use $TypeMasks::ShapeBaseObjectType
First the header files quite a few mostly going down the chain of the function
in Sceneobject.h and ShapeBase.h add this
virtual bool containsParticle(Point3F point, Point3F* norm);
in TSShapeInstance.h
bool containsParticle(const Point3F point, S32 dl, Point3F* norm);
in TsMesh of Tsmesh.h
bool containsParticle(S32 frame, Point3F point, Point3F* norm);
in SceneContainer in SceneContainer.h
bool containsParticle(Point3F point, U32 mask, Point3F *norm);
and
Point2I getBinPoint(Point3F pos);
now to add some variables to the particle system These are prob messy and could stand being cleaned up.
add to ParticleData in particle.h
S32 pollRate;//rate at which particle checks to see if it is in a collision mesh. This should be in miliseconds 0 means we do not check for collisions
U32 CollisionMask;
in Particle in particle.h
F32 PollTick;//num mil secs till next inside mesh check
Now to make those variables do things. Collision Mask is likely not very important as the system works now but in the future it could be alot more useful
in ParticleData::ParticleData() in particle.cpp add
pollRate = 0;
CollisionMask = 0;
ParticledData::initpersist
addField("pollRate", TYPEID< S32 >(), Offset(pollRate, ParticleData), "Rate at which particle tests itself for clipping a zero value does not clip.\n");
addField("CollisionMask", TYPEID< S32 >(), Offset(CollisionMask, ParticleData), "Typemask that this particle can collide against.\n");
ParticleData::Packupdate somewhere add
stream->write(pollRate);
stream->write(CollisionMask);
in a matching place in ParticleData::UnpackUpdate
stream->read(&pollRate);
stream->read(&CollisionMask);
ok onto the meat of the code
in particle.cpp
void ParticleData::initializeParticle(Particle* init, const Point3F&amp; inheritVelocity)
we want to add
if (pollRate &gt; 0)
{
init->PollTick = mRandI(0, pollRate);//We want a even distribution between when the particles go and poll themselves to spread the load out some
}
else
init->PollTick = -1;
Notice we are randomizing the inital Polltick to count down that way we should get a even spread of when particles go to check if they are in a object making the processing cost much more even rather than spikeing every pollrate worth of mill seconds
Change ParticleEmitter::Update to be list this you can ommit the afx related code if you are not using that
void ParticleEmitter::update( U32 ms )
{
F32 t = F32(ms)/1000.0f; // AFX -- moved outside loop, no need to recalculate this for every particle
for (Particle* part = part_list_head.next; part != NULL; part = part->next)
{
Point3F a;
if (part->PollTick != -1)
{
if (ms < part->PollTick)
part->PollTick -= ms;
else
{
part->PollTick = part->dataBlock->pollRate + part->PollTick - ms;
//Time to poll our position to see if we are in somthing
SceneContainer* pContainer;
if (mDataBlock->mServerEmitter)
pContainer = &gServerContainer;
else
pContainer = &gClientContainer;
Point3F norm(0,0,0);
if (pContainer->containsParticle(part->pos, part->dataBlock->CollisionMask, &norm))
{
//Just like how contact normals worked we are gonna first remove all force in the direction of our impact surface then scale our speed to be what it was before just a diff direction.
//Otherwise the particles will slide along the surface more could be useful for some applications i suppose
F32 accLen = part->acc.len();
F32 velLen = part->vel.len();
F32 vd = -mDot(part->acc, norm);
if (vd > 0.0f)
{
VectorF dv = norm * vd * 2;
part->acc += dv;
}
vd = -mDot(part->vel, norm);
if (vd > 0.0f)
{
VectorF dv = norm * vd * 2;
part->vel += dv;
}
}
}
}
a = part->acc;
a -= part->vel * part->dataBlock->dragCoefficient;
a -= mWindVelocity * part->dataBlock->windCoefficient;
a.z += -9.81f*part->dataBlock->gravityCoefficient; // AFX -- as long as gravity is a constant, this is faster
part->vel += a * t;
part->pos_local += part->vel * t;
// AFX -- allow subclasses to adjust the particle params here
sub_particleUpdate(part);
if (part->dataBlock->constrain_pos)
part->pos = part->pos_local + this->pos_pe;
else
part->pos = part->pos_local;
updateKeyData( part );
}
}
in SceneObject.cpp will prob do more with this in the future still want it as a stub
bool SceneObject::containsParticle(Point3F point, Point3F* norm)
{
return false;
}
in ShapeBase.cpp
bool ShapeBase::containsParticle(Point3F point, Point3F* norm)
{
if (mShapeInstance/* && containsPoint(point)*/)//if it is not in the world box it ain't in the collision mesh
{
Point3F xformedpoint;
mWorldToObj.mulP(point, &xformedpoint);
xformedpoint.convolveInverse(mObjScale);
for (U32 i = 0; i < mDataBlock->LOSDetails.size(); i++)
{
//mShapeInstance->animate(mDataBlock->LOSDetails[i]);//not sure what purpose this would hold it is asking a bit for particles
if (mShapeInstance->containsParticle(xformedpoint, mDataBlock->LOSDetails[i], norm))
{
mObjToWorld.mulV(*norm);
norm->convolve(mObjScale);
norm->normalize();
return true;
}
}
}
return false;
}
in TsshapeInstance.ccp
bool TSShapeInstance::containsParticle(const Point3F &point, S32 dl, Point3F* norm)
{
if (dl==-1)
return false;
AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::containsPoint");
// get subshape and object detail
const TSDetail * detail = &mShape->details[dl];
S32 ss = detail->subShapeNum;
S32 od = detail->objectDetailNum;
// This detail has no geometry to hit.
if ( ss < 0 )
return false;
S32 start = mShape->subShapeFirstObject[ss];
S32 end = mShape->subShapeNumObjects[ss] + start;
const MatrixF * saveMat = NULL;
bool found = false;
if (start<end)
{
Point3F tpoint;
// set up for first object's node
MatrixF mat;
const MatrixF * previousMat = &mMeshObjects[start].getTransform();
mat = *previousMat;
mat.inverse();
mat.mulP(point,&tpoint);
// run through objects and collide
for (S32 i=start; i<end; i++)
{
MeshObjectInstance * mesh = &mMeshObjects[i];
if (od >= mesh->object->numMeshes)
continue;
if (&mesh->getTransform() != previousMat)
{
// different node from before, set up for this node
previousMat = &mesh->getTransform();
if (previousMat != NULL)
{
mat = *previousMat;
mat.inverse();
mat.mulP(point,&tpoint);
}
}
// collide...
if (mesh->containsParticle(tpoint, od, norm))
{
mat.inverse();
mat.mulV(*norm);
return true;
}
}
}
return false;
}
and
bool TSShapeInstance::MeshObjectInstance::containsParticle(Point3F point, S32 objectDetail, Point3F* norm)
{
TSMesh* mesh = getMesh( objectDetail );
if( mesh && visible > 0.01f )
return mesh->containsParticle(frame, point, norm);
return false;
}
in TsMesh.cpp
bool TSMesh::containsParticle(S32 frame, Point3F point, Point3F* norm)
{
if ( planeNormals.empty() )
buildConvexHull(); // if haven't done it yet...
//we are comparing our point to the convex hull of the object rather than each mesh indvidually.
//This should be alot faster and less complicated but possibly less accurate but hey not like that matters for particles. Heck I am fudging there accuracy anyhow on purpose
PlaneF CurPlane;
F32 BestDist = 9999;
F32 Dist;
S32 startPlane = frame * planesPerFrame;
for ( S32 i = startPlane; i < startPlane + planesPerFrame; i++ )
{
CurPlane.set(planeNormals[i].x, planeNormals[i].y, planeNormals[i].z, planeConstants[i] * -1);
Dist = CurPlane.distToPlane(point) * -1;//Because the normals face outwards we want the inside
//Con::printf("Par Norm %f %f %f, d %f, Dist %f", planeNormals[i].x, planeNormals[i].y, planeNormals[i].z, planeConstants[i], Dist);
if (Dist <= -0.005f)
return false;//point in front of the plane we want it behind it or on it instead
//Now to see if our point is closer to this particular plane.
if (Dist < BestDist)
{
BestDist = Dist;
*norm = CurPlane.getNormal();
}
}
return true;
}
in SceneContainer.cpp add
bool SceneContainer::containsParticle(Point3F point, U32 mask, Point3F *norm)
{
//Worth noteing that most searchs in scenecontainer involve incrementing a seqkey to keep from hitting the same object twice
//We do not do that here because we should only be searching one bin. Since an object cannot be in a bin twice we do not need to bother.
Point2I p = getBinPoint(point);
U32 insertY = p.y % csmNumBins;
U32 base = insertY * csmNumBins;
U32 insertX = p.x % csmNumBins;
SceneObjectRef* chain = mBinArray[base + insertX].nextInBin;
Point3F planeNorm;
bool found = false;
while (chain)
{
SceneObject* object = chain->object;
if ((object->getTypeMask() & mask) != 0 && object->isCollisionEnabled())
{
if (object->containsParticle(point, &planeNorm))
{
*norm += planeNorm;
found = true;
}
}
chain = chain->nextInBin;
}
norm->normalize();
if (found)
return true;
return false;
}
Point2I SceneContainer::getBinPoint(Point3F pos)
{
F32 xCoord = mFmod(pos.x, SceneContainer::csmTotalBinSize);
F32 yCoord = mFmod(pos.y, SceneContainer::csmTotalBinSize);
if (xCoord < 0.0f)
{
xCoord += SceneContainer::csmTotalBinSize;
if (xCoord == SceneContainer::csmTotalBinSize)
xCoord = SceneContainer::csmTotalBinSize - 0.01;
}
if (yCoord < 0.0f)
{
yCoord += SceneContainer::csmTotalBinSize;
if (yCoord == SceneContainer::csmTotalBinSize)
yCoord = SceneContainer::csmTotalBinSize - 0.01;
}
AssertFatal(xCoord >= 0.0 && xCoord < SceneContainer::csmTotalBinSize, "Bad xCoord");
AssertFatal(yCoord >= 0.0 && yCoord < SceneContainer::csmTotalBinSize, "Bad yCoord");
Point2I Coord;
Coord.x = U32(xCoord / SceneContainer::csmBinSize);
Coord.y = U32(yCoord / SceneContainer::csmBinSize);
return Coord;
}
So that should work assuming i did not accidently omit some important bit of code somewhere.
As to what is to come.
Naturally I will prob want to post a vid of this in action but I think I will take the ebil microsoft approach and let someone else do that if they want and add it to the resource here. That way I will know that i got everything included in the resource as it should be.
Naturally adding support for more classes particularly interiors is a real must. Assuming I can figure out a quick containsparticle test for the bsp tree. Terrainblocks and optionaly waterblocks should be simple enough with checking if the point is above or below the block assuming it is not in a appropiate interior.
A few technical notes this does not prevent collisions or from particles entering objects. This tests if the particle is in the object then goes to move it out this is far simpler than doing anything predictive. It also uses a scheme of doing our collision check every X number of milliseconds (stored as pollrate) rather than doing it every position update. The reason for this is one it is much much faster computationaly and second it reduces the number of multiple collision hits on the same object. Naturally lower values of pollrate is faster while higher rates will likely let particles slip through thin meshes and let the particles get farther into objects before being expelled.
How to use:
new values in ParticleData datablocks
PollRate - non zero values makes the particles collide with objects the acctual value to use will depend on experimentation but 500 seems reasonable
CollisionMask - is the mask of object types we $twant to detect as shapebase derrived is all that works right now so use $TypeMasks::ShapeBaseObjectType
First the header files quite a few mostly going down the chain of the function
in Sceneobject.h and ShapeBase.h add this
virtual bool containsParticle(Point3F point, Point3F* norm);
in TSShapeInstance.h
bool containsParticle(const Point3F point, S32 dl, Point3F* norm);
in TsMesh of Tsmesh.h
bool containsParticle(S32 frame, Point3F point, Point3F* norm);
in SceneContainer in SceneContainer.h
bool containsParticle(Point3F point, U32 mask, Point3F *norm);
and
Point2I getBinPoint(Point3F pos);
now to add some variables to the particle system These are prob messy and could stand being cleaned up.
add to ParticleData in particle.h
S32 pollRate;//rate at which particle checks to see if it is in a collision mesh. This should be in miliseconds 0 means we do not check for collisions
U32 CollisionMask;
in Particle in particle.h
F32 PollTick;//num mil secs till next inside mesh check
Now to make those variables do things. Collision Mask is likely not very important as the system works now but in the future it could be alot more useful
in ParticleData::ParticleData() in particle.cpp add
pollRate = 0;
CollisionMask = 0;
ParticledData::initpersist
addField("pollRate", TYPEID< S32 >(), Offset(pollRate, ParticleData), "Rate at which particle tests itself for clipping a zero value does not clip.\n");
addField("CollisionMask", TYPEID< S32 >(), Offset(CollisionMask, ParticleData), "Typemask that this particle can collide against.\n");
ParticleData::Packupdate somewhere add
stream->write(pollRate);
stream->write(CollisionMask);
in a matching place in ParticleData::UnpackUpdate
stream->read(&pollRate);
stream->read(&CollisionMask);
ok onto the meat of the code
in particle.cpp
void ParticleData::initializeParticle(Particle* init, const Point3F&amp; inheritVelocity)
we want to add
if (pollRate &gt; 0)
{
init->PollTick = mRandI(0, pollRate);//We want a even distribution between when the particles go and poll themselves to spread the load out some
}
else
init->PollTick = -1;
Notice we are randomizing the inital Polltick to count down that way we should get a even spread of when particles go to check if they are in a object making the processing cost much more even rather than spikeing every pollrate worth of mill seconds
Change ParticleEmitter::Update to be list this you can ommit the afx related code if you are not using that
void ParticleEmitter::update( U32 ms )
{
F32 t = F32(ms)/1000.0f; // AFX -- moved outside loop, no need to recalculate this for every particle
for (Particle* part = part_list_head.next; part != NULL; part = part->next)
{
Point3F a;
if (part->PollTick != -1)
{
if (ms < part->PollTick)
part->PollTick -= ms;
else
{
part->PollTick = part->dataBlock->pollRate + part->PollTick - ms;
//Time to poll our position to see if we are in somthing
SceneContainer* pContainer;
if (mDataBlock->mServerEmitter)
pContainer = &gServerContainer;
else
pContainer = &gClientContainer;
Point3F norm(0,0,0);
if (pContainer->containsParticle(part->pos, part->dataBlock->CollisionMask, &norm))
{
//Just like how contact normals worked we are gonna first remove all force in the direction of our impact surface then scale our speed to be what it was before just a diff direction.
//Otherwise the particles will slide along the surface more could be useful for some applications i suppose
F32 accLen = part->acc.len();
F32 velLen = part->vel.len();
F32 vd = -mDot(part->acc, norm);
if (vd > 0.0f)
{
VectorF dv = norm * vd * 2;
part->acc += dv;
}
vd = -mDot(part->vel, norm);
if (vd > 0.0f)
{
VectorF dv = norm * vd * 2;
part->vel += dv;
}
}
}
}
a = part->acc;
a -= part->vel * part->dataBlock->dragCoefficient;
a -= mWindVelocity * part->dataBlock->windCoefficient;
a.z += -9.81f*part->dataBlock->gravityCoefficient; // AFX -- as long as gravity is a constant, this is faster
part->vel += a * t;
part->pos_local += part->vel * t;
// AFX -- allow subclasses to adjust the particle params here
sub_particleUpdate(part);
if (part->dataBlock->constrain_pos)
part->pos = part->pos_local + this->pos_pe;
else
part->pos = part->pos_local;
updateKeyData( part );
}
}
in SceneObject.cpp will prob do more with this in the future still want it as a stub
bool SceneObject::containsParticle(Point3F point, Point3F* norm)
{
return false;
}
in ShapeBase.cpp
bool ShapeBase::containsParticle(Point3F point, Point3F* norm)
{
if (mShapeInstance/* && containsPoint(point)*/)//if it is not in the world box it ain't in the collision mesh
{
Point3F xformedpoint;
mWorldToObj.mulP(point, &xformedpoint);
xformedpoint.convolveInverse(mObjScale);
for (U32 i = 0; i < mDataBlock->LOSDetails.size(); i++)
{
//mShapeInstance->animate(mDataBlock->LOSDetails[i]);//not sure what purpose this would hold it is asking a bit for particles
if (mShapeInstance->containsParticle(xformedpoint, mDataBlock->LOSDetails[i], norm))
{
mObjToWorld.mulV(*norm);
norm->convolve(mObjScale);
norm->normalize();
return true;
}
}
}
return false;
}
in TsshapeInstance.ccp
bool TSShapeInstance::containsParticle(const Point3F &point, S32 dl, Point3F* norm)
{
if (dl==-1)
return false;
AssertFatal(dl>=0 && dl<mShape->details.size(),"TSShapeInstance::containsPoint");
// get subshape and object detail
const TSDetail * detail = &mShape->details[dl];
S32 ss = detail->subShapeNum;
S32 od = detail->objectDetailNum;
// This detail has no geometry to hit.
if ( ss < 0 )
return false;
S32 start = mShape->subShapeFirstObject[ss];
S32 end = mShape->subShapeNumObjects[ss] + start;
const MatrixF * saveMat = NULL;
bool found = false;
if (start<end)
{
Point3F tpoint;
// set up for first object's node
MatrixF mat;
const MatrixF * previousMat = &mMeshObjects[start].getTransform();
mat = *previousMat;
mat.inverse();
mat.mulP(point,&tpoint);
// run through objects and collide
for (S32 i=start; i<end; i++)
{
MeshObjectInstance * mesh = &mMeshObjects[i];
if (od >= mesh->object->numMeshes)
continue;
if (&mesh->getTransform() != previousMat)
{
// different node from before, set up for this node
previousMat = &mesh->getTransform();
if (previousMat != NULL)
{
mat = *previousMat;
mat.inverse();
mat.mulP(point,&tpoint);
}
}
// collide...
if (mesh->containsParticle(tpoint, od, norm))
{
mat.inverse();
mat.mulV(*norm);
return true;
}
}
}
return false;
}
and
bool TSShapeInstance::MeshObjectInstance::containsParticle(Point3F point, S32 objectDetail, Point3F* norm)
{
TSMesh* mesh = getMesh( objectDetail );
if( mesh && visible > 0.01f )
return mesh->containsParticle(frame, point, norm);
return false;
}
in TsMesh.cpp
bool TSMesh::containsParticle(S32 frame, Point3F point, Point3F* norm)
{
if ( planeNormals.empty() )
buildConvexHull(); // if haven't done it yet...
//we are comparing our point to the convex hull of the object rather than each mesh indvidually.
//This should be alot faster and less complicated but possibly less accurate but hey not like that matters for particles. Heck I am fudging there accuracy anyhow on purpose
PlaneF CurPlane;
F32 BestDist = 9999;
F32 Dist;
S32 startPlane = frame * planesPerFrame;
for ( S32 i = startPlane; i < startPlane + planesPerFrame; i++ )
{
CurPlane.set(planeNormals[i].x, planeNormals[i].y, planeNormals[i].z, planeConstants[i] * -1);
Dist = CurPlane.distToPlane(point) * -1;//Because the normals face outwards we want the inside
//Con::printf("Par Norm %f %f %f, d %f, Dist %f", planeNormals[i].x, planeNormals[i].y, planeNormals[i].z, planeConstants[i], Dist);
if (Dist <= -0.005f)
return false;//point in front of the plane we want it behind it or on it instead
//Now to see if our point is closer to this particular plane.
if (Dist < BestDist)
{
BestDist = Dist;
*norm = CurPlane.getNormal();
}
}
return true;
}
in SceneContainer.cpp add
bool SceneContainer::containsParticle(Point3F point, U32 mask, Point3F *norm)
{
//Worth noteing that most searchs in scenecontainer involve incrementing a seqkey to keep from hitting the same object twice
//We do not do that here because we should only be searching one bin. Since an object cannot be in a bin twice we do not need to bother.
Point2I p = getBinPoint(point);
U32 insertY = p.y % csmNumBins;
U32 base = insertY * csmNumBins;
U32 insertX = p.x % csmNumBins;
SceneObjectRef* chain = mBinArray[base + insertX].nextInBin;
Point3F planeNorm;
bool found = false;
while (chain)
{
SceneObject* object = chain->object;
if ((object->getTypeMask() & mask) != 0 && object->isCollisionEnabled())
{
if (object->containsParticle(point, &planeNorm))
{
*norm += planeNorm;
found = true;
}
}
chain = chain->nextInBin;
}
norm->normalize();
if (found)
return true;
return false;
}
Point2I SceneContainer::getBinPoint(Point3F pos)
{
F32 xCoord = mFmod(pos.x, SceneContainer::csmTotalBinSize);
F32 yCoord = mFmod(pos.y, SceneContainer::csmTotalBinSize);
if (xCoord < 0.0f)
{
xCoord += SceneContainer::csmTotalBinSize;
if (xCoord == SceneContainer::csmTotalBinSize)
xCoord = SceneContainer::csmTotalBinSize - 0.01;
}
if (yCoord < 0.0f)
{
yCoord += SceneContainer::csmTotalBinSize;
if (yCoord == SceneContainer::csmTotalBinSize)
yCoord = SceneContainer::csmTotalBinSize - 0.01;
}
AssertFatal(xCoord >= 0.0 && xCoord < SceneContainer::csmTotalBinSize, "Bad xCoord");
AssertFatal(yCoord >= 0.0 && yCoord < SceneContainer::csmTotalBinSize, "Bad yCoord");
Point2I Coord;
Coord.x = U32(xCoord / SceneContainer::csmBinSize);
Coord.y = U32(yCoord / SceneContainer::csmBinSize);
return Coord;
}
So that should work assuming i did not accidently omit some important bit of code somewhere.
As to what is to come.
Naturally I will prob want to post a vid of this in action but I think I will take the ebil microsoft approach and let someone else do that if they want and add it to the resource here. That way I will know that i got everything included in the resource as it should be.
Naturally adding support for more classes particularly interiors is a real must. Assuming I can figure out a quick containsparticle test for the bsp tree. Terrainblocks and optionaly waterblocks should be simple enough with checking if the point is above or below the block assuming it is not in a appropiate interior.
About the author
#2
10/03/2014 (11:54 am)
absolutly no particular reason other than not gotten to it yet. TsStatic looks to store it's collision mesh almost identicly to shapebase. Being more light weight is about the only difference to staticshape if i recall right. Making it work with TsStatic should be trivial. I might even be able to just copy and paste the function though might want to reference the cast ray and build polylist functions to be sure.
#3
10/03/2014 (11:56 am)
Should probably provide a patch/diff file or complete sources to the files you changed to make it easier to see what you've done considering that by editing your blog/resource post GG's forum editor bug murdered your code snippets.
#4
10/03/2014 (12:03 pm)
Let me know Wesley if u get to testing that. Id love to know!
#5
*Paul Ok after a quick look through of the cast ray functions there differences seem trivial I think in theory my shapebase::ContainsParticle function should work just fine for TsStatic with little to no modification. If you can get it to compile i would not be surprised if it worked as is.
10/03/2014 (12:06 pm)
*Nathan never been quite sure what the recomended method for hosting source files for the forums is what do you recomend using?*Paul Ok after a quick look through of the cast ray functions there differences seem trivial I think in theory my shapebase::ContainsParticle function should work just fine for TsStatic with little to no modification. If you can get it to compile i would not be surprised if it worked as is.
#6
if u look at the resources section, you did it the way most people do. Id just try and make sure that the code itself is cleaned up a bit more, try it once in a fresh build, and then post it up.
10/03/2014 (12:33 pm)
Wesley: if u look at the resources section, you did it the way most people do. Id just try and make sure that the code itself is cleaned up a bit more, try it once in a fresh build, and then post it up.
#7
"if (mShapeInstance &amp;&amp; containsPoint(point))"
that getting cleaned up would be a great help
10/03/2014 (12:43 pm)
ok, I noticed where the code was getting chewed up a bit..."if (mShapeInstance &amp;&amp; containsPoint(point))"
that getting cleaned up would be a great help
#8
10/03/2014 (12:43 pm)
hmm I think i see what you mean the forum has messed up my code a bit. Well unfortuantly i do not have time to fix it today. I will get to it tommorow.
#9
is what it should be
if (mShapeInstance->containsParticle(xformedpoint, mDataBlock->LOSDetails[i], norm))
Edit: Ok as I thought you can just copy the shapebase::containsparticle method over to TsStatic provided you remove mDataBlock-> from it that is litterally the only difference.
10/04/2014 (12:21 am)
ok the forums flat out will not cooperate here. I am gonna hafta do somthing different.is what it should be
if (mShapeInstance->containsParticle(xformedpoint, mDataBlock->LOSDetails[i], norm))
Edit: Ok as I thought you can just copy the shapebase::containsparticle method over to TsStatic provided you remove mDataBlock-> from it that is litterally the only difference.

Torque 3D Owner Paul Yoskowitz
WinterLeaf Entertainment