feat(physics): wire physx sdk into build

This commit is contained in:
2026-04-15 12:22:15 +08:00
parent 5bf258df6d
commit 31f40e2cbb
2044 changed files with 752623 additions and 1 deletions

View File

@@ -0,0 +1,73 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "foundation/PxBounds3.h"
#include "foundation/PxErrorCallback.h"
#include "foundation/PxFoundation.h"
#include "extensions/PxBroadPhaseExt.h"
using namespace physx;
PxU32 PxBroadPhaseExt::createRegionsFromWorldBounds(PxBounds3* regions, const PxBounds3& globalBounds, PxU32 nbSubdiv, PxU32 upAxis)
{
PX_CHECK_MSG(globalBounds.isValid(), "PxBroadPhaseExt::createRegionsFromWorldBounds(): invalid bounds provided!");
PX_CHECK_MSG(upAxis<3, "PxBroadPhaseExt::createRegionsFromWorldBounds(): invalid up-axis provided!");
const PxVec3& min = globalBounds.minimum;
const PxVec3& max = globalBounds.maximum;
const float dx = (max.x - min.x) / float(nbSubdiv);
const float dy = (max.y - min.y) / float(nbSubdiv);
const float dz = (max.z - min.z) / float(nbSubdiv);
PxU32 nbRegions = 0;
PxVec3 currentMin, currentMax;
for(PxU32 j=0;j<nbSubdiv;j++)
{
for(PxU32 i=0;i<nbSubdiv;i++)
{
if(upAxis==0)
{
currentMin = PxVec3(min.x, min.y + dy * float(i), min.z + dz * float(j));
currentMax = PxVec3(max.x, min.y + dy * float(i+1), min.z + dz * float(j+1));
}
else if(upAxis==1)
{
currentMin = PxVec3(min.x + dx * float(i), min.y, min.z + dz * float(j));
currentMax = PxVec3(min.x + dx * float(i+1), max.y, min.z + dz * float(j+1));
}
else if(upAxis==2)
{
currentMin = PxVec3(min.x + dx * float(i), min.y + dy * float(j), min.z);
currentMax = PxVec3(min.x + dx * float(i+1), min.y + dy * float(j+1), max.z);
}
regions[nbRegions++] = PxBounds3(currentMin, currentMax);
}
}
return nbRegions;
}

View File

@@ -0,0 +1,232 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "common/PxBase.h"
#include "geometry/PxConvexMesh.h"
#include "geometry/PxTriangleMesh.h"
#include "geometry/PxHeightField.h"
#include "extensions/PxJoint.h"
#include "extensions/PxConstraintExt.h"
#include "extensions/PxCollectionExt.h"
#include "PxShape.h"
#include "PxMaterial.h"
#include "PxArticulationReducedCoordinate.h"
#include "PxAggregate.h"
#include "PxPhysics.h"
#include "PxScene.h"
#include "PxPruningStructure.h"
#include "foundation/PxArray.h"
using namespace physx;
void PxCollectionExt::releaseObjects(PxCollection& collection, bool releaseExclusiveShapes)
{
PxArray<PxBase*> releasableObjects;
for (PxU32 i = 0; i < collection.getNbObjects(); ++i)
{
PxBase* s = &collection.getObject(i);
// pruning structure must be released before its actors
if(s->is<PxPruningStructure>())
{
if(!releasableObjects.empty())
{
PxBase* first = releasableObjects[0];
releasableObjects.pushBack(first);
releasableObjects[0] = s;
}
}
else
{
if (s->isReleasable() && (releaseExclusiveShapes || !s->is<PxShape>() || !s->is<PxShape>()->isExclusive()))
releasableObjects.pushBack(s);
}
}
for (PxU32 i = 0; i < releasableObjects.size(); ++i)
releasableObjects[i]->release();
while (collection.getNbObjects() > 0)
collection.remove(collection.getObject(0));
}
void PxCollectionExt::remove(PxCollection& collection, PxType concreteType, PxCollection* to)
{
PxArray<PxBase*> removeObjects;
for (PxU32 i = 0; i < collection.getNbObjects(); i++)
{
PxBase& object = collection.getObject(i);
if(concreteType == object.getConcreteType())
{
if(to)
to->add(object);
removeObjects.pushBack(&object);
}
}
for (PxU32 i = 0; i < removeObjects.size(); ++i)
collection.remove(*removeObjects[i]);
}
PxCollection* PxCollectionExt::createCollection(PxPhysics& physics)
{
PxCollection* collection = PxCreateCollection();
if (!collection)
return NULL;
// Collect convexes
{
PxArray<PxConvexMesh*> objects(physics.getNbConvexMeshes());
const PxU32 nb = physics.getConvexMeshes(objects.begin(), objects.size());
PX_ASSERT(nb == objects.size());
PX_UNUSED(nb);
for(PxU32 i=0;i<objects.size();i++)
collection->add(*objects[i]);
}
// Collect triangle meshes
{
PxArray<PxTriangleMesh*> objects(physics.getNbTriangleMeshes());
const PxU32 nb = physics.getTriangleMeshes(objects.begin(), objects.size());
PX_ASSERT(nb == objects.size());
PX_UNUSED(nb);
for(PxU32 i=0;i<objects.size();i++)
collection->add(*objects[i]);
}
// Collect heightfields
{
PxArray<PxHeightField*> objects(physics.getNbHeightFields());
const PxU32 nb = physics.getHeightFields(objects.begin(), objects.size());
PX_ASSERT(nb == objects.size());
PX_UNUSED(nb);
for(PxU32 i=0;i<objects.size();i++)
collection->add(*objects[i]);
}
// Collect materials
{
PxArray<PxMaterial*> objects(physics.getNbMaterials());
const PxU32 nb = physics.getMaterials(objects.begin(), objects.size());
PX_ASSERT(nb == objects.size());
PX_UNUSED(nb);
for(PxU32 i=0;i<objects.size();i++)
collection->add(*objects[i]);
}
// Collect shapes
{
PxArray<PxShape*> objects(physics.getNbShapes());
const PxU32 nb = physics.getShapes(objects.begin(), objects.size());
PX_ASSERT(nb == objects.size());
PX_UNUSED(nb);
for(PxU32 i=0;i<objects.size();i++)
collection->add(*objects[i]);
}
return collection;
}
PxCollection* PxCollectionExt::createCollection(PxScene& scene)
{
PxCollection* collection = PxCreateCollection();
if (!collection)
return NULL;
// Collect actors
{
PxActorTypeFlags selectionFlags = PxActorTypeFlag::eRIGID_STATIC | PxActorTypeFlag::eRIGID_DYNAMIC;
PxArray<PxActor*> objects(scene.getNbActors(selectionFlags));
const PxU32 nb = scene.getActors(selectionFlags, objects.begin(), objects.size());
PX_ASSERT(nb==objects.size());
PX_UNUSED(nb);
for(PxU32 i=0;i<objects.size();i++)
collection->add(*objects[i]);
}
// Collect constraints
{
PxArray<PxConstraint*> objects(scene.getNbConstraints());
const PxU32 nb = scene.getConstraints(objects.begin(), objects.size());
PX_ASSERT(nb==objects.size());
PX_UNUSED(nb);
for(PxU32 i=0;i<objects.size();i++)
{
PxU32 typeId;
PxJoint* joint = reinterpret_cast<PxJoint*>(objects[i]->getExternalReference(typeId));
if(typeId == PxConstraintExtIDs::eJOINT)
collection->add(*joint);
}
}
// Collect articulations
{
PxArray<PxArticulationReducedCoordinate*> objects(scene.getNbArticulations());
const PxU32 nb = scene.getArticulations(objects.begin(), objects.size());
PX_ASSERT(nb==objects.size());
PX_UNUSED(nb);
for(PxU32 i=0;i<objects.size();i++)
collection->add(*objects[i]);
}
// Collect aggregates
{
PxArray<PxAggregate*> objects(scene.getNbAggregates());
const PxU32 nb = scene.getAggregates(objects.begin(), objects.size());
PX_ASSERT(nb==objects.size());
PX_UNUSED(nb);
for(PxU32 i=0;i<objects.size();i++)
collection->add(*objects[i]);
}
return collection;
}

View File

@@ -0,0 +1,424 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_CONSTRAINT_HELPER_H
#define EXT_CONSTRAINT_HELPER_H
#include "foundation/PxAssert.h"
#include "foundation/PxTransform.h"
#include "foundation/PxMat33.h"
#include "foundation/PxSIMDHelpers.h"
#include "extensions/PxD6Joint.h"
#include "ExtJointData.h"
#include "foundation/PxVecMath.h"
namespace physx
{
namespace Ext
{
namespace joint
{
PX_FORCE_INLINE void applyNeighborhoodOperator(const PxTransform32& cA2w, PxTransform32& cB2w)
{
if(cA2w.q.dot(cB2w.q)<0.0f) // minimum dist quat (equiv to flipping cB2bB.q, which we don't use anywhere)
cB2w.q = -cB2w.q;
}
/*
\brief Transform the two joint frames into the world frame using the global poses of the associated actors.
\param[out] cA2w joint frame associated with body 0 expressed in the world frame
ie if g0 is the global pose of actor 0 then cA2w = g0 * jointFrame_0.
\param[out] cB2w joint frame associated with body 1 expressed in the world frame
ie if g1 is the global pose of actor 1 then cB2w = g1 * jointFrame_1.
\param[in] data contains cmLocalPose^-1 * jointFrame for each body.
\param[in] bA2w pose of the centre of mass of body 0 expressed in the world frame.
\param[in] bB2w pose of the centre of mass of body 1 expressed in the world frame.
\note b2w = g*cmLocalPose so we have g = b2w*cmLocalPose^-1.
We therefore have g * jointFrame = b2w * cmLocalPose^-1 * jointFrame = b2w * data.c2b
*/
PX_INLINE void computeJointFrames(PxTransform32& cA2w, PxTransform32& cB2w, const JointData& data, const PxTransform& bA2w, const PxTransform& bB2w)
{
PX_ASSERT(bA2w.isValid() && bB2w.isValid());
//cA2w = bA2w * (cMassLocalPose0^-1 * jointFrame0)
//cB2w = bB2w * (cMassLocalPose1^-1 * jointFrame1)
aos::transformMultiply<false, true>(cA2w, bA2w, data.c2b[0]);
aos::transformMultiply<false, true>(cB2w, bB2w, data.c2b[1]);
PX_ASSERT(cA2w.isValid() && cB2w.isValid());
}
PX_INLINE void computeJacobianAxes(PxVec3 row[3], const PxQuat& qa, const PxQuat& qb)
{
// Compute jacobian matrix for (qa* qb) [[* means conjugate in this expr]]
// d/dt (qa* qb) = 1/2 L(qa*) R(qb) (omega_b - omega_a)
// result is L(qa*) R(qb), where L(q) and R(q) are left/right q multiply matrix
const PxReal wa = qa.w, wb = qb.w;
const PxVec3 va(qa.x,qa.y,qa.z), vb(qb.x,qb.y,qb.z);
const PxVec3 c = vb*wa + va*wb;
const PxReal d0 = wa*wb;
const PxReal d1 = va.dot(vb);
const PxReal d = d0 - d1;
row[0] = (va * vb.x + vb * va.x + PxVec3(d, c.z, -c.y)) * 0.5f;
row[1] = (va * vb.y + vb * va.y + PxVec3(-c.z, d, c.x)) * 0.5f;
row[2] = (va * vb.z + vb * va.z + PxVec3(c.y, -c.x, d)) * 0.5f;
if((d0 + d1) != 0.0f) // check if relative rotation is 180 degrees which can lead to singular matrix
return;
else
{
row[0].x += PX_EPS_F32;
row[1].y += PX_EPS_F32;
row[2].z += PX_EPS_F32;
}
}
PX_FORCE_INLINE Px1DConstraint* _linear(const PxVec3& axis, const PxVec3& ra, const PxVec3& rb, PxReal posErr, PxConstraintSolveHint::Enum hint, Px1DConstraint* c)
{
c->solveHint = PxU16(hint);
c->linear0 = axis;
c->angular0 = ra.cross(axis);
c->linear1 = axis;
c->angular1 = rb.cross(axis);
c->geometricError = posErr;
PX_ASSERT(c->linear0.isFinite());
PX_ASSERT(c->linear1.isFinite());
PX_ASSERT(c->angular0.isFinite());
PX_ASSERT(c->angular1.isFinite());
return c;
}
PX_FORCE_INLINE Px1DConstraint* _angular(const PxVec3& axis, PxReal posErr, PxConstraintSolveHint::Enum hint, Px1DConstraint* c)
{
c->solveHint = PxU16(hint);
c->linear0 = PxVec3(0.0f);
c->angular0 = axis;
c->linear1 = PxVec3(0.0f);
c->angular1 = axis;
c->geometricError = posErr;
c->flags |= Px1DConstraintFlag::eANGULAR_CONSTRAINT;
return c;
}
class ConstraintHelper
{
Px1DConstraint* mConstraints;
Px1DConstraint* mCurrent;
PX_ALIGN(16, PxVec3p mRa);
PX_ALIGN(16, PxVec3p mRb);
PX_ALIGN(16, PxVec3p mCA2w);
PX_ALIGN(16, PxVec3p mCB2w);
public:
ConstraintHelper(Px1DConstraint* c, const PxVec3& ra, const PxVec3& rb)
: mConstraints(c), mCurrent(c), mRa(ra), mRb(rb) {}
/*PX_NOINLINE*/ ConstraintHelper(Px1DConstraint* c, PxConstraintInvMassScale& invMassScale,
PxTransform32& cA2w, PxTransform32& cB2w, PxVec3p& body0WorldOffset,
const JointData& data, const PxTransform& bA2w, const PxTransform& bB2w)
: mConstraints(c), mCurrent(c)
{
using namespace aos;
V4StoreA(V4LoadA(&data.invMassScale.linear0), &invMassScale.linear0); //invMassScale = data.invMassScale;
computeJointFrames(cA2w, cB2w, data, bA2w, bB2w);
if(1)
{
const Vec4V cB2wV = V4LoadA(&cB2w.p.x);
const Vec4V raV = V4Sub(cB2wV, V4LoadU(&bA2w.p.x)); // const PxVec3 ra = cB2w.p - bA2w.p;
V4StoreU(raV, &body0WorldOffset.x); // body0WorldOffset = ra;
V4StoreA(raV, &mRa.x); // mRa = ra;
V4StoreA(V4Sub(cB2wV, V4LoadU(&bB2w.p.x)), &mRb.x); // mRb = cB2w.p - bB2w.p;
V4StoreA(V4LoadA(&cA2w.p.x), &mCA2w.x); // mCA2w = cA2w.p;
V4StoreA(cB2wV, &mCB2w.x); // mCB2w = cB2w.p;
}
else
{
const PxVec3 ra = cB2w.p - bA2w.p;
body0WorldOffset = ra;
mRa = ra;
mRb = cB2w.p - bB2w.p;
mCA2w = cA2w.p;
mCB2w = cB2w.p;
}
}
PX_FORCE_INLINE const PxVec3& getRa() const { return mRa; }
PX_FORCE_INLINE const PxVec3& getRb() const { return mRb; }
// hard linear & angular
PX_FORCE_INLINE void linearHard(const PxVec3& axis, PxReal posErr)
{
Px1DConstraint* c = linear(axis, posErr, PxConstraintSolveHint::eEQUALITY);
c->flags |= Px1DConstraintFlag::eOUTPUT_FORCE;
}
PX_FORCE_INLINE void angularHard(const PxVec3& axis, PxReal posErr)
{
Px1DConstraint* c = angular(axis, posErr, PxConstraintSolveHint::eEQUALITY);
c->flags |= Px1DConstraintFlag::eOUTPUT_FORCE;
}
// limited linear & angular
PX_FORCE_INLINE void linearLimit(const PxVec3& axis, PxReal ordinate, PxReal limitValue, const PxJointLimitParameters& limit)
{
if(!limit.isSoft() || ordinate > limitValue)
addLimit(linear(axis, limitValue - ordinate, PxConstraintSolveHint::eNONE), limit);
}
PX_FORCE_INLINE void angularLimit(const PxVec3& axis, PxReal ordinate, PxReal limitValue, const PxJointLimitParameters& limit)
{
if(!limit.isSoft() || ordinate > limitValue)
addLimit(angular(axis, limitValue - ordinate, PxConstraintSolveHint::eNONE), limit);
}
PX_FORCE_INLINE void angularLimit(const PxVec3& axis, PxReal error, const PxJointLimitParameters& limit)
{
addLimit(angular(axis, error, PxConstraintSolveHint::eNONE), limit);
}
PX_FORCE_INLINE void anglePair(PxReal angle, PxReal lower, PxReal upper, const PxVec3& axis, const PxJointLimitParameters& limit)
{
PX_ASSERT(lower<upper);
const bool softLimit = limit.isSoft();
if(!softLimit || angle < lower)
angularLimit(-axis, -(lower - angle), limit);
if(!softLimit || angle > upper)
angularLimit(axis, (upper - angle), limit);
}
// driven linear & angular
PX_FORCE_INLINE void linear(const PxVec3& axis, PxReal velTarget, PxReal error, const PxD6JointDrive& drive)
{
addDrive(linear(axis, error, PxConstraintSolveHint::eNONE), velTarget, drive);
}
PX_FORCE_INLINE void angular(const PxVec3& axis, PxReal velTarget, PxReal error, const PxD6JointDrive& drive, PxConstraintSolveHint::Enum hint = PxConstraintSolveHint::eNONE)
{
addDrive(angular(axis, error, hint), velTarget, drive);
}
PX_FORCE_INLINE PxU32 getCount() const { return PxU32(mCurrent - mConstraints); }
void prepareLockedAxes(const PxQuat& qA, const PxQuat& qB, const PxVec3& cB2cAp, PxU32 lin, PxU32 ang, PxVec3& raOut, PxVec3& rbOut, PxVec3* axis=NULL)
{
Px1DConstraint* current = mCurrent;
PxVec3 errorVector(0.0f);
PxVec3 ra = mRa;
PxVec3 rb = mRb;
if(lin)
{
const PxMat33Padded axes(qA);
if(axis)
*axis = axes.column0;
if(lin&1) errorVector -= axes.column0 * cB2cAp.x;
if(lin&2) errorVector -= axes.column1 * cB2cAp.y;
if(lin&4) errorVector -= axes.column2 * cB2cAp.z;
ra += errorVector;
//Note that our convention is that C(s) = geometricError = (xA + rA) - (xB + rB)
//where xA, xB are the positions of the two bodies in the world frame and rA, rB
//are the vectors in the world frame from each body to the joint anchor.
//We solve Jv + C(s)/dt = Jv + geometricError/dt = 0.
//With GA, GB denoting the actor poses in world frame and LA, LB denoting the
//associated joint frames we have: cB2cAp = [(GA*LA)^-1 * (GB*LB)].p
//But cB2cAp = (GA*LA).q.getConjugate() * ((xB + rB) - (xA + rA))
//To match our convention we want geometricError = (GA*LA).q.getConjugate() * ((xA + rA) - (xB + rB))
//cB2cAp therefore has the wrong sign to be used directly as the geometric error.
//We need to negate cB2cAp to ensure that we set geometricError with the correct sign.
if(lin&1) _linear(axes.column0, ra, rb, -cB2cAp.x, PxConstraintSolveHint::eEQUALITY, current++);
if(lin&2) _linear(axes.column1, ra, rb, -cB2cAp.y, PxConstraintSolveHint::eEQUALITY, current++);
if(lin&4) _linear(axes.column2, ra, rb, -cB2cAp.z, PxConstraintSolveHint::eEQUALITY, current++);
}
if (ang)
{
const PxQuat qB2qA = qA.getConjugate() * qB;
PxVec3 row[3];
computeJacobianAxes(row, qA, qB);
if (ang & 1) _angular(row[0], -qB2qA.x, PxConstraintSolveHint::eEQUALITY, current++);
if (ang & 2) _angular(row[1], -qB2qA.y, PxConstraintSolveHint::eEQUALITY, current++);
if (ang & 4) _angular(row[2], -qB2qA.z, PxConstraintSolveHint::eEQUALITY, current++);
}
raOut = ra;
rbOut = rb;
for(Px1DConstraint* front = mCurrent; front < current; front++)
front->flags |= Px1DConstraintFlag::eOUTPUT_FORCE;
mCurrent = current;
}
PX_FORCE_INLINE Px1DConstraint* getConstraintRow()
{
return mCurrent++;
}
private:
PX_FORCE_INLINE Px1DConstraint* linear(const PxVec3& axis, PxReal posErr, PxConstraintSolveHint::Enum hint)
{
return _linear(axis, mRa, mRb, posErr, hint, mCurrent++);
}
PX_FORCE_INLINE Px1DConstraint* angular(const PxVec3& axis, PxReal posErr, PxConstraintSolveHint::Enum hint)
{
return _angular(axis, posErr, hint, mCurrent++);
}
void addLimit(Px1DConstraint* c, const PxJointLimitParameters& limit)
{
PxU16 flags = PxU16(c->flags | Px1DConstraintFlag::eOUTPUT_FORCE);
if(limit.isSoft())
{
flags |= Px1DConstraintFlag::eSPRING;
c->mods.spring.stiffness = limit.stiffness;
c->mods.spring.damping = limit.damping;
}
else
{
c->solveHint = PxConstraintSolveHint::eINEQUALITY;
c->mods.bounce.restitution = limit.restitution;
c->mods.bounce.velocityThreshold = limit.bounceThreshold;
if (c->geometricError > 0.0f)
{
flags |= Px1DConstraintFlag::eKEEPBIAS;
// note: positive error is the scenario where the limit is not hit yet. It reflects the
// distance to the limit. Using eKEEPBIAS feels unintuitive in general but what seems to
// be solved with this is:
//
// imagine the following scenario: object o moving towards a limit with velocity v
//
// |
// o---> v |
// |
//
// and let's denote the following distances
//
// |<-------->| |v|*dt (travel distance assuming time step dt)
// |<-------------->| |ge| (distance to limit = geometric error)
//
// furthermore, the sign convention is that v as drawn here is negative and ge is
// positive. Since -v*dt is smaller than ge, the limit will not get hit in the dt time
// step range. This means, the velocity after the sim step should not change and remain v.
// For the solver this means no impulse should get applied.
// The impulse applied by the solver is of the form:
//
// impulse = -r * ((v - vT) + ge/dt) (r is a positive scalar value)
//
// for this example, let's assume the target velocity vT is zero, so:
//
// impulse = -r * (v + ge/dt) (1)
//
// Without Px1DConstraintFlag::eKEEPBIAS, the part related to the geometric error is ignored
// during velocity iterations:
//
// impulse = -r * v
//
// The solver will apply the resulting (positive) impulse and this will change the velocity
// of the object. That would be wrong though because the object does not hit the limit yet
// and the velocity should stay the same.
//
// Why does Px1DConstraintFlag::eKEEPBIAS prevent this from happening? In this case, equation
// (1) applies and since -v*dt < ge, the resulting impulse will be negative ((v + ge/dt) is
// positive). Limit constraints are inequality constraints and clamp the impulse in the range
// [0, maxImpulse], thus the negative impulse will get clamped to zero and the velocity will
// not change (as desired).
//
// Why then create this constraint at all? Imagine the same scenario but with a velocity
// magnitude such that the limit gets hit in the dt time step range:
//
// |<--------------------->| |v|*dt
// |<-------------->| |ge|
//
// (v + ge/dt) will be negative and the impulse positive. The impulse will get applied and
// will make sure that the velocity is reduced by the right amount such that the object
// stops at the limit (and does not breach it).
}
if(limit.restitution>0.0f)
flags |= Px1DConstraintFlag::eRESTITUTION;
}
c->flags = flags;
c->minImpulse = 0.0f;
}
void addDrive(Px1DConstraint* c, PxReal velTarget, const PxD6JointDrive& drive)
{
c->velocityTarget = velTarget;
PxU16 flags = PxU16(c->flags | Px1DConstraintFlag::eSPRING | Px1DConstraintFlag::eHAS_DRIVE_LIMIT);
if(drive.flags & PxD6JointDriveFlag::eACCELERATION)
flags |= Px1DConstraintFlag::eACCELERATION_SPRING;
if (drive.flags & PxD6JointDriveFlag::eOUTPUT_FORCE)
flags |= Px1DConstraintFlag::eOUTPUT_FORCE;
c->flags = flags;
c->mods.spring.stiffness = drive.stiffness;
c->mods.spring.damping = drive.damping;
c->minImpulse = -drive.forceLimit;
c->maxImpulse = drive.forceLimit;
PX_ASSERT(c->linear0.isFinite());
PX_ASSERT(c->angular0.isFinite());
}
};
}
} // namespace
}
#endif

View File

@@ -0,0 +1,42 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxConvexCoreExt.h"
#include "GuConvexGeometry.h"
using namespace physx;
void PxConvexCoreExt::computeMassInfo(const PxConvexCoreGeometry& convex, PxReal& density1Mass, PxMat33& inertiaTensor, PxVec3& centerOfMass)
{
Gu::computeMassInfo(convex, density1Mass, inertiaTensor, centerOfMass);
}
void PxConvexCoreExt::visualize(const PxConvexCoreGeometry& convex, const PxTransform& pose, bool drawCore, const PxBounds3& cullbox, PxRenderOutput& out)
{
Gu::visualize(convex, pose, drawCore, cullbox, out);
}

View File

@@ -0,0 +1,89 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "foundation/PxPlane.h"
#include "geometry/PxConvexMeshGeometry.h"
#include "geometry/PxConvexMesh.h"
#include "extensions/PxConvexMeshExt.h"
using namespace physx;
static const PxReal gEpsilon = .01f;
PxU32 physx::PxFindFaceIndex(const PxConvexMeshGeometry& convexGeom, const PxTransform& pose,
const PxVec3& impactPos, const PxVec3& unitDir)
{
PX_ASSERT(unitDir.isFinite());
PX_ASSERT(unitDir.isNormalized());
PX_ASSERT(impactPos.isFinite());
PX_ASSERT(pose.isFinite());
const PxVec3 impact = impactPos - unitDir * gEpsilon;
const PxVec3 localPoint = pose.transformInv(impact);
const PxVec3 localDir = pose.rotateInv(unitDir);
// Create shape to vertex scale transformation matrix
const PxMeshScale& meshScale = convexGeom.scale;
const PxMat33 rot(meshScale.rotation);
PxMat33 shape2VertexSkew = rot.getTranspose();
const PxMat33 diagonal = PxMat33::createDiagonal(PxVec3(1.0f / meshScale.scale.x, 1.0f / meshScale.scale.y, 1.0f / meshScale.scale.z));
shape2VertexSkew = shape2VertexSkew * diagonal;
shape2VertexSkew = shape2VertexSkew * rot;
const PxU32 nbPolys = convexGeom.convexMesh->getNbPolygons();
PxU32 minIndex = 0;
PxReal minD = PX_MAX_REAL;
for (PxU32 j = 0; j < nbPolys; j++)
{
PxHullPolygon hullPolygon;
convexGeom.convexMesh->getPolygonData(j, hullPolygon);
// transform hull plane into shape space
PxPlane plane;
const PxVec3 tmp = shape2VertexSkew.transformTranspose(PxVec3(hullPolygon.mPlane[0],hullPolygon.mPlane[1],hullPolygon.mPlane[2]));
const PxReal denom = 1.0f / tmp.magnitude();
plane.n = tmp * denom;
plane.d = hullPolygon.mPlane[3] * denom;
PxReal d = plane.distance(localPoint);
if (d < 0.0f)
continue;
const PxReal tweak = plane.n.dot(localDir) * gEpsilon;
d += tweak;
if (d < minD)
{
minIndex = j;
minD = d;
}
}
return minIndex;
}

View File

@@ -0,0 +1,92 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "task/PxTask.h"
#include "ExtCpuWorkerThread.h"
#include "ExtDefaultCpuDispatcher.h"
#include "foundation/PxFPU.h"
using namespace physx;
Ext::CpuWorkerThread::CpuWorkerThread() : mOwner(NULL), mThreadId(0)
{
}
Ext::CpuWorkerThread::~CpuWorkerThread()
{
}
#define HighPriority true
#define RegularPriority false
void Ext::CpuWorkerThread::execute()
{
mThreadId = getId();
const PxDefaultCpuDispatcherWaitForWorkMode::Enum ownerWaitForWorkMode = mOwner->getWaitForWorkMode();
while(!quitIsSignalled())
{
if(PxDefaultCpuDispatcherWaitForWorkMode::eWAIT_FOR_WORK == ownerWaitForWorkMode)
mOwner->resetWakeSignal();
// PT: look for high priority tasks first, across threads
PxBaseTask* task = getJob<HighPriority>();
if(!task)
task = mOwner->fetchNextTask<HighPriority>();
// PT: then look for regular tasks
if(!task)
task = getJob<RegularPriority>();
if(!task)
task = mOwner->fetchNextTask<RegularPriority>();
if(task)
{
mOwner->runTask(*task);
task->release();
}
else if(PxDefaultCpuDispatcherWaitForWorkMode::eYIELD_THREAD == ownerWaitForWorkMode)
{
PxThread::yield();
}
else if(PxDefaultCpuDispatcherWaitForWorkMode::eYIELD_PROCESSOR == ownerWaitForWorkMode)
{
const PxU32 pauseCounter = mOwner->getYieldProcessorCount();
for(PxU32 j = 0; j < pauseCounter; j++)
PxThread::yieldProcesor();
}
else
{
PX_ASSERT(PxDefaultCpuDispatcherWaitForWorkMode::eWAIT_FOR_WORK == ownerWaitForWorkMode);
mOwner->waitForWork();
}
}
quit();
}

View File

@@ -0,0 +1,81 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_CPU_WORKER_THREAD_H
#define EXT_CPU_WORKER_THREAD_H
#include "foundation/PxThread.h"
#include "ExtTaskQueueHelper.h"
#include "ExtSharedQueueEntryPool.h"
namespace physx
{
namespace Ext
{
class DefaultCpuDispatcher;
#if PX_VC
#pragma warning(push)
#pragma warning(disable:4324) // Padding was added at the end of a structure because of a __declspec(align) value.
#endif // Because of the SList member I assume
class CpuWorkerThread : public PxThread
{
public:
CpuWorkerThread();
~CpuWorkerThread();
PX_FORCE_INLINE void initialize(DefaultCpuDispatcher* ownerDispatcher) { mOwner = ownerDispatcher; }
PX_FORCE_INLINE PxThread::Id getWorkerThreadId() const { return mThreadId; }
template<const bool highPriorityT>
PX_FORCE_INLINE PxBaseTask* getJob() { return mHelper.fetchTask<highPriorityT>(); }
void execute();
PX_FORCE_INLINE bool tryAcceptJobToLocalQueue(PxBaseTask& task, PxThread::Id taskSubmitionThread)
{
if(taskSubmitionThread == mThreadId)
return mHelper.tryAcceptJobToQueue(task);
return false;
}
protected:
DefaultCpuDispatcher* mOwner;
TaskQueueHelper mHelper;
PxThread::Id mThreadId;
};
#if PX_VC
#pragma warning(pop)
#endif
} // namespace Ext
}
#endif

View File

@@ -0,0 +1,994 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxCustomGeometryExt.h"
#include <geometry/PxGeometryHelpers.h>
#include <geometry/PxGeometryQuery.h>
#include <geometry/PxMeshQuery.h>
#include <geometry/PxTriangle.h>
#include <geometry/PxTriangleMesh.h>
#include <geometry/PxTriangleMeshGeometry.h>
#include <geomutils/PxContactBuffer.h>
#include <common/PxRenderOutput.h>
#include <extensions/PxGjkQueryExt.h>
#include <extensions/PxMassProperties.h>
#include <PxImmediateMode.h>
#include "omnipvd/ExtOmniPvdSetData.h"
using namespace physx;
#if PX_SUPPORT_OMNI_PVD
using namespace Ext;
#endif
static const PxU32 gCollisionShapeColor = PxU32(PxDebugColor::eARGB_MAGENTA);
///////////////////////////////////////////////////////////////////////////////
static const PxU32 MAX_TRIANGLES = 512;
static const PxU32 MAX_TRIANGLE_CONTACTS = 6;
const PxReal FACE_CONTACT_THRESHOLD = 0.99999f;
struct TrimeshContactFilter
{
PxU32 triCount;
PxU32 triIndices[MAX_TRIANGLES];
PxU32 triAdjacencies[MAX_TRIANGLES][3];
PxU32 triContactCounts[MAX_TRIANGLES][2];
PxContactPoint triContacts[MAX_TRIANGLES][MAX_TRIANGLE_CONTACTS];
TrimeshContactFilter() : triCount(0) {}
void addTriangleContacts(const PxContactPoint* points, PxU32 count, const PxU32 triIndex, const PxVec3 triVerts[3], const PxU32 triAdjacency[3])
{
if (triCount == MAX_TRIANGLES)
return;
triIndices[triCount] = triIndex;
PxU32& faceContactCount = triContactCounts[triCount][0];
PxU32& edgeContactCount = triContactCounts[triCount][1];
faceContactCount = edgeContactCount = 0;
for (PxU32 i = 0; i < 3; ++i)
triAdjacencies[triCount][i] = triAdjacency[i];
PxVec3 triNormal = (triVerts[1] - triVerts[0]).cross(triVerts[2] - triVerts[0]).getNormalized();
for (PxU32 i = 0; i < count; ++i)
{
const PxContactPoint& point = points[i];
bool faceContact = fabsf(point.normal.dot(triNormal)) > FACE_CONTACT_THRESHOLD;
if (faceContactCount + edgeContactCount < MAX_TRIANGLE_CONTACTS)
{
if (faceContact) triContacts[triCount][faceContactCount++] = point;
else triContacts[triCount][MAX_TRIANGLE_CONTACTS - 1 - edgeContactCount++] = point;
}
}
++triCount;
}
void writeToBuffer(PxContactBuffer& buffer)
{
for (PxU32 i = 0; i < triCount; ++i)
{
if (triContactCounts[i][1] > 0)
{
for (PxU32 j = 0; j < triCount; ++j)
{
if (triIndices[j] == triAdjacencies[i][0] || triIndices[j] == triAdjacencies[i][1] || triIndices[j] == triAdjacencies[i][2])
{
if (triContactCounts[j][0] > 0)
{
triContactCounts[i][1] = 0;
break;
}
}
}
}
for (PxU32 j = 0; j < triContactCounts[i][0]; ++j)
buffer.contact(triContacts[i][j]);
for (PxU32 j = 0; j < triContactCounts[i][1]; ++j)
buffer.contact(triContacts[i][MAX_TRIANGLE_CONTACTS - 1 - j]);
}
}
PX_NOCOPY(TrimeshContactFilter)
};
///////////////////////////////////////////////////////////////////////////////
struct TriangleSupport : PxGjkQuery::Support
{
PxVec3 v0, v1, v2;
PxReal margin;
TriangleSupport(const PxVec3& _v0, const PxVec3& _v1, const PxVec3& _v2, PxReal _margin)
:
v0(_v0), v1(_v1), v2(_v2), margin(_margin)
{}
virtual PxReal getMargin() const
{
return margin;
}
virtual PxVec3 supportLocal(const PxVec3& dir) const
{
float d0 = dir.dot(v0), d1 = dir.dot(v1), d2 = dir.dot(v2);
return (d0 > d1 && d0 > d2) ? v0 : (d1 > d2) ? v1 : v2;
}
};
///////////////////////////////////////////////////////////////////////////////
static void drawArc(const PxVec3& center, const PxVec3& radius, const PxVec3& axis, PxReal angle, PxReal error, PxRenderOutput& out)
{
int sides = int(ceilf(angle / (2 * acosf(1.0f - error))));
float step = angle / sides;
out << PxRenderOutput::LINESTRIP;
for (int i = 0; i <= sides; ++i)
out << center + PxQuat(step * i, axis).rotate(radius);
}
static void drawCircle(const PxVec3& center, const PxVec3& radius, const PxVec3& axis, PxReal error, PxRenderOutput& out)
{
drawArc(center, radius, axis, PxTwoPi, error, out);
}
static void drawQuarterCircle(const PxVec3& center, const PxVec3& radius, const PxVec3& axis, PxReal error, PxRenderOutput& out)
{
drawArc(center, radius, axis, PxPiDivTwo, error, out);
}
static void drawLine(const PxVec3& s, const PxVec3& e, PxRenderOutput& out)
{
out << PxRenderOutput::LINES << s << e;
}
///////////////////////////////////////////////////////////////////////////////
PxBounds3 PxCustomGeometryExt::BaseConvexCallbacks::getLocalBounds(const PxGeometry&) const
{
const PxVec3 min(supportLocal(PxVec3(-1, 0, 0)).x, supportLocal(PxVec3(0, -1, 0)).y, supportLocal(PxVec3(0, 0, -1)).z);
const PxVec3 max(supportLocal(PxVec3(1, 0, 0)).x, supportLocal(PxVec3(0, 1, 0)).y, supportLocal(PxVec3(0, 0, 1)).z);
PxBounds3 bounds(min, max);
bounds.fattenSafe(getMargin());
return bounds;
}
bool PxCustomGeometryExt::BaseConvexCallbacks::generateContacts(const PxGeometry& geom0, const PxGeometry& geom1,
const PxTransform& pose0, const PxTransform& pose1, const PxReal contactDistance, const PxReal meshContactMargin,
const PxReal toleranceLength, PxContactBuffer& contactBuffer) const
{
struct ContactRecorder : immediate::PxContactRecorder
{
PxContactBuffer* contactBuffer;
ContactRecorder(PxContactBuffer& _contactBuffer) : contactBuffer(&_contactBuffer) {}
virtual bool recordContacts(const PxContactPoint* contactPoints, PxU32 nbContacts, PxU32 /*index*/)
{
for (PxU32 i = 0; i < nbContacts; ++i)
contactBuffer->contact(contactPoints[i]);
return true;
}
}
contactRecorder(contactBuffer);
PxCache contactCache;
struct ContactCacheAllocator : PxCacheAllocator
{
PxU8 buffer[1024];
ContactCacheAllocator() { PxMemSet(buffer, 0, sizeof(buffer)); }
virtual PxU8* allocateCacheData(const PxU32 /*byteSize*/) { return reinterpret_cast<PxU8*>(size_t(buffer + 0xf) & ~0xf); }
}
contactCacheAllocator;
const PxTransform identityPose(PxIdentity);
switch (geom1.getType())
{
case PxGeometryType::eSPHERE:
case PxGeometryType::eCAPSULE:
case PxGeometryType::eBOX:
case PxGeometryType::eCONVEXCORE:
case PxGeometryType::eCONVEXMESH:
{
PxGjkQueryExt::ConvexGeomSupport geomSupport(geom1);
if (PxGjkQueryExt::generateContacts(*this, geomSupport, pose0, pose1, contactDistance, toleranceLength, contactBuffer))
{
PxGeometryHolder substituteGeom; PxTransform preTransform;
if (useSubstituteGeometry(substituteGeom, preTransform, contactBuffer.contacts[contactBuffer.count - 1], pose0))
{
contactBuffer.count--;
const PxGeometry* pGeom0 = &substituteGeom.any();
const PxGeometry* pGeom1 = &geom1;
// PT:: tag: scalar transform*transform
PxTransform pose = pose0.transform(preTransform);
immediate::PxGenerateContacts(&pGeom0, &pGeom1, &pose, &pose1, &contactCache, 1, contactRecorder,
contactDistance, meshContactMargin, toleranceLength, contactCacheAllocator);
}
}
break;
}
case PxGeometryType::ePLANE:
{
const PxPlane plane = PxPlane(1.0f, 0.0f, 0.0f, 0.0f).transform(pose1);
const PxPlane localPlane = plane.inverseTransform(pose0);
const PxVec3 point = supportLocal(-localPlane.n) - localPlane.n * margin;
const float dist = localPlane.distance(point);
if (dist < contactDistance)
{
const PxVec3 n = localPlane.n;
const PxVec3 p = point - n * dist * 0.5f;
PxContactPoint contact;
contact.point = pose0.transform(p);
contact.normal = pose0.rotate(n);
contact.separation = dist;
contactBuffer.contact(contact);
PxGeometryHolder substituteGeom; PxTransform preTransform;
if (useSubstituteGeometry(substituteGeom, preTransform, contactBuffer.contacts[contactBuffer.count - 1], pose0))
{
contactBuffer.count--;
const PxGeometry* pGeom0 = &substituteGeom.any();
const PxGeometry* pGeom1 = &geom1;
// PT:: tag: scalar transform*transform
PxTransform pose = pose0.transform(preTransform);
immediate::PxGenerateContacts(&pGeom0, &pGeom1, &pose, &pose1, &contactCache, 1, contactRecorder,
contactDistance, meshContactMargin, toleranceLength, contactCacheAllocator);
}
}
break;
}
case PxGeometryType::eTRIANGLEMESH:
case PxGeometryType::eHEIGHTFIELD:
{
// As a triangle has no thickness, we need to choose some collision margin - the distance
// considered a touching contact. I set it as 1% of the custom shape minimum dimension.
PxReal meshMargin = getLocalBounds(geom0).getDimensions().minElement() * 0.01f;
TrimeshContactFilter contactFilter;
bool hasAdjacency = (geom1.getType() != PxGeometryType::eTRIANGLEMESH || (static_cast<const PxTriangleMeshGeometry&>(geom1).triangleMesh->getTriangleMeshFlags() & PxTriangleMeshFlag::eADJACENCY_INFO));
PxBoxGeometry boxGeom(getLocalBounds(geom0).getExtents() + PxVec3(contactDistance + meshMargin));
PxU32 triangles[MAX_TRIANGLES];
bool overflow = false;
PxU32 triangleCount = (geom1.getType() == PxGeometryType::eTRIANGLEMESH) ?
PxMeshQuery::findOverlapTriangleMesh(boxGeom, pose0, static_cast<const PxTriangleMeshGeometry&>(geom1), pose1, triangles, MAX_TRIANGLES, 0, overflow) :
PxMeshQuery::findOverlapHeightField(boxGeom, pose0, static_cast<const PxHeightFieldGeometry&>(geom1), pose1, triangles, MAX_TRIANGLES, 0, overflow);
if (overflow)
PxGetFoundation().error(PxErrorCode::eDEBUG_INFO, PX_FL, "PxCustomGeometryExt::BaseConvexCallbacks::generateContacts() Too many triangles.\n");
for (PxU32 i = 0; i < triangleCount; ++i)
{
PxTriangle tri; PxU32 adjacent[3];
if (geom1.getType() == PxGeometryType::eTRIANGLEMESH)
PxMeshQuery::getTriangle(static_cast<const PxTriangleMeshGeometry&>(geom1), pose1, triangles[i], tri, NULL, hasAdjacency ? adjacent : NULL);
else
PxMeshQuery::getTriangle(static_cast<const PxHeightFieldGeometry&>(geom1), pose1, triangles[i], tri, NULL, adjacent);
TriangleSupport triSupport(tri.verts[0], tri.verts[1], tri.verts[2], meshMargin);
if (PxGjkQueryExt::generateContacts(*this, triSupport, pose0, identityPose, contactDistance, toleranceLength, contactBuffer))
{
contactBuffer.contacts[contactBuffer.count - 1].internalFaceIndex1 = triangles[i];
PxGeometryHolder substituteGeom; PxTransform preTransform;
if (useSubstituteGeometry(substituteGeom, preTransform, contactBuffer.contacts[contactBuffer.count - 1], pose0))
{
contactBuffer.count--;
const PxGeometry& geom = substituteGeom.any();
// PT:: tag: scalar transform*transform
PxTransform pose = pose0.transform(preTransform);
PxGeometryQuery::generateTriangleContacts(geom, pose, tri.verts, triangles[i], contactDistance, meshMargin, toleranceLength, contactBuffer);
}
}
if (hasAdjacency)
{
contactFilter.addTriangleContacts(contactBuffer.contacts, contactBuffer.count, triangles[i], tri.verts, adjacent);
contactBuffer.count = 0;
}
}
if (hasAdjacency)
contactFilter.writeToBuffer(contactBuffer);
break;
}
case PxGeometryType::eCUSTOM:
{
const PxCustomGeometry& customGeom1 = static_cast<const PxCustomGeometry&>(geom1);
if (customGeom1.getCustomType() == CylinderCallbacks::TYPE() ||
customGeom1.getCustomType() == ConeCallbacks::TYPE()) // It's a CustomConvex
{
BaseConvexCallbacks* custom1 = static_cast<BaseConvexCallbacks*>(customGeom1.callbacks);
if (PxGjkQueryExt::generateContacts(*this, *custom1, pose0, pose1, contactDistance, toleranceLength, contactBuffer))
{
PxGeometryHolder substituteGeom; PxTransform preTransform;
if (useSubstituteGeometry(substituteGeom, preTransform, contactBuffer.contacts[contactBuffer.count - 1], pose0))
{
contactBuffer.count--;
PxU32 oldCount = contactBuffer.count;
const PxGeometry* pGeom0 = &substituteGeom.any();
const PxGeometry* pGeom1 = &geom1;
// PT:: tag: scalar transform*transform
PxTransform pose = pose0.transform(preTransform);
immediate::PxGenerateContacts(&pGeom1, &pGeom0, &pose1, &pose, &contactCache, 1, contactRecorder,
contactDistance, meshContactMargin, toleranceLength, contactCacheAllocator);
for (int i = oldCount; i < int(contactBuffer.count); ++i)
contactBuffer.contacts[i].normal = -contactBuffer.contacts[i].normal;
}
}
}
else
{
const PxGeometry* pGeom0 = &geom0;
const PxGeometry* pGeom1 = &geom1;
immediate::PxGenerateContacts(&pGeom1, &pGeom0, &pose1, &pose0, &contactCache, 1, contactRecorder,
contactDistance, meshContactMargin, toleranceLength, contactCacheAllocator);
for (int i = 0; i < int(contactBuffer.count); ++i)
contactBuffer.contacts[i].normal = -contactBuffer.contacts[i].normal;
}
break;
}
default:
break;
}
return contactBuffer.count > 0;
}
PxU32 PxCustomGeometryExt::BaseConvexCallbacks::raycast(const PxVec3& origin, const PxVec3& unitDir, const PxGeometry& geom, const PxTransform& pose,
PxReal maxDist, PxHitFlags /*hitFlags*/, PxU32 /*maxHits*/, PxGeomRaycastHit* rayHits, PxU32 /*stride*/, PxRaycastThreadContext*) const
{
// When FLT_MAX is used as maxDist, it works bad with GJK algorithm.
// Here I compute the maximum needed distance (wiseDist) as the diagonal
// of the bounding box of both the geometry and the ray origin.
PxBounds3 bounds = PxBounds3::transformFast(pose, getLocalBounds(geom));
bounds.include(origin);
PxReal wiseDist = PxMin(maxDist, bounds.getDimensions().magnitude());
PxReal t;
PxVec3 n, p;
if (PxGjkQuery::raycast(*this, pose, origin, unitDir, wiseDist, t, n, p))
{
PxGeomRaycastHit& hit = *rayHits;
hit.distance = t;
hit.position = p;
hit.normal = n;
hit.flags |= PxHitFlag::ePOSITION | PxHitFlag::eNORMAL;
return 1;
}
return 0;
}
bool PxCustomGeometryExt::BaseConvexCallbacks::overlap(const PxGeometry& /*geom0*/, const PxTransform& pose0, const PxGeometry& geom1, const PxTransform& pose1, PxOverlapThreadContext*) const
{
switch (geom1.getType())
{
case PxGeometryType::eSPHERE:
case PxGeometryType::eCAPSULE:
case PxGeometryType::eBOX:
case PxGeometryType::eCONVEXMESH:
{
PxGjkQueryExt::ConvexGeomSupport geomSupport(geom1);
if (PxGjkQuery::overlap(*this, geomSupport, pose0, pose1))
return true;
break;
}
default:
break;
}
return false;
}
bool PxCustomGeometryExt::BaseConvexCallbacks::sweep(const PxVec3& unitDir, const PxReal maxDist,
const PxGeometry& geom0, const PxTransform& pose0, const PxGeometry& geom1, const PxTransform& pose1,
PxGeomSweepHit& sweepHit, PxHitFlags /*hitFlags*/, const PxReal inflation, PxSweepThreadContext*) const
{
PxBounds3 bounds0 = PxBounds3::transformFast(pose0, getLocalBounds(geom0));
switch (geom1.getType())
{
case PxGeometryType::eSPHERE:
case PxGeometryType::eCAPSULE:
case PxGeometryType::eBOX:
case PxGeometryType::eCONVEXMESH:
{
// See comment in BaseConvexCallbacks::raycast
PxBounds3 bounds; PxGeometryQuery::computeGeomBounds(bounds, geom1, pose1, 0, inflation);
bounds.include(bounds0);
PxReal wiseDist = PxMin(maxDist, bounds.getDimensions().magnitude());
PxGjkQueryExt::ConvexGeomSupport geomSupport(geom1, inflation);
PxReal t;
PxVec3 n, p;
if (PxGjkQuery::sweep(*this, geomSupport, pose0, pose1, unitDir, wiseDist, t, n, p))
{
sweepHit.distance = t;
sweepHit.position = p;
sweepHit.normal = n;
sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL;
return true;
}
break;
}
// sweep against triangle meshes and height fields for CCD support
case PxGeometryType::eTRIANGLEMESH:
case PxGeometryType::eHEIGHTFIELD:
{
PxReal radius = getLocalBounds(geom0).getExtents().magnitude();
PxGeometryHolder sweepGeom = PxSphereGeometry(radius + inflation);
PxTransform sweepGeomPose = pose0;
// for some reason, capsule can't have near zero height
// [assert @ source\geomutils\src\intersection\GuIntersectionCapsuleTriangle.cpp(37)]
// so I use capsule only if height is larger than 0.1% of radius
if (maxDist > radius * 0.001f)
{
sweepGeom = PxCapsuleGeometry(radius + inflation, maxDist * 0.5f);
sweepGeomPose = PxTransform(pose0.p - unitDir * maxDist * 0.5f, PxShortestRotation(PxVec3(1, 0, 0), unitDir));
}
PxU32 triangles[MAX_TRIANGLES];
bool overflow = false;
PxU32 triangleCount = (geom1.getType() == PxGeometryType::eTRIANGLEMESH) ?
PxMeshQuery::findOverlapTriangleMesh(sweepGeom.any(), sweepGeomPose, static_cast<const PxTriangleMeshGeometry&>(geom1), pose1, triangles, MAX_TRIANGLES, 0, overflow) :
PxMeshQuery::findOverlapHeightField(sweepGeom.any(), sweepGeomPose, static_cast<const PxHeightFieldGeometry&>(geom1), pose1, triangles, MAX_TRIANGLES, 0, overflow);
if(overflow)
PxGetFoundation().error(PxErrorCode::eDEBUG_INFO, PX_FL, "PxCustomGeometryExt::BaseConvexCallbacks::sweep() Too many triangles.\n");
sweepHit.distance = PX_MAX_F32;
const PxTransform identityPose(PxIdentity);
for (PxU32 i = 0; i < triangleCount; ++i)
{
PxTriangle tri;
if (geom1.getType() == PxGeometryType::eTRIANGLEMESH)
PxMeshQuery::getTriangle(static_cast<const PxTriangleMeshGeometry&>(geom1), pose1, triangles[i], tri);
else
PxMeshQuery::getTriangle(static_cast<const PxHeightFieldGeometry&>(geom1), pose1, triangles[i], tri);
TriangleSupport triSupport(tri.verts[0], tri.verts[1], tri.verts[2], inflation);
PxBounds3 bounds = bounds0;
for (PxU32 j = 0; j < 3; ++j)
bounds.include(tri.verts[j]);
bounds.fattenFast(inflation);
// See comment in BaseConvexCallbacks::raycast
PxReal wiseDist = PxMin(maxDist, bounds.getDimensions().magnitude());
PxReal t;
PxVec3 n, p;
if (PxGjkQuery::sweep(*this, triSupport, pose0, identityPose, unitDir, wiseDist, t, n, p))
{
if (sweepHit.distance > t)
{
sweepHit.faceIndex = triangles[i];
sweepHit.distance = t;
sweepHit.position = p;
sweepHit.normal = n;
sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX;
}
}
}
if (sweepHit.distance < PX_MAX_F32)
return true;
break;
}
default:
break;
}
return false;
}
bool PxCustomGeometryExt::BaseConvexCallbacks::usePersistentContactManifold(const PxGeometry& /*geometry*/, PxReal& breakingThreshold) const
{
// Even if we don't use persistent manifold, we still need to set proper breakingThreshold
// because the other geometry still can force the PCM usage. FLT_EPSILON ensures that
// the contacts will be discarded and recomputed every frame if actor moves.
breakingThreshold = FLT_EPSILON;
return false;
}
void PxCustomGeometryExt::BaseConvexCallbacks::setMargin(float m)
{
margin = m;
#if PX_SUPPORT_OMNI_PVD
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtBaseConvexCallbacks, margin, *this, margin);
#endif
}
///////////////////////////////////////////////////////////////////////////////
IMPLEMENT_CUSTOM_GEOMETRY_TYPE(PxCustomGeometryExt::CylinderCallbacks)
PxCustomGeometryExt::CylinderCallbacks::CylinderCallbacks(float _height, float _radius, int _axis, float _margin)
:
BaseConvexCallbacks(_margin), height(_height), radius(_radius), axis(_axis)
{
#if PX_SUPPORT_OMNI_PVD
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
OMNI_PVD_CREATE_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtCylinderCallbacks, *this);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtBaseConvexCallbacks, margin, *static_cast<BaseConvexCallbacks*>(this), margin);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtCylinderCallbacks, height, *this, height);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtCylinderCallbacks, radius, *this, radius);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtCylinderCallbacks, axis, *this, axis);
OMNI_PVD_WRITE_SCOPE_END
#endif
}
void PxCustomGeometryExt::CylinderCallbacks::setHeight(float h)
{
height = h;
#if PX_SUPPORT_OMNI_PVD
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtCylinderCallbacks, height, *this, height);
#endif
}
void PxCustomGeometryExt::CylinderCallbacks::setRadius(float r)
{
radius = r;
#if PX_SUPPORT_OMNI_PVD
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtCylinderCallbacks, radius, *this, radius);
#endif
}
void PxCustomGeometryExt::CylinderCallbacks::setAxis(int a)
{
axis = a;
#if PX_SUPPORT_OMNI_PVD
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtCylinderCallbacks, axis, *this, axis);
#endif
}
void PxCustomGeometryExt::CylinderCallbacks::visualize(const PxGeometry&, PxRenderOutput& out, const PxTransform& transform, const PxBounds3&) const
{
const float ERR = 0.001f;
out << gCollisionShapeColor;
out << transform;
int axis1 = (axis + 1) % 3;
int axis2 = (axis + 2) % 3;
PxVec3 zr(PxZero), rd(PxZero), ax(PxZero), ax1(PxZero), ax2(PxZero), r0(PxZero), r1(PxZero);
ax[axis] = ax1[axis1] = ax2[axis2] = 1.0f;
r0[axis1] = r1[axis2] = radius;
rd[axis1] = radius;
rd[axis] = -(height * 0.5f + margin);
drawCircle(zr, rd, ax, ERR, out);
rd[axis] = (height * 0.5f + margin);
drawCircle(zr, rd, ax, ERR, out);
rd[axis1] = radius + margin;
rd[axis] = -(height * 0.5f);
drawCircle(zr, rd, ax, ERR, out);
rd[axis] = (height * 0.5f);
drawCircle(zr, rd, ax, ERR, out);
drawLine(-ax * height * 0.5f + ax1 * (radius + margin), ax * height * 0.5f + ax1 * (radius + margin), out);
drawLine(-ax * height * 0.5f - ax1 * (radius + margin), ax * height * 0.5f - ax1 * (radius + margin), out);
drawLine(-ax * height * 0.5f + ax2 * (radius + margin), ax * height * 0.5f + ax2 * (radius + margin), out);
drawLine(-ax * height * 0.5f - ax2 * (radius + margin), ax * height * 0.5f - ax2 * (radius + margin), out);
drawQuarterCircle(-ax * height * 0.5f + ax1 * radius, -ax * margin, -ax2, ERR, out);
drawQuarterCircle(-ax * height * 0.5f - ax1 * radius, -ax * margin, ax2, ERR, out);
drawQuarterCircle(-ax * height * 0.5f + ax2 * radius, -ax * margin, ax1, ERR, out);
drawQuarterCircle(-ax * height * 0.5f - ax2 * radius, -ax * margin, -ax1, ERR, out);
drawQuarterCircle(ax * height * 0.5f + ax1 * radius, ax * margin, ax2, ERR, out);
drawQuarterCircle(ax * height * 0.5f - ax1 * radius, ax * margin, -ax2, ERR, out);
drawQuarterCircle(ax * height * 0.5f + ax2 * radius, ax * margin, -ax1, ERR, out);
drawQuarterCircle(ax * height * 0.5f - ax2 * radius, ax * margin, ax1, ERR, out);
}
PxVec3 PxCustomGeometryExt::CylinderCallbacks::supportLocal(const PxVec3& dir) const
{
float halfHeight = height * 0.5f;
PxVec3 d = dir.getNormalized();
switch (axis)
{
case 0: // X
{
if (PxSign2(d.x) != 0 && PxSign2(d.y) == 0 && PxSign2(d.z) == 0) return PxVec3(PxSign2(d.x) * halfHeight, 0, 0);
return PxVec3(PxSign2(d.x) * halfHeight, 0, 0) + PxVec3(0, d.y, d.z).getNormalized() * radius;
}
case 1: // Y
{
if (PxSign2(d.x) == 0 && PxSign2(d.y) != 0 && PxSign2(d.z) == 0) return PxVec3(0, PxSign2(d.y) * halfHeight, 0);
return PxVec3(0, PxSign2(d.y) * halfHeight, 0) + PxVec3(d.x, 0, d.z).getNormalized() * radius;
}
case 2: // Z
{
if (PxSign2(d.x) == 0 && PxSign2(d.y) == 0 && PxSign2(d.z) != 0) return PxVec3(0, 0, PxSign2(d.z) * halfHeight);
return PxVec3(0, 0, PxSign2(d.z) * halfHeight) + PxVec3(d.x, d.y, 0).getNormalized() * radius;
}
}
return PxVec3(0);
}
void PxCustomGeometryExt::CylinderCallbacks::computeMassProperties(const PxGeometry& /*geometry*/, PxMassProperties& massProperties) const
{
if (margin == 0)
{
PxMassProperties& mass = massProperties;
float R = radius, H = height;
mass.mass = PxPi * R * R * H;
mass.inertiaTensor = PxMat33(PxZero);
mass.centerOfMass = PxVec3(PxZero);
float I0 = mass.mass * R * R / 2.0f;
float I1 = mass.mass * (3 * R * R + H * H) / 12.0f;
mass.inertiaTensor[axis][axis] = I0;
mass.inertiaTensor[(axis + 1) % 3][(axis + 1) % 3] = mass.inertiaTensor[(axis + 2) % 3][(axis + 2) % 3] = I1;
}
else
{
const int SLICE_COUNT = 32;
PxMassProperties sliceMasses[SLICE_COUNT];
PxTransform slicePoses[SLICE_COUNT];
float sliceHeight = height / SLICE_COUNT;
for (int i = 0; i < SLICE_COUNT; ++i)
{
float t = -height * 0.5f + i * sliceHeight + sliceHeight * 0.5f;
float R = getRadiusAtHeight(t), H = sliceHeight;
PxMassProperties mass;
mass.mass = PxPi * R * R * H;
mass.inertiaTensor = PxMat33(PxZero);
mass.centerOfMass = PxVec3(PxZero);
float I0 = mass.mass * R * R / 2.0f;
float I1 = mass.mass * (3 * R * R + H * H) / 12.0f;
mass.inertiaTensor[axis][axis] = I0;
mass.inertiaTensor[(axis + 1) % 3][(axis + 1) % 3] = mass.inertiaTensor[(axis + 2) % 3][(axis + 2) % 3] = I1;
mass.centerOfMass[axis] = t;
sliceMasses[i] = mass;
slicePoses[i] = PxTransform(PxIdentity);
}
massProperties = PxMassProperties::sum(sliceMasses, slicePoses, SLICE_COUNT);
}
}
bool PxCustomGeometryExt::CylinderCallbacks::useSubstituteGeometry(PxGeometryHolder& geom, PxTransform& preTransform, const PxContactPoint& p, const PxTransform& pose0) const
{
// here I check if we contact with the cylender bases or the lateral surface
// where more than 1 contact point can be generated.
PxVec3 locN = pose0.rotateInv(p.normal);
float nAng = acosf(PxClamp(-locN[axis], -1.0f, 1.0f));
float epsAng = PxPi / 36.0f; // 5 degrees
if (nAng < epsAng || nAng > PxPi - epsAng)
{
// if we contact with the bases
// make the substitute geometry a box and rotate it so one of
// the corners matches the contact point
PxVec3 halfSize;
halfSize[axis] = height * 0.5f + margin;
halfSize[(axis + 1) % 3] = halfSize[(axis + 2) % 3] = radius / sqrtf(2.0f);
geom = PxBoxGeometry(halfSize);
PxVec3 axisDir(PxZero); axisDir[axis] = 1.0f;
PxVec3 locP = pose0.transformInv(p.point);
float s1 = locP[(axis + 1) % 3], s2 = locP[(axis + 2) % 3];
float ang = ((s1 * s1) + (s2 * s2) > 1e-3f) ? atan2f(s2, s1) : 0;
preTransform = PxTransform(PxQuat(ang + PxPi * 0.25f, axisDir));
return true;
}
else if (nAng > PxPiDivTwo - epsAng && nAng < PxPiDivTwo + epsAng)
{
// if it's the lateral surface
// make the substitute geometry a capsule and rotate it so it aligns
// with the cylinder surface
geom = PxCapsuleGeometry(radius + margin, height * 0.5f);
switch (axis)
{
case 0: preTransform = PxTransform(PxIdentity); break;
case 1: preTransform = PxTransform(PxQuat(PxPi * 0.5f, PxVec3(0, 0, 1))); break;
case 2: preTransform = PxTransform(PxQuat(PxPi * 0.5f, PxVec3(0, 1, 0))); break;
}
return true;
}
return false;
}
float PxCustomGeometryExt::CylinderCallbacks::getRadiusAtHeight(float h) const
{
if (h >= -height * 0.5f && h <= height * 0.5f)
return radius + margin;
if (h < -height * 0.5f)
{
float a = -h - height * 0.5f;
return radius + sqrtf(margin * margin - a * a);
}
if (h > height * 0.5f)
{
float a = h - height * 0.5f;
return radius + sqrtf(margin * margin - a * a);
}
PX_ASSERT(0);
return 0;
}
///////////////////////////////////////////////////////////////////////////////
IMPLEMENT_CUSTOM_GEOMETRY_TYPE(PxCustomGeometryExt::ConeCallbacks)
PxCustomGeometryExt::ConeCallbacks::ConeCallbacks(float _height, float _radius, int _axis, float _margin)
:
BaseConvexCallbacks(_margin), height(_height), radius(_radius), axis(_axis)
{
#if PX_SUPPORT_OMNI_PVD
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
OMNI_PVD_CREATE_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtConeCallbacks, *this);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtBaseConvexCallbacks, margin, *static_cast<BaseConvexCallbacks*>(this), margin);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtConeCallbacks, height, *this, height);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtConeCallbacks, radius, *this, radius);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtConeCallbacks, axis, *this, axis);
OMNI_PVD_WRITE_SCOPE_END
#endif
}
void PxCustomGeometryExt::ConeCallbacks::setHeight(float h)
{
height = h;
#if PX_SUPPORT_OMNI_PVD
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtConeCallbacks, height, *this, height);
#endif
}
void PxCustomGeometryExt::ConeCallbacks::setRadius(float r)
{
radius = r;
#if PX_SUPPORT_OMNI_PVD
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtConeCallbacks, radius, *this, radius);
#endif
}
void PxCustomGeometryExt::ConeCallbacks::setAxis(int a)
{
axis = a;
#if PX_SUPPORT_OMNI_PVD
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxCustomGeometryExtConeCallbacks, axis, *this, axis);
#endif
}
void PxCustomGeometryExt::ConeCallbacks::visualize(const PxGeometry&, PxRenderOutput& out, const PxTransform& transform, const PxBounds3&) const
{
const float ERR = 0.001f;
out << gCollisionShapeColor;
out << transform;
int axis1 = (axis + 1) % 3;
int axis2 = (axis + 2) % 3;
PxVec3 zr(PxZero), rd(PxZero), ax(PxZero), ax1(PxZero), ax2(PxZero), r0(PxZero), r1(PxZero);
ax[axis] = ax1[axis1] = ax2[axis2] = 1.0f;
r0[axis1] = r1[axis2] = radius;
float ang = atan2f(radius, height);
float aSin = sinf(ang);
rd[axis1] = radius;
rd[axis] = -(height * 0.5f + margin);
drawCircle(zr, rd, ax, ERR, out);
rd[axis] = -(height * 0.5f) + margin * aSin;
rd[axis1] = getRadiusAtHeight(rd[axis]);
drawCircle(zr, rd, ax, ERR, out);
rd[axis] = height * 0.5f + margin * aSin;
rd[axis1] = getRadiusAtHeight(rd[axis]);
drawCircle(zr, rd, ax, ERR, out);
float h0 = -height * 0.5f + margin * aSin, h1 = height * 0.5f + margin * aSin;
float s0 = getRadiusAtHeight(h0), s1 = getRadiusAtHeight(h1);
drawLine(ax * h0 + ax1 * s0, ax * h1 + ax1 * s1, out);
drawLine(ax * h0 - ax1 * s0, ax * h1 - ax1 * s1, out);
drawLine(ax * h0 + ax2 * s0, ax * h1 + ax2 * s1, out);
drawLine(ax * h0 - ax2 * s0, ax * h1 - ax2 * s1, out);
drawArc(-ax * height * 0.5f + ax1 * radius, -ax * margin, -ax2, PxPiDivTwo + ang, ERR, out);
drawArc(-ax * height * 0.5f - ax1 * radius, -ax * margin, ax2, PxPiDivTwo + ang, ERR, out);
drawArc(-ax * height * 0.5f + ax2 * radius, -ax * margin, ax1, PxPiDivTwo + ang, ERR, out);
drawArc(-ax * height * 0.5f - ax2 * radius, -ax * margin, -ax1, PxPiDivTwo + ang, ERR, out);
drawArc(ax * height * 0.5f, ax * margin, ax2, PxPiDivTwo - ang, ERR, out);
drawArc(ax * height * 0.5f, ax * margin, -ax2, PxPiDivTwo - ang, ERR, out);
drawArc(ax * height * 0.5f, ax * margin, -ax1, PxPiDivTwo - ang, ERR, out);
drawArc(ax * height * 0.5f, ax * margin, ax1, PxPiDivTwo - ang, ERR, out);
}
PxVec3 PxCustomGeometryExt::ConeCallbacks::supportLocal(const PxVec3& dir) const
{
float halfHeight = height * 0.5f;
float cosAlph = radius / sqrtf(height * height + radius * radius);
PxVec3 d = dir.getNormalized();
switch (axis)
{
case 0: // X
{
if (d.x > cosAlph || (PxSign2(d.x) != 0 && PxSign2(d.y) == 0 && PxSign2(d.z) == 0)) return PxVec3(PxSign2(d.x) * halfHeight, 0, 0);
return PxVec3(-halfHeight, 0, 0) + PxVec3(0, d.y, d.z).getNormalized() * radius;
}
case 1: // Y
{
if (d.y > cosAlph || (PxSign2(d.y) != 0 && PxSign2(d.x) == 0 && PxSign2(d.z) == 0)) return PxVec3(0, PxSign2(d.y) * halfHeight, 0);
return PxVec3(0, -halfHeight, 0) + PxVec3(d.x, 0, d.z).getNormalized() * radius;
}
case 2: // Z
{
if (d.z > cosAlph || (PxSign2(d.z) != 0 && PxSign2(d.x) == 0 && PxSign2(d.y) == 0)) return PxVec3(0, 0, PxSign2(d.z) * halfHeight);
return PxVec3(0, 0, -halfHeight) + PxVec3(d.x, d.y, 0).getNormalized() * radius;
}
}
return PxVec3(0);
}
void PxCustomGeometryExt::ConeCallbacks::computeMassProperties(const PxGeometry& /*geometry*/, PxMassProperties& massProperties) const
{
if (margin == 0)
{
PxMassProperties& mass = massProperties;
float H = height, R = radius;
mass.mass = PxPi * R * R * H / 3.0f;
mass.inertiaTensor = PxMat33(PxZero);
mass.centerOfMass = PxVec3(PxZero);
float I0 = mass.mass * R * R * 3.0f / 10.0f;
float I1 = mass.mass * (R * R * 3.0f / 20.0f + H * H * 3.0f / 80.0f);
mass.inertiaTensor[axis][axis] = I0;
mass.inertiaTensor[(axis + 1) % 3][(axis + 1) % 3] = mass.inertiaTensor[(axis + 2) % 3][(axis + 2) % 3] = I1;
mass.centerOfMass[axis] = -H / 4.0f;
mass.inertiaTensor = PxMassProperties::translateInertia(mass.inertiaTensor, mass.mass, mass.centerOfMass);
}
else
{
const int SLICE_COUNT = 32;
PxMassProperties sliceMasses[SLICE_COUNT];
PxTransform slicePoses[SLICE_COUNT];
float sliceHeight = height / SLICE_COUNT;
for (int i = 0; i < SLICE_COUNT; ++i)
{
float t = -height * 0.5f + i * sliceHeight + sliceHeight * 0.5f;
float R = getRadiusAtHeight(t), H = sliceHeight;
PxMassProperties mass;
mass.mass = PxPi * R * R * H;
mass.inertiaTensor = PxMat33(PxZero);
mass.centerOfMass = PxVec3(PxZero);
float I0 = mass.mass * R * R / 2.0f;
float I1 = mass.mass * (3 * R * R + H * H) / 12.0f;
mass.inertiaTensor[axis][axis] = I0;
mass.inertiaTensor[(axis + 1) % 3][(axis + 1) % 3] = mass.inertiaTensor[(axis + 2) % 3][(axis + 2) % 3] = I1;
mass.centerOfMass[axis] = t;
sliceMasses[i] = mass;
slicePoses[i] = PxTransform(PxIdentity);
}
massProperties = PxMassProperties::sum(sliceMasses, slicePoses, SLICE_COUNT);
massProperties.inertiaTensor = PxMassProperties::translateInertia(massProperties.inertiaTensor, massProperties.mass, massProperties.centerOfMass);
}
}
bool PxCustomGeometryExt::ConeCallbacks::useSubstituteGeometry(PxGeometryHolder& geom, PxTransform& preTransform, const PxContactPoint& p, const PxTransform& pose0) const
{
// here I check if we contact with the cone base or the lateral surface
// where more than 1 contact point can be generated.
PxVec3 locN = pose0.rotateInv(p.normal);
float nAng = acosf(PxClamp(-locN[axis], -1.0f, 1.0f));
float epsAng = PxPi / 36.0f; // 5 degrees
float coneAng = atan2f(radius, height);
if (nAng > PxPi - epsAng)
{
// if we contact with the base
// make the substitute geometry a box and rotate it so one of
// the corners matches the contact point
PxVec3 halfSize;
halfSize[axis] = height * 0.5f + margin;
halfSize[(axis + 1) % 3] = halfSize[(axis + 2) % 3] = radius / sqrtf(2.0f);
geom = PxBoxGeometry(halfSize);
PxVec3 axisDir(PxZero); axisDir[axis] = 1.0f;
PxVec3 locP = pose0.transformInv(p.point);
float s1 = locP[(axis + 1) % 3], s2 = locP[(axis + 2) % 3];
float ang = ((s1 * s1) + (s2 * s2) > 1e-3f) ? atan2f(s2, s1) : 0;
preTransform = PxTransform(PxQuat(ang + PxPi * 0.25f, axisDir));
return true;
}
else if (nAng > PxPiDivTwo - coneAng - epsAng && nAng < PxPiDivTwo - coneAng + epsAng)
{
// if it's the lateral surface
// make the substitute geometry a capsule and rotate it so it aligns
// with the cone surface
geom = PxCapsuleGeometry(radius * 0.25f + margin, sqrtf(height * height + radius * radius) * 0.5f);
switch (axis)
{
case 0:
{
PxVec3 capC = (PxVec3(height * 0.5f, 0, 0) + PxVec3(-height * 0.5f, radius, 0)) * 0.5f - PxVec3(radius, height, 0).getNormalized() * radius * 0.25f;
preTransform = PxTransform(capC, PxQuat(-coneAng, PxVec3(0, 0, 1)));
break;
}
case 1:
{
PxVec3 capC = (PxVec3(0, height * 0.5f, 0) + PxVec3(0, -height * 0.5f, radius)) * 0.5f - PxVec3(0, radius, height).getNormalized() * radius * 0.25f;
preTransform = PxTransform(capC, PxQuat(-coneAng, PxVec3(1, 0, 0)) * PxQuat(PxPiDivTwo, PxVec3(0, 0, 1)));
break;
}
case 2:
{
PxVec3 capC = (PxVec3(0, 0, height * 0.5f) + PxVec3(radius, 0, -height * 0.5f)) * 0.5f - PxVec3(height, 0, radius).getNormalized() * radius * 0.25f;
preTransform = PxTransform(capC, PxQuat(-coneAng, PxVec3(0, 1, 0)) * PxQuat(PxPiDivTwo, PxVec3(0, 1, 0)));
break;
}
}
PxVec3 axisDir(PxZero); axisDir[axis] = 1.0f;
float n1 = -locN[(axis + 1) % 3], n2 = -locN[(axis + 2) % 3];
float ang = atan2f(n2, n1);
// PT:: tag: scalar transform*transform
preTransform = PxTransform(PxQuat(ang, axisDir)) * preTransform;
return true;
}
return false;
}
float PxCustomGeometryExt::ConeCallbacks::getRadiusAtHeight(float h) const
{
float angle = atan2f(radius, height);
float aSin = sinf(angle);
float aCos = cosf(angle);
if (h >= -height * 0.5f + margin * aSin && h <= height * 0.5f + margin * aSin)
return radius * (height * 0.5f - h) / height + margin / aCos;
if (h < -height * 0.5f + margin * aSin)
{
float a = -h - height * 0.5f;
return radius + sqrtf(margin * margin - a * a);
}
if (h > height * 0.5f + margin * aSin)
{
float a = h - height * 0.5f;
return sqrtf(margin * margin - a * a);
}
PX_ASSERT(0);
return 0;
}

View File

@@ -0,0 +1,511 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxCustomSceneQuerySystem.h"
#include "extensions/PxShapeExt.h"
#include "foundation/PxAlloca.h"
#include "foundation/PxHashMap.h"
#include "foundation/PxUserAllocated.h"
#include "geometry/PxBVH.h"
#include "GuActorShapeMap.h"
#include "ExtSqQuery.h"
#include "SqFactory.h"
#include "PxRigidActor.h"
#include "PxPruningStructure.h"
using namespace physx;
using namespace Sq;
using namespace Gu;
// PT: this customized version uses:
// - a modified version of Sq::PrunerManager, named Sq::ExtPrunerManager, located in ExtSqManager.cpp
// - a modified version of Sq::SceneQueries, named Sq::ExtSceneQueries, located in ExtSqQuery.cpp
//
// Sq::PrunerManager and Sq::SceneQueries live in the SceneQuery lib, and are used by PhysX internally
// to implement the regular SQ system.
//
// Sq::ExtPrunerManager and Sq::ExtSceneQueries live in the Extensions lib, and are not used by the
// regular PhysX SQ system. They are examples of how the default code can be customized.
//
static CompanionPrunerType getCompanionType(PxDynamicTreeSecondaryPruner::Enum type)
{
switch(type)
{
case PxDynamicTreeSecondaryPruner::eNONE: return COMPANION_PRUNER_NONE;
case PxDynamicTreeSecondaryPruner::eBUCKET: return COMPANION_PRUNER_BUCKET;
case PxDynamicTreeSecondaryPruner::eINCREMENTAL: return COMPANION_PRUNER_INCREMENTAL;
case PxDynamicTreeSecondaryPruner::eBVH: return COMPANION_PRUNER_AABB_TREE;
case PxDynamicTreeSecondaryPruner::eLAST: return COMPANION_PRUNER_NONE;
}
return COMPANION_PRUNER_NONE;
}
static BVHBuildStrategy getBuildStrategy(PxBVHBuildStrategy::Enum bs)
{
switch(bs)
{
case PxBVHBuildStrategy::eFAST: return BVH_SPLATTER_POINTS;
case PxBVHBuildStrategy::eDEFAULT: return BVH_SPLATTER_POINTS_SPLIT_GEOM_CENTER;
case PxBVHBuildStrategy::eSAH: return BVH_SAH;
case PxBVHBuildStrategy::eLAST: return BVH_SPLATTER_POINTS;
}
return BVH_SPLATTER_POINTS;
}
static Pruner* create(PxPruningStructureType::Enum type, PxU64 contextID, PxDynamicTreeSecondaryPruner::Enum secondaryType, PxBVHBuildStrategy::Enum buildStrategy, PxU32 nbObjectsPerNode)
{
// if(0)
// return createIncrementalPruner(contextID);
const CompanionPrunerType cpType = getCompanionType(secondaryType);
const BVHBuildStrategy bs = getBuildStrategy(buildStrategy);
Pruner* pruner = NULL;
switch(type)
{
case PxPruningStructureType::eNONE: { pruner = createBucketPruner(contextID); break; }
case PxPruningStructureType::eDYNAMIC_AABB_TREE: { pruner = createAABBPruner(contextID, true, cpType, bs, nbObjectsPerNode); break; }
case PxPruningStructureType::eSTATIC_AABB_TREE: { pruner = createAABBPruner(contextID, false, cpType, bs, nbObjectsPerNode); break; }
case PxPruningStructureType::eLAST: break;
}
return pruner;
}
#define EXT_PRUNER_EPSILON 0.005f
// PT: in this external implementation we'll use Px pointers instead of Np pointers in the payload.
static PX_FORCE_INLINE void setPayload(PrunerPayload& pp, const PxShape* shape, const PxRigidActor* actor)
{
pp.data[0] = size_t(shape);
pp.data[1] = size_t(actor);
}
static PX_FORCE_INLINE PxShape* getShapeFromPayload(const PrunerPayload& payload)
{
return reinterpret_cast<PxShape*>(payload.data[0]);
}
static PX_FORCE_INLINE PxRigidActor* getActorFromPayload(const PrunerPayload& payload)
{
return reinterpret_cast<PxRigidActor*>(payload.data[1]);
}
static PX_FORCE_INLINE bool isDynamicActor(const PxRigidActor& actor)
{
const PxType actorType = actor.getConcreteType();
return actorType != PxConcreteType::eRIGID_STATIC;
}
///////////////////////////////////////////////////////////////////////////////
static PX_FORCE_INLINE ActorShapeData createActorShapeData(PrunerHandle h, PrunerCompoundId id) { return (ActorShapeData(id) << 32) | ActorShapeData(h); }
static PX_FORCE_INLINE PrunerHandle getPrunerHandle(ActorShapeData data) { return PrunerHandle(data); }
static PX_FORCE_INLINE PrunerCompoundId getCompoundID(ActorShapeData data) { return PrunerCompoundId(data >> 32); }
///////////////////////////////////////////////////////////////////////////////
namespace
{
class ExtSqAdapter : public ExtQueryAdapter
{
PX_NOCOPY(ExtSqAdapter)
public:
ExtSqAdapter(const PxCustomSceneQuerySystemAdapter& adapter) : mUserAdapter(adapter)/*, mFilterData(NULL)*/ {}
virtual ~ExtSqAdapter() {}
// Adapter
virtual const PxGeometry& getGeometry(const PrunerPayload& payload) const;
//~Adapter
// ExtQueryAdapter
virtual PrunerHandle findPrunerHandle(const PxQueryCache& cache, PrunerCompoundId& compoundId, PxU32& prunerIndex) const;
virtual void getFilterData(const PrunerPayload& payload, PxFilterData& filterData) const;
virtual void getActorShape(const PrunerPayload& payload, PxActorShape& actorShape) const;
virtual bool processPruner(PxU32 prunerIndex, const PxQueryThreadContext* context, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall) const;
//~ExtQueryAdapter
const PxCustomSceneQuerySystemAdapter& mUserAdapter;
ActorShapeMap mDatabase;
const PxQueryFilterData* mFilterData;
};
}
const PxGeometry& ExtSqAdapter::getGeometry(const PrunerPayload& payload) const
{
PxShape* shape = getShapeFromPayload(payload);
return shape->getGeometry();
}
PrunerHandle ExtSqAdapter::findPrunerHandle(const PxQueryCache& cache, PrunerCompoundId& compoundId, PxU32& prunerIndex) const
{
const PxU32 actorIndex = cache.actor->getInternalActorIndex();
PX_ASSERT(actorIndex!=0xffffffff);
const ActorShapeData actorShapeData = mDatabase.find(actorIndex, cache.actor, cache.shape);
compoundId = getCompoundID(actorShapeData);
prunerIndex = mUserAdapter.getPrunerIndex(*cache.actor, *cache.shape);
return getPrunerHandle(actorShapeData);
}
void ExtSqAdapter::getFilterData(const PrunerPayload& payload, PxFilterData& filterData) const
{
PxShape* shape = getShapeFromPayload(payload);
filterData = shape->getQueryFilterData();
}
void ExtSqAdapter::getActorShape(const PrunerPayload& payload, PxActorShape& actorShape) const
{
actorShape.actor = getActorFromPayload(payload);
actorShape.shape = getShapeFromPayload(payload);
}
bool ExtSqAdapter::processPruner(PxU32 prunerIndex, const PxQueryThreadContext* context, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall) const
{
return mUserAdapter.processPruner(prunerIndex, context, filterData, filterCall);
}
///////////////////////////////////////////////////////////////////////////////
namespace
{
class CustomPxSQ : public PxCustomSceneQuerySystem, public PxUserAllocated
{
public:
CustomPxSQ(const PxCustomSceneQuerySystemAdapter& adapter, ExtPVDCapture* pvd, PxU64 contextID,
PxSceneQueryUpdateMode::Enum mode, bool usesTreeOfPruners) :
mExtAdapter (adapter),
mQueries (pvd, contextID, EXT_PRUNER_EPSILON, mExtAdapter, usesTreeOfPruners),
mUpdateMode (mode),
mRefCount (1)
{}
virtual ~CustomPxSQ() {}
virtual void release();
virtual void acquireReference();
virtual void preallocate(PxU32 prunerIndex, PxU32 nbShapes) { SQ().preallocate(prunerIndex, nbShapes); }
virtual void addSQShape( const PxRigidActor& actor, const PxShape& shape, const PxBounds3& bounds,
const PxTransform& transform, const PxSQCompoundHandle* compoundHandle, bool hasPruningStructure);
virtual void removeSQShape(const PxRigidActor& actor, const PxShape& shape);
virtual void updateSQShape(const PxRigidActor& actor, const PxShape& shape, const PxTransform& transform);
virtual PxSQCompoundHandle addSQCompound(const PxRigidActor& actor, const PxShape** shapes, const PxBVH& pxbvh, const PxTransform* transforms);
virtual void removeSQCompound(PxSQCompoundHandle compoundHandle);
virtual void updateSQCompound(PxSQCompoundHandle compoundHandle, const PxTransform& compoundTransform);
virtual void flushUpdates() { SQ().flushUpdates(); }
virtual void flushMemory() { SQ().flushMemory(); }
virtual void visualize(PxU32 prunerIndex, PxRenderOutput& out) const { SQ().visualize(prunerIndex, out); }
virtual void shiftOrigin(const PxVec3& shift) { SQ().shiftOrigin(shift); }
virtual PxSQBuildStepHandle prepareSceneQueryBuildStep(PxU32 prunerIndex);
virtual void sceneQueryBuildStep(PxSQBuildStepHandle handle);
virtual void finalizeUpdates();
virtual void setDynamicTreeRebuildRateHint(PxU32 dynTreeRebuildRateHint) { SQ().setDynamicTreeRebuildRateHint(dynTreeRebuildRateHint); }
virtual PxU32 getDynamicTreeRebuildRateHint() const { return SQ().getDynamicTreeRebuildRateHint(); }
virtual void forceRebuildDynamicTree(PxU32 prunerIndex) { SQ().forceRebuildDynamicTree(prunerIndex); }
virtual PxSceneQueryUpdateMode::Enum getUpdateMode() const { return mUpdateMode; }
virtual void setUpdateMode(PxSceneQueryUpdateMode::Enum mode) { mUpdateMode = mode; }
virtual PxU32 getStaticTimestamp() const { return SQ().getStaticTimestamp(); }
virtual void merge(const PxPruningStructure& pxps);
virtual bool raycast(const PxVec3& origin, const PxVec3& unitDir, const PxReal distance,
PxRaycastCallback& hitCall, PxHitFlags hitFlags,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, PxGeometryQueryFlags flags) const;
virtual bool sweep( const PxGeometry& geometry, const PxTransform& pose,
const PxVec3& unitDir, const PxReal distance,
PxSweepCallback& hitCall, PxHitFlags hitFlags,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, const PxReal inflation, PxGeometryQueryFlags flags) const;
virtual bool overlap(const PxGeometry& geometry, const PxTransform& transform,
PxOverlapCallback& hitCall,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, PxGeometryQueryFlags flags) const;
virtual PxSQPrunerHandle getHandle(const PxRigidActor& actor, const PxShape& shape, PxU32& prunerIndex) const;
virtual void sync(PxU32 prunerIndex, const PxSQPrunerHandle* handles, const PxU32* indices, const PxBounds3* bounds,
const PxTransform32* transforms, PxU32 count, const PxBitMap& ignoredIndices);
virtual PxU32 addPruner(PxPruningStructureType::Enum primaryType, PxDynamicTreeSecondaryPruner::Enum secondaryType, PxU32 preallocated);
virtual PxU32 startCustomBuildstep();
virtual void customBuildstep(PxU32 index);
virtual void finishCustomBuildstep();
PX_FORCE_INLINE ExtPrunerManager& SQ() { return mQueries.mSQManager; }
PX_FORCE_INLINE const ExtPrunerManager& SQ() const { return mQueries.mSQManager; }
ExtSqAdapter mExtAdapter;
ExtSceneQueries mQueries;
PxSceneQueryUpdateMode::Enum mUpdateMode;
PxU32 mRefCount;
};
}
///////////////////////////////////////////////////////////////////////////////
void addExternalSQ(PxSceneQuerySystem* added);
void removeExternalSQ(PxSceneQuerySystem* removed);
void CustomPxSQ::release()
{
mRefCount--;
if(!mRefCount)
{
removeExternalSQ(this);
PX_DELETE_THIS;
}
}
void CustomPxSQ::acquireReference()
{
mRefCount++;
}
void CustomPxSQ::addSQShape(const PxRigidActor& actor, const PxShape& shape, const PxBounds3& bounds, const PxTransform& transform, const PxSQCompoundHandle* compoundHandle, bool hasPruningStructure)
{
PrunerPayload payload;
setPayload(payload, &shape, &actor);
const bool isDynamic = isDynamicActor(actor);
const PxU32 prunerIndex = mExtAdapter.mUserAdapter.getPrunerIndex(actor, shape);
const PrunerCompoundId cid = compoundHandle ? PrunerCompoundId(*compoundHandle) : INVALID_COMPOUND_ID;
const PrunerHandle shapeHandle = SQ().addPrunerShape(payload, prunerIndex, isDynamic, cid, bounds, transform, hasPruningStructure);
const PxU32 actorIndex = actor.getInternalActorIndex();
PX_ASSERT(actorIndex!=0xffffffff);
mExtAdapter.mDatabase.add(actorIndex, &actor, &shape, createActorShapeData(shapeHandle, cid));
}
namespace
{
struct DatabaseCleaner : PrunerPayloadRemovalCallback
{
DatabaseCleaner(ExtSqAdapter& adapter) : mAdapter(adapter){}
virtual void invoke(PxU32 nbRemoved, const PrunerPayload* removed) PX_OVERRIDE PX_FINAL
{
PxU32 actorIndex = 0xffffffff;
const PxRigidActor* cachedActor = NULL;
while(nbRemoved--)
{
const PrunerPayload& payload = *removed++;
const PxRigidActor* actor = getActorFromPayload(payload);
if(actor!=cachedActor)
{
actorIndex = actor->getInternalActorIndex();
cachedActor = actor;
}
PX_ASSERT(actorIndex!=0xffffffff);
bool status = mAdapter.mDatabase.remove(actorIndex, actor, getShapeFromPayload(payload), NULL);
PX_ASSERT(status);
PX_UNUSED(status);
}
}
ExtSqAdapter& mAdapter;
PX_NOCOPY(DatabaseCleaner)
};
}
void CustomPxSQ::removeSQShape(const PxRigidActor& actor, const PxShape& shape)
{
const bool isDynamic = isDynamicActor(actor);
const PxU32 prunerIndex = mExtAdapter.mUserAdapter.getPrunerIndex(actor, shape);
const PxU32 actorIndex = actor.getInternalActorIndex();
PX_ASSERT(actorIndex!=0xffffffff);
ActorShapeData actorShapeData;
mExtAdapter.mDatabase.remove(actorIndex, &actor, &shape, &actorShapeData);
const PrunerHandle shapeHandle = getPrunerHandle(actorShapeData);
const PrunerCompoundId compoundId = getCompoundID(actorShapeData);
SQ().removePrunerShape(prunerIndex, isDynamic, compoundId, shapeHandle, NULL);
}
void CustomPxSQ::updateSQShape(const PxRigidActor& actor, const PxShape& shape, const PxTransform& transform)
{
const bool isDynamic = isDynamicActor(actor);
const PxU32 prunerIndex = mExtAdapter.mUserAdapter.getPrunerIndex(actor, shape);
const PxU32 actorIndex = actor.getInternalActorIndex();
PX_ASSERT(actorIndex!=0xffffffff);
const ActorShapeData actorShapeData = mExtAdapter.mDatabase.find(actorIndex, &actor, &shape);
const PrunerHandle shapeHandle = getPrunerHandle(actorShapeData);
const PrunerCompoundId cid = getCompoundID(actorShapeData);
SQ().markForUpdate(prunerIndex, isDynamic, cid, shapeHandle, transform);
}
PxSQCompoundHandle CustomPxSQ::addSQCompound(const PxRigidActor& actor, const PxShape** shapes, const PxBVH& bvh, const PxTransform* transforms)
{
const PxU32 numSqShapes = bvh.getNbBounds();
PX_ALLOCA(payloads, PrunerPayload, numSqShapes);
for(PxU32 i=0; i<numSqShapes; i++)
setPayload(payloads[i], shapes[i], &actor);
const PxU32 actorIndex = actor.getInternalActorIndex();
PX_ASSERT(actorIndex!=0xffffffff);
PX_ALLOCA(shapeHandles, PrunerHandle, numSqShapes);
SQ().addCompoundShape(bvh, actorIndex, actor.getGlobalPose(), shapeHandles, payloads, transforms, isDynamicActor(actor));
for(PxU32 i=0; i<numSqShapes; i++)
{
// PT: TODO: actorIndex is now redundant!
mExtAdapter.mDatabase.add(actorIndex, &actor, shapes[i], createActorShapeData(shapeHandles[i], actorIndex));
}
return PxSQCompoundHandle(actorIndex);
}
void CustomPxSQ::removeSQCompound(PxSQCompoundHandle compoundHandle)
{
DatabaseCleaner cleaner(mExtAdapter);
SQ().removeCompoundActor(PrunerCompoundId(compoundHandle), &cleaner);
}
void CustomPxSQ::updateSQCompound(PxSQCompoundHandle compoundHandle, const PxTransform& compoundTransform)
{
SQ().updateCompoundActor(PrunerCompoundId(compoundHandle), compoundTransform);
}
PxSQBuildStepHandle CustomPxSQ::prepareSceneQueryBuildStep(PxU32 prunerIndex)
{
return SQ().prepareSceneQueriesUpdate(prunerIndex);
}
void CustomPxSQ::sceneQueryBuildStep(PxSQBuildStepHandle handle)
{
SQ().sceneQueryBuildStep(handle);
}
void CustomPxSQ::finalizeUpdates()
{
switch(mUpdateMode)
{
case PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_ENABLED: SQ().afterSync(true, true); break;
case PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_DISABLED: SQ().afterSync(true, false); break;
case PxSceneQueryUpdateMode::eBUILD_DISABLED_COMMIT_DISABLED: SQ().afterSync(false, false); break;
}
}
void CustomPxSQ::merge(const PxPruningStructure& /*pxps*/)
{
PX_ASSERT(!"Not supported by this custom SQ system");
// PT: PxPruningStructure only knows about the regular static/dynamic pruners, so it is not
// compatible with this custom version.
}
bool CustomPxSQ::raycast( const PxVec3& origin, const PxVec3& unitDir, const PxReal distance,
PxRaycastCallback& hitCall, PxHitFlags hitFlags,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, PxGeometryQueryFlags flags) const
{
return mQueries._raycast(origin, unitDir, distance, hitCall, hitFlags, filterData, filterCall, cache, flags);
}
bool CustomPxSQ::sweep( const PxGeometry& geometry, const PxTransform& pose,
const PxVec3& unitDir, const PxReal distance,
PxSweepCallback& hitCall, PxHitFlags hitFlags,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, const PxReal inflation, PxGeometryQueryFlags flags) const
{
return mQueries._sweep(geometry, pose, unitDir, distance, hitCall, hitFlags, filterData, filterCall, cache, inflation, flags);
}
bool CustomPxSQ::overlap( const PxGeometry& geometry, const PxTransform& transform,
PxOverlapCallback& hitCall,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, PxGeometryQueryFlags flags) const
{
return mQueries._overlap( geometry, transform, hitCall, filterData, filterCall, cache, flags);
}
PxSQPrunerHandle CustomPxSQ::getHandle(const PxRigidActor& actor, const PxShape& shape, PxU32& prunerIndex) const
{
const PxU32 actorIndex = actor.getInternalActorIndex();
PX_ASSERT(actorIndex!=0xffffffff);
const ActorShapeData actorShapeData = mExtAdapter.mDatabase.find(actorIndex, &actor, &shape);
prunerIndex = mExtAdapter.mUserAdapter.getPrunerIndex(actor, shape);
return PxSQPrunerHandle(getPrunerHandle(actorShapeData));
}
void CustomPxSQ::sync(PxU32 prunerIndex, const PxSQPrunerHandle* handles, const PxU32* indices, const PxBounds3* bounds,
const PxTransform32* transforms, PxU32 count, const PxBitMap& ignoredIndices)
{
SQ().sync(prunerIndex, handles, indices, bounds, transforms, count, ignoredIndices);
}
PxU32 CustomPxSQ::addPruner(PxPruningStructureType::Enum primaryType, PxDynamicTreeSecondaryPruner::Enum secondaryType, PxU32 preallocated)
{
Pruner* pruner = create(primaryType, mQueries.getContextId(), secondaryType, PxBVHBuildStrategy::eFAST, 4);
return mQueries.mSQManager.addPruner(pruner, preallocated);
}
PxU32 CustomPxSQ::startCustomBuildstep()
{
return SQ().startCustomBuildstep();
}
void CustomPxSQ::customBuildstep(PxU32 index)
{
SQ().customBuildstep(index);
}
void CustomPxSQ::finishCustomBuildstep()
{
SQ().finishCustomBuildstep();
}
///////////////////////////////////////////////////////////////////////////////
PxCustomSceneQuerySystem* physx::PxCreateCustomSceneQuerySystem(PxSceneQueryUpdateMode::Enum sceneQueryUpdateMode, PxU64 contextID, const PxCustomSceneQuerySystemAdapter& adapter, bool usesTreeOfPruners)
{
ExtPVDCapture* pvd = NULL;
CustomPxSQ* pxsq = PX_NEW(CustomPxSQ)(adapter, pvd, contextID, sceneQueryUpdateMode, usesTreeOfPruners);
addExternalSQ(pxsq);
return pxsq;
}
///////////////////////////////////////////////////////////////////////////////

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,172 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_D6_JOINT_H
#define EXT_D6_JOINT_H
#include "extensions/PxD6Joint.h"
#include "ExtJoint.h"
namespace physx
{
struct PxD6JointGeneratedValues;
namespace Ext
{
struct D6JointData : public JointData
{
static constexpr PxU32 sDriveEntryCapacity = 6;
PxD6Motion::Enum motion[6];
PxJointLinearLimit distanceLimit;
PxJointLinearLimitPair linearLimitX;
PxJointLinearLimitPair linearLimitY;
PxJointLinearLimitPair linearLimitZ;
PxJointAngularLimitPair twistLimit;
PxJointLimitCone swingLimit;
PxJointLimitPyramid pyramidSwingLimit;
PxD6JointDrive drive[sDriveEntryCapacity];
PxTransform drivePosition;
PxVec3 driveLinearVelocity;
PxVec3 driveAngularVelocity;
// derived quantities
PxU32 locked; // bitmap of locked DOFs
PxU32 limited; // bitmap of limited DOFs
PxU32 driving; // bitmap of active drives (implies driven DOFs not locked)
PxReal distanceMinDist; // distance limit minimum distance to get a good direction
// PT: the PxD6Motion values are now shared for both kind of linear limits, so we need
// an extra bool to know which one(s) should be actually used.
bool mUseDistanceLimit;
bool mUseNewLinearLimits;
// PT: the swing limits can now be a cone or a pyramid, so we need
// an extra bool to know which one(s) should be actually used.
bool mUseConeLimit;
bool mUsePyramidLimits;
PxU8 angularDriveConfig; // stores the angular drive config (PxD6AngularDriveConfig::Enum)
private:
D6JointData(const PxJointLinearLimit& distance,
const PxJointLinearLimitPair& linearX,
const PxJointLinearLimitPair& linearY,
const PxJointLinearLimitPair& linearZ,
const PxJointAngularLimitPair& twist,
const PxJointLimitCone& swing,
const PxJointLimitPyramid& pyramid) :
distanceLimit (distance),
linearLimitX (linearX),
linearLimitY (linearY),
linearLimitZ (linearZ),
twistLimit (twist),
swingLimit (swing),
pyramidSwingLimit (pyramid),
mUseDistanceLimit (false),
mUseNewLinearLimits (false),
mUseConeLimit (false),
mUsePyramidLimits (false),
angularDriveConfig (PxD6AngularDriveConfig::eLEGACY)
{}
};
typedef JointT<PxD6Joint, D6JointData, PxD6JointGeneratedValues> D6JointT;
class D6Joint : public D6JointT
{
public:
// PX_SERIALIZATION
D6Joint(PxBaseFlags baseFlags) : D6JointT(baseFlags) {}
void resolveReferences(PxDeserializationContext& context);
static D6Joint* createObject(PxU8*& address, PxDeserializationContext& context) { return createJointObject<D6Joint>(address, context); }
//~PX_SERIALIZATION
D6Joint(const PxTolerancesScale& scale, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1);
#if PX_SUPPORT_OMNI_PVD
virtual ~D6Joint();
#endif
// PxD6Joint
virtual void setMotion(PxD6Axis::Enum index, PxD6Motion::Enum t) PX_OVERRIDE;
virtual PxD6Motion::Enum getMotion(PxD6Axis::Enum index) const PX_OVERRIDE;
virtual PxReal getTwistAngle() const PX_OVERRIDE;
virtual PxReal getSwingYAngle() const PX_OVERRIDE;
virtual PxReal getSwingZAngle() const PX_OVERRIDE;
virtual void setDistanceLimit(const PxJointLinearLimit& l) PX_OVERRIDE;
virtual PxJointLinearLimit getDistanceLimit() const PX_OVERRIDE;
virtual void setLinearLimit(PxD6Axis::Enum axis, const PxJointLinearLimitPair& limit) PX_OVERRIDE;
virtual PxJointLinearLimitPair getLinearLimit(PxD6Axis::Enum axis) const PX_OVERRIDE;
virtual void setTwistLimit(const PxJointAngularLimitPair& l) PX_OVERRIDE;
virtual PxJointAngularLimitPair getTwistLimit() const PX_OVERRIDE;
virtual void setSwingLimit(const PxJointLimitCone& l) PX_OVERRIDE;
virtual PxJointLimitCone getSwingLimit() const PX_OVERRIDE;
virtual void setPyramidSwingLimit(const PxJointLimitPyramid& limit) PX_OVERRIDE;
virtual PxJointLimitPyramid getPyramidSwingLimit() const PX_OVERRIDE;
virtual void setDrive(PxD6Drive::Enum index, const PxD6JointDrive& d) PX_OVERRIDE;
virtual PxD6JointDrive getDrive(PxD6Drive::Enum index) const PX_OVERRIDE;
virtual void setDrivePosition(const PxTransform& pose, bool autowake = true) PX_OVERRIDE;
virtual PxTransform getDrivePosition() const PX_OVERRIDE;
virtual void setDriveVelocity(const PxVec3& linear, const PxVec3& angular, bool autowake = true) PX_OVERRIDE;
virtual void getDriveVelocity(PxVec3& linear, PxVec3& angular) const PX_OVERRIDE;
virtual PxD6JointGPUIndex getGPUIndex() const PX_OVERRIDE;
virtual void setAngularDriveConfig(PxD6AngularDriveConfig::Enum) PX_OVERRIDE;
virtual PxD6AngularDriveConfig::Enum getAngularDriveConfig() const PX_OVERRIDE;
//~PxD6Joint
// PxConstraintConnector
virtual PxConstraintSolverPrep getPrep() const PX_OVERRIDE;
virtual void* prepareData() PX_OVERRIDE;
#if PX_SUPPORT_OMNI_PVD
virtual void updateOmniPvdProperties() const PX_OVERRIDE;
#endif
//~PxConstraintConnector
#if PX_SUPPORT_OMNI_PVD
friend void omniPvdInitJoint<D6Joint>(D6Joint& joint);
#endif
private:
PX_FORCE_INLINE bool isDriveActive(PxU32 index) const
{
const PxD6JointDrive& d = data().drive[index];
return d.stiffness!=0 || d.damping != 0;
}
bool mRecomputeMotion;
bool mPadding[3]; // PT: padding from prev bool
};
} // namespace Ext
} // namespace physx
#endif

View File

@@ -0,0 +1,250 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "foundation/PxMathUtils.h"
#include "extensions/PxD6JointCreate.h"
#include "extensions/PxD6Joint.h"
#include "extensions/PxFixedJoint.h"
#include "extensions/PxRevoluteJoint.h"
#include "extensions/PxSphericalJoint.h"
#include "extensions/PxPrismaticJoint.h"
#include "extensions/PxDistanceJoint.h"
#include "PxPhysics.h"
using namespace physx;
static const PxVec3 gX(1.0f, 0.0f, 0.0f);
PxJoint* physx::PxD6JointCreate_Fixed(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, bool useD6)
{
const PxTransform jointFrame0(localPos0);
const PxTransform jointFrame1(localPos1);
if(useD6)
// PT: by default all D6 axes are locked, i.e. it is a fixed joint.
return PxD6JointCreate(physics, actor0, jointFrame0, actor1, jointFrame1);
else
return PxFixedJointCreate(physics, actor0, jointFrame0, actor1, jointFrame1);
}
PxJoint* physx::PxD6JointCreate_Distance(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, float maxDist, bool useD6)
{
const PxTransform localFrame0(localPos0);
const PxTransform localFrame1(localPos1);
if(useD6)
{
PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1);
j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE);
j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eFREE);
j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eFREE);
j->setMotion(PxD6Axis::eX, PxD6Motion::eLIMITED);
j->setMotion(PxD6Axis::eY, PxD6Motion::eLIMITED);
j->setMotion(PxD6Axis::eZ, PxD6Motion::eLIMITED);
j->setDistanceLimit(PxJointLinearLimit(maxDist));
return j;
}
else
{
PxDistanceJoint* j = PxDistanceJointCreate(physics, actor0, localFrame0, actor1, localFrame1);
j->setDistanceJointFlag(PxDistanceJointFlag::eMAX_DISTANCE_ENABLED, true);
j->setMaxDistance(maxDist);
return j;
}
}
PxJoint* physx::PxD6JointCreate_Prismatic(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, float minLimit, float maxLimit, bool useD6)
{
const PxQuat q = PxShortestRotation(gX, axis);
const PxTransform localFrame0(localPos0, q);
const PxTransform localFrame1(localPos1, q);
const PxJointLinearLimitPair limit(PxTolerancesScale(), minLimit, maxLimit);
if(useD6)
{
PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1);
j->setMotion(PxD6Axis::eX, PxD6Motion::eFREE);
if(minLimit==maxLimit)
j->setMotion(PxD6Axis::eX, PxD6Motion::eLOCKED);
else if(minLimit>maxLimit)
j->setMotion(PxD6Axis::eX, PxD6Motion::eFREE);
else// if(minLimit<maxLimit)
{
j->setMotion(PxD6Axis::eX, PxD6Motion::eLIMITED);
j->setLinearLimit(PxD6Axis::eX, limit);
}
return j;
}
else
{
PxPrismaticJoint* j = PxPrismaticJointCreate(physics, actor0, localFrame0, actor1, localFrame1);
if(minLimit<maxLimit)
{
j->setPrismaticJointFlag(PxPrismaticJointFlag::eLIMIT_ENABLED, true);
j->setLimit(limit);
}
return j;
}
}
PxJoint* physx::PxD6JointCreate_Revolute(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, float minLimit, float maxLimit, bool useD6)
{
const PxQuat q = PxShortestRotation(gX, axis);
const PxTransform localFrame0(localPos0, q);
const PxTransform localFrame1(localPos1, q);
const PxJointAngularLimitPair limit(minLimit, maxLimit);
if(useD6)
{
PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1);
if(minLimit==maxLimit)
j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eLOCKED);
else if(minLimit>maxLimit)
j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE);
else// if(minLimit<maxLimit)
{
j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eLIMITED);
j->setTwistLimit(limit);
}
return j;
}
else
{
PxRevoluteJoint* j = PxRevoluteJointCreate(physics, actor0, localFrame0, actor1, localFrame1);
if(minLimit<maxLimit)
{
j->setRevoluteJointFlag(PxRevoluteJointFlag::eLIMIT_ENABLED, true);
j->setLimit(limit);
}
return j;
}
}
PxJoint* physx::PxD6JointCreate_Spherical(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, float limit1, float limit2, bool useD6)
{
const PxQuat q = PxShortestRotation(gX, axis);
const PxTransform localFrame0(localPos0, q);
const PxTransform localFrame1(localPos1, q);
const PxJointLimitCone limit(limit1, limit2);
if(useD6)
{
PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1);
j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE);
if(limit1>0.0f && limit2>0.0f)
{
j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eLIMITED);
j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eLIMITED);
j->setSwingLimit(limit);
}
else
{
j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eFREE);
j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eFREE);
}
return j;
}
else
{
PxSphericalJoint* j = PxSphericalJointCreate(physics, actor0, localFrame0, actor1, localFrame1);
if(limit1>0.0f && limit2>0.0f)
{
j->setSphericalJointFlag(PxSphericalJointFlag::eLIMIT_ENABLED, true);
j->setLimitCone(limit);
}
return j;
}
}
PxJoint* physx::PxD6JointCreate_GenericCone(float& apiroty, float& apirotz, PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, float minLimit1, float maxLimit1, float minLimit2, float maxLimit2, bool useD6)
{
const float DesiredMinSwingY = minLimit1;
const float DesiredMaxSwingY = maxLimit1;
const float DesiredMinSwingZ = minLimit2;
const float DesiredMaxSwingZ = maxLimit2;
const float APIMaxY = (DesiredMaxSwingY - DesiredMinSwingY)*0.5f;
const float APIMaxZ = (DesiredMaxSwingZ - DesiredMinSwingZ)*0.5f;
const float APIRotY = (DesiredMaxSwingY + DesiredMinSwingY)*0.5f;
const float APIRotZ = (DesiredMaxSwingZ + DesiredMinSwingZ)*0.5f;
apiroty = APIRotY;
apirotz = APIRotZ;
const PxQuat RotY = PxGetRotYQuat(APIRotY);
const PxQuat RotZ = PxGetRotZQuat(APIRotZ);
const PxQuat Rot = RotY * RotZ;
const PxTransform localFrame0(localPos0, Rot);
const PxTransform localFrame1(localPos1);
const PxJointLimitCone limit(APIMaxY, APIMaxZ);
if(useD6)
{
PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1);
j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE);
j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eLIMITED);
j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eLIMITED);
j->setSwingLimit(limit);
return j;
}
else
{
PxSphericalJoint* j = PxSphericalJointCreate(physics, actor0, localFrame0, actor1, localFrame1);
j->setSphericalJointFlag(PxSphericalJointFlag::eLIMIT_ENABLED, true);
j->setLimitCone(limit);
return j;
}
}
PxJoint* physx::PxD6JointCreate_Pyramid(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis,
float minLimit1, float maxLimit1, float minLimit2, float maxLimit2)
{
const PxQuat q = PxShortestRotation(gX, axis);
const PxTransform localFrame0(localPos0, q);
const PxTransform localFrame1(localPos1, q);
const PxJointLimitPyramid limit(minLimit1, maxLimit1, minLimit2, maxLimit2);
PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1);
j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE);
if(limit.isValid())
{
j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eLIMITED);
j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eLIMITED);
j->setPyramidSwingLimit(limit);
}
else
{
j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eFREE);
j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eFREE);
}
return j;
}

View File

@@ -0,0 +1,183 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "ExtDefaultCpuDispatcher.h"
#include "ExtCpuWorkerThread.h"
#include "ExtTaskQueueHelper.h"
#include "foundation/PxString.h"
using namespace physx;
PxDefaultCpuDispatcher* physx::PxDefaultCpuDispatcherCreate(PxU32 numThreads, PxU32* affinityMasks, PxDefaultCpuDispatcherWaitForWorkMode::Enum mode, PxU32 yieldProcessorCount)
{
return PX_NEW(Ext::DefaultCpuDispatcher)(numThreads, affinityMasks, mode, yieldProcessorCount);
}
#if !PX_SWITCH
void Ext::DefaultCpuDispatcher::getAffinityMasks(PxU32* affinityMasks, PxU32 threadCount)
{
for(PxU32 i=0; i < threadCount; i++)
{
affinityMasks[i] = 0;
}
}
#endif
Ext::DefaultCpuDispatcher::DefaultCpuDispatcher(PxU32 numThreads, PxU32* affinityMasks, PxDefaultCpuDispatcherWaitForWorkMode::Enum mode, PxU32 yieldProcessorCount) : mNumThreads(numThreads), mShuttingDown(false)
#if PX_PROFILE
,mRunProfiled(true)
#else
,mRunProfiled(false)
#endif
, mWaitForWorkMode(mode)
, mYieldProcessorCount(yieldProcessorCount)
{
PX_CHECK_MSG((((PxDefaultCpuDispatcherWaitForWorkMode::eYIELD_PROCESSOR == mWaitForWorkMode) && (mYieldProcessorCount > 0)) ||
(((PxDefaultCpuDispatcherWaitForWorkMode::eYIELD_THREAD == mWaitForWorkMode) || (PxDefaultCpuDispatcherWaitForWorkMode::eWAIT_FOR_WORK == mWaitForWorkMode)) && (0 == mYieldProcessorCount))), "Illegal yield processor count for chosen execute mode");
PxU32* defaultAffinityMasks = NULL;
if(!affinityMasks)
{
defaultAffinityMasks = PX_ALLOCATE(PxU32, numThreads, "ThreadAffinityMasks");
getAffinityMasks(defaultAffinityMasks, numThreads);
affinityMasks = defaultAffinityMasks;
}
// initialize threads first, then start
mWorkerThreads = PX_ALLOCATE(CpuWorkerThread, numThreads, "CpuWorkerThread");
const PxU32 nameLength = 32;
mThreadNames = PX_ALLOCATE(PxU8, nameLength * numThreads, "CpuWorkerThreadName");
if (mWorkerThreads)
{
for(PxU32 i = 0; i < numThreads; ++i)
{
PX_PLACEMENT_NEW(mWorkerThreads+i, CpuWorkerThread)();
mWorkerThreads[i].initialize(this);
}
for(PxU32 i = 0; i < numThreads; ++i)
{
if (mThreadNames)
{
char* threadName = reinterpret_cast<char*>(mThreadNames + (i*nameLength));
Pxsnprintf(threadName, nameLength, "PxWorker%02d", i);
mWorkerThreads[i].setName(threadName);
}
mWorkerThreads[i].setAffinityMask(affinityMasks[i]);
mWorkerThreads[i].start(PxThread::getDefaultStackSize());
}
PX_FREE(defaultAffinityMasks);
}
else
{
mNumThreads = 0;
}
}
Ext::DefaultCpuDispatcher::~DefaultCpuDispatcher()
{
for(PxU32 i = 0; i < mNumThreads; ++i)
mWorkerThreads[i].signalQuit();
mShuttingDown = true;
if(PxDefaultCpuDispatcherWaitForWorkMode::eWAIT_FOR_WORK == mWaitForWorkMode)
mWorkReady.set();
for(PxU32 i = 0; i < mNumThreads; ++i)
mWorkerThreads[i].waitForQuit();
for(PxU32 i = 0; i < mNumThreads; ++i)
mWorkerThreads[i].~CpuWorkerThread();
PX_FREE(mWorkerThreads);
PX_FREE(mThreadNames);
}
void Ext::DefaultCpuDispatcher::release()
{
PX_DELETE_THIS;
}
void Ext::DefaultCpuDispatcher::submitTask(PxBaseTask& task)
{
if(!mNumThreads)
{
// no worker threads, run directly
runTask(task);
task.release();
return;
}
// TODO: Could use TLS to make this more efficient
const PxThread::Id currentThread = PxThread::getId();
const PxU32 nbThreads = mNumThreads;
for(PxU32 i=0; i<nbThreads; ++i)
{
if(mWorkerThreads[i].tryAcceptJobToLocalQueue(task, currentThread))
{
if(PxDefaultCpuDispatcherWaitForWorkMode::eWAIT_FOR_WORK == mWaitForWorkMode)
mWorkReady.set();
else
PX_ASSERT(PxDefaultCpuDispatcherWaitForWorkMode::eYIELD_PROCESSOR == mWaitForWorkMode || PxDefaultCpuDispatcherWaitForWorkMode::eYIELD_THREAD == mWaitForWorkMode);
return;
}
}
if(mHelper.tryAcceptJobToQueue(task))
{
if(PxDefaultCpuDispatcherWaitForWorkMode::eWAIT_FOR_WORK == mWaitForWorkMode)
mWorkReady.set();
}
}
void Ext::DefaultCpuDispatcher::resetWakeSignal()
{
PX_ASSERT(PxDefaultCpuDispatcherWaitForWorkMode::eWAIT_FOR_WORK == mWaitForWorkMode);
mWorkReady.reset();
// The code below is necessary to avoid deadlocks on shut down.
// A thread usually loops as follows:
// while quit is not signaled
// 1) reset wake signal
// 2) fetch work
// 3) if work -> process
// 4) else -> wait for wake signal
//
// If a thread reaches 1) after the thread pool signaled wake up,
// the wake up sync gets reset and all other threads which have not
// passed 4) already will wait forever.
// The code below makes sure that on shutdown, the wake up signal gets
// sent again after it was reset
//
if (mShuttingDown)
mWorkReady.set();
}

View File

@@ -0,0 +1,127 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_DEFAULT_CPU_DISPATCHER_H
#define EXT_DEFAULT_CPU_DISPATCHER_H
#include "common/PxProfileZone.h"
#include "task/PxTask.h"
#include "extensions/PxDefaultCpuDispatcher.h"
#include "foundation/PxUserAllocated.h"
#include "foundation/PxSync.h"
#include "ExtSharedQueueEntryPool.h"
#include "ExtTaskQueueHelper.h"
#include "ExtCpuWorkerThread.h"
namespace physx
{
namespace Ext
{
#if PX_VC
#pragma warning(push)
#pragma warning(disable:4324) // Padding was added at the end of a structure because of a __declspec(align) value.
#endif // Because of the SList member I assume
class DefaultCpuDispatcher : public PxDefaultCpuDispatcher, public PxUserAllocated
{
PX_NOCOPY(DefaultCpuDispatcher)
private:
~DefaultCpuDispatcher();
public:
DefaultCpuDispatcher(PxU32 numThreads, PxU32* affinityMasks, PxDefaultCpuDispatcherWaitForWorkMode::Enum mode = PxDefaultCpuDispatcherWaitForWorkMode::eWAIT_FOR_WORK, PxU32 yieldProcessorCount = 0);
// PxCpuDispatcher
virtual void submitTask(PxBaseTask& task) PX_OVERRIDE;
virtual PxU32 getWorkerCount() const PX_OVERRIDE { return mNumThreads; }
//~PxCpuDispatcher
// PxDefaultCpuDispatcher
virtual void release() PX_OVERRIDE;
virtual void setRunProfiled(bool runProfiled) PX_OVERRIDE { mRunProfiled = runProfiled; }
virtual bool getRunProfiled() const PX_OVERRIDE { return mRunProfiled; }
//~PxDefaultCpuDispatcher
template<const bool highPriorityT>
PxBaseTask* fetchNextTask()
{
// PT: get job from local list
PxBaseTask* task = mHelper.fetchTask<highPriorityT>();
if(!task)
{
// PT: steal job from other threads
const PxU32 nbThreads = mNumThreads;
for(PxU32 i=0; i<nbThreads; ++i)
{
task = mWorkerThreads[i].getJob<highPriorityT>();
if(task)
return task;
}
}
return task;
}
PX_FORCE_INLINE void runTask(PxBaseTask& task)
{
if(mRunProfiled)
{
PX_PROFILE_ZONE(task.getName(), task.getContextId());
task.run();
}
else
task.run();
}
void waitForWork() { PX_ASSERT(PxDefaultCpuDispatcherWaitForWorkMode::eWAIT_FOR_WORK == mWaitForWorkMode); mWorkReady.wait(); }
void resetWakeSignal();
static void getAffinityMasks(PxU32* affinityMasks, PxU32 threadCount);
PX_FORCE_INLINE PxDefaultCpuDispatcherWaitForWorkMode::Enum getWaitForWorkMode() const { return mWaitForWorkMode; }
PX_FORCE_INLINE PxU32 getYieldProcessorCount() const { return mYieldProcessorCount; }
protected:
CpuWorkerThread* mWorkerThreads;
TaskQueueHelper mHelper;
PxSync mWorkReady;
PxU8* mThreadNames;
PxU32 mNumThreads;
bool mShuttingDown;
bool mRunProfiled;
const PxDefaultCpuDispatcherWaitForWorkMode::Enum mWaitForWorkMode;
const PxU32 mYieldProcessorCount;
};
#if PX_VC
#pragma warning(pop)
#endif
} // namespace Ext
}
#endif

View File

@@ -0,0 +1,98 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "foundation/PxAssert.h"
#include "foundation/PxString.h"
#include "foundation/PxThread.h"
#include "extensions/PxDefaultErrorCallback.h"
#include <stdio.h>
using namespace physx;
PxDefaultErrorCallback::PxDefaultErrorCallback()
{
}
PxDefaultErrorCallback::~PxDefaultErrorCallback()
{
}
void PxDefaultErrorCallback::reportError(PxErrorCode::Enum e, const char* message, const char* file, int line)
{
const char* errorCode = NULL;
switch (e)
{
case PxErrorCode::eNO_ERROR:
errorCode = "no error";
break;
case PxErrorCode::eINVALID_PARAMETER:
errorCode = "invalid parameter";
break;
case PxErrorCode::eINVALID_OPERATION:
errorCode = "invalid operation";
break;
case PxErrorCode::eOUT_OF_MEMORY:
errorCode = "out of memory";
break;
case PxErrorCode::eDEBUG_INFO:
errorCode = "info";
break;
case PxErrorCode::eDEBUG_WARNING:
errorCode = "warning";
break;
case PxErrorCode::ePERF_WARNING:
errorCode = "performance warning";
break;
case PxErrorCode::eABORT:
errorCode = "abort";
break;
case PxErrorCode::eINTERNAL_ERROR:
errorCode = "internal error";
break;
case PxErrorCode::eMASK_ALL:
errorCode = "unknown error";
break;
}
PX_ASSERT(errorCode);
if(errorCode)
{
char buffer[1024];
sprintf(buffer, "%s (%d) : %s : %s\n", file, line, errorCode, message);
PxPrintString(buffer);
PX_ASSERT(e != PxErrorCode::eABORT);
if (e == PxErrorCode::eABORT)
PxThread::sleep(1000); // make sure that the error message is flushed
}
}

View File

@@ -0,0 +1,233 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "foundation/PxAssert.h"
#include "foundation/PxTime.h"
#include "foundation/PxThread.h"
#include "ExtDefaultProfiler.h"
using namespace physx;
const PxU32 gMinBufferSize = 32767;
// Ensure alignment for all structs is as expected.
#define DEFAULT_PROFILER_CHECK_ALIGNMENT_SIZE(type, alignment) PX_COMPILE_TIME_ASSERT((sizeof(type) & (alignment - 1)) == 0);
DEFAULT_PROFILER_CHECK_ALIGNMENT_SIZE(PxDefaultProfilerVersionInfo, DEFAULT_PROFILER_REQUIRED_ALIGNMENT)
DEFAULT_PROFILER_CHECK_ALIGNMENT_SIZE(PxDefaultProfilerHeader, DEFAULT_PROFILER_REQUIRED_ALIGNMENT)
DEFAULT_PROFILER_CHECK_ALIGNMENT_SIZE(PxDefaultProfilerThread, DEFAULT_PROFILER_REQUIRED_ALIGNMENT)
DEFAULT_PROFILER_CHECK_ALIGNMENT_SIZE(PxDefaultProfilerName, DEFAULT_PROFILER_REQUIRED_ALIGNMENT)
DEFAULT_PROFILER_CHECK_ALIGNMENT_SIZE(PxDefaultProfilerEvent, DEFAULT_PROFILER_REQUIRED_ALIGNMENT)
DEFAULT_PROFILER_CHECK_ALIGNMENT_SIZE(PxDefaultProfilerValueEvent, DEFAULT_PROFILER_REQUIRED_ALIGNMENT)
PxDefaultProfiler* physx::PxDefaultProfilerCreate(PxOutputStream& outputStream, PxU32 numberOfBuffers, PxU32 bufferSize)
{
return PX_NEW(Ext::DefaultProfiler)(outputStream, numberOfBuffers, bufferSize);
}
Ext::DefaultProfiler::DefaultProfiler(PxOutputStream& outputStream, PxU32 numberOfBuffers, PxU32 bufferSize) :
mOutputStream(outputStream)
{
mTlsSlotId = PxTlsAlloc();
// Ensure the buffer size is large enough to hold some minimal set of data.
if(bufferSize < gMinBufferSize)
{
PxGetFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, PX_FL,
"Ext::DefaultProfiler::DefaultProfiler: buffer was increased to the minimum buffer size of %d bytes.",
gMinBufferSize);
mBufferSize = gMinBufferSize;
}
else
{
mBufferSize = bufferSize;
}
// Pre-allocate all of the profiler data blocks.
for(PxU32 i = 0; i < numberOfBuffers; i++)
{
DefaultProfilerDataBlock* dataBlock = PX_NEW(DefaultProfilerDataBlock)(bufferSize, outputStream, mMutex);
mEmptyList.push(*dataBlock);
}
// Save version info.
PxDefaultProfilerVersionInfo versionInfo;
versionInfo.major = PX_PROFILER_VERSION_MAJOR;
versionInfo.minor = PX_PROFILER_VERSION_MINOR;
outputStream.write(&versionInfo, sizeof(PxDefaultProfilerVersionInfo));
}
Ext::DefaultProfiler::~DefaultProfiler()
{
flush();
// Delete all of the used and empty blocks.
DefaultProfilerDataBlock* dataBlock = reinterpret_cast<DefaultProfilerDataBlock*>(mEmptyList.pop());
while(dataBlock)
{
PX_DELETE(dataBlock);
dataBlock = reinterpret_cast<DefaultProfilerDataBlock*>(mEmptyList.pop());
}
dataBlock = reinterpret_cast<DefaultProfilerDataBlock*>(mUsedList.pop());
while(dataBlock)
{
PX_DELETE(dataBlock);
dataBlock = reinterpret_cast<DefaultProfilerDataBlock*>(mUsedList.pop());
}
PxTlsFree(mTlsSlotId);
}
void Ext::DefaultProfiler::release()
{
PX_DELETE_THIS;
}
void Ext::DefaultProfiler::flush()
{
// Loop through every used data block and flush them to the stream.
DefaultProfilerDataBlock* usedDataBlock = reinterpret_cast<DefaultProfilerDataBlock*>(mUsedList.flush());
while(usedDataBlock)
{
usedDataBlock->flush();
DefaultProfilerDataBlock* previousDataBlock = usedDataBlock;
usedDataBlock = reinterpret_cast<DefaultProfilerDataBlock*>(usedDataBlock->next());
mUsedList.push(*previousDataBlock);
}
}
Ext::DefaultProfilerDataBlock* Ext::DefaultProfiler::getThreadData()
{
DefaultProfilerDataBlock* dataBlock = reinterpret_cast<DefaultProfilerDataBlock*>(PxTlsGet(mTlsSlotId));
// This thread never had a profiling callback, grab a data block from the pool of pre-allocated blocks.
// If there are none left, allocate a new block.
if(!dataBlock)
{
dataBlock = static_cast<DefaultProfilerDataBlock*>(mEmptyList.pop());
if(!dataBlock)
{
dataBlock = PX_NEW(DefaultProfilerDataBlock)(mBufferSize, mOutputStream, mMutex);
}
// Store the data block in the TLS.
PxTlsSet(mTlsSlotId, dataBlock);
// Write the event block header.
PxU64 threadId = PxThread::getId();
PxDefaultProfilerThread* thread;
dataBlock->write(PxDefaultProfilerDataType::eTHREAD_BLOCK, thread);
thread->threadId = threadId;
// Save the new data block in the user list.
mUsedList.push(*dataBlock);
}
return dataBlock;
}
template <typename TEntry>
TEntry* Ext::DefaultProfiler::writeProfilerEvent(const char* name, PxDefaultProfilerDataType::Enum type, PxU64 contextId)
{
Ext::DefaultProfilerDataBlock* dataBlock = getThreadData();
TEntry* entry;
dataBlock->write(type, entry, name);
PxDefaultProfilerEvent*event = static_cast<PxDefaultProfilerEvent*>(entry);
event->time = PxTime::getCurrentTimeInTensOfNanoSeconds();
event->contextId = contextId;
event->nameKey = PxU64(name);
return entry;
}
void* Ext::DefaultProfiler::zoneStart(const char* eventName, bool detached, uint64_t contextId)
{
PxDefaultProfilerDataType::Enum type;
if(detached)
{
type = PxDefaultProfilerDataType::eZONE_START_CROSS_THREAD;
}
else
{
type = PxDefaultProfilerDataType::eZONE_START;
}
writeProfilerEvent<PxDefaultProfilerEvent>(eventName, type, contextId);
return NULL;
}
void Ext::DefaultProfiler::zoneEnd(void*, const char* eventName, bool detached, uint64_t contextId)
{
PxDefaultProfilerDataType::Enum type;
if(detached)
{
type = PxDefaultProfilerDataType::eZONE_END_CROSS_THREAD;
}
else
{
type = PxDefaultProfilerDataType::eZONE_END;
}
writeProfilerEvent<PxDefaultProfilerEvent>(eventName, type, contextId);
}
void Ext::DefaultProfiler::recordData(int32_t value, const char* valueName, uint64_t contextId)
{
PxDefaultProfilerValueEvent* valueEvent = writeProfilerEvent<PxDefaultProfilerValueEvent>(valueName, PxDefaultProfilerDataType::eVALUE_INT, contextId);
valueEvent->intValue = value;
}
void Ext::DefaultProfiler::recordData(float value, const char* valueName, uint64_t contextId)
{
PxDefaultProfilerValueEvent* valueEvent = writeProfilerEvent<PxDefaultProfilerValueEvent>(valueName, PxDefaultProfilerDataType::eVALUE_FLOAT, contextId);
valueEvent->floatValue = value;
}
void Ext::DefaultProfiler::recordFrame(const char* name, uint64_t contextId)
{
writeProfilerEvent<PxDefaultProfilerEvent>(name, PxDefaultProfilerDataType::eFRAME, contextId);
}

View File

@@ -0,0 +1,220 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_DEFAULT_PROFILER_H
#define EXT_DEFAULT_PROFILER_H
#include "extensions/PxDefaultProfiler.h"
#include "foundation/PxProfiler.h"
#include "foundation/PxHashSet.h"
#include "foundation/PxArray.h"
#include "foundation/PxMutex.h"
#include "foundation/PxSList.h"
#define DEFAULT_PROFILER_CHECK_ALIGNMENT(value, alignment) PX_ASSERT((value & (alignment - 1)) == 0);
#define DEFAULT_PROFILER_REQUIRED_ALIGNMENT 8
namespace physx
{
namespace Ext
{
class DefaultProfilerDataBlock : public PxSListEntry, public PxUserAllocated
{
public:
char* mBuffer;
char* mNextEntry;
char* mEndBuffer;
PxHashSet<PxU64> mNameKeys;
PxOutputStream& mOutputStream;
PxMutex& mMutex;
DefaultProfilerDataBlock(const PxU32 bufferSize, PxOutputStream& outputStream, PxMutex& mutex) :
mOutputStream(outputStream),
mMutex(mutex)
{
mBuffer = static_cast<char*>(PxAlignedAllocator<DEFAULT_PROFILER_REQUIRED_ALIGNMENT>().allocate(bufferSize, PX_FL));
mNextEntry = mBuffer;
mEndBuffer = mBuffer + bufferSize;
}
~DefaultProfilerDataBlock()
{
PxAlignedAllocator<DEFAULT_PROFILER_REQUIRED_ALIGNMENT>().deallocate(mBuffer);
}
PxU32 size() const
{
return (PxU32)(mNextEntry - mBuffer);
}
bool isFull(const PxU32 size) const
{
return (mNextEntry + size >= mEndBuffer);
}
void seek(const PxU32 offset)
{
mNextEntry = mBuffer + offset;
}
void flush()
{
// The thread ID is stored at the start of every data block and must be preserved.
static const PxU32 sThreadOffset = sizeof(PxDefaultProfilerHeader) + sizeof(PxDefaultProfilerThread);
// The thread must be locked to prevent multiple writes to the stream at the same time.
PxMutex::ScopedLock scopedLock(mMutex);
// Keep the header that lists the thread Id for this data block.
mOutputStream.write(mBuffer, size());
seek(sThreadOffset);
}
template <typename TEntry>
void write(PxDefaultProfilerDataType::Enum type, TEntry*& entry, const char* name = nullptr)
{
static const PxU32 sNameSize = sizeof(PxDefaultProfilerHeader) + sizeof(PxDefaultProfilerName);
PxU64 key = (PxU64)name;
PxU32 nameSize;
PxU32 nameStringSize;
PxU32 paddedNameStringSize;
if(name && mNameKeys.contains(key) == false)
{
nameSize = sNameSize;
nameStringSize = (PxU32)strlen(name) + 1;
// Pad the string buffer.
paddedNameStringSize = (nameStringSize + (DEFAULT_PROFILER_REQUIRED_ALIGNMENT - 1)) & ~(DEFAULT_PROFILER_REQUIRED_ALIGNMENT - 1);
}
else
{
nameSize = 0;
nameStringSize = 0;
paddedNameStringSize = 0;
}
// Check if there is enough space in the buffer.
const PxU32 requiredSize = sizeof(PxDefaultProfilerHeader) + sizeof(TEntry) + nameSize + paddedNameStringSize;
if(isFull(requiredSize))
{
flush();
}
PxDefaultProfilerHeader* header = reinterpret_cast<PxDefaultProfilerHeader*>(mNextEntry);
DEFAULT_PROFILER_CHECK_ALIGNMENT((PxU64)header, DEFAULT_PROFILER_REQUIRED_ALIGNMENT)
mNextEntry += sizeof(PxDefaultProfilerHeader);
header->type = type;
// Point the entry to the correct point in the buffer. Write to the buffer outside of this funciton.
entry = reinterpret_cast<TEntry*>(mNextEntry);
DEFAULT_PROFILER_CHECK_ALIGNMENT((PxU64)entry, DEFAULT_PROFILER_REQUIRED_ALIGNMENT)
mNextEntry += sizeof(TEntry);
// Write the name if necessary.
if(nameSize > 0)
{
header = reinterpret_cast<PxDefaultProfilerHeader*>(mNextEntry);
DEFAULT_PROFILER_CHECK_ALIGNMENT((PxU64)header, DEFAULT_PROFILER_REQUIRED_ALIGNMENT)
mNextEntry += sizeof(PxDefaultProfilerHeader);
header->type = PxDefaultProfilerDataType::eNAME_REGISTRATION;
PxDefaultProfilerName* profilerName = reinterpret_cast<PxDefaultProfilerName*>(mNextEntry);
DEFAULT_PROFILER_CHECK_ALIGNMENT((PxU64)profilerName, DEFAULT_PROFILER_REQUIRED_ALIGNMENT)
mNextEntry += sizeof(PxDefaultProfilerName);
profilerName->key = key;
profilerName->size = paddedNameStringSize;
DEFAULT_PROFILER_CHECK_ALIGNMENT((PxU64)mNextEntry, DEFAULT_PROFILER_REQUIRED_ALIGNMENT)
PxMemCopy(mNextEntry, name, nameStringSize);
mNextEntry += paddedNameStringSize;
mNameKeys.insert(key);
}
}
};
class DefaultProfiler : public PxDefaultProfiler, public PxUserAllocated
{
PX_NOCOPY(DefaultProfiler)
private:
~DefaultProfiler();
public:
DefaultProfiler(PxOutputStream& outputStream, PxU32 numberOfBuffers, PxU32 bufferSize);
virtual void release() PX_OVERRIDE;
virtual void flush() PX_OVERRIDE;
virtual void* zoneStart(const char* eventName, bool detached, uint64_t contextId) PX_OVERRIDE;
virtual void zoneEnd(void* profilerData, const char* eventName, bool detached, uint64_t contextId) PX_OVERRIDE;
virtual void recordData(int32_t value, const char* valueName, uint64_t contextId) PX_OVERRIDE;
virtual void recordData(float value, const char* valueName, uint64_t contextId) PX_OVERRIDE;
virtual void recordFrame(const char* name, uint64_t contextId) PX_OVERRIDE;
protected:
// Return a pointer to the data block.
DefaultProfilerDataBlock* getThreadData();
template <typename TEntry>
TEntry* writeProfilerEvent(const char* name, PxDefaultProfilerDataType::Enum type, PxU64 contextId);
PxOutputStream& mOutputStream;
// Container for the pre-allocated event data blocks.
PxSList mEmptyList;
// Container for the used event entry data.
PxSList mUsedList;
// TLS Implementation.
PxU32 mTlsSlotId;
PxMutex mMutex;
PxU32 mBufferSize;
};
} // namespace Ext
}
#endif

View File

@@ -0,0 +1,360 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxDefaultSimulationFilterShader.h"
#include "PxRigidActor.h"
#include "PxShape.h"
#include "PxDeformableSurface.h"
#include "PxDeformableVolume.h"
#include "foundation/PxIntrinsics.h"
#include "foundation/PxAllocator.h"
#include "foundation/PxInlineArray.h"
using namespace physx;
namespace
{
#define GROUP_SIZE 32
struct PxCollisionBitMap
{
PX_INLINE PxCollisionBitMap() : enable(true) {}
bool operator()() const { return enable; }
bool& operator= (const bool &v) { enable = v; return enable; }
private:
bool enable;
};
PxCollisionBitMap gCollisionTable[GROUP_SIZE][GROUP_SIZE];
PxFilterOp::Enum gFilterOps[3] = { PxFilterOp::PX_FILTEROP_AND, PxFilterOp::PX_FILTEROP_AND, PxFilterOp::PX_FILTEROP_AND };
PxGroupsMask gFilterConstants[2];
bool gFilterBool = false;
static void gAND(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1)
{
results.bits0 = PxU16(mask0.bits0 & mask1.bits0);
results.bits1 = PxU16(mask0.bits1 & mask1.bits1);
results.bits2 = PxU16(mask0.bits2 & mask1.bits2);
results.bits3 = PxU16(mask0.bits3 & mask1.bits3);
}
static void gOR(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1)
{
results.bits0 = PxU16(mask0.bits0 | mask1.bits0);
results.bits1 = PxU16(mask0.bits1 | mask1.bits1);
results.bits2 = PxU16(mask0.bits2 | mask1.bits2);
results.bits3 = PxU16(mask0.bits3 | mask1.bits3);
}
static void gXOR(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1)
{
results.bits0 = PxU16(mask0.bits0 ^ mask1.bits0);
results.bits1 = PxU16(mask0.bits1 ^ mask1.bits1);
results.bits2 = PxU16(mask0.bits2 ^ mask1.bits2);
results.bits3 = PxU16(mask0.bits3 ^ mask1.bits3);
}
static void gNAND(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1)
{
results.bits0 = PxU16(~(mask0.bits0 & mask1.bits0));
results.bits1 = PxU16(~(mask0.bits1 & mask1.bits1));
results.bits2 = PxU16(~(mask0.bits2 & mask1.bits2));
results.bits3 = PxU16(~(mask0.bits3 & mask1.bits3));
}
static void gNOR(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1)
{
results.bits0 = PxU16(~(mask0.bits0 | mask1.bits0));
results.bits1 = PxU16(~(mask0.bits1 | mask1.bits1));
results.bits2 = PxU16(~(mask0.bits2 | mask1.bits2));
results.bits3 = PxU16(~(mask0.bits3 | mask1.bits3));
}
static void gNXOR(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1)
{
results.bits0 = PxU16(~(mask0.bits0 ^ mask1.bits0));
results.bits1 = PxU16(~(mask0.bits1 ^ mask1.bits1));
results.bits2 = PxU16(~(mask0.bits2 ^ mask1.bits2));
results.bits3 = PxU16(~(mask0.bits3 ^ mask1.bits3));
}
static void gSWAP_AND(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1)
{
results.bits0 = PxU16(mask0.bits0 & mask1.bits2);
results.bits1 = PxU16(mask0.bits1 & mask1.bits3);
results.bits2 = PxU16(mask0.bits2 & mask1.bits0);
results.bits3 = PxU16(mask0.bits3 & mask1.bits1);
}
typedef void (*FilterFunction) (PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1);
FilterFunction const gTable[] = { gAND, gOR, gXOR, gNAND, gNOR, gNXOR, gSWAP_AND };
static PxFilterData convert(const PxGroupsMask& mask)
{
PxFilterData fd;
fd.word2 = PxU32(mask.bits0 | (mask.bits1 << 16));
fd.word3 = PxU32(mask.bits2 | (mask.bits3 << 16));
return fd;
}
static PxGroupsMask convert(const PxFilterData& fd)
{
PxGroupsMask mask;
mask.bits0 = PxU16((fd.word2 & 0xffff));
mask.bits1 = PxU16((fd.word2 >> 16));
mask.bits2 = PxU16((fd.word3 & 0xffff));
mask.bits3 = PxU16((fd.word3 >> 16));
return mask;
}
static bool getFilterData(const PxActor& actor, PxFilterData& fd)
{
PxActorType::Enum aType = actor.getType();
switch (aType)
{
case PxActorType::eRIGID_DYNAMIC:
case PxActorType::eRIGID_STATIC:
case PxActorType::eARTICULATION_LINK:
{
const PxRigidActor& rActor = static_cast<const PxRigidActor&>(actor);
PX_CHECK_AND_RETURN_VAL(rActor.getNbShapes() >= 1,"There must be a shape in actor", false);
PxShape* shape = NULL;
rActor.getShapes(&shape, 1);
fd = shape->getSimulationFilterData();
}
break;
default:
break;
}
return true;
}
PX_FORCE_INLINE static void adjustFilterData(bool groupsMask, const PxFilterData& src, PxFilterData& dst)
{
if (groupsMask)
{
dst.word2 = src.word2;
dst.word3 = src.word3;
}
else
dst.word0 = src.word0;
}
template<bool TGroupsMask>
static void setFilterData(PxActor& actor, const PxFilterData& fd)
{
PxActorType::Enum aType = actor.getType();
switch (aType)
{
case PxActorType::eRIGID_DYNAMIC:
case PxActorType::eRIGID_STATIC:
case PxActorType::eARTICULATION_LINK:
{
const PxRigidActor& rActor = static_cast<const PxRigidActor&>(actor);
PxShape* shape;
for(PxU32 i=0; i < rActor.getNbShapes(); i++)
{
rActor.getShapes(&shape, 1, i);
// retrieve current group mask
PxFilterData resultFd = shape->getSimulationFilterData();
adjustFilterData(TGroupsMask, fd, resultFd);
// set new filter data
shape->setSimulationFilterData(resultFd);
}
}
break;
case PxActorType::eDEFORMABLE_SURFACE:
{
PxDeformableSurface& sActor = static_cast<PxDeformableSurface&>(actor);
PxShape* shape = sActor.getShape();
// retrieve current group mask
PxFilterData resultFd = shape->getSimulationFilterData();
adjustFilterData(TGroupsMask, fd, resultFd);
// set new filter data
shape->setSimulationFilterData(resultFd);
}
break;
case PxActorType::eDEFORMABLE_VOLUME:
{
PxDeformableVolume& sActor = static_cast<PxDeformableVolume&>(actor);
PxShape* shape = sActor.getShape();
// retrieve current group mask
PxFilterData resultFd = shape->getSimulationFilterData();
adjustFilterData(TGroupsMask, fd, resultFd);
// set new filter data
shape->setSimulationFilterData(resultFd);
}
break;
default:
break;
}
}
}
PxFilterFlags physx::PxDefaultSimulationFilterShader(
PxFilterObjectAttributes attributes0,
PxFilterData filterData0,
PxFilterObjectAttributes attributes1,
PxFilterData filterData1,
PxPairFlags& pairFlags,
const void* constantBlock,
PxU32 constantBlockSize)
{
PX_UNUSED(constantBlock);
PX_UNUSED(constantBlockSize);
// let triggers through
if(PxFilterObjectIsTrigger(attributes0) || PxFilterObjectIsTrigger(attributes1))
{
pairFlags = PxPairFlag::eTRIGGER_DEFAULT;
return PxFilterFlags();
}
// Collision Group
if (!gCollisionTable[filterData0.word0][filterData1.word0]())
{
return PxFilterFlag::eSUPPRESS;
}
// Filter function
PxGroupsMask g0 = convert(filterData0);
PxGroupsMask g1 = convert(filterData1);
PxGroupsMask g0k0; gTable[gFilterOps[0]](g0k0, g0, gFilterConstants[0]);
PxGroupsMask g1k1; gTable[gFilterOps[1]](g1k1, g1, gFilterConstants[1]);
PxGroupsMask final; gTable[gFilterOps[2]](final, g0k0, g1k1);
bool r = final.bits0 || final.bits1 || final.bits2 || final.bits3;
if (r != gFilterBool)
{
return PxFilterFlag::eSUPPRESS;
}
pairFlags = PxPairFlag::eCONTACT_DEFAULT;
return PxFilterFlags();
}
bool physx::PxGetGroupCollisionFlag(const PxU16 group1, const PxU16 group2)
{
PX_CHECK_AND_RETURN_NULL(group1 < 32 && group2 < 32, "Group must be less than 32");
return gCollisionTable[group1][group2]();
}
void physx::PxSetGroupCollisionFlag(const PxU16 group1, const PxU16 group2, const bool enable)
{
PX_CHECK_AND_RETURN(group1 < 32 && group2 < 32, "Group must be less than 32");
gCollisionTable[group1][group2] = enable;
gCollisionTable[group2][group1] = enable;
}
PxU16 physx::PxGetGroup(const PxActor& actor)
{
PxFilterData fd;
getFilterData(actor, fd);
return PxU16(fd.word0);
}
void physx::PxSetGroup(PxActor& actor, const PxU16 collisionGroup)
{
PX_CHECK_AND_RETURN(collisionGroup < 32,"Collision group must be less than 32");
PxFilterData fd(collisionGroup, 0, 0, 0);
setFilterData<false>(actor, fd);
}
void physx::PxGetFilterOps(PxFilterOp::Enum& op0, PxFilterOp::Enum& op1, PxFilterOp::Enum& op2)
{
op0 = gFilterOps[0];
op1 = gFilterOps[1];
op2 = gFilterOps[2];
}
void physx::PxSetFilterOps(const PxFilterOp::Enum& op0, const PxFilterOp::Enum& op1, const PxFilterOp::Enum& op2)
{
gFilterOps[0] = op0;
gFilterOps[1] = op1;
gFilterOps[2] = op2;
}
bool physx::PxGetFilterBool()
{
return gFilterBool;
}
void physx::PxSetFilterBool(const bool enable)
{
gFilterBool = enable;
}
void physx::PxGetFilterConstants(PxGroupsMask& c0, PxGroupsMask& c1)
{
c0 = gFilterConstants[0];
c1 = gFilterConstants[1];
}
void physx::PxSetFilterConstants(const PxGroupsMask& c0, const PxGroupsMask& c1)
{
gFilterConstants[0] = c0;
gFilterConstants[1] = c1;
}
PxGroupsMask physx::PxGetGroupsMask(const PxActor& actor)
{
PxFilterData fd;
if (getFilterData(actor, fd))
return convert(fd);
else
return PxGroupsMask();
}
void physx::PxSetGroupsMask(PxActor& actor, const PxGroupsMask& mask)
{
PxFilterData fd = convert(mask);
setFilterData<true>(actor, fd);
}

View File

@@ -0,0 +1,193 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "foundation/PxPreprocessor.h"
#include "foundation/PxAssert.h"
#include "foundation/PxAllocatorCallback.h"
#include "foundation/PxMemory.h"
#include "foundation/PxBitUtils.h"
#include "extensions/PxDefaultStreams.h"
#include "SnFile.h"
#include "foundation/PxUtilities.h"
#include <errno.h>
using namespace physx;
PxDefaultMemoryOutputStream::PxDefaultMemoryOutputStream(PxAllocatorCallback &allocator)
: mAllocator (allocator)
, mData (NULL)
, mSize (0)
, mCapacity (0)
{
}
PxDefaultMemoryOutputStream::~PxDefaultMemoryOutputStream()
{
if(mData)
mAllocator.deallocate(mData);
}
PxU32 PxDefaultMemoryOutputStream::write(const void* src, PxU32 size)
{
PxU32 expectedSize = mSize + size;
if(expectedSize > mCapacity)
{
mCapacity = PxMax(PxNextPowerOfTwo(expectedSize), 4096u);
PxU8* newData = reinterpret_cast<PxU8*>(mAllocator.allocate(mCapacity,"PxDefaultMemoryOutputStream",__FILE__,__LINE__));
PX_ASSERT(newData!=NULL);
PxMemCopy(newData, mData, mSize);
if(mData)
mAllocator.deallocate(mData);
mData = newData;
}
PxMemCopy(mData+mSize, src, size);
mSize += size;
return size;
}
///////////////////////////////////////////////////////////////////////////////
PxDefaultMemoryInputData::PxDefaultMemoryInputData(PxU8* data, PxU32 length) :
mSize (length),
mData (data),
mPos (0)
{
}
PxU32 PxDefaultMemoryInputData::read(void* dest, PxU32 count)
{
PxU32 length = PxMin<PxU32>(count, mSize-mPos);
PxMemCopy(dest, mData+mPos, length);
mPos += length;
return length;
}
PxU32 PxDefaultMemoryInputData::getLength() const
{
return mSize;
}
void PxDefaultMemoryInputData::seek(PxU32 offset)
{
mPos = PxMin<PxU32>(mSize, offset);
}
PxU32 PxDefaultMemoryInputData::tell() const
{
return mPos;
}
PxDefaultFileOutputStream::PxDefaultFileOutputStream(const char* filename)
{
mFile = NULL;
sn::fopen_s(&mFile, filename, "wb");
// PT: when this fails, check that:
// - the path is correct
// - the file does not already exist. If it does, check that it is not write protected.
if(NULL == mFile)
{
PxGetFoundation().error(PxErrorCode::eINTERNAL_ERROR, PX_FL,
"Unable to open file %s, errno 0x%x\n",filename,errno);
}
PX_ASSERT(mFile);
}
PxDefaultFileOutputStream::~PxDefaultFileOutputStream()
{
if(mFile)
fclose(mFile);
mFile = NULL;
}
PxU32 PxDefaultFileOutputStream::write(const void* src, PxU32 count)
{
return mFile ? PxU32(fwrite(src, 1, count, mFile)) : 0;
}
bool PxDefaultFileOutputStream::isValid()
{
return mFile != NULL;
}
///////////////////////////////////////////////////////////////////////////////
PxDefaultFileInputData::PxDefaultFileInputData(const char* filename)
{
mFile = NULL;
sn::fopen_s(&mFile, filename, "rb");
if(mFile)
{
fseek(mFile, 0, SEEK_END);
mLength = PxU32(ftell(mFile));
fseek(mFile, 0, SEEK_SET);
}
else
{
mLength = 0;
}
}
PxDefaultFileInputData::~PxDefaultFileInputData()
{
if(mFile)
fclose(mFile);
}
PxU32 PxDefaultFileInputData::read(void* dest, PxU32 count)
{
PX_ASSERT(mFile);
const size_t size = fread(dest, 1, count, mFile);
// there should be no assert here since by spec of PxInputStream we can read fewer bytes than expected
return PxU32(size);
}
PxU32 PxDefaultFileInputData::getLength() const
{
return mLength;
}
void PxDefaultFileInputData::seek(PxU32 pos)
{
fseek(mFile, long(pos), SEEK_SET);
}
PxU32 PxDefaultFileInputData::tell() const
{
return PxU32(ftell(mFile));
}
bool PxDefaultFileInputData::isValid() const
{
return mFile != NULL;
}

View File

@@ -0,0 +1,112 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxDeformableSkinningExt.h"
#include "GuAABBTree.h"
#include "foundation/PxMathUtils.h"
#include "foundation/PxFPU.h"
using namespace physx;
void PxDeformableSkinningExt::initializeInterpolatedVertices(
PxTriangleMeshEmbeddingInfo* embeddingInfo,
const PxVec3* guideVertices, const PxVec3* guideNormals,
const PxU32* guideTriangles, PxU32 numGuideTriangles, const PxVec3* embeddedVertices,
PxU32 numEmbeddedVertices)
{
PX_UNUSED(guideNormals); //The guideNormals could be used for higher quality embedding infos
PX_SIMD_GUARD
Gu::TinyBVH bvh;
Gu::TinyBVH::constructFromTriangles(guideTriangles, numGuideTriangles, guideVertices, bvh);
Gu::ClosestDistanceToTrimeshTraversalController traversalController(guideTriangles, guideVertices, bvh.mTree.begin());
//Implements to most basic variant - lots of room for improvements
for (PxU32 i = 0; i < numEmbeddedVertices; ++i)
{
traversalController.setQueryPoint(embeddedVertices[i]);
bvh.Traverse(traversalController);
const PxU32 closestTriangleid = traversalController.getClosestTriId();
const PxU32* tri = &guideTriangles[3 * closestTriangleid];
const PxVec3& v0 = guideVertices[tri[0]];
const PxVec3& v1 = guideVertices[tri[1]];
const PxVec3& v2 = guideVertices[tri[2]];
const PxVec3 closestPoint = traversalController.getClosestPoint();
PxVec3 triNormal = (v1 - v0).cross(v2 - v0);
triNormal.normalize();
PxVec3 distVec = (embeddedVertices[i] - closestPoint);
PxReal normalOffset = distVec.dot(triNormal);
PxVec3 projPoint = embeddedVertices[i] - normalOffset*triNormal;
PxVec4 barycentric;
PxComputeBarycentric(v0, v1, v2, projPoint, barycentric);
embeddingInfo[i] = PxTriangleMeshEmbeddingInfo(PxVec2(barycentric.x, barycentric.y), normalOffset, closestTriangleid);
}
}
void PxDeformableSkinningExt::initializeInterpolatedVertices(
PxTetrahedronMeshEmbeddingInfo* embeddingInfo,
const PxVec3* guideVertices,
const PxU32* guideTetrahedra, PxU32 numGuideTetrahedra, const PxVec3* embeddedVertices,
PxU32 numEmbeddedVertices )
{
PX_SIMD_GUARD
Gu::TinyBVH bvh;
Gu::TinyBVH::constructFromTetrahedra(guideTetrahedra, numGuideTetrahedra, guideVertices, bvh);
Gu::ClosestDistanceToTetmeshTraversalController traversalController(guideTetrahedra, guideVertices, bvh.mTree.begin());
//Implements to most basic variant - lots of room for improvements
for (PxU32 i = 0; i < numEmbeddedVertices; ++i)
{
traversalController.setQueryPoint(embeddedVertices[i]);
bvh.Traverse(traversalController);
const PxU32 closestTetid = traversalController.getClosestTetId();
const PxU32* tet = &guideTetrahedra[4 * closestTetid];
const PxVec3 closestPoint = traversalController.getClosestPoint();
PxVec4 barycentric;
PxComputeBarycentric(guideVertices[tet[0]], guideVertices[tet[1]], guideVertices[tet[2]], guideVertices[tet[3]], closestPoint, barycentric);
embeddingInfo[i] = PxTetrahedronMeshEmbeddingInfo(barycentric.getXYZ(), closestTetid);
}
}

View File

@@ -0,0 +1,272 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxDeformableSurfaceExt.h"
#include "PxPhysics.h"
#include "cooking/PxCooking.h"
#include "cudamanager/PxCudaContext.h"
#include "cudamanager/PxCudaContextManager.h"
#include "extensions/PxCudaHelpersExt.h"
using namespace physx;
void PxDeformableSurfaceExt::copyToDevice(PxDeformableSurface& fm, PxDeformableSurfaceDataFlags flags, PxU32 nbVertices,
PxVec4* positionInvMassPinned, PxVec4* velocityPinned, PxVec4* restPositionPinned,
CUstream stream)
{
#if PX_SUPPORT_GPU_PHYSX
PxScopedCudaLock _lock(*fm.getCudaContextManager());
PxCudaContext* ctx = fm.getCudaContextManager()->getCudaContext();
if(flags & PxDeformableSurfaceDataFlag::ePOSITION_INVMASS && positionInvMassPinned)
ctx->memcpyHtoDAsync(reinterpret_cast<CUdeviceptr>(fm.getPositionInvMassBufferD()), positionInvMassPinned,
nbVertices * sizeof(PxVec4), stream);
if(flags & PxDeformableSurfaceDataFlag::eVELOCITY && velocityPinned)
ctx->memcpyHtoDAsync(reinterpret_cast<CUdeviceptr>(fm.getVelocityBufferD()), velocityPinned,
nbVertices * sizeof(PxVec4), stream);
if(flags & PxDeformableSurfaceDataFlag::eREST_POSITION && restPositionPinned)
ctx->memcpyHtoDAsync(reinterpret_cast<CUdeviceptr>(fm.getRestPositionBufferD()), restPositionPinned,
nbVertices * sizeof(PxVec4), stream);
if(stream == 0)
ctx->streamSynchronize(stream);
#else
PX_UNUSED(nbVertices);
PX_UNUSED(positionInvMassPinned);
PX_UNUSED(velocityPinned);
PX_UNUSED(restPositionPinned);
PX_UNUSED(stream);
#endif
fm.markDirty(flags);
}
PxU32 PxDeformableSurfaceExt::allocateAndInitializeHostMirror(PxDeformableSurface& deformableSurface, const PxVec3* positions,
const PxVec3* velocities, const PxVec3* restPositions,
float clothMass, const PxTransform& transform,
PxCudaContextManager* cudaContextManager,
PxVec4*& positionInvMassPinned, PxVec4*& velocityPinned,
PxVec4*& restPositionPinned)
{
PX_ASSERT(cudaContextManager != NULL);
PxShape* shape = deformableSurface.getShape();
if(shape == NULL)
return 0;
const PxTriangleMeshGeometry& triangleMeshGeometry = static_cast<const PxTriangleMeshGeometry&>(shape->getGeometry());
PxTriangleMesh* triangleMesh = triangleMeshGeometry.triangleMesh;
if(triangleMesh == NULL)
return 0;
const PxU32 nbVertices = triangleMesh->getNbVertices();
const PxVec3* const initPositions = positions ? positions : triangleMesh->getVertices(); // triangle mesh may represent "rest configuration".
allocateAndInitializeHostMirror(initPositions, velocities, restPositions, nbVertices, clothMass, transform,
cudaContextManager, positionInvMassPinned, velocityPinned, restPositionPinned);
return nbVertices;
}
PxU32 PxDeformableSurfaceExt::allocateAndInitializeHostMirror(const PxVec3* positions, const PxVec3* velocities,
const PxVec3* restPositions, PxU32 nbVertices, float clothMass,
const PxTransform& transform,
PxCudaContextManager* cudaContextManager,
PxVec4*& positionInvMassPinned, PxVec4*& velocityPinned,
PxVec4*& restPositionPinned)
{
#if PX_SUPPORT_GPU_PHYSX
positionInvMassPinned = PX_EXT_PINNED_MEMORY_ALLOC(PxVec4, *cudaContextManager, nbVertices);
velocityPinned = PX_EXT_PINNED_MEMORY_ALLOC(PxVec4, *cudaContextManager, nbVertices);
restPositionPinned = PX_EXT_PINNED_MEMORY_ALLOC(PxVec4, *cudaContextManager, nbVertices);
#else
PX_UNUSED(cudaContextManager);
#endif
float invMass = static_cast<float>(nbVertices) / clothMass;
for(PxU32 i = 0; i < nbVertices; ++i)
{
PxVec3 pos = positions[i];
pos = transform.transform(pos);
positionInvMassPinned[i] = PxVec4(pos.x, pos.y, pos.z, invMass);
velocityPinned[i] = velocities ? PxVec4(velocities[i].x, velocities[i].y, velocities[i].z, invMass)
: PxVec4(0.f, 0.f, 0.f, invMass);
restPositionPinned[i] = restPositions
? PxVec4(restPositions[i].x, restPositions[i].y, restPositions[i].z, invMass)
: PxVec4(pos.x, pos.y, pos.z, invMass);
}
return nbVertices;
}
void PxDeformableSurfaceExt::distributeTriangleMassToVertices(PxDeformableSurface& deformableSurface, const PxReal* triangleMasses,
PxVec4* positionInvMassPinned)
{
PxShape* shape = deformableSurface.getShape();
const PxTriangleMeshGeometry& triangleMeshGeometry = static_cast<const PxTriangleMeshGeometry&>(shape->getGeometry());
PxTriangleMesh* triangleMesh = triangleMeshGeometry.triangleMesh;
const PxU32 numVerts = triangleMesh->getNbVertices();
const PxU32 numTriangles = triangleMesh->getNbTriangles();
PxArray<PxReal> vertexMasses(numVerts, 0.f);
const bool has16bitIndices = triangleMesh->getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES;
const void* indices = triangleMesh->getTriangles();
PxReal oneThird = 1.0f / 3.0f;
for(PxU32 i = 0; i < numTriangles; ++i)
{
PxU32 vref0, vref1, vref2;
if (has16bitIndices)
{
vref0 = ((const PxU16*)indices)[i * 3 + 0];
vref1 = ((const PxU16*)indices)[i * 3 + 1];
vref2 = ((const PxU16*)indices)[i * 3 + 2];
}
else
{
vref0 = ((const PxU32*)indices)[i * 3 + 0];
vref1 = ((const PxU32*)indices)[i * 3 + 1];
vref2 = ((const PxU32*)indices)[i * 3 + 2];
}
PxReal vertexMass = oneThird * triangleMasses[i];
vertexMasses[vref0] += vertexMass;
vertexMasses[vref1] += vertexMass;
vertexMasses[vref2] += vertexMass;
}
for(PxU32 i = 0; i < numVerts; ++i)
{
PxReal invMass = 1.f / vertexMasses[i];
positionInvMassPinned[i].w = invMass;
}
}
void PxDeformableSurfaceExt::distributeDensityToVertices(PxDeformableSurface& deformableSurface, PxReal massPerVolume, PxReal clothThickness,
PxVec4* positionInvMassPinned)
{
const PxReal massPerArea = massPerVolume * clothThickness;
PxShape* shape = deformableSurface.getShape();
const PxTriangleMeshGeometry& triangleMeshGeometry = static_cast<const PxTriangleMeshGeometry&>(shape->getGeometry());
PxTriangleMesh* triangleMesh = triangleMeshGeometry.triangleMesh;
const PxU32 numVerts = triangleMesh->getNbVertices();
const PxU32 numTriangles = triangleMesh->getNbTriangles();
PxArray<PxReal> vertexMasses(numVerts, 0.f);
const bool has16bitIndices = triangleMesh->getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES;
const void* indices = triangleMesh->getTriangles();
const PxVec3* vertices = triangleMesh->getVertices();
PxReal oneThird = 1.0f / 3.0f;
for (PxU32 i = 0; i < numTriangles; ++i)
{
PxU32 vref0, vref1, vref2;
if (has16bitIndices)
{
vref0 = ((const PxU16*)indices)[i * 3 + 0];
vref1 = ((const PxU16*)indices)[i * 3 + 1];
vref2 = ((const PxU16*)indices)[i * 3 + 2];
}
else
{
vref0 = ((const PxU32*)indices)[i * 3 + 0];
vref1 = ((const PxU32*)indices)[i * 3 + 1];
vref2 = ((const PxU32*)indices)[i * 3 + 2];
}
const PxVec3 a = vertices[vref0];
const PxVec3 b = vertices[vref1];
const PxVec3 c = vertices[vref2];
PxReal triangleArea = 0.5f * (b - a).cross(c - a).magnitude();
PxReal triangleMass = massPerArea* triangleArea;
PxReal vertexMass = oneThird * triangleMass;
vertexMasses[vref0] += vertexMass;
vertexMasses[vref1] += vertexMass;
vertexMasses[vref2] += vertexMass;
}
for (PxU32 i = 0; i < numVerts; ++i)
{
PxReal invMass = 1.f / vertexMasses[i];
positionInvMassPinned[i].w = invMass;
}
}
void PxDeformableSurfaceExt::distributeMassToVertices(PxDeformableSurface& deformableSurface, PxReal totalMass,
PxVec4* positionInvMassPinned)
{
PxShape* shape = deformableSurface.getShape();
const PxTriangleMeshGeometry& triangleMeshGeometry = static_cast<const PxTriangleMeshGeometry&>(shape->getGeometry());
PxTriangleMesh* triangleMesh = triangleMeshGeometry.triangleMesh;
PxReal totalArea = 0.0f;
const PxU32 numVerts = triangleMesh->getNbVertices();
const PxU32 numTriangles = triangleMesh->getNbTriangles();
PxArray<PxReal> vertexMasses(numVerts, 0.f);
const bool has16bitIndices = triangleMesh->getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES;
const void* indices = triangleMesh->getTriangles();
const PxVec3* vertices = triangleMesh->getVertices();
for (PxU32 i = 0; i < numTriangles; ++i)
{
PxU32 vref0, vref1, vref2;
if (has16bitIndices)
{
vref0 = ((const PxU16*)indices)[i * 3 + 0];
vref1 = ((const PxU16*)indices)[i * 3 + 1];
vref2 = ((const PxU16*)indices)[i * 3 + 2];
}
else
{
vref0 = ((const PxU32*)indices)[i * 3 + 0];
vref1 = ((const PxU32*)indices)[i * 3 + 1];
vref2 = ((const PxU32*)indices)[i * 3 + 2];
}
const PxVec3 a = vertices[vref0];
const PxVec3 b = vertices[vref1];
const PxVec3 c = vertices[vref2];
totalArea += (b - a).cross(c - a).magnitude();
}
totalArea *= 0.5f;
PxReal massPerArea = totalMass / totalArea;
const PxReal clothThickness = 1.0f; //Value does not matter since it cancels out
distributeDensityToVertices(deformableSurface, massPerArea / clothThickness, clothThickness, positionInvMassPinned);
}

View File

@@ -0,0 +1,633 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxDeformableVolumeExt.h"
#include "extensions/PxTetMakerExt.h"
#include "GuTetrahedronMesh.h"
#include "GuTetrahedronMeshUtils.h"
#include "cooking/PxCooking.h"
#include "PxPhysics.h"
#include "extensions/PxRemeshingExt.h"
#include "cudamanager/PxCudaContextManager.h"
#include "cudamanager/PxCudaContext.h"
#include "extensions/PxCudaHelpersExt.h"
using namespace physx;
using namespace Cm;
//Computes the volume of the simulation mesh defined as the sum of the volumes of all tetrahedra
static PxReal computeSimulationMeshVolume(PxDeformableVolume& deformableVolume, PxVec4* simPositions)
{
const PxU32 numTetsGM = deformableVolume.getSimulationMesh()->getNbTetrahedrons();
const PxU32* tetPtr32 = reinterpret_cast<const PxU32*>(deformableVolume.getSimulationMesh()->getTetrahedrons());
const PxU16* tetPtr16 = reinterpret_cast<const PxU16*>(deformableVolume.getSimulationMesh()->getTetrahedrons());
const bool sixteenBit = deformableVolume.getSimulationMesh()->getTetrahedronMeshFlags() & PxTetrahedronMeshFlag::e16_BIT_INDICES;
PxReal volume = 0;
for (PxU32 i = 0; i < numTetsGM; ++i)
{
PxVec4& x0 = simPositions[sixteenBit ? tetPtr16[4 * i] : tetPtr32[4 * i]];
PxVec4& x1 = simPositions[sixteenBit ? tetPtr16[4 * i + 1] : tetPtr32[4 * i + 1]];
PxVec4& x2 = simPositions[sixteenBit ? tetPtr16[4 * i + 2] : tetPtr32[4 * i + 2]];
PxVec4& x3 = simPositions[sixteenBit ? tetPtr16[4 * i + 3] : tetPtr32[4 * i + 3]];
const PxVec3 u1 = x1.getXYZ() - x0.getXYZ();
const PxVec3 u2 = x2.getXYZ() - x0.getXYZ();
const PxVec3 u3 = x3.getXYZ() - x0.getXYZ();
PxMat33 Q = PxMat33(u1, u2, u3);
const PxReal det = Q.getDeterminant();
volume += det;
}
volume /= 6.0f;
return volume;
}
//Recomputes the volume associated with a vertex. Every tetrahedron distributes a quarter of its volume to
//each vertex it is connected to. Finally the volume stored for every vertex is inverted.
static void updateNodeInverseVolumes(PxDeformableVolume& deformableVolume, PxVec4* simPositions)
{
const PxU32 numVertsGM = deformableVolume.getSimulationMesh()->getNbVertices();
const PxU32 numTetsGM = deformableVolume.getSimulationMesh()->getNbTetrahedrons();
const PxU32* tetPtr32 = reinterpret_cast<const PxU32*>(deformableVolume.getSimulationMesh()->getTetrahedrons());
const PxU16* tetPtr16 = reinterpret_cast<const PxU16*>(deformableVolume.getSimulationMesh()->getTetrahedrons());
const bool sixteenBit = deformableVolume.getSimulationMesh()->getTetrahedronMeshFlags() & PxTetrahedronMeshFlag::e16_BIT_INDICES;
for (PxU32 i = 0; i < numVertsGM; ++i)
simPositions[i].w = 0.0f;
for (PxU32 i = 0; i < numTetsGM; ++i)
{
PxVec4& x0 = simPositions[sixteenBit ? tetPtr16[4 * i] : tetPtr32[4 * i]];
PxVec4& x1 = simPositions[sixteenBit ? tetPtr16[4 * i + 1] : tetPtr32[4 * i + 1]];
PxVec4& x2 = simPositions[sixteenBit ? tetPtr16[4 * i + 2] : tetPtr32[4 * i + 2]];
PxVec4& x3 = simPositions[sixteenBit ? tetPtr16[4 * i + 3] : tetPtr32[4 * i + 3]];
const PxVec3 u1 = x1.getXYZ() - x0.getXYZ();
const PxVec3 u2 = x2.getXYZ() - x0.getXYZ();
const PxVec3 u3 = x3.getXYZ() - x0.getXYZ();
PxMat33 Q = PxMat33(u1, u2, u3);
//det should be positive
const PxReal det = Q.getDeterminant();
if (det <= 1.e-9f)
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "updateNodeInverseVolumes(): tetrahedron is degenerate or inverted");
//Distribute one quarter of the volume to each vertex the tetrahedron is connected to
const PxReal volume = det / 6.0f;
x0.w += 0.25f * volume;
x1.w += 0.25f * volume;
x2.w += 0.25f * volume;
x3.w += 0.25f * volume;
}
//Invert the volumes stored per vertex and copy it to the velocity buffer
for (PxU32 i = 0; i < numVertsGM; ++i)
{
if (simPositions[i].w > 0)
{
simPositions[i].w = 1.0f / simPositions[i].w;
}
}
}
void PxDeformableVolumeExt::updateMass(PxDeformableVolume& deformableVolume, const PxReal density, const PxReal maxInvMassRatio, PxVec4* simPositionsPinned)
{
//Inverse volumes are recomputed to ensure that multiple subsequent calls of this method lead to correct results
updateNodeInverseVolumes(deformableVolume, simPositionsPinned);
const PxU32 numVertsGM = deformableVolume.getSimulationMesh()->getNbVertices();
PxReal minInvMass = PX_MAX_F32, maxInvMass = 0.f;
for (PxU32 i = 0; i < numVertsGM; ++i)
{
const PxVec4& vert = simPositionsPinned[i];
PxReal invMass = vert.w;
invMass = invMass / (density);
minInvMass = invMass > 0.f ? PxMin(invMass, minInvMass) : minInvMass;
maxInvMass = PxMax(invMass, maxInvMass);
simPositionsPinned[i] = PxVec4(vert.x, vert.y, vert.z, invMass);
}
if (minInvMass != PX_MAX_F32)
{
const PxReal ratio = maxInvMass / minInvMass;
if (ratio > maxInvMassRatio)
{
//Clamp the upper limit...
maxInvMass = minInvMass * maxInvMassRatio;
for (PxU32 i = 0; i < numVertsGM; ++i)
{
PxVec4& posInvMass = simPositionsPinned[i];
posInvMass.w = PxMin(posInvMass.w, maxInvMass);
}
}
}
}
void PxDeformableVolumeExt::setMass(PxDeformableVolume& deformableVolume, const PxReal mass, const PxReal maxInvMassRatio, PxVec4* simPositionsPinned)
{
//Compute the density such that density times volume is equal to the desired mass
updateMass(deformableVolume, mass / computeSimulationMeshVolume(deformableVolume, simPositionsPinned), maxInvMassRatio, simPositionsPinned);
}
void PxDeformableVolumeExt::transform(PxDeformableVolume& deformableVolume, const PxTransform& transform, const PxReal scale, PxVec4* simPositionsPinned, PxVec4* simVelocitiesPinned, PxVec4* collPositionsPinned, PxVec4* restPositionsPinned)
{
const PxU32 numVertsGM = deformableVolume.getSimulationMesh()->getNbVertices();
const PxU32 numVerts = deformableVolume.getCollisionMesh()->getNbVertices();
for (PxU32 i = 0; i < numVertsGM; ++i)
{
const PxVec4 tpInvMass = simPositionsPinned[i];
const PxReal invMass = tpInvMass.w;
PxVec3 vert = PxVec3(tpInvMass.x * scale, tpInvMass.y * scale, tpInvMass.z * scale);
//Transform the vertex position and keep the inverse mass
vert = transform.transform(vert);
simPositionsPinned[i] = PxVec4(vert.x, vert.y, vert.z, invMass);
PxVec4 vel = simVelocitiesPinned[i];
PxVec3 v = PxVec3(vel.x * scale, vel.y * scale, vel.z * scale);
//Velocities are translation invariant, therefore only the direction needs to get adjusted.
//The inverse mass is stored as well to optimize memory access on the GPU
v = transform.rotate(v);
simVelocitiesPinned[i] = PxVec4(v.x, v.y, v.z, invMass);
}
for (PxU32 i = 0; i < numVerts; ++i)
{
restPositionsPinned[i] = PxVec4(restPositionsPinned[i].x*scale, restPositionsPinned[i].y*scale, restPositionsPinned[i].z*scale, 1.f);
const PxVec4 tpInvMass = collPositionsPinned[i];
PxVec3 vert = PxVec3(tpInvMass.x * scale, tpInvMass.y * scale, tpInvMass.z * scale);
vert = transform.transform(vert);
collPositionsPinned[i] = PxVec4(vert.x, vert.y, vert.z, tpInvMass.w);
}
PxMat33* tetraRestPosesGM = static_cast<Gu::DeformableVolumeAuxData*>(deformableVolume.getDeformableVolumeAuxData())->getGridModelRestPosesFast(); // reinterpret_cast<PxMat33*>(simMeshData->deformableVolumeAuxData.getGridModelRestPosesFast());
const PxU32 nbTetraGM = deformableVolume.getSimulationMesh()->getNbTetrahedrons();
const PxReal invScale = 1.0f / scale;
for (PxU32 i = 0; i < nbTetraGM; ++i)
{
PxMat33& m = tetraRestPosesGM[i];
//Scale the rest pose
m.column0 = m.column0 * invScale;
m.column1 = m.column1 * invScale;
m.column2 = m.column2 * invScale;
//The rest pose is translation invariant, it only needs be rotated
PxVec3 row0 = transform.rotateInv(PxVec3(m.column0.x, m.column1.x, m.column2.x));
PxVec3 row1 = transform.rotateInv(PxVec3(m.column0.y, m.column1.y, m.column2.y));
PxVec3 row2 = transform.rotateInv(PxVec3(m.column0.z, m.column1.z, m.column2.z));
m.column0 = PxVec3(row0.x, row1.x, row2.x);
m.column1 = PxVec3(row0.y, row1.y, row2.y);
m.column2 = PxVec3(row0.z, row1.z, row2.z);
}
PxMat33* tetraRestPoses = static_cast<Gu::DeformableVolumeAuxData*>(deformableVolume.getDeformableVolumeAuxData())->getRestPosesFast(); // reinterpret_cast<PxMat33*>(simMeshData->deformableVolumeAuxData.getGridModelRestPosesFast());
const PxU32 nbTetra = deformableVolume.getCollisionMesh()->getNbTetrahedrons();
for (PxU32 i = 0; i < nbTetra; ++i)
{
PxMat33& m = tetraRestPoses[i];
//Scale the rest pose
m.column0 = m.column0 * invScale;
m.column1 = m.column1 * invScale;
m.column2 = m.column2 * invScale;
//The rest pose is translation invariant, it only needs be rotated
PxVec3 row0 = transform.rotateInv(PxVec3(m.column0.x, m.column1.x, m.column2.x));
PxVec3 row1 = transform.rotateInv(PxVec3(m.column0.y, m.column1.y, m.column2.y));
PxVec3 row2 = transform.rotateInv(PxVec3(m.column0.z, m.column1.z, m.column2.z));
m.column0 = PxVec3(row0.x, row1.x, row2.x);
m.column1 = PxVec3(row0.y, row1.y, row2.y);
m.column2 = PxVec3(row0.z, row1.z, row2.z);
}
}
void PxDeformableVolumeExt::updateEmbeddedCollisionMesh(PxDeformableVolume& deformableVolume, PxVec4* simPositionsPinned, PxVec4* collPositionsPinned)
{
Gu::DeformableVolumeAuxData* deformableVolumeAuxData = static_cast<Gu::DeformableVolumeAuxData*>(deformableVolume.getDeformableVolumeAuxData());
const PxU32* remapTable = deformableVolumeAuxData->mVertsRemapInGridModel;
PxReal* barycentricCoordinates = deformableVolumeAuxData->mVertsBarycentricInGridModel;
PxTetrahedronMesh* simMesh = deformableVolume.getSimulationMesh();
const void* tets = simMesh->getTetrahedrons();
const PxU32* tets32 = static_cast<const PxU32*>(tets);
const PxU16* tets16 = static_cast<const PxU16*>(tets);
bool sixteenBit = simMesh->getTetrahedronMeshFlags() & PxTetrahedronMeshFlag::e16_BIT_INDICES;
const PxU32 numVerts = deformableVolume.getCollisionMesh()->getNbVertices();
for (PxU32 i = 0; i < numVerts; ++i)
{
//The tetrahedra are ordered differently on the GPU, therefore the index must be taken from the remap table
const PxU32 tetrahedronIdx = remapTable[i];
const PxVec4 p0 = simPositionsPinned[sixteenBit ? tets16[4 * tetrahedronIdx] : tets32[4 * tetrahedronIdx]];
const PxVec4 p1 = simPositionsPinned[sixteenBit ? tets16[4 * tetrahedronIdx + 1] : tets32[4 * tetrahedronIdx + 1]];
const PxVec4 p2 = simPositionsPinned[sixteenBit ? tets16[4 * tetrahedronIdx + 2] : tets32[4 * tetrahedronIdx + 2]];
const PxVec4 p3 = simPositionsPinned[sixteenBit ? tets16[4 * tetrahedronIdx + 3] : tets32[4 * tetrahedronIdx + 3]];
const PxReal* barycentric = &barycentricCoordinates[4*i];
//Compute the embedded position as a weigted sum of vertices from the simulation mesh
//This ensures that all tranformations and scale changes applied to the simulation mesh get transferred
//to the collision mesh
collPositionsPinned[i] = p0 * barycentric[0] + p1 * barycentric[1] + p2 * barycentric[2] + p3 * barycentric[3];
collPositionsPinned[i].w = 1.0f;
}
}
void PxDeformableVolumeExt::copyToDevice(PxDeformableVolume& deformableVolume, PxDeformableVolumeDataFlags flags, PxVec4* simPositionsPinned, PxVec4* simVelocitiesPinned, PxVec4* collPositionsPinned, PxVec4* restPositionsPinned, CUstream stream)
{
//Updating the collision mesh's vertices ensures that simulation mesh and collision mesh are
//represented in the same coordinate system and the same scale
updateEmbeddedCollisionMesh(deformableVolume, simPositionsPinned, collPositionsPinned);
#if PX_SUPPORT_GPU_PHYSX
PxScopedCudaLock _lock(*deformableVolume.getCudaContextManager());
PxCudaContext* ctx = deformableVolume.getCudaContextManager()->getCudaContext();
if (flags & PxDeformableVolumeDataFlag::ePOSITION_INVMASS && collPositionsPinned)
ctx->memcpyHtoDAsync(reinterpret_cast<CUdeviceptr>(deformableVolume.getPositionInvMassBufferD()), collPositionsPinned, deformableVolume.getCollisionMesh()->getNbVertices() * sizeof(PxVec4), stream);
if (flags & PxDeformableVolumeDataFlag::eREST_POSITION_INVMASS && restPositionsPinned)
ctx->memcpyHtoDAsync(reinterpret_cast<CUdeviceptr>(deformableVolume.getRestPositionBufferD()), restPositionsPinned, deformableVolume.getCollisionMesh()->getNbVertices() * sizeof(PxVec4), stream);
if (flags & PxDeformableVolumeDataFlag::eSIM_POSITION_INVMASS && simPositionsPinned)
ctx->memcpyHtoDAsync(reinterpret_cast<CUdeviceptr>(deformableVolume.getSimPositionInvMassBufferD()), simPositionsPinned, deformableVolume.getSimulationMesh()->getNbVertices() * sizeof(PxVec4), stream);
if (flags & PxDeformableVolumeDataFlag::eSIM_VELOCITY && simVelocitiesPinned)
ctx->memcpyHtoDAsync(reinterpret_cast<CUdeviceptr>(deformableVolume.getSimVelocityBufferD()), simVelocitiesPinned, deformableVolume.getSimulationMesh()->getNbVertices() * sizeof(PxVec4), stream);
// we need to synchronize if the stream is the default argument.
if (stream == 0)
{
ctx->streamSynchronize(stream);
}
#else
PX_UNUSED(restPositionsPinned);
PX_UNUSED(simVelocitiesPinned);
PX_UNUSED(stream);
#endif
deformableVolume.markDirty(flags);
}
PxDeformableVolumeMesh* PxDeformableVolumeExt::createDeformableVolumeMesh(const PxCookingParams& params, const PxSimpleTriangleMesh& surfaceMesh, PxU32 numVoxelsAlongLongestAABBAxis, PxInsertionCallback& insertionCallback, const bool validate)
{
//Compute collision mesh
physx::PxArray<physx::PxVec3> collisionMeshVertices;
physx::PxArray<physx::PxU32> collisionMeshIndices;
if (!PxTetMaker::createConformingTetrahedronMesh(surfaceMesh, collisionMeshVertices, collisionMeshIndices, validate))
return NULL;
PxTetrahedronMeshDesc meshDesc(collisionMeshVertices, collisionMeshIndices);
//Compute simulation mesh
physx::PxArray<physx::PxI32> vertexToTet;
vertexToTet.resize(meshDesc.points.count);
physx::PxArray<physx::PxVec3> simulationMeshVertices;
physx::PxArray<physx::PxU32> simulationMeshIndices;
PxTetMaker::createVoxelTetrahedronMesh(meshDesc, numVoxelsAlongLongestAABBAxis, simulationMeshVertices, simulationMeshIndices, vertexToTet.begin());
PxTetrahedronMeshDesc simMeshDesc(simulationMeshVertices, simulationMeshIndices, PxTetrahedronMeshDesc::PxMeshFormat::eHEX_MESH);
PxDeformableVolumeSimulationDataDesc simDesc(vertexToTet);
physx::PxDeformableVolumeMesh* deformableVolumeMesh = PxCreateDeformableVolumeMesh(params, simMeshDesc, meshDesc, simDesc, insertionCallback);
return deformableVolumeMesh;
}
PxDeformableVolumeMesh* PxDeformableVolumeExt::createDeformableVolumeMeshNoVoxels(const PxCookingParams& params, const PxSimpleTriangleMesh& surfaceMesh, PxInsertionCallback& insertionCallback, PxReal maxWeightRatioInTet, const bool validate)
{
PxCookingParams p = params;
p.maxWeightRatioInTet = maxWeightRatioInTet;
physx::PxArray<physx::PxVec3> collisionMeshVertices;
physx::PxArray<physx::PxU32> collisionMeshIndices;
if (!PxTetMaker::createConformingTetrahedronMesh(surfaceMesh, collisionMeshVertices, collisionMeshIndices, validate))
return NULL;
PxTetrahedronMeshDesc meshDesc(collisionMeshVertices, collisionMeshIndices);
PxDeformableVolumeSimulationDataDesc simDesc;
physx::PxDeformableVolumeMesh* deformableVolumeMesh = PxCreateDeformableVolumeMesh(p, meshDesc, meshDesc, simDesc, insertionCallback);
return deformableVolumeMesh;
}
PxDeformableVolume* PxDeformableVolumeExt::createDeformableVolumeFromMesh(PxDeformableVolumeMesh* deformableVolumeMesh, const PxTransform& transform, const PxDeformableVolumeMaterial& material, PxCudaContextManager& cudaContextManager,
PxReal density, PxReal scale)
{
PxDeformableVolume* deformableVolume = PxGetPhysics().createDeformableVolume(cudaContextManager);
if (deformableVolume)
{
PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE;
PxTetrahedronMeshGeometry geometry(deformableVolumeMesh->getCollisionMesh());
PxDeformableVolumeMaterial* materialPointer = const_cast<PxDeformableVolumeMaterial*>(&material);
PxShape* shape = PxGetPhysics().createShape(geometry, &materialPointer, 1, true, shapeFlags);
if (shape)
{
deformableVolume->attachShape(*shape);
}
deformableVolume->attachSimulationMesh(*deformableVolumeMesh->getSimulationMesh(), *deformableVolumeMesh->getDeformableVolumeAuxData());
PxVec4* simPositionInvMassPinned;
PxVec4* simVelocityPinned;
PxVec4* collPositionInvMassPinned;
PxVec4* restPositionPinned;
PxDeformableVolumeExt::allocateAndInitializeHostMirror(*deformableVolume, &cudaContextManager, simPositionInvMassPinned, simVelocityPinned, collPositionInvMassPinned, restPositionPinned);
const PxReal maxInvMassRatio = 50.f;
PxDeformableVolumeExt::transform(*deformableVolume, transform, scale, simPositionInvMassPinned, simVelocityPinned, collPositionInvMassPinned, restPositionPinned);
PxDeformableVolumeExt::updateMass(*deformableVolume, density, maxInvMassRatio, simPositionInvMassPinned);
PxDeformableVolumeExt::copyToDevice(*deformableVolume, PxDeformableVolumeDataFlag::eALL, simPositionInvMassPinned, simVelocityPinned, collPositionInvMassPinned, restPositionPinned);
#if PX_SUPPORT_GPU_PHYSX
PxCudaContextManager* mgr = &cudaContextManager;
PX_EXT_PINNED_MEMORY_FREE(*mgr, simPositionInvMassPinned);
PX_EXT_PINNED_MEMORY_FREE(*mgr, simVelocityPinned);
PX_EXT_PINNED_MEMORY_FREE(*mgr, collPositionInvMassPinned);
PX_EXT_PINNED_MEMORY_FREE(*mgr, restPositionPinned)
#endif
}
return deformableVolume;
}
PxDeformableVolume* PxDeformableVolumeExt::createDeformableVolumeBox(const PxTransform& transform, const PxVec3& boxDimensions, const PxDeformableVolumeMaterial& material,
PxCudaContextManager& cudaContextManager, PxReal maxEdgeLength, PxReal density, PxU32 numVoxelsAlongLongestAABBAxis, PxReal scale)
{
PxArray<PxVec3> triVerts;
triVerts.reserve(8);
triVerts.pushBack(PxVec3(0.5f, -0.5f, -0.5f).multiply(boxDimensions));
triVerts.pushBack(PxVec3(0.5f, -0.5f, 0.5f).multiply(boxDimensions));
triVerts.pushBack(PxVec3(-0.5f, -0.5f, 0.5f).multiply(boxDimensions));
triVerts.pushBack(PxVec3(-0.5f, -0.5f, -0.5f).multiply(boxDimensions));
triVerts.pushBack(PxVec3(0.5f, 0.5f, -0.5f).multiply(boxDimensions));
triVerts.pushBack(PxVec3(0.5f, 0.5f, 0.5f).multiply(boxDimensions));
triVerts.pushBack(PxVec3(-0.5f, 0.5f, 0.5f).multiply(boxDimensions));
triVerts.pushBack(PxVec3(-0.5f, 0.5f, -0.5f).multiply(boxDimensions));
PxArray<PxU32> triIndices;
triIndices.reserve(12 * 3);
triIndices.pushBack(1); triIndices.pushBack(2); triIndices.pushBack(3);
triIndices.pushBack(7); triIndices.pushBack(6); triIndices.pushBack(5);
triIndices.pushBack(4); triIndices.pushBack(5); triIndices.pushBack(1);
triIndices.pushBack(5); triIndices.pushBack(6); triIndices.pushBack(2);
triIndices.pushBack(2); triIndices.pushBack(6); triIndices.pushBack(7);
triIndices.pushBack(0); triIndices.pushBack(3); triIndices.pushBack(7);
triIndices.pushBack(0); triIndices.pushBack(1); triIndices.pushBack(3);
triIndices.pushBack(4); triIndices.pushBack(7); triIndices.pushBack(5);
triIndices.pushBack(0); triIndices.pushBack(4); triIndices.pushBack(1);
triIndices.pushBack(1); triIndices.pushBack(5); triIndices.pushBack(2);
triIndices.pushBack(3); triIndices.pushBack(2); triIndices.pushBack(7);
triIndices.pushBack(4); triIndices.pushBack(0); triIndices.pushBack(7);
if (maxEdgeLength > 0.0f)
PxRemeshingExt::limitMaxEdgeLength(triIndices, triVerts, maxEdgeLength, 3);
PxSimpleTriangleMesh surfaceMesh;
surfaceMesh.points.count = triVerts.size();
surfaceMesh.points.data = triVerts.begin();
surfaceMesh.triangles.count = triIndices.size() / 3;
surfaceMesh.triangles.data = triIndices.begin();
PxTolerancesScale tolerancesScale;
PxCookingParams params(tolerancesScale);
params.meshWeldTolerance = 0.001f;
params.meshPreprocessParams = PxMeshPreprocessingFlags(PxMeshPreprocessingFlag::eWELD_VERTICES);
params.buildTriangleAdjacencies = false;
params.buildGPUData = true;
params.midphaseDesc = PxMeshMidPhase::eBVH34;
PxDeformableVolumeMesh* deformableVolumeMesh = createDeformableVolumeMesh(params, surfaceMesh, numVoxelsAlongLongestAABBAxis, PxGetPhysics().getPhysicsInsertionCallback());
return createDeformableVolumeFromMesh(deformableVolumeMesh, transform, material, cudaContextManager, density, scale);
}
void PxDeformableVolumeExt::allocateAndInitializeHostMirror(PxDeformableVolume& deformableVolume, PxCudaContextManager* cudaContextManager, PxVec4*& simPositionInvMassPinned, PxVec4*& simVelocityPinned, PxVec4*& collPositionInvMassPinned, PxVec4*& restPositionPinned)
{
PX_ASSERT(deformableVolume.getCollisionMesh() != NULL);
PX_ASSERT(deformableVolume.getSimulationMesh() != NULL);
PxU32 nbCollVerts = deformableVolume.getCollisionMesh()->getNbVertices();
PxU32 nbSimVerts = deformableVolume.getSimulationMesh()->getNbVertices();
#if PX_SUPPORT_GPU_PHYSX
simPositionInvMassPinned = PX_EXT_PINNED_MEMORY_ALLOC(PxVec4, *cudaContextManager, nbSimVerts);
simVelocityPinned = PX_EXT_PINNED_MEMORY_ALLOC(PxVec4, *cudaContextManager, nbSimVerts);
collPositionInvMassPinned = PX_EXT_PINNED_MEMORY_ALLOC(PxVec4, *cudaContextManager, nbCollVerts);
restPositionPinned = PX_EXT_PINNED_MEMORY_ALLOC(PxVec4, *cudaContextManager, nbCollVerts);
#else
PX_UNUSED(cudaContextManager);
#endif
// write positionInvMass into CPU part.
const PxVec3* positions = deformableVolume.getCollisionMesh()->getVertices();
for (PxU32 i = 0; i < nbCollVerts; ++i)
{
PxVec3 vert = positions[i];
collPositionInvMassPinned[i] = PxVec4(vert, 1.f);
restPositionPinned[i] = PxVec4(vert, 1.f);
}
// write sim mesh part.
PxDeformableVolumeAuxData* s = deformableVolume.getDeformableVolumeAuxData();
PxReal* invMassGM = s->getGridModelInvMass();
const PxVec3* simPositions = deformableVolume.getSimulationMesh()->getVertices();
for (PxU32 i = 0; i < nbSimVerts; ++i)
{
PxReal invMass = invMassGM ? invMassGM[i] : 1.f;
simPositionInvMassPinned[i] = PxVec4(simPositions[i], invMass);
simVelocityPinned[i] = PxVec4(0.f, 0.f, 0.f, invMass);
}
}
struct InternalDeformableVolumeState
{
PxVec4* mVertices;
PxArray<PxReal> mInvMasses;
const PxU32* mTetrahedra;
PxArray<PxMat33> mInvRestPose;
PxArray<PxVec3> mPrevPos;
InternalDeformableVolumeState(const PxVec4* verticesOriginal, PxVec4* verticesDeformed, PxU32 nbVertices, const PxU32* tetrahedra, PxU32 nbTetraheda, const bool* vertexIsFixed) :
mVertices(verticesDeformed), mTetrahedra(tetrahedra)
{
PxReal density = 1.0f;
mInvMasses.resize(nbVertices, 0.0f);
mInvRestPose.resize(nbTetraheda, PxMat33(PxZero));
for (PxU32 i = 0; i < nbTetraheda; i++)
{
const PxU32* t = &mTetrahedra[4 * i];
const PxVec3 a = verticesOriginal[t[0]].getXYZ();
PxMat33 ir(verticesOriginal[t[1]].getXYZ() - a, verticesOriginal[t[2]].getXYZ() - a, verticesOriginal[t[3]].getXYZ() - a);
PxReal volume = ir.getDeterminant() / 6.0f;
if (volume > 1e-8f)
mInvRestPose[i] = ir.getInverse();
PxReal m = 0.25f * volume * density;
mInvMasses[t[0]] += m;
mInvMasses[t[1]] += m;
mInvMasses[t[2]] += m;
mInvMasses[t[3]] += m;
}
for (PxU32 i = 0; i < nbVertices; i++)
{
bool fixed = vertexIsFixed ? vertexIsFixed[i] : verticesOriginal[i].w == 0.0f;
if (mInvMasses[i] != 0.0f && !fixed)
mInvMasses[i] = 1.0f / mInvMasses[i];
else
mInvMasses[i] = 0.0f;
}
mPrevPos.resize(nbVertices);
for (PxU32 i = 0; i < mPrevPos.size(); ++i)
mPrevPos[i] = mVertices[i].getXYZ();
}
void applyDelta()
{
for (PxU32 i = 0; i < mPrevPos.size(); ++i)
{
PxVec3 delta = mVertices[i].getXYZ() - mPrevPos[i];
mPrevPos[i] = mVertices[i].getXYZ();
mVertices[i] += PxVec4(0.99f * delta, 0.0f);
}
}
PX_FORCE_INLINE void applyToElem(PxU32 elemNr, PxReal C, PxReal compliance, const PxVec3& g1, const PxVec3& g2, const PxVec3& g3, const PxVec4& invMasses)
{
if (C == 0.0f)
return;
const PxVec3 g0 = -g1 - g2 - g3;
const PxU32* t = &mTetrahedra[4 * elemNr];
const PxReal w = g0.magnitudeSquared() * invMasses.x + g1.magnitudeSquared() * invMasses.y + g2.magnitudeSquared() * invMasses.z + g3.magnitudeSquared() * invMasses.w;
if (w == 0.0f)
return;
const PxReal alpha = compliance;
const PxReal dlambda = -C / (w + alpha);
if (invMasses.x != 0.0f)
mVertices[t[0]] += PxVec4(g0 * dlambda * invMasses.x, 0.0f);
if (invMasses.y != 0.0f)
mVertices[t[1]] += PxVec4(g1 * dlambda * invMasses.y, 0.0f);
if (invMasses.z != 0.0f)
mVertices[t[2]] += PxVec4(g2 * dlambda * invMasses.z, 0.0f);
if (invMasses.w != 0.0f)
mVertices[t[3]] += PxVec4(g3 * dlambda * invMasses.w, 0.0f);
}
void solveElem(PxU32 elemNr)
{
const PxMat33& ir = mInvRestPose[elemNr];
if (ir == PxMat33(PxZero))
return;
const PxU32* tet = &mTetrahedra[4 * elemNr];
PxVec4 invMasses(mInvMasses[tet[0]], mInvMasses[tet[1]], mInvMasses[tet[2]], mInvMasses[tet[3]]);
PxMat33 P;
P.column0 = mVertices[tet[1]].getXYZ() - mVertices[tet[0]].getXYZ();
P.column1 = mVertices[tet[2]].getXYZ() - mVertices[tet[0]].getXYZ();
P.column2 = mVertices[tet[3]].getXYZ() - mVertices[tet[0]].getXYZ();
PxMat33 F = P * ir;
PxVec3 g1 = F.column0 * 2.0f * ir.column0.x + F.column1 * 2.0f * ir.column1.x + F.column2 * 2.0f * ir.column2.x;
PxVec3 g2 = F.column0 * 2.0f * ir.column0.y + F.column1 * 2.0f * ir.column1.y + F.column2 * 2.0f * ir.column2.y;
PxVec3 g3 = F.column0 * 2.0f * ir.column0.z + F.column1 * 2.0f * ir.column1.z + F.column2 * 2.0f * ir.column2.z;
PxReal C = F.column0.magnitudeSquared() + F.column1.magnitudeSquared() + F.column2.magnitudeSquared() - 3.0f;
applyToElem(elemNr, C, 0.0f, g1, g2, g3, invMasses);
P.column0 = mVertices[tet[1]].getXYZ() - mVertices[tet[0]].getXYZ();
P.column1 = mVertices[tet[2]].getXYZ() - mVertices[tet[0]].getXYZ();
P.column2 = mVertices[tet[3]].getXYZ() - mVertices[tet[0]].getXYZ();
F = P * ir;
PxMat33& dF = P; //Re-use memory, possible since P is not used anymore afterwards
dF.column0 = F.column1.cross(F.column2);
dF.column1 = F.column2.cross(F.column0);
dF.column2 = F.column0.cross(F.column1);
g1 = dF.column0 * ir.column0.x + dF.column1 * ir.column1.x + dF.column2 * ir.column2.x;
g2 = dF.column0 * ir.column0.y + dF.column1 * ir.column1.y + dF.column2 * ir.column2.y;
g3 = dF.column0 * ir.column0.z + dF.column1 * ir.column1.z + dF.column2 * ir.column2.z;
C = F.getDeterminant() - 1.0f;
applyToElem(elemNr, C, 0.0f, g1, g2, g3, invMasses);
}
};
void PxDeformableVolumeExt::relaxDeformableVolumeMesh(const PxVec4* verticesOriginal, PxVec4* verticesDeformed, PxU32 nbVertices, const PxU32* tetrahedra, PxU32 nbTetraheda, const bool* vertexIsFixed, PxU32 numIterations)
{
InternalDeformableVolumeState state(verticesOriginal, verticesDeformed, nbVertices, tetrahedra, nbTetraheda, vertexIsFixed);
for (PxU32 iter = 0; iter < numIterations; ++iter)
{
state.applyDelta();
for (PxU32 i = 0; i < nbTetraheda; ++i)
state.solveElem(i);
}
return;
}
void PxDeformableVolumeExt::convertCollisionToSimulationTet(PxDeformableVolume& deformableVolume, PxU32 tetId, const PxVec4& tetBarycentric, PxU32& outTetId, PxVec4& outTetBarycentric)
{
const PxTetrahedronMesh* simulationMesh = deformableVolume.getSimulationMesh();
const Gu::DeformableVolumeAuxData* deformableVolumeAuxData = static_cast<const Gu::DeformableVolumeAuxData*>(deformableVolume.getDeformableVolumeAuxData());
const Gu::BVTetrahedronMesh* collisionMesh = static_cast<const Gu::BVTetrahedronMesh*>(deformableVolume.getCollisionMesh());
// Sim mesh uses the same ordering for both CPU and GPU tet ids so we can use Gu::convertDeformableVolumeCollisionToSimMeshTets to convert from CPU collision tet id to CPU/GPU sim tet id.
Gu::convertDeformableVolumeCollisionToSimMeshTets(*simulationMesh, *deformableVolumeAuxData, *collisionMesh, tetId, tetBarycentric, outTetId, outTetBarycentric, false);
}

View File

@@ -0,0 +1,331 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "ExtDistanceJoint.h"
#include "ExtConstraintHelper.h"
#include "omnipvd/ExtOmniPvdSetData.h"
using namespace physx;
using namespace Ext;
DistanceJoint::DistanceJoint(const PxTolerancesScale& scale, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) :
DistanceJointT(PxJointConcreteType::eDISTANCE, actor0, localFrame0, actor1, localFrame1, "DistanceJointData")
{
DistanceJointData* data = static_cast<DistanceJointData*>(mData);
data->stiffness = 0.0f;
data->damping = 0.0f;
data->minDistance = 0.0f;
data->maxDistance = 0.0f;
data->tolerance = 0.025f * scale.length;
data->jointFlags = PxDistanceJointFlag::eMAX_DISTANCE_ENABLED;
}
PxReal DistanceJoint::getDistance() const
{
return getRelativeTransform().p.magnitude();
}
void DistanceJoint::setMinDistance(PxReal distance)
{
PX_CHECK_AND_RETURN(PxIsFinite(distance) && distance>=0.0f, "PxDistanceJoint::setMinDistance: invalid parameter");
data().minDistance = distance;
markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, minDistance, static_cast<PxDistanceJoint&>(*this), distance)
}
PxReal DistanceJoint::getMinDistance() const
{
return data().minDistance;
}
void DistanceJoint::setMaxDistance(PxReal distance)
{
PX_CHECK_AND_RETURN(PxIsFinite(distance) && distance>=0.0f, "PxDistanceJoint::setMaxDistance: invalid parameter");
data().maxDistance = distance;
markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, maxDistance, static_cast<PxDistanceJoint&>(*this), distance)
}
PxReal DistanceJoint::getMaxDistance() const
{
return data().maxDistance;
}
void DistanceJoint::setTolerance(PxReal tolerance)
{
PX_CHECK_AND_RETURN(PxIsFinite(tolerance), "PxDistanceJoint::setTolerance: invalid parameter");
data().tolerance = tolerance;
markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, tolerance, static_cast<PxDistanceJoint&>(*this), tolerance)
}
PxReal DistanceJoint::getTolerance() const
{
return data().tolerance;
}
void DistanceJoint::setStiffness(PxReal stiffness)
{
PX_CHECK_AND_RETURN(PxIsFinite(stiffness), "PxDistanceJoint::setStiffness: invalid parameter");
data().stiffness = stiffness;
markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, stiffness, static_cast<PxDistanceJoint&>(*this), stiffness)
}
PxReal DistanceJoint::getStiffness() const
{
return data().stiffness;
}
void DistanceJoint::setDamping(PxReal damping)
{
PX_CHECK_AND_RETURN(PxIsFinite(damping), "PxDistanceJoint::setDamping: invalid parameter");
data().damping = damping;
markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, damping, static_cast<PxDistanceJoint&>(*this), damping)
}
PxReal DistanceJoint::getDamping() const
{
return data().damping;
}
PxDistanceJointFlags DistanceJoint::getDistanceJointFlags() const
{
return data().jointFlags;
}
void DistanceJoint::setDistanceJointFlags(PxDistanceJointFlags flags)
{
data().jointFlags = flags;
markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, jointFlags, static_cast<PxDistanceJoint&>(*this), flags)
}
void DistanceJoint::setDistanceJointFlag(PxDistanceJointFlag::Enum flag, bool value)
{
if(value)
data().jointFlags |= flag;
else
data().jointFlags &= ~flag;
markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, jointFlags, static_cast<PxDistanceJoint&>(*this), getDistanceJointFlags())
}
static void DistanceJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags)
{
const DistanceJointData& data = *reinterpret_cast<const DistanceJointData*>(constantBlock);
PxTransform32 cA2w, cB2w;
joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform);
if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES)
viz.visualizeJointFrames(cA2w, cB2w);
// PT: we consider the following is part of the joint's "limits" since that's the only available flag we have
if(flags & PxConstraintVisualizationFlag::eLIMITS)
{
const bool enforceMax = (data.jointFlags & PxDistanceJointFlag::eMAX_DISTANCE_ENABLED);
const bool enforceMin = (data.jointFlags & PxDistanceJointFlag::eMIN_DISTANCE_ENABLED);
if(!enforceMin && !enforceMax)
return;
PxVec3 dir = cB2w.p - cA2w.p;
const float currentDist = dir.normalize();
PxU32 color = 0x00ff00;
if(enforceMax && currentDist>data.maxDistance)
color = 0xff0000;
if(enforceMin && currentDist<data.minDistance)
color = 0x0000ff;
viz.visualizeLine(cA2w.p, cB2w.p, color);
}
}
static PX_FORCE_INLINE void setupConstraint(Px1DConstraint& c, const PxVec3& direction, const PxVec3& angular0, const PxVec3& angular1, const DistanceJointData& data)
{
// constraint is breakable, so we need to output forces
c.flags = Px1DConstraintFlag::eOUTPUT_FORCE;
c.linear0 = direction; c.angular0 = angular0;
c.linear1 = direction; c.angular1 = angular1;
if(data.jointFlags & PxDistanceJointFlag::eSPRING_ENABLED)
{
c.flags |= Px1DConstraintFlag::eSPRING;
c.mods.spring.stiffness = data.stiffness;
c.mods.spring.damping = data.damping;
}
}
static PX_FORCE_INLINE PxU32 setupMinConstraint(Px1DConstraint& c, const PxVec3& direction, const PxVec3& angular0, const PxVec3& angular1, const DistanceJointData& data, float distance)
{
setupConstraint(c, direction, angular0, angular1, data);
c.geometricError = distance - data.minDistance + data.tolerance;
c.minImpulse = 0.0f;
if(distance>=data.minDistance)
c.flags |= Px1DConstraintFlag::eKEEPBIAS;
return 1;
}
static PX_FORCE_INLINE PxU32 setupMaxConstraint(Px1DConstraint& c, const PxVec3& direction, const PxVec3& angular0, const PxVec3& angular1, const DistanceJointData& data, float distance)
{
setupConstraint(c, direction, angular0, angular1, data);
c.geometricError = distance - data.maxDistance - data.tolerance;
c.maxImpulse = 0.0f;
if(distance<=data.maxDistance)
c.flags |= Px1DConstraintFlag::eKEEPBIAS;
return 1;
}
//TAG:solverprepshader
static PxU32 DistanceJointSolverPrep(Px1DConstraint* constraints,
PxVec3p& body0WorldOffset,
PxU32 /*maxConstraints*/,
PxConstraintInvMassScale& invMassScale,
const void* constantBlock,
const PxTransform& bA2w,
const PxTransform& bB2w,
bool /*useExtendedLimits*/,
PxVec3p& cA2wOut, PxVec3p& cB2wOut)
{
const DistanceJointData& data = *reinterpret_cast<const DistanceJointData*>(constantBlock);
const bool enforceMax = (data.jointFlags & PxDistanceJointFlag::eMAX_DISTANCE_ENABLED) && data.maxDistance>=0.0f;
const bool enforceMin = (data.jointFlags & PxDistanceJointFlag::eMIN_DISTANCE_ENABLED) && data.minDistance>=0.0f;
if(!enforceMax && !enforceMin)
return 0;
PxTransform32 cA2w, cB2w;
joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w);
cA2wOut = cB2w.p;
cB2wOut = cB2w.p;
PxVec3 direction = cA2w.p - cB2w.p;
const PxReal distance = direction.normalize();
#define EPS_REAL 1.192092896e-07F
if(distance < EPS_REAL)
direction = PxVec3(1.0f, 0.0f, 0.0f);
Px1DConstraint* c = constraints;
const PxVec3 angular0 = ch.getRa().cross(direction);
const PxVec3 angular1 = ch.getRb().cross(direction);
if(enforceMin && !enforceMax)
return setupMinConstraint(*c, direction, angular0, angular1, data, distance);
else if(enforceMax && !enforceMin)
return setupMaxConstraint(*c, direction, angular0, angular1, data, distance);
else
{
if(data.minDistance == data.maxDistance)
{
setupConstraint(*c, direction, angular0, angular1, data);
//add tolerance so we don't have contact-style jitter problem.
const PxReal error = distance - data.maxDistance;
c->geometricError = error > data.tolerance ? error - data.tolerance :
error < -data.tolerance ? error + data.tolerance : 0.0f;
return 1;
}
// since we dont know the current rigid velocity, we need to insert row for both limits
PxU32 nb = setupMinConstraint(*c, direction, angular0, angular1, data, distance);
if(nb)
c++;
nb += setupMaxConstraint(*c, direction, angular0, angular1, data, distance);
return nb;
}
}
///////////////////////////////////////////////////////////////////////////////
static PxConstraintShaderTable gDistanceJointShaders = { DistanceJointSolverPrep, DistanceJointVisualize, PxConstraintFlag::Enum(0) };
PxConstraintSolverPrep DistanceJoint::getPrep() const { return gDistanceJointShaders.solverPrep; }
PxDistanceJoint* physx::PxDistanceJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1)
{
PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxDistanceJointCreate: local frame 0 is not a valid transform");
PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxDistanceJointCreate: local frame 1 is not a valid transform");
PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxDistanceJointCreate: actors must be different");
PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is<PxRigidBody>()) || (actor1 && actor1->is<PxRigidBody>()), "PxD6JointCreate: at least one actor must be dynamic");
return createJointT<DistanceJoint, DistanceJointData>(physics, actor0, localFrame0, actor1, localFrame1, gDistanceJointShaders);
}
// PX_SERIALIZATION
void DistanceJoint::resolveReferences(PxDeserializationContext& context)
{
mPxConstraint = resolveConstraintPtr(context, mPxConstraint, this, gDistanceJointShaders);
}
//~PX_SERIALIZATION
#if PX_SUPPORT_OMNI_PVD
void DistanceJoint::updateOmniPvdProperties() const
{
const PxDistanceJoint& j = static_cast<const PxDistanceJoint&>(*this);
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, distance, j, getDistance())
}
template<>
void physx::Ext::omniPvdInitJoint<DistanceJoint>(DistanceJoint& joint)
{
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
PxDistanceJoint& j = static_cast<PxDistanceJoint&>(joint);
OMNI_PVD_CREATE_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, j);
omniPvdSetBaseJointParams(static_cast<PxJoint&>(joint), PxJointConcreteType::eDISTANCE);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, minDistance, j, joint.getMinDistance())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, maxDistance, j, joint.getMaxDistance())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, tolerance, j, joint.getTolerance())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, stiffness, j, joint.getStiffness())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, damping, j, joint.getDamping())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, jointFlags, j, joint.getDistanceJointFlags())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxDistanceJoint, distance, j, joint.getDistance())
OMNI_PVD_WRITE_SCOPE_END
}
#endif

View File

@@ -0,0 +1,94 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_DISTANCE_JOINT_H
#define EXT_DISTANCE_JOINT_H
#include "common/PxTolerancesScale.h"
#include "extensions/PxDistanceJoint.h"
#include "ExtJoint.h"
#include "foundation/PxUserAllocated.h"
#include "CmUtils.h"
namespace physx
{
struct PxDistanceJointGeneratedValues;
namespace Ext
{
struct DistanceJointData : public JointData
{
PxReal minDistance;
PxReal maxDistance;
PxReal tolerance;
PxReal stiffness;
PxReal damping;
PxDistanceJointFlags jointFlags;
};
typedef JointT<PxDistanceJoint, DistanceJointData, PxDistanceJointGeneratedValues> DistanceJointT;
class DistanceJoint : public DistanceJointT
{
public:
// PX_SERIALIZATION
DistanceJoint(PxBaseFlags baseFlags) : DistanceJointT(baseFlags) {}
void resolveReferences(PxDeserializationContext& context);
static DistanceJoint* createObject(PxU8*& address, PxDeserializationContext& context) { return createJointObject<DistanceJoint>(address, context); }
//~PX_SERIALIZATION
DistanceJoint(const PxTolerancesScale& scale, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1);
// PxDistanceJoint
virtual PxReal getDistance() const PX_OVERRIDE;
virtual void setMinDistance(PxReal distance) PX_OVERRIDE;
virtual PxReal getMinDistance() const PX_OVERRIDE;
virtual void setMaxDistance(PxReal distance) PX_OVERRIDE;
virtual PxReal getMaxDistance() const PX_OVERRIDE;
virtual void setTolerance(PxReal tolerance) PX_OVERRIDE;
virtual PxReal getTolerance() const PX_OVERRIDE;
virtual void setStiffness(PxReal spring) PX_OVERRIDE;
virtual PxReal getStiffness() const PX_OVERRIDE;
virtual void setDamping(PxReal damping) PX_OVERRIDE;
virtual PxReal getDamping() const PX_OVERRIDE;
virtual void setDistanceJointFlags(PxDistanceJointFlags flags) PX_OVERRIDE;
virtual void setDistanceJointFlag(PxDistanceJointFlag::Enum flag, bool value) PX_OVERRIDE;
virtual PxDistanceJointFlags getDistanceJointFlags() const PX_OVERRIDE;
//~PxDistanceJoint
// PxConstraintConnector
virtual PxConstraintSolverPrep getPrep() const PX_OVERRIDE;
#if PX_SUPPORT_OMNI_PVD
virtual void updateOmniPvdProperties() const PX_OVERRIDE;
#endif
//~PxConstraintConnector
};
} // namespace Ext
} // namespace physx
#endif

View File

@@ -0,0 +1,260 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "foundation/PxIO.h"
#include "common/PxSerializer.h"
#include "extensions/PxExtensionsAPI.h"
#include "extensions/PxRepXSerializer.h"
#include "ExtDistanceJoint.h"
#include "ExtD6Joint.h"
#include "ExtFixedJoint.h"
#include "ExtPrismaticJoint.h"
#include "ExtRevoluteJoint.h"
#include "ExtSphericalJoint.h"
#include "ExtGearJoint.h"
#include "ExtRackAndPinionJoint.h"
#include "ExtSerialization.h"
#include "SnRepXCoreSerializer.h"
#include "SnJointRepXSerializer.h"
#include "PxExtensionMetaDataObjects.h"
#if PX_SUPPORT_PVD
#include "ExtPvd.h"
#include "PxPvdDataStream.h"
#include "PxPvdClient.h"
#include "PsPvd.h"
#endif
#if PX_SUPPORT_OMNI_PVD
# include "omnipvd/PxOmniPvd.h"
# include "omnipvd/OmniPvdPxExtensionsSampler.h"
#endif
using namespace physx;
using namespace physx::pvdsdk;
#if PX_SUPPORT_PVD
struct JointConnectionHandler : public PvdClient
{
JointConnectionHandler() : mPvd(NULL),mConnected(false){}
PvdDataStream* getDataStream()
{
return NULL;
}
void onPvdConnected()
{
PvdDataStream* stream = PvdDataStream::create(mPvd);
if(stream)
{
mConnected = true;
Ext::Pvd::sendClassDescriptions(*stream);
stream->release();
}
}
bool isConnected() const
{
return mConnected;
}
void onPvdDisconnected()
{
mConnected = false;
}
void flush()
{
}
PsPvd* mPvd;
bool mConnected;
};
static JointConnectionHandler gPvdHandler;
#endif
bool PxInitExtensions(PxPhysics& physics, PxPvd* pvd)
{
PX_ASSERT(&physics.getFoundation() == &PxGetFoundation());
PX_UNUSED(physics);
PX_UNUSED(pvd);
PxIncFoundationRefCount();
#if PX_SUPPORT_PVD
if(pvd)
{
gPvdHandler.mPvd = static_cast<PsPvd*>(pvd);
gPvdHandler.mPvd->addClient(&gPvdHandler);
}
#endif
#if PX_SUPPORT_OMNI_PVD
if (physics.getOmniPvd() && physics.getOmniPvd()->getWriter())
{
if (OmniPvdPxExtensionsSampler::createInstance())
{
OmniPvdPxExtensionsSampler::getInstance()->setOmniPvdInstance(physics.getOmniPvd());
OmniPvdPxExtensionsSampler::getInstance()->registerClasses();
}
}
#endif
return true;
}
static PxArray<PxSceneQuerySystem*>* gExternalSQ = NULL;
void addExternalSQ(PxSceneQuerySystem* added)
{
if(!gExternalSQ)
gExternalSQ = new PxArray<PxSceneQuerySystem*>;
gExternalSQ->pushBack(added);
}
void removeExternalSQ(PxSceneQuerySystem* removed)
{
if(gExternalSQ)
{
const PxU32 nb = gExternalSQ->size();
for(PxU32 i=0;i<nb;i++)
{
PxSceneQuerySystem* sq = (*gExternalSQ)[i];
if(sq==removed)
{
gExternalSQ->replaceWithLast(i);
return;
}
}
}
}
static void releaseExternalSQ()
{
if(gExternalSQ)
{
PxArray<PxSceneQuerySystem*>* copy = gExternalSQ;
gExternalSQ = NULL;
const PxU32 nb = copy->size();
for(PxU32 i=0;i<nb;i++)
{
PxSceneQuerySystem* sq = (*copy)[i];
sq->release();
}
PX_DELETE(copy);
}
}
void PxCloseExtensions()
{
releaseExternalSQ();
PxDecFoundationRefCount();
#if PX_SUPPORT_PVD
if(gPvdHandler.mConnected)
{
PX_ASSERT(gPvdHandler.mPvd);
gPvdHandler.mPvd->removeClient(&gPvdHandler);
gPvdHandler.mPvd = NULL;
}
#endif
#if PX_SUPPORT_OMNI_PVD
if (OmniPvdPxExtensionsSampler::getInstance())
{
OmniPvdPxExtensionsSampler::destroyInstance();
}
#endif
}
void Ext::RegisterExtensionsSerializers(PxSerializationRegistry& sr)
{
//for repx serialization
sr.registerRepXSerializer(PxConcreteType::eMATERIAL, PX_NEW_REPX_SERIALIZER( PxMaterialRepXSerializer ));
sr.registerRepXSerializer(PxConcreteType::eSHAPE, PX_NEW_REPX_SERIALIZER( PxShapeRepXSerializer ));
sr.registerRepXSerializer(PxConcreteType::eTRIANGLE_MESH_BVH33, PX_NEW_REPX_SERIALIZER( PxBVH33TriangleMeshRepXSerializer ));
sr.registerRepXSerializer(PxConcreteType::eTRIANGLE_MESH_BVH34, PX_NEW_REPX_SERIALIZER( PxBVH34TriangleMeshRepXSerializer ));
sr.registerRepXSerializer(PxConcreteType::eHEIGHTFIELD, PX_NEW_REPX_SERIALIZER( PxHeightFieldRepXSerializer ));
sr.registerRepXSerializer(PxConcreteType::eCONVEX_MESH, PX_NEW_REPX_SERIALIZER( PxConvexMeshRepXSerializer ));
sr.registerRepXSerializer(PxConcreteType::eRIGID_STATIC, PX_NEW_REPX_SERIALIZER( PxRigidStaticRepXSerializer ));
sr.registerRepXSerializer(PxConcreteType::eRIGID_DYNAMIC, PX_NEW_REPX_SERIALIZER( PxRigidDynamicRepXSerializer ));
sr.registerRepXSerializer(PxConcreteType::eARTICULATION_REDUCED_COORDINATE, PX_NEW_REPX_SERIALIZER( PxArticulationReducedCoordinateRepXSerializer));
sr.registerRepXSerializer(PxConcreteType::eAGGREGATE, PX_NEW_REPX_SERIALIZER( PxAggregateRepXSerializer ));
sr.registerRepXSerializer(PxJointConcreteType::eFIXED, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer<PxFixedJoint> ));
sr.registerRepXSerializer(PxJointConcreteType::eDISTANCE, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer<PxDistanceJoint> ));
sr.registerRepXSerializer(PxJointConcreteType::eD6, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer<PxD6Joint> ));
sr.registerRepXSerializer(PxJointConcreteType::ePRISMATIC, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer<PxPrismaticJoint> ));
sr.registerRepXSerializer(PxJointConcreteType::eREVOLUTE, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer<PxRevoluteJoint> ));
sr.registerRepXSerializer(PxJointConcreteType::eSPHERICAL, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer<PxSphericalJoint> ));
//for binary serialization
sr.registerSerializer(PxJointConcreteType::eFIXED, PX_NEW_SERIALIZER_ADAPTER( FixedJoint ));
sr.registerSerializer(PxJointConcreteType::eDISTANCE, PX_NEW_SERIALIZER_ADAPTER( DistanceJoint ));
sr.registerSerializer(PxJointConcreteType::eD6, PX_NEW_SERIALIZER_ADAPTER( D6Joint) );
sr.registerSerializer(PxJointConcreteType::ePRISMATIC, PX_NEW_SERIALIZER_ADAPTER( PrismaticJoint ));
sr.registerSerializer(PxJointConcreteType::eREVOLUTE, PX_NEW_SERIALIZER_ADAPTER( RevoluteJoint ));
sr.registerSerializer(PxJointConcreteType::eSPHERICAL, PX_NEW_SERIALIZER_ADAPTER( SphericalJoint ));
sr.registerSerializer(PxJointConcreteType::eGEAR, PX_NEW_SERIALIZER_ADAPTER( GearJoint ));
sr.registerSerializer(PxJointConcreteType::eRACK_AND_PINION, PX_NEW_SERIALIZER_ADAPTER( RackAndPinionJoint ));
}
void Ext::UnregisterExtensionsSerializers(PxSerializationRegistry& sr)
{
PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eFIXED));
PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eDISTANCE));
PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eD6 ));
PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::ePRISMATIC));
PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eREVOLUTE));
PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eSPHERICAL));
PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eGEAR));
PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eRACK_AND_PINION));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eMATERIAL));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eSHAPE));
// PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eTRIANGLE_MESH));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eTRIANGLE_MESH_BVH33));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eTRIANGLE_MESH_BVH34));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eHEIGHTFIELD));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eCONVEX_MESH));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eRIGID_STATIC));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eRIGID_DYNAMIC));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eARTICULATION_REDUCED_COORDINATE));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eAGGREGATE));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::eFIXED));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::eDISTANCE));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::eD6));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::ePRISMATIC));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::eREVOLUTE));
PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::eSPHERICAL));
}

View File

@@ -0,0 +1,114 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "ExtFixedJoint.h"
#include "ExtConstraintHelper.h"
#include "omnipvd/ExtOmniPvdSetData.h"
using namespace physx;
using namespace Ext;
FixedJoint::FixedJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) :
FixedJointT(PxJointConcreteType::eFIXED, actor0, localFrame0, actor1, localFrame1, "FixedJointData")
{
// FixedJointData* data = static_cast<FixedJointData*>(mData);
}
static void FixedJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags)
{
if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES)
{
const FixedJointData& data = *reinterpret_cast<const FixedJointData*>(constantBlock);
PxTransform32 cA2w, cB2w;
joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform);
viz.visualizeJointFrames(cA2w, cB2w);
}
}
//TAG:solverprepshader
static PxU32 FixedJointSolverPrep(Px1DConstraint* constraints,
PxVec3p& body0WorldOffset,
PxU32 /*maxConstraints*/,
PxConstraintInvMassScale& invMassScale,
const void* constantBlock,
const PxTransform& bA2w,
const PxTransform& bB2w,
bool /*useExtendedLimits*/,
PxVec3p& cA2wOut, PxVec3p& cB2wOut)
{
const FixedJointData& data = *reinterpret_cast<const FixedJointData*>(constantBlock);
PxTransform32 cA2w, cB2w;
joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w);
joint::applyNeighborhoodOperator(cA2w, cB2w);
PxVec3 ra, rb;
ch.prepareLockedAxes(cA2w.q, cB2w.q, cA2w.transformInv(cB2w.p), 7, 7, ra, rb);
cA2wOut = ra + bA2w.p;
cB2wOut = rb + bB2w.p;
return ch.getCount();
}
///////////////////////////////////////////////////////////////////////////////
static PxConstraintShaderTable gFixedJointShaders = { FixedJointSolverPrep, FixedJointVisualize, PxConstraintFlag::Enum(0) };
PxConstraintSolverPrep FixedJoint::getPrep() const { return gFixedJointShaders.solverPrep; }
PxFixedJoint* physx::PxFixedJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1)
{
PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxFixedJointCreate: local frame 0 is not a valid transform");
PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxFixedJointCreate: local frame 1 is not a valid transform");
PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is<PxRigidBody>()) || (actor1 && actor1->is<PxRigidBody>()), "PxFixedJointCreate: at least one actor must be dynamic");
PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxFixedJointCreate: actors must be different");
return createJointT<FixedJoint, FixedJointData>(physics, actor0, localFrame0, actor1, localFrame1, gFixedJointShaders);
}
// PX_SERIALIZATION
void FixedJoint::resolveReferences(PxDeserializationContext& context)
{
mPxConstraint = resolveConstraintPtr(context, mPxConstraint, this, gFixedJointShaders);
}
//~PX_SERIALIZATION
#if PX_SUPPORT_OMNI_PVD
template<>
void physx::Ext::omniPvdInitJoint<FixedJoint>(FixedJoint& joint)
{
PxFixedJoint& j = static_cast<PxFixedJoint&>(joint);
OMNI_PVD_CREATE(OMNI_PVD_CONTEXT_HANDLE, PxFixedJoint, j);
omniPvdSetBaseJointParams(static_cast<PxJoint&>(joint), PxJointConcreteType::eFIXED);
}
#endif

View File

@@ -0,0 +1,68 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_FIXED_JOINT_H
#define EXT_FIXED_JOINT_H
#include "extensions/PxFixedJoint.h"
#include "ExtJoint.h"
#include "CmUtils.h"
namespace physx
{
struct PxFixedJointGeneratedValues;
namespace Ext
{
struct FixedJointData : public JointData
{
};
typedef JointT<PxFixedJoint, FixedJointData, PxFixedJointGeneratedValues> FixedJointT;
class FixedJoint : public FixedJointT
{
public:
// PX_SERIALIZATION
FixedJoint(PxBaseFlags baseFlags) : FixedJointT(baseFlags) {}
void resolveReferences(PxDeserializationContext& context);
static FixedJoint* createObject(PxU8*& address, PxDeserializationContext& context) { return createJointObject<FixedJoint>(address, context); }
//~PX_SERIALIZATION
FixedJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1);
// PxFixedJoint
//~PxFixedJoint
// PxConstraintConnector
virtual PxConstraintSolverPrep getPrep() const PX_OVERRIDE;
//~PxConstraintConnector
};
} // namespace Ext
} // namespace physx
#endif

View File

@@ -0,0 +1,318 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "ExtGearJoint.h"
#include "ExtConstraintHelper.h"
#include "extensions/PxRevoluteJoint.h"
#include "PxArticulationJointReducedCoordinate.h"
#include "omnipvd/ExtOmniPvdSetData.h"
//#include <stdio.h>
using namespace physx;
using namespace Ext;
PX_IMPLEMENT_OUTPUT_ERROR
GearJoint::GearJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) :
GearJointT(PxJointConcreteType::eGEAR, actor0, localFrame0, actor1, localFrame1, "GearJointData")
{
GearJointData* data = static_cast<GearJointData*>(mData);
data->hingeJoint0 = NULL;
data->hingeJoint1 = NULL;
data->gearRatio = 0.0f;
data->error = 0.0f;
resetError();
}
static bool checkJoint(const PxBase* hinge)
{
if(hinge)
{
bool invalidType;
const PxType type = hinge->getConcreteType();
if(type == PxConcreteType::eARTICULATION_JOINT_REDUCED_COORDINATE)
{
const PxArticulationJointReducedCoordinate* joint = static_cast<const PxArticulationJointReducedCoordinate*>(hinge);
const PxArticulationJointType::Enum artiJointType = joint->getJointType();
invalidType = artiJointType != PxArticulationJointType::eREVOLUTE && artiJointType != PxArticulationJointType::eREVOLUTE_UNWRAPPED;
}
else
{
invalidType = type != PxJointConcreteType::eREVOLUTE && type != PxJointConcreteType::eD6;
}
if(invalidType)
return outputError<PxErrorCode::eINVALID_PARAMETER>(__LINE__, "PxGearJoint::setHinges: passed joint must be either a revolute joint or a D6 joint.");
}
return true;
}
bool GearJoint::setHinges(const PxBase* hinge0, const PxBase* hinge1)
{
GearJointData* data = static_cast<GearJointData*>(mData);
if(!checkJoint(hinge0) || !checkJoint(hinge1))
return false;
data->hingeJoint0 = hinge0;
data->hingeJoint1 = hinge1;
resetError();
markDirty();
#if PX_SUPPORT_OMNI_PVD
const PxBase* joints[] ={ hinge0, hinge1 };
const PxU32 hingeCount = sizeof(joints) / sizeof(joints[0]);
OMNI_PVD_SET_ARRAY(OMNI_PVD_CONTEXT_HANDLE, PxGearJoint, hinges, static_cast<PxGearJoint&>(*this), joints, hingeCount)
#endif
return true;
}
void GearJoint::getHinges(const PxBase*& hinge0, const PxBase*& hinge1) const
{
const GearJointData* data = static_cast<const GearJointData*>(mData);
hinge0 = data->hingeJoint0;
hinge1 = data->hingeJoint1;
}
void GearJoint::setGearRatio(float ratio)
{
GearJointData* data = static_cast<GearJointData*>(mData);
data->gearRatio = ratio;
resetError();
markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxGearJoint, ratio, static_cast<PxGearJoint&>(*this), ratio)
}
float GearJoint::getGearRatio() const
{
const GearJointData* data = static_cast<const GearJointData*>(mData);
return data->gearRatio;
}
static float angleDiff(float angle0, float angle1)
{
const float diff = fmodf(angle1 - angle0 + PxPi, PxTwoPi) - PxPi;
return diff < -PxPi ? diff + PxTwoPi : diff;
}
static void getAngleAndSign(float& angle, float& sign, const PxBase* dataHingeJoint, PxRigidActor* gearActor0, PxRigidActor* gearActor1)
{
PxRigidActor* hingeActor0;
PxRigidActor* hingeActor1;
const PxType type = dataHingeJoint->getConcreteType();
if(type == PxConcreteType::eARTICULATION_JOINT_REDUCED_COORDINATE)
{
const PxArticulationJointReducedCoordinate* artiHingeJoint = static_cast<const PxArticulationJointReducedCoordinate*>(dataHingeJoint);
hingeActor0 = &artiHingeJoint->getParentArticulationLink();
hingeActor1 = &artiHingeJoint->getChildArticulationLink();
angle = artiHingeJoint->getJointPosition(PxArticulationAxis::eTWIST);
}
else
{
const PxJoint* hingeJoint = static_cast<const PxJoint*>(dataHingeJoint);
hingeJoint->getActors(hingeActor0, hingeActor1);
if(type == PxJointConcreteType::eREVOLUTE)
angle = static_cast<const PxRevoluteJoint*>(hingeJoint)->getAngle();
else if(type == PxJointConcreteType::eD6)
angle = static_cast<const PxD6Joint*>(hingeJoint)->getTwistAngle();
}
if(gearActor0 == hingeActor0 || gearActor1 == hingeActor0)
sign = -1.0f;
else if(gearActor0 == hingeActor1 || gearActor1 == hingeActor1)
sign = 1.0f;
else
PX_ASSERT(0);
}
void GearJoint::updateError()
{
GearJointData* data = static_cast<GearJointData*>(mData);
if(!data->hingeJoint0 || !data->hingeJoint1)
return;
PxRigidActor* gearActor0;
PxRigidActor* gearActor1;
getActors(gearActor0, gearActor1);
float Angle0 = 0.0f;
float Sign0 = 0.0f;
getAngleAndSign(Angle0, Sign0, data->hingeJoint0, gearActor0, gearActor1);
float Angle1 = 0.0f;
float Sign1 = 0.0f;
getAngleAndSign(Angle1, Sign1, data->hingeJoint1, gearActor0, gearActor1);
Angle1 = -Angle1;
if(!mInitDone)
{
mInitDone = true;
mPersistentAngle0 = Angle0;
mPersistentAngle1 = Angle1;
}
const float travelThisFrame0 = angleDiff(Angle0, mPersistentAngle0);
const float travelThisFrame1 = angleDiff(Angle1, mPersistentAngle1);
mVirtualAngle0 += travelThisFrame0;
mVirtualAngle1 += travelThisFrame1;
// printf("travelThisFrame0: %f\n", travelThisFrame0);
// printf("travelThisFrame1: %f\n", travelThisFrame1);
// printf("ratio: %f\n", travelThisFrame1/travelThisFrame0);
mPersistentAngle0 = Angle0;
mPersistentAngle1 = Angle1;
const float error = Sign0*mVirtualAngle0*data->gearRatio - Sign1*mVirtualAngle1;
// printf("error: %f\n", error);
data->error = error;
markDirty();
}
void GearJoint::resetError()
{
mVirtualAngle0 = mVirtualAngle1 = 0.0f;
mPersistentAngle0 = mPersistentAngle1 = 0.0f;
mInitDone = false;
}
static const bool gVizJointFrames = true;
static const bool gVizGearAxes = false;
static void GearJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags)
{
if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES)
{
const GearJointData& data = *reinterpret_cast<const GearJointData*>(constantBlock);
// Visualize joint frames
PxTransform32 cA2w, cB2w;
joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform);
if(gVizJointFrames)
viz.visualizeJointFrames(cA2w, cB2w);
if(gVizGearAxes)
{
const PxVec3 gearAxis0 = cA2w.rotate(PxVec3(1.0f, 0.0f, 0.0f)).getNormalized();
const PxVec3 gearAxis1 = cB2w.rotate(PxVec3(1.0f, 0.0f, 0.0f)).getNormalized();
viz.visualizeLine(body0Transform.p+gearAxis0, body0Transform.p, 0xff0000ff);
viz.visualizeLine(body1Transform.p+gearAxis1, body1Transform.p, 0xff0000ff);
}
}
}
//TAG:solverprepshader
static PxU32 GearJointSolverPrep(Px1DConstraint* constraints,
PxVec3p& body0WorldOffset,
PxU32 /*maxConstraints*/,
PxConstraintInvMassScale& invMassScale,
const void* constantBlock,
const PxTransform& bA2w,
const PxTransform& bB2w,
bool /*useExtendedLimits*/,
PxVec3p& cA2wOut, PxVec3p& cB2wOut)
{
const GearJointData& data = *reinterpret_cast<const GearJointData*>(constantBlock);
PxTransform32 cA2w, cB2w;
joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w);
cA2wOut = cB2w.p;
cB2wOut = cB2w.p;
const PxVec3 gearAxis0 = cA2w.q.getBasisVector0();
const PxVec3 gearAxis1 = cB2w.q.getBasisVector0();
Px1DConstraint& con = constraints[0];
con.linear0 = PxVec3(0.0f);
con.linear1 = PxVec3(0.0f);
con.angular0 = gearAxis0*data.gearRatio;
con.angular1 = -gearAxis1;
con.geometricError = -data.error;
con.minImpulse = -PX_MAX_F32;
con.maxImpulse = PX_MAX_F32;
con.velocityTarget = 0.f;
con.solveHint = 0;
con.flags = Px1DConstraintFlag::eOUTPUT_FORCE|Px1DConstraintFlag::eANGULAR_CONSTRAINT;
con.mods.bounce.restitution = 0.0f;
con.mods.bounce.velocityThreshold = 0.0f;
return 1;
}
///////////////////////////////////////////////////////////////////////////////
static PxConstraintShaderTable gGearJointShaders = { GearJointSolverPrep, GearJointVisualize, PxConstraintFlag::eALWAYS_UPDATE };
PxConstraintSolverPrep GearJoint::getPrep() const { return gGearJointShaders.solverPrep; }
PxGearJoint* physx::PxGearJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1)
{
PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxGearJointCreate: local frame 0 is not a valid transform");
PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxGearJointCreate: local frame 1 is not a valid transform");
PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is<PxRigidBody>()) || (actor1 && actor1->is<PxRigidBody>()), "PxGearJointCreate: at least one actor must be dynamic");
PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxGearJointCreate: actors must be different");
return createJointT<GearJoint, GearJointData>(physics, actor0, localFrame0, actor1, localFrame1, gGearJointShaders);
}
// PX_SERIALIZATION
void GearJoint::resolveReferences(PxDeserializationContext& context)
{
mPxConstraint = resolveConstraintPtr(context, mPxConstraint, this, gGearJointShaders);
GearJointData* data = static_cast<GearJointData*>(mData);
context.translatePxBase(data->hingeJoint0);
context.translatePxBase(data->hingeJoint1);
}
//~PX_SERIALIZATION
#if PX_SUPPORT_OMNI_PVD
template<>
void physx::Ext::omniPvdInitJoint<GearJoint>(GearJoint& joint)
{
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
PxGearJoint& j = static_cast<PxGearJoint&>(joint);
OMNI_PVD_CREATE_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxGearJoint, j);
omniPvdSetBaseJointParams(static_cast<PxJoint&>(joint), PxJointConcreteType::eGEAR);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxGearJoint, ratio, j , joint.getGearRatio())
OMNI_PVD_WRITE_SCOPE_END
}
#endif

View File

@@ -0,0 +1,89 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_GEAR_JOINT_H
#define EXT_GEAR_JOINT_H
#include "extensions/PxGearJoint.h"
#include "ExtJoint.h"
#include "CmUtils.h"
namespace physx
{
struct PxGearJointGeneratedValues;
namespace Ext
{
struct GearJointData : public JointData
{
const PxBase* hingeJoint0; //either PxJoint or PxArticulationJointReducedCoordinate
const PxBase* hingeJoint1; //either PxJoint or PxArticulationJointReducedCoordinate
float gearRatio;
float error;
};
typedef JointT<PxGearJoint, GearJointData, PxGearJointGeneratedValues> GearJointT;
class GearJoint : public GearJointT
{
public:
// PX_SERIALIZATION
GearJoint(PxBaseFlags baseFlags) : GearJointT(baseFlags) {}
void resolveReferences(PxDeserializationContext& context);
static GearJoint* createObject(PxU8*& address, PxDeserializationContext& context) { return createJointObject<GearJoint>(address, context); }
//~PX_SERIALIZATION
GearJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1);
// PxGearJoint
virtual bool setHinges(const PxBase* hinge0, const PxBase* hinge1) PX_OVERRIDE;
virtual void getHinges(const PxBase*& hinge0, const PxBase*& hinge1) const PX_OVERRIDE;
virtual void setGearRatio(float ratio) PX_OVERRIDE;
virtual float getGearRatio() const PX_OVERRIDE;
//~PxGearJoint
// PxConstraintConnector
virtual void* prepareData() PX_OVERRIDE
{
updateError();
return mData;
}
virtual PxConstraintSolverPrep getPrep() const PX_OVERRIDE;
//~PxConstraintConnector
private:
float mVirtualAngle0;
float mVirtualAngle1;
float mPersistentAngle0;
float mPersistentAngle1;
bool mInitDone;
void updateError();
void resetError();
};
} // namespace Ext
}
#endif

View File

@@ -0,0 +1,284 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxGjkQueryExt.h"
#include "geometry/PxSphereGeometry.h"
#include "geometry/PxBoxGeometry.h"
#include "geometry/PxPlaneGeometry.h"
#include "geometry/PxCapsuleGeometry.h"
#include "geometry/PxConvexCoreGeometry.h"
#include "geometry/PxConvexMeshGeometry.h"
#include "foundation/PxAllocator.h"
#include "geomutils/PxContactBuffer.h"
#include "GuConvexSupport.h"
#include "GuConvexGeometry.h"
using namespace physx;
///////////////////////////////////////////////////////////////////////////////
PxGjkQueryExt::SphereSupport::SphereSupport() : radius(0.0f)
{
}
PxGjkQueryExt::SphereSupport::SphereSupport(PxReal _radius) : radius(_radius)
{
}
PxGjkQueryExt::SphereSupport::SphereSupport(const PxSphereGeometry& geom) : radius(geom.radius)
{
}
PxReal PxGjkQueryExt::SphereSupport::getMargin() const
{
return radius;
}
PxVec3 PxGjkQueryExt::SphereSupport::supportLocal(const PxVec3& /*dir*/) const
{
return PxVec3(0.0f);
}
///////////////////////////////////////////////////////////////////////////////
PxGjkQueryExt::CapsuleSupport::CapsuleSupport() : radius(0.0f), halfHeight(0.0f)
{
}
PxGjkQueryExt::CapsuleSupport::CapsuleSupport(PxReal _radius, PxReal _halfHeight) : radius(_radius), halfHeight(_halfHeight)
{
}
PxGjkQueryExt::CapsuleSupport::CapsuleSupport(const PxCapsuleGeometry& geom) : radius(geom.radius)
{
}
PxReal PxGjkQueryExt::CapsuleSupport::getMargin() const
{
return radius;
}
PxVec3 PxGjkQueryExt::CapsuleSupport::supportLocal(const PxVec3& dir) const
{
return PxVec3(PxSign2(dir.x) * halfHeight, 0.0f, 0.0f);
}
///////////////////////////////////////////////////////////////////////////////
PxGjkQueryExt::BoxSupport::BoxSupport() : halfExtents(0.0f), margin(0.0f)
{
}
PxGjkQueryExt::BoxSupport::BoxSupport(const PxVec3& _halfExtents, PxReal _margin) : halfExtents(_halfExtents), margin(_margin)
{
}
PxGjkQueryExt::BoxSupport::BoxSupport(const PxBoxGeometry& box, PxReal _margin) : halfExtents(box.halfExtents), margin(_margin)
{
}
PxReal PxGjkQueryExt::BoxSupport::getMargin() const
{
return margin;
}
PxVec3 PxGjkQueryExt::BoxSupport::supportLocal(const PxVec3& dir) const
{
const PxVec3 d = dir.getNormalized();
return PxVec3(PxSign2(d.x) * halfExtents.x, PxSign2(d.y) * halfExtents.y, PxSign2(d.z) * halfExtents.z);
}
///////////////////////////////////////////////////////////////////////////////
PxGjkQueryExt::ConvexCoreSupport::ConvexCoreSupport()
{
}
PxGjkQueryExt::ConvexCoreSupport::ConvexCoreSupport(const PxConvexCoreGeometry& geom, PxReal margin)
{
PX_COMPILE_TIME_ASSERT(sizeof(shapeData) >= sizeof(Gu::ConvexShape));
Gu::ConvexShape& convex = *reinterpret_cast<Gu::ConvexShape*>(shapeData);
Gu::makeConvexShape(geom, PxTransform(PxIdentity), convex);
convex.margin += margin;
}
PxReal PxGjkQueryExt::ConvexCoreSupport::getMargin() const
{
const Gu::ConvexShape& convex = *reinterpret_cast<const Gu::ConvexShape*>(shapeData);
return convex.margin;
}
PxVec3 PxGjkQueryExt::ConvexCoreSupport::supportLocal(const PxVec3& dir) const
{
const Gu::ConvexShape& convex = *reinterpret_cast<const Gu::ConvexShape*>(shapeData);
return convex.supportLocal(dir);
}
///////////////////////////////////////////////////////////////////////////////
PxGjkQueryExt::ConvexMeshSupport::ConvexMeshSupport() :
convexMesh (NULL),
scale (0.0f),
scaleRotation (0.0f),
margin (0.0f)
{
}
PxGjkQueryExt::ConvexMeshSupport::ConvexMeshSupport(const PxConvexMesh& _convexMesh, const PxVec3& _scale, const PxQuat& _scaleRotation, PxReal _margin) :
convexMesh (&_convexMesh),
scale (_scale),
scaleRotation (_scaleRotation),
margin (_margin)
{
}
PxGjkQueryExt::ConvexMeshSupport::ConvexMeshSupport(const PxConvexMeshGeometry& _convexMesh, PxReal _margin) :
convexMesh (_convexMesh.convexMesh),
scale (_convexMesh.scale.scale),
scaleRotation (_convexMesh.scale.rotation),
margin (_margin)
{
}
PxReal PxGjkQueryExt::ConvexMeshSupport::getMargin() const
{
return margin * scale.minElement();
}
PxVec3 PxGjkQueryExt::ConvexMeshSupport::supportLocal(const PxVec3& dir) const
{
if (convexMesh == NULL)
return PxVec3(0.0f);
PxVec3 d = scaleRotation.rotateInv(scaleRotation.rotate(dir).multiply(scale));
const PxVec3* verts = convexMesh->getVertices();
int count = int(convexMesh->getNbVertices());
float maxDot = -FLT_MAX;
int index = -1;
for (int i = 0; i < count; ++i)
{
float dot = verts[i].dot(d);
if (dot > maxDot)
{
maxDot = dot;
index = i;
}
}
if (index == -1)
return PxVec3(0);
return scaleRotation.rotateInv(scaleRotation.rotate(verts[index]).multiply(scale));
}
///////////////////////////////////////////////////////////////////////////////
PxGjkQueryExt::ConvexGeomSupport::ConvexGeomSupport() : mType(PxGeometryType::eINVALID)
{
}
PxGjkQueryExt::ConvexGeomSupport::ConvexGeomSupport(const PxGeometry& geom, PxReal margin)
{
mType = PxGeometryType::eINVALID;
switch (geom.getType())
{
case PxGeometryType::eSPHERE:
{
mType = PxGeometryType::eSPHERE;
const PxSphereGeometry& sphere = static_cast<const PxSphereGeometry&>(geom);
PX_PLACEMENT_NEW(&mSupport, SphereSupport(sphere.radius + margin));
break;
}
case PxGeometryType::eCAPSULE:
{
mType = PxGeometryType::eCAPSULE;
const PxCapsuleGeometry& capsule = static_cast<const PxCapsuleGeometry&>(geom);
PX_PLACEMENT_NEW(&mSupport, CapsuleSupport(capsule.radius + margin, capsule.halfHeight));
break;
}
case PxGeometryType::eBOX:
{
mType = PxGeometryType::eBOX;
PX_PLACEMENT_NEW(&mSupport, BoxSupport(static_cast<const PxBoxGeometry&>(geom), margin));
break;
}
case PxGeometryType::eCONVEXCORE:
{
mType = PxGeometryType::eCONVEXCORE;
PX_PLACEMENT_NEW(&mSupport, ConvexCoreSupport(static_cast<const PxConvexCoreGeometry&>(geom), margin));
break;
}
case PxGeometryType::eCONVEXMESH:
{
mType = PxGeometryType::eCONVEXMESH;
PX_PLACEMENT_NEW(&mSupport, ConvexMeshSupport(static_cast<const PxConvexMeshGeometry&>(geom), margin));
break;
}
default:
break;
}
}
PxGjkQueryExt::ConvexGeomSupport::~ConvexGeomSupport()
{
if (isValid())
reinterpret_cast<Support&>(mSupport).~Support();
}
bool PxGjkQueryExt::ConvexGeomSupport::isValid() const
{
return mType != PxGeometryType::eINVALID;
}
PxReal PxGjkQueryExt::ConvexGeomSupport::getMargin() const
{
return isValid() ? reinterpret_cast<const Support&>(mSupport).getMargin() : 0.0f;
}
PxVec3 PxGjkQueryExt::ConvexGeomSupport::supportLocal(const PxVec3& dir) const
{
return isValid() ? reinterpret_cast<const Support&>(mSupport).supportLocal(dir) : PxVec3(0.0f);
}
///////////////////////////////////////////////////////////////////////////////
bool PxGjkQueryExt::generateContacts(const PxGjkQuery::Support& a, const PxGjkQuery::Support& b, const PxTransform& poseA, const PxTransform& poseB, PxReal contactDistance, PxReal toleranceLength, PxContactBuffer& contactBuffer)
{
PxVec3 pointA, pointB, separatingAxis; PxReal separation;
if (!PxGjkQuery::proximityInfo(a, b, poseA, poseB, contactDistance, toleranceLength, pointA, pointB, separatingAxis, separation))
return false;
PxContactPoint contact;
contact.point = (pointA + pointB) * 0.5f; // VR: should I make it just pointB?
contact.normal = separatingAxis;
contact.separation = separation;
contactBuffer.contact(contact);
return true;
}

View File

@@ -0,0 +1,376 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_INERTIA_TENSOR_H
#define EXT_INERTIA_TENSOR_H
#include "foundation/PxMat33.h"
#include "foundation/PxMathUtils.h"
namespace physx
{
namespace Ext
{
class InertiaTensorComputer
{
public:
InertiaTensorComputer(bool initTozero = true);
InertiaTensorComputer(const PxMat33& inertia, const PxVec3& com, PxReal mass);
~InertiaTensorComputer();
PX_INLINE void zero(); //sets to zero mass
PX_INLINE void setDiagonal(PxReal mass, const PxVec3& diagonal); //sets as a diagonal tensor
PX_INLINE void rotate(const PxMat33& rot); //rotates the mass
void translate(const PxVec3& t); //translates the mass
PX_INLINE void transform(const PxTransform& transform); //transforms the mass
PX_INLINE void scaleDensity(PxReal densityScale); //scales by a density factor
PX_INLINE void add(const InertiaTensorComputer& it); //adds a mass
PX_INLINE void center(); //recenters inertia around center of mass
void setBox(const PxVec3& halfWidths); //sets as an axis aligned box
PX_INLINE void setBox(const PxVec3& halfWidths, const PxTransform* pose); //sets as an oriented box
void setSphere(PxReal radius);
PX_INLINE void setSphere(PxReal radius, const PxTransform* pose);
void setCylinder(int dir, PxReal r, PxReal l);
PX_INLINE void setCylinder(int dir, PxReal r, PxReal l, const PxTransform* pose);
void setCapsule(int dir, PxReal r, PxReal l);
PX_INLINE void setCapsule(int dir, PxReal r, PxReal l, const PxTransform* pose);
void setEllipsoid(PxReal rx, PxReal ry, PxReal rz);
PX_INLINE void setEllipsoid(PxReal rx, PxReal ry, PxReal rz, const PxTransform* pose);
PX_INLINE PxVec3 getCenterOfMass() const { return mG; }
PX_INLINE PxReal getMass() const { return mMass; }
PX_INLINE PxMat33 getInertia() const { return mI; }
private:
PxMat33 mI;
PxVec3 mG;
PxReal mMass;
};
//--------------------------------------------------------------
//
// Helper routines
//
//--------------------------------------------------------------
// Special version allowing 2D quads
PX_INLINE PxReal volume(const PxVec3& extents)
{
PxReal v = 1.0f;
if(extents.x != 0.0f) v*=extents.x;
if(extents.y != 0.0f) v*=extents.y;
if(extents.z != 0.0f) v*=extents.z;
return v;
}
// Sphere
PX_INLINE PxReal computeSphereRatio(PxReal radius) { return (4.0f/3.0f) * PxPi * radius * radius * radius; }
PxReal computeSphereMass(PxReal radius, PxReal density) { return density * computeSphereRatio(radius); }
PxReal computeSphereDensity(PxReal radius, PxReal mass) { return mass / computeSphereRatio(radius); }
// Box
PX_INLINE PxReal computeBoxRatio(const PxVec3& extents) { return volume(extents); }
PxReal computeBoxMass(const PxVec3& extents, PxReal density) { return density * computeBoxRatio(extents); }
PxReal computeBoxDensity(const PxVec3& extents, PxReal mass) { return mass / computeBoxRatio(extents); }
// Ellipsoid
PX_INLINE PxReal computeEllipsoidRatio(const PxVec3& extents) { return (4.0f/3.0f) * PxPi * volume(extents); }
PxReal computeEllipsoidMass(const PxVec3& extents, PxReal density) { return density * computeEllipsoidRatio(extents); }
PxReal computeEllipsoidDensity(const PxVec3& extents, PxReal mass) { return mass / computeEllipsoidRatio(extents); }
// Cylinder
PX_INLINE PxReal computeCylinderRatio(PxReal r, PxReal l) { return PxPi * r * r * (2.0f*l); }
PxReal computeCylinderMass(PxReal r, PxReal l, PxReal density) { return density * computeCylinderRatio(r, l); }
PxReal computeCylinderDensity(PxReal r, PxReal l, PxReal mass) { return mass / computeCylinderRatio(r, l); }
// Capsule
PX_INLINE PxReal computeCapsuleRatio(PxReal r, PxReal l) { return computeSphereRatio(r) + computeCylinderRatio(r, l);}
PxReal computeCapsuleMass(PxReal r, PxReal l, PxReal density) { return density * computeCapsuleRatio(r, l); }
PxReal computeCapsuleDensity(PxReal r, PxReal l, PxReal mass) { return mass / computeCapsuleRatio(r, l); }
// Cone
PX_INLINE PxReal computeConeRatio(PxReal r, PxReal l) { return PxPi * r * r * PxAbs(l)/3.0f; }
PxReal computeConeMass(PxReal r, PxReal l, PxReal density) { return density * computeConeRatio(r, l); }
PxReal computeConeDensity(PxReal r, PxReal l, PxReal mass) { return mass / computeConeRatio(r, l); }
void computeBoxInertiaTensor(PxVec3& inertia, PxReal mass, PxReal xlength, PxReal ylength, PxReal zlength);
void computeSphereInertiaTensor(PxVec3& inertia, PxReal mass, PxReal radius, bool hollow);
bool jacobiTransform(PxI32 n, PxF64 a[], PxF64 w[]);
bool diagonalizeInertiaTensor(const PxMat33& denseInertia, PxVec3& diagonalInertia, PxMat33& rotation);
} // namespace Ext
void Ext::computeBoxInertiaTensor(PxVec3& inertia, PxReal mass, PxReal xlength, PxReal ylength, PxReal zlength)
{
//to model a hollow block, one would have to multiply coeff by up to two.
const PxReal coeff = mass/12;
inertia.x = coeff * (ylength*ylength + zlength*zlength);
inertia.y = coeff * (xlength*xlength + zlength*zlength);
inertia.z = coeff * (xlength*xlength + ylength*ylength);
PX_ASSERT(inertia.x != 0.0f);
PX_ASSERT(inertia.y != 0.0f);
PX_ASSERT(inertia.z != 0.0f);
PX_ASSERT(inertia.isFinite());
}
void Ext::computeSphereInertiaTensor(PxVec3& inertia, PxReal mass, PxReal radius, bool hollow)
{
inertia.x = mass * radius * radius;
if (hollow)
inertia.x *= PxReal(2 / 3.0);
else
inertia.x *= PxReal(2 / 5.0);
inertia.z = inertia.y = inertia.x;
PX_ASSERT(inertia.isFinite());
}
//--------------------------------------------------------------
//
// InertiaTensorComputer implementation
//
//--------------------------------------------------------------
Ext::InertiaTensorComputer::InertiaTensorComputer(bool initTozero)
{
if (initTozero)
zero();
}
Ext::InertiaTensorComputer::InertiaTensorComputer(const PxMat33& inertia, const PxVec3& com, PxReal mass) :
mI(inertia),
mG(com),
mMass(mass)
{
}
Ext::InertiaTensorComputer::~InertiaTensorComputer()
{
}
PX_INLINE void Ext::InertiaTensorComputer::zero()
{
mMass = 0.0f;
mI = PxMat33(PxZero);
mG = PxVec3(0);
}
PX_INLINE void Ext::InertiaTensorComputer::setDiagonal(PxReal mass, const PxVec3& diag)
{
mMass = mass;
mI = PxMat33::createDiagonal(diag);
mG = PxVec3(0);
PX_ASSERT(mI.column0.isFinite() && mI.column1.isFinite() && mI.column2.isFinite());
PX_ASSERT(PxIsFinite(mMass));
}
void Ext::InertiaTensorComputer::setBox(const PxVec3& halfWidths)
{
// Setup inertia tensor for a cube with unit density
const PxReal mass = 8.0f * computeBoxRatio(halfWidths);
const PxReal s =(1.0f/3.0f) * mass;
const PxReal x = halfWidths.x*halfWidths.x;
const PxReal y = halfWidths.y*halfWidths.y;
const PxReal z = halfWidths.z*halfWidths.z;
setDiagonal(mass, PxVec3(y+z, z+x, x+y) * s);
}
PX_INLINE void Ext::InertiaTensorComputer::rotate(const PxMat33& rot)
{
//well known inertia tensor rotation expression is: RIR' -- this could be optimized due to symmetry, see code to do that in Body::updateGlobalInverseInertia
mI = rot * mI * rot.getTranspose();
PX_ASSERT(mI.column0.isFinite() && mI.column1.isFinite() && mI.column2.isFinite());
//com also needs to be rotated
mG = rot * mG;
PX_ASSERT(mG.isFinite());
}
void Ext::InertiaTensorComputer::translate(const PxVec3& t)
{
if (!t.isZero()) //its common for this to be zero
{
PxMat33 t1, t2;
t1.column0 = PxVec3(0, mG.z, -mG.y);
t1.column1 = PxVec3(-mG.z, 0, mG.x);
t1.column2 = PxVec3(mG.y, -mG.x, 0);
PxVec3 sum = mG + t;
if (sum.isZero())
{
mI += (t1 * t1)*mMass;
}
else
{
t2.column0 = PxVec3(0, sum.z, -sum.y);
t2.column1 = PxVec3(-sum.z, 0, sum.x);
t2.column2 = PxVec3(sum.y, -sum.x, 0);
mI += (t1 * t1 - t2 * t2)*mMass;
}
//move center of mass
mG += t;
PX_ASSERT(mI.column0.isFinite() && mI.column1.isFinite() && mI.column2.isFinite());
PX_ASSERT(mG.isFinite());
}
}
PX_INLINE void Ext::InertiaTensorComputer::transform(const PxTransform& transform)
{
rotate(PxMat33(transform.q));
translate(transform.p);
}
PX_INLINE void Ext::InertiaTensorComputer::setBox(const PxVec3& halfWidths, const PxTransform* pose)
{
setBox(halfWidths);
if (pose)
transform(*pose);
}
PX_INLINE void Ext::InertiaTensorComputer::scaleDensity(PxReal densityScale)
{
mI *= densityScale;
mMass *= densityScale;
PX_ASSERT(mI.column0.isFinite() && mI.column1.isFinite() && mI.column2.isFinite());
PX_ASSERT(PxIsFinite(mMass));
}
PX_INLINE void Ext::InertiaTensorComputer::add(const InertiaTensorComputer& it)
{
const PxReal TotalMass = mMass + it.mMass;
mG = (mG * mMass + it.mG * it.mMass) / TotalMass;
mMass = TotalMass;
mI += it.mI;
PX_ASSERT(mI.column0.isFinite() && mI.column1.isFinite() && mI.column2.isFinite());
PX_ASSERT(mG.isFinite());
PX_ASSERT(PxIsFinite(mMass));
}
PX_INLINE void Ext::InertiaTensorComputer::center()
{
PxVec3 center = -mG;
translate(center);
}
void Ext::InertiaTensorComputer::setSphere(PxReal radius)
{
// Compute mass of the sphere
const PxReal m = computeSphereRatio(radius);
// Compute moment of inertia
const PxReal s = m * radius * radius * (2.0f/5.0f);
setDiagonal(m,PxVec3(s,s,s));
}
PX_INLINE void Ext::InertiaTensorComputer::setSphere(PxReal radius, const PxTransform* pose)
{
setSphere(radius);
if (pose)
transform(*pose);
}
void Ext::InertiaTensorComputer::setCylinder(int dir, PxReal r, PxReal l)
{
// Compute mass of cylinder
const PxReal m = computeCylinderRatio(r, l);
const PxReal i1 = r*r*m/2.0f;
const PxReal i2 = (3.0f*r*r+4.0f*l*l)*m/12.0f;
switch(dir)
{
case 0: setDiagonal(m,PxVec3(i1,i2,i2)); break;
case 1: setDiagonal(m,PxVec3(i2,i1,i2)); break;
default: setDiagonal(m,PxVec3(i2,i2,i1)); break;
}
}
PX_INLINE void Ext::InertiaTensorComputer::setCylinder(int dir, PxReal r, PxReal l, const PxTransform* pose)
{
setCylinder(dir, r, l);
if (pose)
transform(*pose);
}
void Ext::InertiaTensorComputer::setCapsule(int dir, PxReal r, PxReal l)
{
// Compute mass of capsule
const PxReal m = computeCapsuleRatio(r, l);
const PxReal t = PxPi * r * r;
const PxReal i1 = t * ((r*r*r * 8.0f/15.0f) + (l*r*r));
const PxReal i2 = t * ((r*r*r * 8.0f/15.0f) + (l*r*r * 3.0f/2.0f) + (l*l*r * 4.0f/3.0f) + (l*l*l * 2.0f/3.0f));
switch(dir)
{
case 0: setDiagonal(m,PxVec3(i1,i2,i2)); break;
case 1: setDiagonal(m,PxVec3(i2,i1,i2)); break;
default: setDiagonal(m,PxVec3(i2,i2,i1)); break;
}
}
PX_INLINE void Ext::InertiaTensorComputer::setCapsule(int dir, PxReal r, PxReal l, const PxTransform* pose)
{
setCapsule(dir, r, l);
if (pose)
transform(*pose);
}
void Ext::InertiaTensorComputer::setEllipsoid(PxReal rx, PxReal ry, PxReal rz)
{
// Compute mass of ellipsoid
const PxReal m = computeEllipsoidRatio(PxVec3(rx, ry, rz));
// Compute moment of inertia
const PxReal s = m * (2.0f/5.0f);
// Setup inertia tensor for an ellipsoid centered at the origin
setDiagonal(m,PxVec3(ry*rz,rz*rx,rx*ry)*s);
}
PX_INLINE void Ext::InertiaTensorComputer::setEllipsoid(PxReal rx, PxReal ry, PxReal rz, const PxTransform* pose)
{
setEllipsoid(rx,ry,rz);
if (pose)
transform(*pose);
}
}
#endif

View File

@@ -0,0 +1,164 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "ExtJoint.h"
#include "omnipvd/ExtOmniPvdSetData.h"
using namespace physx;
using namespace Ext;
// PX_SERIALIZATION
PxConstraint* physx::resolveConstraintPtr(PxDeserializationContext& v, PxConstraint* old, PxConstraintConnector* connector, PxConstraintShaderTable &shaders)
{
v.translatePxBase(old);
PxConstraint* new_nx = static_cast<PxConstraint*>(old);
new_nx->setConstraintFunctions(*connector, shaders);
return new_nx;
}
//~PX_SERIALIZATION
static void normalToTangents(const PxVec3& n, PxVec3& t1, PxVec3& t2)
{
const PxReal m_sqrt1_2 = PxReal(0.7071067811865475244008443621048490);
if(fabsf(n.z) > m_sqrt1_2)
{
const PxReal a = n.y*n.y + n.z*n.z;
const PxReal k = PxReal(1.0)/PxSqrt(a);
t1 = PxVec3(0,-n.z*k,n.y*k);
t2 = PxVec3(a*k,-n.x*t1.z,n.x*t1.y);
}
else
{
const PxReal a = n.x*n.x + n.y*n.y;
const PxReal k = PxReal(1.0)/PxSqrt(a);
t1 = PxVec3(-n.y*k,n.x*k,0);
t2 = PxVec3(-n.z*t1.y,n.z*t1.x,a*k);
}
t1.normalize();
t2.normalize();
}
void PxSetJointGlobalFrame(PxJoint& joint, const PxVec3* wsAnchor, const PxVec3* axisIn)
{
PxRigidActor* actors[2];
joint.getActors(actors[0], actors[1]);
PxTransform localPose[2];
for(PxU32 i=0; i<2; i++)
localPose[i] = PxTransform(PxIdentity);
// 1) global anchor
if(wsAnchor)
{
//transform anchorPoint to local space
for(PxU32 i=0; i<2; i++)
localPose[i].p = actors[i] ? actors[i]->getGlobalPose().transformInv(*wsAnchor) : *wsAnchor;
}
// 2) global axis
if(axisIn)
{
PxVec3 localAxis[2], localNormal[2];
//find 2 orthogonal vectors.
//gotta do this in world space, if we choose them
//separately in local space they won't match up in worldspace.
PxVec3 axisw = *axisIn;
axisw.normalize();
PxVec3 normalw, binormalw;
::normalToTangents(axisw, binormalw, normalw);
//because axis is supposed to be the Z axis of a frame with the other two being X and Y, we need to negate
//Y to make the frame right handed. Note that the above call makes a right handed frame if we pass X --> Y,Z, so
//it need not be changed.
for(PxU32 i=0; i<2; i++)
{
if(actors[i])
{
const PxTransform& m = actors[i]->getGlobalPose();
PxMat33 mM(m.q);
localAxis[i] = mM.transformTranspose(axisw);
localNormal[i] = mM.transformTranspose(normalw);
}
else
{
localAxis[i] = axisw;
localNormal[i] = normalw;
}
PxMat33 rot(localAxis[i], localNormal[i], localAxis[i].cross(localNormal[i]));
localPose[i].q = PxQuat(rot);
localPose[i].q.normalize();
}
}
for(PxU32 i=0; i<2; i++)
joint.setLocalPose(static_cast<PxJointActorIndex::Enum>( i ), localPose[i]);
}
#if PX_SUPPORT_OMNI_PVD
void physx::Ext::omniPvdSetBaseJointParams(const PxJoint& joint, PxJointConcreteType::Enum cType)
{
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
const PxJoint& j = static_cast<const PxJoint&>(joint);
PxRigidActor* actors[2]; j.getActors(actors[0], actors[1]);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, actor0, j, actors[0])
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, actor1, j, actors[1])
PxConstraint* constraint = j.getConstraint();
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, constraint, j, constraint)
PxTransform actor0LocalPose = j.getLocalPose(PxJointActorIndex::eACTOR0);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, actor0LocalPose, j, actor0LocalPose)
PxTransform actor1LocalPose = j.getLocalPose(PxJointActorIndex::eACTOR1);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, actor1LocalPose, j, actor1LocalPose)
PxReal breakForce, breakTorque; j.getBreakForce(breakForce, breakTorque);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, breakForce, j, breakForce)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, breakTorque, j, breakTorque)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, constraintFlags, j, j.getConstraintFlags())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, invMassScale0, j, j.getInvMassScale0())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, invInertiaScale0, j, j.getInvInertiaScale0())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, invMassScale1, j, j.getInvMassScale1())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, invInertiaScale1, j, j.getInvInertiaScale1())
const char* name = j.getName() ? j.getName() : "";
PxU32 nameLen = PxU32(strlen(name)) + 1;
OMNI_PVD_SET_ARRAY_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, name, j, name, nameLen)
const char* typeName = j.getConcreteTypeName();
PxU32 typeNameLen = PxU32(strlen(typeName)) + 1;
OMNI_PVD_SET_ARRAY_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, concreteTypeName, j, typeName, typeNameLen)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, type, j, cType)
OMNI_PVD_WRITE_SCOPE_END
}
#endif

View File

@@ -0,0 +1,726 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_JOINT_H
#define EXT_JOINT_H
#include "PxPhysics.h"
#include "extensions/PxConstraintExt.h"
#include "PxRigidStatic.h"
#include "PxRigidDynamic.h"
#include "PxArticulationLink.h"
#include "PxArticulationReducedCoordinate.h"
#include "PxScene.h"
#include "foundation/PxAllocator.h"
#include "foundation/PxMathUtils.h"
#include "CmUtils.h"
#include "ExtJointData.h"
#if PX_SUPPORT_PVD
#include "pvd/PxPvdSceneClient.h"
#include "ExtPvd.h"
#include "PxPvdClient.h"
#endif
#include "omnipvd/ExtOmniPvdSetData.h"
namespace physx
{
// PX_SERIALIZATION
class PxDeserializationContext;
PxConstraint* resolveConstraintPtr(PxDeserializationContext& v, PxConstraint* old, PxConstraintConnector* connector, PxConstraintShaderTable& shaders);
// ~PX_SERIALIZATION
namespace Ext
{
PX_FORCE_INLINE float computeSwingAngle(float swingYZ, float swingW)
{
return 4.0f * PxAtan2(swingYZ, 1.0f + swingW); // tan (t/2) = sin(t)/(1+cos t), so this is the quarter angle
}
template<class JointType, class DataType>
PX_FORCE_INLINE JointType* createJointT(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1, const PxConstraintShaderTable& shaders)
{
JointType* j;
PX_NEW_SERIALIZED(j, JointType)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1);
if(!physics.createConstraint(actor0, actor1, *j, shaders, sizeof(DataType)))
PX_DELETE(j);
#if PX_SUPPORT_OMNI_PVD
if (j)
{
omniPvdInitJoint(*j);
}
#endif
return j;
}
// PX_SERIALIZATION
template<class JointType>
PX_FORCE_INLINE JointType* createJointObject(PxU8*& address, PxDeserializationContext& context)
{
JointType* obj = PX_PLACEMENT_NEW(address, JointType(PxBaseFlag::eIS_RELEASABLE));
address += sizeof(JointType);
obj->importExtraData(context);
obj->resolveReferences(context);
return obj;
}
//~PX_SERIALIZATION
template <class Base, class DataClass, class ValueStruct>
class JointT : public Base, public PxConstraintConnector, public PxUserAllocated
{
public:
// PX_SERIALIZATION
JointT(PxBaseFlags baseFlags) : Base(baseFlags) {}
void exportExtraData(PxSerializationContext& stream)
{
if(mData)
{
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(mData, sizeof(DataClass));
}
stream.writeName(mName);
}
void importExtraData(PxDeserializationContext& context)
{
if(mData)
mData = context.readExtraData<DataClass, PX_SERIAL_ALIGN>();
context.readName(mName);
}
virtual void preExportDataReset(){}
virtual void requiresObjects(PxProcessPxBaseCallback& c)
{
c.process(*mPxConstraint);
{
PxRigidActor* a0 = NULL;
PxRigidActor* a1 = NULL;
mPxConstraint->getActors(a0,a1);
if (a0)
{
c.process(*a0);
}
if (a1)
{
c.process(*a1);
}
}
}
//~PX_SERIALIZATION
#if PX_SUPPORT_PVD
// PxConstraintConnector
virtual bool updatePvdProperties(physx::pvdsdk::PvdDataStream& pvdConnection, const PxConstraint* c, PxPvdUpdateType::Enum updateType) const PX_OVERRIDE
{
if(updateType == PxPvdUpdateType::UPDATE_SIM_PROPERTIES)
{
Ext::Pvd::simUpdate<Base>(pvdConnection, *this);
return true;
}
else if(updateType == PxPvdUpdateType::UPDATE_ALL_PROPERTIES)
{
Ext::Pvd::updatePvdProperties<Base, ValueStruct>(pvdConnection, *this);
return true;
}
else if(updateType == PxPvdUpdateType::CREATE_INSTANCE)
{
Ext::Pvd::createPvdInstance<Base>(pvdConnection, *c, *this);
return true;
}
else if(updateType == PxPvdUpdateType::RELEASE_INSTANCE)
{
Ext::Pvd::releasePvdInstance(pvdConnection, *c, *this);
return true;
}
return false;
}
#else
virtual bool updatePvdProperties(physx::pvdsdk::PvdDataStream&, const PxConstraint*, PxPvdUpdateType::Enum) const PX_OVERRIDE
{
return false;
}
#endif
// PxConstraintConnector
virtual void updateOmniPvdProperties() const PX_OVERRIDE
{
}
// PxJoint
virtual void setActors(PxRigidActor* actor0, PxRigidActor* actor1) PX_OVERRIDE
{
//TODO SDK-DEV
//You can get the debugger stream from the NpScene
//Ext::Pvd::setActors( stream, this, mPxConstraint, actor0, actor1 );
PX_CHECK_AND_RETURN(actor0 != actor1, "PxJoint::setActors: actors must be different");
PX_CHECK_AND_RETURN((actor0 && !actor0->is<PxRigidStatic>()) || (actor1 && !actor1->is<PxRigidStatic>()), "PxJoint::setActors: at least one actor must be non-static");
#if PX_SUPPORT_PVD
PxScene* scene = getScene();
if(scene)
{
//if pvd not connect data stream is NULL
physx::pvdsdk::PvdDataStream* conn = scene->getScenePvdClient()->getClientInternal()->getDataStream();
if( conn != NULL )
Ext::Pvd::setActors(
*conn,
*this,
*mPxConstraint,
actor0,
actor1
);
}
#endif
mPxConstraint->setActors(actor0, actor1);
mData->c2b[0] = getCom(actor0).transformInv(mLocalPose[0]);
mData->c2b[1] = getCom(actor1).transformInv(mLocalPose[1]);
mPxConstraint->markDirty();
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, actor0, static_cast<PxJoint&>(*this), actor0)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, actor1, static_cast<PxJoint&>(*this), actor1)
OMNI_PVD_WRITE_SCOPE_END
}
// PxJoint
virtual void getActors(PxRigidActor*& actor0, PxRigidActor*& actor1) const PX_OVERRIDE
{
if(mPxConstraint)
mPxConstraint->getActors(actor0,actor1);
else
{
actor0 = NULL;
actor1 = NULL;
}
}
// this is the local pose relative to the actor, and we store internally the local
// pose relative to the body
// PxJoint
virtual void setLocalPose(PxJointActorIndex::Enum actor, const PxTransform& pose) PX_OVERRIDE
{
PX_CHECK_AND_RETURN(pose.isSane(), "PxJoint::setLocalPose: transform is invalid");
const PxTransform p = pose.getNormalized();
mLocalPose[actor] = p;
mData->c2b[actor] = getCom(actor).transformInv(p);
mPxConstraint->markDirty();
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, actor0LocalPose, static_cast<PxJoint&>(*this), mLocalPose[0])
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, actor1LocalPose, static_cast<PxJoint&>(*this), mLocalPose[1])
OMNI_PVD_WRITE_SCOPE_END
}
// PxJoint
virtual PxTransform getLocalPose(PxJointActorIndex::Enum actor) const PX_OVERRIDE
{
return mLocalPose[actor];
}
static PxTransform getGlobalPose(const PxRigidActor* actor)
{
if(!actor)
return PxTransform(PxIdentity);
return actor->getGlobalPose();
}
static void getActorVelocity(const PxRigidActor* actor, PxVec3& linear, PxVec3& angular)
{
if(!actor || actor->is<PxRigidStatic>())
{
linear = angular = PxVec3(0.0f);
return;
}
linear = static_cast<const PxRigidBody*>(actor)->getLinearVelocity();
angular = static_cast<const PxRigidBody*>(actor)->getAngularVelocity();
}
// PxJoint
virtual PxTransform getRelativeTransform() const PX_OVERRIDE
{
PxRigidActor* actor0, * actor1;
mPxConstraint->getActors(actor0, actor1);
const PxTransform t0 = getGlobalPose(actor0) * mLocalPose[0];
const PxTransform t1 = getGlobalPose(actor1) * mLocalPose[1];
return t0.transformInv(t1);
}
// PxJoint
virtual PxVec3 getRelativeLinearVelocity() const PX_OVERRIDE
{
PxRigidActor* actor0, * actor1;
PxVec3 l0, a0, l1, a1;
mPxConstraint->getActors(actor0, actor1);
const PxTransform actor0ToWorld = getGlobalPose(actor0);
const PxTransform actor1ToWorld = getGlobalPose(actor1);
const PxTransform body0ToActor = getCom(actor0);
const PxTransform body1ToActor = getCom(actor1);
getActorVelocity(actor0, l0, a0);
getActorVelocity(actor1, l1, a1);
// the offset from center of mass to joint frame origin (in the
// actor frame)
const PxVec3 jointFrame0CoMOffsetLocal = mLocalPose[0].p - body0ToActor.p;
const PxVec3 jointFrame1CoMOffsetLocal = mLocalPose[1].p - body1ToActor.p;
const PxVec3 jointFrame0CoMOffsetWorld = actor0ToWorld.rotate(jointFrame0CoMOffsetLocal);
const PxVec3 jointFrame1CoMOffsetWorld = actor1ToWorld.rotate(jointFrame1CoMOffsetLocal);
const PxVec3 relativeVelWorld = (l1 + a1.cross(jointFrame1CoMOffsetWorld)) -
(l0 + a0.cross(jointFrame0CoMOffsetWorld));
const PxQuat jointFrame0ToWorld = actor0ToWorld.q * mLocalPose[0].q;
const PxVec3 relativeVelJointFrame0 = jointFrame0ToWorld.rotateInv(relativeVelWorld);
return relativeVelJointFrame0;
}
// PxJoint
virtual PxVec3 getRelativeAngularVelocity() const PX_OVERRIDE
{
PxRigidActor* actor0, * actor1;
PxVec3 l0, a0, l1, a1;
mPxConstraint->getActors(actor0, actor1);
const PxTransform actor0ToWorld = getGlobalPose(actor0);
getActorVelocity(actor0, l0, a0);
getActorVelocity(actor1, l1, a1);
const PxVec3 relativeVelWorld = a1 - a0;
const PxQuat jointFrame0ToWorld = actor0ToWorld.q * mLocalPose[0].q;
const PxVec3 relativeVelJointFrame0 = jointFrame0ToWorld.rotateInv(relativeVelWorld);
return relativeVelJointFrame0;
}
// PxJoint
virtual void setBreakForce(PxReal force, PxReal torque) PX_OVERRIDE
{
PX_CHECK_AND_RETURN(PxIsFinite(force) && PxIsFinite(torque), "PxJoint::setBreakForce: invalid float");
mPxConstraint->setBreakForce(force,torque);
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, breakForce, static_cast<PxJoint&>(*this), force)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxJoint, breakTorque, static_cast<PxJoint&>(*this), torque)
OMNI_PVD_WRITE_SCOPE_END
}
// PxJoint
virtual void getBreakForce(PxReal& force, PxReal& torque) const PX_OVERRIDE
{
mPxConstraint->getBreakForce(force,torque);
}
// PxJoint
virtual void setConstraintFlags(PxConstraintFlags flags) PX_OVERRIDE
{
mPxConstraint->setFlags(flags);
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxJoint, constraintFlags, static_cast<PxJoint&>(*this), flags)
}
// PxJoint
virtual void setConstraintFlag(PxConstraintFlag::Enum flag, bool value) PX_OVERRIDE
{
mPxConstraint->setFlag(flag, value);
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxJoint, constraintFlags, static_cast<PxJoint&>(*this), getConstraintFlags())
}
// PxJoint
virtual PxConstraintFlags getConstraintFlags() const PX_OVERRIDE
{
return mPxConstraint->getFlags();
}
// PxJoint
virtual void setInvMassScale0(PxReal invMassScale) PX_OVERRIDE
{
PX_CHECK_AND_RETURN(PxIsFinite(invMassScale) && invMassScale>=0, "PxJoint::setInvMassScale0: scale must be non-negative");
mData->invMassScale.linear0 = invMassScale;
mPxConstraint->markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxJoint, invMassScale0, static_cast<PxJoint&>(*this), invMassScale)
}
// PxJoint
virtual PxReal getInvMassScale0() const PX_OVERRIDE
{
return mData->invMassScale.linear0;
}
// PxJoint
virtual void setInvInertiaScale0(PxReal invInertiaScale) PX_OVERRIDE
{
PX_CHECK_AND_RETURN(PxIsFinite(invInertiaScale) && invInertiaScale>=0, "PxJoint::setInvInertiaScale0: scale must be non-negative");
mData->invMassScale.angular0 = invInertiaScale;
mPxConstraint->markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxJoint, invInertiaScale0, static_cast<PxJoint&>(*this), invInertiaScale)
}
// PxJoint
virtual PxReal getInvInertiaScale0() const PX_OVERRIDE
{
return mData->invMassScale.angular0;
}
// PxJoint
virtual void setInvMassScale1(PxReal invMassScale) PX_OVERRIDE
{
PX_CHECK_AND_RETURN(PxIsFinite(invMassScale) && invMassScale>=0, "PxJoint::setInvMassScale1: scale must be non-negative");
mData->invMassScale.linear1 = invMassScale;
mPxConstraint->markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxJoint, invMassScale1, static_cast<PxJoint&>(*this), invMassScale)
}
// PxJoint
virtual PxReal getInvMassScale1() const PX_OVERRIDE
{
return mData->invMassScale.linear1;
}
// PxJoint
virtual void setInvInertiaScale1(PxReal invInertiaScale) PX_OVERRIDE
{
PX_CHECK_AND_RETURN(PxIsFinite(invInertiaScale) && invInertiaScale>=0, "PxJoint::setInvInertiaScale: scale must be non-negative");
mData->invMassScale.angular1 = invInertiaScale;
mPxConstraint->markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxJoint, invInertiaScale1, static_cast<PxJoint&>(*this), invInertiaScale)
}
// PxJoint
virtual PxReal getInvInertiaScale1() const PX_OVERRIDE
{
return mData->invMassScale.angular1;
}
// PxJoint
virtual PxConstraint* getConstraint() const PX_OVERRIDE
{
return mPxConstraint;
}
// PxJoint
virtual void setName(const char* name) PX_OVERRIDE
{
mName = name;
#if PX_SUPPORT_OMNI_PVD
const char* n = name ? name : "";
PxU32 nLen = PxU32(strlen(n)) + 1;
OMNI_PVD_SET_ARRAY(OMNI_PVD_CONTEXT_HANDLE, PxJoint, name, static_cast<PxJoint&>(*this), n, nLen)
#endif
}
// PxJoint
virtual const char* getName() const PX_OVERRIDE
{
return mName;
}
// PxJoint
virtual void release() PX_OVERRIDE
{
mPxConstraint->release();
}
// PxJoint
virtual PxScene* getScene() const PX_OVERRIDE
{
return mPxConstraint ? mPxConstraint->getScene() : NULL;
}
// PxConstraintConnector
virtual void onComShift(PxU32 actor) PX_OVERRIDE
{
mData->c2b[actor] = getCom(actor).transformInv(mLocalPose[actor]);
markDirty();
}
// PxConstraintConnector
virtual void onOriginShift(const PxVec3& shift) PX_OVERRIDE
{
PxRigidActor* a[2];
mPxConstraint->getActors(a[0], a[1]);
if (!a[0])
{
mLocalPose[0].p -= shift;
mData->c2b[0].p -= shift;
markDirty();
}
else if (!a[1])
{
mLocalPose[1].p -= shift;
mData->c2b[1].p -= shift;
markDirty();
}
}
// PxConstraintConnector
virtual void* prepareData() PX_OVERRIDE
{
return mData;
}
// PxConstraintConnector
virtual void* getExternalReference(PxU32& typeID) PX_OVERRIDE
{
typeID = PxConstraintExtIDs::eJOINT;
return static_cast<PxJoint*>( this );
}
// PxConstraintConnector
virtual PxBase* getSerializable() PX_OVERRIDE
{
return this;
}
// PxConstraintConnector
virtual void onConstraintRelease() PX_OVERRIDE
{
PX_FREE(mData);
PX_DELETE_THIS;
}
// PxConstraintConnector
virtual const void* getConstantBlock() const PX_OVERRIDE
{
return mData;
}
virtual void connectToConstraint(PxConstraint* c) PX_OVERRIDE
{
mPxConstraint = c;
}
private:
PxTransform getCom(PxU32 index) const
{
PxRigidActor* a[2];
mPxConstraint->getActors(a[0],a[1]);
return getCom(a[index]);
}
PxTransform getCom(PxRigidActor* actor) const
{
if (!actor)
return PxTransform(PxIdentity);
else if (actor->getType() == PxActorType::eRIGID_DYNAMIC || actor->getType() == PxActorType::eARTICULATION_LINK)
return static_cast<PxRigidBody*>(actor)->getCMassLocalPose();
else
{
PX_ASSERT(actor->getType() == PxActorType::eRIGID_STATIC);
return static_cast<PxRigidStatic*>(actor)->getGlobalPose().getInverse();
}
}
protected:
JointT(PxType concreteType, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1, const char* name) :
Base (concreteType, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE),
mName (NULL),
mPxConstraint (NULL)
{
PX_UNUSED(name);
Base::userData = NULL;
const PxU32 size = sizeof(DataClass);
JointData* data = reinterpret_cast<JointData*>(PX_ALLOC(size, name));
PxMarkSerializedMemory(data, size);
mLocalPose[0] = localFrame0.getNormalized();
mLocalPose[1] = localFrame1.getNormalized();
data->c2b[0] = getCom(actor0).transformInv(mLocalPose[0]);
data->c2b[1] = getCom(actor1).transformInv(mLocalPose[1]);
data->invMassScale.linear0 = 1.0f;
data->invMassScale.angular0 = 1.0f;
data->invMassScale.linear1 = 1.0f;
data->invMassScale.angular1 = 1.0f;
mData = data;
}
virtual ~JointT()
{
if(Base::getBaseFlags() & PxBaseFlag::eOWNS_MEMORY)
PX_FREE(mData);
OMNI_PVD_DESTROY(OMNI_PVD_CONTEXT_HANDLE, PxJoint, static_cast<Base&>(*this))
}
PX_FORCE_INLINE DataClass& data() const
{
return *static_cast<DataClass*>(mData);
}
PX_FORCE_INLINE void markDirty()
{
mPxConstraint->markDirty();
}
void wakeUpActors()
{
PxRigidActor* a[2];
mPxConstraint->getActors(a[0], a[1]);
for(PxU32 i = 0; i < 2; i++)
{
if(a[i] && a[i]->getScene())
{
if(a[i]->getType() == PxActorType::eRIGID_DYNAMIC)
{
PxRigidDynamic* rd = static_cast<PxRigidDynamic*>(a[i]);
if(!(rd->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC))
{
const PxScene* scene = rd->getScene();
const PxReal wakeCounterResetValue = scene->getWakeCounterResetValue();
const PxReal wakeCounter = rd->getWakeCounter();
if(wakeCounter < wakeCounterResetValue)
{
rd->wakeUp();
}
}
}
else if(a[i]->getType() == PxActorType::eARTICULATION_LINK)
{
PxArticulationLink* link = reinterpret_cast<PxArticulationLink*>(a[i]);
PxArticulationReducedCoordinate& articulation = link->getArticulation();
const PxReal wakeCounter = articulation.getWakeCounter();
const PxScene* scene = link->getScene();
const PxReal wakeCounterResetValue = scene->getWakeCounterResetValue();
if(wakeCounter < wakeCounterResetValue)
{
articulation.wakeUp();
}
}
}
}
}
PX_FORCE_INLINE PxQuat getTwistOrSwing(bool needTwist) const
{
const PxQuat q = getRelativeTransform().q;
// PT: TODO: we don't need to compute both quats here
PxQuat swing, twist;
PxSeparateSwingTwist(q, swing, twist);
return needTwist ? twist : swing;
}
PxReal getTwistAngle_Internal() const
{
const PxQuat twist = getTwistOrSwing(true);
// PT: the angle-axis formulation creates the quat like this:
//
// const float a = angleRadians * 0.5f;
// const float s = PxSin(a);
// w = PxCos(a);
// x = unitAxis.x * s;
// y = unitAxis.y * s;
// z = unitAxis.z * s;
//
// With the twist axis = (1;0;0) this gives:
//
// w = PxCos(angleRadians * 0.5f);
// x = PxSin(angleRadians * 0.5f);
// y = 0.0f;
// z = 0.0f;
//
// Thus the quat's "getAngle" function returns:
//
// angle = PxAcos(w) * 2.0f;
//
// PxAcos will return an angle between 0 and PI in radians, so "getAngle" will return an angle between 0 and PI*2.
PxReal angle = twist.getAngle();
if(twist.x<0.0f)
angle = -angle;
return angle;
}
PxReal getSwingYAngle_Internal() const
{
PxQuat swing = getTwistOrSwing(false);
if(swing.w < 0.0f) // choose the shortest rotation
swing = -swing;
const PxReal angle = computeSwingAngle(swing.y, swing.w);
PX_ASSERT(angle>-PxPi && angle<=PxPi); // since |y| < w+1, the atan magnitude is < PI/4
return angle;
}
PxReal getSwingZAngle_Internal() const
{
PxQuat swing = getTwistOrSwing(false);
if(swing.w < 0.0f) // choose the shortest rotation
swing = -swing;
const PxReal angle = computeSwingAngle(swing.z, swing.w);
PX_ASSERT(angle>-PxPi && angle <= PxPi); // since |y| < w+1, the atan magnitude is < PI/4
return angle;
}
const char* mName;
PxTransform mLocalPose[2];
PxConstraint* mPxConstraint;
JointData* mData;
};
#if PX_SUPPORT_OMNI_PVD
void omniPvdSetBaseJointParams(const PxJoint& joint, PxJointConcreteType::Enum cType);
template<typename JointType> void omniPvdInitJoint(JointType& joint);
#endif
} // namespace Ext
}
#endif

View File

@@ -0,0 +1,49 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_JOINT_DATA_H
#define EXT_JOINT_DATA_H
#include "extensions/PxJointLimit.h"
namespace physx
{
namespace Ext
{
struct JointData
{
PxConstraintInvMassScale invMassScale;
PxTransform32 c2b[2];
protected:
~JointData() {}
};
} // namespace Ext
}
#endif

View File

@@ -0,0 +1,64 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_JOINT_META_DATA_EXTENSIONS_H
#define EXT_JOINT_META_DATA_EXTENSIONS_H
#include "PvdMetaDataExtensions.h"
namespace physx
{
namespace pvdsdk
{
struct PxExtensionPvdOnlyProperties
{
enum Enum
{
FirstProp = PxExtensionsPropertyInfoName::LastPxPropertyInfoName,
DEFINE_ENUM_RANGE( PxJoint_Actors, 2 ),
DEFINE_ENUM_RANGE( PxJoint_BreakForce, 2 ),
DEFINE_ENUM_RANGE( PxD6Joint_DriveVelocity, 2 ),
DEFINE_ENUM_RANGE( PxD6Joint_Motion, PxD6Axis::eCOUNT ),
DEFINE_ENUM_RANGE( PxD6Joint_Drive, PxD6Drive::eCOUNT * ( PxExtensionsPropertyInfoName::PxD6JointDrive_PropertiesStop - PxExtensionsPropertyInfoName::PxD6JointDrive_PropertiesStart ) ),
DEFINE_ENUM_RANGE( PxD6Joint_LinearLimit, 100 ),
DEFINE_ENUM_RANGE( PxD6Joint_SwingLimit, 100 ),
DEFINE_ENUM_RANGE( PxD6Joint_TwistLimit, 100 ),
DEFINE_ENUM_RANGE( PxPrismaticJoint_Limit, 100 ),
DEFINE_ENUM_RANGE( PxRevoluteJoint_Limit, 100 ),
DEFINE_ENUM_RANGE( PxSphericalJoint_LimitCone, 100 ),
DEFINE_ENUM_RANGE( PxJoint_LocalPose, 2 )
};
};
}
}
#endif

View File

@@ -0,0 +1,464 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxParticleClothCooker.h"
#include "foundation/PxArray.h"
#include "foundation/PxSort.h"
#include "foundation/PxHashMap.h"
#include "foundation/PxHashSet.h"
#include "GuInternal.h"
namespace physx
{
namespace ExtGpu
{
namespace
{
struct Edge
{
Edge(PxU32 v0, PxU32 v1, PxU32 triangle0, PxU32 triangle1, bool isLongest0, bool isLongest1)
{
a = PxMin(v0, v1);
b = PxMax(v0, v1);
triangleA = triangle0;
triangleB = triangle1;
longestA = isLongest0;
longestB = isLongest1;
PX_ASSERT_WITH_MESSAGE(a != b, "PxCreateInflatableFromMesh encountered a degenerate edge inside a triangle.");
}
PxU32 a, b;
PxU32 triangleA, triangleB;
bool longestA, longestB; //true if it is the longest edge of triangle
bool operator<(const Edge& other) const
{
if(a == other.a)
return b < other.b;
return a < other.a;
}
bool operator==(const Edge& other) const
{
if(a == other.a)
return b == other.b;
return false;
}
};
class EdgeHash
{
public:
uint32_t operator()(const Edge& e) const
{
return PxComputeHash(e.a) ^ PxComputeHash(e.b);
}
bool equal(const Edge& e0, const Edge& e1) const
{
return e0.a == e1.a && e0.b == e1.b;
}
};
class EdgeSet : public PxHashSet<Edge, EdgeHash>
{
public:
typedef PxHashSet<Edge, EdgeHash> Base;
typedef Base::Iterator Iterator;
void insert(const Edge& newEdge)
{
PX_ASSERT(newEdge.a < newEdge.b);
bool exists;
Edge* edge = mBase.create(newEdge, exists);
if (!exists)
{
PX_PLACEMENT_NEW(edge, Edge)(newEdge);
}
else
{
PX_ASSERT_WITH_MESSAGE(edge->triangleB == 0xffffffff, "Edge with more than 2 triangles found in PxCreateInflatableFromMesh");
edge->triangleB = newEdge.triangleA; //Add triangle info from duplicate to Unique edge
edge->longestB = newEdge.longestA;
}
}
};
template<typename T>
void partialSum(T* begin, T* end, T* dest)
{
*dest = *begin;
dest++;
begin++;
while(begin!=end)
{
*dest = dest[-1] + *begin;
dest++;
begin++;
}
}
/*
outAdjacencies is a list of adjacent vertices in outAdjacencies
outAdjacencyIndices is a list of indices to quickly find adjacent vertices in outAdjacencies.
all the adjacent vertices to vertex V are stored in outAdjacencies starting at outAdjacencyIndices[V] and ending at outAdjacencyIndices[V+1]
so the first vertex is outAdjacencies[outAdjacencyIndices[V]], and the last one is outAdjacencies[outAdjacencyIndices[V+1]-1]
*/
void gatherAdjacencies(PxArray<PxU32>& outAdjacencyIndices, PxArray<PxU32>& outAdjacencies,
PxU32 vertexCount, PxArray<Edge> const& inUniqueEdges, bool ignoreDiagonals = false
)
{
PX_ASSERT(outAdjacencyIndices.size() == 0);
PX_ASSERT(outAdjacencies.size() == 0);
outAdjacencyIndices.resize(vertexCount+1, 0);
//calculate valency
for(PxU32 i = 0; i < inUniqueEdges.size(); i++)
{
const Edge& edge = inUniqueEdges[i];
if(ignoreDiagonals && edge.longestA && edge.longestB)
continue;
outAdjacencyIndices[edge.a]++;
outAdjacencyIndices[edge.b]++;
}
partialSum(outAdjacencyIndices.begin(), outAdjacencyIndices.end(), outAdjacencyIndices.begin());
outAdjacencyIndices.back() = outAdjacencyIndices[vertexCount-1];
outAdjacencies.resize(outAdjacencyIndices.back(),0xffffffff);
for(PxU32 i = 0; i < inUniqueEdges.size(); i++)
{
const Edge& edge = inUniqueEdges[i];
if(ignoreDiagonals && edge.longestA && edge.longestB)
continue;
outAdjacencyIndices[edge.a]--;
outAdjacencies[outAdjacencyIndices[edge.a]]=edge.b;
outAdjacencyIndices[edge.b]--;
outAdjacencies[outAdjacencyIndices[edge.b]] = edge.a;
}
}
template <typename T, typename A>
A MaxArg(T const& vA, T const& vB, A const& aA, A const& aB)
{
if(vA > vB)
return aA;
return aB;
}
PxU32 GetOppositeVertex(PxU32* inTriangleIndices, PxU32 triangleIndex, PxU32 a, PxU32 b)
{
for(int i = 0; i<3; i++)
{
if(inTriangleIndices[triangleIndex+i] != a && inTriangleIndices[triangleIndex + i] !=b)
return inTriangleIndices[triangleIndex + i];
}
PX_ASSERT_WITH_MESSAGE(0, "Degenerate Triangle found in PxCreateInflatableFromMesh");
return 0;
}
Edge GetAlternateDiagonal(Edge const& edge, PxU32* inTriangleIndices)
{
PxU32 vA = GetOppositeVertex(inTriangleIndices, edge.triangleA, edge.a, edge.b);
PxU32 vB = GetOppositeVertex(inTriangleIndices, edge.triangleB, edge.a, edge.b);
bool longestA = true;
bool longestB = true;
PxU32 tA = 0xffffffff;
PxU32 tB = 0xffffffff;
return Edge(vA, vB, tA, tB, longestA, longestB);
}
} //namespace
class PxParticleClothCookerImpl : public PxParticleClothCooker, public PxUserAllocated
{
public:
PxParticleClothCookerImpl(PxU32 vertexCount, physx::PxVec4* inVertices, PxU32 triangleIndexCount, PxU32* inTriangleIndices,
PxU32 constraintTypeFlags, PxVec3 verticalDirection, PxReal bendingConstraintMaxAngle)
:
mVertexCount(vertexCount),
mVertices(inVertices),
mTriangleIndexCount(triangleIndexCount),
mTriangleIndices(inTriangleIndices),
mConstraintTypeFlags(constraintTypeFlags),
mVerticalDirection(verticalDirection),
mBendingConstraintMaxAngle(bendingConstraintMaxAngle)
{
}
virtual void release()
{
PX_DELETE_THIS;
}
/**
\brief generate the constraint and triangle per vertex information.
*/
virtual void cookConstraints(const PxParticleClothConstraint* constraints, const PxU32 numConstraints);
virtual PxU32* getTriangleIndices() { return mTriangleIndexBuffer.begin(); }
virtual PxU32 getTriangleIndicesCount() { return mTriangleIndexBuffer.size(); }
virtual PxParticleClothConstraint* getConstraints() { return mConstraintBuffer.begin(); }
virtual PxU32 getConstraintCount() { return mConstraintBuffer.size(); }
/**
\brief Computes the volume of a closed mesh and the contraintScale. Expects vertices in local space - 'close' to origin.
*/
virtual void calculateMeshVolume();
virtual float getMeshVolume() {return mMeshVolume;}
private:
PxArray<PxU32> mTriangleIndexBuffer;
PxArray<PxParticleClothConstraint> mConstraintBuffer;
PxU32 mVertexCount;
physx::PxVec4* mVertices; //we don't own this
PxU32 mTriangleIndexCount;
PxU32* mTriangleIndices; //we don't own this
PxU32 mConstraintTypeFlags;
PxVec3 mVerticalDirection;
float mBendingConstraintMaxAngle;
float mMeshVolume;
void addTriangle(PxArray<PxU32>& trianglesPerVertex, PxU32 triangleIndex)
{
for(int j = 0; j < 3; j++)
{
PxU32 vertexIndex = mTriangleIndices[triangleIndex + j];
mTriangleIndexBuffer.pushBack(vertexIndex);
trianglesPerVertex[vertexIndex]++;
}
}
};
void PxParticleClothCookerImpl::cookConstraints(const PxParticleClothConstraint* constraints, const PxU32 numConstraints)
{
EdgeSet edgeSet;
edgeSet.reserve(mTriangleIndexCount);
PxArray<PxU32> trianglesPerVertex;
trianglesPerVertex.resize(mVertexCount, 0);
mTriangleIndexBuffer.clear();
mTriangleIndexBuffer.reserve(mTriangleIndexCount);
mConstraintBuffer.clear();
mConstraintBuffer.reserve(mVertexCount*12);
//Add all edges to Edges
for(PxU32 i = 0; i<mTriangleIndexCount; i+=3)
{
//Get vertex indices
PxU32 v0 = mTriangleIndices[i + 0];
PxU32 v1 = mTriangleIndices[i + 1];
PxU32 v2 = mTriangleIndices[i + 2];
//Get vertex points
PxVec3 p0 = mVertices[v0].getXYZ();
PxVec3 p1 = mVertices[v1].getXYZ();
PxVec3 p2 = mVertices[v2].getXYZ();
//check which edge is the longest
float len0 = (p0 - p1).magnitude();
float len1 = (p1 - p2).magnitude();
float len2 = (p2 - p0).magnitude();
int longest = MaxArg(len0, PxMax(len1,len2), 0, MaxArg(len1, len2, 1, 2));
//Store edges
edgeSet.insert(Edge(v0, v1, i, 0xffffffff, longest == 0, false));
edgeSet.insert(Edge(v1, v2, i, 0xffffffff, longest == 1, false));
edgeSet.insert(Edge(v2, v0, i, 0xffffffff, longest == 2, false));
//Add triangle to mTriangleIndexBuffer and increment trianglesPerVertex values
addTriangle(trianglesPerVertex,i);
}
if (constraints)
{
//skip constraints cooking if provided by user
mConstraintBuffer.assign(constraints, constraints + numConstraints);
return;
}
trianglesPerVertex.clear();
trianglesPerVertex.shrink();
PxArray<Edge> uniqueEdges;
uniqueEdges.reserve(mTriangleIndexCount); //over allocate to avoid resizes
for (EdgeSet::Iterator iter = edgeSet.getIterator(); !iter.done(); ++iter)
{
const Edge& e = *iter;
uniqueEdges.pushBack(e);
}
//Maximum angle before it is a horizontal constraint
const float cosAngle45 = cosf(45.0f / 360.0f * PxTwoPi);
//Add all horizontal, vertical and shearing constraints
PxU32 constraintCount = uniqueEdges.size(); //we are going to push back more edges, but we don't need to process them
for(PxU32 i = 0; i < constraintCount; i++)
{
const Edge& edge = uniqueEdges[i];
PxParticleClothConstraint c;
c.particleIndexA = edge.a;
c.particleIndexB = edge.b;
//Get vertices's
PxVec3 vA = mVertices[c.particleIndexA].getXYZ();
PxVec3 vB = mVertices[c.particleIndexB].getXYZ();
//Calculate rest length
c.length = (vA - vB).magnitude();
if(edge.longestA && edge.longestB && (mConstraintTypeFlags & PxParticleClothConstraint::eTYPE_DIAGONAL_CONSTRAINT))
{
//Shearing constraint
c.constraintType = c.eTYPE_DIAGONAL_CONSTRAINT;
//add constraint
mConstraintBuffer.pushBack(c);
//We only have one of the quad diagonals in a triangle mesh, get the other one here
const Edge alternateEdge = GetAlternateDiagonal(edge, mTriangleIndices);
c.particleIndexA = alternateEdge.a;
c.particleIndexB = alternateEdge.b;
//Get vertices's
PxVec3 vA2 = mVertices[c.particleIndexA].getXYZ();
PxVec3 vB2 = mVertices[c.particleIndexB].getXYZ();
//Calculate rest length
c.length = (vA2 - vB2).magnitude();
//add constraint
mConstraintBuffer.pushBack(c);
if (mConstraintTypeFlags & PxParticleClothConstraint::eTYPE_DIAGONAL_BENDING_CONSTRAINT)
{
if (!edgeSet.contains(alternateEdge))
{
edgeSet.insert(alternateEdge);
uniqueEdges.pushBack(alternateEdge); //Add edge for bending constraint step
}
}
}
else
{
//horizontal/vertical constraint
PxVec3 dir = (vA - vB) / c.length;
if(mVerticalDirection.dot(dir)> cosAngle45)
c.constraintType = c.eTYPE_VERTICAL_CONSTRAINT;
else
c.constraintType = c.eTYPE_HORIZONTAL_CONSTRAINT;
if(mConstraintTypeFlags & c.constraintType)
{
//add constraint
mConstraintBuffer.pushBack(c);
}
}
}
if(!(mConstraintTypeFlags & PxParticleClothConstraint::eTYPE_BENDING_CONSTRAINT))
return;
//Get adjacency information needed for the bending constraints
PxArray<PxU32> adjacencyIndices;
PxArray<PxU32> adjacencies;
gatherAdjacencies(adjacencyIndices, adjacencies, mVertexCount, uniqueEdges, !(mConstraintTypeFlags & PxParticleClothConstraint::eTYPE_DIAGONAL_BENDING_CONSTRAINT));
//Maximum angle we consider to be parallel for the bending constraints
const float maxCosAngle = PxCos(mBendingConstraintMaxAngle);
for(PxU32 i = 0; i<mVertexCount; i++)
{
//For each vertex, find all adjacent vertex pairs, and add bending constraints for pairs that form roughly a straight line
PxVec3 center = mVertices[i].getXYZ();
for(PxU32 adjA = adjacencyIndices[i]; PxI32(adjA) < PxI32(adjacencyIndices[i+1])-1; adjA++)
{
PxVec3 a = mVertices[adjacencies[adjA]].getXYZ();
PxVec3 dir1 = (a-center).getNormalized();
float bestCosAngle = -1.0f;
PxU32 bestAdjB = 0xffffffff;
//Choose the most parallel adjB
for(PxU32 adjB = adjA+1; adjB < adjacencyIndices[i + 1]; adjB++)
{
PxVec3 b = mVertices[adjacencies[adjB]].getXYZ();
PxVec3 dir2 = (b - center).getNormalized();
float cosAngleAbs = PxAbs(dir1.dot(dir2));
if(cosAngleAbs > bestCosAngle)
{
bestCosAngle = cosAngleAbs;
bestAdjB = adjB;
}
}
//Check if the lines a-center and center-b are roughly parallel
if(bestCosAngle > maxCosAngle)
{
//Add bending constraint
PxParticleClothConstraint c;
c.particleIndexA = adjacencies[adjA];
c.particleIndexB = adjacencies[bestAdjB];
PX_ASSERT(c.particleIndexA != c.particleIndexB);
//Get vertices's
PxVec3 vA = mVertices[c.particleIndexA].getXYZ();
PxVec3 vB = mVertices[c.particleIndexB].getXYZ();
//Calculate rest length
c.length = (vA - vB).magnitude();
c.constraintType = c.eTYPE_BENDING_CONSTRAINT;
//add constraint
mConstraintBuffer.pushBack(c);
}
}
}
}
void PxParticleClothCookerImpl::calculateMeshVolume()
{
// the physx api takes volume*6 now.
mMeshVolume = Gu::computeTriangleMeshVolume(mVertices, mTriangleIndices, mTriangleIndexCount / 3) * 6.0f;
}
} // namespace ExtGpu
ExtGpu::PxParticleClothCooker* PxCreateParticleClothCooker(PxU32 vertexCount, PxVec4* inVertices, PxU32 triangleIndexCount, PxU32* inTriangleIndices,
PxU32 constraintTypeFlags, PxVec3 verticalDirection, PxReal bendingConstraintMaxAngle)
{
return PX_NEW(ExtGpu::PxParticleClothCookerImpl)(vertexCount, inVertices, triangleIndexCount, inTriangleIndices,
constraintTypeFlags, verticalDirection, bendingConstraintMaxAngle);
}
} // namespace physx

View File

@@ -0,0 +1,826 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "PxParticleBuffer.h"
#include "extensions/PxCudaHelpersExt.h"
#include "extensions/PxParticleExt.h"
#include "foundation/PxUserAllocated.h"
#include "PxScene.h"
#include "PxPhysics.h"
#include "PxRigidBody.h"
#include "cudamanager/PxCudaContextManager.h"
#include "cudamanager/PxCudaContext.h"
namespace physx
{
namespace ExtGpu
{
void PxDmaDataToDevice(PxCudaContextManager* cudaContextManager, PxParticleBuffer* particleBuffer, const PxParticleBufferDesc& desc)
{
#if PX_SUPPORT_GPU_PHYSX
cudaContextManager->acquireContext();
PxVec4* posInvMass = particleBuffer->getPositionInvMasses();
PxVec4* velocities = particleBuffer->getVelocities();
PxU32* phases = particleBuffer->getPhases();
PxParticleVolume* volumes = particleBuffer->getParticleVolumes();
PxCudaContext* cudaContext = cudaContextManager->getCudaContext();
//KS - TODO - use an event to wait for this
cudaContext->memcpyHtoDAsync(CUdeviceptr(posInvMass), desc.positions, desc.numActiveParticles * sizeof(PxVec4), 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(velocities), desc.velocities, desc.numActiveParticles * sizeof(PxVec4), 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(phases), desc.phases, desc.numActiveParticles * sizeof(PxU32), 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(volumes), desc.volumes, desc.numVolumes * sizeof(PxParticleVolume), 0);
particleBuffer->setNbActiveParticles(desc.numActiveParticles);
particleBuffer->setNbParticleVolumes(desc.numVolumes);
cudaContext->streamSynchronize(0);
cudaContextManager->releaseContext();
#else
PX_UNUSED(cudaContextManager);
PX_UNUSED(particleBuffer);
PX_UNUSED(desc);
#endif
}
PxParticleBuffer* PxCreateAndPopulateParticleBuffer(const PxParticleBufferDesc& desc, PxCudaContextManager* cudaContextManager)
{
PxParticleBuffer* particleBuffer = PxGetPhysics().createParticleBuffer(desc.maxParticles, desc.maxVolumes, cudaContextManager);
PxDmaDataToDevice(cudaContextManager, particleBuffer, desc);
return particleBuffer;
}
PxParticleAndDiffuseBuffer* PxCreateAndPopulateParticleAndDiffuseBuffer(const PxParticleAndDiffuseBufferDesc& desc, PxCudaContextManager* cudaContextManager)
{
PxParticleAndDiffuseBuffer* particleBuffer = PxGetPhysics().createParticleAndDiffuseBuffer(desc.maxParticles, desc.maxVolumes, desc.maxDiffuseParticles, cudaContextManager);
PxDmaDataToDevice(cudaContextManager, particleBuffer, desc);
particleBuffer->setMaxActiveDiffuseParticles(desc.maxActiveDiffuseParticles);
return particleBuffer;
}
PxParticleClothBuffer* PxCreateAndPopulateParticleClothBuffer(const PxParticleBufferDesc& desc, const PxParticleClothDesc& clothDesc, PxPartitionedParticleCloth& output, PxCudaContextManager* cudaContextManager)
{
#if PX_SUPPORT_GPU_PHYSX
cudaContextManager->acquireContext();
PxParticleClothBuffer* clothBuffer = PxGetPhysics().createParticleClothBuffer(desc.maxParticles, desc.maxVolumes, clothDesc.nbCloths, clothDesc.nbTriangles, clothDesc.nbSprings, cudaContextManager);
PxVec4* posInvMass = clothBuffer->getPositionInvMasses();
PxVec4* velocities = clothBuffer->getVelocities();
PxU32* phases = clothBuffer->getPhases();
PxParticleVolume* volumes = clothBuffer->getParticleVolumes();
PxU32* triangles = clothBuffer->getTriangles();
PxVec4* restPositions = clothBuffer->getRestPositions();
PxCudaContext* cudaContext = cudaContextManager->getCudaContext();
cudaContext->memcpyHtoDAsync(CUdeviceptr(posInvMass), desc.positions, desc.numActiveParticles * sizeof(PxVec4), 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(velocities), desc.velocities, desc.numActiveParticles * sizeof(PxVec4), 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(phases), desc.phases, desc.numActiveParticles * sizeof(PxU32), 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(volumes), desc.volumes, desc.numVolumes * sizeof(PxParticleVolume), 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(triangles), clothDesc.triangles, clothDesc.nbTriangles * sizeof(PxU32) * 3, 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(restPositions), clothDesc.restPositions, desc.numActiveParticles * sizeof(PxVec4), 0);
clothBuffer->setNbActiveParticles(desc.numActiveParticles);
clothBuffer->setNbParticleVolumes(desc.numVolumes);
clothBuffer->setNbTriangles(clothDesc.nbTriangles);
clothBuffer->setCloths(output);
cudaContext->streamSynchronize(0);
cudaContextManager->releaseContext();
return clothBuffer;
#else
PX_UNUSED(desc);
PX_UNUSED(clothDesc);
PX_UNUSED(output);
PX_UNUSED(cudaContextManager);
return NULL;
#endif
}
PxParticleRigidBuffer* PxCreateAndPopulateParticleRigidBuffer(const PxParticleBufferDesc& desc, const PxParticleRigidDesc& rigidDesc, PxCudaContextManager* cudaContextManager)
{
#if PX_SUPPORT_GPU_PHYSX
cudaContextManager->acquireContext();
PxParticleRigidBuffer* rigidBuffer = PxGetPhysics().createParticleRigidBuffer(desc.maxParticles, desc.maxVolumes, rigidDesc.maxRigids, cudaContextManager);
PxVec4* posInvMassd = rigidBuffer->getPositionInvMasses();
PxVec4* velocitiesd = rigidBuffer->getVelocities();
PxU32* phasesd = rigidBuffer->getPhases();
PxParticleVolume* volumesd = rigidBuffer->getParticleVolumes();
PxU32* rigidOffsetsd = rigidBuffer->getRigidOffsets();
PxReal* rigidCoefficientsd = rigidBuffer->getRigidCoefficients();
PxVec4* rigidTranslationsd = rigidBuffer->getRigidTranslations();
PxVec4* rigidRotationsd = rigidBuffer->getRigidRotations();
PxVec4* rigidLocalPositionsd = rigidBuffer->getRigidLocalPositions();
PxVec4* rigidLocalNormalsd = rigidBuffer->getRigidLocalNormals();
PxCudaContext* cudaContext = cudaContextManager->getCudaContext();
const PxU32 numRigids = rigidDesc.numActiveRigids;
const PxU32 numActiveParticles = desc.numActiveParticles;
cudaContext->memcpyHtoDAsync(CUdeviceptr(posInvMassd), desc.positions, numActiveParticles * sizeof(PxVec4), 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(velocitiesd), desc.velocities, numActiveParticles * sizeof(PxVec4), 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(phasesd), desc.phases, numActiveParticles * sizeof(PxU32), 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(volumesd), desc.volumes, desc.numVolumes * sizeof(PxParticleVolume), 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(rigidOffsetsd), rigidDesc.rigidOffsets, sizeof(PxU32) * (numRigids + 1), 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(rigidCoefficientsd), rigidDesc.rigidCoefficients, sizeof(PxReal) * numRigids, 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(rigidTranslationsd), rigidDesc.rigidTranslations, sizeof(PxVec4) * numRigids, 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(rigidRotationsd), rigidDesc.rigidRotations, sizeof(PxQuat) * numRigids, 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(rigidLocalPositionsd), rigidDesc.rigidLocalPositions, sizeof(PxVec4) * desc.numActiveParticles, 0);
cudaContext->memcpyHtoDAsync(CUdeviceptr(rigidLocalNormalsd), rigidDesc.rigidLocalNormals, sizeof(PxVec4) * desc.numActiveParticles, 0);
rigidBuffer->setNbActiveParticles(numActiveParticles);
rigidBuffer->setNbRigids(numRigids);
rigidBuffer->setNbParticleVolumes(desc.numVolumes);
cudaContext->streamSynchronize(0);
cudaContextManager->releaseContext();
return rigidBuffer;
#else
PX_UNUSED(desc);
PX_UNUSED(rigidDesc);
PX_UNUSED(cudaContextManager);
return NULL;
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PxParticleAttachmentBuffer::PxParticleAttachmentBuffer(PxParticleBuffer& particleBuffer, PxPBDParticleSystem& particleSystem) : mParticleBuffer(particleBuffer),
mDeviceAttachments(NULL), mDeviceFilters(NULL), mNumDeviceAttachments(0), mNumDeviceFilters(0), mCudaContextManager(particleSystem.getCudaContextManager()),
mParticleSystem(particleSystem), mDirty(false)
{
}
PxParticleAttachmentBuffer::~PxParticleAttachmentBuffer()
{
#if PX_SUPPORT_GPU_PHYSX
mCudaContextManager->acquireContext();
PxCudaContext* cudaContext = mCudaContextManager->getCudaContext();
if (mDeviceAttachments)
cudaContext->memFree((CUdeviceptr)mDeviceAttachments);
if (mDeviceFilters)
cudaContext->memFree((CUdeviceptr)mDeviceFilters);
mDeviceAttachments = NULL;
mDeviceFilters = NULL;
mCudaContextManager->releaseContext();
#endif
}
void PxParticleAttachmentBuffer::copyToDevice(CUstream stream)
{
#if PX_SUPPORT_GPU_PHYSX
mCudaContextManager->acquireContext();
PxCudaContext* cudaContext = mCudaContextManager->getCudaContext();
if (mAttachments.size() > mNumDeviceAttachments)
{
if (mDeviceAttachments)
cudaContext->memFree((CUdeviceptr)mDeviceAttachments);
cudaContext->memAlloc((CUdeviceptr*)&mDeviceAttachments, sizeof(PxParticleRigidAttachment)*mAttachments.size());
mNumDeviceAttachments = mAttachments.size();
}
if (mFilters.size() > mNumDeviceFilters)
{
if (mDeviceFilters)
cudaContext->memFree((CUdeviceptr)mDeviceFilters);
cudaContext->memAlloc((CUdeviceptr*)&mDeviceFilters, sizeof(PxParticleRigidFilterPair)*mFilters.size());
mNumDeviceFilters = mFilters.size();
}
if (mAttachments.size())
cudaContext->memcpyHtoDAsync((CUdeviceptr)mDeviceAttachments, mAttachments.begin(), sizeof(PxParticleRigidAttachment)*mAttachments.size(), stream);
if (mFilters.size())
cudaContext->memcpyHtoDAsync((CUdeviceptr)mDeviceFilters, mFilters.begin(), sizeof(PxParticleRigidFilterPair)*mFilters.size(), stream);
mParticleBuffer.setRigidAttachments(mDeviceAttachments, mAttachments.size());
mParticleBuffer.setRigidFilters(mDeviceFilters, mFilters.size());
mDirty = true;
for (PxU32 i = 0; i < mNewReferencedBodies.size(); ++i)
{
if (mReferencedBodies[mNewReferencedBodies[i]] > 0)
mParticleSystem.addRigidAttachment(mNewReferencedBodies[i]);
}
for (PxU32 i = 0; i < mDestroyedRefrencedBodies.size(); ++i)
{
if (mReferencedBodies[mDestroyedRefrencedBodies[i]] == 0)
mParticleSystem.removeRigidAttachment(mDestroyedRefrencedBodies[i]);
}
mNewReferencedBodies.resize(0);
mDestroyedRefrencedBodies.resize(0);
mCudaContextManager->releaseContext();
#else
PX_UNUSED(stream);
#endif
}
void PxParticleAttachmentBuffer::addRigidAttachment(PxRigidActor* rigidActor, const PxU32 particleID, const PxVec3& localPose, PxConeLimitedConstraint* coneLimit)
{
PX_CHECK_AND_RETURN(coneLimit == NULL || coneLimit->isValid(), "PxParticleAttachmentBuffer::addRigidAttachment: PxConeLimitedConstraint needs to be valid if specified.");
PX_ASSERT(particleID < mParticleBuffer.getNbActiveParticles());
PxParticleRigidAttachment attachment(PxConeLimitedConstraint(), PxVec4(0.0f));
if (rigidActor == NULL)
{
PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"PxParticleAttachmentBuffer::addRigidAttachment: rigidActor cannot be NULL.");
return;
}
if (coneLimit)
{
attachment.mConeLimitParams.axisAngle = PxVec4(coneLimit->mAxis, coneLimit->mAngle);
attachment.mConeLimitParams.lowHighLimits = PxVec4(coneLimit->mLowLimit, coneLimit->mHighLimit, 0.f, 0.f);
}
if (rigidActor->getType() == PxActorType::eRIGID_STATIC)
{
// attachments to rigid static work in global space
attachment.mLocalPose0 = PxVec4(static_cast<PxRigidBody*>(rigidActor)->getGlobalPose().transform(localPose), 0.0f);
attachment.mID0 = PxNodeIndex().getInd();
}
else
{
// others use body space.
PxRigidBody* rigid = static_cast<PxRigidBody*>(rigidActor);
PxTransform body2Actor = rigid->getCMassLocalPose();
attachment.mLocalPose0 = PxVec4(body2Actor.transformInv(localPose), 0.f);
attachment.mID0 = rigid->getInternalIslandNodeIndex().getInd();
}
attachment.mID1 = particleID;
//Insert in order...
PxU32 l = 0, r = PxU32(mAttachments.size());
while (l < r) //If difference is just 1, we've found an item...
{
PxU32 index = (l + r) / 2;
if (attachment < mAttachments[index])
r = index;
else if (attachment > mAttachments[index])
l = index + 1;
else
l = r = index; //This is a match so insert before l
}
mAttachments.insert();
for (PxU32 i = mAttachments.size()-1; i > l; --i)
{
mAttachments[i] = mAttachments[i - 1];
}
mAttachments[l] = attachment;
mDirty = true;
if (rigidActor)
{
PxU32& refCount = mReferencedBodies[rigidActor];
if (refCount == 0)
mNewReferencedBodies.pushBack(rigidActor);
refCount++;
}
}
bool PxParticleAttachmentBuffer::removeRigidAttachment(PxRigidActor* rigidActor, const PxU32 particleID)
{
PX_ASSERT(particleID < mParticleBuffer.getNbActiveParticles());
if (rigidActor == NULL)
{
PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"PxParticleAttachmentBuffer::removeRigidAttachment: rigidActor cannot be NULL.");
return false;
}
if (rigidActor)
{
PxU32& refCount = mReferencedBodies[rigidActor];
refCount--;
if (refCount == 0)
mDestroyedRefrencedBodies.pushBack(rigidActor);
}
PxParticleRigidFilterPair attachment;
attachment.mID0 = rigidActor->getType() != PxActorType::eRIGID_STATIC ? static_cast<PxRigidBody*>(rigidActor)->getInternalIslandNodeIndex().getInd() :
PxNodeIndex().getInd();
attachment.mID1 = particleID;
PxU32 l = 0, r = PxU32(mAttachments.size());
while (l < r) //If difference is just 1, we've found an item...
{
PxU32 index = (l + r) / 2;
if (attachment < mAttachments[index])
r = index;
else if (attachment > mAttachments[index])
l = index + 1;
else
l = r = index; //This is a match so insert before l
}
if (mAttachments[l] == attachment)
{
mDirty = true;
//Remove
mAttachments.remove(l);
return true;
}
return false;
}
void PxParticleAttachmentBuffer::addRigidFilter(PxRigidActor* rigidActor, const PxU32 particleID)
{
PX_ASSERT(particleID < mParticleBuffer.getNbActiveParticles());
PxParticleRigidFilterPair attachment;
attachment.mID0 = rigidActor->getType() != PxActorType::eRIGID_STATIC ? static_cast<PxRigidBody*>(rigidActor)->getInternalIslandNodeIndex().getInd() :
PxNodeIndex().getInd();
attachment.mID1 = particleID;
//Insert in order...
PxU32 l = 0, r = PxU32(mFilters.size());
while (l < r) //If difference is just 1, we've found an item...
{
PxU32 index = (l + r) / 2;
if (attachment < mFilters[index])
r = index;
else if (attachment > mFilters[index])
l = index + 1;
else
l = r = index; //This is a match so insert before l
}
mFilters.insert();
for (PxU32 i = mFilters.size() - 1; i > l; --i)
{
mFilters[i] = mFilters[i - 1];
}
mFilters[l] = attachment;
mDirty = true;
}
bool PxParticleAttachmentBuffer::removeRigidFilter(PxRigidActor* rigidActor, const PxU32 particleID)
{
PX_ASSERT(particleID < mParticleBuffer.getNbActiveParticles());
PxParticleRigidFilterPair attachment;
attachment.mID0 = rigidActor->getType() != PxActorType::eRIGID_STATIC ? static_cast<PxRigidBody*>(rigidActor)->getInternalIslandNodeIndex().getInd() :
PxNodeIndex().getInd();
attachment.mID1 = particleID;
PxU32 l = 0, r = PxU32(mFilters.size());
while (l < r) //If difference is just 1, we've found an item...
{
PxU32 index = (l + r) / 2;
if (attachment < mFilters[index])
r = index;
else if (attachment > mFilters[index])
l = index + 1;
else
l = r = index; //This is a match so insert before l
}
if (mFilters[l] == attachment)
{
mDirty = true;
//Remove
mFilters.remove(l);
return true;
}
return false;
}
PxParticleAttachmentBuffer* PxCreateParticleAttachmentBuffer(PxParticleBuffer& buffer, PxParticleSystem& particleSystem)
{
return PX_NEW(PxParticleAttachmentBuffer)(buffer, particleSystem);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct ParticleClothBuffersImpl : public PxParticleClothBufferHelper, public PxUserAllocated
{
ParticleClothBuffersImpl(const PxU32 maxCloths, const PxU32 maxTriangles, const PxU32 maxSprings, const PxU32 maxParticles, PxCudaContextManager* cudaContextManager)
: mCudaContextManager(cudaContextManager)
{
mMaxParticles = maxParticles;
mClothDesc.nbParticles = 0;
mMaxTriangles = maxTriangles;
mClothDesc.nbTriangles = 0;
mMaxSprings = maxSprings;
mClothDesc.nbSprings = 0;
mMaxCloths = maxCloths;
mClothDesc.nbCloths = 0;
#if PX_SUPPORT_GPU_PHYSX
mClothDesc.restPositions = PX_EXT_PINNED_MEMORY_ALLOC(PxVec4, *mCudaContextManager, maxParticles);
mClothDesc.triangles = PX_EXT_PINNED_MEMORY_ALLOC(PxU32, *mCudaContextManager, maxTriangles * 3);
mClothDesc.springs = PX_EXT_PINNED_MEMORY_ALLOC(PxParticleSpring, *mCudaContextManager, maxSprings);
mClothDesc.cloths = PX_EXT_PINNED_MEMORY_ALLOC(PxParticleCloth, *mCudaContextManager, maxCloths);
#endif
}
void release()
{
#if PX_SUPPORT_GPU_PHYSX
PX_EXT_PINNED_MEMORY_FREE(*mCudaContextManager, mClothDesc.cloths);
PX_EXT_PINNED_MEMORY_FREE(*mCudaContextManager, mClothDesc.restPositions);
PX_EXT_PINNED_MEMORY_FREE(*mCudaContextManager, mClothDesc.triangles);
PX_EXT_PINNED_MEMORY_FREE(*mCudaContextManager, mClothDesc.springs);
#endif
PX_DELETE_THIS;
}
PxU32 getMaxCloths() const { return mMaxCloths; }
PxU32 getNumCloths() const { return mClothDesc.nbCloths; }
PxU32 getMaxSprings() const { return mMaxSprings; }
PxU32 getNumSprings() const { return mClothDesc.nbSprings; }
PxU32 getMaxTriangles() const { return mMaxTriangles; }
PxU32 getNumTriangles() const { return mClothDesc.nbTriangles; }
PxU32 getMaxParticles() const { return mMaxParticles; }
PxU32 getNumParticles() const { return mClothDesc.nbParticles; }
void addCloth(const PxParticleCloth& particleCloth, const PxU32* triangles, const PxU32 numTriangles,
const PxParticleSpring* springs, const PxU32 numSprings, const PxVec4* restPositions, const PxU32 numParticles)
{
if (mClothDesc.nbCloths + 1 > mMaxCloths)
{
PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"PxParticleClothBufferHelper::addCloth: exceeding maximal number of cloths that can be added.");
return;
}
if (mClothDesc.nbSprings + numSprings > mMaxSprings)
{
PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"PxParticleClothBufferHelper::addCloth: exceeding maximal number of springs that can be added.");
return;
}
if (mClothDesc.nbTriangles + numTriangles > mMaxTriangles)
{
PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"PxParticleClothBufferHelper::addCloth: exceeding maximal number of triangles that can be added.");
return;
}
if (mClothDesc.nbParticles + numParticles > mMaxParticles)
{
PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"PxParticleClothBufferHelper::addCloth: exceeding maximal number of particles that can be added.");
return;
}
mClothDesc.cloths[mClothDesc.nbCloths] = particleCloth;
mClothDesc.nbCloths += 1;
for (PxU32 i = 0; i < numSprings; ++i)
{
PxParticleSpring& dst = mClothDesc.springs[mClothDesc.nbSprings + i];
dst = springs[i];
dst.ind0 += mClothDesc.nbParticles;
dst.ind1 += mClothDesc.nbParticles;
}
mClothDesc.nbSprings += numSprings;
for (PxU32 i = 0; i < numTriangles*3; ++i)
{
PxU32& dst = mClothDesc.triangles[mClothDesc.nbTriangles*3 + i];
dst = triangles[i] + mClothDesc.nbParticles;
}
mClothDesc.nbTriangles += numTriangles;
PxMemCopy(mClothDesc.restPositions + mClothDesc.nbParticles, restPositions, sizeof(PxVec4)*numParticles);
mClothDesc.nbParticles += numParticles;
}
void addCloth(const PxReal blendScale, const PxReal restVolume, const PxReal pressure, const PxU32* triangles, const PxU32 numTriangles,
const PxParticleSpring* springs, const PxU32 numSprings, const PxVec4* restPositions, const PxU32 numParticles)
{
PX_UNUSED(blendScale);
PxParticleCloth particleCloth;
//particleCloth.clothBlendScale = blendScale;
particleCloth.restVolume = restVolume;
particleCloth.pressure = pressure;
particleCloth.startVertexIndex = mClothDesc.nbParticles;
particleCloth.numVertices = numParticles;
particleCloth.startTriangleIndex = mClothDesc.nbTriangles * 3;
particleCloth.numTriangles = numTriangles;
addCloth(particleCloth, triangles, numTriangles, springs, numSprings, restPositions, numParticles);
}
PxParticleClothDesc& getParticleClothDesc()
{
return mClothDesc;
}
PxU32 mMaxCloths;
PxU32 mMaxSprings;
PxU32 mMaxTriangles;
PxU32 mMaxParticles;
PxParticleClothDesc mClothDesc;
PxCudaContextManager* mCudaContextManager;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct ParticleVolumeBuffersImpl : public PxParticleVolumeBufferHelper, public PxUserAllocated
{
ParticleVolumeBuffersImpl(PxU32 maxVolumes, PxU32 maxTriangles, PxCudaContextManager* cudaContextManager)
{
mMaxVolumes = maxVolumes;
mNumVolumes = 0;
mMaxTriangles = maxTriangles;
mNumTriangles = 0;
mParticleVolumeMeshes = reinterpret_cast<PxParticleVolumeMesh*>(PX_ALLOC(sizeof(PxParticleVolumeMesh) * maxVolumes, "ParticleVolumeBuffersImpl::mParticleVolumeMeshes"));
mTriangles = reinterpret_cast<PxU32*>(PX_ALLOC(sizeof(PxU32) * maxTriangles * 3, "ParticleVolumeBuffersImpl::mTriangles"));
#if PX_SUPPORT_GPU_PHYSX
mParticleVolumes = PX_EXT_PINNED_MEMORY_ALLOC(PxParticleVolume, *cudaContextManager, maxVolumes);
mCudaContextManager = cudaContextManager;
#else
PX_UNUSED(cudaContextManager);
#endif
}
void release()
{
#if PX_SUPPORT_GPU_PHYSX
PX_EXT_PINNED_MEMORY_FREE(*mCudaContextManager, mParticleVolumes);
#endif
PX_FREE(mParticleVolumeMeshes);
PX_FREE(mTriangles);
PX_DELETE_THIS;
}
virtual PxU32 getMaxVolumes() const
{
return mMaxVolumes;
}
virtual PxU32 getNumVolumes() const
{
return mNumVolumes;
}
virtual PxU32 getMaxTriangles() const
{
return mMaxTriangles;
}
virtual PxU32 getNumTriangles() const
{
return mNumTriangles;
}
virtual PxParticleVolume* getParticleVolumes()
{
return mParticleVolumes;
}
virtual PxParticleVolumeMesh* getParticleVolumeMeshes()
{
return mParticleVolumeMeshes;
}
virtual PxU32* getTriangles()
{
return mTriangles;
}
virtual void addVolume(const PxParticleVolume& volume, const PxParticleVolumeMesh& volumeMesh, const PxU32* triangles, const PxU32 numTriangles)
{
if (mNumVolumes < mMaxVolumes && mNumTriangles + numTriangles <= mMaxTriangles)
{
PX_ASSERT(volumeMesh.startIndex == mNumTriangles);
mParticleVolumes[mNumVolumes] = volume;
mParticleVolumeMeshes[mNumVolumes] = volumeMesh;
mNumVolumes++;
for (PxU32 i = 0; i < numTriangles*3; ++i)
{
mTriangles[mNumTriangles*3 + i] = triangles[i] + volumeMesh.startIndex;
}
mNumTriangles += numTriangles;
}
}
virtual void addVolume(const PxU32 particleOffset, const PxU32 numParticles, const PxU32* triangles, const PxU32 numTriangles)
{
if (mNumVolumes < mMaxVolumes && mNumTriangles + numTriangles <= mMaxTriangles)
{
PxParticleVolume particleVolume;
particleVolume.bound.setEmpty();
particleVolume.particleIndicesOffset = particleOffset;
particleVolume.numParticles = numParticles;
PxParticleVolumeMesh particleVolumeMesh;
particleVolumeMesh.startIndex = mNumTriangles;
particleVolumeMesh.count = numTriangles;
mParticleVolumes[mNumVolumes] = particleVolume;
mParticleVolumeMeshes[mNumVolumes] = particleVolumeMesh;
mNumVolumes++;
for (PxU32 i = 0; i < numTriangles*3; ++i)
{
mTriangles[mNumTriangles*3 + i] = triangles[i] + particleOffset;
}
mNumTriangles += numTriangles;
}
}
PxU32 mMaxVolumes;
PxU32 mNumVolumes;
PxU32 mMaxTriangles;
PxU32 mNumTriangles;
PxParticleVolume* mParticleVolumes;
PxParticleVolumeMesh* mParticleVolumeMeshes;
PxU32* mTriangles;
PxCudaContextManager* mCudaContextManager;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct ParticleRigidBuffersImpl : public PxParticleRigidBufferHelper, public PxUserAllocated
{
ParticleRigidBuffersImpl(PxU32 maxRigids, PxU32 maxParticles, PxCudaContextManager* cudaContextManager)
: mCudaContextManager(cudaContextManager)
{
mRigidDesc.maxRigids = maxRigids;
mRigidDesc.numActiveRigids = 0;
mMaxParticles = maxParticles;
mNumParticles = 0;
#if PX_SUPPORT_GPU_PHYSX
mRigidDesc.rigidOffsets = PX_EXT_PINNED_MEMORY_ALLOC(PxU32, *mCudaContextManager, maxRigids + 1);
mRigidDesc.rigidCoefficients = PX_EXT_PINNED_MEMORY_ALLOC(PxReal, *mCudaContextManager, maxRigids);
mRigidDesc.rigidTranslations = PX_EXT_PINNED_MEMORY_ALLOC(PxVec4, *mCudaContextManager, maxRigids);
mRigidDesc.rigidRotations = PX_EXT_PINNED_MEMORY_ALLOC(PxQuat, *mCudaContextManager, maxRigids);
mRigidDesc.rigidLocalPositions = PX_EXT_PINNED_MEMORY_ALLOC(PxVec4, *mCudaContextManager, maxParticles);
mRigidDesc.rigidLocalNormals = PX_EXT_PINNED_MEMORY_ALLOC(PxVec4, *mCudaContextManager, maxParticles);
#endif
}
void release()
{
#if PX_SUPPORT_GPU_PHYSX
PX_EXT_PINNED_MEMORY_FREE(*mCudaContextManager, mRigidDesc.rigidOffsets);
PX_EXT_PINNED_MEMORY_FREE(*mCudaContextManager, mRigidDesc.rigidCoefficients);
PX_EXT_PINNED_MEMORY_FREE(*mCudaContextManager, mRigidDesc.rigidTranslations);
PX_EXT_PINNED_MEMORY_FREE(*mCudaContextManager, mRigidDesc.rigidRotations);
PX_EXT_PINNED_MEMORY_FREE(*mCudaContextManager, mRigidDesc.rigidLocalPositions);
PX_EXT_PINNED_MEMORY_FREE(*mCudaContextManager, mRigidDesc.rigidLocalNormals);
#endif
PX_DELETE_THIS;
}
virtual PxU32 getMaxRigids() const { return mRigidDesc.maxRigids; }
virtual PxU32 getNumRigids() const { return mRigidDesc.numActiveRigids; }
virtual PxU32 getMaxParticles() const { return mMaxParticles; }
virtual PxU32 getNumParticles() const { return mNumParticles; }
void addRigid(const PxVec3& translation, const PxQuat& rotation, const PxReal coefficient,
const PxVec4* localPositions, const PxVec4* localNormals, PxU32 numParticles)
{
PX_ASSERT(numParticles > 0);
const PxU32 numRigids = mRigidDesc.numActiveRigids;
if (numParticles > 0 && numRigids < mRigidDesc.maxRigids && mNumParticles + numParticles <= mMaxParticles)
{
mRigidDesc.rigidOffsets[numRigids] = mNumParticles;
mRigidDesc.rigidOffsets[numRigids + 1] = mNumParticles + numParticles;
mRigidDesc.rigidTranslations[numRigids] = PxVec4(translation, 0.0f);
mRigidDesc.rigidRotations[numRigids] = rotation;
mRigidDesc.rigidCoefficients[numRigids] = coefficient;
PxMemCopy(mRigidDesc.rigidLocalPositions + mNumParticles, localPositions, numParticles * sizeof(PxVec4));
PxMemCopy(mRigidDesc.rigidLocalNormals + mNumParticles, localNormals, numParticles * sizeof(PxVec4));
mRigidDesc.numActiveRigids += 1;
mNumParticles += numParticles;
}
}
PxParticleRigidDesc& getParticleRigidDesc()
{
return mRigidDesc;
}
PxU32 mMaxParticles;
PxU32 mNumParticles;
PxParticleRigidDesc mRigidDesc;
PxCudaContextManager* mCudaContextManager;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PxParticleVolumeBufferHelper* PxCreateParticleVolumeBufferHelper(PxU32 maxVolumes, PxU32 maxTriangles, PxCudaContextManager* cudaContextManager)
{
PxParticleVolumeBufferHelper* ret = NULL;
#if PX_SUPPORT_GPU_PHYSX
ret = PX_NEW(ParticleVolumeBuffersImpl)(maxVolumes, maxTriangles, cudaContextManager);
#else
PX_UNUSED(maxVolumes);
PX_UNUSED(maxTriangles);
PX_UNUSED(cudaContextManager);
#endif
return ret;
}
PxParticleClothBufferHelper* PxCreateParticleClothBufferHelper(const PxU32 maxCloths, const PxU32 maxTriangles, const PxU32 maxSprings, const PxU32 maxParticles, PxCudaContextManager* cudaContextManager)
{
PxParticleClothBufferHelper* ret = NULL;
#if PX_SUPPORT_GPU_PHYSX
ret = PX_NEW(ParticleClothBuffersImpl)(maxCloths, maxTriangles, maxSprings, maxParticles, cudaContextManager);
#else
PX_UNUSED(maxCloths);
PX_UNUSED(maxTriangles);
PX_UNUSED(maxSprings);
PX_UNUSED(maxParticles);
PX_UNUSED(cudaContextManager);
#endif
return ret;
}
PxParticleRigidBufferHelper* PxCreateParticleRigidBufferHelper(const PxU32 maxRigids, const PxU32 maxParticles, PxCudaContextManager* cudaContextManager)
{
PxParticleRigidBufferHelper* ret = NULL;
#if PX_SUPPORT_GPU_PHYSX
ret = PX_NEW(ParticleRigidBuffersImpl)(maxRigids, maxParticles, cudaContextManager);
#else
PX_UNUSED(maxRigids);
PX_UNUSED(maxParticles);
PX_UNUSED(cudaContextManager);
#endif
return ret;
}
} //namespace ExtGpu
} //namespace physx

View File

@@ -0,0 +1,35 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef PLATFORM_H
#define PLATFORM_H
#include <assert.h>
#include "foundation/PxThread.h"
#endif

View File

@@ -0,0 +1,209 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "ExtPrismaticJoint.h"
#include "ExtConstraintHelper.h"
#include "omnipvd/ExtOmniPvdSetData.h"
using namespace physx;
using namespace Ext;
PrismaticJoint::PrismaticJoint(const PxTolerancesScale& scale, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) :
PrismaticJointT(PxJointConcreteType::ePRISMATIC, actor0, localFrame0, actor1, localFrame1, "PrismaticJointData")
{
PrismaticJointData* data = static_cast<PrismaticJointData*>(mData);
data->limit = PxJointLinearLimitPair(scale);
data->jointFlags = PxPrismaticJointFlags();
}
PxPrismaticJointFlags PrismaticJoint::getPrismaticJointFlags() const
{
return data().jointFlags;
}
void PrismaticJoint::setPrismaticJointFlags(PxPrismaticJointFlags flags)
{
data().jointFlags = flags; markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, jointFlags, static_cast<PxPrismaticJoint&>(*this), flags)
}
void PrismaticJoint::setPrismaticJointFlag(PxPrismaticJointFlag::Enum flag, bool value)
{
if(value)
data().jointFlags |= flag;
else
data().jointFlags &= ~flag;
markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, jointFlags, static_cast<PxPrismaticJoint&>(*this), getPrismaticJointFlags())
}
PxJointLinearLimitPair PrismaticJoint::getLimit() const
{
return data().limit;
}
void PrismaticJoint::setLimit(const PxJointLinearLimitPair& limit)
{
PX_CHECK_AND_RETURN(limit.isValid(), "PxPrismaticJoint::setLimit: invalid parameter");
data().limit = limit;
markDirty();
#if PX_SUPPORT_OMNI_PVD
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
PxPrismaticJoint& j = static_cast<PxPrismaticJoint&>(*this);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, limitLower, j, limit.lower)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, limitUpper, j, limit.upper)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, limitRestitution, j, limit.restitution)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, limitBounceThreshold, j, limit.bounceThreshold)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, limitStiffness, j, limit.stiffness)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, limitDamping, j, limit.damping)
OMNI_PVD_WRITE_SCOPE_END
#endif
}
static void PrismaticJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags)
{
const PrismaticJointData& data = *reinterpret_cast<const PrismaticJointData*>(constantBlock);
PxTransform32 cA2w, cB2w;
joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform);
if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES)
viz.visualizeJointFrames(cA2w, cB2w);
if((flags & PxConstraintVisualizationFlag::eLIMITS) && (data.jointFlags & PxPrismaticJointFlag::eLIMIT_ENABLED))
{
viz.visualizeLinearLimit(cA2w, cB2w, data.limit.lower);
viz.visualizeLinearLimit(cA2w, cB2w, data.limit.upper);
}
}
//TAG:solverprepshader
static PxU32 PrismaticJointSolverPrep(Px1DConstraint* constraints,
PxVec3p& body0WorldOffset,
PxU32 /*maxConstraints*/,
PxConstraintInvMassScale& invMassScale,
const void* constantBlock,
const PxTransform& bA2w,
const PxTransform& bB2w,
bool /*useExtendedLimits*/,
PxVec3p& cA2wOut, PxVec3p& cB2wOut)
{
const PrismaticJointData& data = *reinterpret_cast<const PrismaticJointData*>(constantBlock);
PxTransform32 cA2w, cB2w;
joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w);
joint::applyNeighborhoodOperator(cA2w, cB2w);
const bool limitEnabled = data.jointFlags & PxPrismaticJointFlag::eLIMIT_ENABLED;
const PxJointLinearLimitPair& limit = data.limit;
const bool limitIsLocked = limitEnabled && limit.lower >= limit.upper;
const PxVec3 bOriginInA = cA2w.transformInv(cB2w.p);
PxVec3 ra, rb, axis;
ch.prepareLockedAxes(cA2w.q, cB2w.q, bOriginInA, limitIsLocked ? 7ul : 6ul, 7ul, ra, rb, &axis);
cA2wOut = ra + bA2w.p;
cB2wOut = rb + bB2w.p;
if(limitEnabled && !limitIsLocked)
{
const PxReal ordinate = bOriginInA.x;
ch.linearLimit(axis, ordinate, limit.upper, limit);
ch.linearLimit(-axis, -ordinate, -limit.lower, limit);
}
return ch.getCount();
}
///////////////////////////////////////////////////////////////////////////////
static PxConstraintShaderTable gPrismaticJointShaders = { PrismaticJointSolverPrep, PrismaticJointVisualize, PxConstraintFlag::Enum(0) };
PxConstraintSolverPrep PrismaticJoint::getPrep() const { return gPrismaticJointShaders.solverPrep; }
PxPrismaticJoint* physx::PxPrismaticJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1)
{
PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxPrismaticJointCreate: local frame 0 is not a valid transform");
PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxPrismaticJointCreate: local frame 1 is not a valid transform");
PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is<PxRigidBody>()) || (actor1 && actor1->is<PxRigidBody>()), "PxPrismaticJointCreate: at least one actor must be dynamic");
PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxPrismaticJointCreate: actors must be different");
return createJointT<PrismaticJoint, PrismaticJointData>(physics, actor0, localFrame0, actor1, localFrame1, gPrismaticJointShaders);
}
// PX_SERIALIZATION
void PrismaticJoint::resolveReferences(PxDeserializationContext& context)
{
mPxConstraint = resolveConstraintPtr(context, mPxConstraint, this, gPrismaticJointShaders);
}
//~PX_SERIALIZATION
#if PX_SUPPORT_OMNI_PVD
void PrismaticJoint::updateOmniPvdProperties() const
{
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
const PxPrismaticJoint& j = static_cast<const PxPrismaticJoint&>(*this);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, position, j, getPosition())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, velocity, j, getVelocity())
OMNI_PVD_WRITE_SCOPE_END
}
template<>
void physx::Ext::omniPvdInitJoint<PrismaticJoint>(PrismaticJoint& joint)
{
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
PxPrismaticJoint& j = static_cast<PxPrismaticJoint&>(joint);
OMNI_PVD_CREATE_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, j);
omniPvdSetBaseJointParams(static_cast<PxJoint&>(joint), PxJointConcreteType::ePRISMATIC);
PxJointLinearLimitPair limit = joint.getLimit();
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, limitLower, j, limit.lower)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, limitUpper, j, limit.upper)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, limitRestitution, j, limit.restitution)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, limitBounceThreshold, j, limit.bounceThreshold)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, limitStiffness, j, limit.stiffness)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, limitDamping, j, limit.damping)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, position, j, joint.getPosition())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxPrismaticJoint, velocity, j, joint.getVelocity())
OMNI_PVD_WRITE_SCOPE_END
}
#endif

View File

@@ -0,0 +1,85 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_PRISMATIC_JOINT_H
#define EXT_PRISMATIC_JOINT_H
#include "common/PxTolerancesScale.h"
#include "extensions/PxPrismaticJoint.h"
#include "ExtJoint.h"
#include "CmUtils.h"
namespace physx
{
struct PxPrismaticJointGeneratedValues;
namespace Ext
{
struct PrismaticJointData : public JointData
{
PxJointLinearLimitPair limit;
PxPrismaticJointFlags jointFlags;
private:
PrismaticJointData(const PxJointLinearLimitPair& pair) : limit(pair) {}
};
typedef JointT<PxPrismaticJoint, PrismaticJointData, PxPrismaticJointGeneratedValues> PrismaticJointT;
class PrismaticJoint : public PrismaticJointT
{
public:
// PX_SERIALIZATION
PrismaticJoint(PxBaseFlags baseFlags) : PrismaticJointT(baseFlags) {}
void resolveReferences(PxDeserializationContext& context);
static PrismaticJoint* createObject(PxU8*& address, PxDeserializationContext& context) { return createJointObject<PrismaticJoint>(address, context); }
//~PX_SERIALIZATION
PrismaticJoint(const PxTolerancesScale& scale, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1);
// PxPrismaticJoint
virtual PxReal getPosition() const PX_OVERRIDE { return getRelativeTransform().p.x; }
virtual PxReal getVelocity() const PX_OVERRIDE { return getRelativeLinearVelocity().x; }
virtual void setLimit(const PxJointLinearLimitPair& limit) PX_OVERRIDE;
virtual PxJointLinearLimitPair getLimit() const PX_OVERRIDE;
virtual void setPrismaticJointFlags(PxPrismaticJointFlags flags) PX_OVERRIDE;
virtual void setPrismaticJointFlag(PxPrismaticJointFlag::Enum flag, bool value) PX_OVERRIDE;
virtual PxPrismaticJointFlags getPrismaticJointFlags() const PX_OVERRIDE;
//~PxPrismaticJoint
// PxConstraintConnector
virtual PxConstraintSolverPrep getPrep() const PX_OVERRIDE;
#if PX_SUPPORT_OMNI_PVD
virtual void updateOmniPvdProperties() const PX_OVERRIDE;
#endif
//~PxConstraintConnector
};
} // namespace Ext
} // namespace physx
#endif

View File

@@ -0,0 +1,158 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
// suppress LNK4221
#include "foundation/PxPreprocessor.h"
PX_DUMMY_SYMBOL
#if PX_SUPPORT_PVD
#include "ExtPvd.h"
#include "PxExtensionMetaDataObjects.h"
#include "ExtD6Joint.h"
#include "ExtFixedJoint.h"
#include "ExtSphericalJoint.h"
#include "ExtDistanceJoint.h"
#include "ExtRevoluteJoint.h"
#include "ExtPrismaticJoint.h"
#include "ExtJointMetaDataExtensions.h"
#include "PvdMetaDataPropertyVisitor.h"
#include "PvdMetaDataDefineProperties.h"
using namespace physx;
using namespace Ext;
using namespace physx::Vd;
template<typename TObjType, typename TOperator>
static inline void visitPvdInstanceProperties( TOperator inOperator )
{
PxClassInfoTraits<TObjType>().Info.visitInstanceProperties( makePvdPropertyFilter( inOperator ), 0 );
}
template<typename TObjType, typename TOperator>
static inline void visitPvdProperties( TOperator inOperator )
{
PvdPropertyFilter<TOperator> theFilter( makePvdPropertyFilter( inOperator ) );
PxU32 thePropCount = PxClassInfoTraits<TObjType>().Info.visitBaseProperties( theFilter );
PxClassInfoTraits<TObjType>().Info.visitInstanceProperties( theFilter, thePropCount );
}
Pvd::PvdNameSpace::PvdNameSpace(physx::pvdsdk::PvdDataStream& conn, const char* /*name*/)
: mConnection(conn)
{
}
Pvd::PvdNameSpace::~PvdNameSpace()
{
}
void Pvd::releasePvdInstance(physx::pvdsdk::PvdDataStream& pvdConnection, const PxConstraint& c, const PxJoint& joint)
{
if(!pvdConnection.isConnected())
return;
//remove from scene and from any attached actors.
PxRigidActor* actor0, *actor1;
c.getActors( actor0, actor1 );
PxScene* scene = c.getScene();
if(scene) pvdConnection.removeObjectRef( scene, "Joints", &joint );
if ( actor0 && actor0->getScene() ) pvdConnection.removeObjectRef( actor0, "Joints", &joint );
if ( actor1 && actor1->getScene()) pvdConnection.removeObjectRef( actor1, "Joints", &joint );
pvdConnection.destroyInstance(&joint);
}
template<typename TObjType>
static void registerProperties( PvdDataStream& inStream )
{
inStream.createClass<TObjType>();
PvdPropertyDefinitionHelper& theHelper( inStream.getPropertyDefinitionHelper() );
PvdClassInfoDefine theDefinitionObj( theHelper, getPvdNamespacedNameForType<TObjType>() );
visitPvdInstanceProperties<TObjType>( theDefinitionObj );
}
template<typename TObjType, typename TValueStructType>
static void registerPropertiesAndValueStruct( PvdDataStream& inStream )
{
inStream.createClass<TObjType>();
inStream.deriveClass<PxJoint,TObjType>();
PvdPropertyDefinitionHelper& theHelper( inStream.getPropertyDefinitionHelper() );
{
PvdClassInfoDefine theDefinitionObj( theHelper, getPvdNamespacedNameForType<TObjType>() );
visitPvdInstanceProperties<TObjType>( theDefinitionObj );
}
{
PvdClassInfoValueStructDefine theDefinitionObj( theHelper );
visitPvdProperties<TObjType>( theDefinitionObj );
theHelper.addPropertyMessage<TObjType,TValueStructType>();
}
}
void Pvd::sendClassDescriptions(physx::pvdsdk::PvdDataStream& inStream)
{
if (inStream.isClassExist<PxJoint>())
return;
{ //PxJoint
registerProperties<PxJoint>( inStream );
inStream.createProperty<PxJoint,ObjectRef>( "Parent", "parents" );
registerPropertiesAndValueStruct<PxDistanceJoint,PxDistanceJointGeneratedValues>( inStream);
registerPropertiesAndValueStruct<PxFixedJoint,PxFixedJointGeneratedValues>( inStream);
registerPropertiesAndValueStruct<PxPrismaticJoint,PxPrismaticJointGeneratedValues>( inStream);
registerPropertiesAndValueStruct<PxSphericalJoint,PxSphericalJointGeneratedValues>( inStream);
registerPropertiesAndValueStruct<PxRevoluteJoint,PxRevoluteJointGeneratedValues>( inStream);
registerPropertiesAndValueStruct<PxD6Joint,PxD6JointGeneratedValues>( inStream);
registerPropertiesAndValueStruct<PxGearJoint,PxGearJointGeneratedValues>( inStream);
registerPropertiesAndValueStruct<PxRackAndPinionJoint,PxRackAndPinionJointGeneratedValues>( inStream);
}
}
void Pvd::setActors( physx::pvdsdk::PvdDataStream& inStream, const PxJoint& inJoint, const PxConstraint& c, const PxActor* newActor0, const PxActor* newActor1 )
{
PxRigidActor* actor0, *actor1;
c.getActors( actor0, actor1 );
if ( actor0 )
inStream.removeObjectRef( actor0, "Joints", &inJoint );
if ( actor1 )
inStream.removeObjectRef( actor1, "Joints", &inJoint );
if ( newActor0 && newActor0->getScene())
inStream.pushBackObjectRef( newActor0, "Joints", &inJoint );
if ( newActor1 && newActor1->getScene())
inStream.pushBackObjectRef( newActor1, "Joints", &inJoint );
inStream.setPropertyValue( &inJoint, "Actors.actor0", reinterpret_cast<const void*>(newActor0) );
inStream.setPropertyValue( &inJoint, "Actors.actor1", reinterpret_cast<const void*>(newActor1) );
const void* parent = newActor0 ? newActor0 : newActor1;
inStream.setPropertyValue( &inJoint, "Parent", parent );
if((newActor0 && !newActor0->getScene()) || (newActor1 && !newActor1->getScene()))
{
inStream.removeObjectRef( c.getScene(), "Joints", &inJoint );
}
}
#endif // PX_SUPPORT_PVD

View File

@@ -0,0 +1,200 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_PVD_H
#define EXT_PVD_H
#if PX_SUPPORT_PVD
#include "extensions/PxJoint.h"
#include "foundation/PxUserAllocated.h"
#include "PxPvdDataStream.h"
#include "PvdTypeNames.h"
#include "PxPvdObjectModelBaseTypes.h"
#if PX_LINUX && PX_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreserved-identifier"
#endif
#include "PxExtensionMetaDataObjects.h"
#if PX_LINUX && PX_CLANG
#pragma clang diagnostic pop
#endif
namespace physx
{
class PxJoint;
class PxD6Joint;
class PxDistanceJoint;
class PxFixedJoint;
class PxPrismaticJoint;
class PxRevoluteJoint;
class PxSphericalJoint;
class PxGearJoint;
class PxRackAndPinionJoint;
}
#define JOINT_GROUP 3
namespace physx
{
namespace pvdsdk {
#define DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP( type ) DEFINE_PVD_TYPE_NAME_MAP( physx::type, "physx3", #type )
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxJoint)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxJointGeneratedValues)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxFixedJoint)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxFixedJointGeneratedValues)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxDistanceJoint)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxDistanceJointGeneratedValues)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxPrismaticJoint)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxPrismaticJointGeneratedValues)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRevoluteJoint)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRevoluteJointGeneratedValues)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSphericalJoint)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSphericalJointGeneratedValues)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxD6Joint)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxD6JointGeneratedValues)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxGearJoint)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxGearJointGeneratedValues)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRackAndPinionJoint)
DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRackAndPinionJointGeneratedValues)
#undef DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP
} //pvdsdk
} // physx
namespace physx
{
namespace Ext
{
using namespace physx::pvdsdk;
class Pvd: public physx::PxUserAllocated
{
Pvd& operator=(const Pvd&);
public:
class PvdNameSpace
{
public:
PvdNameSpace(PvdDataStream& conn, const char* name);
~PvdNameSpace();
private:
PvdNameSpace& operator=(const PvdNameSpace&);
PvdDataStream& mConnection;
};
static void setActors( PvdDataStream& PvdDataStream,
const PxJoint& inJoint, const PxConstraint& c, const PxActor* newActor0, const PxActor* newActor1 );
template<typename TObjType>
static void createInstance( PvdDataStream& inStream, const PxConstraint& c, const TObjType& inSource )
{
inStream.createInstance( &inSource );
inStream.pushBackObjectRef( c.getScene(), "Joints", &inSource );
class ConstraintUpdateCmd : public PvdDataStream::PvdCommand
{
ConstraintUpdateCmd &operator=(const ConstraintUpdateCmd&) { PX_ASSERT(0); return *this; } //PX_NOCOPY doesn't work for local classes
public:
const PxConstraint& mConstraint;
const PxJoint& mJoint;
PxRigidActor* actor0, *actor1;
ConstraintUpdateCmd(const PxConstraint& constraint, const PxJoint& joint):PvdDataStream::PvdCommand(), mConstraint(constraint), mJoint(joint)
{
mConstraint.getActors( actor0, actor1 );
}
//Assigned is needed for copying
ConstraintUpdateCmd(const ConstraintUpdateCmd& cmd)
:PvdDataStream::PvdCommand(), mConstraint(cmd.mConstraint), mJoint(cmd.mJoint)
{
}
virtual bool canRun(PvdInstanceDataStream &inStream_ )
{
PX_ASSERT(inStream_.isInstanceValid(&mJoint));
//When run this command, the constraint maybe buffer removed
return ((actor0 == NULL) || inStream_.isInstanceValid(actor0))
&& ((actor1 == NULL) || inStream_.isInstanceValid(actor1));
}
virtual void run( PvdInstanceDataStream &inStream_ )
{
//When run this command, the constraint maybe buffer removed
if(!inStream_.isInstanceValid(&mJoint))
return;
PxRigidActor* actor0_, *actor1_;
mConstraint.getActors( actor0_, actor1_ );
if ( actor0_ && (inStream_.isInstanceValid(actor0_)) )
inStream_.pushBackObjectRef( actor0_, "Joints", &mJoint );
if ( actor1_ && (inStream_.isInstanceValid(actor1_)) )
inStream_.pushBackObjectRef( actor1_, "Joints", &mJoint );
const void* parent = actor0_ ? actor0_ : actor1_;
inStream_.setPropertyValue( &mJoint, "Parent", parent );
}
};
ConstraintUpdateCmd* cmd = PX_PLACEMENT_NEW(inStream.allocateMemForCmd(sizeof(ConstraintUpdateCmd)),
ConstraintUpdateCmd)(c, inSource);
if(cmd->canRun( inStream ))
cmd->run( inStream );
else
inStream.pushPvdCommand( *cmd );
}
template<typename jointtype, typename structValue>
static void updatePvdProperties(PvdDataStream& pvdConnection, const jointtype& joint)
{
structValue theValueStruct( &joint );
pvdConnection.setPropertyMessage( &joint, theValueStruct );
}
template<typename jointtype>
static void simUpdate(PvdDataStream& /*pvdConnection*/, const jointtype& /*joint*/) {}
template<typename jointtype>
static void createPvdInstance(PvdDataStream& pvdConnection, const PxConstraint& c, const jointtype& joint)
{
createInstance<jointtype>( pvdConnection, c, joint );
}
static void releasePvdInstance(PvdDataStream& pvdConnection, const PxConstraint& c, const PxJoint& joint);
static void sendClassDescriptions(PvdDataStream& pvdConnection);
};
} // ext
} // physx
#endif // PX_SUPPORT_PVD
#endif // EXT_PVD_H

View File

@@ -0,0 +1,96 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "foundation/PxAllocatorCallback.h"
#include "foundation/PxString.h"
#include "foundation/PxUserAllocated.h"
#include "extensions/PxStringTableExt.h"
#include "PxProfileAllocatorWrapper.h" //tools for using a custom allocator
using namespace physx;
using namespace physx::profile;
namespace
{
class PxStringTableImpl : public PxStringTable, public PxUserAllocated
{
typedef PxProfileHashMap<const char*, PxU32> THashMapType;
PxProfileAllocatorWrapper mWrapper;
THashMapType mHashMap;
public:
PxStringTableImpl( PxAllocatorCallback& inAllocator )
: mWrapper ( inAllocator )
, mHashMap ( mWrapper )
{
}
virtual ~PxStringTableImpl()
{
for ( THashMapType::Iterator iter = mHashMap.getIterator();
iter.done() == false;
++iter )
PX_PROFILE_DELETE( mWrapper, const_cast<char*>( iter->first ) );
mHashMap.clear();
}
virtual const char* allocateStr( const char* inSrc )
{
if ( inSrc == NULL )
inSrc = "";
const THashMapType::Entry* existing( mHashMap.find( inSrc ) );
if ( existing == NULL )
{
size_t len( strlen( inSrc ) );
len += 1;
char* newMem = reinterpret_cast<char*>(mWrapper.getAllocator().allocate( len, "PxStringTableImpl: const char*", PX_FL));
physx::Pxstrlcpy( newMem, len, inSrc );
mHashMap.insert( newMem, 1 );
return newMem;
}
else
{
++const_cast<THashMapType::Entry*>(existing)->second;
return existing->first;
}
}
/**
* Release the string table and all the strings associated with it.
*/
virtual void release()
{
PX_PROFILE_DELETE( mWrapper.getAllocator(), this );
}
};
}
PxStringTable& physx::PxStringTableExt::createStringTable( PxAllocatorCallback& inAllocator )
{
return *PX_PROFILE_NEW( inAllocator, PxStringTableImpl )( inAllocator );
}

View File

@@ -0,0 +1,392 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "ExtRackAndPinionJoint.h"
#include "ExtConstraintHelper.h"
#include "extensions/PxRevoluteJoint.h"
#include "extensions/PxPrismaticJoint.h"
#include "PxArticulationJointReducedCoordinate.h"
//#include <stdio.h>
#include "omnipvd/ExtOmniPvdSetData.h"
using namespace physx;
using namespace Ext;
PX_IMPLEMENT_OUTPUT_ERROR
RackAndPinionJoint::RackAndPinionJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) :
RackAndPinionJointT(PxJointConcreteType::eRACK_AND_PINION, actor0, localFrame0, actor1, localFrame1, "RackAndPinionJointData")
{
RackAndPinionJointData* data = static_cast<RackAndPinionJointData*>(mData);
data->hingeJoint = NULL;
data->prismaticJoint = NULL;
data->ratio = 1.0f;
data->px = 0.0f;
data->vangle = 0.0f;
resetError();
}
void RackAndPinionJoint::setRatio(float ratio)
{
RackAndPinionJointData* data = reinterpret_cast<RackAndPinionJointData*>(mData);
data->ratio = ratio;
resetError();
markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxRackAndPinionJoint, ratio, static_cast<PxRackAndPinionJoint&>(*this), ratio)
}
float RackAndPinionJoint::getRatio() const
{
RackAndPinionJointData* data = reinterpret_cast<RackAndPinionJointData*>(mData);
return data->ratio;
}
bool RackAndPinionJoint::setData(PxU32 nbRackTeeth, PxU32 nbPinionTeeth, float rackLength)
{
if(!nbRackTeeth)
return outputError<PxErrorCode::eINVALID_PARAMETER>(__LINE__, "PxRackAndPinionJoint::setData: nbRackTeeth cannot be zero.");
if(!nbPinionTeeth)
return outputError<PxErrorCode::eINVALID_PARAMETER>(__LINE__, "PxRackAndPinionJoint::setData: nbPinionTeeth cannot be zero.");
if(rackLength<=0.0f)
return outputError<PxErrorCode::eINVALID_PARAMETER>(__LINE__, "PxRackAndPinionJoint::setData: rackLength must be positive.");
RackAndPinionJointData* data = reinterpret_cast<RackAndPinionJointData*>(mData);
data->ratio = (PxTwoPi*nbRackTeeth)/(rackLength*nbPinionTeeth);
resetError();
markDirty();
return true;
}
bool RackAndPinionJoint::setJoints(const PxBase* hinge, const PxBase* prismatic)
{
RackAndPinionJointData* data = static_cast<RackAndPinionJointData*>(mData);
if(hinge)
{
const PxType type0 = hinge->getConcreteType();
if(type0 == PxConcreteType::eARTICULATION_JOINT_REDUCED_COORDINATE)
{
const PxArticulationJointReducedCoordinate* joint0 = static_cast<const PxArticulationJointReducedCoordinate*>(hinge);
const PxArticulationJointType::Enum artiJointType0 = joint0->getJointType();
if(artiJointType0 != PxArticulationJointType::eREVOLUTE && artiJointType0 != PxArticulationJointType::eREVOLUTE_UNWRAPPED)
return outputError<PxErrorCode::eINVALID_PARAMETER>(__LINE__, "PxRackAndPinionJoint::setJoints: passed joint must be a revolute joint.");
}
else
{
if(type0 != PxJointConcreteType::eREVOLUTE && type0 != PxJointConcreteType::eD6)
return outputError<PxErrorCode::eINVALID_PARAMETER>(__LINE__, "PxRackAndPinionJoint::setJoints: passed hinge joint must be either a revolute joint or a D6 joint.");
}
}
if(prismatic)
{
const PxType type1 = prismatic->getConcreteType();
if(type1 == PxConcreteType::eARTICULATION_JOINT_REDUCED_COORDINATE)
{
const PxArticulationJointReducedCoordinate* joint1 = static_cast<const PxArticulationJointReducedCoordinate*>(prismatic);
const PxArticulationJointType::Enum artiJointType1 = joint1->getJointType();
if(artiJointType1 != PxArticulationJointType::ePRISMATIC)
return outputError<PxErrorCode::eINVALID_PARAMETER>(__LINE__, "PxRackAndPinionJoint::setJoints: passed joint must be a prismatic joint.");
}
else
{
if(type1 != PxJointConcreteType::ePRISMATIC && type1 != PxJointConcreteType::eD6)
return outputError<PxErrorCode::eINVALID_PARAMETER>(__LINE__, "PxRackAndPinionJoint::setJoints: passed prismatic joint must be either a prismatic joint or a D6 joint.");
}
}
data->hingeJoint = hinge;
data->prismaticJoint = prismatic;
resetError();
markDirty();
#if PX_SUPPORT_OMNI_PVD
const PxBase* joints[] = { hinge, prismatic };
PxU32 jointCount = sizeof(joints) / sizeof(joints[0]);
OMNI_PVD_SET_ARRAY(OMNI_PVD_CONTEXT_HANDLE, PxRackAndPinionJoint, joints, static_cast<PxRackAndPinionJoint&>(*this), joints, jointCount)
#endif
return true;
}
void RackAndPinionJoint::getJoints(const PxBase*& hinge, const PxBase*& prismatic) const
{
const RackAndPinionJointData* data = static_cast<const RackAndPinionJointData*>(mData);
hinge = data->hingeJoint;
prismatic = data->prismaticJoint;
}
static float angleDiff(float angle0, float angle1)
{
const float diff = fmodf( angle1 - angle0 + PxPi, PxTwoPi) - PxPi;
return diff < -PxPi ? diff + PxTwoPi : diff;
}
void RackAndPinionJoint::updateError()
{
RackAndPinionJointData* data = static_cast<RackAndPinionJointData*>(mData);
if(!data->hingeJoint || !data->prismaticJoint)
return;
PxRigidActor* rackActor0;
PxRigidActor* rackActor1;
getActors(rackActor0, rackActor1);
float Angle0 = 0.0f;
float Sign0 = 0.0f;
{
PxRigidActor* hingeActor0;
PxRigidActor* hingeActor1;
const PxType type = data->hingeJoint->getConcreteType();
if(type == PxConcreteType::eARTICULATION_JOINT_REDUCED_COORDINATE)
{
const PxArticulationJointReducedCoordinate* artiHingeJoint = static_cast<const PxArticulationJointReducedCoordinate*>(data->hingeJoint);
hingeActor0 = &artiHingeJoint->getParentArticulationLink();
hingeActor1 = &artiHingeJoint->getChildArticulationLink();
Angle0 = artiHingeJoint->getJointPosition(PxArticulationAxis::eTWIST);
}
else
{
const PxJoint* hingeJoint = static_cast<const PxJoint*>(data->hingeJoint);
hingeJoint->getActors(hingeActor0, hingeActor1);
if(type == PxJointConcreteType::eREVOLUTE)
Angle0 = static_cast<const PxRevoluteJoint*>(hingeJoint)->getAngle();
else if (type == PxJointConcreteType::eD6)
Angle0 = static_cast<const PxD6Joint*>(hingeJoint)->getTwistAngle();
}
if(rackActor0 == hingeActor0 || rackActor1 == hingeActor0)
Sign0 = -1.0f;
else if (rackActor0 == hingeActor1 || rackActor1 == hingeActor1)
Sign0 = 1.0f;
else
PX_ASSERT(0);
}
if(!mInitDone)
{
mInitDone = true;
mPersistentAngle0 = Angle0;
}
const float travelThisFrame0 = angleDiff(Angle0, mPersistentAngle0);
mVirtualAngle0 += travelThisFrame0;
// printf("mVirtualAngle0: %f\n", mVirtualAngle0);
mPersistentAngle0 = Angle0;
float px = 0.0f;
float Sign1 = 0.0f;
{
PxRigidActor* prismaticActor0;
PxRigidActor* prismaticActor1;
const PxType type = data->prismaticJoint->getConcreteType();
if(type == PxConcreteType::eARTICULATION_JOINT_REDUCED_COORDINATE)
{
const PxArticulationJointReducedCoordinate* artiPrismaticJoint = static_cast<const PxArticulationJointReducedCoordinate*>(data->prismaticJoint);
prismaticActor0 = &artiPrismaticJoint->getParentArticulationLink();
prismaticActor1 = &artiPrismaticJoint->getChildArticulationLink();
px = artiPrismaticJoint->getJointPosition(PxArticulationAxis::eX);
}
else
{
const PxJoint* prismaticJoint = static_cast<const PxJoint*>(data->prismaticJoint);
prismaticJoint->getActors(prismaticActor0, prismaticActor1);
if(type==PxJointConcreteType::ePRISMATIC)
px = static_cast<const PxPrismaticJoint*>(prismaticJoint)->getPosition();
else if(type==PxJointConcreteType::eD6)
px = prismaticJoint->getRelativeTransform().p.x;
}
if(rackActor0 == prismaticActor0 || rackActor1 == prismaticActor0)
Sign1 = -1.0f;
else if(rackActor0 == prismaticActor1 || rackActor1 == prismaticActor1)
Sign1 = 1.0f;
else
PX_ASSERT(0);
}
// printf("px: %f\n", px);
data->px = Sign1*px;
data->vangle = Sign0*mVirtualAngle0;
markDirty();
}
void RackAndPinionJoint::resetError()
{
mVirtualAngle0 = 0.0f;
mPersistentAngle0 = 0.0f;
mInitDone = false;
}
static void RackAndPinionJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags)
{
if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES)
{
const RackAndPinionJointData& data = *reinterpret_cast<const RackAndPinionJointData*>(constantBlock);
// Visualize joint frames
PxTransform32 cA2w, cB2w;
joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform);
viz.visualizeJointFrames(cA2w, cB2w);
}
if(0)
{
const RackAndPinionJointData& data = *reinterpret_cast<const RackAndPinionJointData*>(constantBlock);
if(0)
{
PxTransform32 cA2w, cB2w;
joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform);
const PxVec3 gearAxis0 = cA2w.q.getBasisVector0();
const PxVec3 rackPrismaticAxis = cB2w.q.getBasisVector0();
viz.visualizeLine(cA2w.p, cA2w.p + gearAxis0, 0xff0000ff);
viz.visualizeLine(cB2w.p, cB2w.p + rackPrismaticAxis, 0xff0000ff);
}
}
}
//TAG:solverprepshader
static PxU32 RackAndPinionJointSolverPrep(Px1DConstraint* constraints,
PxVec3p& body0WorldOffset,
PxU32 /*maxConstraints*/,
PxConstraintInvMassScale& invMassScale,
const void* constantBlock,
const PxTransform& bA2w,
const PxTransform& bB2w,
bool /*useExtendedLimits*/,
PxVec3p& cA2wOut, PxVec3p& cB2wOut)
{
const RackAndPinionJointData& data = *reinterpret_cast<const RackAndPinionJointData*>(constantBlock);
PxTransform32 cA2w, cB2w;
joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w);
cA2wOut = cB2w.p;
cB2wOut = cB2w.p;
const PxVec3 gearAxis = cA2w.q.getBasisVector0();
const PxVec3 rackPrismaticAxis = cB2w.q.getBasisVector0();
// PT: this optional bit of code tries to fix the ratio for cases where the "same" rack is moved e.g. above or below a gear.
// In that case the rack would move in one direction or another depending on its position compared to the gear, and to avoid
// having to use a negative ratio in one of these cases this code tries to compute the proper sign and handle both cases the
// same way from the user's perspective. This created unexpected issues in ill-defined cases where e.g. the gear and the rack
// completely overlap, and we end up with a +0 or -0 for "dp" in the code below. So now this code disables itself in these
// cases but it would probably be better to disable it entirely. We don't do it though since it could break existing scenes.
// We might want to revisit these decisions at some point.
float Coeff = 1.0f;
const float epsilon = 0.001f;
const PxVec3 delta = cB2w.p - cA2w.p;
if(delta.magnitudeSquared()>epsilon*epsilon)
{
const PxVec3 velocity = gearAxis.cross(delta);
if(velocity.magnitudeSquared()>epsilon*epsilon)
{
const float dp = velocity.dot(rackPrismaticAxis);
Coeff = fabsf(dp)>epsilon ? PxSign(dp) : 1.0f;
}
}
Px1DConstraint& con = constraints[0];
con.linear0 = PxVec3(0.0f);
con.linear1 = rackPrismaticAxis * data.ratio*Coeff;
con.angular0 = gearAxis;
con.angular1 = PxVec3(0.0f);
con.geometricError = -Coeff*data.px*data.ratio - data.vangle;
con.minImpulse = -PX_MAX_F32;
con.maxImpulse = PX_MAX_F32;
con.velocityTarget = 0.f;
con.solveHint = 0;
con.flags = Px1DConstraintFlag::eOUTPUT_FORCE|Px1DConstraintFlag::eANGULAR_CONSTRAINT;
con.mods.bounce.restitution = 0.0f;
con.mods.bounce.velocityThreshold = 0.0f;
return 1;
}
///////////////////////////////////////////////////////////////////////////////
static PxConstraintShaderTable gRackAndPinionJointShaders = { RackAndPinionJointSolverPrep, RackAndPinionJointVisualize, PxConstraintFlag::eALWAYS_UPDATE };
PxConstraintSolverPrep RackAndPinionJoint::getPrep() const { return gRackAndPinionJointShaders.solverPrep; }
PxRackAndPinionJoint* physx::PxRackAndPinionJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1)
{
PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxRackAndPinionJointCreate: local frame 0 is not a valid transform");
PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxRackAndPinionJointCreate: local frame 1 is not a valid transform");
PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is<PxRigidBody>()) || (actor1 && actor1->is<PxRigidBody>()), "PxRackAndPinionJointCreate: at least one actor must be dynamic");
PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxRackAndPinionJointCreate: actors must be different");
return createJointT<RackAndPinionJoint, RackAndPinionJointData>(physics, actor0, localFrame0, actor1, localFrame1, gRackAndPinionJointShaders);
}
// PX_SERIALIZATION
void RackAndPinionJoint::resolveReferences(PxDeserializationContext& context)
{
mPxConstraint = resolveConstraintPtr(context, mPxConstraint, this, gRackAndPinionJointShaders);
RackAndPinionJointData* data = static_cast<RackAndPinionJointData*>(mData);
context.translatePxBase(data->hingeJoint);
context.translatePxBase(data->prismaticJoint);
}
//~PX_SERIALIZATION
#if PX_SUPPORT_OMNI_PVD
template<>
void physx::Ext::omniPvdInitJoint<RackAndPinionJoint>(RackAndPinionJoint& joint)
{
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
PxRackAndPinionJoint& j = static_cast<PxRackAndPinionJoint&>(joint);
OMNI_PVD_CREATE_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRackAndPinionJoint, j);
omniPvdSetBaseJointParams(static_cast<PxJoint&>(joint), PxJointConcreteType::eRACK_AND_PINION);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRackAndPinionJoint, ratio, j, joint.getRatio())
OMNI_PVD_WRITE_SCOPE_END
}
#endif

View File

@@ -0,0 +1,90 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_RACK_AND_PINION_JOINT_H
#define EXT_RACK_AND_PINION_JOINT_H
#include "extensions/PxRackAndPinionJoint.h"
#include "ExtJoint.h"
#include "CmUtils.h"
namespace physx
{
struct PxRackAndPinionJointGeneratedValues;
namespace Ext
{
struct RackAndPinionJointData : public JointData
{
const PxBase* hingeJoint;
const PxBase* prismaticJoint;
float ratio;
float px;
float vangle;
};
typedef JointT<PxRackAndPinionJoint, RackAndPinionJointData, PxRackAndPinionJointGeneratedValues> RackAndPinionJointT;
class RackAndPinionJoint : public RackAndPinionJointT
{
public:
// PX_SERIALIZATION
RackAndPinionJoint(PxBaseFlags baseFlags) : RackAndPinionJointT(baseFlags) {}
void resolveReferences(PxDeserializationContext& context);
static RackAndPinionJoint* createObject(PxU8*& address, PxDeserializationContext& context) { return createJointObject<RackAndPinionJoint>(address, context); }
//~PX_SERIALIZATION
RackAndPinionJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1);
// PxRackAndPinionJoint
virtual bool setJoints(const PxBase* hinge, const PxBase* prismatic) PX_OVERRIDE;
virtual void getJoints(const PxBase*& hinge, const PxBase*& prismatic) const PX_OVERRIDE;
virtual void setRatio(float ratio) PX_OVERRIDE;
virtual float getRatio() const PX_OVERRIDE;
virtual bool setData(PxU32 nbRackTeeth, PxU32 nbPinionTeeth, float rackLength) PX_OVERRIDE;
//~PxRackAndPinionJoint
// PxConstraintConnector
virtual void* prepareData() PX_OVERRIDE
{
updateError();
return mData;
}
virtual PxConstraintSolverPrep getPrep() const PX_OVERRIDE;
//~PxConstraintConnector
private:
float mVirtualAngle0;
float mPersistentAngle0;
bool mInitDone;
void updateError();
void resetError();
};
} // namespace Ext
}
#endif

View File

@@ -0,0 +1,319 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "geometry/PxBoxGeometry.h"
#include "geometry/PxSphereGeometry.h"
#include "geometry/PxCapsuleGeometry.h"
#include "geometry/PxConvexMeshGeometry.h"
#include "geometry/PxConvexMesh.h"
#include "extensions/PxShapeExt.h"
#include "extensions/PxRaycastCCD.h"
#include "PxScene.h"
#include "PxRigidDynamic.h"
#include "foundation/PxArray.h"
using namespace physx;
namespace physx
{
class RaycastCCDManagerInternal
{
PX_NOCOPY(RaycastCCDManagerInternal)
public:
RaycastCCDManagerInternal(PxScene* scene) : mScene(scene) {}
~RaycastCCDManagerInternal(){}
bool registerRaycastCCDObject(PxRigidDynamic* actor, PxShape* shape);
bool unregisterRaycastCCDObject(PxRigidDynamic* actor, PxShape* shape);
void doRaycastCCD(bool doDynamicDynamicCCD);
struct CCDObject
{
PX_FORCE_INLINE CCDObject(PxRigidDynamic* actor, PxShape* shape, const PxVec3& witness) : mActor(actor), mShape(shape), mWitness(witness) {}
PxRigidDynamic* mActor;
PxShape* mShape;
PxVec3 mWitness;
};
private:
PxScene* mScene;
physx::PxArray<CCDObject> mObjects;
};
}
static PxVec3 getShapeCenter(PxShape* shape, const PxTransform& pose)
{
PxVec3 offset(0.0f);
const PxGeometry& geom = shape->getGeometry();
if(geom.getType()==PxGeometryType::eCONVEXMESH)
{
const PxConvexMeshGeometry& geometry = static_cast<const PxConvexMeshGeometry&>(geom);
PxReal mass;
PxMat33 localInertia;
PxVec3 localCenterOfMass;
geometry.convexMesh->getMassInformation(mass, localInertia, localCenterOfMass);
offset += localCenterOfMass;
}
return pose.transform(offset);
}
static PX_FORCE_INLINE PxVec3 getShapeCenter(PxRigidActor* actor, PxShape* shape)
{
const PxTransform pose = PxShapeExt::getGlobalPose(*shape, *actor);
return getShapeCenter(shape, pose);
}
static PxReal computeInternalRadius(PxRigidActor* actor, PxShape* shape, const PxVec3& dir)
{
const PxBounds3 bounds = PxShapeExt::getWorldBounds(*shape, *actor);
const PxReal diagonal = (bounds.maximum - bounds.minimum).magnitude();
const PxReal offsetFromOrigin = diagonal * 2.0f;
PxTransform pose = PxShapeExt::getGlobalPose(*shape, *actor);
PxReal internalRadius = 0.0f;
const PxReal length = offsetFromOrigin*2.0f;
const PxGeometry& geom = shape->getGeometry();
switch(geom.getType())
{
case PxGeometryType::eSPHERE:
{
const PxSphereGeometry& geometry = static_cast<const PxSphereGeometry&>(geom);
internalRadius = geometry.radius;
}
break;
case PxGeometryType::eBOX:
case PxGeometryType::eCAPSULE:
{
pose.p = PxVec3(0.0f);
const PxVec3 virtualOrigin = pose.p + dir * offsetFromOrigin;
PxRaycastHit hit;
PxU32 nbHits = PxGeometryQuery::raycast(virtualOrigin, -dir, shape->getGeometry(), pose, length, PxHitFlags(0), 1, &hit);
PX_UNUSED(nbHits);
PX_ASSERT(nbHits);
internalRadius = offsetFromOrigin - hit.distance;
}
break;
case PxGeometryType::eCONVEXMESH:
{
PxVec3 shapeCenter = getShapeCenter(shape, pose);
shapeCenter -= pose.p;
pose.p = PxVec3(0.0f);
const PxVec3 virtualOrigin = shapeCenter + dir * offsetFromOrigin;
PxRaycastHit hit;
PxU32 nbHits = PxGeometryQuery::raycast(virtualOrigin, -dir, shape->getGeometry(), pose, length, PxHitFlags(0), 1, &hit);
PX_UNUSED(nbHits);
PX_ASSERT(nbHits);
internalRadius = offsetFromOrigin - hit.distance;
}
break;
default:
break;
}
return internalRadius;
}
class CCDRaycastFilterCallback : public PxQueryFilterCallback
{
public:
CCDRaycastFilterCallback(PxRigidActor* actor, PxShape* shape) : mActor(actor), mShape(shape){}
PxRigidActor* mActor;
PxShape* mShape;
virtual PxQueryHitType::Enum preFilter(const PxFilterData&, const PxShape* shape, const PxRigidActor* actor, PxHitFlags&)
{
if(mActor==actor && mShape==shape)
return PxQueryHitType::eNONE;
return PxQueryHitType::eBLOCK;
}
virtual PxQueryHitType::Enum postFilter(const PxFilterData&, const PxQueryHit&, const PxShape*, const PxRigidActor*)
{
return PxQueryHitType::eNONE;
}
};
static bool CCDRaycast(PxScene* scene, PxRigidActor* actor, PxShape* shape, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, PxRaycastHit& hit, bool dyna_dyna)
{
const PxQueryFlags qf(dyna_dyna ? PxQueryFlags(PxQueryFlag::eSTATIC|PxQueryFlag::eDYNAMIC|PxQueryFlag::ePREFILTER) : PxQueryFlags(PxQueryFlag::eSTATIC));
const PxQueryFilterData filterData(PxFilterData(), qf);
CCDRaycastFilterCallback CB(actor, shape);
PxRaycastBuffer buf1;
scene->raycast(origin, unitDir, distance, buf1, PxHitFlags(0), filterData, &CB);
hit = buf1.block;
return buf1.hasBlock;
}
static PxRigidDynamic* canDoCCD(PxRigidActor& actor, PxShape* /*shape*/)
{
if(actor.getConcreteType()!=PxConcreteType::eRIGID_DYNAMIC)
return NULL; // PT: no need to do it for statics
PxRigidDynamic* dyna = static_cast<PxRigidDynamic*>(&actor);
const PxU32 nbShapes = dyna->getNbShapes();
if(nbShapes!=1)
return NULL; // PT: only works with simple actors for now
if(dyna->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC)
return NULL; // PT: no need to do it for kinematics
return dyna;
}
static bool doRaycastCCD(PxScene* scene, const RaycastCCDManagerInternal::CCDObject& object, PxTransform& newPose, PxVec3& newShapeCenter, bool dyna_dyna)
{
PxRigidDynamic* dyna = canDoCCD(*object.mActor, object.mShape);
if(!dyna)
return true;
bool updateCCDWitness = true;
const PxVec3 offset = newPose.p - newShapeCenter;
const PxVec3& origin = object.mWitness;
const PxVec3& dest = newShapeCenter;
PxVec3 dir = dest - origin;
const PxReal length = dir.magnitude();
if(length!=0.0f)
{
dir /= length;
const PxReal internalRadius = computeInternalRadius(object.mActor, object.mShape, dir);
PxRaycastHit hit;
if(internalRadius!=0.0f && CCDRaycast(scene, object.mActor, object.mShape, origin, dir, length, hit, dyna_dyna))
{
updateCCDWitness = false;
const PxReal radiusLimit = internalRadius * 0.75f;
if(hit.distance>radiusLimit)
{
newShapeCenter = origin + dir * (hit.distance - radiusLimit);
}
else
{
if(hit.actor->getConcreteType()==PxConcreteType::eRIGID_DYNAMIC)
return true;
newShapeCenter = origin;
}
newPose.p = offset + newShapeCenter;
const PxTransform shapeLocalPose = object.mShape->getLocalPose();
const PxTransform inverseShapeLocalPose = shapeLocalPose.getInverse();
const PxTransform newGlobalPose = newPose * inverseShapeLocalPose;
dyna->setGlobalPose(newGlobalPose);
}
}
return updateCCDWitness;
}
bool RaycastCCDManagerInternal::registerRaycastCCDObject(PxRigidDynamic* actor, PxShape* shape)
{
if(!actor || !shape)
return false;
mObjects.pushBack(CCDObject(actor, shape, getShapeCenter(actor, shape)));
return true;
}
bool RaycastCCDManagerInternal::unregisterRaycastCCDObject(PxRigidDynamic* actor, PxShape* shape)
{
if(!actor || !shape)
return false;
const PxU32 nbObjects = mObjects.size();
for(PxU32 i=0;i<nbObjects;i++)
{
if(mObjects[i].mActor==actor && mObjects[i].mShape==shape)
{
mObjects[i] = mObjects[nbObjects-1];
mObjects.popBack();
return true;
}
}
return false;
}
void RaycastCCDManagerInternal::doRaycastCCD(bool doDynamicDynamicCCD)
{
const PxU32 nbObjects = mObjects.size();
for(PxU32 i=0;i<nbObjects;i++)
{
CCDObject& object = mObjects[i];
if(object.mActor->isSleeping())
continue;
PxTransform newPose = PxShapeExt::getGlobalPose(*object.mShape, *object.mActor);
PxVec3 newShapeCenter = getShapeCenter(object.mShape, newPose);
if(::doRaycastCCD(mScene, object, newPose, newShapeCenter, doDynamicDynamicCCD))
object.mWitness = newShapeCenter;
}
}
RaycastCCDManager::RaycastCCDManager(PxScene* scene)
{
mImpl = new RaycastCCDManagerInternal(scene);
}
RaycastCCDManager::~RaycastCCDManager()
{
delete mImpl;
}
bool RaycastCCDManager::registerRaycastCCDObject(PxRigidDynamic* actor, PxShape* shape)
{
return mImpl->registerRaycastCCDObject(actor, shape);
}
bool RaycastCCDManager::unregisterRaycastCCDObject(PxRigidDynamic* actor, PxShape* shape)
{
return mImpl->unregisterRaycastCCDObject(actor, shape);
}
void RaycastCCDManager::doRaycastCCD(bool doDynamicDynamicCCD)
{
mImpl->doRaycastCCD(doDynamicDynamicCCD);
}

View File

@@ -0,0 +1,438 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxRemeshingExt.h"
#include "foundation/PxHashMap.h"
using namespace physx;
static void assignTriangle(PxArray<PxU32>& triangles, PxU32 triIndex, PxU32 a, PxU32 b, PxU32 c)
{
triangles[3 * triIndex] = a;
triangles[3 * triIndex + 1] = b;
triangles[3 * triIndex + 2] = c;
}
static void addTriangle(PxArray<PxU32>& triangles, PxU32 a, PxU32 b, PxU32 c)
{
triangles.pushBack(a);
triangles.pushBack(b);
triangles.pushBack(c);
}
static void subdivideTriangle(int i, int ab, int bc, int ac, PxArray<PxU32>& triangles, PxArray<PxVec3>& points)
{
PxU32 tri[3] = { triangles[3 * i],triangles[3 * i + 1],triangles[3 * i + 2] };
if (ab >= 0 && bc >= 0 && ac >= 0)
{
addTriangle(triangles, tri[0], ab, ac);
addTriangle(triangles, tri[1], bc, ab);
addTriangle(triangles, tri[2], ac, bc);
assignTriangle(triangles, i, ab, bc, ac);
}
else if (ac >= 0 && ab >= 0)
{
float dB = (points[ac] - points[tri[1]]).magnitudeSquared();
float dC = (points[ab] - points[tri[2]]).magnitudeSquared();
if (dB < dC)
{
addTriangle(triangles, tri[1], tri[2], ac);
addTriangle(triangles, tri[1], ac, ab);
}
else
{
addTriangle(triangles, tri[1], tri[2], ab);
addTriangle(triangles, tri[2], ac, ab);
}
assignTriangle(triangles, i, tri[0], ab, ac);
}
else if (ab >= 0 && bc >= 0)
{
float dA = (points[bc] - points[tri[0]]).magnitudeSquared();
float dC = (points[ab] - points[tri[2]]).magnitudeSquared();
if (dC < dA)
{
addTriangle(triangles, tri[2], tri[0], ab);
addTriangle(triangles, tri[2], ab, bc);
}
else
{
addTriangle(triangles, tri[2], tri[0], bc);
addTriangle(triangles, tri[0], ab, bc);
}
assignTriangle(triangles, i, tri[1], bc, ab);
}
else if (bc >= 0 && ac >= 0)
{
float dA = (points[bc] - points[tri[0]]).magnitudeSquared();
float dB = (points[ac] - points[tri[1]]).magnitudeSquared();
if (dA < dB)
{
addTriangle(triangles, tri[0], tri[1], bc);
addTriangle(triangles, tri[0], bc, ac);
}
else
{
addTriangle(triangles, tri[0], tri[1], ac);
addTriangle(triangles, tri[1], bc, ac);
}
assignTriangle(triangles, i, tri[2], ac, bc);
}
else if (ab >= 0)
{
addTriangle(triangles, tri[1], tri[2], ab);
assignTriangle(triangles, i, tri[2], tri[0], ab);
}
else if (bc >= 0)
{
addTriangle(triangles, tri[2], tri[0], bc);
assignTriangle(triangles, i, tri[0], tri[1], bc);
}
else if (ac >= 0)
{
addTriangle(triangles, tri[1], tri[2], ac);
assignTriangle(triangles, i, tri[0], tri[1], ac);
}
}
static PX_FORCE_INLINE PxU64 key(PxU32 a, PxU32 b)
{
if (a < b)
return ((PxU64(a)) << 32) | (PxU64(b));
else
return ((PxU64(b)) << 32) | (PxU64(a));
}
static void checkEdge(PxU32 a, PxU32 b, PxHashMap<PxU64, PxI32>& edges, PxArray<PxVec3>& points, PxReal maxEdgeLength)
{
if ((points[a] - points[b]).magnitudeSquared() < maxEdgeLength * maxEdgeLength)
return;
PxU64 k = key(a, b);
if (edges.find(k))
return;
edges.insert(k, points.size());
points.pushBack(0.5f * (points[a] + points[b]));
}
static PX_FORCE_INLINE PxI32 getEdge(PxU32 a, PxU32 b, PxHashMap<PxU64, PxI32>& edges)
{
if (const PxPair<const PxU64, PxI32>* ptr = edges.find(key(a, b)))
return ptr->second;
return -1;
}
namespace
{
struct Info
{
PxI32 StartIndex;
PxI32 Count;
Info(PxI32 startIndex, PxI32 count)
{
StartIndex = startIndex;
Count = count;
}
};
}
static void checkEdge(PxU32 a, PxU32 b, PxHashMap<PxU64, Info>& edges, PxArray<PxVec3>& points, PxReal maxEdgeLength)
{
if (a > b)
PxSwap(a, b);
PxReal l = (points[a] - points[b]).magnitudeSquared();
if (l < maxEdgeLength * maxEdgeLength)
return;
l = PxSqrt(l);
PxU32 numSubdivisions = (PxU32)(l / maxEdgeLength);
if (numSubdivisions <= 1)
return;
PxU64 k = key(a, b);
if (edges.find(k))
return;
edges.insert(k, Info(points.size(), numSubdivisions - 1));
for (PxU32 i = 1; i < numSubdivisions; ++i)
{
PxReal p = (PxReal)i / numSubdivisions;
points.pushBack((1 - p) * points[a] + p * points[b]);
}
}
static PX_FORCE_INLINE Info getEdge(PxU32 a, PxU32 b, PxHashMap<PxU64, Info>& edges)
{
const PxPair<const PxU64, Info>* value = edges.find(key(a, b));
if (value)
return value->second;
return Info(-1, -1);
}
static PX_FORCE_INLINE void addPoints(PxArray<PxU32>& polygon, const Info& ab, bool reverse)
{
if (reverse)
{
for (PxI32 i = ab.Count - 1; i >= 0; --i)
polygon.pushBack(ab.StartIndex + i);
}
else
{
for (PxI32 i = 0; i < ab.Count; ++i)
polygon.pushBack(ab.StartIndex + i);
}
}
static PX_FORCE_INLINE PxReal angle(const PxVec3& l, const PxVec3& r)
{
PxReal d = l.dot(r) / PxSqrt(l.magnitudeSquared() * r.magnitudeSquared());
if (d <= -1) return PxPi;
if (d >= 1) return 0.0f;
return PxAcos(d);
}
static PX_FORCE_INLINE PxReal evaluateCost(const PxArray<PxVec3>& vertices, PxU32 a, PxU32 b, PxU32 c)
{
const PxVec3& aa = vertices[a];
const PxVec3& bb = vertices[b];
const PxVec3& cc = vertices[c];
PxReal a1 = angle(bb - aa, cc - aa);
PxReal a2 = angle(aa - bb, cc - bb);
PxReal a3 = angle(aa - cc, bb - cc);
return PxMax(a1, PxMax(a2, a3));
//return (aa - bb).magnitude() + (aa - cc).magnitude() + (bb - cc).magnitude();
}
static void triangulateConvex(PxU32 originalTriangleIndexTimes3, PxArray<PxU32>& polygon, const PxArray<PxVec3>& vertices, PxArray<PxU32>& triangles, PxArray<PxI32>& offsets)
{
offsets.forceSize_Unsafe(0);
offsets.reserve(polygon.size());
for (PxU32 i = 0; i < polygon.size(); ++i)
offsets.pushBack(1);
PxI32 start = 0;
PxU32 count = polygon.size();
PxU32 triCounter = 0;
while (count > 2)
{
PxReal minCost = FLT_MAX;
PxI32 best = -1;
PxI32 i = start;
for (PxU32 j = 0; j < count; ++j)
{
PxU32 a = polygon[i];
PX_ASSERT(offsets[i] >= 0);
PxI32 n = (i + offsets[i]) % polygon.size();
PX_ASSERT(offsets[n] >= 0);
PxU32 b = polygon[n];
PxU32 nn = (n + offsets[n]) % polygon.size();
PX_ASSERT(offsets[nn] >= 0);
PxU32 c = polygon[nn];
PxReal cost = evaluateCost(vertices, a, b, c);
if (cost < minCost)
{
minCost = cost;
best = i;
}
i = n;
}
{
PxU32 a = polygon[best];
PxI32 n = (best + offsets[best]) % polygon.size();
PxU32 b = polygon[n];
PxU32 nn = (n + offsets[n]) % polygon.size();
PxU32 c = polygon[nn];
if (n == start)
start += offsets[n];
offsets[best] += offsets[n];
offsets[n] = -1;
PX_ASSERT(offsets[(best + offsets[best]) % polygon.size()] >= 0);
if (triCounter == 0)
{
triangles[originalTriangleIndexTimes3 + 0] = a;
triangles[originalTriangleIndexTimes3 + 1] = b;
triangles[originalTriangleIndexTimes3 + 2] = c;
}
else
{
triangles.pushBack(a);
triangles.pushBack(b);
triangles.pushBack(c);
}
++triCounter;
}
--count;
}
}
static bool limitMaxEdgeLengthAdaptive(PxArray<PxU32>& triangles, PxArray<PxVec3>& points, PxReal maxEdgeLength, PxArray<PxU32>* triangleMap = NULL)
{
PxHashMap<PxU64, Info> edges;
bool split = false;
//Analyze edges
for (PxU32 i = 0; i < triangles.size(); i += 3)
{
const PxU32* t = &triangles[i];
checkEdge(t[0], t[1], edges, points, maxEdgeLength);
checkEdge(t[1], t[2], edges, points, maxEdgeLength);
checkEdge(t[0], t[2], edges, points, maxEdgeLength);
}
PxArray<PxI32> offsets;
PxArray<PxU32> polygon;
//Subdivide triangles if required
PxU32 size = triangles.size();
for (PxU32 i = 0; i < size; i += 3)
{
const PxU32* t = &triangles[i];
Info ab = getEdge(t[0], t[1], edges);
Info bc = getEdge(t[1], t[2], edges);
Info ac = getEdge(t[0], t[2], edges);
if (ab.StartIndex >= 0 || bc.StartIndex >= 0 || ac.StartIndex >= 0)
{
polygon.forceSize_Unsafe(0);
polygon.pushBack(t[0]);
addPoints(polygon, ab, t[0] > t[1]);
polygon.pushBack(t[1]);
addPoints(polygon, bc, t[1] > t[2]);
polygon.pushBack(t[2]);
addPoints(polygon, ac, t[2] > t[0]);
PxU32 s = triangles.size();
triangulateConvex(i, polygon, points, triangles, offsets);
split = true;
if (triangleMap != NULL)
{
for (PxU32 j = s; j < triangles.size(); ++j)
triangleMap->pushBack((*triangleMap)[i]);
}
}
/*else
{
result.pushBack(t[0]);
result.pushBack(t[1]);
result.pushBack(t[2]);
if (triangleMap != NULL)
triangleMap->pushBack((*triangleMap)[i]);
}*/
}
return split;
}
bool PxRemeshingExt::reduceSliverTriangles(PxArray<PxU32>& triangles, PxArray<PxVec3>& points, PxReal maxEdgeLength, PxU32 maxIterations, PxArray<PxU32>* triangleMap, PxU32 triangleCountThreshold)
{
bool split = limitMaxEdgeLengthAdaptive(triangles, points, maxEdgeLength, triangleMap);
if (!split)
return false;
for (PxU32 i = 1; i < maxIterations; ++i)
{
split = limitMaxEdgeLengthAdaptive(triangles, points, maxEdgeLength, triangleMap);
if (!split)
break;
if (triangles.size() >= triangleCountThreshold)
break;
}
return true;
}
bool PxRemeshingExt::limitMaxEdgeLength(PxArray<PxU32>& triangles, PxArray<PxVec3>& points, PxReal maxEdgeLength, PxU32 maxIterations, PxArray<PxU32>* triangleMap, PxU32 triangleCountThreshold)
{
if (triangleMap)
{
triangleMap->clear();
triangleMap->reserve(triangles.size() / 3);
for (PxU32 i = 0; i < triangles.size() / 3; ++i)
triangleMap->pushBack(i);
}
PxU32 numIndices = triangles.size();
PxHashMap<PxU64, PxI32> edges;
bool success = true;
for (PxU32 k = 0; k < maxIterations && success; ++k)
{
success = false;
edges.clear();
//Analyze edges
for (PxU32 i = 0; i < triangles.size(); i += 3)
{
checkEdge(triangles[i], triangles[i + 1], edges, points, maxEdgeLength);
checkEdge(triangles[i + 1], triangles[i + 2], edges, points, maxEdgeLength);
checkEdge(triangles[i], triangles[i + 2], edges, points, maxEdgeLength);
}
//Subdivide triangles if required
PxU32 size = triangles.size();
for (PxU32 i = 0; i < size; i += 3)
{
PxI32 ab = getEdge(triangles[i], triangles[i + 1], edges);
PxI32 bc = getEdge(triangles[i + 1], triangles[i + 2], edges);
PxI32 ac = getEdge(triangles[i], triangles[i + 2], edges);
if (ab >= 0 || bc >= 0 || ac >= 0)
{
PxU32 s = triangles.size();
subdivideTriangle(i / 3, ab, bc, ac, triangles, points);
success = true;
if (triangleMap)
{
for (PxU32 j = s / 3; j < triangles.size() / 3; ++j)
triangleMap->pushBack((*triangleMap)[i / 3]);
}
}
}
if (triangles.size() >= triangleCountThreshold)
break;
}
return numIndices != triangles.size();
}

View File

@@ -0,0 +1,327 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "ExtRevoluteJoint.h"
#include "ExtConstraintHelper.h"
#include "omnipvd/ExtOmniPvdSetData.h"
using namespace physx;
using namespace Ext;
RevoluteJoint::RevoluteJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) :
RevoluteJointT(PxJointConcreteType::eREVOLUTE, actor0, localFrame0, actor1, localFrame1, "RevoluteJointData")
{
RevoluteJointData* data = static_cast<RevoluteJointData*>(mData);
data->driveForceLimit = PX_MAX_F32;
data->driveVelocity = 0.0f;
data->driveGearRatio = 1.0f;
data->limit = PxJointAngularLimitPair(-PxPi/2, PxPi/2);
data->jointFlags = PxRevoluteJointFlags();
}
PxReal RevoluteJoint::getAngle() const
{
return getTwistAngle_Internal();
}
PxReal RevoluteJoint::getVelocity() const
{
return getRelativeAngularVelocity().magnitude();
}
PxJointAngularLimitPair RevoluteJoint::getLimit() const
{
return data().limit;
}
void RevoluteJoint::setLimit(const PxJointAngularLimitPair& limit)
{
PX_CHECK_AND_RETURN(limit.isValid(), "PxRevoluteJoint::setLimit: limit invalid");
PX_CHECK_AND_RETURN(limit.lower>-PxTwoPi && limit.upper<PxTwoPi , "PxRevoluteJoint::twist limit must be strictly between -2*PI and 2*PI");
data().limit = limit;
markDirty();
#if PX_SUPPORT_OMNI_PVD
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
PxRevoluteJoint& j = static_cast<PxRevoluteJoint&>(*this);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, limitLower, j, limit.lower)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, limitUpper, j, limit.upper)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, limitRestitution, j, limit.restitution)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, limitBounceThreshold, j, limit.bounceThreshold)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, limitStiffness, j, limit.stiffness)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, limitDamping, j, limit.damping)
OMNI_PVD_WRITE_SCOPE_END
#endif
}
PxReal RevoluteJoint::getDriveVelocity() const
{
return data().driveVelocity;
}
void RevoluteJoint::setDriveVelocity(PxReal velocity, bool autowake)
{
PX_CHECK_AND_RETURN(PxIsFinite(velocity), "PxRevoluteJoint::setDriveVelocity: invalid parameter");
data().driveVelocity = velocity;
if(autowake)
wakeUpActors();
markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, driveVelocity, static_cast<PxRevoluteJoint&>(*this), velocity)
}
PxReal RevoluteJoint::getDriveForceLimit() const
{
return data().driveForceLimit;
}
void RevoluteJoint::setDriveForceLimit(PxReal forceLimit)
{
PX_CHECK_AND_RETURN(PxIsFinite(forceLimit), "PxRevoluteJoint::setDriveForceLimit: invalid parameter");
data().driveForceLimit = forceLimit;
markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, driveForceLimit, static_cast<PxRevoluteJoint&>(*this), forceLimit)
}
PxReal RevoluteJoint::getDriveGearRatio() const
{
return data().driveGearRatio;
}
void RevoluteJoint::setDriveGearRatio(PxReal gearRatio)
{
PX_CHECK_AND_RETURN(PxIsFinite(gearRatio) && gearRatio>0, "PxRevoluteJoint::setDriveGearRatio: invalid parameter");
data().driveGearRatio = gearRatio;
markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, driveGearRatio, static_cast<PxRevoluteJoint&>(*this), gearRatio)
}
PxRevoluteJointFlags RevoluteJoint::getRevoluteJointFlags() const
{
return data().jointFlags;
}
void RevoluteJoint::setRevoluteJointFlags(PxRevoluteJointFlags flags)
{
data().jointFlags = flags;
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, jointFlags, static_cast<PxRevoluteJoint&>(*this), flags)
}
void RevoluteJoint::setRevoluteJointFlag(PxRevoluteJointFlag::Enum flag, bool value)
{
if(value)
data().jointFlags |= flag;
else
data().jointFlags &= ~flag;
markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, jointFlags, static_cast<PxRevoluteJoint&>(*this), getRevoluteJointFlags())
}
static PxQuat computeTwist(const PxTransform& cA2w, const PxTransform& cB2w)
{
// PT: following code is the same as this part of the "getAngle" function:
// const PxQuat q = getRelativeTransform().q;
// PxQuat swing, twist;
// PxSeparateSwingTwist(q, swing, twist);
// But it's done a little bit more efficiently since we don't need the swing quat.
// PT: rotation part of "const PxTransform cB2cA = cA2w.transformInv(cB2w);"
const PxQuat cB2cAq = cA2w.q.getConjugate() * cB2w.q;
// PT: twist part of "PxSeparateSwingTwist(cB2cAq,swing,twist)" (more or less)
return PxQuat(cB2cAq.x, 0.0f, 0.0f, cB2cAq.w);
}
// PT: this version is similar to the "getAngle" function, but the twist is computed slightly differently.
static PX_FORCE_INLINE PxReal computePhi(const PxTransform& cA2w, const PxTransform& cB2w)
{
PxQuat twist = computeTwist(cA2w, cB2w);
twist.normalize();
PxReal angle = twist.getAngle();
if(twist.x<0.0f)
angle = -angle;
return angle;
}
static void RevoluteJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags)
{
const RevoluteJointData& data = *reinterpret_cast<const RevoluteJointData*>(constantBlock);
PxTransform32 cA2w, cB2w;
joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform);
if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES)
viz.visualizeJointFrames(cA2w, cB2w);
if((data.jointFlags & PxRevoluteJointFlag::eLIMIT_ENABLED) && (flags & PxConstraintVisualizationFlag::eLIMITS))
viz.visualizeAngularLimit(cA2w, data.limit.lower, data.limit.upper);
}
//TAG:solverprepshader
static PxU32 RevoluteJointSolverPrep(Px1DConstraint* constraints,
PxVec3p& body0WorldOffset,
PxU32 /*maxConstraints*/,
PxConstraintInvMassScale& invMassScale,
const void* constantBlock,
const PxTransform& bA2w,
const PxTransform& bB2w,
bool useExtendedLimits,
PxVec3p& cA2wOut, PxVec3p& cB2wOut)
{
const RevoluteJointData& data = *reinterpret_cast<const RevoluteJointData*>(constantBlock);
PxTransform32 cA2w, cB2w;
joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w);
const PxJointAngularLimitPair& limit = data.limit;
const bool limitEnabled = data.jointFlags & PxRevoluteJointFlag::eLIMIT_ENABLED;
const bool limitIsLocked = limitEnabled && limit.lower >= limit.upper;
// PT: it is a mistake to use the neighborhood operator since it
// prevents us from using the quat's double-cover feature.
if(!useExtendedLimits)
joint::applyNeighborhoodOperator(cA2w, cB2w);
PxVec3 ra, rb, axis;
ch.prepareLockedAxes(cA2w.q, cB2w.q, cA2w.transformInv(cB2w.p), 7, PxU32(limitIsLocked ? 7 : 6), ra, rb, &axis);
cA2wOut = ra + bA2w.p;
cB2wOut = rb + bB2w.p;
if(limitIsLocked)
return ch.getCount();
if(data.jointFlags & PxRevoluteJointFlag::eDRIVE_ENABLED)
{
Px1DConstraint* c = ch.getConstraintRow();
c->solveHint = PxConstraintSolveHint::eNONE;
c->linear0 = PxVec3(0.0f);
c->angular0 = -axis;
c->linear1 = PxVec3(0.0f);
c->angular1 = -axis * data.driveGearRatio;
c->velocityTarget = data.driveVelocity;
c->minImpulse = -data.driveForceLimit;
c->maxImpulse = data.driveForceLimit;
c->flags |= Px1DConstraintFlag::eANGULAR_CONSTRAINT;
if(data.jointFlags & PxRevoluteJointFlag::eDRIVE_FREESPIN)
{
if(data.driveVelocity > 0.0f)
c->minImpulse = 0.0f;
if(data.driveVelocity < 0.0f)
c->maxImpulse = 0.0f;
}
c->flags |= Px1DConstraintFlag::eHAS_DRIVE_LIMIT;
}
if(limitEnabled)
{
const PxReal phi = computePhi(cA2w, cB2w);
ch.anglePair(phi, data.limit.lower, data.limit.upper, axis, limit);
}
return ch.getCount();
}
///////////////////////////////////////////////////////////////////////////////
static PxConstraintShaderTable gRevoluteJointShaders = { RevoluteJointSolverPrep, RevoluteJointVisualize, PxConstraintFlag::Enum(0) };
PxConstraintSolverPrep RevoluteJoint::getPrep() const { return gRevoluteJointShaders.solverPrep; }
// PT: for tests / benchmarks
PxConstraintSolverPrep getRevoluteJointPrep() { return gRevoluteJointShaders.solverPrep; }
PxRevoluteJoint* physx::PxRevoluteJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1)
{
PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxRevoluteJointCreate: local frame 0 is not a valid transform");
PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxRevoluteJointCreate: local frame 1 is not a valid transform");
PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxRevoluteJointCreate: actors must be different");
PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is<PxRigidBody>()) || (actor1 && actor1->is<PxRigidBody>()), "PxRevoluteJointCreate: at least one actor must be dynamic");
return createJointT<RevoluteJoint, RevoluteJointData>(physics, actor0, localFrame0, actor1, localFrame1, gRevoluteJointShaders);
}
// PX_SERIALIZATION
void RevoluteJoint::resolveReferences(PxDeserializationContext& context)
{
mPxConstraint = resolveConstraintPtr(context, mPxConstraint, this, gRevoluteJointShaders);
}
//~PX_SERIALIZATION
#if PX_SUPPORT_OMNI_PVD
void RevoluteJoint::updateOmniPvdProperties() const
{
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
const PxRevoluteJoint& j = static_cast<const PxRevoluteJoint&>(*this);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, angle, j, getAngle())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, velocity, j, getVelocity())
OMNI_PVD_WRITE_SCOPE_END
}
template<>
void physx::Ext::omniPvdInitJoint<RevoluteJoint>(RevoluteJoint& joint)
{
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
PxRevoluteJoint& j = static_cast<PxRevoluteJoint&>(joint);
OMNI_PVD_CREATE_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, j);
omniPvdSetBaseJointParams(static_cast<PxJoint&>(joint), PxJointConcreteType::eREVOLUTE);
PxJointAngularLimitPair limit = joint.getLimit();
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, limitLower, j, limit.lower)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, limitUpper, j, limit.upper)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, limitRestitution, j, limit.restitution)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, limitBounceThreshold, j, limit.bounceThreshold)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, limitStiffness, j, limit.stiffness)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, limitDamping, j, limit.damping)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, driveVelocity, j, joint.getDriveVelocity())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, driveForceLimit, j, joint.getDriveForceLimit())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, driveGearRatio, j, joint.getDriveGearRatio())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, jointFlags, j, joint.getRevoluteJointFlags())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, angle, j, joint.getAngle())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxRevoluteJoint, velocity, j, joint.getVelocity())
OMNI_PVD_WRITE_SCOPE_END
}
#endif

View File

@@ -0,0 +1,96 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_REVOLUTE_JOINT_H
#define EXT_REVOLUTE_JOINT_H
#include "extensions/PxRevoluteJoint.h"
#include "ExtJoint.h"
#include "foundation/PxIntrinsics.h"
#include "CmUtils.h"
namespace physx
{
struct PxRevoluteJointGeneratedValues;
namespace Ext
{
struct RevoluteJointData : public JointData
{
PxReal driveVelocity;
PxReal driveForceLimit;
PxReal driveGearRatio;
PxJointAngularLimitPair limit;
PxRevoluteJointFlags jointFlags;
// private: // PT: must be public for a benchmark
RevoluteJointData(const PxJointAngularLimitPair& pair) : limit(pair) {}
};
typedef JointT<PxRevoluteJoint, RevoluteJointData, PxRevoluteJointGeneratedValues> RevoluteJointT;
class RevoluteJoint : public RevoluteJointT
{
public:
// PX_SERIALIZATION
RevoluteJoint(PxBaseFlags baseFlags) : RevoluteJointT(baseFlags) {}
void resolveReferences(PxDeserializationContext& context);
static RevoluteJoint* createObject(PxU8*& address, PxDeserializationContext& context) { return createJointObject<RevoluteJoint>(address, context); }
//~PX_SERIALIZATION
RevoluteJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1);
// PxRevoluteJoint
virtual PxReal getAngle() const PX_OVERRIDE;
virtual PxReal getVelocity() const PX_OVERRIDE;
virtual void setLimit(const PxJointAngularLimitPair& limit) PX_OVERRIDE;
virtual PxJointAngularLimitPair getLimit() const PX_OVERRIDE;
virtual void setDriveVelocity(PxReal velocity, bool autowake = true) PX_OVERRIDE;
virtual PxReal getDriveVelocity() const PX_OVERRIDE;
virtual void setDriveForceLimit(PxReal forceLimit) PX_OVERRIDE;
virtual PxReal getDriveForceLimit() const PX_OVERRIDE;
virtual void setDriveGearRatio(PxReal gearRatio) PX_OVERRIDE;
virtual PxReal getDriveGearRatio() const PX_OVERRIDE;
virtual void setRevoluteJointFlags(PxRevoluteJointFlags flags) PX_OVERRIDE;
virtual void setRevoluteJointFlag(PxRevoluteJointFlag::Enum flag, bool value) PX_OVERRIDE;
virtual PxRevoluteJointFlags getRevoluteJointFlags() const PX_OVERRIDE;
//~PxRevoluteJoint
// PxConstraintConnector
virtual PxConstraintSolverPrep getPrep() const PX_OVERRIDE;
#if PX_SUPPORT_OMNI_PVD
virtual void updateOmniPvdProperties() const PX_OVERRIDE;
#endif
//~PxConstraintConnector
};
} // namespace Ext
} // namespace physx
#endif

View File

@@ -0,0 +1,89 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxRigidActorExt.h"
#include "foundation/PxFPU.h"
#include "foundation/PxAllocator.h"
#include "foundation/PxInlineArray.h"
#include "geometry/PxGeometryQuery.h"
#include "cooking/PxBVHDesc.h"
#include "cooking/PxCooking.h"
using namespace physx;
PxBounds3* PxRigidActorExt::getRigidActorShapeLocalBoundsList(const PxRigidActor& actor, PxU32& numBounds)
{
const PxU32 numShapes = actor.getNbShapes();
if(numShapes == 0)
return NULL;
PxInlineArray<PxShape*, 64> shapes("PxShape*");
shapes.resize(numShapes);
actor.getShapes(shapes.begin(), shapes.size());
PxU32 numSqShapes = 0;
for(PxU32 i=0; i<numShapes; i++)
{
if(shapes[i]->getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE)
numSqShapes++;
}
PxBounds3* bounds = PX_ALLOCATE(PxBounds3, numSqShapes, "PxBounds3");
numSqShapes = 0;
{
PX_SIMD_GUARD // PT: external guard because we use PxGeometryQueryFlag::Enum(0) below
for(PxU32 i=0; i<numShapes; i++)
{
if(shapes[i]->getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE)
PxGeometryQuery::computeGeomBounds(bounds[numSqShapes++], shapes[i]->getGeometry(), shapes[i]->getLocalPose(), 0.0f, 1.0f, PxGeometryQueryFlag::Enum(0));
}
}
numBounds = numSqShapes;
return bounds;
}
PxBVH* PxRigidActorExt::createBVHFromActor(PxPhysics& physics, const PxRigidActor& actor)
{
PxU32 nbBounds = 0;
PxBounds3* bounds = PxRigidActorExt::getRigidActorShapeLocalBoundsList(actor, nbBounds);
PxBVHDesc bvhDesc;
bvhDesc.bounds.count = nbBounds;
bvhDesc.bounds.data = bounds;
bvhDesc.bounds.stride = sizeof(PxBounds3);
PxBVH* bvh = PxCreateBVH(bvhDesc, physics.getPhysicsInsertionCallback());
PX_FREE(bounds);
return bvh;
}

View File

@@ -0,0 +1,639 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "geometry/PxBoxGeometry.h"
#include "geometry/PxSphereGeometry.h"
#include "geometry/PxCapsuleGeometry.h"
#include "geometry/PxPlaneGeometry.h"
#include "geometry/PxConvexMeshGeometry.h"
#include "geometry/PxTriangleMeshGeometry.h"
#include "geometry/PxHeightFieldGeometry.h"
#include "geometry/PxGeometryHelpers.h"
#include "geometry/PxConvexMesh.h"
#include "geometry/PxTriangleMesh.h"
#include "extensions/PxRigidBodyExt.h"
#include "extensions/PxShapeExt.h"
#include "extensions/PxMassProperties.h"
#include "PxShape.h"
#include "PxScene.h"
#include "PxRigidDynamic.h"
#include "PxRigidStatic.h"
#include "ExtInertiaTensor.h"
#include "foundation/PxAllocator.h"
#include "foundation/PxSIMDHelpers.h"
#include "CmUtils.h"
using namespace physx;
using namespace Cm;
static bool computeMassAndDiagInertia(Ext::InertiaTensorComputer& inertiaComp,
PxVec3& diagTensor, PxQuat& orient, PxReal& massOut, PxVec3& coM, bool lockCOM, const PxRigidBody& body, const char* errorStr)
{
// The inertia tensor and center of mass is relative to the actor at this point. Transform to the
// body frame directly if CoM is specified, else use computed center of mass
if (lockCOM)
{
inertiaComp.translate(-coM); // base the tensor on user's desired center of mass.
}
else
{
//get center of mass - has to be done BEFORE centering.
coM = inertiaComp.getCenterOfMass();
//the computed result now needs to be centered around the computed center of mass:
inertiaComp.center();
}
// The inertia matrix is now based on the body's center of mass desc.massLocalPose.p
massOut = inertiaComp.getMass();
diagTensor = PxDiagonalize(inertiaComp.getInertia(), orient);
if ((diagTensor.x > 0.0f) && (diagTensor.y > 0.0f) && (diagTensor.z > 0.0f))
return true;
else
{
PxGetFoundation().error(PxErrorCode::eDEBUG_WARNING, PX_FL,
"%s: inertia tensor has negative components (ill-conditioned input expected). Approximation for inertia tensor will be used instead.", errorStr);
// keep center of mass but use the AABB as a crude approximation for the inertia tensor
PxBounds3 bounds = body.getWorldBounds();
const PxTransform pose = body.getGlobalPose();
bounds = PxBounds3::transformFast(pose.getInverse(), bounds);
Ext::InertiaTensorComputer it(false);
it.setBox(bounds.getExtents());
it.scaleDensity(massOut / it.getMass());
const PxMat33 inertia = it.getInertia();
diagTensor = PxVec3(inertia.column0.x, inertia.column1.y, inertia.column2.z);
orient = PxQuat(PxIdentity);
return true;
}
}
static bool computeMassAndInertia(Ext::InertiaTensorComputer& inertiaComp, bool multipleMassOrDensity, PxRigidBody& body, const PxReal* densities, const PxReal* masses, PxU32 densityOrMassCount, bool includeNonSimShapes)
{
PX_ASSERT(!densities || !masses);
PX_ASSERT((densities || masses) && (densityOrMassCount > 0));
PxInlineArray<PxShape*, 16> shapes("PxShape*"); shapes.resize(body.getNbShapes());
body.getShapes(shapes.begin(), shapes.size());
PxU32 validShapeIndex = 0;
PxReal currentMassOrDensity;
const PxReal* massOrDensityArray;
if (densities)
{
massOrDensityArray = densities;
currentMassOrDensity = densities[0];
}
else
{
massOrDensityArray = masses;
currentMassOrDensity = masses[0];
}
if (!PxIsFinite(currentMassOrDensity))
return PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "computeMassAndInertia: Provided mass or density has no valid value");
for(PxU32 i=0; i < shapes.size(); i++)
{
if ((!(shapes[i]->getFlags() & PxShapeFlag::eSIMULATION_SHAPE)) && (!includeNonSimShapes))
continue;
if (multipleMassOrDensity)
{
if (validShapeIndex < densityOrMassCount)
{
currentMassOrDensity = massOrDensityArray[validShapeIndex];
if (!PxIsFinite(currentMassOrDensity))
return PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "computeMassAndInertia: Provided mass or density has no valid value");
}
else
return PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "computeMassAndInertia: Not enough mass/density values provided for all (simulation) shapes");
}
Ext::InertiaTensorComputer it(false);
const PxGeometry& geom = shapes[i]->getGeometry();
switch(geom.getType())
{
case PxGeometryType::eSPHERE :
{
const PxSphereGeometry& g = static_cast<const PxSphereGeometry&>(geom);
PxTransform temp(shapes[i]->getLocalPose());
it.setSphere(g.radius, &temp);
}
break;
case PxGeometryType::eBOX :
{
const PxBoxGeometry& g = static_cast<const PxBoxGeometry&>(geom);
PxTransform temp(shapes[i]->getLocalPose());
it.setBox(g.halfExtents, &temp);
}
break;
case PxGeometryType::eCAPSULE :
{
const PxCapsuleGeometry& g = static_cast<const PxCapsuleGeometry&>(geom);
PxTransform temp(shapes[i]->getLocalPose());
it.setCapsule(0, g.radius, g.halfHeight, &temp);
}
break;
case PxGeometryType::eCONVEXCORE:
{
PxMassProperties mp(shapes[i]->getGeometry());
it = Ext::InertiaTensorComputer(mp.inertiaTensor, mp.centerOfMass, mp.mass);
it.transform(shapes[i]->getLocalPose());
}
break;
case PxGeometryType::eCONVEXMESH :
{
const PxConvexMeshGeometry& g = static_cast<const PxConvexMeshGeometry&>(geom);
PxConvexMesh& convMesh = *g.convexMesh;
PxReal convMass;
PxMat33 convInertia;
PxVec3 convCoM;
convMesh.getMassInformation(convMass, convInertia, convCoM);
if (!g.scale.isIdentity())
{
//scale the mass properties
convMass *= (g.scale.scale.x * g.scale.scale.y * g.scale.scale.z);
convCoM = g.scale.transform(convCoM);
convInertia = PxMassProperties::scaleInertia(convInertia, g.scale.rotation, g.scale.scale);
}
it = Ext::InertiaTensorComputer(convInertia, convCoM, convMass);
it.transform(shapes[i]->getLocalPose());
}
break;
case PxGeometryType::eCUSTOM:
{
PxMassProperties mp(shapes[i]->getGeometry());
it = Ext::InertiaTensorComputer(mp.inertiaTensor, mp.centerOfMass, mp.mass);
it.transform(shapes[i]->getLocalPose());
}
break;
case PxGeometryType::eTRIANGLEMESH:
{
const PxTriangleMeshGeometry& g = static_cast<const PxTriangleMeshGeometry&>(geom);
PxReal mass;
PxMat33 inertia;
PxVec3 centerOfMass;
g.triangleMesh->getMassInformation(mass, inertia, centerOfMass);
if (!g.scale.isIdentity())
{
//scale the mass properties
mass *= (g.scale.scale.x * g.scale.scale.y * g.scale.scale.z);
centerOfMass = g.scale.transform(centerOfMass);
inertia = PxMassProperties::scaleInertia(inertia, g.scale.rotation, g.scale.scale);
}
it = Ext::InertiaTensorComputer(inertia, centerOfMass, mass);
it.transform(shapes[i]->getLocalPose());
}
break;
default:
{
return PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "computeMassAndInertia: Dynamic actor with illegal collision shapes");
}
}
if (densities)
it.scaleDensity(currentMassOrDensity);
else if (multipleMassOrDensity) // mass per shape -> need to scale density per shape
it.scaleDensity(currentMassOrDensity / it.getMass());
inertiaComp.add(it);
validShapeIndex++;
}
if (validShapeIndex && masses && (!multipleMassOrDensity)) // at least one simulation shape and single mass for all shapes -> scale density at the end
inertiaComp.scaleDensity(currentMassOrDensity / inertiaComp.getMass());
return true;
}
static bool updateMassAndInertia(bool multipleMassOrDensity, PxRigidBody& body, const PxReal* densities, PxU32 densityCount, const PxVec3* massLocalPose, bool includeNonSimShapes)
{
bool success;
// default values in case there were no shapes
PxReal massOut = 1.0f;
PxVec3 diagTensor(1.0f);
PxQuat orient(PxIdentity);
bool lockCom = massLocalPose != NULL;
PxVec3 com = lockCom ? *massLocalPose : PxVec3(0);
const char* errorStr = "PxRigidBodyExt::updateMassAndInertia";
if (densities && densityCount)
{
Ext::InertiaTensorComputer inertiaComp(true);
if(computeMassAndInertia(inertiaComp, multipleMassOrDensity, body, densities, NULL, densityCount, includeNonSimShapes))
{
if(inertiaComp.getMass()!=0 && computeMassAndDiagInertia(inertiaComp, diagTensor, orient, massOut, com, lockCom, body, errorStr))
success = true;
else
success = false; // body with no shapes provided or computeMassAndDiagInertia() failed
}
else
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL,
"%s: Mass and inertia computation failed, setting mass to 1 and inertia to (1,1,1)", errorStr);
success = false;
}
}
else
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL,
"%s: No density specified, setting mass to 1 and inertia to (1,1,1)", errorStr);
success = false;
}
PX_ASSERT(orient.isFinite());
PX_ASSERT(diagTensor.isFinite());
PX_ASSERT(PxIsFinite(massOut));
body.setMass(massOut);
body.setMassSpaceInertiaTensor(diagTensor);
body.setCMassLocalPose(PxTransform(com, orient));
return success;
}
bool PxRigidBodyExt::updateMassAndInertia(PxRigidBody& body, const PxReal* densities, PxU32 densityCount, const PxVec3* massLocalPose, bool includeNonSimShapes)
{
return ::updateMassAndInertia(true, body, densities, densityCount, massLocalPose, includeNonSimShapes);
}
bool PxRigidBodyExt::updateMassAndInertia(PxRigidBody& body, PxReal density, const PxVec3* massLocalPose, bool includeNonSimShapes)
{
return ::updateMassAndInertia(false, body, &density, 1, massLocalPose, includeNonSimShapes);
}
static bool setMassAndUpdateInertia(bool multipleMassOrDensity, PxRigidBody& body, const PxReal* masses, PxU32 massCount, const PxVec3* massLocalPose, bool includeNonSimShapes)
{
bool success;
// default values in case there were no shapes
PxReal massOut = 1.0f;
PxVec3 diagTensor(1.0f);
PxQuat orient(PxIdentity);
bool lockCom = massLocalPose != NULL;
PxVec3 com = lockCom ? *massLocalPose : PxVec3(0);
const char* errorStr = "PxRigidBodyExt::setMassAndUpdateInertia";
if(masses && massCount)
{
Ext::InertiaTensorComputer inertiaComp(true);
if(computeMassAndInertia(inertiaComp, multipleMassOrDensity, body, NULL, masses, massCount, includeNonSimShapes))
{
success = true;
if (inertiaComp.getMass()!=0 && !computeMassAndDiagInertia(inertiaComp, diagTensor, orient, massOut, com, lockCom, body, errorStr))
success = false; // computeMassAndDiagInertia() failed (mass zero?)
if (massCount == 1)
massOut = masses[0]; // to cover special case where body has no simulation shape
}
else
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL,
"%s: Mass and inertia computation failed, setting mass to 1 and inertia to (1,1,1)", errorStr);
success = false;
}
}
else
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL,
"%s: No mass specified, setting mass to 1 and inertia to (1,1,1)", errorStr);
success = false;
}
PX_ASSERT(orient.isFinite());
PX_ASSERT(diagTensor.isFinite());
body.setMass(massOut);
body.setMassSpaceInertiaTensor(diagTensor);
body.setCMassLocalPose(PxTransform(com, orient));
return success;
}
bool PxRigidBodyExt::setMassAndUpdateInertia(PxRigidBody& body, const PxReal* masses, PxU32 massCount, const PxVec3* massLocalPose, bool includeNonSimShapes)
{
return ::setMassAndUpdateInertia(true, body, masses, massCount, massLocalPose, includeNonSimShapes);
}
bool PxRigidBodyExt::setMassAndUpdateInertia(PxRigidBody& body, PxReal mass, const PxVec3* massLocalPose, bool includeNonSimShapes)
{
return ::setMassAndUpdateInertia(false, body, &mass, 1, massLocalPose, includeNonSimShapes);
}
PxMassProperties PxRigidBodyExt::computeMassPropertiesFromShapes(const PxShape* const* shapes, PxU32 shapeCount)
{
PxInlineArray<PxMassProperties, 16> massProps;
massProps.reserve(shapeCount);
PxInlineArray<PxTransform, 16> localTransforms;
localTransforms.reserve(shapeCount);
for(PxU32 shapeIdx=0; shapeIdx < shapeCount; shapeIdx++)
{
const PxShape* shape = shapes[shapeIdx];
PxMassProperties mp(shape->getGeometry());
massProps.pushBack(mp);
localTransforms.pushBack(shape->getLocalPose());
}
return PxMassProperties::sum(massProps.begin(), localTransforms.begin(), shapeCount);
}
PX_INLINE void addForceAtPosInternal(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup)
{
if(mode == PxForceMode::eACCELERATION || mode == PxForceMode::eVELOCITY_CHANGE)
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL,
"PxRigidBodyExt::addForce methods do not support eACCELERATION or eVELOCITY_CHANGE modes");
return;
}
const PxTransform globalPose = body.getGlobalPose();
const PxVec3 centerOfMass = globalPose.transform(body.getCMassLocalPose().p);
const PxVec3 torque = (pos - centerOfMass).cross(force);
body.addForce(force, mode, wakeup);
body.addTorque(torque, mode, wakeup);
}
void PxRigidBodyExt::addForceAtPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup)
{
addForceAtPosInternal(body, force, pos, mode, wakeup);
}
void PxRigidBodyExt::addForceAtLocalPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup)
{
//transform pos to world space
const PxVec3 globalForcePos = body.getGlobalPose().transform(pos);
addForceAtPosInternal(body, force, globalForcePos, mode, wakeup);
}
void PxRigidBodyExt::addLocalForceAtPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup)
{
const PxVec3 globalForce = body.getGlobalPose().rotate(force);
addForceAtPosInternal(body, globalForce, pos, mode, wakeup);
}
void PxRigidBodyExt::addLocalForceAtLocalPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup)
{
const PxTransform globalPose = body.getGlobalPose();
const PxVec3 globalForcePos = globalPose.transform(pos);
const PxVec3 globalForce = globalPose.rotate(force);
addForceAtPosInternal(body, globalForce, globalForcePos, mode, wakeup);
}
PX_INLINE PxVec3 getVelocityAtPosInternal(const PxRigidBody& body, const PxVec3& point)
{
PxVec3 velocity = body.getLinearVelocity();
velocity += body.getAngularVelocity().cross(point);
return velocity;
}
PxVec3 PxRigidBodyExt::getVelocityAtPos(const PxRigidBody& body, const PxVec3& point)
{
const PxTransform globalPose = body.getGlobalPose();
const PxVec3 centerOfMass = globalPose.transform(body.getCMassLocalPose().p);
const PxVec3 rpoint = point - centerOfMass;
return getVelocityAtPosInternal(body, rpoint);
}
PxVec3 PxRigidBodyExt::getLocalVelocityAtLocalPos(const PxRigidBody& body, const PxVec3& point)
{
const PxTransform globalPose = body.getGlobalPose();
const PxVec3 centerOfMass = globalPose.transform(body.getCMassLocalPose().p);
const PxVec3 rpoint = globalPose.transform(point) - centerOfMass;
return getVelocityAtPosInternal(body, rpoint);
}
PxVec3 PxRigidBodyExt::getVelocityAtOffset(const PxRigidBody& body, const PxVec3& point)
{
const PxTransform globalPose = body.getGlobalPose();
const PxVec3 centerOfMass = globalPose.rotate(body.getCMassLocalPose().p);
const PxVec3 rpoint = point - centerOfMass;
return getVelocityAtPosInternal(body, rpoint);
}
void PxRigidBodyExt::computeVelocityDeltaFromImpulse(const PxRigidBody& body, const PxTransform& globalPose, const PxVec3& point, const PxVec3& impulse, const PxReal invMassScale,
const PxReal invInertiaScale, PxVec3& linearVelocityChange, PxVec3& angularVelocityChange)
{
const PxVec3 centerOfMass = globalPose.transform(body.getCMassLocalPose().p);
const PxReal invMass = body.getInvMass() * invMassScale;
const PxVec3 invInertiaMS = body.getMassSpaceInvInertiaTensor() * invInertiaScale;
PxMat33 invInertia;
transformInertiaTensor(invInertiaMS, PxMat33Padded(globalPose.q), invInertia);
linearVelocityChange = impulse * invMass;
const PxVec3 rXI = (point - centerOfMass).cross(impulse);
angularVelocityChange = invInertia * rXI;
}
void PxRigidBodyExt::computeLinearAngularImpulse(const PxRigidBody& body, const PxTransform& globalPose, const PxVec3& point, const PxVec3& impulse, const PxReal invMassScale,
const PxReal invInertiaScale, PxVec3& linearImpulse, PxVec3& angularImpulse)
{
const PxVec3 centerOfMass = globalPose.transform(body.getCMassLocalPose().p);
linearImpulse = impulse * invMassScale;
angularImpulse = (point - centerOfMass).cross(impulse) * invInertiaScale;
}
void PxRigidBodyExt::computeVelocityDeltaFromImpulse(const PxRigidBody& body, const PxVec3& impulsiveForce, const PxVec3& impulsiveTorque, PxVec3& deltaLinearVelocity, PxVec3& deltaAngularVelocity)
{
{
const PxF32 recipMass = body.getInvMass();
deltaLinearVelocity = impulsiveForce*recipMass;
}
{
const PxTransform globalPose = body.getGlobalPose();
const PxTransform cmLocalPose = body.getCMassLocalPose();
// PT:: tag: scalar transform*transform
const PxTransform body2World = globalPose*cmLocalPose;
const PxMat33Padded M(body2World.q);
const PxVec3 recipInertiaBodySpace = body.getMassSpaceInvInertiaTensor();
PxMat33 recipInertiaWorldSpace;
const float axx = recipInertiaBodySpace.x*M(0,0), axy = recipInertiaBodySpace.x*M(1,0), axz = recipInertiaBodySpace.x*M(2,0);
const float byx = recipInertiaBodySpace.y*M(0,1), byy = recipInertiaBodySpace.y*M(1,1), byz = recipInertiaBodySpace.y*M(2,1);
const float czx = recipInertiaBodySpace.z*M(0,2), czy = recipInertiaBodySpace.z*M(1,2), czz = recipInertiaBodySpace.z*M(2,2);
recipInertiaWorldSpace(0,0) = axx*M(0,0) + byx*M(0,1) + czx*M(0,2);
recipInertiaWorldSpace(1,1) = axy*M(1,0) + byy*M(1,1) + czy*M(1,2);
recipInertiaWorldSpace(2,2) = axz*M(2,0) + byz*M(2,1) + czz*M(2,2);
recipInertiaWorldSpace(0,1) = recipInertiaWorldSpace(1,0) = axx*M(1,0) + byx*M(1,1) + czx*M(1,2);
recipInertiaWorldSpace(0,2) = recipInertiaWorldSpace(2,0) = axx*M(2,0) + byx*M(2,1) + czx*M(2,2);
recipInertiaWorldSpace(1,2) = recipInertiaWorldSpace(2,1) = axy*M(2,0) + byy*M(2,1) + czy*M(2,2);
deltaAngularVelocity = recipInertiaWorldSpace*(impulsiveTorque);
}
}
//=================================================================================
// Single closest hit compound sweep
bool PxRigidBodyExt::linearSweepSingle(
PxRigidBody& body, PxScene& scene, const PxVec3& unitDir, const PxReal distance,
PxHitFlags outputFlags, PxSweepHit& closestHit, PxU32& shapeIndex,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, const PxReal inflation)
{
shapeIndex = 0xFFFFffff;
PxReal closestDist = distance;
PxU32 nbShapes = body.getNbShapes();
for(PxU32 i=0; i < nbShapes; i++)
{
PxShape* shape = NULL;
body.getShapes(&shape, 1, i);
PX_ASSERT(shape != NULL);
PxTransform pose = PxShapeExt::getGlobalPose(*shape, body);
PxQueryFilterData fd;
fd.flags = filterData.flags;
PxU32 or4 = (filterData.data.word0 | filterData.data.word1 | filterData.data.word2 | filterData.data.word3);
fd.data = or4 ? filterData.data : shape->getQueryFilterData();
PxSweepBuffer subHit; // touching hits are not allowed to be returned from the filters
scene.sweep(shape->getGeometry(), pose, unitDir, distance, subHit, outputFlags, fd, filterCall, cache, inflation);
if (subHit.hasBlock && subHit.block.distance < closestDist)
{
closestDist = subHit.block.distance;
closestHit = subHit.block;
shapeIndex = i;
}
}
return (shapeIndex != 0xFFFFffff);
}
//=================================================================================
// Multiple hits compound sweep
// AP: we might be able to improve the return results API but no time for it in 3.3
PxU32 PxRigidBodyExt::linearSweepMultiple(
PxRigidBody& body, PxScene& scene, const PxVec3& unitDir, const PxReal distance, PxHitFlags outputFlags,
PxSweepHit* hitBuffer, PxU32* hitShapeIndices, PxU32 hitBufferSize, PxSweepHit& block, PxI32& blockingHitShapeIndex,
bool& overflow, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, const PxReal inflation)
{
overflow = false;
blockingHitShapeIndex = -1;
for (PxU32 i = 0; i < hitBufferSize; i++)
hitShapeIndices[i] = 0xFFFFffff;
PxI32 sumNbResults = 0;
PxU32 nbShapes = body.getNbShapes();
PxF32 shrunkMaxDistance = distance;
for(PxU32 i=0; i < nbShapes; i++)
{
PxShape* shape = NULL;
body.getShapes(&shape, 1, i);
PX_ASSERT(shape != NULL);
PxTransform pose = PxShapeExt::getGlobalPose(*shape, body);
PxQueryFilterData fd;
fd.flags = filterData.flags;
PxU32 or4 = (filterData.data.word0 | filterData.data.word1 | filterData.data.word2 | filterData.data.word3);
fd.data = or4 ? filterData.data : shape->getQueryFilterData();
PxU32 bufSizeLeft = hitBufferSize-sumNbResults;
PxSweepHit extraHit;
PxSweepBuffer buffer(bufSizeLeft == 0 ? &extraHit : hitBuffer+sumNbResults, bufSizeLeft == 0 ? 1 : hitBufferSize-sumNbResults);
scene.sweep(shape->getGeometry(), pose, unitDir, shrunkMaxDistance, buffer, outputFlags, fd, filterCall, cache, inflation);
// Check and abort on overflow. Assume overflow if result count is bufSize.
PxU32 nbNewResults = buffer.getNbTouches();
overflow |= (nbNewResults >= bufSizeLeft);
if (bufSizeLeft == 0) // this is for when we used the extraHit buffer
nbNewResults = 0;
// set hitShapeIndices for each new non-blocking hit
for (PxU32 j = 0; j < nbNewResults; j++)
if (sumNbResults + PxU32(j) < hitBufferSize)
hitShapeIndices[sumNbResults+j] = i;
if (buffer.hasBlock) // there's a blocking hit in the most recent sweepMultiple results
{
// overwrite the return result blocking hit with the new blocking hit if under
if (blockingHitShapeIndex == -1 || buffer.block.distance < block.distance)
{
blockingHitShapeIndex = PxI32(i);
block = buffer.block;
}
// Remove all the old touching hits below the new maxDist
// sumNbResults is not updated yet at this point
// and represents the count accumulated so far excluding the very last query
PxI32 nbNewResultsSigned = PxI32(nbNewResults); // need a signed version, see nbNewResultsSigned-- below
for (PxI32 j = sumNbResults-1; j >= 0; j--) // iterate over "old" hits (up to shapeIndex-1)
if (buffer.block.distance < hitBuffer[j].distance)
{
// overwrite with last "new" hit
PxI32 sourceIndex = PxI32(sumNbResults)+nbNewResultsSigned-1; PX_ASSERT(sourceIndex >= j);
hitBuffer[j] = hitBuffer[sourceIndex];
hitShapeIndices[j] = hitShapeIndices[sourceIndex];
nbNewResultsSigned--; // can get negative, that means we are shifting the last results array
}
sumNbResults += nbNewResultsSigned;
} else // if there was no new blocking hit we don't need to do anything special, simply append all results to touch array
sumNbResults += nbNewResults;
PX_ASSERT(sumNbResults >= 0 && sumNbResults <= PxI32(hitBufferSize));
}
return PxU32(sumNbResults);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,696 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxSceneQueryExt.h"
#include "geometry/PxGeometryHelpers.h"
#include "foundation/PxAllocatorCallback.h"
#include "CmUtils.h"
#include "foundation/PxAllocator.h"
using namespace physx;
bool PxSceneQueryExt::raycastAny( const PxScene& scene,
const PxVec3& origin, const PxVec3& unitDir, const PxReal distance,
PxSceneQueryHit& hit, const PxSceneQueryFilterData& filterData,
PxSceneQueryFilterCallback* filterCall, const PxSceneQueryCache* cache)
{
PxSceneQueryFilterData fdAny = filterData;
fdAny.flags |= PxQueryFlag::eANY_HIT;
PxRaycastBuffer buf;
scene.raycast(origin, unitDir, distance, buf, PxHitFlag::eANY_HIT, fdAny, filterCall, cache);
hit = buf.block;
return buf.hasBlock;
}
bool PxSceneQueryExt::raycastSingle(const PxScene& scene,
const PxVec3& origin, const PxVec3& unitDir, const PxReal distance,
PxSceneQueryFlags outputFlags, PxRaycastHit& hit,
const PxSceneQueryFilterData& filterData,
PxSceneQueryFilterCallback* filterCall, const PxSceneQueryCache* cache)
{
PxRaycastBuffer buf;
PxQueryFilterData fd1 = filterData;
scene.raycast(origin, unitDir, distance, buf, outputFlags, fd1, filterCall, cache);
hit = buf.block;
return buf.hasBlock;
}
PxI32 PxSceneQueryExt::raycastMultiple( const PxScene& scene,
const PxVec3& origin, const PxVec3& unitDir, const PxReal distance,
PxSceneQueryFlags outputFlags,
PxRaycastHit* hitBuffer, PxU32 hitBufferSize, bool& blockingHit,
const PxSceneQueryFilterData& filterData,
PxSceneQueryFilterCallback* filterCall, const PxSceneQueryCache* cache)
{
PxRaycastBuffer buf(hitBuffer, hitBufferSize);
PxQueryFilterData fd1 = filterData;
scene.raycast(origin, unitDir, distance, buf, outputFlags, fd1, filterCall, cache);
blockingHit = buf.hasBlock;
if(blockingHit)
{
if(buf.nbTouches < hitBufferSize)
{
hitBuffer[buf.nbTouches] = buf.block;
return PxI32(buf.nbTouches+1);
}
else // overflow, drop the last touch
{
hitBuffer[hitBufferSize-1] = buf.block;
return -1;
}
} else
// no block
return PxI32(buf.nbTouches);
}
bool PxSceneQueryExt::sweepAny( const PxScene& scene,
const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance,
PxSceneQueryFlags queryFlags,
PxSceneQueryHit& hit,
const PxSceneQueryFilterData& filterData,
PxSceneQueryFilterCallback* filterCall,
const PxSceneQueryCache* cache,
PxReal inflation)
{
PxSceneQueryFilterData fdAny = filterData;
fdAny.flags |= PxQueryFlag::eANY_HIT;
PxSweepBuffer buf;
scene.sweep(geometry, pose, unitDir, distance, buf, queryFlags|PxHitFlag::eANY_HIT, fdAny, filterCall, cache, inflation);
hit = buf.block;
return buf.hasBlock;
}
bool PxSceneQueryExt::sweepSingle( const PxScene& scene,
const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance,
PxSceneQueryFlags outputFlags,
PxSweepHit& hit,
const PxSceneQueryFilterData& filterData,
PxSceneQueryFilterCallback* filterCall,
const PxSceneQueryCache* cache,
PxReal inflation)
{
PxSweepBuffer buf;
PxQueryFilterData fd1 = filterData;
scene.sweep(geometry, pose, unitDir, distance, buf, outputFlags, fd1, filterCall, cache, inflation);
hit = buf.block;
return buf.hasBlock;
}
PxI32 PxSceneQueryExt::sweepMultiple( const PxScene& scene,
const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance,
PxSceneQueryFlags outputFlags, PxSweepHit* hitBuffer, PxU32 hitBufferSize, bool& blockingHit,
const PxSceneQueryFilterData& filterData,
PxSceneQueryFilterCallback* filterCall, const PxSceneQueryCache* cache,
PxReal inflation)
{
PxQueryFilterData fd1 = filterData;
PxSweepBuffer buf(hitBuffer, hitBufferSize);
scene.sweep(geometry, pose, unitDir, distance, buf, outputFlags, fd1, filterCall, cache, inflation);
blockingHit = buf.hasBlock;
if(blockingHit)
{
if(buf.nbTouches < hitBufferSize)
{
hitBuffer[buf.nbTouches] = buf.block;
return PxI32(buf.nbTouches+1);
}
else // overflow, drop the last touch
{
hitBuffer[hitBufferSize-1] = buf.block;
return -1;
}
} else
// no block
return PxI32(buf.nbTouches);
}
PxI32 PxSceneQueryExt::overlapMultiple( const PxScene& scene,
const PxGeometry& geometry, const PxTransform& pose,
PxOverlapHit* hitBuffer, PxU32 hitBufferSize,
const PxSceneQueryFilterData& filterData,
PxSceneQueryFilterCallback* filterCall)
{
PxQueryFilterData fd1 = filterData;
fd1.flags |= PxQueryFlag::eNO_BLOCK;
PxOverlapBuffer buf(hitBuffer, hitBufferSize);
scene.overlap(geometry, pose, buf, fd1, filterCall);
if(buf.hasBlock)
{
if(buf.nbTouches < hitBufferSize)
{
hitBuffer[buf.nbTouches] = buf.block;
return PxI32(buf.nbTouches+1);
}
else // overflow, drop the last touch
{
hitBuffer[hitBufferSize-1] = buf.block;
return -1;
}
} else
// no block
return PxI32(buf.nbTouches);
}
bool PxSceneQueryExt::overlapAny( const PxScene& scene,
const PxGeometry& geometry, const PxTransform& pose,
PxOverlapHit& hit,
const PxSceneQueryFilterData& filterData,
PxSceneQueryFilterCallback* filterCall)
{
PxSceneQueryFilterData fdAny = filterData;
fdAny.flags |= (PxQueryFlag::eANY_HIT | PxQueryFlag::eNO_BLOCK);
PxOverlapBuffer buf;
scene.overlap(geometry, pose, buf, fdAny, filterCall);
hit = buf.block;
return buf.hasBlock;
}
namespace
{
struct Raycast
{
PxVec3 origin;
PxVec3 unitDir;
PxReal distance;
PxHitFlags hitFlags;
PxQueryFilterData filterData;
const PxQueryCache* cache;
};
struct Sweep
{
PxGeometryHolder geometry;
PxTransform pose;
PxVec3 unitDir;
PxReal distance;
PxHitFlags hitFlags;
PxQueryFilterData filterData;
const PxQueryCache* cache;
PxReal inflation;
};
struct Overlap
{
PxGeometryHolder geometry;
PxTransform pose;
PxQueryFilterData filterData;
const PxQueryCache* cache;
};
}
template<typename HitType>
struct NpOverflowBuffer : PxHitBuffer<HitType>
{
bool overflow;
bool processCalled;
PxU32 saveNbTouches;
NpOverflowBuffer(HitType* hits, PxU32 count) : PxHitBuffer<HitType>(hits, count), overflow(false), processCalled(false), saveNbTouches(0)
{
}
virtual PxAgain processTouches(const HitType* /*hits*/, PxU32 /*count*/)
{
if (processCalled)
return false;
saveNbTouches = this->nbTouches;
processCalled = true;
return true;
}
virtual void finalizeQuery()
{
if (processCalled)
{
overflow = (this->nbTouches > 0);
this->nbTouches = saveNbTouches;
}
}
};
class ExtBatchQuery : public PxBatchQueryExt
{
PX_NOCOPY(ExtBatchQuery)
public:
ExtBatchQuery(
const PxScene& scene,
PxQueryFilterCallback* queryFilterCallback,
PxRaycastBuffer* raycastBuffers, Raycast* raycastQueries, const PxU32 maxNbRaycasts, PxRaycastHit* raycastTouches, const PxU32 maxNbRaycastTouches,
PxSweepBuffer* sweepBuffers, Sweep* sweepQueries, const PxU32 maxNbSweeps, PxSweepHit* sweepTouches, const PxU32 maxNbSweepTouches,
PxOverlapBuffer* overlapBuffers, Overlap* overlapQueries, const PxU32 maxNbOverlaps, PxOverlapHit* overlapTouches, const PxU32 maxNbOverlapTouches);
~ExtBatchQuery() {}
virtual void release();
virtual PxRaycastBuffer* raycast(
const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, const PxU16 maxNbTouches,
PxHitFlags hitFlags = PxHitFlags(PxHitFlag::eDEFAULT),
const PxQueryFilterData& filterData = PxQueryFilterData(),
const PxQueryCache* cache = NULL);
virtual PxSweepBuffer* sweep(
const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, const PxU16 maxNbTouches,
PxHitFlags hitFlags = PxHitFlags(PxHitFlag::eDEFAULT),
const PxQueryFilterData& filterData = PxQueryFilterData(),
const PxQueryCache* cache = NULL,
const PxReal inflation = 0.f);
virtual PxOverlapBuffer* overlap(
const PxGeometry& geometry, const PxTransform& pose, PxU16 maxNbTouches = 0,
const PxQueryFilterData& filterData = PxQueryFilterData(),
const PxQueryCache* cache = NULL);
virtual void execute();
private:
template<typename HitType, typename QueryType> struct Query
{
PxHitBuffer<HitType>* mBuffers;
QueryType* mQueries;
PxU32 mMaxNbBuffers;
HitType* mTouches;
PxU32 mMaxNbTouches;
PxU32 mBufferTide;
Query()
: mBuffers(NULL),
mQueries(NULL),
mMaxNbBuffers(0),
mTouches(NULL),
mMaxNbTouches(0),
mBufferTide(0)
{
}
Query(PxHitBuffer<HitType>* buffers, QueryType* queries, const PxU32 maxNbBuffers, HitType* touches, const PxU32 maxNbTouches)
: mBuffers(buffers),
mQueries(queries),
mMaxNbBuffers(maxNbBuffers),
mTouches(touches),
mMaxNbTouches(maxNbTouches),
mBufferTide(0)
{
for (PxU32 i = 0; i < mMaxNbBuffers; i++)
{
mBuffers[i].hasBlock = false;
mBuffers[i].nbTouches = 0;
}
}
PxHitBuffer<HitType>* addQuery(const QueryType& query, const PxU32 maxNbTouches)
{
if ((mBufferTide + 1) > mMaxNbBuffers)
{
//Ran out of queries.
return NULL;
}
PxHitBuffer<HitType>* buffer = mBuffers + mBufferTide;
buffer->touches = NULL;
buffer->maxNbTouches = maxNbTouches;
buffer->hasBlock = false;
buffer->nbTouches = 0xffffffff;
mQueries[mBufferTide] = query;
mBufferTide++;
return buffer;
}
static void performQuery(const PxScene& scene, const Raycast& query, NpOverflowBuffer<PxRaycastHit>& hitBuffer, PxQueryFilterCallback* qfcb)
{
scene.raycast(
query.origin, query.unitDir, query.distance,
hitBuffer,
query.hitFlags,
query.filterData, qfcb,
query.cache);
}
static void performQuery(const PxScene& scene, const Sweep& query, NpOverflowBuffer<PxSweepHit>& hitBuffer, PxQueryFilterCallback* qfcb)
{
scene.sweep(
query.geometry.any(), query.pose, query.unitDir, query.distance,
hitBuffer,
query.hitFlags,
query.filterData, qfcb,
query.cache,
query.inflation);
}
static void performQuery(const PxScene& scene, const Overlap& query, NpOverflowBuffer<PxOverlapHit>& hitBuffer, PxQueryFilterCallback* qfcb)
{
scene.overlap(
query.geometry.any(), query.pose,
hitBuffer,
query.filterData, qfcb,
query.cache);
}
void execute(const PxScene& scene, PxQueryFilterCallback* qfcb)
{
PxU32 touchesTide = 0;
for (PxU32 i = 0; i < mBufferTide; i++)
{
PX_ASSERT(0xffffffff == mBuffers[i].nbTouches);
PX_ASSERT(0xffffffff != mBuffers[i].maxNbTouches);
PX_ASSERT(!mBuffers[i].touches);
bool noTouchesRemaining = false;
if (mBuffers[i].maxNbTouches > 0)
{
if (touchesTide >= mMaxNbTouches)
{
//No resources left.
mBuffers[i].maxNbTouches = 0;
mBuffers[i].touches = NULL;
noTouchesRemaining = true;
}
else if ((touchesTide + mBuffers[i].maxNbTouches) > mMaxNbTouches)
{
//Some resources left but not enough to match requested number.
//This might be enough but it depends on the number of hits generated by the query.
mBuffers[i].maxNbTouches = mMaxNbTouches - touchesTide;
mBuffers[i].touches = mTouches + touchesTide;
}
else
{
//Enough resources left to match request.
mBuffers[i].touches = mTouches + touchesTide;
}
}
bool overflow = false;
{
PX_ALIGN(16, NpOverflowBuffer<HitType> overflowBuffer)(mBuffers[i].touches, mBuffers[i].maxNbTouches);
performQuery(scene, mQueries[i], overflowBuffer, qfcb);
overflow = overflowBuffer.overflow || noTouchesRemaining;
mBuffers[i].hasBlock = overflowBuffer.hasBlock;
mBuffers[i].block = overflowBuffer.block;
mBuffers[i].nbTouches = overflowBuffer.nbTouches;
}
if(overflow)
{
mBuffers[i].maxNbTouches = 0xffffffff;
}
touchesTide += mBuffers[i].nbTouches;
}
mBufferTide = 0;
}
};
const PxScene& mScene;
PxQueryFilterCallback* mQueryFilterCallback;
Query<PxRaycastHit, Raycast> mRaycasts;
Query<PxSweepHit, Sweep> mSweeps;
Query<PxOverlapHit, Overlap> mOverlaps;
};
template<typename HitType>
class ExtBatchQueryDesc
{
public:
ExtBatchQueryDesc(const PxU32 maxNbResults, const PxU32 maxNbTouches)
: mResults(NULL),
mMaxNbResults(maxNbResults),
mTouches(NULL),
mMaxNbTouches(maxNbTouches)
{
}
ExtBatchQueryDesc(PxHitBuffer<HitType>* results, const PxU32 maxNbResults, HitType* touches, PxU32 maxNbTouches)
: mResults(results),
mMaxNbResults(maxNbResults),
mTouches(touches),
mMaxNbTouches(maxNbTouches)
{
}
PX_FORCE_INLINE PxHitBuffer<HitType>* getResults() const { return mResults; }
PX_FORCE_INLINE PxU32 getNbResults() const { return mMaxNbResults; }
PX_FORCE_INLINE HitType* getTouches() const { return mTouches; }
PX_FORCE_INLINE PxU32 getNbTouches() const { return mMaxNbTouches; }
private:
PxHitBuffer<HitType>* mResults;
PxU32 mMaxNbResults;
HitType* mTouches;
PxU32 mMaxNbTouches;
};
template <typename HitType, typename QueryType>
PxU32 computeByteSize(const ExtBatchQueryDesc<HitType>& queryDesc)
{
PxU32 byteSize = 0;
if (queryDesc.getNbResults() > 0)
{
byteSize += sizeof(QueryType)*queryDesc.getNbResults();
if (!queryDesc.getResults())
{
byteSize += sizeof(PxHitBuffer<HitType>)*queryDesc.getNbResults() + sizeof(HitType)*queryDesc.getNbTouches();
}
}
return byteSize;
}
template <typename HitType, typename QueryType> PxU8* parseDesc
(PxU8* bufIn, const ExtBatchQueryDesc<HitType>& queryDesc,
PxHitBuffer<HitType>*& results,
QueryType*& queries,
PxU32& maxBufferSize,
HitType*& touches,
PxU32& maxNbTouches)
{
PxU8* bufOut = bufIn;
results = queryDesc.getResults();
queries = NULL;
maxBufferSize = queryDesc.getNbResults();
touches = queryDesc.getTouches();
maxNbTouches = queryDesc.getNbTouches();
if (maxBufferSize > 0)
{
queries = reinterpret_cast<QueryType*>(bufOut);
bufOut += sizeof(QueryType)*maxBufferSize;
if (!results)
{
results = reinterpret_cast<PxHitBuffer<HitType>*>(bufOut);
for (PxU32 i = 0; i < maxBufferSize; i++)
{
PX_PLACEMENT_NEW(results + i, PxHitBuffer<HitType>);
}
bufOut += sizeof(PxHitBuffer<HitType>)*maxBufferSize;
if (maxNbTouches > 0)
{
touches = reinterpret_cast<HitType*>(bufOut);
bufOut += sizeof(HitType)*maxNbTouches;
}
}
}
return bufOut;
}
PxBatchQueryExt* create
(const PxScene& scene, PxQueryFilterCallback* queryFilterCallback,
const ExtBatchQueryDesc<PxRaycastHit>& raycastDesc, const ExtBatchQueryDesc<PxSweepHit>& sweepDesc, const ExtBatchQueryDesc<PxOverlapHit>& overlapDesc)
{
const PxU32 byteSize =
sizeof(ExtBatchQuery) +
computeByteSize<PxRaycastHit, Raycast>(raycastDesc) +
computeByteSize<PxSweepHit, Sweep>(sweepDesc) +
computeByteSize<PxOverlapHit, Overlap>(overlapDesc);
PxAllocatorCallback& allocator = *PxGetAllocatorCallback();
PxU8* buf = reinterpret_cast<PxU8*>(allocator.allocate(byteSize, "NpBatchQueryExt", PX_FL));
PX_CHECK_AND_RETURN_NULL(buf, "PxCreateBatchQueryExt - alllocation failed");
ExtBatchQuery* bq = reinterpret_cast<ExtBatchQuery*>(buf);
buf += sizeof(ExtBatchQuery);
PxHitBuffer<PxRaycastHit>* raycastBuffers = NULL;
Raycast* raycastQueries = NULL;
PxU32 maxNbRaycasts = 0;
PxRaycastHit* raycastTouches = NULL;
PxU32 maxNbRaycastTouches = 0;
buf = parseDesc<PxRaycastHit, Raycast>(buf, raycastDesc, raycastBuffers, raycastQueries, maxNbRaycasts, raycastTouches, maxNbRaycastTouches);
PxHitBuffer<PxSweepHit>* sweepBuffers = NULL;
Sweep* sweepQueries = NULL;
PxU32 maxNbSweeps = 0;
PxSweepHit* sweepTouches = NULL;
PxU32 maxNbSweepTouches = 0;
buf = parseDesc<PxSweepHit, Sweep>(buf, sweepDesc, sweepBuffers, sweepQueries, maxNbSweeps, sweepTouches, maxNbSweepTouches);
PxHitBuffer<PxOverlapHit>* overlapBuffers = NULL;
Overlap* overlapQueries = NULL;
PxU32 maxNbOverlaps = 0;
PxOverlapHit* overlapTouches = NULL;
PxU32 maxNbOverlapTouches = 0;
buf = parseDesc<PxOverlapHit, Overlap>(buf, overlapDesc, overlapBuffers, overlapQueries, maxNbOverlaps, overlapTouches, maxNbOverlapTouches);
PX_ASSERT((reinterpret_cast<PxU8*>(bq) + byteSize) == buf);
PX_PLACEMENT_NEW(bq, ExtBatchQuery)(
scene, queryFilterCallback,
raycastBuffers, raycastQueries, maxNbRaycasts, raycastTouches, maxNbRaycastTouches,
sweepBuffers, sweepQueries, maxNbSweeps, sweepTouches, maxNbSweepTouches,
overlapBuffers, overlapQueries, maxNbOverlaps, overlapTouches, maxNbOverlapTouches);
return bq;
}
PxBatchQueryExt* physx::PxCreateBatchQueryExt(
const PxScene& scene, PxQueryFilterCallback* queryFilterCallback,
const PxU32 maxNbRaycasts, const PxU32 maxNbRaycastTouches,
const PxU32 maxNbSweeps, const PxU32 maxNbSweepTouches,
const PxU32 maxNbOverlaps, const PxU32 maxNbOverlapTouches)
{
PX_CHECK_AND_RETURN_NULL(!((0 != maxNbRaycastTouches) && (0 == maxNbRaycasts)),
"PxCreateBatchQueryExt - maxNbRaycastTouches is non-zero but maxNbRaycasts is zero");
PX_CHECK_AND_RETURN_NULL(!((0 != maxNbSweepTouches) && (0 == maxNbSweeps)),
"PxCreateBatchQueryExt - maxNbSweepTouches is non-zero but maxNbSweeps is zero");
PX_CHECK_AND_RETURN_NULL(!((0 != maxNbOverlapTouches) && (0 == maxNbOverlaps)),
"PxCreateBatchQueryExt - maxNbOverlaps is non-zero but maxNbOverlaps is zero");
return create(scene, queryFilterCallback,
ExtBatchQueryDesc<PxRaycastHit>(maxNbRaycasts, maxNbRaycastTouches),
ExtBatchQueryDesc<PxSweepHit>(maxNbSweeps, maxNbSweepTouches),
ExtBatchQueryDesc<PxOverlapHit>(maxNbOverlaps, maxNbOverlapTouches));
}
PxBatchQueryExt* physx::PxCreateBatchQueryExt(
const PxScene& scene, PxQueryFilterCallback* queryFilterCallback,
PxRaycastBuffer* raycastBuffers, const PxU32 maxNbRaycasts, PxRaycastHit* raycastTouches, const PxU32 maxNbRaycastTouches,
PxSweepBuffer* sweepBuffers, const PxU32 maxNbSweeps, PxSweepHit* sweepTouches, const PxU32 maxNbSweepTouches,
PxOverlapBuffer* overlapBuffers, const PxU32 maxNbOverlaps, PxOverlapHit* overlapTouches, const PxU32 maxNbOverlapTouches)
{
PX_CHECK_AND_RETURN_NULL(!(!raycastTouches && (maxNbRaycastTouches != 0)),
"PxCreateBatchQueryExt - maxNbRaycastTouches > 0 but raycastTouches is NULL");
PX_CHECK_AND_RETURN_NULL(!(!raycastBuffers && (maxNbRaycasts != 0)),
"PxCreateBatchQueryExt - maxNbRaycasts > 0 but raycastBuffers is NULL");
PX_CHECK_AND_RETURN_NULL(!(!raycastBuffers && raycastTouches),
"PxCreateBatchQueryExt - raycastBuffers is NULL but raycastTouches is non-NULL");
PX_CHECK_AND_RETURN_NULL(!(!sweepTouches && (maxNbSweepTouches != 0)),
"PxCreateBatchQueryExt - maxNbSweepTouches > 0 but sweepTouches is NULL");
PX_CHECK_AND_RETURN_NULL(!(!sweepBuffers && (maxNbSweeps != 0)),
"PxCreateBatchQueryExt - maxNbSweeps > 0 but sweepBuffers is NULL");
PX_CHECK_AND_RETURN_NULL(!(!sweepBuffers && sweepTouches),
"PxCreateBatchQueryExt - sweepBuffers is NULL but sweepTouches is non-NULL");
PX_CHECK_AND_RETURN_NULL(!(!overlapTouches && (maxNbOverlapTouches != 0)),
"PxCreateBatchQueryExt - maxNbOverlapTouches > 0 but overlapTouches is NULL");
PX_CHECK_AND_RETURN_NULL(!(!overlapBuffers && (maxNbOverlaps != 0)),
"PxCreateBatchQueryExt - maxNbOverlaps > 0 but overlapBuffers is NULL");
PX_CHECK_AND_RETURN_NULL(!(!overlapBuffers && overlapTouches),
"PxCreateBatchQueryExt - overlapBuffers is NULL but overlapTouches is non-NULL");
return create(scene, queryFilterCallback,
ExtBatchQueryDesc<PxRaycastHit>(raycastBuffers, maxNbRaycasts, raycastTouches, maxNbRaycastTouches),
ExtBatchQueryDesc<PxSweepHit>(sweepBuffers, maxNbSweeps, sweepTouches, maxNbSweepTouches),
ExtBatchQueryDesc<PxOverlapHit>(overlapBuffers, maxNbOverlaps, overlapTouches, maxNbOverlapTouches));
}
ExtBatchQuery::ExtBatchQuery
(const PxScene& scene, PxQueryFilterCallback* queryFilterCallback,
PxRaycastBuffer* raycastBuffers, Raycast* raycastQueries, const PxU32 maxNbRaycasts, PxRaycastHit* raycastTouches, const PxU32 maxNbRaycastTouches,
PxSweepBuffer* sweepBuffers, Sweep* sweepQueries, const PxU32 maxNbSweeps, PxSweepHit* sweepTouches, const PxU32 maxNbSweepTouches,
PxOverlapBuffer* overlapBuffers, Overlap* overlapQueries, const PxU32 maxNbOverlaps, PxOverlapHit* overlapTouches, const PxU32 maxNbOverlapTouches)
: mScene(scene),
mQueryFilterCallback(queryFilterCallback)
{
typedef Query<PxRaycastHit, Raycast> QueryRaycast;
typedef Query<PxSweepHit, Sweep> QuerySweep;
typedef Query<PxOverlapHit, Overlap> QueryOverlap;
PX_PLACEMENT_NEW(&mRaycasts, QueryRaycast)(raycastBuffers, raycastQueries, maxNbRaycasts, raycastTouches, maxNbRaycastTouches);
PX_PLACEMENT_NEW(&mSweeps, QuerySweep)(sweepBuffers, sweepQueries, maxNbSweeps, sweepTouches, maxNbSweepTouches);
PX_PLACEMENT_NEW(&mOverlaps, QueryOverlap)(overlapBuffers, overlapQueries, maxNbOverlaps, overlapTouches, maxNbOverlapTouches);
}
void ExtBatchQuery::release()
{
PxGetAllocatorCallback()->deallocate(this);
}
PxRaycastBuffer* ExtBatchQuery::raycast
(const PxVec3& origin, const PxVec3& unitDir, const PxReal distance,
const PxU16 maxNbTouches,
PxHitFlags hitFlags,
const PxQueryFilterData& filterData,
const PxQueryCache* cache)
{
const PxQueryFilterData qfd(filterData.data, filterData.flags | PxQueryFlag::eBATCH_QUERY_LEGACY_BEHAVIOUR);
const Raycast raycast = { origin, unitDir, distance, hitFlags, qfd, cache };
PxRaycastBuffer* buffer = mRaycasts.addQuery(raycast, maxNbTouches);
PX_CHECK_MSG(buffer, "PxBatchQueryExt::raycast - number of raycast() calls exceeds maxNbRaycasts. query discarded");
return buffer;
}
PxSweepBuffer* ExtBatchQuery::sweep
(const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance,
const PxU16 maxNbTouches,
PxHitFlags hitFlags,
const PxQueryFilterData& filterData,
const PxQueryCache* cache,
const PxReal inflation)
{
const PxQueryFilterData qfd(filterData.data, filterData.flags | PxQueryFlag::eBATCH_QUERY_LEGACY_BEHAVIOUR);
const Sweep sweep = { geometry, pose, unitDir, distance, hitFlags, qfd, cache, inflation};
PxSweepBuffer* buffer = mSweeps.addQuery(sweep, maxNbTouches);
PX_CHECK_MSG(buffer, "PxBatchQueryExt::sweep - number of sweep() calls exceeds maxNbSweeps. query discarded");
return buffer;
}
PxOverlapBuffer* ExtBatchQuery::overlap
(const PxGeometry& geometry, const PxTransform& pose, PxU16 maxNbTouches,
const PxQueryFilterData& filterData,
const PxQueryCache* cache)
{
const PxQueryFilterData qfd(filterData.data, filterData.flags | PxQueryFlag::eBATCH_QUERY_LEGACY_BEHAVIOUR);
const Overlap overlap = { geometry, pose, qfd, cache};
PxOverlapBuffer* buffer = mOverlaps.addQuery(overlap, maxNbTouches);
PX_CHECK_MSG(buffer, "PxBatchQueryExt::overlap - number of overlap() calls exceeds maxNbOverlaps. query discarded");
return buffer;
}
void ExtBatchQuery::execute()
{
mRaycasts.execute(mScene, mQueryFilterCallback);
mSweeps.execute(mScene, mQueryFilterCallback);
mOverlaps.execute(mScene, mQueryFilterCallback);
}

View File

@@ -0,0 +1,469 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxSceneQuerySystemExt.h"
#include "extensions/PxShapeExt.h"
#include "foundation/PxAlloca.h"
#include "foundation/PxHashMap.h"
#include "foundation/PxUserAllocated.h"
#include "geometry/PxBVH.h"
#include "GuActorShapeMap.h"
#include "SqQuery.h"
#include "SqFactory.h"
#include "PxRigidActor.h"
#include "PxPruningStructure.h"
#include "PxSceneDesc.h" // PT: for PxSceneLimits TODO: remove
// PT: this file re-implements a scene-queries system for PxScene inside PxExtensions. All SQ-related calls from the PxScene API
// will be re-routed to this class. This version re-uses the same internal code as PhysX (most notably from SqQuery.h) so there is
// little differences between the internal PhysX version and this one except minor implementation details like what we store in the
// PrunerPayload. The whole API is available, it uses the same number of "pruners" for the queries, etc. This is a good working
// starting point for users who want to start tinkering with the system without starting from scratch.
using namespace physx;
using namespace Sq;
using namespace Gu;
#define EXT_PRUNER_EPSILON 0.005f
// PT: in this external implementation we'll use Px pointers instead of Np pointers in the payload.
static PX_FORCE_INLINE void setPayload(PrunerPayload& pp, const PxShape* shape, const PxRigidActor* actor)
{
pp.data[0] = size_t(shape);
pp.data[1] = size_t(actor);
}
static PX_FORCE_INLINE PxShape* getShapeFromPayload(const PrunerPayload& payload)
{
return reinterpret_cast<PxShape*>(payload.data[0]);
}
static PX_FORCE_INLINE PxRigidActor* getActorFromPayload(const PrunerPayload& payload)
{
return reinterpret_cast<PxRigidActor*>(payload.data[1]);
}
static PX_FORCE_INLINE bool isDynamicActor(const PxRigidActor& actor)
{
const PxType actorType = actor.getConcreteType();
return actorType != PxConcreteType::eRIGID_STATIC;
}
///////////////////////////////////////////////////////////////////////////////
static PX_FORCE_INLINE ActorShapeData createActorShapeData(PrunerData data, PrunerCompoundId id) { return (ActorShapeData(id) << 32) | ActorShapeData(data); }
static PX_FORCE_INLINE PrunerData getPrunerData(ActorShapeData data) { return PrunerData(data); }
static PX_FORCE_INLINE PrunerCompoundId getCompoundID(ActorShapeData data) { return PrunerCompoundId(data >> 32); }
///////////////////////////////////////////////////////////////////////////////
namespace
{
class ExtSqAdapter : public QueryAdapter
{
public:
ExtSqAdapter() {}
virtual ~ExtSqAdapter() {}
// Adapter
virtual const PxGeometry& getGeometry(const PrunerPayload& payload) const;
//~Adapter
// QueryAdapter
virtual PrunerHandle findPrunerHandle(const PxQueryCache& cache, PrunerCompoundId& compoundId, PxU32& prunerIndex) const;
virtual void getFilterData(const PrunerPayload& payload, PxFilterData& filterData) const;
virtual void getActorShape(const PrunerPayload& payload, PxActorShape& actorShape) const;
//~QueryAdapter
ActorShapeMap mDatabase;
};
}
const PxGeometry& ExtSqAdapter::getGeometry(const PrunerPayload& payload) const
{
PxShape* shape = getShapeFromPayload(payload);
return shape->getGeometry();
}
PrunerHandle ExtSqAdapter::findPrunerHandle(const PxQueryCache& cache, PrunerCompoundId& compoundId, PxU32& prunerIndex) const
{
const PxU32 actorIndex = cache.actor->getInternalActorIndex();
PX_ASSERT(actorIndex!=0xffffffff);
const ActorShapeData actorShapeData = mDatabase.find(actorIndex, cache.actor, cache.shape);
const PrunerData prunerData = getPrunerData(actorShapeData);
compoundId = getCompoundID(actorShapeData);
prunerIndex = getPrunerIndex(prunerData);
return getPrunerHandle(prunerData);
}
void ExtSqAdapter::getFilterData(const PrunerPayload& payload, PxFilterData& filterData) const
{
PxShape* shape = getShapeFromPayload(payload);
filterData = shape->getQueryFilterData();
}
void ExtSqAdapter::getActorShape(const PrunerPayload& payload, PxActorShape& actorShape) const
{
actorShape.actor = getActorFromPayload(payload);
actorShape.shape = getShapeFromPayload(payload);
}
///////////////////////////////////////////////////////////////////////////////
namespace
{
class ExternalPxSQ : public PxSceneQuerySystem, public PxUserAllocated
{
public:
ExternalPxSQ(PVDCapture* pvd, PxU64 contextID, Pruner* staticPruner, Pruner* dynamicPruner,
PxU32 dynamicTreeRebuildRateHint, PxSceneQueryUpdateMode::Enum mode, const PxSceneLimits& limits) :
mQueries (pvd, contextID, staticPruner, dynamicPruner, dynamicTreeRebuildRateHint, EXT_PRUNER_EPSILON, limits, mExtAdapter),
mUpdateMode (mode),
mRefCount (1)
{}
virtual ~ExternalPxSQ() {}
virtual void release();
virtual void acquireReference();
virtual void preallocate(PxU32 prunerIndex, PxU32 nbShapes) { SQ().preallocate(prunerIndex, nbShapes); }
virtual void addSQShape( const PxRigidActor& actor, const PxShape& shape, const PxBounds3& bounds,
const PxTransform& transform, const PxSQCompoundHandle* compoundHandle, bool hasPruningStructure);
virtual void removeSQShape(const PxRigidActor& actor, const PxShape& shape);
virtual void updateSQShape(const PxRigidActor& actor, const PxShape& shape, const PxTransform& transform);
virtual PxSQCompoundHandle addSQCompound(const PxRigidActor& actor, const PxShape** shapes, const PxBVH& pxbvh, const PxTransform* transforms);
virtual void removeSQCompound(PxSQCompoundHandle compoundHandle);
virtual void updateSQCompound(PxSQCompoundHandle compoundHandle, const PxTransform& compoundTransform);
virtual void flushUpdates() { SQ().flushUpdates(); }
virtual void flushMemory() { SQ().flushMemory(); }
virtual void visualize(PxU32 prunerIndex, PxRenderOutput& out) const { SQ().visualize(prunerIndex, out); }
virtual void shiftOrigin(const PxVec3& shift) { SQ().shiftOrigin(shift); }
virtual PxSQBuildStepHandle prepareSceneQueryBuildStep(PxU32 prunerIndex);
virtual void sceneQueryBuildStep(PxSQBuildStepHandle handle);
virtual void finalizeUpdates();
virtual void setDynamicTreeRebuildRateHint(PxU32 dynTreeRebuildRateHint) { SQ().setDynamicTreeRebuildRateHint(dynTreeRebuildRateHint); }
virtual PxU32 getDynamicTreeRebuildRateHint() const { return SQ().getDynamicTreeRebuildRateHint(); }
virtual void forceRebuildDynamicTree(PxU32 prunerIndex) { SQ().forceRebuildDynamicTree(prunerIndex); }
virtual PxSceneQueryUpdateMode::Enum getUpdateMode() const { return mUpdateMode; }
virtual void setUpdateMode(PxSceneQueryUpdateMode::Enum mode) { mUpdateMode = mode; }
virtual PxU32 getStaticTimestamp() const { return SQ().getStaticTimestamp(); }
virtual void merge(const PxPruningStructure& pxps);
virtual bool raycast(const PxVec3& origin, const PxVec3& unitDir, const PxReal distance,
PxRaycastCallback& hitCall, PxHitFlags hitFlags,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, PxGeometryQueryFlags flags) const;
virtual bool sweep( const PxGeometry& geometry, const PxTransform& pose,
const PxVec3& unitDir, const PxReal distance,
PxSweepCallback& hitCall, PxHitFlags hitFlags,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, const PxReal inflation, PxGeometryQueryFlags flags) const;
virtual bool overlap(const PxGeometry& geometry, const PxTransform& transform,
PxOverlapCallback& hitCall,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, PxGeometryQueryFlags flags) const;
virtual PxSQPrunerHandle getHandle(const PxRigidActor& actor, const PxShape& shape, PxU32& prunerIndex) const;
virtual void sync(PxU32 prunerIndex, const PxSQPrunerHandle* handles, const PxU32* indices, const PxBounds3* bounds,
const PxTransform32* transforms, PxU32 count, const PxBitMap& ignoredIndices);
PX_FORCE_INLINE PrunerManager& SQ() { return mQueries.mSQManager; }
PX_FORCE_INLINE const PrunerManager& SQ() const { return mQueries.mSQManager; }
ExtSqAdapter mExtAdapter;
SceneQueries mQueries;
PxSceneQueryUpdateMode::Enum mUpdateMode;
PxU32 mRefCount;
};
}
///////////////////////////////////////////////////////////////////////////////
void addExternalSQ(PxSceneQuerySystem* added);
void removeExternalSQ(PxSceneQuerySystem* removed);
void ExternalPxSQ::release()
{
mRefCount--;
if(!mRefCount)
{
removeExternalSQ(this);
PX_DELETE_THIS;
}
}
void ExternalPxSQ::acquireReference()
{
mRefCount++;
}
void ExternalPxSQ::addSQShape(const PxRigidActor& actor, const PxShape& shape, const PxBounds3& bounds, const PxTransform& transform, const PxSQCompoundHandle* compoundHandle, bool hasPruningStructure)
{
PrunerPayload payload;
setPayload(payload, &shape, &actor);
const PrunerCompoundId cid = compoundHandle ? PrunerCompoundId(*compoundHandle) : INVALID_COMPOUND_ID;
const PrunerData prunerData = SQ().addPrunerShape(payload, isDynamicActor(actor), cid, bounds, transform, hasPruningStructure);
const PxU32 actorIndex = actor.getInternalActorIndex();
PX_ASSERT(actorIndex!=0xffffffff);
mExtAdapter.mDatabase.add(actorIndex, &actor, &shape, createActorShapeData(prunerData, cid));
}
namespace
{
struct DatabaseCleaner : PrunerPayloadRemovalCallback
{
DatabaseCleaner(ExtSqAdapter& adapter) : mAdapter(adapter){}
virtual void invoke(PxU32 nbRemoved, const PrunerPayload* removed) PX_OVERRIDE PX_FINAL
{
PxU32 actorIndex = 0xffffffff;
const PxRigidActor* cachedActor = NULL;
while(nbRemoved--)
{
const PrunerPayload& payload = *removed++;
const PxRigidActor* actor = getActorFromPayload(payload);
if(actor!=cachedActor)
{
actorIndex = actor->getInternalActorIndex();
cachedActor = actor;
}
PX_ASSERT(actorIndex!=0xffffffff);
bool status = mAdapter.mDatabase.remove(actorIndex, actor, getShapeFromPayload(payload), NULL);
PX_ASSERT(status);
PX_UNUSED(status);
}
}
ExtSqAdapter& mAdapter;
PX_NOCOPY(DatabaseCleaner)
};
}
void ExternalPxSQ::removeSQShape(const PxRigidActor& actor, const PxShape& shape)
{
const PxU32 actorIndex = actor.getInternalActorIndex();
PX_ASSERT(actorIndex!=0xffffffff);
ActorShapeData actorShapeData;
mExtAdapter.mDatabase.remove(actorIndex, &actor, &shape, &actorShapeData);
const PrunerData data = getPrunerData(actorShapeData);
const PrunerCompoundId compoundId = getCompoundID(actorShapeData);
SQ().removePrunerShape(compoundId, data, NULL);
}
void ExternalPxSQ::updateSQShape(const PxRigidActor& actor, const PxShape& shape, const PxTransform& transform)
{
const PxU32 actorIndex = actor.getInternalActorIndex();
PX_ASSERT(actorIndex!=0xffffffff);
const ActorShapeData actorShapeData = mExtAdapter.mDatabase.find(actorIndex, &actor, &shape);
const PrunerData shapeHandle = getPrunerData(actorShapeData);
const PrunerCompoundId cid = getCompoundID(actorShapeData);
SQ().markForUpdate(cid, shapeHandle, transform);
}
PxSQCompoundHandle ExternalPxSQ::addSQCompound(const PxRigidActor& actor, const PxShape** shapes, const PxBVH& bvh, const PxTransform* transforms)
{
const PxU32 numSqShapes = bvh.getNbBounds();
PX_ALLOCA(payloads, PrunerPayload, numSqShapes);
for(PxU32 i=0; i<numSqShapes; i++)
setPayload(payloads[i], shapes[i], &actor);
const PxU32 actorIndex = actor.getInternalActorIndex();
PX_ASSERT(actorIndex!=0xffffffff);
PX_ALLOCA(shapeHandles, PrunerData, numSqShapes);
SQ().addCompoundShape(bvh, actorIndex, actor.getGlobalPose(), shapeHandles, payloads, transforms, isDynamicActor(actor));
for(PxU32 i=0; i<numSqShapes; i++)
{
// PT: TODO: actorIndex is now redundant!
mExtAdapter.mDatabase.add(actorIndex, &actor, shapes[i], createActorShapeData(shapeHandles[i], actorIndex));
}
return PxSQCompoundHandle(actorIndex);
}
void ExternalPxSQ::removeSQCompound(PxSQCompoundHandle compoundHandle)
{
DatabaseCleaner cleaner(mExtAdapter);
SQ().removeCompoundActor(PrunerCompoundId(compoundHandle), &cleaner);
}
void ExternalPxSQ::updateSQCompound(PxSQCompoundHandle compoundHandle, const PxTransform& compoundTransform)
{
SQ().updateCompoundActor(PrunerCompoundId(compoundHandle), compoundTransform);
}
PxSQBuildStepHandle ExternalPxSQ::prepareSceneQueryBuildStep(PxU32 prunerIndex)
{
return SQ().prepareSceneQueriesUpdate(PruningIndex::Enum(prunerIndex));
}
void ExternalPxSQ::sceneQueryBuildStep(PxSQBuildStepHandle handle)
{
SQ().sceneQueryBuildStep(handle);
}
void ExternalPxSQ::finalizeUpdates()
{
switch(mUpdateMode)
{
case PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_ENABLED: SQ().afterSync(true, true); break;
case PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_DISABLED: SQ().afterSync(true, false); break;
case PxSceneQueryUpdateMode::eBUILD_DISABLED_COMMIT_DISABLED: SQ().afterSync(false, false); break;
}
}
void ExternalPxSQ::merge(const PxPruningStructure& pxps)
{
Pruner* staticPruner = SQ().getPruner(PruningIndex::eSTATIC);
if(staticPruner)
staticPruner->merge(pxps.getStaticMergeData());
Pruner* dynamicPruner = SQ().getPruner(PruningIndex::eDYNAMIC);
if(dynamicPruner)
dynamicPruner->merge(pxps.getDynamicMergeData());
}
bool ExternalPxSQ::raycast( const PxVec3& origin, const PxVec3& unitDir, const PxReal distance,
PxRaycastCallback& hitCall, PxHitFlags hitFlags,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, PxGeometryQueryFlags flags) const
{
return mQueries._raycast(origin, unitDir, distance, hitCall, hitFlags, filterData, filterCall, cache, flags);
}
bool ExternalPxSQ::sweep( const PxGeometry& geometry, const PxTransform& pose,
const PxVec3& unitDir, const PxReal distance,
PxSweepCallback& hitCall, PxHitFlags hitFlags,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, const PxReal inflation, PxGeometryQueryFlags flags) const
{
return mQueries._sweep(geometry, pose, unitDir, distance, hitCall, hitFlags, filterData, filterCall, cache, inflation, flags);
}
bool ExternalPxSQ::overlap( const PxGeometry& geometry, const PxTransform& transform,
PxOverlapCallback& hitCall,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, PxGeometryQueryFlags flags) const
{
return mQueries._overlap( geometry, transform, hitCall, filterData, filterCall, cache, flags);
}
PxSQPrunerHandle ExternalPxSQ::getHandle(const PxRigidActor& actor, const PxShape& shape, PxU32& prunerIndex) const
{
const PxU32 actorIndex = actor.getInternalActorIndex();
PX_ASSERT(actorIndex!=0xffffffff);
const ActorShapeData actorShapeData = mExtAdapter.mDatabase.find(actorIndex, &actor, &shape);
const PrunerData prunerData = getPrunerData(actorShapeData);
prunerIndex = getPrunerIndex(prunerData);
return PxSQPrunerHandle(getPrunerHandle(prunerData));
}
void ExternalPxSQ::sync(PxU32 prunerIndex, const PxSQPrunerHandle* handles, const PxU32* indices, const PxBounds3* bounds,
const PxTransform32* transforms, PxU32 count, const PxBitMap& ignoredIndices)
{
PX_ASSERT(prunerIndex==PruningIndex::eDYNAMIC);
if(prunerIndex==PruningIndex::eDYNAMIC)
SQ().sync(handles, indices, bounds, transforms, count, ignoredIndices);
}
///////////////////////////////////////////////////////////////////////////////
static CompanionPrunerType getCompanionType(PxDynamicTreeSecondaryPruner::Enum type)
{
switch(type)
{
case PxDynamicTreeSecondaryPruner::eNONE: return COMPANION_PRUNER_NONE;
case PxDynamicTreeSecondaryPruner::eBUCKET: return COMPANION_PRUNER_BUCKET;
case PxDynamicTreeSecondaryPruner::eINCREMENTAL: return COMPANION_PRUNER_INCREMENTAL;
case PxDynamicTreeSecondaryPruner::eBVH: return COMPANION_PRUNER_AABB_TREE;
case PxDynamicTreeSecondaryPruner::eLAST: return COMPANION_PRUNER_NONE;
}
return COMPANION_PRUNER_NONE;
}
static BVHBuildStrategy getBuildStrategy(PxBVHBuildStrategy::Enum bs)
{
switch(bs)
{
case PxBVHBuildStrategy::eFAST: return BVH_SPLATTER_POINTS;
case PxBVHBuildStrategy::eDEFAULT: return BVH_SPLATTER_POINTS_SPLIT_GEOM_CENTER;
case PxBVHBuildStrategy::eSAH: return BVH_SAH;
case PxBVHBuildStrategy::eLAST: return BVH_SPLATTER_POINTS;
}
return BVH_SPLATTER_POINTS;
}
static Pruner* create(PxPruningStructureType::Enum type, PxU64 contextID, PxDynamicTreeSecondaryPruner::Enum secondaryType, PxBVHBuildStrategy::Enum buildStrategy, PxU32 nbObjectsPerNode)
{
// if(0)
// return createIncrementalPruner(contextID);
const CompanionPrunerType cpType = getCompanionType(secondaryType);
const BVHBuildStrategy bs = getBuildStrategy(buildStrategy);
Pruner* pruner = NULL;
switch(type)
{
case PxPruningStructureType::eNONE: { pruner = createBucketPruner(contextID); break; }
case PxPruningStructureType::eDYNAMIC_AABB_TREE: { pruner = createAABBPruner(contextID, true, cpType, bs, nbObjectsPerNode); break; }
case PxPruningStructureType::eSTATIC_AABB_TREE: { pruner = createAABBPruner(contextID, false, cpType, bs, nbObjectsPerNode); break; }
case PxPruningStructureType::eLAST: break;
}
return pruner;
}
PxSceneQuerySystem* physx::PxCreateExternalSceneQuerySystem(const PxSceneQueryDesc& desc, PxU64 contextID)
{
PVDCapture* pvd = NULL;
Pruner* staticPruner = create(desc.staticStructure, contextID, desc.dynamicTreeSecondaryPruner, desc.staticBVHBuildStrategy, desc.staticNbObjectsPerNode);
Pruner* dynamicPruner = create(desc.dynamicStructure, contextID, desc.dynamicTreeSecondaryPruner, desc.dynamicBVHBuildStrategy, desc.dynamicNbObjectsPerNode);
ExternalPxSQ* pxsq = PX_NEW(ExternalPxSQ)(pvd, contextID, staticPruner, dynamicPruner, desc.dynamicTreeRebuildRateHint, desc.sceneQueryUpdateMode, PxSceneLimits());
addExternalSQ(pxsq);
return pxsq;
}

View File

@@ -0,0 +1,42 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_SERIALIZATION_H
#define EXT_SERIALIZATION_H
namespace physx
{
namespace Ext
{
void RegisterExtensionsSerializers(PxSerializationRegistry& sr);
void UnregisterExtensionsSerializers(PxSerializationRegistry& sr);
}
}
#endif

View File

@@ -0,0 +1,151 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_SHARED_QUEUE_ENTRY_POOL_H
#define EXT_SHARED_QUEUE_ENTRY_POOL_H
#include "foundation/PxAllocator.h"
#include "foundation/PxArray.h"
#include "foundation/PxSList.h"
namespace physx
{
namespace Ext
{
class SharedQueueEntry : public PxSListEntry
{
public:
SharedQueueEntry(void* objectRef) : mObjectRef(objectRef), mPooledEntry(false) {}
SharedQueueEntry() : mObjectRef(NULL), mPooledEntry(true) {}
public:
void* mObjectRef;
bool mPooledEntry; // True if the entry was preallocated in a pool
};
#if PX_VC
#pragma warning(push)
#pragma warning(disable:4324) // Padding was added at the end of a structure because of a __declspec(align) value.
#endif // Because of the SList member I assume*/
template<class Alloc = typename PxAllocatorTraits<SharedQueueEntry>::Type >
class SharedQueueEntryPool : private Alloc
{
public:
SharedQueueEntryPool(PxU32 poolSize, const Alloc& alloc = Alloc("SharedQueueEntryPool"));
~SharedQueueEntryPool();
SharedQueueEntry* getEntry(void* objectRef);
void putEntry(SharedQueueEntry& entry);
private:
SharedQueueEntry* mTaskEntryPool;
PxSList mTaskEntryPtrPool;
};
#if PX_VC
#pragma warning(pop)
#endif
template <class Alloc>
SharedQueueEntryPool<Alloc>::SharedQueueEntryPool(PxU32 poolSize, const Alloc& alloc)
: Alloc(alloc)
{
PxAlignedAllocator<PX_SLIST_ALIGNMENT, Alloc> alignedAlloc("SharedQueueEntryPool");
mTaskEntryPool = poolSize ? reinterpret_cast<SharedQueueEntry*>(alignedAlloc.allocate(sizeof(SharedQueueEntry) * poolSize, PX_FL)) : NULL;
if (mTaskEntryPool)
{
for(PxU32 i=0; i < poolSize; i++)
{
PX_ASSERT((size_t(&mTaskEntryPool[i]) & (PX_SLIST_ALIGNMENT-1)) == 0); // The SList entry must be aligned according to PX_SLIST_ALIGNMENT
PX_PLACEMENT_NEW(&mTaskEntryPool[i], SharedQueueEntry)();
PX_ASSERT(mTaskEntryPool[i].mPooledEntry == true);
mTaskEntryPtrPool.push(mTaskEntryPool[i]);
}
}
}
template <class Alloc>
SharedQueueEntryPool<Alloc>::~SharedQueueEntryPool()
{
if (mTaskEntryPool)
{
PxAlignedAllocator<PX_SLIST_ALIGNMENT, Alloc> alignedAlloc("SharedQueueEntryPool");
alignedAlloc.deallocate(mTaskEntryPool);
}
}
template <class Alloc>
SharedQueueEntry* SharedQueueEntryPool<Alloc>::getEntry(void* objectRef)
{
SharedQueueEntry* e = static_cast<SharedQueueEntry*>(mTaskEntryPtrPool.pop());
if (e)
{
PX_ASSERT(e->mPooledEntry == true);
e->mObjectRef = objectRef;
return e;
}
else
{
PxAlignedAllocator<PX_SLIST_ALIGNMENT, Alloc> alignedAlloc;
e = reinterpret_cast<SharedQueueEntry*>(alignedAlloc.allocate(sizeof(SharedQueueEntry), PX_FL));
if (e)
{
PX_PLACEMENT_NEW(e, SharedQueueEntry)(objectRef);
PX_ASSERT(e->mPooledEntry == false);
}
return e;
}
}
template <class Alloc>
void SharedQueueEntryPool<Alloc>::putEntry(SharedQueueEntry& entry)
{
if (entry.mPooledEntry)
{
entry.mObjectRef = NULL;
mTaskEntryPtrPool.push(entry);
}
else
{
PxAlignedAllocator<PX_SLIST_ALIGNMENT, Alloc> alignedAlloc;
alignedAlloc.deallocate(&entry);
}
}
} // namespace Ext
} // namespace physx
#endif

View File

@@ -0,0 +1,376 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "foundation/PxMathUtils.h"
#include "foundation/PxQuat.h"
#include "geometry/PxSphereGeometry.h"
#include "geometry/PxBoxGeometry.h"
#include "geometry/PxCapsuleGeometry.h"
#include "geometry/PxConvexMeshGeometry.h"
#include "geometry/PxPlaneGeometry.h"
#include "extensions/PxRigidBodyExt.h"
#include "extensions/PxSimpleFactory.h"
#include "PxPhysics.h"
#include "PxScene.h"
#include "PxRigidStatic.h"
#include "PxRigidStatic.h"
#include "PxRigidDynamic.h"
#include "PxShape.h"
#include "foundation/PxUtilities.h"
#include "foundation/PxInlineArray.h"
using namespace physx;
static bool isDynamicGeometry(PxGeometryType::Enum type)
{
return type == PxGeometryType::eBOX
|| type == PxGeometryType::eSPHERE
|| type == PxGeometryType::eCAPSULE
|| type == PxGeometryType::eCONVEXCORE
|| type == PxGeometryType::eCUSTOM
|| type == PxGeometryType::eCONVEXMESH;
}
namespace physx
{
PxRigidDynamic* PxCreateDynamic(PxPhysics& sdk,
const PxTransform& transform,
PxShape& shape,
PxReal density)
{
PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateDynamic: transform is not valid.");
PxRigidDynamic* actor = sdk.createRigidDynamic(transform);
if(actor)
{
if(!actor->attachShape(shape))
{
actor->release();
return NULL;
}
if(!PxRigidBodyExt::updateMassAndInertia(*actor, density))
{
actor->release();
return NULL;
}
}
return actor;
}
PxRigidDynamic* PxCreateDynamic(PxPhysics& sdk,
const PxTransform& transform,
const PxGeometry& geometry,
PxMaterial& material,
PxReal density,
const PxTransform& shapeOffset)
{
PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateDynamic: transform is not valid.");
PX_CHECK_AND_RETURN_NULL(shapeOffset.isValid(), "PxCreateDynamic: shapeOffset is not valid.");
if(!isDynamicGeometry(geometry.getType()) || density <= 0.0f)
return NULL;
PxShape* shape = sdk.createShape(geometry, material, true);
if(!shape)
return NULL;
shape->setLocalPose(shapeOffset);
PxRigidDynamic* body = PxCreateDynamic(sdk, transform, *shape, density);
shape->release();
return body;
}
PxRigidDynamic* PxCreateKinematic(PxPhysics& sdk,
const PxTransform& transform,
PxShape& shape,
PxReal density)
{
PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateKinematic: transform is not valid.");
bool isDynGeom = isDynamicGeometry(shape.getGeometry().getType());
if(isDynGeom && density <= 0.0f)
return NULL;
PxRigidDynamic* actor = sdk.createRigidDynamic(transform);
if(actor)
{
actor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true);
if(!isDynGeom)
shape.setFlag(PxShapeFlag::eSIMULATION_SHAPE, false);
actor->attachShape(shape);
if(isDynGeom)
PxRigidBodyExt::updateMassAndInertia(*actor, density);
else
{
actor->setMass(1.f);
actor->setMassSpaceInertiaTensor(PxVec3(1.f,1.f,1.f));
}
}
return actor;
}
PxRigidDynamic* PxCreateKinematic(PxPhysics& sdk,
const PxTransform& transform,
const PxGeometry& geometry,
PxMaterial& material,
PxReal density,
const PxTransform& shapeOffset)
{
PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateKinematic: transform is not valid.");
PX_CHECK_AND_RETURN_NULL(shapeOffset.isValid(), "PxCreateKinematic: shapeOffset is not valid.");
bool isDynGeom = isDynamicGeometry(geometry.getType());
if(isDynGeom && density <= 0.0f)
return NULL;
PxShape* shape = sdk.createShape(geometry, material, true);
if(!shape)
return NULL;
shape->setLocalPose(shapeOffset);
PxRigidDynamic* body = PxCreateKinematic(sdk, transform, *shape, density);
shape->release();
return body;
}
PxRigidStatic* PxCreateStatic(PxPhysics& sdk,
const PxTransform& transform,
PxShape& shape)
{
PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateStatic: transform is not valid.");
PxRigidStatic* s = sdk.createRigidStatic(transform);
if(s)
s->attachShape(shape);
return s;
}
PxRigidStatic* PxCreateStatic(PxPhysics& sdk,
const PxTransform& transform,
const PxGeometry& geometry,
PxMaterial& material,
const PxTransform& shapeOffset)
{
PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateStatic: transform is not valid.");
PX_CHECK_AND_RETURN_NULL(shapeOffset.isValid(), "PxCreateStatic: shapeOffset is not valid.");
PxShape* shape = sdk.createShape(geometry, material, true);
if(!shape)
return NULL;
shape->setLocalPose(shapeOffset);
PxRigidStatic* s = PxCreateStatic(sdk, transform, *shape);
shape->release();
return s;
}
PxRigidStatic* PxCreatePlane(PxPhysics& sdk,
const PxPlane& plane,
PxMaterial& material)
{
PX_CHECK_AND_RETURN_NULL(plane.n.isFinite(), "PxCreatePlane: plane normal is not valid.");
if (!plane.n.isNormalized())
return NULL;
return PxCreateStatic(sdk, PxTransformFromPlaneEquation(plane), PxPlaneGeometry(), material);
}
PxShape* PxCloneShape(PxPhysics& physics, const PxShape& from, bool isExclusive)
{
PxInlineArray<PxMaterial*, 64> materials;
PxU16 materialCount = from.getNbMaterials();
materials.resize(materialCount);
from.getMaterials(materials.begin(), materialCount);
PxShape* to = physics.createShape(from.getGeometry(), materials.begin(), materialCount, isExclusive, from.getFlags());
to->setLocalPose(from.getLocalPose());
to->setContactOffset(from.getContactOffset());
to->setRestOffset(from.getRestOffset());
to->setSimulationFilterData(from.getSimulationFilterData());
to->setQueryFilterData(from.getQueryFilterData());
to->setTorsionalPatchRadius(from.getTorsionalPatchRadius());
to->setMinTorsionalPatchRadius(from.getMinTorsionalPatchRadius());
return to;
}
static void copyStaticProperties(PxPhysics& physics, PxRigidActor& to, const PxRigidActor& from)
{
PxInlineArray<PxShape*, 64> shapes;
shapes.resize(from.getNbShapes());
PxU32 shapeCount = from.getNbShapes();
from.getShapes(shapes.begin(), shapeCount);
for(PxU32 i = 0; i < shapeCount; i++)
{
PxShape* s = shapes[i];
if(!s->isExclusive())
to.attachShape(*s);
else
{
PxShape* newShape = physx::PxCloneShape(physics, *s, true);
to.attachShape(*newShape);
newShape->release();
}
}
to.setActorFlags(from.getActorFlags());
to.setOwnerClient(from.getOwnerClient());
to.setDominanceGroup(from.getDominanceGroup());
to.setEnvironmentID(from.getEnvironmentID());
}
PxRigidStatic* PxCloneStatic(PxPhysics& physicsSDK,
const PxTransform& transform,
const PxRigidActor& from)
{
PxRigidStatic* to = physicsSDK.createRigidStatic(transform);
if(!to)
return NULL;
copyStaticProperties(physicsSDK, *to, from);
return to;
}
PxRigidDynamic* PxCloneDynamic(PxPhysics& physicsSDK,
const PxTransform& transform,
const PxRigidDynamic& from)
{
PxRigidDynamic* to = physicsSDK.createRigidDynamic(transform);
if(!to)
return NULL;
copyStaticProperties(physicsSDK, *to, from);
to->setRigidBodyFlags(from.getRigidBodyFlags());
to->setMass(from.getMass());
to->setMassSpaceInertiaTensor(from.getMassSpaceInertiaTensor());
to->setCMassLocalPose(from.getCMassLocalPose());
to->setLinearVelocity(from.getLinearVelocity());
to->setAngularVelocity(from.getAngularVelocity());
to->setLinearDamping(from.getLinearDamping());
to->setAngularDamping(from.getAngularDamping());
PxU32 posIters, velIters;
from.getSolverIterationCounts(posIters, velIters);
to->setSolverIterationCounts(posIters, velIters);
to->setMaxLinearVelocity(from.getMaxLinearVelocity());
to->setMaxAngularVelocity(from.getMaxAngularVelocity());
to->setMaxDepenetrationVelocity(from.getMaxDepenetrationVelocity());
to->setSleepThreshold(from.getSleepThreshold());
to->setStabilizationThreshold(from.getStabilizationThreshold());
to->setMinCCDAdvanceCoefficient(from.getMinCCDAdvanceCoefficient());
to->setContactReportThreshold(from.getContactReportThreshold());
to->setMaxContactImpulse(from.getMaxContactImpulse());
PxTransform target;
if (from.getKinematicTarget(target))
to->setKinematicTarget(target);
to->setRigidDynamicLockFlags(from.getRigidDynamicLockFlags());
return to;
}
static PxTransform scalePosition(const PxTransform& t, PxReal scale)
{
return PxTransform(t.p*scale, t.q);
}
void PxScaleRigidActor(PxRigidActor& actor, PxReal scale, bool scaleMassProps)
{
PX_CHECK_AND_RETURN(scale > 0,
"PxScaleRigidActor requires that the scale parameter is greater than zero");
PxInlineArray<PxShape*, 64> shapes;
shapes.resize(actor.getNbShapes());
actor.getShapes(shapes.begin(), shapes.size());
for(PxU32 i=0;i<shapes.size();i++)
{
shapes[i]->setLocalPose(scalePosition(shapes[i]->getLocalPose(), scale));
PxGeometryHolder h(shapes[i]->getGeometry());
switch(h.getType())
{
case PxGeometryType::eSPHERE:
h.sphere().radius *= scale;
break;
case PxGeometryType::ePLANE:
break;
case PxGeometryType::eCAPSULE:
h.capsule().halfHeight *= scale;
h.capsule().radius *= scale;
break;
case PxGeometryType::eBOX:
h.box().halfExtents *= scale;
break;
case PxGeometryType::eCONVEXMESH:
h.convexMesh().scale.scale *= scale;
break;
case PxGeometryType::eTRIANGLEMESH:
h.triangleMesh().scale.scale *= scale;
break;
case PxGeometryType::eHEIGHTFIELD:
h.heightField().heightScale *= scale;
h.heightField().rowScale *= scale;
h.heightField().columnScale *= scale;
break;
default:
PX_ASSERT(0);
}
shapes[i]->setGeometry(h.any());
}
if(!scaleMassProps)
return;
PxRigidDynamic* dynamic = (&actor)->is<PxRigidDynamic>();
if(!dynamic)
return;
PxReal scale3 = scale*scale*scale;
dynamic->setMass(dynamic->getMass()*scale3);
dynamic->setMassSpaceInertiaTensor(dynamic->getMassSpaceInertiaTensor()*scale3*scale*scale);
dynamic->setCMassLocalPose(scalePosition(dynamic->getCMassLocalPose(), scale));
}
}

View File

@@ -0,0 +1,143 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "foundation/PxMemory.h"
#include "extensions/PxSmoothNormals.h"
#include "foundation/PxMathUtils.h"
#include "foundation/PxUserAllocated.h"
#include "foundation/PxUtilities.h"
using namespace physx;
static PxReal computeAngle(const PxVec3* verts, const PxU32* refs, PxU32 vref)
{
PxU32 e0=0,e2=0;
if(vref==refs[0])
{
e0 = 2;
e2 = 1;
}
else if(vref==refs[1])
{
e0 = 2;
e2 = 0;
}
else if(vref==refs[2])
{
e0 = 0;
e2 = 1;
}
else
{
PX_ASSERT(0);
}
const PxVec3 edge0 = verts[refs[e0]] - verts[vref];
const PxVec3 edge1 = verts[refs[e2]] - verts[vref];
return PxComputeAngle(edge0, edge1);
}
bool PxBuildSmoothNormals(PxU32 nbTris, PxU32 nbVerts, const PxVec3* verts, const PxU32* dFaces, const PxU16* wFaces, PxVec3* normals, bool flip)
{
if(!verts || !normals || !nbTris || !nbVerts)
return false;
// Get correct destination buffers
// - if available, write directly to user-provided buffers
// - else get some ram and keep track of it
PxVec3* FNormals = PX_ALLOCATE(PxVec3, nbTris, "PxVec3");
if(!FNormals) return false;
// Compute face normals
const PxU32 c = PxU32(flip!=0);
for(PxU32 i=0; i<nbTris; i++)
{
// compute indices outside of array index to workaround
// SNC bug which was generating incorrect addresses
const PxU32 i0 = i*3+0;
const PxU32 i1 = i*3+1+c;
const PxU32 i2 = i*3+2-c;
const PxU32 Ref0 = dFaces ? dFaces[i0] : wFaces ? wFaces[i0] : 0;
const PxU32 Ref1 = dFaces ? dFaces[i1] : wFaces ? wFaces[i1] : 1;
const PxU32 Ref2 = dFaces ? dFaces[i2] : wFaces ? wFaces[i2] : 2;
FNormals[i] = (verts[Ref2]-verts[Ref0]).cross(verts[Ref1] - verts[Ref0]);
PX_ASSERT(!FNormals[i].isZero());
FNormals[i].normalize();
}
// Compute vertex normals
PxMemSet(normals, 0, nbVerts*sizeof(PxVec3));
// TTP 3751
PxVec3* TmpNormals = PX_ALLOCATE(PxVec3, nbVerts, "PxVec3");
PxMemSet(TmpNormals, 0, nbVerts*sizeof(PxVec3));
for(PxU32 i=0;i<nbTris;i++)
{
PxU32 Ref[3];
Ref[0] = dFaces ? dFaces[i*3+0] : wFaces ? wFaces[i*3+0] : 0;
Ref[1] = dFaces ? dFaces[i*3+1] : wFaces ? wFaces[i*3+1] : 1;
Ref[2] = dFaces ? dFaces[i*3+2] : wFaces ? wFaces[i*3+2] : 2;
for(PxU32 j=0;j<3;j++)
{
if(TmpNormals[Ref[j]].isZero())
TmpNormals[Ref[j]] = FNormals[i];
}
}
//~TTP 3751
for(PxU32 i=0;i<nbTris;i++)
{
PxU32 Ref[3];
Ref[0] = dFaces ? dFaces[i*3+0] : wFaces ? wFaces[i*3+0] : 0;
Ref[1] = dFaces ? dFaces[i*3+1] : wFaces ? wFaces[i*3+1] : 1;
Ref[2] = dFaces ? dFaces[i*3+2] : wFaces ? wFaces[i*3+2] : 2;
normals[Ref[0]] += FNormals[i] * computeAngle(verts, Ref, Ref[0]);
normals[Ref[1]] += FNormals[i] * computeAngle(verts, Ref, Ref[1]);
normals[Ref[2]] += FNormals[i] * computeAngle(verts, Ref, Ref[2]);
}
// Normalize vertex normals
for(PxU32 i=0;i<nbVerts;i++)
{
if(normals[i].isZero())
normals[i] = TmpNormals[i];
// PX_ASSERT(!normals[i].isZero());
normals[i].normalize();
}
PX_FREE(TmpNormals); // TTP 3751
PX_FREE(FNormals);
return true;
}

View File

@@ -0,0 +1,225 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "ExtSphericalJoint.h"
#include "ExtConstraintHelper.h"
#include "CmConeLimitHelper.h"
#include "omnipvd/ExtOmniPvdSetData.h"
using namespace physx;
using namespace Ext;
SphericalJoint::SphericalJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) :
SphericalJointT(PxJointConcreteType::eSPHERICAL, actor0, localFrame0, actor1, localFrame1, "SphericalJointData")
{
SphericalJointData* data = static_cast<SphericalJointData*>(mData);
data->limit = PxJointLimitCone(PxPi/2, PxPi/2);
data->jointFlags = PxSphericalJointFlags();
}
void SphericalJoint::setLimitCone(const PxJointLimitCone &limit)
{
PX_CHECK_AND_RETURN(limit.isValid(), "PxSphericalJoint::setLimit: invalid parameter");
data().limit = limit;
markDirty();
#if PX_SUPPORT_OMNI_PVD
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
PxSphericalJoint& j = static_cast<PxSphericalJoint&>(*this);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, limitYAngle, j, limit.yAngle)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, limitZAngle, j, limit.zAngle)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, limitRestitution, j, limit.restitution)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, limitBounceThreshold, j, limit.bounceThreshold)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, limitStiffness, j, limit.stiffness)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, limitDamping, j, limit.damping)
OMNI_PVD_WRITE_SCOPE_END
#endif
}
PxJointLimitCone SphericalJoint::getLimitCone() const
{
return data().limit;
}
PxSphericalJointFlags SphericalJoint::getSphericalJointFlags() const
{
return data().jointFlags;
}
void SphericalJoint::setSphericalJointFlags(PxSphericalJointFlags flags)
{
data().jointFlags = flags;
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, jointFlags, static_cast<PxSphericalJoint&>(*this), flags)
}
void SphericalJoint::setSphericalJointFlag(PxSphericalJointFlag::Enum flag, bool value)
{
if(value)
data().jointFlags |= flag;
else
data().jointFlags &= ~flag;
markDirty();
OMNI_PVD_SET(OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, jointFlags, static_cast<PxSphericalJoint&>(*this), getSphericalJointFlags())
}
PxReal SphericalJoint::getSwingYAngle() const
{
return getSwingYAngle_Internal();
}
PxReal SphericalJoint::getSwingZAngle() const
{
return getSwingZAngle_Internal();
}
static void SphericalJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags)
{
const SphericalJointData& data = *reinterpret_cast<const SphericalJointData*>(constantBlock);
PxTransform32 cA2w, cB2w;
joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform);
if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES)
viz.visualizeJointFrames(cA2w, cB2w);
if((flags & PxConstraintVisualizationFlag::eLIMITS) && (data.jointFlags & PxSphericalJointFlag::eLIMIT_ENABLED))
{
joint::applyNeighborhoodOperator(cA2w, cB2w);
const PxTransform cB2cA = cA2w.transformInv(cB2w);
PxQuat swing, twist;
PxSeparateSwingTwist(cB2cA.q, swing, twist);
viz.visualizeLimitCone(cA2w, PxTan(data.limit.zAngle/4), PxTan(data.limit.yAngle/4));
}
}
//TAG:solverprepshader
static PxU32 SphericalJointSolverPrep(Px1DConstraint* constraints,
PxVec3p& body0WorldOffset,
PxU32 /*maxConstraints*/,
PxConstraintInvMassScale& invMassScale,
const void* constantBlock,
const PxTransform& bA2w,
const PxTransform& bB2w,
bool /*useExtendedLimits*/,
PxVec3p& cA2wOut, PxVec3p& cB2wOut)
{
const SphericalJointData& data = *reinterpret_cast<const SphericalJointData*>(constantBlock);
PxTransform32 cA2w, cB2w;
joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w);
joint::applyNeighborhoodOperator(cA2w, cB2w);
if(data.jointFlags & PxSphericalJointFlag::eLIMIT_ENABLED)
{
PxQuat swing, twist;
PxSeparateSwingTwist(cA2w.q.getConjugate() * cB2w.q, swing, twist);
PX_ASSERT(PxAbs(swing.x)<1e-6f);
PxVec3 axis;
PxReal error;
const Cm::ConeLimitHelperTanLess coneHelper(data.limit.yAngle, data.limit.zAngle);
coneHelper.getLimit(swing, axis, error);
ch.angularLimit(cA2w.rotate(axis), error, data.limit);
}
PxVec3 ra, rb;
ch.prepareLockedAxes(cA2w.q, cB2w.q, cA2w.transformInv(cB2w.p), 7, 0, ra, rb);
cA2wOut = ra + bA2w.p;
cB2wOut = rb + bB2w.p;
return ch.getCount();
}
///////////////////////////////////////////////////////////////////////////////
static PxConstraintShaderTable gSphericalJointShaders = { SphericalJointSolverPrep, SphericalJointVisualize, PxConstraintFlag::Enum(0) };
PxConstraintSolverPrep SphericalJoint::getPrep() const { return gSphericalJointShaders.solverPrep; }
PxSphericalJoint* physx::PxSphericalJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1)
{
PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxSphericalJointCreate: local frame 0 is not a valid transform");
PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxSphericalJointCreate: local frame 1 is not a valid transform");
PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxSphericalJointCreate: actors must be different");
PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is<PxRigidBody>()) || (actor1 && actor1->is<PxRigidBody>()), "PxSphericalJointCreate: at least one actor must be dynamic");
return createJointT<SphericalJoint, SphericalJointData>(physics, actor0, localFrame0, actor1, localFrame1, gSphericalJointShaders);
}
// PX_SERIALIZATION
void SphericalJoint::resolveReferences(PxDeserializationContext& context)
{
mPxConstraint = resolveConstraintPtr(context, mPxConstraint, this, gSphericalJointShaders);
}
//~PX_SERIALIZATION
#if PX_SUPPORT_OMNI_PVD
void SphericalJoint::updateOmniPvdProperties() const
{
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
const PxSphericalJoint& j = static_cast<const PxSphericalJoint&>(*this);
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, swingYAngle, j, getSwingYAngle())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, swingZAngle, j, getSwingZAngle())
OMNI_PVD_WRITE_SCOPE_END
}
template<>
void physx::Ext::omniPvdInitJoint<SphericalJoint>(SphericalJoint& joint)
{
OMNI_PVD_WRITE_SCOPE_BEGIN(pvdWriter, pvdRegData)
PxSphericalJoint& j = static_cast<PxSphericalJoint&>(joint);
OMNI_PVD_CREATE_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, j);
omniPvdSetBaseJointParams(static_cast<PxJoint&>(joint), PxJointConcreteType::eSPHERICAL);
PxJointLimitCone limit = joint.getLimitCone();
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, limitYAngle, j, limit.yAngle)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, limitZAngle, j, limit.zAngle)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, limitRestitution, j, limit.restitution)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, limitBounceThreshold, j, limit.bounceThreshold)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, limitStiffness, j, limit.stiffness)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, limitDamping, j, limit.damping)
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, jointFlags, j, joint.getSphericalJointFlags())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, swingYAngle, j, joint.getSwingYAngle())
OMNI_PVD_SET_EXPLICIT(pvdWriter, pvdRegData, OMNI_PVD_CONTEXT_HANDLE, PxSphericalJoint, swingZAngle, j, joint.getSwingZAngle())
OMNI_PVD_WRITE_SCOPE_END
}
#endif

View File

@@ -0,0 +1,84 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_SPHERICAL_JOINT_H
#define EXT_SPHERICAL_JOINT_H
#include "extensions/PxSphericalJoint.h"
#include "ExtJoint.h"
#include "CmUtils.h"
namespace physx
{
struct PxSphericalJointGeneratedValues;
namespace Ext
{
struct SphericalJointData: public JointData
{
PxJointLimitCone limit;
PxSphericalJointFlags jointFlags;
private:
SphericalJointData(const PxJointLimitCone& cone) : limit(cone) {}
};
typedef JointT<PxSphericalJoint, SphericalJointData, PxSphericalJointGeneratedValues> SphericalJointT;
class SphericalJoint : public SphericalJointT
{
public:
// PX_SERIALIZATION
SphericalJoint(PxBaseFlags baseFlags) : SphericalJointT(baseFlags) {}
void resolveReferences(PxDeserializationContext& context);
static SphericalJoint* createObject(PxU8*& address, PxDeserializationContext& context) { return createJointObject<SphericalJoint>(address, context); }
//~PX_SERIALIZATION
SphericalJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1);
// PxSphericalJoint
virtual void setLimitCone(const PxJointLimitCone &limit) PX_OVERRIDE;
virtual PxJointLimitCone getLimitCone() const PX_OVERRIDE;
virtual void setSphericalJointFlags(PxSphericalJointFlags flags) PX_OVERRIDE;
virtual void setSphericalJointFlag(PxSphericalJointFlag::Enum flag, bool value) PX_OVERRIDE;
virtual PxSphericalJointFlags getSphericalJointFlags() const PX_OVERRIDE;
virtual PxReal getSwingYAngle() const PX_OVERRIDE;
virtual PxReal getSwingZAngle() const PX_OVERRIDE;
//~PxSphericalJoint
// PxConstraintConnector
virtual PxConstraintSolverPrep getPrep() const PX_OVERRIDE;
#if PX_SUPPORT_OMNI_PVD
virtual void updateOmniPvdProperties() const PX_OVERRIDE;
#endif
//~PxConstraintConnector
};
} // namespace Ext
} // namespace physx
#endif

View File

@@ -0,0 +1,578 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "ExtSqManager.h"
#define SQ_DEBUG_VIZ_STATIC_COLOR PxU32(PxDebugColor::eARGB_BLUE)
#define SQ_DEBUG_VIZ_DYNAMIC_COLOR PxU32(PxDebugColor::eARGB_RED)
#define SQ_DEBUG_VIZ_STATIC_COLOR2 PxU32(PxDebugColor::eARGB_DARKBLUE)
#define SQ_DEBUG_VIZ_DYNAMIC_COLOR2 PxU32(PxDebugColor::eARGB_DARKRED)
#define SQ_DEBUG_VIZ_COMPOUND_COLOR PxU32(PxDebugColor::eARGB_MAGENTA)
#include "GuBounds.h"
using namespace physx;
using namespace Sq;
using namespace Gu;
///////////////////////////////////////////////////////////////////////////////
#include "SqFactory.h"
#include "common/PxProfileZone.h"
#include "common/PxRenderBuffer.h"
#include "GuBVH.h"
#include "foundation/PxAlloca.h"
// PT: this is a customized version of physx::Sq::PrunerManager that supports more than 2 hardcoded pruners.
// It might not be possible to support the whole PxSceneQuerySystem API with an arbitrary number of pruners.
ExtPrunerManager::ExtPrunerManager(PxU64 contextID, float inflation, const Adapter& adapter, bool usesTreeOfPruners) :
mAdapter (adapter),
mTreeOfPruners (NULL),
mContextID (contextID),
mStaticTimestamp (0),
mRebuildRateHint (100),
mInflation (inflation),
mPrunerNeedsUpdating (false),
mTimestampNeedsUpdating (false),
mUsesTreeOfPruners (usesTreeOfPruners)
{
mCompoundPrunerExt.mPruner = createCompoundPruner(contextID);
}
ExtPrunerManager::~ExtPrunerManager()
{
PX_DELETE(mTreeOfPruners);
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
PX_DELETE(pe);
}
}
PxU32 ExtPrunerManager::addPruner(Pruner* pruner, PxU32 preallocated)
{
const PxU32 index = mPrunerExt.size();
PrunerExt* pe = PX_NEW(PrunerExt);
pe->init(pruner);
if(preallocated)
pe->preallocate(preallocated);
mPrunerExt.pushBack(pe);
return index;
}
void ExtPrunerManager::preallocate(PxU32 prunerIndex, PxU32 nbShapes)
{
const bool preallocateCompoundPruner = prunerIndex==0xffffffff;
if(preallocateCompoundPruner)
{
mCompoundPrunerExt.preallocate(nbShapes);
}
else
{
if(prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return;
}
mPrunerExt[prunerIndex]->preallocate(nbShapes);
}
}
void ExtPrunerManager::flushMemory()
{
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
pe->flushMemory();
}
mCompoundPrunerExt.flushMemory();
}
PrunerHandle ExtPrunerManager::addPrunerShape(const PrunerPayload& payload, PxU32 prunerIndex, bool dynamic, PrunerCompoundId compoundId, const PxBounds3& bounds, const PxTransform& transform, bool hasPruningStructure)
{
if(compoundId==INVALID_COMPOUND_ID && prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return INVALID_PRUNERHANDLE;
}
mPrunerNeedsUpdating = true;
if(!dynamic)
invalidateStaticTimestamp();
PrunerHandle handle;
if(compoundId == INVALID_COMPOUND_ID)
{
PX_ASSERT(prunerIndex<mPrunerExt.size());
PX_ASSERT(mPrunerExt[prunerIndex]->pruner());
mPrunerExt[prunerIndex]->pruner()->addObjects(&handle, &bounds, &payload, &transform, 1, hasPruningStructure);
//mPrunerExt[prunerIndex].growDirtyList(handle);
}
else
{
PX_ASSERT(mCompoundPrunerExt.pruner());
mCompoundPrunerExt.pruner()->addObject(compoundId, handle, bounds, payload, transform);
}
return handle;
}
void ExtPrunerManager::removePrunerShape(PxU32 prunerIndex, bool dynamic, PrunerCompoundId compoundId, PrunerHandle shapeHandle, PrunerPayloadRemovalCallback* removalCallback)
{
if(compoundId==INVALID_COMPOUND_ID && prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return;
}
mPrunerNeedsUpdating = true;
if(!dynamic)
invalidateStaticTimestamp();
if(compoundId == INVALID_COMPOUND_ID)
{
PX_ASSERT(prunerIndex<mPrunerExt.size());
PX_ASSERT(mPrunerExt[prunerIndex]->pruner());
mPrunerExt[prunerIndex]->removeFromDirtyList(shapeHandle);
mPrunerExt[prunerIndex]->pruner()->removeObjects(&shapeHandle, 1, removalCallback);
}
else
{
mCompoundPrunerExt.removeFromDirtyList(compoundId, shapeHandle);
mCompoundPrunerExt.pruner()->removeObject(compoundId, shapeHandle, removalCallback);
}
}
void ExtPrunerManager::markForUpdate(PxU32 prunerIndex, bool dynamic, PrunerCompoundId compoundId, PrunerHandle shapeHandle, const PxTransform& transform)
{
if(compoundId==INVALID_COMPOUND_ID && prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return;
}
mPrunerNeedsUpdating = true;
if(!dynamic)
invalidateStaticTimestamp();
if(compoundId == INVALID_COMPOUND_ID)
{
PX_ASSERT(prunerIndex<mPrunerExt.size());
PX_ASSERT(mPrunerExt[prunerIndex]->pruner());
// PT: TODO: at this point do we still need a dirty list? we could just update the bounds directly?
mPrunerExt[prunerIndex]->addToDirtyList(shapeHandle, dynamic, transform);
}
else
mCompoundPrunerExt.addToDirtyList(compoundId, shapeHandle, transform);
}
void ExtPrunerManager::setDynamicTreeRebuildRateHint(PxU32 rebuildRateHint)
{
mRebuildRateHint = rebuildRateHint;
// PT: we are still using the same rebuild hint for all pruners here, which may or may
// not make sense. We could also have a different build rate for each pruner.
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
Pruner* pruner = pe->pruner();
if(pruner && pruner->isDynamic())
static_cast<DynamicPruner*>(pruner)->setRebuildRateHint(rebuildRateHint);
}
}
void ExtPrunerManager::afterSync(bool buildStep, bool commit)
{
PX_PROFILE_ZONE("Sim.sceneQueryBuildStep", mContextID);
if(!buildStep && !commit)
{
mPrunerNeedsUpdating = true;
return;
}
// flush user modified objects
flushShapes();
// PT: TODO: with N pruners this part would be worth multi-threading
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
Pruner* pruner = pe->pruner();
if(pruner)
{
if(pruner->isDynamic())
static_cast<DynamicPruner*>(pruner)->buildStep(true);
if(commit)
pruner->commit();
}
}
if(commit)
{
if(mUsesTreeOfPruners)
createTreeOfPruners();
}
mPrunerNeedsUpdating = !commit;
}
void ExtPrunerManager::flushShapes()
{
PX_PROFILE_ZONE("SceneQuery.flushShapes", mContextID);
// must already have acquired writer lock here
const float inflation = 1.0f + mInflation;
bool mustInvalidateStaticTimestamp = false;
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
if(pe->processDirtyList(i, mAdapter, inflation))
mustInvalidateStaticTimestamp = true;
}
if(mustInvalidateStaticTimestamp)
invalidateStaticTimestamp();
mCompoundPrunerExt.flushShapes(mAdapter, inflation);
}
void ExtPrunerManager::flushUpdates()
{
PX_PROFILE_ZONE("SceneQuery.flushUpdates", mContextID);
if(mPrunerNeedsUpdating)
{
// no need to take lock if manual sq update is enabled
// as flushUpdates will only be called from NpScene::flushQueryUpdates()
mSQLock.lock();
if(mPrunerNeedsUpdating)
{
flushShapes();
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
if(pe->pruner())
pe->pruner()->commit();
}
if(mUsesTreeOfPruners)
createTreeOfPruners();
PxMemoryBarrier();
mPrunerNeedsUpdating = false;
}
mSQLock.unlock();
}
}
void ExtPrunerManager::forceRebuildDynamicTree(PxU32 prunerIndex)
{
// PT: beware here when called from the PxScene, the prunerIndex may not match
PX_PROFILE_ZONE("SceneQuery.forceDynamicTreeRebuild", mContextID);
if(prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return;
}
PxMutex::ScopedLock lock(mSQLock);
PrunerExt* pe = mPrunerExt[prunerIndex];
Pruner* pruner = pe->pruner();
if(pruner && pruner->isDynamic())
{
static_cast<DynamicPruner*>(pruner)->purge();
static_cast<DynamicPruner*>(pruner)->commit();
}
}
void* ExtPrunerManager::prepareSceneQueriesUpdate(PxU32 prunerIndex)
{
// PT: beware here when called from the PxScene, the prunerIndex may not match
if(prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return NULL;
}
PX_ASSERT(mPrunerExt[prunerIndex]->pruner());
bool retVal = false;
Pruner* pruner = mPrunerExt[prunerIndex]->pruner();
if(pruner && pruner->isDynamic())
retVal = static_cast<DynamicPruner*>(pruner)->prepareBuild();
return retVal ? pruner : NULL;
}
void ExtPrunerManager::sceneQueryBuildStep(void* handle)
{
PX_PROFILE_ZONE("SceneQuery.sceneQueryBuildStep", mContextID);
Pruner* pruner = reinterpret_cast<Pruner*>(handle);
if(pruner && pruner->isDynamic())
{
const bool buildFinished = static_cast<DynamicPruner*>(pruner)->buildStep(false);
if(buildFinished)
mPrunerNeedsUpdating = true;
}
}
void ExtPrunerManager::visualize(PxU32 prunerIndex, PxRenderOutput& out) const
{
// PT: beware here when called from the PxScene, the prunerIndex may not match
// This is quite awkward and should be improved.
// PT: problem here is that this function will be called by the regular PhysX scene when plugged to its PxSceneDesc,
// and the calling code only understands the regular PhysX pruners.
const bool visualizeCompoundPruner = prunerIndex==0xffffffff;
if(visualizeCompoundPruner)
{
const CompoundPruner* cp = mCompoundPrunerExt.pruner();
if(cp)
cp->visualizeEx(out, SQ_DEBUG_VIZ_COMPOUND_COLOR, true, true);
}
else
{
if(prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return;
}
PrunerExt* pe = mPrunerExt[prunerIndex];
Pruner* pruner = pe->pruner();
if(pruner)
{
// PT: TODO: this doesn't really work: the static color was for static shapes but they
// could still be stored in a dynamic pruner. So this code doesn't use a color scheme
// consistent with what we use in the default code.
if(pruner->isDynamic())
{
//if(visDynamic)
pruner->visualize(out, SQ_DEBUG_VIZ_DYNAMIC_COLOR, SQ_DEBUG_VIZ_DYNAMIC_COLOR2);
}
else
{
//if(visStatic)
pruner->visualize(out, SQ_DEBUG_VIZ_STATIC_COLOR, SQ_DEBUG_VIZ_STATIC_COLOR2);
}
}
}
}
void ExtPrunerManager::shiftOrigin(const PxVec3& shift)
{
const PxU32 nb = mPrunerExt.size();
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
Pruner* pruner = pe->pruner();
if(pruner)
pruner->shiftOrigin(shift);
}
mCompoundPrunerExt.pruner()->shiftOrigin(shift);
}
void ExtPrunerManager::addCompoundShape(const PxBVH& pxbvh, PrunerCompoundId compoundId, const PxTransform& compoundTransform, PrunerHandle* prunerHandle, const PrunerPayload* payloads, const PxTransform* transforms, bool isDynamic)
{
const Gu::BVH& bvh = static_cast<const Gu::BVH&>(pxbvh);
PX_ASSERT(mCompoundPrunerExt.mPruner);
mCompoundPrunerExt.mPruner->addCompound(prunerHandle, bvh, compoundId, compoundTransform, isDynamic, payloads, transforms);
if(!isDynamic)
invalidateStaticTimestamp();
}
void ExtPrunerManager::updateCompoundActor(PrunerCompoundId compoundId, const PxTransform& compoundTransform)
{
PX_ASSERT(mCompoundPrunerExt.mPruner);
const bool isDynamic = mCompoundPrunerExt.mPruner->updateCompound(compoundId, compoundTransform);
if(!isDynamic)
invalidateStaticTimestamp();
}
void ExtPrunerManager::removeCompoundActor(PrunerCompoundId compoundId, PrunerPayloadRemovalCallback* removalCallback)
{
PX_ASSERT(mCompoundPrunerExt.mPruner);
const bool isDynamic = mCompoundPrunerExt.mPruner->removeCompound(compoundId, removalCallback);
if(!isDynamic)
invalidateStaticTimestamp();
}
void ExtPrunerManager::sync(PxU32 prunerIndex, const PrunerHandle* handles, const PxU32* boundsIndices, const PxBounds3* bounds, const PxTransform32* transforms, PxU32 count, const PxBitMap& ignoredIndices)
{
if(!count)
return;
if(prunerIndex>=mPrunerExt.size())
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Invalid pruner index");
return;
}
Pruner* pruner = getPruner(prunerIndex);
if(!pruner)
return;
PxU32 startIndex = 0;
PxU32 numIndices = count;
// if shape sim map is not empty, parse the indices and skip update for the dirty one
if(ignoredIndices.count())
{
// PT: I think this codepath was used with SCB / buffered changes, but it's not needed anymore
numIndices = 0;
for(PxU32 i=0; i<count; i++)
{
// if(ignoredIndices.test(boundsIndices[i]))
if(ignoredIndices.boundedTest(boundsIndices[i]))
{
pruner->updateObjects(handles + startIndex, numIndices, mInflation, boundsIndices + startIndex, bounds, transforms);
numIndices = 0;
startIndex = i + 1;
}
else
numIndices++;
}
// PT: we fallback to the next line on purpose - no "else"
}
pruner->updateObjects(handles + startIndex, numIndices, mInflation, boundsIndices + startIndex, bounds, transforms);
}
PxU32 ExtPrunerManager::startCustomBuildstep()
{
PX_PROFILE_ZONE("SceneQuery.startCustomBuildstep", mContextID);
// flush user modified objects
//flushShapes();
{
PX_PROFILE_ZONE("SceneQuery.flushShapes", mContextID);
// PT: only flush the compound pruner synchronously
// must already have acquired writer lock here
const float inflation = 1.0f + mInflation;
mCompoundPrunerExt.flushShapes(mAdapter, inflation);
}
mTimestampNeedsUpdating = false;
return mPrunerExt.size();
}
void ExtPrunerManager::customBuildstep(PxU32 index)
{
PX_PROFILE_ZONE("SceneQuery.customBuildstep", mContextID);
PX_ASSERT(index<mPrunerExt.size());
PrunerExt* pe = mPrunerExt[index];
Pruner* pruner = pe->pruner();
//void ExtPrunerManager::flushShapes()
{
PX_PROFILE_ZONE("SceneQuery.flushShapes", mContextID);
// must already have acquired writer lock here
const float inflation = 1.0f + mInflation;
if(pe->processDirtyList(index, mAdapter, inflation))
mTimestampNeedsUpdating = true;
}
if(pruner)
{
if(pruner->isDynamic())
static_cast<DynamicPruner*>(pruner)->buildStep(true); // PT: "true" because that parameter was made for PxSceneQuerySystem::sceneQueryBuildStep(), not us
pruner->commit();
}
}
void ExtPrunerManager::finishCustomBuildstep()
{
PX_PROFILE_ZONE("SceneQuery.finishCustomBuildstep", mContextID);
if(mUsesTreeOfPruners)
createTreeOfPruners();
mPrunerNeedsUpdating = false;
if(mTimestampNeedsUpdating)
invalidateStaticTimestamp();
}
void ExtPrunerManager::createTreeOfPruners()
{
PX_PROFILE_ZONE("SceneQuery.createTreeOfPruners", mContextID);
PX_DELETE(mTreeOfPruners);
mTreeOfPruners = PX_NEW(BVH)(NULL);
const PxU32 nb = mPrunerExt.size();
PxBounds3* prunerBounds = reinterpret_cast<PxBounds3*>(PxAlloca(sizeof(PxBounds3)*(nb+1)));
PxU32 nbBounds = 0;
for(PxU32 i=0;i<nb;i++)
{
PrunerExt* pe = mPrunerExt[i];
Pruner* pruner = pe->pruner();
if(pruner)
pruner->getGlobalBounds(prunerBounds[nbBounds++]);
}
mTreeOfPruners->init(nbBounds, NULL, prunerBounds, sizeof(PxBounds3), BVH_SPLATTER_POINTS, 1, 0.01f);
}

View File

@@ -0,0 +1,139 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_SQ_MANAGER_H
#define EXT_SQ_MANAGER_H
#include "common/PxPhysXCommonConfig.h"
#include "foundation/PxBitMap.h"
#include "foundation/PxArray.h"
#include "SqPruner.h"
#include "SqManager.h"
#include "foundation/PxHashSet.h"
namespace physx
{
namespace Sq
{
class CompoundPruner;
}
}
#include "foundation/PxMutex.h"
namespace physx
{
class PxRenderOutput;
class PxBVH;
class PxSceneLimits;
namespace Gu
{
class BVH;
}
namespace Sq
{
// PT: this is a customized version of physx::Sq::PrunerManager that supports more than 2 hardcoded pruners.
// It might not be possible to support the whole PxSceneQuerySystem API with an arbitrary number of pruners.
class ExtPrunerManager : public PxUserAllocated
{
public:
ExtPrunerManager(PxU64 contextID, float inflation, const Adapter& adapter, bool usesTreeOfPruners);
~ExtPrunerManager();
PxU32 addPruner(Gu::Pruner* pruner, PxU32 preallocated);
Gu::PrunerHandle addPrunerShape(const Gu::PrunerPayload& payload, PxU32 prunerIndex, bool dynamic, PrunerCompoundId compoundId, const PxBounds3& bounds, const PxTransform& transform, bool hasPruningStructure=false);
void addCompoundShape(const PxBVH& bvh, PrunerCompoundId compoundId, const PxTransform& compoundTransform, Gu::PrunerHandle* prunerHandle, const Gu::PrunerPayload* payloads, const PxTransform* transforms, bool isDynamic);
void markForUpdate(PxU32 prunerIndex, bool dynamic, PrunerCompoundId compoundId, Gu::PrunerHandle shapeHandle, const PxTransform& transform);
void removePrunerShape(PxU32 prunerIndex, bool dynamic, PrunerCompoundId compoundId, Gu::PrunerHandle shapeHandle, Gu::PrunerPayloadRemovalCallback* removalCallback);
PX_FORCE_INLINE PxU32 getNbPruners() const { return mPrunerExt.size(); }
PX_FORCE_INLINE const Gu::Pruner* getPruner(PxU32 index) const { return mPrunerExt[index]->mPruner; }
PX_FORCE_INLINE Gu::Pruner* getPruner(PxU32 index) { return mPrunerExt[index]->mPruner; }
PX_FORCE_INLINE const CompoundPruner* getCompoundPruner() const { return mCompoundPrunerExt.mPruner; }
PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; }
void preallocate(PxU32 prunerIndex, PxU32 nbShapes);
void setDynamicTreeRebuildRateHint(PxU32 dynTreeRebuildRateHint);
PX_FORCE_INLINE PxU32 getDynamicTreeRebuildRateHint() const { return mRebuildRateHint; }
void flushUpdates();
void forceRebuildDynamicTree(PxU32 prunerIndex);
void updateCompoundActor(PrunerCompoundId compoundId, const PxTransform& compoundTransform);
void removeCompoundActor(PrunerCompoundId compoundId, Gu::PrunerPayloadRemovalCallback* removalCallback);
void* prepareSceneQueriesUpdate(PxU32 prunerIndex);
void sceneQueryBuildStep(void* handle);
void sync(PxU32 prunerIndex, const Gu::PrunerHandle* handles, const PxU32* boundsIndices, const PxBounds3* bounds, const PxTransform32* transforms, PxU32 count, const PxBitMap& ignoredIndices);
void afterSync(bool buildStep, bool commit);
void shiftOrigin(const PxVec3& shift);
void visualize(PxU32 prunerIndex, PxRenderOutput& out) const;
void flushMemory();
PX_FORCE_INLINE PxU32 getStaticTimestamp() const { return mStaticTimestamp; }
PX_FORCE_INLINE const Adapter& getAdapter() const { return mAdapter; }
PX_FORCE_INLINE const Gu::BVH* getTreeOfPruners() const { return mTreeOfPruners; }
PxU32 startCustomBuildstep();
void customBuildstep(PxU32 index);
void finishCustomBuildstep();
void createTreeOfPruners();
private:
const Adapter& mAdapter;
PxArray<PrunerExt*> mPrunerExt;
CompoundPrunerExt mCompoundPrunerExt;
Gu::BVH* mTreeOfPruners;
const PxU64 mContextID;
PxU32 mStaticTimestamp;
PxU32 mRebuildRateHint;
const float mInflation; // SQ_PRUNER_EPSILON
PxMutex mSQLock; // to make sure only one query updates the dirty pruner structure if multiple queries run in parallel
volatile bool mPrunerNeedsUpdating;
volatile bool mTimestampNeedsUpdating;
const bool mUsesTreeOfPruners;
void flushShapes();
PX_FORCE_INLINE void invalidateStaticTimestamp() { mStaticTimestamp++; }
PX_NOCOPY(ExtPrunerManager)
};
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,142 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_SQ_QUERY_H
#define EXT_SQ_QUERY_H
#include "foundation/PxSimpleTypes.h"
#include "geometry/PxGeometryQueryFlags.h"
#include "ExtSqManager.h"
#include "PxQueryReport.h"
#include "GuCachedFuncs.h"
namespace physx
{
class PxGeometry;
struct PxQueryFilterData;
struct PxFilterData;
class PxQueryFilterCallback;
namespace Sq
{
struct ExtMultiQueryInput;
class ExtPVDCapture
{
public:
ExtPVDCapture() {}
virtual ~ExtPVDCapture() {}
virtual bool transmitSceneQueries() = 0;
virtual void raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal distance, const PxRaycastHit* hit, PxU32 hitsNum, const PxQueryFilterData& filterData, bool multipleHits) = 0;
virtual void sweep(const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, PxReal distance, const PxSweepHit* hit, PxU32 hitsNum, const PxQueryFilterData& filterData, bool multipleHits) = 0;
virtual void overlap(const PxGeometry& geometry, const PxTransform& pose, const PxOverlapHit* hit, PxU32 hitsNum, const PxQueryFilterData& filterData) = 0;
};
// SceneQueries-level adapter. Augments the PrunerManager-level adapter with functions needed to perform queries.
class ExtQueryAdapter : public Adapter
{
public:
ExtQueryAdapter() {}
virtual ~ExtQueryAdapter() {}
// PT: TODO: decouple from PxQueryCache?
virtual Gu::PrunerHandle findPrunerHandle(const PxQueryCache& cache, PrunerCompoundId& compoundId, PxU32& prunerIndex) const = 0;
// PT: TODO: return reference? but this version is at least consistent with getActorShape
virtual void getFilterData(const Gu::PrunerPayload& payload, PxFilterData& filterData) const = 0;
virtual void getActorShape(const Gu::PrunerPayload& payload, PxActorShape& actorShape) const = 0;
// PT: new for this customized version: a function to perform per-pruner filtering
virtual bool processPruner(PxU32 prunerIndex, const PxQueryThreadContext* context, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall) const = 0;
};
}
// PT: this is a customized version of physx::Sq::SceneQueries that supports more than 2 hardcoded pruners.
// It might not be possible to support the whole PxSceneQuerySystem API with an arbitrary number of pruners.
class ExtSceneQueries
{
PX_NOCOPY(ExtSceneQueries)
public:
ExtSceneQueries(Sq::ExtPVDCapture* pvd, PxU64 contextID,
float inflation, const Sq::ExtQueryAdapter& adapter, bool usesTreeOfPruners);
~ExtSceneQueries();
PX_FORCE_INLINE Sq::ExtPrunerManager& getPrunerManagerFast() { return mSQManager; }
PX_FORCE_INLINE const Sq::ExtPrunerManager& getPrunerManagerFast() const { return mSQManager; }
template<typename QueryHit>
bool multiQuery(
const Sq::ExtMultiQueryInput& in,
PxHitCallback<QueryHit>& hits, PxHitFlags hitFlags, const PxQueryCache*,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall) const;
bool _raycast(
const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, // Ray data
PxRaycastCallback& hitCall, PxHitFlags hitFlags,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, PxGeometryQueryFlags flags) const;
bool _sweep(
const PxGeometry& geometry, const PxTransform& pose, // GeomObject data
const PxVec3& unitDir, const PxReal distance, // Ray data
PxSweepCallback& hitCall, PxHitFlags hitFlags,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, const PxReal inflation, PxGeometryQueryFlags flags) const;
bool _overlap(
const PxGeometry& geometry, const PxTransform& transform, // GeomObject data
PxOverlapCallback& hitCall,
const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall,
const PxQueryCache* cache, PxGeometryQueryFlags flags) const;
PX_FORCE_INLINE PxU64 getContextId() const { return mSQManager.getContextId(); }
Sq::ExtPrunerManager mSQManager;
public:
Gu::CachedFuncs mCachedFuncs;
Sq::ExtPVDCapture* mPVD;
};
#if PX_SUPPORT_EXTERN_TEMPLATE
//explicit template instantiation declaration
extern template
bool ExtSceneQueries::multiQuery<PxRaycastHit>(const Sq::ExtMultiQueryInput&, PxHitCallback<PxRaycastHit>&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*) const;
extern template
bool ExtSceneQueries::multiQuery<PxOverlapHit>(const Sq::ExtMultiQueryInput&, PxHitCallback<PxOverlapHit>&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*) const;
extern template
bool ExtSceneQueries::multiQuery<PxSweepHit>(const Sq::ExtMultiQueryInput&, PxHitCallback<PxSweepHit>&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*) const;
#endif
}
#endif

View File

@@ -0,0 +1,102 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_TASK_QUEUE_HELPER_H
#define EXT_TASK_QUEUE_HELPER_H
#include "task/PxTask.h"
#include "ExtSharedQueueEntryPool.h"
namespace physx
{
#define EXT_TASK_QUEUE_ENTRY_POOL_SIZE 128
#define EXT_TASK_QUEUE_ENTRY_HIGH_PRIORITY_POOL_SIZE 32
namespace Ext
{
class TaskQueueHelper
{
SharedQueueEntryPool<> mQueueEntryPool;
PxSList mJobList;
SharedQueueEntryPool<> mHighPriorityQueueEntryPool;
PxSList mHighPriorityJobList;
public:
TaskQueueHelper() : mQueueEntryPool(EXT_TASK_QUEUE_ENTRY_POOL_SIZE, "QueueEntryPool"),
mHighPriorityQueueEntryPool(EXT_TASK_QUEUE_ENTRY_HIGH_PRIORITY_POOL_SIZE, "HighPriorityQueueEntryPool")
{}
PX_FORCE_INLINE bool tryAcceptJobToQueue(PxBaseTask& task)
{
if(task.isHighPriority())
{
SharedQueueEntry* entry = mHighPriorityQueueEntryPool.getEntry(&task);
if(entry)
{
mHighPriorityJobList.push(*entry);
return true;
}
}
SharedQueueEntry* entry = mQueueEntryPool.getEntry(&task);
if(entry)
{
mJobList.push(*entry);
return true;
}
else
{
return false; // PT: we never actually reach this
}
}
template<const bool highPriorityT>
PxBaseTask* fetchTask()
{
SharedQueueEntry* entry = highPriorityT ? static_cast<SharedQueueEntry*>(mHighPriorityJobList.pop()) : static_cast<SharedQueueEntry*>(mJobList.pop());
if(entry)
{
PxBaseTask* task = reinterpret_cast<PxBaseTask*>(entry->mObjectRef);
if(highPriorityT)
mHighPriorityQueueEntryPool.putEntry(*entry);
else
mQueueEntryPool.putEntry(*entry);
return task;
}
return NULL;
}
};
} // namespace Ext
}
#endif

View File

@@ -0,0 +1,383 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "tet/ExtDelaunayBoundaryInserter.h"
#include "extensions/PxTetMakerExt.h"
#include "cooking/PxTetrahedronMeshDesc.h"
#include "geometry/PxTriangleMesh.h"
#include "tet/ExtMeshSimplificator.h"
#include "tet/ExtRemesher.h"
#include "tet/ExtOctreeTetrahedralizer.h"
#include "tet/ExtVoxelTetrahedralizer.h"
#include "foundation/PxMat33.h"
#include <stdio.h>
using namespace physx;
static PX_FORCE_INLINE PxReal computeTetrahedronVolume(const PxVec3& x0, const PxVec3& x1, const PxVec3& x2, const PxVec3& x3)
{
const PxVec3 u1 = x1 - x0;
const PxVec3 u2 = x2 - x0;
const PxVec3 u3 = x3 - x0;
PxMat33 edgeMatrix = PxMat33(u1, u2, u3);
const PxReal det = edgeMatrix.getDeterminant();
const PxReal volume = det / 6.0f;
return volume;
}
//Remove tets with small volume
static void removeSmallVolumeTetrahedra(PxArray<::physx::PxVec3>& vertices, PxArray<PxU32>& indices, PxReal volumeThreshold = 1e-8f)
{
uint32_t indexer = 0;
for (uint32_t i = 0; i < indices.size(); i += 4)
{
for (uint32_t j = 0; j < 4; ++j)
{
indices[indexer + j] = indices[i + j];
}
if (computeTetrahedronVolume(vertices[indices[i]], vertices[indices[i + 1]], vertices[indices[i + 2]], vertices[indices[i + 3]]) >= volumeThreshold)
{
indexer += 4;
}
}
if (indexer < indices.size())
{
indices.removeRange(indexer, indices.size() - indexer);
}
}
//Removes vertices not referenced by any tetrahedron and maps the tet's indices to match the compacted vertex list
static void removeUnusedVertices(PxArray<::physx::PxVec3>& vertices, PxArray<PxU32>& tets, PxU32 numPointsToKeepAtBeginning = 0)
{
PxArray<PxI32> compressorMap;
compressorMap.resize(vertices.size());
for (PxU32 i = 0; i < numPointsToKeepAtBeginning; ++i)
compressorMap[i] = 0;
for (PxU32 i = numPointsToKeepAtBeginning; i < compressorMap.size(); ++i)
compressorMap[i] = -1;
for (PxU32 i = 0; i < tets.size(); i += 4)
{
const PxU32* tet = &tets[i];
if (tet[0] == 0xFFFFFFFFu)
continue;
compressorMap[tet[0]] = 0;
compressorMap[tet[1]] = 0;
compressorMap[tet[2]] = 0;
compressorMap[tet[3]] = 0;
}
PxU32 indexer = 0;
for (PxU32 i = 0; i < compressorMap.size(); ++i)
{
if (compressorMap[i] >= 0)
{
compressorMap[i] = indexer;
vertices[indexer] = vertices[i];
indexer++;
}
}
for (PxU32 i = 0; i < tets.size(); i += 4)
{
PxU32* tet = &tets[i];
if (tet[0] == 0xFFFFFFFFu)
continue;
tet[0] = compressorMap[tet[0]];
tet[1] = compressorMap[tet[1]];
tet[2] = compressorMap[tet[2]];
tet[3] = compressorMap[tet[3]];
}
if (indexer < vertices.size())
vertices.removeRange(indexer, vertices.size() - indexer);
}
static PX_FORCE_INLINE PxU64 buildKey(PxI32 a, PxI32 b)
{
if (a < b)
return ((PxU64(a)) << 32) | (PxU64(b));
else
return ((PxU64(b)) << 32) | (PxU64(a));
}
static const PxI32 neighborEdgeList[3][2] = { { 0, 1 }, { 0, 2 }, { 1, 2 } };
static void buildTriangleNeighborhood(const PxI32* tris, PxU32 numTris, PxArray<PxI32>& result)
{
PxU32 l = 4 * numTris; //Waste one element in neighborhood info but allow bit shift access instead
result.clear();
result.resize(l, -1);
PxHashMap<PxU64, PxI32> faces;
for (PxU32 i = 0; i < numTris; ++i)
{
const PxI32* tri = &tris[3 * i];
if (tris[0] < 0)
continue;
for (PxI32 j = 0; j < 3; ++j)
{
PxU64 key = buildKey(tri[neighborEdgeList[j][0]], tri[neighborEdgeList[j][1]]);
if (const PxPair<const PxU64, PxI32>* ptr = faces.find(key))
{
if (ptr->second < 0)
{
//PX_ASSERT(false); //Invalid tetmesh since a face is shared by more than 2 tetrahedra
continue;
}
result[4 * i + j] = ptr->second;
result[ptr->second] = 4 * i + j;
faces[key] = -1;
}
else
faces.insert(key, 4 * i + j);
}
}
}
void PxTetMaker::detectTriangleIslands(const PxI32* triangles, PxU32 numTriangles, PxArray<PxU32>& islandIndexPerTriangle)
{
//Detect islands
PxArray<PxI32> neighborhood;
buildTriangleNeighborhood(triangles, numTriangles, neighborhood);
const PxU32 noIslandAssignedMarker = 0xFFFFFFFF;
islandIndexPerTriangle.resize(numTriangles, noIslandAssignedMarker);
PxU32 start = 0;
PxI32 color = -1;
PxArray<PxI32> stack;
while (true)
{
stack.clear();
while (start < islandIndexPerTriangle.size())
{
if (islandIndexPerTriangle[start] == noIslandAssignedMarker)
{
stack.pushBack(start);
++color;
islandIndexPerTriangle[start] = color;
break;
}
++start;
}
if (start == islandIndexPerTriangle.size())
break;
while (stack.size() > 0)
{
PxI32 id = stack.popBack();
for (PxI32 i = 0; i < 3; ++i)
{
PxI32 a = neighborhood[4 * id + i];
PxI32 tetId = a >> 2;
if (tetId >= 0 && islandIndexPerTriangle[tetId] == noIslandAssignedMarker)
{
stack.pushBack(tetId);
islandIndexPerTriangle[tetId] = color;
}
}
}
}
}
PxU32 PxTetMaker::findLargestIslandId(const PxU32* islandIndexPerTriangle, PxU32 numTriangles)
{
PxU32 numIslands = 0;
for (PxU32 i = 0; i < numTriangles; ++i)
numIslands = PxMax(numIslands, islandIndexPerTriangle[i]);
++numIslands;
PxArray<PxU32> numEntriesPerColor;
numEntriesPerColor.resize(numIslands, 0);
for (PxU32 i = 0; i < numTriangles; ++i)
numEntriesPerColor[islandIndexPerTriangle[i]] += 1;
PxU32 colorWithHighestTetCount = 0;
for (PxU32 i = 1; i < numEntriesPerColor.size(); ++i)
if (numEntriesPerColor[i] > numEntriesPerColor[colorWithHighestTetCount])
colorWithHighestTetCount = i;
return colorWithHighestTetCount;
}
bool PxTetMaker::createConformingTetrahedronMesh(const PxSimpleTriangleMesh& triangleMesh,
physx::PxArray<physx::PxVec3>& outVertices, physx::PxArray<physx::PxU32>& outTetIndices, const bool validate, PxReal volumeThreshold)
{
if (validate)
{
PxTriangleMeshAnalysisResults result = PxTetMaker::validateTriangleMesh(triangleMesh);
if (result & PxTriangleMeshAnalysisResult::eMESH_IS_INVALID)
{
PxGetFoundation().error(PxErrorCode::eDEBUG_INFO, PX_FL, "createConformingTetrahedronMesh(): Input triangle mesh is not suited to create a tetmesh due to deficiencies. Please call PxTetMaker::validateTriangleMesh(triangleMesh) for more details.");
return false;
}
}
Ext::generateTetmesh(triangleMesh.points, triangleMesh.triangles, triangleMesh.flags & PxMeshFlag::e16_BIT_INDICES, outVertices, outTetIndices);
if (volumeThreshold > 0.0f)
removeSmallVolumeTetrahedra(outVertices, outTetIndices, volumeThreshold);
PxU32 numRemoveAtEnd = Ext::removeDisconnectedIslands(reinterpret_cast<PxI32*>(outTetIndices.begin()), outTetIndices.size() / 4);
if (numRemoveAtEnd > 0)
outTetIndices.removeRange(outTetIndices.size() - 4 * numRemoveAtEnd, 4 * numRemoveAtEnd);
removeUnusedVertices(outVertices, outTetIndices, triangleMesh.points.count);
return true;
}
bool PxTetMaker::createVoxelTetrahedronMesh(const PxTetrahedronMeshDesc& tetMesh,
const PxU32 numVoxelsAlongLongestBoundingBoxAxis, physx::PxArray<physx::PxVec3>& outVertices, physx::PxArray<physx::PxU32>& outTetIndices,
PxI32* intputPointToOutputTetIndex, const PxU32* anchorNodeIndices, PxU32 numTetsPerVoxel)
{
//numTetsPerVoxel has only two valid values.
if (numTetsPerVoxel != 5 && numTetsPerVoxel != 6)
numTetsPerVoxel = 5;
Ext::generateVoxelTetmesh(tetMesh.points, tetMesh.tetrahedrons, numVoxelsAlongLongestBoundingBoxAxis, outVertices, outTetIndices, intputPointToOutputTetIndex, anchorNodeIndices, numTetsPerVoxel);
return true;
}
bool PxTetMaker::createVoxelTetrahedronMeshFromEdgeLength(const PxTetrahedronMeshDesc& tetMesh,
const PxReal voxelEdgeLength, physx::PxArray<physx::PxVec3>& outVertices, physx::PxArray<physx::PxU32>& outTetIndices,
PxI32* intputPointToOutputTetIndex, const PxU32* anchorNodeIndices, PxU32 numTetsPerVoxel)
{
//numTetsPerVoxel has only two valid values.
if (numTetsPerVoxel != 5 && numTetsPerVoxel != 6)
numTetsPerVoxel = 5;
Ext::generateVoxelTetmesh(tetMesh.points, tetMesh.tetrahedrons, voxelEdgeLength, outVertices, outTetIndices, intputPointToOutputTetIndex, anchorNodeIndices, numTetsPerVoxel);
return true;
}
PxTriangleMeshAnalysisResults PxTetMaker::validateTriangleMesh(const PxSimpleTriangleMesh& triangleMesh, const PxReal minVolumeThreshold, const PxReal minTriangleAngleRadians)
{
return Ext::validateTriangleMesh(triangleMesh.points, triangleMesh.triangles, triangleMesh.flags & PxMeshFlag::e16_BIT_INDICES, minVolumeThreshold, minTriangleAngleRadians);
}
PxTetrahedronMeshAnalysisResults PxTetMaker::validateTetrahedronMesh(const PxBoundedData& points, const PxBoundedData& tetrahedra, const PxReal minTetVolumeThreshold)
{
return Ext::validateTetrahedronMesh(points, tetrahedra, false, minTetVolumeThreshold);
}
void PxTetMaker::simplifyTriangleMesh(const PxArray<PxVec3>& inputVertices, const PxArray<PxU32>&inputIndices, int targetTriangleCount, PxF32 maximalEdgeLength,
PxArray<PxVec3>& outputVertices, PxArray<PxU32>& outputIndices,
PxArray<PxU32> *vertexMap, PxReal edgeLengthCostWeight, PxReal flatnessDetectionThreshold,
bool projectSimplifiedPointsOnInputMeshSurface, PxArray<PxU32>* outputVertexToInputTriangle, bool removeDisconnectedPatches)
{
Ext::MeshSimplificator ms;
PxArray<PxU32> indexMapToFullTriangleSet;
if (removeDisconnectedPatches)
{
PxU32 numTriangles = inputIndices.size() / 3;
PxArray<PxU32> islandIndexPerTriangle;
PxTetMaker::detectTriangleIslands(reinterpret_cast<const PxI32*>(inputIndices.begin()), numTriangles, islandIndexPerTriangle);
PxU32 biggestIslandIndex = PxTetMaker::findLargestIslandId(islandIndexPerTriangle.begin(), islandIndexPerTriangle.size());
PxArray<PxU32> connectedTriangleSet;
for (PxU32 i = 0; i < numTriangles; ++i)
{
if (islandIndexPerTriangle[i] == biggestIslandIndex)
{
for (PxU32 j = 0; j < 3; ++j)
connectedTriangleSet.pushBack(inputIndices[3 * i + j]);
indexMapToFullTriangleSet.pushBack(i);
}
}
ms.init(inputVertices, connectedTriangleSet, edgeLengthCostWeight, flatnessDetectionThreshold, projectSimplifiedPointsOnInputMeshSurface);
ms.decimateBySize(targetTriangleCount, maximalEdgeLength);
ms.readBack(outputVertices, outputIndices, vertexMap, outputVertexToInputTriangle);
}
else
{
ms.init(inputVertices, inputIndices, edgeLengthCostWeight, flatnessDetectionThreshold, projectSimplifiedPointsOnInputMeshSurface);
ms.decimateBySize(targetTriangleCount, maximalEdgeLength);
ms.readBack(outputVertices, outputIndices, vertexMap, outputVertexToInputTriangle);
}
if (removeDisconnectedPatches && projectSimplifiedPointsOnInputMeshSurface && outputVertexToInputTriangle)
{
for (PxU32 i = 0; i < outputVertexToInputTriangle->size(); ++i)
(*outputVertexToInputTriangle)[i] = indexMapToFullTriangleSet[(*outputVertexToInputTriangle)[i]];
}
}
void PxTetMaker::remeshTriangleMesh(const PxArray<PxVec3>& inputVertices, const PxArray<PxU32>&inputIndices, PxU32 gridResolution,
PxArray<PxVec3>& outputVertices, PxArray<PxU32>& outputIndices, PxArray<PxU32> *vertexMap)
{
Ext::Remesher rm;
rm.remesh(inputVertices, inputIndices, gridResolution, vertexMap);
rm.readBack(outputVertices, outputIndices);
}
void PxTetMaker::remeshTriangleMesh(const PxVec3* inputVertices, PxU32 nbVertices, const PxU32* inputIndices, PxU32 nbIndices, PxU32 gridResolution,
PxArray<PxVec3>& outputVertices, PxArray<PxU32>& outputIndices, PxArray<PxU32> *vertexMap)
{
Ext::Remesher rm;
rm.remesh(inputVertices, nbVertices, inputIndices, nbIndices, gridResolution, vertexMap);
rm.readBack(outputVertices, outputIndices);
}
void PxTetMaker::createTreeBasedTetrahedralMesh(const PxArray<PxVec3>& inputVertices, const PxArray<PxU32>&inputIndices,
bool useTreeNodes, PxArray<PxVec3>& outputVertices, PxArray<PxU32>& outputIndices, PxReal volumeThreshold)
{
Ext::OctreeTetrahedralizer ot;
ot.createTetMesh(inputVertices, inputIndices, useTreeNodes);
ot.readBack(outputVertices, outputIndices);
if (volumeThreshold > 0.0f)
removeSmallVolumeTetrahedra(outputVertices, outputIndices, volumeThreshold);
PxU32 numRemoveAtEnd = Ext::removeDisconnectedIslands(reinterpret_cast<PxI32*>(outputIndices.begin()), outputIndices.size() / 4);
if (numRemoveAtEnd > 0)
outputIndices.removeRange(outputIndices.size() - 4 * numRemoveAtEnd, 4 * numRemoveAtEnd);
removeUnusedVertices(outputVertices, outputIndices, inputVertices.size());
}
void PxTetMaker::createRelaxedVoxelTetrahedralMesh(const PxArray<PxVec3>& inputVertices, const PxArray<PxU32>&inputIndices,
PxArray<PxVec3>& outputVertices, PxArray<PxU32>& outputIndices,
PxI32 resolution, PxI32 numRelaxationIters, PxF32 relMinTetVolume)
{
Ext::VoxelTetrahedralizer vt;
vt.createTetMesh(inputVertices, inputIndices, resolution, numRelaxationIters, relMinTetVolume);
vt.readBack(outputVertices, outputIndices);
}

View File

@@ -0,0 +1,465 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxTetrahedronMeshExt.h"
#include "foundation/PxMathUtils.h"
#include "foundation/PxHashMap.h"
#include "GuTetrahedronMesh.h"
#include "GuBox.h"
#include "GuBV4.h"
#include "GuBV4_Common.h"
#include "GuDistancePointTetrahedron.h"
#include "GuAABBTreeNode.h"
#include "GuAABBTree.h"
#include "GuAABBTreeBounds.h"
#include "GuAABBTreeQuery.h"
using namespace physx;
namespace
{
struct TetrahedronFinderCallback
{
PxVec3 mQueryPoint;
PxI32 mTetId;
PxVec4 mBary;
const PxVec3* mVertices;
const PxU32* mTets;
PxReal mTolerance = 1e-6f;
TetrahedronFinderCallback(const PxVec3& queryPoint, const PxVec3* vertices, const PxU32* tets, const PxReal tolerance = 1e-6f) :
mQueryPoint(queryPoint), mTetId(-1), mVertices(vertices), mTets(tets), mTolerance(tolerance) {}
PX_FORCE_INLINE bool testPrimitive(const PxU32 primitiveStartId, const PxU32 numPrimitives)
{
for (PxU32 i = 0; i < numPrimitives; ++i)
{
const PxU32* tet = &mTets[4 * (primitiveStartId + i)];
PxComputeBarycentric(mVertices[tet[0]], mVertices[tet[1]], mVertices[tet[2]], mVertices[tet[3]], mQueryPoint, mBary);
if (mBary.x >= -mTolerance && mBary.x <= 1 + mTolerance && mBary.y >= -mTolerance && mBary.y <= 1 + mTolerance &&
mBary.z >= -mTolerance && mBary.z <= 1 + mTolerance && mBary.w >= -mTolerance && mBary.w <= 1 + mTolerance)
{
mTetId = PxI32(primitiveStartId + i);
return true;
}
}
return false;
}
PX_FORCE_INLINE bool testBox(const float boxMinX, const float boxMinY, const float boxMinZ, const float boxMaxX, const float boxMaxY, const float boxMaxZ)
{
return mQueryPoint.x >= boxMinX && mQueryPoint.y >= boxMinY && mQueryPoint.z >= boxMinZ &&
mQueryPoint.x <= boxMaxX && mQueryPoint.y <= boxMaxY && mQueryPoint.z <= boxMaxZ;
}
};
struct ClosestTetrahedronFinderCallback
{
PxVec3 mQueryPoint;
PxI32 mTetId;
PxVec4 mBary;
PxReal mDist = 1.84467e+19f; // sqrtf(FLT_MAX)
const PxVec3* mVertices;
const PxU32* mTets;
PxReal mTolerance = 1e-6f;
ClosestTetrahedronFinderCallback(const PxVec3& queryPoint, const PxVec3* vertices, const PxU32* tets) :
mQueryPoint(queryPoint), mTetId(-1), mVertices(vertices), mTets(tets) {}
PX_FORCE_INLINE bool testPrimitive(const PxU32 primitiveStartId, const PxU32 numPrimitives)
{
for (PxU32 i = 0; i < numPrimitives; ++i)
{
PxVec4 bary;
const PxU32* tet = &mTets[4 * (primitiveStartId + i)];
PxComputeBarycentric(mVertices[tet[0]], mVertices[tet[1]], mVertices[tet[2]], mVertices[tet[3]], mQueryPoint, bary);
if (bary.x >= -mTolerance && bary.x <= 1 + mTolerance && bary.y >= -mTolerance && bary.y <= 1 + mTolerance &&
bary.z >= -mTolerance && bary.z <= 1 + mTolerance && bary.w >= -mTolerance && bary.w <= 1 + mTolerance)
{
mTetId = PxI32(primitiveStartId + i);
mBary = bary;
mDist = 0;
return true;
}
PxVec3 closest = Gu::closestPtPointTetrahedron(mQueryPoint, mVertices[tet[0]], mVertices[tet[1]], mVertices[tet[2]], mVertices[tet[3]]);
PxReal distSq = (closest - mQueryPoint).magnitudeSquared();
if (distSq < mDist * mDist)
{
mTetId = PxI32(primitiveStartId + i);
mBary = bary;
mDist = PxSqrt(distSq);
}
}
return false;
}
PX_FORCE_INLINE bool testBox(const float boxMinX, const float boxMinY, const float boxMinZ, const float boxMaxX, const float boxMaxY, const float boxMaxZ)
{
if (mQueryPoint.x >= boxMinX && mQueryPoint.y >= boxMinY && mQueryPoint.z >= boxMinZ &&
mQueryPoint.x <= boxMaxX && mQueryPoint.y <= boxMaxY && mQueryPoint.z <= boxMaxZ)
return true;
PxVec3 closest = mQueryPoint;
closest.x = PxClamp(closest.x, boxMinX, boxMaxX);
closest.y = PxClamp(closest.y, boxMinY, boxMaxY);
closest.z = PxClamp(closest.z, boxMinZ, boxMaxZ);
PxReal distSq = (closest - mQueryPoint).magnitudeSquared();
return distSq < mDist * mDist;
}
};
}
template<typename T, PxU32 i>
static int process(PxU32* stack, PxU32& stackSize, const Gu::BVDataSwizzledNQ* node, T& callback)
{
if (callback.testBox(node->mMinX[i], node->mMinY[i], node->mMinZ[i], node->mMaxX[i], node->mMaxY[i], node->mMaxZ[i]))
{
if (node->isLeaf(i))
{
PxU32 primitiveIndex = node->getPrimitive(i);
const PxU32 numPrimitives = Gu::getNbPrimitives(primitiveIndex);
if(callback.testPrimitive(primitiveIndex, numPrimitives)) //Returns true if the query should be terminated immediately
return 1;
}
else
stack[stackSize++] = node->getChildData(i);
}
return 0;
}
template<typename T>
static void traverseBVH(const Gu::BV4Tree& tree, T& callback)
{
const Gu::BVDataPackedNQ* root = static_cast<const Gu::BVDataPackedNQ*>(tree.mNodes);
PxU32 stack[GU_BV4_STACK_SIZE];
PxU32 stackSize = 0;
stack[stackSize++] = tree.mInitData;
while (stackSize > 0)
{
const PxU32 childData = stack[--stackSize];
const Gu::BVDataSwizzledNQ* node = reinterpret_cast<const Gu::BVDataSwizzledNQ*>(root + Gu::getChildOffset(childData));
const PxU32 nodeType = Gu::getChildType(childData);
if (nodeType > 1)
if (process<T, 3>(stack, stackSize, node, callback)) return;
if (nodeType > 0)
if (process<T, 2>(stack, stackSize, node, callback)) return;
if (process<T, 1>(stack, stackSize, node, callback)) return;
if (process<T, 0>(stack, stackSize, node, callback)) return;
}
}
PxI32 PxTetrahedronMeshExt::findTetrahedronContainingPoint(const PxTetrahedronMesh* mesh, const PxVec3& point, PxVec4& bary, PxReal tolerance)
{
TetrahedronFinderCallback callback(point, mesh->getVertices(), static_cast<const PxU32*>(mesh->getTetrahedrons()), tolerance);
traverseBVH(static_cast<const Gu::BVTetrahedronMesh*>(mesh)->getBV4Tree(), callback);
bary = callback.mBary;
return callback.mTetId;
}
PxI32 PxTetrahedronMeshExt::findTetrahedronClosestToPoint(const PxTetrahedronMesh* mesh, const PxVec3& point, PxVec4& bary)
{
ClosestTetrahedronFinderCallback callback(point, mesh->getVertices(), static_cast<const PxU32*>(mesh->getTetrahedrons()));
const Gu::BV4Tree& tree = static_cast<const Gu::BVTetrahedronMesh*>(mesh)->getBV4Tree();
if (tree.mNbNodes) traverseBVH(tree, callback);
else callback.testPrimitive(0, mesh->getNbTetrahedrons());
bary = callback.mBary;
return callback.mTetId;
}
namespace
{
class ClosestDistanceToTetmeshTraversalController
{
private:
PxReal mClosestDistanceSquared;
const PxU32* mTetrahedra;
const PxVec3* mPoints;
const Gu::BVHNode* mNodes;
PxVec3 mQueryPoint;
PxVec3 mClosestPoint;
PxI32 mClosestTetId;
public:
PX_FORCE_INLINE ClosestDistanceToTetmeshTraversalController() {}
PX_FORCE_INLINE ClosestDistanceToTetmeshTraversalController(const PxU32* tetrahedra, const PxVec3* points, Gu::BVHNode* nodes) :
mTetrahedra(tetrahedra), mPoints(points), mNodes(nodes), mQueryPoint(0.0f), mClosestPoint(0.0f), mClosestTetId(-1)
{
initialize(tetrahedra, points, nodes);
}
void initialize(const PxU32* tetrahedra, const PxVec3* points, Gu::BVHNode* nodes)
{
mTetrahedra = tetrahedra;
mPoints = points;
mNodes = nodes;
mQueryPoint = PxVec3(0.0f);
mClosestPoint = PxVec3(0.0f);
mClosestTetId = -1;
mClosestDistanceSquared = PX_MAX_F32;
}
PX_FORCE_INLINE void setQueryPoint(const PxVec3& queryPoint)
{
this->mQueryPoint = queryPoint;
mClosestDistanceSquared = FLT_MAX;
mClosestPoint = PxVec3(0.0f);
mClosestTetId = -1;
}
PX_FORCE_INLINE const PxVec3& getClosestPoint() const
{
return mClosestPoint;
}
PX_FORCE_INLINE PxReal distancePointBoxSquared(const PxBounds3& box, const PxVec3& point)
{
PxVec3 closestPt = box.minimum.maximum(box.maximum.minimum(point));
return (closestPt - point).magnitudeSquared();
}
PX_FORCE_INLINE Gu::TraversalControl::Enum analyze(const Gu::BVHNode& node, PxI32)
{
if (distancePointBoxSquared(node.mBV, mQueryPoint) >= mClosestDistanceSquared)
return Gu::TraversalControl::eDontGoDeeper;
if (node.isLeaf())
{
const PxI32 j = node.getPrimitiveIndex();
const PxU32* tet = &mTetrahedra[4 * j];
PxVec4 bary;
PxComputeBarycentric(mPoints[tet[0]], mPoints[tet[1]], mPoints[tet[2]], mPoints[tet[3]], mQueryPoint, bary);
const PxReal tolerance = 0.0f;
if (bary.x >= -tolerance && bary.x <= 1 + tolerance && bary.y >= -tolerance && bary.y <= 1 + tolerance &&
bary.z >= -tolerance && bary.z <= 1 + tolerance && bary.w >= -tolerance && bary.w <= 1 + tolerance)
{
mClosestDistanceSquared = 0;
mClosestTetId = j;
mClosestPoint = mQueryPoint;
return Gu::TraversalControl::eAbort;
}
PxVec3 closest = Gu::closestPtPointTetrahedron(mQueryPoint, mPoints[tet[0]], mPoints[tet[1]], mPoints[tet[2]], mPoints[tet[3]]);
PxReal d2 = (closest - mQueryPoint).magnitudeSquared();
if (d2 < mClosestDistanceSquared)
{
mClosestDistanceSquared = d2;
mClosestTetId = j;
mClosestPoint = closest;
}
return Gu::TraversalControl::eDontGoDeeper;
}
const Gu::BVHNode& nodePos = mNodes[node.getPosIndex()];
const PxReal distSquaredPos = distancePointBoxSquared(nodePos.mBV, mQueryPoint);
const Gu::BVHNode& nodeNeg = mNodes[node.getNegIndex()];
const PxReal distSquaredNeg = distancePointBoxSquared(nodeNeg.mBV, mQueryPoint);
if (distSquaredPos < distSquaredNeg)
{
if (distSquaredPos < mClosestDistanceSquared)
return Gu::TraversalControl::eGoDeeper;
}
else
{
if (distSquaredNeg < mClosestDistanceSquared)
return Gu::TraversalControl::eGoDeeperNegFirst;
}
return Gu::TraversalControl::eDontGoDeeper;
}
PxI32 getClosestTetId() const { return mClosestTetId; }
void setClosestStart(const PxReal closestDistanceSquared, PxI32 closestTetrahedron, const PxVec3& closestPoint)
{
mClosestDistanceSquared = closestDistanceSquared;
mClosestTetId = closestTetrahedron;
mClosestPoint = closestPoint;
}
private:
PX_NOCOPY(ClosestDistanceToTetmeshTraversalController)
};
}
static void buildTree(const PxU32* tetrahedra, const PxU32 numTetrahedra, const PxVec3* points, PxArray<Gu::BVHNode>& tree, PxF32 enlargement = 1e-4f)
{
//Computes a bounding box for every triangle in triangles
Gu::AABBTreeBounds boxes;
boxes.init(numTetrahedra);
for (PxU32 i = 0; i < numTetrahedra; ++i)
{
const PxU32* tri = &tetrahedra[4 * i];
PxBounds3 box = PxBounds3::empty();
box.include(points[tri[0]]);
box.include(points[tri[1]]);
box.include(points[tri[2]]);
box.include(points[tri[3]]);
box.fattenFast(enlargement);
boxes.getBounds()[i] = box;
}
Gu::buildAABBTree(numTetrahedra, boxes, tree);
}
void PxTetrahedronMeshExt::createPointsToTetrahedronMap(const PxArray<PxVec3>& tetMeshVertices, const PxArray<PxU32>& tetMeshIndices,
const PxArray<PxVec3>& pointsToEmbed, PxArray<PxVec4>& barycentricCoordinates, PxArray<PxU32>& tetLinks)
{
barycentricCoordinates.resize(0);
tetLinks.resize(0);
if (tetMeshVertices.size() == 0 || tetMeshIndices.size() == 0)
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Point in Tetmesh embedding: Input mesh does not have any tetrahedra or points.");
return;
}
if (pointsToEmbed.size() == 0)
return;
PxArray<Gu::BVHNode> tree;
buildTree(tetMeshIndices.begin(), tetMeshIndices.size() / 4, tetMeshVertices.begin(), tree);
if (tree.size() == 0)
return;
ClosestDistanceToTetmeshTraversalController cd(tetMeshIndices.begin(), tetMeshVertices.begin(), tree.begin());
barycentricCoordinates.resize(pointsToEmbed.size());
tetLinks.resize(pointsToEmbed.size());
for (PxU32 i = 0; i < pointsToEmbed.size(); ++i)
{
cd.setQueryPoint(pointsToEmbed[i]);
Gu::traverseBVH(tree.begin(), cd);
const PxU32* tet = &tetMeshIndices[4 * cd.getClosestTetId()];
PxVec4 bary;
PxComputeBarycentric(tetMeshVertices[tet[0]], tetMeshVertices[tet[1]], tetMeshVertices[tet[2]], tetMeshVertices[tet[3]], cd.getClosestPoint(), bary);
barycentricCoordinates[i] = bary;
tetLinks[i] = cd.getClosestTetId();
}
}
namespace
{
struct SortedTriangle
{
public:
PxU32 A;
PxU32 B;
PxU32 C;
PxI32 TetIndex;
bool Flipped;
PX_FORCE_INLINE SortedTriangle(PxU32 a, PxU32 b, PxU32 c, PxI32 tetIndex = -1)
{
A = a; B = b; C = c; Flipped = false; TetIndex = tetIndex;
if (A > B) { PxSwap(A, B); Flipped = !Flipped; }
if (B > C) { PxSwap(B, C); Flipped = !Flipped; }
if (A > B) { PxSwap(A, B); Flipped = !Flipped; }
}
};
struct TriangleHash
{
PX_FORCE_INLINE std::size_t operator()(const SortedTriangle& k) const
{
return k.A ^ k.B ^ k.C;
}
PX_FORCE_INLINE bool equal(const SortedTriangle& first, const SortedTriangle& second) const
{
return first.A == second.A && first.B == second.B && first.C == second.C;
}
};
}
static const PxU32 tetFaces[4][3] = { {0, 2, 1}, {0, 1, 3}, {0, 3, 2}, {1, 2, 3} };
void PxTetrahedronMeshExt::extractTetMeshSurface(const void* tetrahedra, PxU32 numTetrahedra, bool sixteenBitIndices, PxArray<PxU32>& surfaceTriangles, PxArray<PxU32>* surfaceTriangleToTet, bool flipTriangleOrientation)
{
PxHashMap<SortedTriangle, PxU32, TriangleHash> tris;
const PxU32* tets32 = reinterpret_cast<const PxU32*>(tetrahedra);
const PxU16* tets16 = reinterpret_cast<const PxU16*>(tetrahedra);
PxU32 l = 4 * numTetrahedra;
for (PxU32 i = 0; i < l; i += 4)
{
for (PxU32 j = 0; j < 4; ++j)
{
SortedTriangle tri(sixteenBitIndices ? tets16[i + tetFaces[j][0]] : tets32[i + tetFaces[j][0]],
sixteenBitIndices ? tets16[i + tetFaces[j][1]] : tets32[i + tetFaces[j][1]],
sixteenBitIndices ? tets16[i + tetFaces[j][2]] : tets32[i + tetFaces[j][2]], i);
if (const PxPair<const SortedTriangle, PxU32>* ptr = tris.find(tri))
tris[tri] = ptr->second + 1;
else
tris.insert(tri, 1);
}
}
surfaceTriangles.clear();
if (surfaceTriangleToTet)
surfaceTriangleToTet->clear();
for (PxHashMap<SortedTriangle, PxU32, TriangleHash>::Iterator iter = tris.getIterator(); !iter.done(); ++iter)
{
if (iter->second == 1) {
surfaceTriangles.pushBack(iter->first.A);
if (iter->first.Flipped != flipTriangleOrientation)
{
surfaceTriangles.pushBack(iter->first.C);
surfaceTriangles.pushBack(iter->first.B);
}
else
{
surfaceTriangles.pushBack(iter->first.B);
surfaceTriangles.pushBack(iter->first.C);
}
if (surfaceTriangleToTet)
surfaceTriangleToTet->pushBack(iter->first.TetIndex);
}
}
}
void PxTetrahedronMeshExt::extractTetMeshSurface(const PxTetrahedronMesh* mesh, PxArray<PxU32>& surfaceTriangles, PxArray<PxU32>* surfaceTriangleToTet, bool flipTriangleOrientation)
{
extractTetMeshSurface(mesh->getTetrahedrons(), mesh->getNbTetrahedrons(), mesh->getTetrahedronMeshFlags() & PxTetrahedronMeshFlag::e16_BIT_INDICES, surfaceTriangles, surfaceTriangleToTet, flipTriangleOrientation);
}

View File

@@ -0,0 +1,189 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "geometry/PxMeshQuery.h"
#include "geometry/PxGeometryQuery.h"
#include "geometry/PxTriangleMeshGeometry.h"
#include "geometry/PxHeightFieldGeometry.h"
#include "geometry/PxHeightField.h"
#include "geometry/PxTriangleMesh.h"
#include "extensions/PxTriangleMeshExt.h"
#include "GuSDF.h"
#include "GuTriangleMesh.h"
#include "foundation/PxAllocator.h"
using namespace physx;
PxMeshOverlapUtil::PxMeshOverlapUtil() : mResultsMemory(mResults), mNbResults(0), mMaxNbResults(256)
{
}
PxMeshOverlapUtil::~PxMeshOverlapUtil()
{
if(mResultsMemory != mResults)
PX_FREE(mResultsMemory);
}
PxU32 PxMeshOverlapUtil::findOverlap(const PxGeometry& geom, const PxTransform& geomPose, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose)
{
bool overflow;
PxU32 nbTouchedTris = PxMeshQuery::findOverlapTriangleMesh(geom, geomPose, meshGeom, meshPose, mResultsMemory, mMaxNbResults, 0, overflow);
if(overflow)
{
const PxU32 maxNbTris = meshGeom.triangleMesh->getNbTriangles();
if(!maxNbTris)
{
mNbResults = 0;
return 0;
}
if(mMaxNbResults<maxNbTris)
{
if(mResultsMemory != mResults)
PX_FREE(mResultsMemory);
mResultsMemory = PX_ALLOCATE(PxU32, maxNbTris, "PxMeshOverlapUtil::findOverlap");
mMaxNbResults = maxNbTris;
}
nbTouchedTris = PxMeshQuery::findOverlapTriangleMesh(geom, geomPose, meshGeom, meshPose, mResultsMemory, mMaxNbResults, 0, overflow);
PX_ASSERT(nbTouchedTris);
PX_ASSERT(!overflow);
}
mNbResults = nbTouchedTris;
return nbTouchedTris;
}
PxU32 PxMeshOverlapUtil::findOverlap(const PxGeometry& geom, const PxTransform& geomPose, const PxHeightFieldGeometry& hfGeom, const PxTransform& hfPose)
{
bool overflow = true;
PxU32 nbTouchedTris = PxMeshQuery::findOverlapHeightField(geom, geomPose, hfGeom, hfPose, mResultsMemory, mMaxNbResults, 0, overflow);
if(overflow)
{
const PxU32 maxNbTris = hfGeom.heightField->getNbRows()*hfGeom.heightField->getNbColumns()*2;
if(!maxNbTris)
{
mNbResults = 0;
return 0;
}
if(mMaxNbResults<maxNbTris)
{
if(mResultsMemory != mResults)
PX_FREE(mResultsMemory);
mResultsMemory = PX_ALLOCATE(PxU32, maxNbTris, "PxMeshOverlapUtil::findOverlap");
mMaxNbResults = maxNbTris;
}
nbTouchedTris = PxMeshQuery::findOverlapHeightField(geom, geomPose, hfGeom, hfPose, mResultsMemory, mMaxNbResults, 0, overflow);
PX_ASSERT(nbTouchedTris);
PX_ASSERT(!overflow);
}
mNbResults = nbTouchedTris;
return nbTouchedTris;
}
namespace
{
template<typename MeshGeometry>
bool computeMeshPenetrationT(PxVec3& direction,
PxReal& depth,
const PxGeometry& geom,
const PxTransform& geomPose,
const MeshGeometry& meshGeom,
const PxTransform& meshPose,
PxU32 maxIter,
PxU32* nbIterOut)
{
PxU32 nbIter = 0;
PxTransform pose = geomPose;
for (; nbIter < maxIter; nbIter++)
{
PxVec3 currentDir;
PxF32 currentDepth;
if (!PxGeometryQuery::computePenetration(currentDir, currentDepth, geom, pose, meshGeom, meshPose))
break;
pose.p += currentDir * currentDepth;
}
if(nbIterOut)
*nbIterOut = nbIter;
PxVec3 diff = pose.p - geomPose.p;
depth = diff.magnitude();
if (depth>0)
direction = diff / depth;
return nbIter!=0;
}
}
bool physx::PxComputeTriangleMeshPenetration(PxVec3& direction,
PxReal& depth,
const PxGeometry& geom,
const PxTransform& geomPose,
const PxTriangleMeshGeometry& meshGeom,
const PxTransform& meshPose,
PxU32 maxIter,
PxU32* nbIter)
{
return computeMeshPenetrationT(direction, depth, geom, geomPose, meshGeom, meshPose, maxIter, nbIter);
}
bool physx::PxComputeHeightFieldPenetration(PxVec3& direction,
PxReal& depth,
const PxGeometry& geom,
const PxTransform& geomPose,
const PxHeightFieldGeometry& hfGeom,
const PxTransform& meshPose,
PxU32 maxIter,
PxU32* nbIter)
{
return computeMeshPenetrationT(direction, depth, geom, geomPose, hfGeom, meshPose, maxIter, nbIter);
}
bool physx::PxExtractIsosurfaceFromSDF(const PxTriangleMesh& triangleMesh, PxArray<PxVec3>& isosurfaceVertices, PxArray<PxU32>& isosurfaceTriangleIndices)
{
PxU32 dimX, dimY, dimZ;
triangleMesh.getSDFDimensions(dimX, dimY, dimZ);
if (dimX == 0 || dimY == 0 || dimZ == 0)
return false;
const Gu::TriangleMesh* guTriangleMesh = static_cast<const Gu::TriangleMesh*>(&triangleMesh);
const Gu::SDF& sdf = guTriangleMesh->getSdfDataFast();
extractIsosurfaceFromSDF(sdf, isosurfaceVertices, isosurfaceTriangleIndices);
return true;
}

View File

@@ -0,0 +1,53 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "ExtOmniPvdRegistrationData.h"
#if PX_SUPPORT_OMNI_PVD
namespace physx
{
namespace Ext
{
void OmniPvdPxExtensionsRegistrationData::registerData(OmniPvdWriter& writer)
{
// auto-generate class/attribute registration code from object definition file
#define OMNI_PVD_WRITER_VAR writer
#include "omnipvd/CmOmniPvdAutoGenRegisterData.h"
#include "OmniPvdPxExtensionsTypes.h"
#include "omnipvd/CmOmniPvdAutoGenClearDefines.h"
#undef OMNI_PVD_WRITER_VAR
}
}
}
#endif

View File

@@ -0,0 +1,72 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_OMNI_PVD_REGISTRATION_DATA_H
#define EXT_OMNI_PVD_REGISTRATION_DATA_H
#if PX_SUPPORT_OMNI_PVD
#include "../pvdruntime/include/OmniPvdWriter.h"
#include "extensions/PxD6Joint.h" // for PxD6Motion
#include "extensions/PxDistanceJoint.h" // for PxDistanceJointFlags
#include "extensions/PxPrismaticJoint.h" // for PxPrismaticJointFlags
#include "extensions/PxRevoluteJoint.h" // for PxRevoluteJointFlags
#include "extensions/PxSphericalJoint.h" // for PxSphericalJointFlags
#include "extensions/PxCustomGeometryExt.h" // for PxCustomGeometryExtBaseConvexCallbacks etc.
namespace physx
{
class PxFixedJoint;
class PxGearJoint;
class PxRackAndPinionJoint;
namespace Ext
{
struct OmniPvdPxExtensionsRegistrationData
{
void registerData(OmniPvdWriter&);
// auto-generate members and setter methods from object definition file
#include "omnipvd/CmOmniPvdAutoGenCreateRegistrationStruct.h"
#include "OmniPvdPxExtensionsTypes.h"
#include "omnipvd/CmOmniPvdAutoGenClearDefines.h"
};
}
}
#endif // PX_SUPPORT_OMNI_PVD
#endif // EXT_OMNI_PVD_REGISTRATION_DATA_H

View File

@@ -0,0 +1,68 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef EXT_OMNI_PVD_SET_DATA_H
#define EXT_OMNI_PVD_SET_DATA_H
//
// This header has to be included to use macros like OMNI_PVD_SET() (set attribute values,
// object instance registration etc.)
//
#define OMNI_PVD_CONTEXT_HANDLE 1
#if PX_SUPPORT_OMNI_PVD
#include "OmniPvdPxExtensionsSampler.h"
#include "omnipvd/PxOmniPvd.h"
//
// Define the macros needed in CmOmniPvdAutoGenSetData.h
//
#undef OMNI_PVD_GET_WRITER
#define OMNI_PVD_GET_WRITER(writer) \
physx::PxOmniPvd::ScopedExclusiveWriter writeLock(physx::Ext::OmniPvdGetInstance()); \
OmniPvdWriter* writer = writeLock.getWriter();
#undef OMNI_PVD_GET_REGISTRATION_DATA
#define OMNI_PVD_GET_REGISTRATION_DATA(registrationData) \
const OmniPvdPxExtensionsRegistrationData* registrationData = physx::Ext::OmniPvdGetPxExtensionsRegistrationData();
#endif // PX_SUPPORT_OMNI_PVD
#include "omnipvd/CmOmniPvdAutoGenSetData.h"
// note: included in all cases since it will provide empty definitions of the helper macros such
// that not all of them have to be guarded by PX_SUPPORT_OMNI_PVD
#endif // EXT_OMNI_PVD_SET_DATA_H

View File

@@ -0,0 +1,123 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#if PX_SUPPORT_OMNI_PVD
#include "OmniPvdPxExtensionsSampler.h"
#include "omnipvd/PxOmniPvd.h"
using namespace physx;
void OmniPvdPxExtensionsSampler::registerClasses()
{
PxOmniPvd::ScopedExclusiveWriter scope(mOmniPvdInstance);
OmniPvdWriter* writer = scope.getWriter();
if (writer)
{
mRegistrationData.registerData(*mOmniPvdInstance->getWriter());
}
}
OmniPvdPxExtensionsSampler::OmniPvdPxExtensionsSampler()
{
mOmniPvdInstance = NULL;
}
OmniPvdPxExtensionsSampler::~OmniPvdPxExtensionsSampler()
{
}
void OmniPvdPxExtensionsSampler::setOmniPvdInstance(physx::PxOmniPvd* omniPvdInstance)
{
mOmniPvdInstance = omniPvdInstance;
}
physx::PxOmniPvd* OmniPvdPxExtensionsSampler::getOmniPvdInstance() {
return mOmniPvdInstance;
}
///////////////////////////////////////////////////////////////////////////////
static OmniPvdPxExtensionsSampler* gOmniPvdPxExtensionsSampler = NULL;
bool OmniPvdPxExtensionsSampler::createInstance()
{
gOmniPvdPxExtensionsSampler = PX_NEW(OmniPvdPxExtensionsSampler)();
return gOmniPvdPxExtensionsSampler != NULL;
}
OmniPvdPxExtensionsSampler* OmniPvdPxExtensionsSampler::getInstance()
{
return gOmniPvdPxExtensionsSampler;
}
void OmniPvdPxExtensionsSampler::destroyInstance()
{
PX_DELETE(gOmniPvdPxExtensionsSampler);
}
namespace physx
{
namespace Ext
{
const OmniPvdPxExtensionsRegistrationData* OmniPvdGetPxExtensionsRegistrationData()
{
OmniPvdPxExtensionsSampler* sampler = OmniPvdPxExtensionsSampler::getInstance();
if (sampler)
{
return &sampler->getRegistrationData();
}
else
{
return NULL;
}
}
PxOmniPvd* OmniPvdGetInstance()
{
OmniPvdPxExtensionsSampler* sampler = OmniPvdPxExtensionsSampler::getInstance();
if (sampler)
{
return sampler->getOmniPvdInstance();
}
else
{
return NULL;
}
}
}
}
#endif

View File

@@ -0,0 +1,79 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef OMNI_PVD_EXTENSION_SAMPLER_H
#define OMNI_PVD_EXTENSION_SAMPLER_H
#if PX_SUPPORT_OMNI_PVD
#include "foundation/PxUserAllocated.h"
#include "ExtOmniPvdRegistrationData.h"
namespace physx
{
class PxOmniPvd;
}
class OmniPvdPxExtensionsSampler : public physx::PxUserAllocated
{
public:
OmniPvdPxExtensionsSampler();
~OmniPvdPxExtensionsSampler();
void setOmniPvdInstance(physx::PxOmniPvd* omniPvdInstance);
physx::PxOmniPvd* getOmniPvdInstance();
void registerClasses();
const physx::Ext::OmniPvdPxExtensionsRegistrationData& getRegistrationData() const { return mRegistrationData; }
// OmniPvdPxExtensionsSampler singleton
static bool createInstance();
static OmniPvdPxExtensionsSampler* getInstance();
static void destroyInstance();
private:
physx::PxOmniPvd* mOmniPvdInstance;
physx::Ext::OmniPvdPxExtensionsRegistrationData mRegistrationData;
};
namespace physx
{
namespace Ext
{
const OmniPvdPxExtensionsRegistrationData* OmniPvdGetPxExtensionsRegistrationData();
PxOmniPvd* OmniPvdGetInstance();
}
}
#endif
#endif

View File

@@ -0,0 +1,294 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
// Declare OMNI_PVD Types and Attributes here!
// The last two attribute parameters could now be derived from the other data, so could be removed in a refactor,
// though explicit control may be better.
// Note that HANDLE attributes have to use (Type const *) style, otherwise it won't compile!
////////////////////////////////////////////////////////////////////////////////
// Enums
////////////////////////////////////////////////////////////////////////////////
OMNI_PVD_ENUM_BEGIN (PxConstraintFlag)
OMNI_PVD_ENUM_VALUE (PxConstraintFlag, eBROKEN)
OMNI_PVD_ENUM_VALUE (PxConstraintFlag, eCOLLISION_ENABLED)
OMNI_PVD_ENUM_VALUE (PxConstraintFlag, eVISUALIZATION)
OMNI_PVD_ENUM_VALUE (PxConstraintFlag, eDRIVE_LIMITS_ARE_FORCES)
OMNI_PVD_ENUM_VALUE (PxConstraintFlag, eIMPROVED_SLERP)
OMNI_PVD_ENUM_VALUE (PxConstraintFlag, eDISABLE_PREPROCESSING)
OMNI_PVD_ENUM_VALUE (PxConstraintFlag, eENABLE_EXTENDED_LIMITS)
OMNI_PVD_ENUM_VALUE (PxConstraintFlag, eGPU_COMPATIBLE)
OMNI_PVD_ENUM_VALUE (PxConstraintFlag, eALWAYS_UPDATE)
OMNI_PVD_ENUM_VALUE (PxConstraintFlag, eDISABLE_CONSTRAINT)
OMNI_PVD_ENUM_END (PxConstraintFlag)
OMNI_PVD_ENUM_BEGIN (PxRevoluteJointFlag)
OMNI_PVD_ENUM_VALUE (PxRevoluteJointFlag, eLIMIT_ENABLED)
OMNI_PVD_ENUM_VALUE (PxRevoluteJointFlag, eDRIVE_ENABLED)
OMNI_PVD_ENUM_VALUE (PxRevoluteJointFlag, eDRIVE_FREESPIN)
OMNI_PVD_ENUM_END (PxRevoluteJointFlag)
OMNI_PVD_ENUM_BEGIN (PxPrismaticJointFlag)
OMNI_PVD_ENUM_VALUE (PxPrismaticJointFlag, eLIMIT_ENABLED)
OMNI_PVD_ENUM_END (PxPrismaticJointFlag)
OMNI_PVD_ENUM_BEGIN (PxDistanceJointFlag)
OMNI_PVD_ENUM_VALUE (PxDistanceJointFlag, eMAX_DISTANCE_ENABLED)
OMNI_PVD_ENUM_VALUE (PxDistanceJointFlag, eMIN_DISTANCE_ENABLED)
OMNI_PVD_ENUM_VALUE (PxDistanceJointFlag, eSPRING_ENABLED)
OMNI_PVD_ENUM_END (PxDistanceJointFlag)
OMNI_PVD_ENUM_BEGIN (PxSphericalJointFlag)
OMNI_PVD_ENUM_VALUE (PxSphericalJointFlag, eLIMIT_ENABLED)
OMNI_PVD_ENUM_END (PxSphericalJointFlag)
OMNI_PVD_ENUM_BEGIN (PxD6JointDriveFlag)
OMNI_PVD_ENUM_VALUE (PxD6JointDriveFlag, eACCELERATION)
OMNI_PVD_ENUM_VALUE (PxD6JointDriveFlag, eOUTPUT_FORCE)
OMNI_PVD_ENUM_END (PxD6JointDriveFlag)
OMNI_PVD_ENUM_BEGIN (PxJointConcreteType)
OMNI_PVD_ENUM_VALUE (PxJointConcreteType, eSPHERICAL)
OMNI_PVD_ENUM_VALUE (PxJointConcreteType, eREVOLUTE)
OMNI_PVD_ENUM_VALUE (PxJointConcreteType, ePRISMATIC)
OMNI_PVD_ENUM_VALUE (PxJointConcreteType, eFIXED)
OMNI_PVD_ENUM_VALUE (PxJointConcreteType, eDISTANCE)
OMNI_PVD_ENUM_VALUE (PxJointConcreteType, eD6)
OMNI_PVD_ENUM_VALUE (PxJointConcreteType, eGEAR)
OMNI_PVD_ENUM_VALUE (PxJointConcreteType, eRACK_AND_PINION)
OMNI_PVD_ENUM_END (PxJointConcreteType)
OMNI_PVD_ENUM_BEGIN (PxD6Motion)
OMNI_PVD_ENUM_VALUE (PxD6Motion, eLOCKED)
OMNI_PVD_ENUM_VALUE (PxD6Motion, eLIMITED)
OMNI_PVD_ENUM_VALUE (PxD6Motion, eFREE)
OMNI_PVD_ENUM_END (PxD6Motion)
OMNI_PVD_ENUM_BEGIN (PxD6AngularDriveConfig)
OMNI_PVD_ENUM_VALUE (PxD6AngularDriveConfig, eSWING_TWIST)
OMNI_PVD_ENUM_VALUE (PxD6AngularDriveConfig, eSLERP)
OMNI_PVD_ENUM_VALUE (PxD6AngularDriveConfig, eLEGACY)
OMNI_PVD_ENUM_END (PxD6AngularDriveConfig)
////////////////////////////////////////////////////////////////////////////////
// Classes
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// PxJoint
////////////////////////////////////////////////////////////////////////////////
OMNI_PVD_CLASS_BEGIN (PxJoint)
OMNI_PVD_ATTRIBUTE (PxJoint, constraint, PxConstraint* const, OmniPvdDataType::eOBJECT_HANDLE)
OMNI_PVD_ATTRIBUTE_FLAG (PxJoint, type, PxJointConcreteType::Enum, PxJointConcreteType)
OMNI_PVD_ATTRIBUTE (PxJoint, actor0, PxRigidActor* const, OmniPvdDataType::eOBJECT_HANDLE)
OMNI_PVD_ATTRIBUTE (PxJoint, actor1, PxRigidActor* const, OmniPvdDataType::eOBJECT_HANDLE)
OMNI_PVD_ATTRIBUTE_ARRAY_FIXED_SIZE (PxJoint, actor0LocalPose, PxTransform, OmniPvdDataType::eFLOAT32, 7)
OMNI_PVD_ATTRIBUTE_ARRAY_FIXED_SIZE (PxJoint, actor1LocalPose, PxTransform, OmniPvdDataType::eFLOAT32, 7)
OMNI_PVD_ATTRIBUTE (PxJoint, breakForce, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxJoint, breakTorque, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_FLAG (PxJoint, constraintFlags, PxConstraintFlags, PxConstraintFlag)
OMNI_PVD_ATTRIBUTE (PxJoint, invMassScale0, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxJoint, invInertiaScale0, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxJoint, invMassScale1, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxJoint, invInertiaScale1, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_STRING (PxJoint, name)
OMNI_PVD_ATTRIBUTE_STRING (PxJoint, concreteTypeName)
OMNI_PVD_CLASS_END (PxJoint)
////////////////////////////////////////////////////////////////////////////////
// PxFixedJoint
////////////////////////////////////////////////////////////////////////////////
OMNI_PVD_CLASS_DERIVED_BEGIN (PxFixedJoint, PxJoint)
OMNI_PVD_CLASS_END (PxFixedJoint)
////////////////////////////////////////////////////////////////////////////////
// PxPrismaticJoint
////////////////////////////////////////////////////////////////////////////////
OMNI_PVD_CLASS_DERIVED_BEGIN (PxPrismaticJoint, PxJoint)
OMNI_PVD_ATTRIBUTE (PxPrismaticJoint, position, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxPrismaticJoint, velocity, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxPrismaticJoint, limitLower, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxPrismaticJoint, limitUpper, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxPrismaticJoint, limitRestitution, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxPrismaticJoint, limitBounceThreshold, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxPrismaticJoint, limitStiffness, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxPrismaticJoint, limitDamping, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_FLAG (PxPrismaticJoint, jointFlags, PxPrismaticJointFlags, PxPrismaticJointFlag)
OMNI_PVD_CLASS_END (PxPrismaticJoint)
////////////////////////////////////////////////////////////////////////////////
// PxRevoluteJoint
////////////////////////////////////////////////////////////////////////////////
OMNI_PVD_CLASS_DERIVED_BEGIN (PxRevoluteJoint, PxJoint)
OMNI_PVD_ATTRIBUTE (PxRevoluteJoint, angle, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxRevoluteJoint, velocity, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxRevoluteJoint, limitLower, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxRevoluteJoint, limitUpper, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxRevoluteJoint, limitRestitution, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxRevoluteJoint, limitBounceThreshold, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxRevoluteJoint, limitStiffness, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxRevoluteJoint, limitDamping, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxRevoluteJoint, driveVelocity, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxRevoluteJoint, driveForceLimit, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxRevoluteJoint, driveGearRatio, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_FLAG (PxRevoluteJoint, jointFlags, PxRevoluteJointFlags, PxRevoluteJointFlag)
OMNI_PVD_CLASS_END (PxRevoluteJoint)
////////////////////////////////////////////////////////////////////////////////
// PxSphericalJoint
////////////////////////////////////////////////////////////////////////////////
OMNI_PVD_CLASS_DERIVED_BEGIN (PxSphericalJoint, PxJoint)
OMNI_PVD_ATTRIBUTE (PxSphericalJoint, swingYAngle, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxSphericalJoint, swingZAngle, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxSphericalJoint, limitYAngle, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxSphericalJoint, limitZAngle, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxSphericalJoint, limitRestitution, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxSphericalJoint, limitBounceThreshold, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxSphericalJoint, limitStiffness, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxSphericalJoint, limitDamping, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_FLAG (PxSphericalJoint, jointFlags, PxSphericalJointFlags, PxSphericalJointFlag)
OMNI_PVD_CLASS_END (PxSphericalJoint)
////////////////////////////////////////////////////////////////////////////////
// PxDistanceJoint
////////////////////////////////////////////////////////////////////////////////
OMNI_PVD_CLASS_DERIVED_BEGIN (PxDistanceJoint, PxJoint)
OMNI_PVD_ATTRIBUTE (PxDistanceJoint, distance, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxDistanceJoint, minDistance, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxDistanceJoint, maxDistance, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxDistanceJoint, tolerance, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxDistanceJoint, stiffness, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxDistanceJoint, damping, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_FLAG (PxDistanceJoint, jointFlags, PxDistanceJointFlags, PxDistanceJointFlag)
OMNI_PVD_CLASS_END (PxDistanceJoint)
////////////////////////////////////////////////////////////////////////////////
// PxGearJoint
////////////////////////////////////////////////////////////////////////////////
OMNI_PVD_CLASS_DERIVED_BEGIN (PxGearJoint, PxJoint)
OMNI_PVD_ATTRIBUTE (PxGearJoint, ratio, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_ARRAY_VARIABLE_SIZE (PxGearJoint, hinges, PxBase* const, OmniPvdDataType::eOBJECT_HANDLE)
OMNI_PVD_CLASS_END (PxGearJoint)
////////////////////////////////////////////////////////////////////////////////
// PxRackAndPinionJoint
////////////////////////////////////////////////////////////////////////////////
OMNI_PVD_CLASS_DERIVED_BEGIN (PxRackAndPinionJoint, PxJoint)
OMNI_PVD_ATTRIBUTE (PxRackAndPinionJoint, ratio, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_ARRAY_VARIABLE_SIZE (PxRackAndPinionJoint, joints, PxBase* const, OmniPvdDataType::eOBJECT_HANDLE)
OMNI_PVD_CLASS_END (PxRackAndPinionJoint)
////////////////////////////////////////////////////////////////////////////////
// PxD6JointDrive
////////////////////////////////////////////////////////////////////////////////
OMNI_PVD_CLASS_BEGIN (PxD6JointDrive)
OMNI_PVD_ATTRIBUTE (PxD6JointDrive, stiffness, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6JointDrive, damping, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6JointDrive, forceLimit, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_FLAG (PxD6JointDrive, flags, PxD6JointDriveFlags, PxD6JointDriveFlag)
OMNI_PVD_CLASS_END (PxD6JointDrive)
////////////////////////////////////////////////////////////////////////////////
// PxD6Joint
////////////////////////////////////////////////////////////////////////////////
OMNI_PVD_CLASS_DERIVED_BEGIN (PxD6Joint, PxJoint)
OMNI_PVD_ATTRIBUTE (PxD6Joint, twistAngle, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, swingYAngle, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, swingZAngle, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_ARRAY_VARIABLE_SIZE (PxD6Joint, motions, PxD6Motion::Enum, OmniPvdDataType::eUINT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, distanceLimitValue, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, distanceLimitRestitution, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, distanceLimitBounceThreshold, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, distanceLimitStiffness, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, distanceLimitDamping, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_ARRAY_VARIABLE_SIZE (PxD6Joint, linearLimitLower, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_ARRAY_VARIABLE_SIZE (PxD6Joint, linearLimitUpper, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_ARRAY_VARIABLE_SIZE (PxD6Joint, linearLimitRestitution, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_ARRAY_VARIABLE_SIZE (PxD6Joint, linearLimitBounceThreshold, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_ARRAY_VARIABLE_SIZE (PxD6Joint, linearLimitStiffness, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE_ARRAY_VARIABLE_SIZE (PxD6Joint, linearLimitDamping, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, twistLimitLower, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, twistLimitUpper, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, twistLimitRestitution, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, twistLimitBounceThreshold, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, twistLimitStiffness, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, twistLimitDamping, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, swingLimitYAngle, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, swingLimitZAngle, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, swingLimitRestitution, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, swingLimitBounceThreshold, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, swingLimitStiffness, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, swingLimitDamping, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, pyramidSwingLimitYAngleMin, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, pyramidSwingLimitYAngleMax, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, pyramidSwingLimitZAngleMin, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, pyramidSwingLimitZAngleMax, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, pyramidSwingLimitRestitution, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, pyramidSwingLimitBounceThreshold, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, pyramidSwingLimitStiffness, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, pyramidSwingLimitDamping, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxD6Joint, driveX, PxD6JointDrive* const, OmniPvdDataType::eOBJECT_HANDLE)
OMNI_PVD_ATTRIBUTE (PxD6Joint, driveY, PxD6JointDrive* const, OmniPvdDataType::eOBJECT_HANDLE)
OMNI_PVD_ATTRIBUTE (PxD6Joint, driveZ, PxD6JointDrive* const, OmniPvdDataType::eOBJECT_HANDLE)
OMNI_PVD_ATTRIBUTE (PxD6Joint, driveSwing, PxD6JointDrive* const, OmniPvdDataType::eOBJECT_HANDLE)
OMNI_PVD_ATTRIBUTE (PxD6Joint, driveTwist, PxD6JointDrive* const, OmniPvdDataType::eOBJECT_HANDLE)
OMNI_PVD_ATTRIBUTE (PxD6Joint, driveSlerp, PxD6JointDrive* const, OmniPvdDataType::eOBJECT_HANDLE)
OMNI_PVD_ATTRIBUTE (PxD6Joint, driveSwing1, PxD6JointDrive* const, OmniPvdDataType::eOBJECT_HANDLE)
OMNI_PVD_ATTRIBUTE (PxD6Joint, driveSwing2, PxD6JointDrive* const, OmniPvdDataType::eOBJECT_HANDLE)
OMNI_PVD_ATTRIBUTE_ARRAY_FIXED_SIZE (PxD6Joint, drivePosition, PxTransform, OmniPvdDataType::eFLOAT32, 7)
OMNI_PVD_ATTRIBUTE_ARRAY_FIXED_SIZE (PxD6Joint, driveLinVelocity, PxVec3, OmniPvdDataType::eFLOAT32, 3)
OMNI_PVD_ATTRIBUTE_ARRAY_FIXED_SIZE (PxD6Joint, driveAngVelocity, PxVec3, OmniPvdDataType::eFLOAT32, 3)
OMNI_PVD_ATTRIBUTE_FLAG (PxD6Joint, angularDriveConfig, PxD6AngularDriveConfig::Enum, PxD6AngularDriveConfig)
OMNI_PVD_CLASS_END (PxD6Joint)
////////////////////////////////////////////////////////////////////////////////
// PxCustomGeometryExt::BaseConvexCallbacks
////////////////////////////////////////////////////////////////////////////////
OMNI_PVD_CLASS_BEGIN (PxCustomGeometryExtBaseConvexCallbacks)
OMNI_PVD_ATTRIBUTE (PxCustomGeometryExtBaseConvexCallbacks, margin, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_CLASS_END (PxCustomGeometryExtBaseConvexCallbacks)
////////////////////////////////////////////////////////////////////////////////
// PxCustomGeometryExt::CylinderCallbacks
////////////////////////////////////////////////////////////////////////////////
OMNI_PVD_CLASS_DERIVED_BEGIN (PxCustomGeometryExtCylinderCallbacks, PxCustomGeometryExtBaseConvexCallbacks)
OMNI_PVD_ATTRIBUTE (PxCustomGeometryExtCylinderCallbacks, height, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxCustomGeometryExtCylinderCallbacks, radius, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxCustomGeometryExtCylinderCallbacks, axis, PxI32, OmniPvdDataType::eINT32)
OMNI_PVD_CLASS_END (PxCustomGeometryExtCylinderCallbacks)
////////////////////////////////////////////////////////////////////////////////
// PxCustomGeometryExt::ConeCallbacks
////////////////////////////////////////////////////////////////////////////////
OMNI_PVD_CLASS_DERIVED_BEGIN (PxCustomGeometryExtConeCallbacks, PxCustomGeometryExtBaseConvexCallbacks)
OMNI_PVD_ATTRIBUTE (PxCustomGeometryExtConeCallbacks, height, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxCustomGeometryExtConeCallbacks, radius, PxReal, OmniPvdDataType::eFLOAT32)
OMNI_PVD_ATTRIBUTE (PxCustomGeometryExtConeCallbacks, axis, PxI32, OmniPvdDataType::eINT32)
OMNI_PVD_CLASS_END (PxCustomGeometryExtConeCallbacks)

View File

@@ -0,0 +1,300 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "common/PxSerializer.h"
#include "foundation/PxHash.h"
#include "foundation/PxHashMap.h"
#include "foundation/PxString.h"
#include "extensions/PxSerialization.h"
#include "PxPhysics.h"
#include "PxPhysicsSerialization.h"
#include "SnFile.h"
#include "SnSerializationContext.h"
#include "serialization/SnSerializationRegistry.h"
#include "serialization/SnSerialUtils.h"
#include "CmCollection.h"
using namespace physx;
using namespace Sn;
namespace
{
PX_INLINE PxU8* alignPtr(PxU8* ptr, PxU32 alignment = PX_SERIAL_ALIGN)
{
if(!alignment)
return ptr;
const PxU32 padding = getPadding(size_t(ptr), alignment);
PX_ASSERT(!getPadding(size_t(ptr + padding), alignment));
return ptr + padding;
}
PX_FORCE_INLINE PxU32 read32(PxU8*& address)
{
const PxU32 value = *reinterpret_cast<PxU32*>(address);
address += sizeof(PxU32);
return value;
}
bool readHeader(PxU8*& address)
{
const PxU32 header = read32(address);
PX_UNUSED(header);
const PxU32 version = read32(address);
PX_UNUSED(version);
char binaryVersionGuid[SN_BINARY_VERSION_GUID_NUM_CHARS + 1];
PxMemCopy(binaryVersionGuid, address, SN_BINARY_VERSION_GUID_NUM_CHARS);
binaryVersionGuid[SN_BINARY_VERSION_GUID_NUM_CHARS] = 0;
address += SN_BINARY_VERSION_GUID_NUM_CHARS;
PX_UNUSED(binaryVersionGuid);
const PxU32 platformTag = read32(address);
PX_UNUSED(platformTag);
const PxU32 markedPadding = read32(address);
PX_UNUSED(markedPadding);
if (header != PX_MAKE_FOURCC('S','E','B','D'))
return PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"Buffer contains data with wrong header indicating invalid binary data.");
if (!checkCompatibility(binaryVersionGuid))
return PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"Buffer contains binary data version 0x%s and is incompatible with this PhysX sdk (0x%s).\n",
binaryVersionGuid, getBinaryVersionGuid());
if (platformTag != getBinaryPlatformTag())
return PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"Buffer contains data with platform mismatch:\nExpected: %s \nActual: %s\n",
getBinaryPlatformName(getBinaryPlatformTag()),
getBinaryPlatformName(platformTag));
return true;
}
bool checkImportReferences(const ImportReference* importReferences, PxU32 nbImportReferences, const Cm::Collection* externalRefs)
{
if (!externalRefs)
{
if (nbImportReferences > 0)
return PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "PxSerialization::createCollectionFromBinary: External references needed but no externalRefs collection specified.");
}
else
{
for (PxU32 i=0; i<nbImportReferences;i++)
{
PxSerialObjectId id = importReferences[i].id;
PxType type = importReferences[i].type;
PxBase* referencedObject = externalRefs->find(id);
if (!referencedObject)
return PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "PxSerialization::createCollectionFromBinary: External reference %llu expected in externalRefs collection but not found.", id);
if (referencedObject->getConcreteType() != type)
return PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "PxSerialization::createCollectionFromBinary: External reference %d type mismatch. Expected %d but found %d in externalRefs collection.", type, referencedObject->getConcreteType());
}
}
return true;
}
}
PxCollection* PxSerialization::createCollectionFromBinary(void* memBlock, PxSerializationRegistry& sr, const PxCollection* pxExternalRefs)
{
if(size_t(memBlock) & (PX_SERIAL_FILE_ALIGN-1))
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "Buffer must be 128-bytes aligned.");
return NULL;
}
PxU8* address = reinterpret_cast<PxU8*>(memBlock);
const Cm::Collection* externalRefs = static_cast<const Cm::Collection*>(pxExternalRefs);
if (!readHeader(address))
{
return NULL;
}
ManifestEntry* manifestTable;
PxU32 nbManifestEntries;
PxU32 nbObjectsInCollection;
PxU32 objectDataEndOffset;
// read number of objects in collection
address = alignPtr(address);
nbObjectsInCollection = read32(address);
// read manifest (PxU32 offset, PxConcreteType type)
{
address = alignPtr(address);
nbManifestEntries = read32(address);
PX_ASSERT(*reinterpret_cast<PxU32*>(address) == 0); //first offset is always 0
manifestTable = (nbManifestEntries > 0) ? reinterpret_cast<ManifestEntry*>(address) : NULL;
address += nbManifestEntries*sizeof(ManifestEntry);
objectDataEndOffset = read32(address);
}
ImportReference* importReferences;
PxU32 nbImportReferences;
// read import references
{
address = alignPtr(address);
nbImportReferences = read32(address);
importReferences = (nbImportReferences > 0) ? reinterpret_cast<ImportReference*>(address) : NULL;
address += nbImportReferences*sizeof(ImportReference);
}
if (!checkImportReferences(importReferences, nbImportReferences, externalRefs))
{
return NULL;
}
ExportReference* exportReferences;
PxU32 nbExportReferences;
// read export references
{
address = alignPtr(address);
nbExportReferences = read32(address);
exportReferences = (nbExportReferences > 0) ? reinterpret_cast<ExportReference*>(address) : NULL;
address += nbExportReferences*sizeof(ExportReference);
}
// read internal references arrays
PxU32 nbInternalPtrReferences = 0;
PxU32 nbInternalHandle16References = 0;
InternalReferencePtr* internalPtrReferences = NULL;
InternalReferenceHandle16* internalHandle16References = NULL;
{
address = alignPtr(address);
nbInternalPtrReferences = read32(address);
internalPtrReferences = (nbInternalPtrReferences > 0) ? reinterpret_cast<InternalReferencePtr*>(address) : NULL;
address += nbInternalPtrReferences*sizeof(InternalReferencePtr);
nbInternalHandle16References = read32(address);
internalHandle16References = (nbInternalHandle16References > 0) ? reinterpret_cast<InternalReferenceHandle16*>(address) : NULL;
address += nbInternalHandle16References*sizeof(InternalReferenceHandle16);
}
// create internal references map
InternalPtrRefMap internalPtrReferencesMap(nbInternalPtrReferences*2);
{
//create hash (we should load the hashes directly from memory)
for (PxU32 i = 0; i < nbInternalPtrReferences; i++)
{
const InternalReferencePtr& ref = internalPtrReferences[i];
internalPtrReferencesMap.insertUnique(ref.reference, SerialObjectIndex(ref.objIndex));
}
}
InternalHandle16RefMap internalHandle16ReferencesMap(nbInternalHandle16References*2);
{
for (PxU32 i=0;i<nbInternalHandle16References;i++)
{
const InternalReferenceHandle16& ref = internalHandle16References[i];
internalHandle16ReferencesMap.insertUnique(ref.reference, SerialObjectIndex(ref.objIndex));
}
}
SerializationRegistry& sn = static_cast<SerializationRegistry&>(sr);
Cm::Collection* collection = static_cast<Cm::Collection*>(PxCreateCollection());
PX_ASSERT(collection);
collection->mObjects.reserve(nbObjectsInCollection*2);
if(nbExportReferences > 0)
collection->mIds.reserve(nbExportReferences*2);
PxU8* addressObjectData = alignPtr(address);
PxU8* addressExtraData = alignPtr(addressObjectData + objectDataEndOffset);
DeserializationContext context(manifestTable, importReferences, addressObjectData, internalPtrReferencesMap, internalHandle16ReferencesMap, externalRefs, addressExtraData);
// iterate over memory containing PxBase objects, create the instances, resolve the addresses, import the external data, add to collection.
{
PxU32 nbObjects = nbObjectsInCollection;
while(nbObjects--)
{
address = alignPtr(address);
context.alignExtraData();
// read PxBase header with type and get corresponding serializer.
PxBase* header = reinterpret_cast<PxBase*>(address);
const PxType classType = header->getConcreteType();
const PxSerializer* serializer = sn.getSerializer(classType);
PX_ASSERT(serializer);
PxBase* instance = serializer->createObject(address, context);
if (!instance)
{
PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"Cannot create class instance for concrete type %d.", classType);
collection->release();
return NULL;
}
collection->internalAdd(instance);
}
}
PX_ASSERT(nbObjectsInCollection == collection->internalGetNbObjects());
// update new collection with export references
{
bool manifestTableAccessError = false;
PX_ASSERT(addressObjectData != NULL);
for (PxU32 i=0;i<nbExportReferences;i++)
{
bool isExternal;
PxU32 manifestIndex = exportReferences[i].objIndex.getIndex(isExternal);
PX_ASSERT(!isExternal);
if (manifestIndex < nbManifestEntries)
{
PxBase* obj = reinterpret_cast<PxBase*>(addressObjectData + manifestTable[manifestIndex].offset);
collection->mIds.insertUnique(exportReferences[i].id, obj);
collection->mObjects[obj] = exportReferences[i].id;
}
else
{
manifestTableAccessError = true;
}
}
if (manifestTableAccessError)
{
PxGetFoundation().error(physx::PxErrorCode::eINTERNAL_ERROR, PX_FL, "Manifest table access error");
collection->release();
return NULL;
}
}
PxAddCollectionToPhysics(*collection);
return collection;
}

View File

@@ -0,0 +1,416 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "common/PxSerializer.h"
#include "foundation/PxPhysicsVersion.h"
#include "foundation/PxUtilities.h"
#include "foundation/PxSort.h"
#include "SnSerializationContext.h"
#include "serialization/SnSerialUtils.h"
#include "serialization/SnSerializationRegistry.h"
using namespace physx;
using namespace Cm;
using namespace Sn;
//------------------------------------------------------------------------------------
//// Binary Serialized PxCollection, format documentation
//------------------------------------------------------------------------------------
//
//
//------------------------------------------------------------------------------------
//// overview:
//// header information
//// manifest table
//// import references
//// export references
//// internal references
//// object data
//// extra data
//------------------------------------------------------------------------------------
//
//
//------------------------------------------------------------------------------------
//// header information:
//// header tag plus various version and platform information
//------------------------------------------------------------------------------------
// header SEBD
// PX_PHYSICS_VERSION
// PX_BINARY_SERIAL_VERSION
// platform tag
// markedPadding (on for PX_CHECKED)
// nbObjectsInCollection
//
//
//------------------------------------------------------------------------------------
//// manifest table:
//// one entry per collected object
//// offsets relative to object data buffer
//------------------------------------------------------------------------------------
// alignment
// PxU32 size
// (PxU32 offset, PxType type)*size
// PxU32 endOffset
//
//
//------------------------------------------------------------------------------------
//// import references:
//// one entry per required reference to external collection
//------------------------------------------------------------------------------------
// alignment
// PxU32 size
// (PxSerialObjectId id, PxType type)*size
//
//
//------------------------------------------------------------------------------------
//// export references:
//// one entry per object in the collection with id
//// object indices point into the manifest table (objects in the same collection)
//------------------------------------------------------------------------------------
// alignment
// PxU32 size
// (PxSerialObjectId id, SerialObjectIndex objIndex)*size
//
//
//------------------------------------------------------------------------------------
//// internal references:
//// one entry per reference, kind pair
//// object indices point either into the manifest table or into the import references
//// depending on whether the entry references the same collection or the external one
//// one section for pointer type references and one for index type references.
//------------------------------------------------------------------------------------
// alignment
// PxU32 sizePtrs;
// (size_t reference, PxU32 kind, SerialObjectIndex objIndex)*sizePtrs
// PxU32 sizeHandle16;
// (PxU16 reference, PxU32 kind, SerialObjectIndex objIndex)*sizeHandle16
//
//
//------------------------------------------------------------------------------------
//// object data:
//// serialized PxBase derived class instances
//// each object size depends on specific class
//// offsets are stored in manifest table
//------------------------------------------------------------------------------------
// alignment
// (PxConcreteType type, -----)
// alignment
// (PxConcreteType type, --------)
// alignment
// (PxConcreteType type, --)
// .
// .
//
//
// -----------------------------------------------------------------------------------
//// extra data:
//// extra data memory block
//// serialized and deserialized by PxBase implementations
////----------------------------------------------------------------------------------
// extra data
//
//------------------------------------------------------------------------------------
namespace
{
class OutputStreamWriter
{
public:
PX_INLINE OutputStreamWriter(PxOutputStream& stream)
: mStream(stream)
, mCount(0)
{}
PX_INLINE PxU32 write(const void* src, PxU32 offset)
{
PxU32 count = mStream.write(src, offset);
mCount += count;
return count;
}
PX_INLINE PxU32 getStoredSize()
{
return mCount;
}
private:
OutputStreamWriter& operator=(const OutputStreamWriter&);
PxOutputStream& mStream;
PxU32 mCount;
};
class LegacySerialStream : public PxSerializationContext
{
public:
LegacySerialStream(OutputStreamWriter& writer,
const PxCollection& collection,
bool exportNames) : mWriter(writer), mCollection(collection), mExportNames(exportNames) {}
void writeData(const void* buffer, PxU32 size) { mWriter.write(buffer, size); }
PxU32 getTotalStoredSize() { return mWriter.getStoredSize(); }
void alignData(PxU32 alignment)
{
if(!alignment)
return;
PxI32 bytesToPad = PxI32(getPadding(getTotalStoredSize(), alignment));
static const PxI32 BUFSIZE = 64;
char buf[BUFSIZE];
PxMemSet(buf, 0, bytesToPad < BUFSIZE ? PxU32(bytesToPad) : PxU32(BUFSIZE));
while(bytesToPad > 0)
{
writeData(buf, bytesToPad < BUFSIZE ? PxU32(bytesToPad) : PxU32(BUFSIZE));
bytesToPad -= BUFSIZE;
}
PX_ASSERT(!getPadding(getTotalStoredSize(), alignment));
}
virtual void registerReference(PxBase&, PxU32, size_t)
{
PxGetFoundation().error(physx::PxErrorCode::eINVALID_OPERATION, PX_FL,
"Cannot register references during exportData, exportExtraData.");
}
virtual const PxCollection& getCollection() const
{
return mCollection;
}
virtual void writeName(const char* name)
{
PxU32 len = name && mExportNames ? PxU32(strlen(name)) + 1 : 0;
writeData(&len, sizeof(len));
if(len) writeData(name, len);
}
private:
LegacySerialStream& operator=(const LegacySerialStream&);
OutputStreamWriter& mWriter;
const PxCollection& mCollection;
bool mExportNames;
};
void writeHeader(PxSerializationContext& stream, bool hasDeserializedAssets)
{
PX_UNUSED(hasDeserializedAssets);
//serialized binary data.
const PxU32 header = PX_MAKE_FOURCC('S','E','B','D');
stream.writeData(&header, sizeof(PxU32));
PxU32 version = PX_PHYSICS_VERSION;
stream.writeData(&version, sizeof(PxU32));
stream.writeData(PX_BINARY_SERIAL_VERSION, SN_BINARY_VERSION_GUID_NUM_CHARS);
PxU32 platformTag = getBinaryPlatformTag();
stream.writeData(&platformTag, sizeof(PxU32));
PxU32 markedPadding = 0;
#if PX_CHECKED
if(!hasDeserializedAssets)
markedPadding = 1;
#endif
stream.writeData(&markedPadding, sizeof(PxU32));
}
template<typename InternalReferenceType>
struct InternalReferencePredicate
{
PX_FORCE_INLINE bool operator()(InternalReferenceType& a, InternalReferenceType& b) const { return a.objIndex < b.objIndex; }
};
}
bool PxSerialization::serializeCollectionToBinary(PxOutputStream& outputStream, PxCollection& pxCollection, PxSerializationRegistry& sr, const PxCollection* pxExternalRefs, bool exportNames)
{
if(!PxSerialization::isSerializable(pxCollection, sr, pxExternalRefs))
return false;
Collection& collection = static_cast<Collection&>(pxCollection);
const Collection* externalRefs = static_cast<const Collection*>(pxExternalRefs);
//temporary memory stream which allows fixing up data up stream
SerializationRegistry& sn = static_cast<SerializationRegistry&>(sr);
// sort collection by "order" value (this will be the order in which they get serialized)
sortCollection(collection, sn, false);
//initialized the context with the sorted collection.
SerializationContext context(collection, externalRefs);
// gather reference information
bool hasDeserializedAssets = false;
{
const PxU32 nb = collection.internalGetNbObjects();
for(PxU32 i=0;i<nb;i++)
{
PxBase* s = collection.internalGetObject(i);
PX_ASSERT(s && s->getConcreteType());
#if PX_CHECKED
//can't guarantee marked padding for deserialized instances
if(!(s->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY))
hasDeserializedAssets = true;
#endif
const PxSerializer* serializer = sn.getSerializer(s->getConcreteType());
PX_ASSERT(serializer);
serializer->registerReferences(*s, context);
}
}
// now start the actual serialization into the output stream
OutputStreamWriter writer(outputStream);
LegacySerialStream stream(writer, collection, exportNames);
writeHeader(stream, hasDeserializedAssets);
// write size of collection
stream.alignData(PX_SERIAL_ALIGN);
PxU32 nbObjectsInCollection = collection.internalGetNbObjects();
stream.writeData(&nbObjectsInCollection, sizeof(PxU32));
// write the manifest table (PxU32 offset, PxConcreteType type)
{
PxArray<ManifestEntry> manifestTable(collection.internalGetNbObjects());
PxU32 headerOffset = 0;
for(PxU32 i=0;i<collection.internalGetNbObjects();i++)
{
PxBase* s = collection.internalGetObject(i);
PX_ASSERT(s && s->getConcreteType());
PxType concreteType = s->getConcreteType();
const PxSerializer* serializer = sn.getSerializer(concreteType);
PX_ASSERT(serializer);
manifestTable[i] = ManifestEntry(headerOffset, concreteType);
PxU32 classSize = PxU32(serializer->getClassSize());
headerOffset += getPadding(classSize, PX_SERIAL_ALIGN) + classSize;
}
stream.alignData(PX_SERIAL_ALIGN);
const PxU32 nb = manifestTable.size();
stream.writeData(&nb, sizeof(PxU32));
stream.writeData(manifestTable.begin(), manifestTable.size()*sizeof(ManifestEntry));
//store offset for end of object buffer (PxU32 offset)
stream.writeData(&headerOffset, sizeof(PxU32));
}
// write import references
{
const PxArray<ImportReference>& importReferences = context.getImportReferences();
stream.alignData(PX_SERIAL_ALIGN);
const PxU32 nb = importReferences.size();
stream.writeData(&nb, sizeof(PxU32));
stream.writeData(importReferences.begin(), importReferences.size()*sizeof(ImportReference));
}
// write export references
{
PxU32 nbIds = collection.getNbIds();
PxArray<ExportReference> exportReferences(nbIds);
//we can't get quickly from id to object index in collection.
//if we only need this here, its not worth to build a hash
nbIds = 0;
for (PxU32 i=0;i<collection.getNbObjects();i++)
{
PxBase& obj = collection.getObject(i);
PxSerialObjectId id = collection.getId(obj);
if (id != PX_SERIAL_OBJECT_ID_INVALID)
{
SerialObjectIndex objIndex(i, false); //i corresponds to manifest entry
exportReferences[nbIds++] = ExportReference(id, objIndex);
}
}
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(&nbIds, sizeof(PxU32));
stream.writeData(exportReferences.begin(), exportReferences.size()*sizeof(ExportReference));
}
// write internal references
{
InternalPtrRefMap& internalPtrReferencesMap = context.getInternalPtrReferencesMap();
PxArray<InternalReferencePtr> internalReferencesPtr(internalPtrReferencesMap.size());
PxU32 nbInternalPtrReferences = 0;
InternalHandle16RefMap& internalHandle16ReferencesMap = context.getInternalHandle16ReferencesMap();
PxArray<InternalReferenceHandle16> internalReferencesHandle16(internalHandle16ReferencesMap.size());
PxU32 nbInternalHandle16References = 0;
{
for(InternalPtrRefMap::Iterator iter = internalPtrReferencesMap.getIterator(); !iter.done(); ++iter)
internalReferencesPtr[nbInternalPtrReferences++] = InternalReferencePtr(iter->first, iter->second);
for(InternalHandle16RefMap::Iterator iter = internalHandle16ReferencesMap.getIterator(); !iter.done(); ++iter)
internalReferencesHandle16[nbInternalHandle16References++] = InternalReferenceHandle16(PxTo16(iter->first), iter->second);
//sort InternalReferences according to SerialObjectIndex for determinism
PxSort<InternalReferencePtr, InternalReferencePredicate<InternalReferencePtr> >(internalReferencesPtr.begin(), internalReferencesPtr.size(), InternalReferencePredicate<InternalReferencePtr>());
PxSort<InternalReferenceHandle16, InternalReferencePredicate<InternalReferenceHandle16> >(internalReferencesHandle16.begin(), internalReferencesHandle16.size(), InternalReferencePredicate<InternalReferenceHandle16>());
}
stream.alignData(PX_SERIAL_ALIGN);
stream.writeData(&nbInternalPtrReferences, sizeof(PxU32));
stream.writeData(internalReferencesPtr.begin(), internalReferencesPtr.size()*sizeof(InternalReferencePtr));
stream.writeData(&nbInternalHandle16References, sizeof(PxU32));
stream.writeData(internalReferencesHandle16.begin(), internalReferencesHandle16.size()*sizeof(InternalReferenceHandle16));
}
// write object data
{
stream.alignData(PX_SERIAL_ALIGN);
const PxU32 nb = collection.internalGetNbObjects();
for(PxU32 i=0;i<nb;i++)
{
PxBase* s = collection.internalGetObject(i);
PX_ASSERT(s && s->getConcreteType());
const PxSerializer* serializer = sn.getSerializer(s->getConcreteType());
PX_ASSERT(serializer);
stream.alignData(PX_SERIAL_ALIGN);
serializer->exportData(*s, stream);
}
}
// write extra data
{
const PxU32 nb = collection.internalGetNbObjects();
for(PxU32 i=0;i<nb;i++)
{
PxBase* s = collection.internalGetObject(i);
PX_ASSERT(s && s->getConcreteType());
const PxSerializer* serializer = sn.getSerializer(s->getConcreteType());
PX_ASSERT(serializer);
stream.alignData(PX_SERIAL_ALIGN);
serializer->exportExtraData(*s, stream);
}
}
return true;
}

View File

@@ -0,0 +1,113 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "common/PxBase.h"
#include "SnSerializationContext.h"
using namespace physx;
using namespace Sn;
PxBase* DeserializationContext::resolveReference(PxU32 kind, size_t reference) const
{
SerialObjectIndex objIndex;
if (kind == PX_SERIAL_REF_KIND_PXBASE)
{
const InternalPtrRefMap::Entry* entry0 = mInternalPtrReferencesMap.find(reference);
PX_ASSERT(entry0);
objIndex = entry0->second;
}
else if (kind == PX_SERIAL_REF_KIND_MATERIAL_IDX)
{
const InternalHandle16RefMap::Entry* entry0 = mInternalHandle16ReferencesMap.find(PxU16(reference));
PX_ASSERT(entry0);
objIndex = entry0->second;
}
else
{
return NULL;
}
bool isExternal;
PxU32 index = objIndex.getIndex(isExternal);
PxBase* base = NULL;
if (isExternal)
{
const ImportReference& entry = mImportReferences[index];
base = mExternalRefs->find(entry.id);
}
else
{
const ManifestEntry& entry = mManifestTable[index];
base = reinterpret_cast<PxBase*>(mObjectDataAddress + entry.offset);
}
PX_ASSERT(base);
return base;
}
void SerializationContext::registerReference(PxBase& serializable, PxU32 kind, size_t reference)
{
#if PX_CHECKED
if ((kind & PX_SERIAL_REF_KIND_PTR_TYPE_BIT) == 0 && reference > 0xffff)
{
PxGetFoundation().error(PxErrorCode::eINVALID_PARAMETER, PX_FL, "PxSerializationContext::registerReference: only 16 bit handles supported.");
return;
}
#endif
bool isExternal = mExternalRefs && mExternalRefs->contains(serializable);
PxU32 index;
if (isExternal)
{
PxSerialObjectId id = mExternalRefs->getId(serializable);
PX_ASSERT(id != PX_SERIAL_OBJECT_ID_INVALID);
if (const PxHashMap<PxSerialObjectId, PxU32>::Entry* entry = mImportReferencesMap.find(id))
{
index = entry->second;
}
else
{
index = mImportReferences.size();
mImportReferencesMap.insert(id, index);
mImportReferences.pushBack(ImportReference(id, serializable.getConcreteType()));
}
}
else
{
PX_ASSERT(mCollection.contains(serializable));
index = mObjToCollectionIndexMap[&serializable];
}
if (kind & PX_SERIAL_REF_KIND_PXBASE)
{
mInternalPtrReferencesMap[reference] = SerialObjectIndex(index, isExternal);
}
else if (kind & PX_SERIAL_REF_KIND_MATERIAL_IDX)
{
mInternalHandle16ReferencesMap[PxU16(reference)] = SerialObjectIndex(index, isExternal);
}
}

View File

@@ -0,0 +1,282 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef SN_SERIALIZATION_CONTEXT_H
#define SN_SERIALIZATION_CONTEXT_H
#include "foundation/PxAssert.h"
#include "foundation/PxMemory.h"
#include "foundation/PxHash.h"
#include "foundation/PxUserAllocated.h"
#include "common/PxSerialFramework.h"
#include "extensions/PxDefaultStreams.h"
#include "CmCollection.h"
#include "CmUtils.h"
#include "../SnSerialUtils.h"
namespace physx
{
namespace Sn
{
struct ManifestEntry
{
PX_FORCE_INLINE ManifestEntry(PxU32 _offset, PxType _type)
{
PxMarkSerializedMemory(this, sizeof(ManifestEntry));
offset = _offset;
type = _type;
}
PX_FORCE_INLINE ManifestEntry() { PxMarkSerializedMemory(this, sizeof(ManifestEntry)); }
PX_FORCE_INLINE void operator =(const ManifestEntry& m)
{
PxMemCopy(this, &m, sizeof(ManifestEntry));
}
PxU32 offset;
PxType type;
};
struct ImportReference
{
PX_FORCE_INLINE ImportReference(PxSerialObjectId _id, PxType _type)
{
PxMarkSerializedMemory(this, sizeof(ImportReference));
id = _id;
type = _type;
}
PX_FORCE_INLINE ImportReference() { PxMarkSerializedMemory(this, sizeof(ImportReference)); }
PX_FORCE_INLINE void operator =(const ImportReference& m)
{
PxMemCopy(this, &m, sizeof(ImportReference));
}
PxSerialObjectId id;
PxType type;
};
#define SERIAL_OBJECT_INDEX_TYPE_BIT (1u<<31)
struct SerialObjectIndex
{
PX_FORCE_INLINE SerialObjectIndex(PxU32 index, bool external) { setIndex(index, external); }
PX_FORCE_INLINE SerialObjectIndex(const SerialObjectIndex& objIndex) : mObjIndex(objIndex.mObjIndex) {}
PX_FORCE_INLINE SerialObjectIndex() : mObjIndex(PX_INVALID_U32) {}
PX_FORCE_INLINE void setIndex(PxU32 index, bool external)
{
PX_ASSERT((index & SERIAL_OBJECT_INDEX_TYPE_BIT) == 0);
mObjIndex = index | (external ? SERIAL_OBJECT_INDEX_TYPE_BIT : 0);
}
PX_FORCE_INLINE PxU32 getIndex(bool& isExternal)
{
PX_ASSERT(mObjIndex != PX_INVALID_U32);
isExternal = (mObjIndex & SERIAL_OBJECT_INDEX_TYPE_BIT) > 0;
return mObjIndex & ~SERIAL_OBJECT_INDEX_TYPE_BIT;
}
PX_FORCE_INLINE bool operator < (const SerialObjectIndex& so) const
{
return mObjIndex < so.mObjIndex;
}
private:
PxU32 mObjIndex;
};
struct ExportReference
{
PX_FORCE_INLINE ExportReference(PxSerialObjectId _id, SerialObjectIndex _objIndex)
{
PxMarkSerializedMemory(this, sizeof(ExportReference));
id = _id;
objIndex = _objIndex;
}
PX_FORCE_INLINE ExportReference() { PxMarkSerializedMemory(this, sizeof(ExportReference)); }
PX_FORCE_INLINE void operator =(const ExportReference& m)
{
PxMemCopy(this, &m, sizeof(ExportReference));
}
PxSerialObjectId id;
SerialObjectIndex objIndex;
};
struct InternalReferencePtr
{
PX_FORCE_INLINE InternalReferencePtr() {}
PX_FORCE_INLINE InternalReferencePtr(size_t _reference, SerialObjectIndex _objIndex) :
reference(_reference),
objIndex(_objIndex)
#if PX_P64_FAMILY
,pad(PX_PADDING_32)
#endif
{
}
size_t reference;
SerialObjectIndex objIndex;
#if PX_P64_FAMILY
PxU32 pad;
#endif
};
struct InternalReferenceHandle16
{
PX_FORCE_INLINE InternalReferenceHandle16() {}
PX_FORCE_INLINE InternalReferenceHandle16(PxU16 _reference, SerialObjectIndex _objIndex) :
reference(_reference),
pad(PX_PADDING_16),
objIndex(_objIndex)
{
}
PxU16 reference;
PxU16 pad;
SerialObjectIndex objIndex;
};
typedef Cm::CollectionHashMap<size_t, SerialObjectIndex> InternalPtrRefMap;
typedef Cm::CollectionHashMap<PxU16, SerialObjectIndex> InternalHandle16RefMap;
class DeserializationContext : public PxDeserializationContext, public PxUserAllocated
{
PX_NOCOPY(DeserializationContext)
public:
DeserializationContext(const ManifestEntry* manifestTable,
const ImportReference* importReferences,
PxU8* objectDataAddress,
const InternalPtrRefMap& internalPtrReferencesMap,
const InternalHandle16RefMap& internalHandle16ReferencesMap,
const Cm::Collection* externalRefs,
PxU8* extraData)
: mManifestTable(manifestTable)
, mImportReferences(importReferences)
, mObjectDataAddress(objectDataAddress)
, mInternalPtrReferencesMap(internalPtrReferencesMap)
, mInternalHandle16ReferencesMap(internalHandle16ReferencesMap)
, mExternalRefs(externalRefs)
{
mExtraDataAddress = extraData;
}
virtual PxBase* resolveReference(PxU32 kind, size_t reference) const;
private:
//various pointers to deserialized data
const ManifestEntry* mManifestTable;
const ImportReference* mImportReferences;
PxU8* mObjectDataAddress;
//internal references maps for resolving references.
const InternalPtrRefMap& mInternalPtrReferencesMap;
const InternalHandle16RefMap& mInternalHandle16ReferencesMap;
//external collection for resolving import references.
const Cm::Collection* mExternalRefs;
//const PxU32 mPhysXVersion;
};
class SerializationContext : public PxSerializationContext, public PxUserAllocated
{
PX_NOCOPY(SerializationContext)
public:
SerializationContext(const Cm::Collection& collection, const Cm::Collection* externalRefs)
: mCollection(collection)
, mExternalRefs(externalRefs)
{
// fill object to collection index map (same ordering as manifest)
for (PxU32 i=0;i<mCollection.internalGetNbObjects();i++)
{
mObjToCollectionIndexMap[mCollection.internalGetObject(i)] = i;
}
}
virtual void writeData(const void* buffer, PxU32 size) { mMemStream.write(buffer, size); }
virtual PxU32 getTotalStoredSize() { return mMemStream.getSize(); }
virtual void alignData(PxU32 alignment = PX_SERIAL_ALIGN)
{
if(!alignment)
return;
PxI32 bytesToPad = PxI32(getPadding(mMemStream.getSize(), alignment));
static const PxI32 BUFSIZE = 64;
char buf[BUFSIZE];
PxMemSet(buf, 0, bytesToPad < BUFSIZE ? PxU32(bytesToPad) : PxU32(BUFSIZE));
while(bytesToPad > 0)
{
mMemStream.write(buf, bytesToPad < BUFSIZE ? PxU32(bytesToPad) : PxU32(BUFSIZE));
bytesToPad -= BUFSIZE;
}
PX_ASSERT(!getPadding(getTotalStoredSize(), alignment));
}
virtual void writeName(const char*)
{
PxGetFoundation().error(physx::PxErrorCode::eINVALID_OPERATION, PX_FL,
"Cannot export names during exportData.");
}
const PxCollection& getCollection() const { return mCollection; }
virtual void registerReference(PxBase& serializable, PxU32 kind, size_t reference);
const PxArray<ImportReference>& getImportReferences() { return mImportReferences; }
InternalPtrRefMap& getInternalPtrReferencesMap() { return mInternalPtrReferencesMap; }
InternalHandle16RefMap& getInternalHandle16ReferencesMap() { return mInternalHandle16ReferencesMap; }
PxU32 getSize() const { return mMemStream.getSize(); }
PxU8* getData() const { return mMemStream.getData(); }
private:
//import reference map for unique registration of import references and corresponding buffer.
PxHashMap<PxSerialObjectId, PxU32> mImportReferencesMap;
PxArray<ImportReference> mImportReferences;
//maps for unique registration of internal references
InternalPtrRefMap mInternalPtrReferencesMap;
InternalHandle16RefMap mInternalHandle16ReferencesMap;
//map for quick lookup of manifest index.
PxHashMap<const PxBase*, PxU32> mObjToCollectionIndexMap;
//collection and externalRefs collection for assigning references.
const Cm::Collection& mCollection;
const Cm::Collection* mExternalRefs;
PxDefaultMemoryOutputStream mMemStream;
};
} // namespace Sn
}
#endif

View File

@@ -0,0 +1,84 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef SN_FILE_H
#define SN_FILE_H
// fopen_s - returns 0 on success, non-zero on failure
#if PX_WINDOWS_FAMILY
#include <stdio.h>
namespace physx
{
namespace sn
{
PX_INLINE PxI32 fopen_s(FILE** file, const char* name, const char* mode)
{
static const PxU32 MAX_LEN = 300;
char buf[MAX_LEN+1];
PxU32 i;
for(i = 0; i<MAX_LEN && name[i]; i++)
buf[i] = name[i] == '/' ? '\\' : name[i];
buf[i] = 0;
return i == MAX_LEN ? -1 : ::fopen_s(file, buf, mode);
};
} // namespace sn
} // namespace physx
#elif PX_UNIX_FAMILY || PX_SWITCH
#include <stdio.h>
namespace physx
{
namespace sn
{
PX_INLINE PxI32 fopen_s(FILE** file, const char* name, const char* mode)
{
FILE* fp = ::fopen(name, mode);
if(fp)
{
*file = fp;
return PxI32(0);
}
return -1;
}
} // namespace sn
} // namespace physx
#else
#error "Platform not supported!"
#endif
#endif //SN_FILE_H

View File

@@ -0,0 +1,131 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "extensions/PxSerialization.h"
#include "foundation/PxPhysicsVersion.h"
#include "SnSerialUtils.h"
#include "foundation/PxString.h"
#include "foundation/PxBasicTemplates.h"
using namespace physx;
namespace
{
#define SN_NUM_BINARY_PLATFORMS 9
const PxU32 sBinaryPlatformTags[SN_NUM_BINARY_PLATFORMS] =
{
PX_MAKE_FOURCC('W','_','3','2'),
PX_MAKE_FOURCC('W','_','6','4'),
PX_MAKE_FOURCC('L','_','3','2'),
PX_MAKE_FOURCC('L','_','6','4'),
PX_MAKE_FOURCC('M','_','3','2'),
PX_MAKE_FOURCC('M','_','6','4'),
PX_MAKE_FOURCC('N','X','3','2'),
PX_MAKE_FOURCC('N','X','6','4'),
PX_MAKE_FOURCC('L','A','6','4')
};
const char* sBinaryPlatformNames[SN_NUM_BINARY_PLATFORMS] =
{
"win32",
"win64",
"linux32",
"linux64",
"mac32",
"mac64",
"switch32",
"switch64",
"linuxaarch64"
};
}
namespace physx { namespace Sn {
PxU32 getBinaryPlatformTag()
{
#if PX_WINDOWS && PX_X86
return sBinaryPlatformTags[0];
#elif PX_WINDOWS && PX_X64
return sBinaryPlatformTags[1];
#elif PX_LINUX && PX_X86
return sBinaryPlatformTags[2];
#elif PX_LINUX && PX_X64
return sBinaryPlatformTags[3];
#elif PX_OSX && PX_X86
return sBinaryPlatformTags[4];
#elif PX_OSX && PX_X64
return sBinaryPlatformTags[5];
#elif PX_SWITCH && !PX_A64
return sBinaryPlatformTags[6];
#elif PX_SWITCH && PX_A64
return sBinaryPlatformTags[7];
#elif PX_LINUX && PX_A64
return sBinaryPlatformTags[8];
#else
#error Unknown binary platform
#endif
}
bool isBinaryPlatformTagValid(physx::PxU32 platformTag)
{
PxU32 platformIndex = 0;
while (platformIndex < SN_NUM_BINARY_PLATFORMS && platformTag != sBinaryPlatformTags[platformIndex]) platformIndex++;
return platformIndex < SN_NUM_BINARY_PLATFORMS;
}
const char* getBinaryPlatformName(physx::PxU32 platformTag)
{
PxU32 platformIndex = 0;
while (platformIndex < SN_NUM_BINARY_PLATFORMS && platformTag != sBinaryPlatformTags[platformIndex]) platformIndex++;
return (platformIndex == SN_NUM_BINARY_PLATFORMS) ? "unknown" : sBinaryPlatformNames[platformIndex];
}
const char* getBinaryVersionGuid()
{
PX_COMPILE_TIME_ASSERT(sizeof(PX_BINARY_SERIAL_VERSION) == SN_BINARY_VERSION_GUID_NUM_CHARS + 1);
return PX_BINARY_SERIAL_VERSION;
}
bool checkCompatibility(const char* binaryVersionGuidCandidate)
{
for(PxU32 i=0; i<SN_BINARY_VERSION_GUID_NUM_CHARS; i++)
{
if (binaryVersionGuidCandidate[i] != PX_BINARY_SERIAL_VERSION[i])
{
return false;
}
}
return true;
}
} // Sn
} // physx

View File

@@ -0,0 +1,56 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef SN_SERIAL_UTILS_H
#define SN_SERIAL_UTILS_H
#define SN_BINARY_VERSION_GUID_NUM_CHARS 32
namespace physx
{
namespace Sn
{
PxU32 getBinaryPlatformTag();
bool isBinaryPlatformTagValid(PxU32 platformTag);
const char* getBinaryPlatformName(PxU32 platformTag);
const char* getBinaryVersionGuid();
bool checkCompatibility(const char* binaryVersionGuidCandidate);
PX_INLINE PxU32 getPadding(size_t value, PxU32 alignment)
{
const PxU32 mask = alignment - 1;
const PxU32 overhead = PxU32(value) & mask;
return (alignment - overhead) & mask;
}
}
}
#endif

View File

@@ -0,0 +1,303 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "common/PxSerializer.h"
#include "extensions/PxConstraintExt.h"
#include "foundation/PxPhysicsVersion.h"
#include "PxPhysicsAPI.h"
#include "SnSerializationRegistry.h"
#include "SnSerialUtils.h"
#include "ExtSerialization.h"
#include "CmCollection.h"
using namespace physx;
using namespace Sn;
namespace
{
struct RequiresCallback : public PxProcessPxBaseCallback
{
RequiresCallback(physx::PxCollection& c) : collection(c) {}
void process(PxBase& base)
{
if(!collection.contains(base))
collection.add(base);
}
PxCollection& collection;
PX_NOCOPY(RequiresCallback)
};
struct CompleteCallback : public PxProcessPxBaseCallback
{
CompleteCallback(physx::PxCollection& r, physx::PxCollection& c, const physx::PxCollection* e) :
required(r), complete(c), external(e) {}
void process(PxBase& base)
{
if(complete.contains(base) || (external && external->contains(base)))
return;
if(!required.contains(base))
required.add(base);
}
PxCollection& required;
PxCollection& complete;
const PxCollection* external;
PX_NOCOPY(CompleteCallback)
};
void getRequiresCollection(PxCollection& required, PxCollection& collection, PxCollection& complete, const PxCollection* external, PxSerializationRegistry& sr, bool followJoints)
{
CompleteCallback callback(required, complete, external);
for (PxU32 i = 0; i < collection.getNbObjects(); ++i)
{
PxBase& s = collection.getObject(i);
const PxSerializer* serializer = sr.getSerializer(s.getConcreteType());
PX_ASSERT(serializer);
serializer->requiresObjects(s, callback);
if(followJoints)
{
PxRigidActor* actor = s.is<PxRigidActor>();
if(actor)
{
PxArray<PxConstraint*> objects(actor->getNbConstraints());
actor->getConstraints(objects.begin(), objects.size());
for(PxU32 j=0;j<objects.size();j++)
{
PxU32 typeId;
PxJoint* joint = reinterpret_cast<PxJoint*>(objects[j]->getExternalReference(typeId));
if(typeId == PxConstraintExtIDs::eJOINT)
{
const PxSerializer* sj = sr.getSerializer(joint->getConcreteType());
PX_ASSERT(sj);
sj->requiresObjects(*joint, callback);
if(!required.contains(*joint))
required.add(*joint);
}
}
}
}
}
}
}
bool PxSerialization::isSerializable(PxCollection& collection, PxSerializationRegistry& sr, const PxCollection* externalReferences)
{
PxCollection* subordinateCollection = PxCreateCollection();
PX_ASSERT(subordinateCollection);
for(PxU32 i = 0; i < collection.getNbObjects(); ++i)
{
PxBase& s = collection.getObject(i);
const PxSerializer* serializer = sr.getSerializer(s.getConcreteType());
PX_ASSERT(serializer);
if(serializer->isSubordinate())
subordinateCollection->add(s);
if(externalReferences)
{
PxSerialObjectId id = collection.getId(s);
if(id != PX_SERIAL_OBJECT_ID_INVALID)
{
PxBase* object = externalReferences->find(id);
if(object && (object != &s))
{
subordinateCollection->release();
return PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"PxSerialization::isSerializable: Reference id %llu used both in current collection and in externalReferences. "
"Please use unique identifiers.", id);
}
}
}
}
PxCollection* requiresCollection = PxCreateCollection();
PX_ASSERT(requiresCollection);
RequiresCallback requiresCallback0(*requiresCollection);
for (PxU32 i = 0; i < collection.getNbObjects(); ++i)
{
PxBase& s = collection.getObject(i);
const PxSerializer* serializer = sr.getSerializer(s.getConcreteType());
PX_ASSERT(serializer);
serializer->requiresObjects(s, requiresCallback0);
Cm::Collection* cmRequiresCollection = static_cast<Cm::Collection*>(requiresCollection);
for(PxU32 j = 0; j < cmRequiresCollection->getNbObjects(); ++j)
{
PxBase& s0 = cmRequiresCollection->getObject(j);
if(subordinateCollection->contains(s0))
{
subordinateCollection->remove(s0);
continue;
}
bool requiredIsInCollection = collection.contains(s0);
if(!requiredIsInCollection)
{
if(externalReferences)
{
if(!externalReferences->contains(s0))
{
PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"PxSerialization::isSerializable: Object of type %s references a missing object of type %s. "
"The missing object needs to be added to either the current collection or the externalReferences collection.",
s.getConcreteTypeName(), s0.getConcreteTypeName());
}
else if(externalReferences->getId(s0) == PX_SERIAL_OBJECT_ID_INVALID)
{
PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"PxSerialization::isSerializable: Object of type %s in externalReferences collection requires an id.",
s0.getConcreteTypeName());
}
else
continue;
}
else
{
PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"PxSerialization::isSerializable: Object of type %s references a missing serial object of type %s. "
"Please completed the collection or specify an externalReferences collection containing the object.",
s.getConcreteTypeName(), s0.getConcreteTypeName());
}
subordinateCollection->release();
requiresCollection->release();
return false;
}
}
cmRequiresCollection->mObjects.clear();
}
requiresCollection->release();
PxU32 numOrphans = subordinateCollection->getNbObjects();
for(PxU32 j = 0; j < numOrphans; ++j)
{
PxBase& subordinate = subordinateCollection->getObject(j);
PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"PxSerialization::isSerializable: An object of type %s is subordinate but not required "
"by other objects in the collection (orphan). Please remove the object from the collection or add its owner.",
subordinate.getConcreteTypeName());
}
subordinateCollection->release();
if(numOrphans>0)
return false;
if(externalReferences)
{
PxCollection* oppositeRequiresCollection = PxCreateCollection();
PX_ASSERT(oppositeRequiresCollection);
RequiresCallback requiresCallback(*oppositeRequiresCollection);
for (PxU32 i = 0; i < externalReferences->getNbObjects(); ++i)
{
PxBase& s = externalReferences->getObject(i);
const PxSerializer* serializer = sr.getSerializer(s.getConcreteType());
PX_ASSERT(serializer);
serializer->requiresObjects(s, requiresCallback);
Cm::Collection* cmCollection = static_cast<Cm::Collection*>(oppositeRequiresCollection);
for(PxU32 j = 0; j < cmCollection->getNbObjects(); ++j)
{
PxBase& s0 = cmCollection->getObject(j);
if(collection.contains(s0))
{
oppositeRequiresCollection->release();
PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL,
"PxSerialization::isSerializable: Object of type %s in externalReferences references an object "
"of type %s in collection (circular dependency).",
s.getConcreteTypeName(), s0.getConcreteTypeName());
return false;
}
}
cmCollection->mObjects.clear();
}
oppositeRequiresCollection->release();
}
return true;
}
void PxSerialization::complete(PxCollection& collection, PxSerializationRegistry& sr, const PxCollection* exceptFor, bool followJoints)
{
PxCollection* curCollection = PxCreateCollection();
PX_ASSERT(curCollection);
curCollection->add(collection);
PxCollection* requiresCollection = PxCreateCollection();
PX_ASSERT(requiresCollection);
do
{
getRequiresCollection(*requiresCollection, *curCollection, collection, exceptFor, sr, followJoints);
collection.add(*requiresCollection);
PxCollection* swap = curCollection;
curCollection = requiresCollection;
requiresCollection = swap;
(static_cast<Cm::Collection*>(requiresCollection))->mObjects.clear();
}while(curCollection->getNbObjects() > 0);
requiresCollection->release();
curCollection->release();
}
void PxSerialization::createSerialObjectIds(PxCollection& collection, const PxSerialObjectId base)
{
PxSerialObjectId localBase = base;
PxU32 nbObjects = collection.getNbObjects();
for (PxU32 i = 0; i < nbObjects; ++i)
{
while(collection.find(localBase))
{
localBase++;
}
PxBase& s = collection.getObject(i);
if(PX_SERIAL_OBJECT_ID_INVALID == collection.getId(s))
{
collection.addId(s, localBase);
localBase++;
}
}
}

View File

@@ -0,0 +1,270 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "common/PxSerializer.h"
#include "foundation/PxString.h"
#include "PxPhysics.h"
#include "PxPhysicsSerialization.h"
#include "PxArticulationLink.h"
#include "SnSerializationRegistry.h"
#include "ExtSerialization.h"
#include "CmCollection.h"
using namespace physx;
namespace
{
class CollectionSorter : public PxProcessPxBaseCallback
{
typedef PxPair<PxBase*, PxSerialObjectId> Object;
class Element
{
public:
Object object;
PxArray<PxU32> children;
bool isFinished;
Element(PxBase* obj = NULL) : object(obj, PX_SERIAL_OBJECT_ID_INVALID), isFinished(false) {}
};
public:
CollectionSorter(Cm::Collection& collection, Sn::SerializationRegistry& sr, bool isRepx) : mCollection(collection), mSr(sr), mIsRepx(isRepx) {}
virtual ~CollectionSorter(){}
void process(PxBase& base)
{
addChild(&base);
//ArticulationLink is not a repx serializer, so should require Articulation here
if( mIsRepx && PxConcreteType::eARTICULATION_LINK == base.getConcreteType() )
{
PxArticulationLink* link = static_cast<PxArticulationLink*>(&base);
PxBase* a = reinterpret_cast<PxBase*>(&link->getArticulation());
if(mCurElement->object.first != a ) //don't require itself
addChild(a);
}
}
void sort()
{
Element element;
PxU32 i;
PxU32 nbObject = mCollection.internalGetNbObjects();
const Cm::Collection::ObjectToIdMap::Entry* objectdatas = mCollection.internalGetObjects();
for( i = 0; i < nbObject; ++i )
{
element.object.first = objectdatas[i].first;
element.object.second = objectdatas[i].second;
mObjToIdMap.insert(objectdatas[i].first, mElements.size());
mElements.pushBack(element);
}
for( i = 0; i < nbObject; ++i )
{
mCurElement = &mElements[i];
const PxSerializer* serializer = mSr.getSerializer(mCurElement->object.first->getConcreteType());
PX_ASSERT(serializer);
serializer->requiresObjects(*mCurElement->object.first, *this);
}
for( i = 0; i < nbObject; ++i )
{
if( mElements[i].isFinished )
continue;
AddElement(mElements[i]);
}
mCollection.mObjects.clear();
for(PxArray<Object>::Iterator o = mSorted.begin(); o != mSorted.end(); ++o )
{
mCollection.internalAdd(o->first, o->second);
}
}
void AddElement(Element& e)
{
if( !e.isFinished )
{
for( PxArray<PxU32>::Iterator child = e.children.begin(); child != e.children.end(); ++child )
{
AddElement(mElements[*child]);
}
mSorted.pushBack(e.object);
e.isFinished = true;
}
}
private:
PX_INLINE void addChild(PxBase* base)
{
PX_ASSERT(mCurElement);
const PxHashMap<PxBase*, PxU32>::Entry* entry = mObjToIdMap.find(base);
if(entry)
mCurElement->children.pushBack(entry->second);
}
CollectionSorter& operator=(const CollectionSorter&);
PxHashMap<PxBase*, PxU32> mObjToIdMap;
PxArray<Element> mElements;
Cm::Collection& mCollection;
Sn::SerializationRegistry& mSr;
PxArray<Object> mSorted;
Element* mCurElement;
bool mIsRepx;
};
}
namespace physx { namespace Sn {
SerializationRegistry::SerializationRegistry(PxPhysics& physics)
: mPhysics(physics)
{
PxRegisterPhysicsSerializers(*this);
Ext::RegisterExtensionsSerializers(*this);
}
SerializationRegistry::~SerializationRegistry()
{
PxUnregisterPhysicsSerializers(*this);
Ext::UnregisterExtensionsSerializers(*this);
if(mSerializers.size() > 0)
{
PxGetFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, PX_FL,
"PxSerializationRegistry::release(): some registered PxSerializer instances were not unregistered");
}
if(mRepXSerializers.size() > 0)
{
PxGetFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, PX_FL,
"PxSerializationRegistry::release(): some registered PxRepXSerializer instances were not unregistered");
}
}
void SerializationRegistry::registerSerializer(PxType type, PxSerializer& serializer)
{
if(mSerializers.find(type))
{
PxGetFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, PX_FL,
"PxSerializationRegistry::registerSerializer: Type %d has already been registered", type);
}
mSerializers.insert(type, &serializer);
}
PxSerializer* SerializationRegistry::unregisterSerializer(PxType type)
{
const SerializerMap::Entry* e = mSerializers.find(type);
PxSerializer* s = e ? e->second : NULL;
if(!mSerializers.erase(type))
{
PxGetFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, PX_FL,
"PxSerializationRegistry::unregisterSerializer: failed to find PxSerializer instance for type %d", type);
}
return s;
}
const PxSerializer* SerializationRegistry::getSerializer(PxType type) const
{
const SerializerMap::Entry* e = mSerializers.find(type);
#if PX_CHECKED
if (!e)
{
PxGetFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, PX_FL,
"PxSerializationRegistry::getSerializer: failed to find PxSerializer instance for type %d", type);
}
#endif
return e ? e->second : NULL;
}
PxType SerializationRegistry::getSerializerType(PxU32 index) const
{
PX_ASSERT(index < mSerializers.size());
return mSerializers.getEntries()[index].first;
}
const char* SerializationRegistry::getSerializerName(PxU32 index) const
{
PX_ASSERT(index < mSerializers.size());
return mSerializers.getEntries()[index].second->getConcreteTypeName();
}
void SerializationRegistry::registerRepXSerializer(PxType type, PxRepXSerializer& serializer)
{
if(mRepXSerializers.find(type))
{
PxGetFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, PX_FL,
"PxSerializationRegistry::registerRepXSerializer: Type %d has already been registered", type);
}
mRepXSerializers.insert(type, &serializer);
}
PxRepXSerializer* SerializationRegistry::getRepXSerializer(const char* typeName) const
{
SerializationRegistry* sr = const_cast<SerializationRegistry*>(this);
for( RepXSerializerMap::Iterator iter = sr->mRepXSerializers.getIterator(); !iter.done(); ++iter)
{
if ( physx::Pxstricmp( iter->second->getTypeName(), typeName ) == 0 )
return iter->second;
}
return NULL;
}
PxRepXSerializer* SerializationRegistry::unregisterRepXSerializer(PxType type)
{
const RepXSerializerMap::Entry* e = mRepXSerializers.find(type);
PxRepXSerializer* s = e ? e->second : NULL;
if(!mRepXSerializers.erase(type))
{
PxGetFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, PX_FL,
"PxSerializationRegistry::unregisterRepXSerializer: failed to find PxRepXSerializer instance for type %d", type);
}
return s;
}
void sortCollection(Cm::Collection& collection, SerializationRegistry& sr, bool isRepx)
{
CollectionSorter sorter(collection, sr, isRepx);
sorter.sort();
}
} // Sn
PxSerializationRegistry* PxSerialization::createSerializationRegistry(PxPhysics& physics)
{
return PX_NEW(Sn::SerializationRegistry)(physics);
}
} // physx

View File

@@ -0,0 +1,88 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef SN_SERIALIZATION_REGISTRY_H
#define SN_SERIALIZATION_REGISTRY_H
#include "extensions/PxSerialization.h"
#include "extensions/PxRepXSerializer.h"
#include "foundation/PxUserAllocated.h"
#include "foundation/PxHashMap.h"
#include "foundation/PxArray.h"
namespace physx
{
namespace Cm { class Collection; }
namespace Sn {
class SerializationRegistry : public PxSerializationRegistry, public PxUserAllocated
{
public:
SerializationRegistry(PxPhysics& physics);
virtual ~SerializationRegistry();
virtual void release(){ PX_DELETE_THIS; }
PxPhysics& getPhysics() const { return mPhysics; }
//binary
void registerSerializer(PxType type, PxSerializer& serializer);
PxSerializer* unregisterSerializer(PxType type);
const PxSerializer* getSerializer(PxType type) const;
const char* getSerializerName(PxU32 index) const;
PxType getSerializerType(PxU32 index) const;
PxU32 getNbSerializers() const { return mSerializers.size(); }
//repx
void registerRepXSerializer(PxType type, PxRepXSerializer& serializer);
PxRepXSerializer* getRepXSerializer(const char* typeName) const;
PxRepXSerializer* unregisterRepXSerializer(PxType type);
protected:
SerializationRegistry &operator=(const SerializationRegistry &);
private:
typedef PxCoalescedHashMap<PxType, PxSerializer*> SerializerMap;
typedef PxHashMap<PxType, PxRepXSerializer*> RepXSerializerMap;
PxPhysics& mPhysics;
SerializerMap mSerializers;
RepXSerializerMap mRepXSerializers;
};
void sortCollection(Cm::Collection& collection, SerializationRegistry& sr, bool isRepx);
} // Sn
} // physx
#endif

View File

@@ -0,0 +1,838 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "foundation/PxAssert.h"
#include "foundation/PxMemory.h"
#include "foundation/PxFoundationConfig.h"
#include "foundation/PxAllocator.h"
#include "PsFastXml.h"
#include <stdio.h>
#include <string.h>
#include <new>
#include <ctype.h>
using namespace physx;
namespace
{
#define MIN_CLOSE_COUNT 2
#define DEFAULT_READ_BUFFER_SIZE (16 * 1024)
#define NUM_ENTITY 5
struct Entity
{
const char* str;
unsigned int strLength;
char chr;
};
static const Entity entity[NUM_ENTITY] = {
{ "&lt;", 4, '<' }, { "&amp;", 5, '&' }, { "&gt;", 4, '>' }, { "&quot;", 6, '\"' }, { "&apos;", 6, '\'' }
};
class MyFastXml : public physx::shdfnd::FastXml
{
public:
enum CharType
{
CT_DATA,
CT_EOF,
CT_SOFT,
CT_END_OF_ELEMENT, // either a forward slash or a greater than symbol
CT_END_OF_LINE
};
MyFastXml(Callback* c)
{
mStreamFromMemory = true;
mCallback = c;
memset(mTypes, CT_DATA, sizeof(mTypes));
mTypes[0] = CT_EOF;
mTypes[uint8_t(' ')] = mTypes[uint8_t('\t')] = CT_SOFT;
mTypes[uint8_t('/')] = mTypes[uint8_t('>')] = mTypes[uint8_t('?')] = CT_END_OF_ELEMENT;
mTypes[uint8_t('\n')] = mTypes[uint8_t('\r')] = CT_END_OF_LINE;
mError = 0;
mStackIndex = 0;
mFileBuf = NULL;
mReadBufferEnd = NULL;
mReadBuffer = NULL;
mReadBufferSize = DEFAULT_READ_BUFFER_SIZE;
mOpenCount = 0;
mLastReadLoc = 0;
for(uint32_t i = 0; i < (MAX_STACK + 1); i++)
{
mStack[i] = NULL;
mStackAllocated[i] = false;
}
}
char* processClose(char c, const char* element, char* scan, int32_t argc, const char** argv,
FastXml::Callback* iface, bool& isError)
{
AttributePairs attr(argc, argv);
if(c == '/' || c == '?')
{
char* slash = const_cast<char*>(static_cast<const char*>(strchr(element, c)));
if(slash)
*slash = 0;
if(c == '?' && strcmp(element, "xml") == 0)
{
isError = true;
if(!iface->processXmlDeclaration(attr, 0, mLineNo))
return NULL;
}
else
{
if(!iface->processElement(element, 0, attr, mLineNo))
{
isError = true;
mError = "User aborted the parsing process";
return NULL;
}
pushElement(element);
const char* close = popElement();
if(!iface->processClose(close, mStackIndex, isError))
{
return NULL;
}
}
if(!slash)
++scan;
}
else
{
scan = skipNextData(scan);
char* data = scan; // this is the data portion of the element, only copies memory if we encounter line feeds
char* dest_data = 0;
while(*scan && *scan != '<')
{
if(getCharType(scan) == CT_END_OF_LINE)
{
if(*scan == '\r')
mLineNo++;
dest_data = scan;
*dest_data++ = ' '; // replace the linefeed with a space...
scan = skipNextData(scan);
while(*scan && *scan != '<')
{
if(getCharType(scan) == CT_END_OF_LINE)
{
if(*scan == '\r')
mLineNo++;
*dest_data++ = ' '; // replace the linefeed with a space...
scan = skipNextData(scan);
}
else
{
*dest_data++ = *scan++;
}
}
break;
}
else if('&' == *scan)
{
dest_data = scan;
while(*scan && *scan != '<')
{
if('&' == *scan)
{
if(*(scan + 1) && *(scan + 1) == '#' && *(scan + 2))
{
if(*(scan + 2) == 'x')
{
// Hexadecimal.
if(!*(scan + 3))
break;
char* q = scan + 3;
q = strchr(q, ';');
if(!q || !*q)
PX_ASSERT(0);
--q;
char ch = char(*q > '9' ? (tolower(*q) - 'a' + 10) : *q - '0');
if(*(--q) != tolower('x'))
ch |= char(*q > '9' ? (tolower(*q) - 'a' + 10) : *q - '0') << 4;
*dest_data++ = ch;
}
else
{
// Decimal.
if(!*(scan + 2))
break;
const char* q = scan + 2;
q = strchr(q, ';');
if(!q || !*q)
PX_ASSERT(0);
--q;
char ch = *q - '0';
if(*(--q) != '#')
ch |= (*q - '0') * 10;
*dest_data++ = ch;
}
char* start = scan;
char* end = strchr(start, ';');
if(end)
{
*end = 0;
scan = end + 1;
}
continue;
}
for(int i = 0; i < NUM_ENTITY; ++i)
{
if(strncmp(entity[i].str, scan, entity[i].strLength) == 0)
{
*dest_data++ = entity[i].chr;
scan += entity[i].strLength;
break;
}
}
}
else
{
*dest_data++ = *scan++;
}
}
break;
}
else
++scan;
}
if(*scan == '<')
{
if(scan[1] != '/')
{
PX_ASSERT(mOpenCount > 0);
mOpenCount--;
}
if(dest_data)
{
*dest_data = 0;
}
else
{
*scan = 0;
}
scan++; // skip it..
if(*data == 0)
data = 0;
if(!iface->processElement(element, data, attr, mLineNo))
{
isError = true;
mError = "User aborted the parsing process";
return NULL;
}
pushElement(element);
// check for the comment use case...
if(scan[0] == '!' && scan[1] == '-' && scan[2] == '-')
{
scan += 3;
while(*scan && *scan == ' ')
++scan;
char* comment = scan;
char* comment_end = strstr(scan, "-->");
if(comment_end)
{
*comment_end = 0;
scan = comment_end + 3;
if(!iface->processComment(comment))
{
isError = true;
mError = "User aborted the parsing process";
return NULL;
}
}
}
else if(*scan == '/')
{
scan = processClose(scan, iface, isError);
if(scan == NULL)
{
return NULL;
}
}
}
else
{
isError = true;
mError = "Data portion of an element wasn't terminated properly";
return NULL;
}
}
if(mOpenCount < MIN_CLOSE_COUNT)
{
scan = readData(scan);
}
return scan;
}
char* processClose(char* scan, FastXml::Callback* iface, bool& isError)
{
const char* start = popElement(), *close = start;
if(scan[1] != '>')
{
scan++;
close = scan;
while(*scan && *scan != '>')
scan++;
*scan = 0;
}
if(0 != strcmp(start, close))
{
isError = true;
mError = "Open and closing tags do not match";
return 0;
}
if(!iface->processClose(close, mStackIndex, isError))
{
// we need to set the read pointer!
uint32_t offset = uint32_t(mReadBufferEnd - scan) - 1;
uint32_t readLoc = mLastReadLoc - offset;
mFileBuf->seek(readLoc);
return NULL;
}
++scan;
return scan;
}
virtual bool processXml(physx::PxInputData& fileBuf, bool streamFromMemory)
{
releaseMemory();
mFileBuf = &fileBuf;
mStreamFromMemory = streamFromMemory;
return processXml(mCallback);
}
// if we have finished processing the data we had pending..
char* readData(char* scan)
{
for(uint32_t i = 0; i < (mStackIndex + 1); i++)
{
if(!mStackAllocated[i])
{
const char* text = mStack[i];
if(text)
{
uint32_t tlen = uint32_t(strlen(text));
mStack[i] = static_cast<const char*>(mCallback->allocate(tlen + 1));
PxMemCopy(const_cast<void*>(static_cast<const void*>(mStack[i])), text, tlen + 1);
mStackAllocated[i] = true;
}
}
}
if(!mStreamFromMemory)
{
if(scan == NULL)
{
uint32_t seekLoc = mFileBuf->tell();
mReadBufferSize = (mFileBuf->getLength() - seekLoc);
}
else
{
return scan;
}
}
if(mReadBuffer == NULL)
{
mReadBuffer = static_cast<char*>(mCallback->allocate(mReadBufferSize + 1));
}
uint32_t offset = 0;
uint32_t readLen = mReadBufferSize;
if(scan)
{
offset = uint32_t(scan - mReadBuffer);
uint32_t copyLen = mReadBufferSize - offset;
if(copyLen)
{
PX_ASSERT(scan >= mReadBuffer);
memmove(mReadBuffer, scan, copyLen);
mReadBuffer[copyLen] = 0;
readLen = mReadBufferSize - copyLen;
}
offset = copyLen;
}
uint32_t readCount = mFileBuf->read(&mReadBuffer[offset], readLen);
while(readCount > 0)
{
mReadBuffer[readCount + offset] = 0; // end of string terminator...
mReadBufferEnd = &mReadBuffer[readCount + offset];
const char* scan_ = &mReadBuffer[offset];
while(*scan_)
{
if(*scan_ == '<' && scan_[1] != '/')
{
mOpenCount++;
}
scan_++;
}
if(mOpenCount < MIN_CLOSE_COUNT)
{
uint32_t oldSize = uint32_t(mReadBufferEnd - mReadBuffer);
mReadBufferSize = mReadBufferSize * 2;
char* oldReadBuffer = mReadBuffer;
mReadBuffer = static_cast<char*>(mCallback->allocate(mReadBufferSize + 1));
PxMemCopy(mReadBuffer, oldReadBuffer, oldSize);
mCallback->deallocate(oldReadBuffer);
offset = oldSize;
uint32_t readSize = mReadBufferSize - oldSize;
readCount = mFileBuf->read(&mReadBuffer[offset], readSize);
if(readCount == 0)
break;
}
else
{
break;
}
}
mLastReadLoc = mFileBuf->tell();
return mReadBuffer;
}
bool processXml(FastXml::Callback* iface)
{
bool ret = true;
const int MAX_ATTRIBUTE = 2048; // can't imagine having more than 2,048 attributes in a single element right?
mLineNo = 1;
char* element, *scan = readData(0);
while(*scan)
{
scan = skipNextData(scan);
if(*scan == 0)
break;
if(*scan == '<')
{
if(scan[1] != '/')
{
PX_ASSERT(mOpenCount > 0);
mOpenCount--;
}
scan++;
if(*scan == '?') // Allow xml declarations
{
scan++;
}
else if(scan[0] == '!' && scan[1] == '-' && scan[2] == '-')
{
scan += 3;
while(*scan && *scan == ' ')
scan++;
char* comment = scan, *comment_end = strstr(scan, "-->");
if(comment_end)
{
*comment_end = 0;
scan = comment_end + 3;
if(!iface->processComment(comment))
{
mError = "User aborted the parsing process";
return false;
}
}
continue;
}
else if(scan[0] == '!') // Allow doctype
{
scan++;
// DOCTYPE syntax differs from usual XML so we parse it here
// Read DOCTYPE
const char* tag = "DOCTYPE";
if(!strstr(scan, tag))
{
mError = "Invalid DOCTYPE";
return false;
}
scan += strlen(tag);
// Skip whites
while(CT_SOFT == getCharType(scan))
++scan;
// Read rootElement
const char* rootElement = scan;
while(CT_DATA == getCharType(scan))
++scan;
char* endRootElement = scan;
// TODO: read remaining fields (fpi, uri, etc.)
while(CT_END_OF_ELEMENT != getCharType(scan++))
;
*endRootElement = 0;
if(!iface->processDoctype(rootElement, 0, 0, 0))
{
mError = "User aborted the parsing process";
return false;
}
continue; // Restart loop
}
}
if(*scan == '/')
{
bool isError = false;
scan = processClose(scan, iface, isError);
if(!scan)
{
if(isError)
{
mError = "User aborted the parsing process";
}
return !isError;
}
}
else
{
if(*scan == '?')
scan++;
element = scan;
int32_t argc = 0;
const char* argv[MAX_ATTRIBUTE];
bool close;
scan = nextSoftOrClose(scan, close);
if(close)
{
char c = *(scan - 1);
if(c != '?' && c != '/')
{
c = '>';
}
*scan++ = 0;
bool isError = false;
scan = processClose(c, element, scan, argc, argv, iface, isError);
if(!scan)
{
if(isError)
{
mError = "User aborted the parsing process";
}
return !isError;
}
}
else
{
if(*scan == 0)
{
return ret;
}
*scan = 0; // place a zero byte to indicate the end of the element name...
scan++;
while(*scan)
{
scan = skipNextData(scan); // advance past any soft seperators (tab or space)
if(getCharType(scan) == CT_END_OF_ELEMENT)
{
char c = *scan++;
if('?' == c)
{
if('>' != *scan) //?>
{
PX_ASSERT(0);
return false;
}
scan++;
}
bool isError = false;
scan = processClose(c, element, scan, argc, argv, iface, isError);
if(!scan)
{
if(isError)
{
mError = "User aborted the parsing process";
}
return !isError;
}
break;
}
else
{
if(argc >= MAX_ATTRIBUTE)
{
mError = "encountered too many attributes";
return false;
}
argv[argc] = scan;
scan = nextSep(scan); // scan up to a space, or an equal
if(*scan)
{
if(*scan != '=')
{
*scan = 0;
scan++;
while(*scan && *scan != '=')
scan++;
if(*scan == '=')
scan++;
}
else
{
*scan = 0;
scan++;
}
if(*scan) // if not eof...
{
scan = skipNextData(scan);
if(*scan == '"')
{
scan++;
argc++;
argv[argc] = scan;
argc++;
while(*scan && *scan != 34)
scan++;
if(*scan == '"')
{
*scan = 0;
scan++;
}
else
{
mError = "Failed to find closing quote for attribute";
return false;
}
}
else
{
// mError = "Expected quote to begin attribute";
// return false;
// PH: let's try to have a more graceful fallback
argc--;
while(*scan != '/' && *scan != '>' && *scan != 0)
scan++;
}
}
} // if( *scan )
} // if ( mTypes[*scan]
} // if( close )
} // if( *scan == '/'
} // while( *scan )
}
if(mStackIndex)
{
mError = "Invalid file format";
return false;
}
return ret;
}
const char* getError(int32_t& lineno)
{
const char* ret = mError;
lineno = mLineNo;
mError = 0;
return ret;
}
virtual void release()
{
Callback* c = mCallback; // get the user allocator interface
MyFastXml* f = this; // cast the this pointer
f->~MyFastXml(); // explicitely invoke the destructor for this class
c->deallocate(f); // now free up the memory associated with it.
}
private:
virtual ~MyFastXml()
{
releaseMemory();
}
PX_INLINE void releaseMemory()
{
mFileBuf = NULL;
mCallback->deallocate(mReadBuffer);
mReadBuffer = NULL;
mStackIndex = 0;
mReadBufferEnd = NULL;
mOpenCount = 0;
mLastReadLoc = 0;
mError = NULL;
for(uint32_t i = 0; i < (mStackIndex + 1); i++)
{
if(mStackAllocated[i])
{
mCallback->deallocate(const_cast<void*>(static_cast<const void*>(mStack[i])));
mStackAllocated[i] = false;
}
mStack[i] = NULL;
}
}
PX_INLINE CharType getCharType(char* scan) const
{
return mTypes[uint8_t(*scan)];
}
PX_INLINE char* nextSoftOrClose(char* scan, bool& close)
{
while(*scan && getCharType(scan) != CT_SOFT && *scan != '>')
scan++;
close = *scan == '>';
return scan;
}
PX_INLINE char* nextSep(char* scan)
{
while(*scan && getCharType(scan) != CT_SOFT && *scan != '=')
scan++;
return scan;
}
PX_INLINE char* skipNextData(char* scan)
{
// while we have data, and we encounter soft seperators or line feeds...
while(*scan && (getCharType(scan) == CT_SOFT || getCharType(scan) == CT_END_OF_LINE))
{
if(*scan == '\n')
mLineNo++;
scan++;
}
return scan;
}
void pushElement(const char* element)
{
PX_ASSERT(mStackIndex < uint32_t(MAX_STACK));
if(mStackIndex < uint32_t(MAX_STACK))
{
if(mStackAllocated[mStackIndex])
{
mCallback->deallocate(const_cast<void*>(static_cast<const void*>(mStack[mStackIndex])));
mStackAllocated[mStackIndex] = false;
}
mStack[mStackIndex++] = element;
}
}
const char* popElement()
{
PX_ASSERT(mStackIndex > 0);
if(mStackAllocated[mStackIndex])
{
mCallback->deallocate(const_cast<void*>(static_cast<const void*>(mStack[mStackIndex])));
mStackAllocated[mStackIndex] = false;
}
mStack[mStackIndex] = NULL;
return mStackIndex ? mStack[--mStackIndex] : NULL;
}
static const int MAX_STACK = 2048;
CharType mTypes[256];
physx::PxInputData* mFileBuf;
char* mReadBuffer;
char* mReadBufferEnd;
uint32_t mOpenCount;
uint32_t mReadBufferSize;
uint32_t mLastReadLoc;
int32_t mLineNo;
const char* mError;
uint32_t mStackIndex;
const char* mStack[MAX_STACK + 1];
bool mStreamFromMemory;
bool mStackAllocated[MAX_STACK + 1];
Callback* mCallback;
};
}
namespace physx
{
namespace shdfnd
{
FastXml* createFastXml(FastXml::Callback* iface)
{
MyFastXml* m = static_cast<MyFastXml*>(iface->allocate(sizeof(MyFastXml)));
if(m)
{
PX_PLACEMENT_NEW(m, MyFastXml(iface));
}
return static_cast<FastXml*>(m);
}
}
}

View File

@@ -0,0 +1,166 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef PSFASTXML_PSFASTXML_H
#define PSFASTXML_PSFASTXML_H
#include "foundation/PxSimpleTypes.h" // defines basic data types; modify for your platform as needed.
#include "foundation/PxIO.h"
#include "foundation/PxAssert.h"
#include "foundation/PxAllocator.h"
namespace physx
{
namespace shdfnd
{
class FastXml
{
PX_NOCOPY(FastXml)
public:
class AttributePairs
{
int argc;
const char** argv;
public:
AttributePairs() : argc(0), argv(NULL)
{
}
AttributePairs(int c, const char** v) : argc(c), argv(v)
{
}
PX_INLINE int getNbAttr() const
{
return argc / 2;
}
const char* getKey(uint32_t index) const
{
PX_ASSERT((index * 2) < uint32_t(argc));
return argv[index * 2];
}
const char* getValue(uint32_t index) const
{
PX_ASSERT((index * 2 + 1) < uint32_t(argc));
return argv[index * 2 + 1];
}
const char* get(const char* attr) const
{
int32_t count = argc / 2;
for(int32_t i = 0; i < count; ++i)
{
const char* key = argv[i * 2], *value = argv[i * 2 + 1];
if(strcmp(key, attr) == 0)
return value;
}
return NULL;
}
};
/***
* Callbacks to the user with the contents of the XML file properly digested.
*/
class Callback
{
public:
virtual ~Callback()
{
}
virtual bool processComment(const char* comment) = 0; // encountered a comment in the XML
// 'element' is the name of the element that is being closed.
// depth is the recursion depth of this element.
// Return true to continue processing the XML file.
// Return false to stop processing the XML file; leaves the read pointer of the stream right after this close
// tag.
// The bool 'isError' indicates whether processing was stopped due to an error, or intentionally canceled early.
virtual bool processClose(const char* element, uint32_t depth, bool& isError) = 0; // process the 'close'
// indicator for a previously
// encountered element
// return true to continue processing the XML document, false to skip.
virtual bool processElement(const char* elementName, // name of the element
const char* elementData, // element data, null if none
const AttributePairs& attr, // attributes
int32_t lineno) = 0; // line number in the source XML file
// process the XML declaration header
virtual bool processXmlDeclaration(const AttributePairs&, // attributes
const char* /*elementData*/, int32_t /*lineno*/)
{
return true;
}
virtual bool processDoctype(const char* /*rootElement*/, // Root element tag
const char* /*type*/, // SYSTEM or PUBLIC
const char* /*fpi*/, // Formal Public Identifier
const char* /*uri*/) // Path to schema file
{
return true;
}
virtual void* allocate(uint32_t size)
{
return PxGetBroadcastAllocator()->allocate(size, "FastXml", PX_FL);
}
virtual void deallocate(void* ptr)
{
PxGetBroadcastAllocator()->deallocate(ptr);
}
};
virtual bool processXml(PxInputData& buff, bool streamFromMemory = false) = 0;
virtual const char* getError(int32_t& lineno) = 0; // report the reason for a parsing error, and the line number
// where it occurred.
FastXml()
{
}
virtual void release() = 0;
protected:
virtual ~FastXml()
{
}
};
FastXml* createFastXml(FastXml::Callback* iface);
} // shdfnd
} // physx
#endif // PSFASTXML_PSFASTXML_H

View File

@@ -0,0 +1,135 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "PxMetaDataObjects.h"
#include "PxExtensionMetaDataObjects.h"
#include "ExtJointMetaDataExtensions.h"
#include "SnJointRepXSerializer.h"
namespace physx {
template<typename TJointType>
inline TJointType* createJoint( PxPhysics& physics,
PxRigidActor* actor0, const PxTransform& localFrame0,
PxRigidActor* actor1, const PxTransform& localFrame1 )
{
PX_UNUSED(physics);
PX_UNUSED(actor0);
PX_UNUSED(actor1);
PX_UNUSED(localFrame0);
PX_UNUSED(localFrame1);
return NULL;
}
template<>
inline PxD6Joint* createJoint<PxD6Joint>(PxPhysics& physics,
PxRigidActor* actor0, const PxTransform& localFrame0,
PxRigidActor* actor1, const PxTransform& localFrame1)
{
return PxD6JointCreate( physics, actor0, localFrame0, actor1, localFrame1 );
}
template<>
inline PxDistanceJoint* createJoint<PxDistanceJoint>(PxPhysics& physics,
PxRigidActor* actor0, const PxTransform& localFrame0,
PxRigidActor* actor1, const PxTransform& localFrame1)
{
return PxDistanceJointCreate( physics, actor0, localFrame0, actor1, localFrame1 );
}
template<>
inline PxFixedJoint* createJoint<PxFixedJoint>(PxPhysics& physics,
PxRigidActor* actor0, const PxTransform& localFrame0,
PxRigidActor* actor1, const PxTransform& localFrame1)
{
return PxFixedJointCreate( physics, actor0, localFrame0, actor1, localFrame1 );
}
template<>
inline PxPrismaticJoint* createJoint<PxPrismaticJoint>(PxPhysics& physics,
PxRigidActor* actor0, const PxTransform& localFrame0,
PxRigidActor* actor1, const PxTransform& localFrame1)
{
return PxPrismaticJointCreate( physics, actor0, localFrame0, actor1, localFrame1 );
}
template<>
inline PxRevoluteJoint* createJoint<PxRevoluteJoint>(PxPhysics& physics,
PxRigidActor* actor0, const PxTransform& localFrame0,
PxRigidActor* actor1, const PxTransform& localFrame1)
{
return PxRevoluteJointCreate( physics, actor0, localFrame0, actor1, localFrame1 );
}
template<>
inline PxSphericalJoint* createJoint<PxSphericalJoint>(PxPhysics& physics,
PxRigidActor* actor0, const PxTransform& localFrame0,
PxRigidActor* actor1, const PxTransform& localFrame1)
{
return PxSphericalJointCreate( physics, actor0, localFrame0, actor1, localFrame1 );
}
template<typename TJointType>
PxRepXObject PxJointRepXSerializer<TJointType>::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection )
{
PxRigidActor* actor0 = NULL;
PxRigidActor* actor1 = NULL;
PxTransform localPose0 = PxTransform(PxIdentity);
PxTransform localPose1 = PxTransform(PxIdentity);
bool ok = true;
if ( inReader.gotoChild( "Actors" ) )
{
ok = readReference<PxRigidActor>( inReader, *inCollection, "actor0", actor0 );
ok &= readReference<PxRigidActor>( inReader, *inCollection, "actor1", actor1 );
inReader.leaveChild();
}
TJointType* theJoint = !ok ? NULL : createJoint<TJointType>( inArgs.physics, actor0, localPose0, actor1, localPose1 );
if ( theJoint )
{
PxConstraint* constraint = theJoint->getConstraint();
PX_ASSERT( constraint );
inCollection->add( *constraint );
this->fileToObjectImpl( theJoint, inReader, inAllocator, inArgs, inCollection );
}
return PxCreateRepXObject(theJoint);
}
template<typename TJointType>
void PxJointRepXSerializer<TJointType>::objectToFileImpl( const TJointType* inObj, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& )
{
writeAllProperties( inObj, inWriter, inTempBuffer, *inCollection );
}
// explicit template instantiations
template struct PxJointRepXSerializer<PxFixedJoint>;
template struct PxJointRepXSerializer<PxDistanceJoint>;
template struct PxJointRepXSerializer<PxD6Joint>;
template struct PxJointRepXSerializer<PxPrismaticJoint>;
template struct PxJointRepXSerializer<PxRevoluteJoint>;
template struct PxJointRepXSerializer<PxSphericalJoint>;
}

View File

@@ -0,0 +1,67 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef SN_JOINT_REPX_SERIALIZER_H
#define SN_JOINT_REPX_SERIALIZER_H
#include "extensions/PxRepXSimpleType.h"
#include "SnRepXSerializerImpl.h"
#if !PX_DOXYGEN
namespace physx
{
#endif
class XmlReader;
class XmlMemoryAllocator;
class XmlWriter;
class MemoryBuffer;
template<typename TJointType>
struct PX_DEPRECATED PxJointRepXSerializer : public RepXSerializerImpl<TJointType>
{
PxJointRepXSerializer(PxAllocatorCallback& inAllocator) : RepXSerializerImpl<TJointType>(inAllocator) {}
virtual PxRepXObject fileToObject(XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection);
virtual void objectToFileImpl(const TJointType* inObj, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs&);
virtual TJointType* allocateObject(PxRepXInstantiationArgs&) { return NULL; }
};
#if PX_SUPPORT_EXTERN_TEMPLATE
// explicit template instantiations declarations
extern template struct PX_DEPRECATED PxJointRepXSerializer<PxD6Joint>;
extern template struct PX_DEPRECATED PxJointRepXSerializer<PxDistanceJoint>;
extern template struct PX_DEPRECATED PxJointRepXSerializer<PxFixedJoint>;
extern template struct PX_DEPRECATED PxJointRepXSerializer<PxPrismaticJoint>;
extern template struct PX_DEPRECATED PxJointRepXSerializer<PxRevoluteJoint>;
extern template struct PX_DEPRECATED PxJointRepXSerializer<PxSphericalJoint>;
#endif
#if !PX_DOXYGEN
} // namespace physx
#endif
#endif

View File

@@ -0,0 +1,134 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef SN_PX_STREAM_OPERATORS_H
#define SN_PX_STREAM_OPERATORS_H
#include "foundation/PxVec3.h"
#include "foundation/PxTransform.h"
#include "foundation/PxBounds3.h"
#include "foundation/PxString.h"
#include "PxFiltering.h"
namespace physx
{
static inline PxU32 strLenght( const char* inStr )
{
return inStr ? PxU32(strlen(inStr)) : 0;
}
}
namespace physx // ADL requires we put the operators in the same namespace as the underlying type of PxOutputStream
{
inline PxOutputStream& operator << ( PxOutputStream& ioStream, const char* inString )
{
if ( inString && *inString )
{
ioStream.write( inString, PxU32(strlen(inString)) );
}
return ioStream;
}
template<typename TDataType>
inline PxOutputStream& toStream( PxOutputStream& ioStream, const char* inFormat, const TDataType inData )
{
char buffer[128] = { 0 };
Pxsnprintf( buffer, 128, inFormat, inData );
ioStream << buffer;
return ioStream;
}
struct endl_obj {};
//static endl_obj endl;
inline PxOutputStream& operator << ( PxOutputStream& ioStream, bool inData ) { ioStream << (inData ? "true" : "false"); return ioStream; }
inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxI32 inData ) { return toStream( ioStream, "%d", inData ); }
inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxU16 inData ) { return toStream( ioStream, "%u", PxU32(inData) ); }
inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxU8 inData ) { return toStream( ioStream, "%u", PxU32(inData) ); }
inline PxOutputStream& operator << ( PxOutputStream& ioStream, char inData ) { return toStream( ioStream, "%c", inData ); }
inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxU32 inData ) { return toStream( ioStream, "%u", inData ); }
inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxU64 inData ) { return toStream( ioStream, "%llu", inData ); }
inline PxOutputStream& operator << ( PxOutputStream& ioStream, const void* inData ) { return ioStream << static_cast<uint64_t>(size_t(inData)); }
inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxF32 inData ) { return toStream( ioStream, "%g", PxF64(inData) ); }
inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxF64 inData ) { return toStream( ioStream, "%g", inData ); }
inline PxOutputStream& operator << ( PxOutputStream& ioStream, endl_obj) { return ioStream << "\n"; }
inline PxOutputStream& operator << ( PxOutputStream& ioStream, const PxVec3& inData )
{
ioStream << inData[0];
ioStream << " ";
ioStream << inData[1];
ioStream << " ";
ioStream << inData[2];
return ioStream;
}
inline PxOutputStream& operator << ( PxOutputStream& ioStream, const PxQuat& inData )
{
ioStream << inData.x;
ioStream << " ";
ioStream << inData.y;
ioStream << " ";
ioStream << inData.z;
ioStream << " ";
ioStream << inData.w;
return ioStream;
}
inline PxOutputStream& operator << ( PxOutputStream& ioStream, const PxTransform& inData )
{
ioStream << inData.q;
ioStream << " ";
ioStream << inData.p;
return ioStream;
}
inline PxOutputStream& operator << ( PxOutputStream& ioStream, const PxBounds3& inData )
{
ioStream << inData.minimum;
ioStream << " ";
ioStream << inData.maximum;
return ioStream;
}
inline PxOutputStream& operator << ( PxOutputStream& ioStream, const PxFilterData& inData )
{
ioStream << inData.word0 << " " << inData.word1 << " " << inData.word2 << " " << inData.word3;
return ioStream;
}
inline PxOutputStream& operator << ( PxOutputStream& ioStream, struct PxMetaDataPlane& inData )
{
ioStream << inData.normal;
ioStream << " ";
ioStream << inData.distance;
return ioStream;
}
}
#endif

View File

@@ -0,0 +1,244 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
DEFINE_REPX_DEFAULT_PROPERTY("PxBoxGeometry.HalfExtents", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphereGeometry.Radius", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Scale", "1 1 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Rotation", "0 0 0 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.ConvexMesh", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Scale", "1 1 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Rotation", "0 0 0 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.MeshFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.TriangleMesh", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightField", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightScale", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.RowScale", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.ColumnScale", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightFieldFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Length", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Mass", "1000" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Speed", "10" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DynamicFriction", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.StaticFriction", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DynamicFrictionV", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.StaticFrictionV", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DirOfAnisotropy", "1 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.FrictionCombineMode", "eAVERAGE" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.RestitutionCombineMode", "eAVERAGE" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.UserData", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.LocalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.SimulationFilterData", "0 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.QueryFilterData", "0 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.ContactOffset", "0.02" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.RestOffset", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Flags", "eSIMULATION_SHAPE|eSCENE_QUERY_SHAPE|eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.UserData", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ActorFlags", "eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.DominanceGroup", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.OwnerClient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ClientBehaviorBits", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.UserData", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.GlobalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ActorFlags", "eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.DominanceGroup", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.OwnerClient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ClientBehaviorBits", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.UserData", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.CMassLocalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Mass", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MassSpaceInertiaTensor", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.GlobalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearVelocity", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularVelocity", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearDamping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularDamping", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MaxAngularVelocity", "7" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SleepEnergyThreshold", "0.005" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minPositionIters", "4" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minVelocityIters", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ContactReportThreshold", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.RigidDynamicFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ParentPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ChildPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetOrientation", "0 0 0 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetVelocity", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.InternalCompliance", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ExternalCompliance", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.yLimit", "0.78539816339" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.zLimit", "0.78539816339" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimitEnabled", "false" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.lower", "-0.78539816339" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.upper", "0.78539816339" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimitEnabled", "false" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ActorFlags", "eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.DominanceGroup", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.OwnerClient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ClientBehaviorBits", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.UserData", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.CMassLocalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Mass", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.MassSpaceInertiaTensor", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.GlobalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.LinearVelocity", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.AngularVelocity", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.MaxProjectionIterations", "4" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SeparationTolerance", "0.1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.InternalDriveIterations", "4" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.ExternalDriveIterations", "4" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minPositionIters", "4" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minVelocityIters", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.UserData", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.UserData", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eX", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eY", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eZ", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eTWIST", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING1", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING2", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.ContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Value", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.ContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Upper", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Lower", "-1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.YAngle", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ZAngle", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DrivePosition", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.linear", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.angular", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionLinearTolerance", "1e+010" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionAngularTolerance", "3.14159" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.UserData", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionLinearTolerance", "1e+010" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionAngularTolerance", "3.14159" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.UserData", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MinDistance", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MaxDistance", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Tolerance", "0.025" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.DistanceJointFlags", "eMAX_DISTANCE_ENABLED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.UserData", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.ContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Upper", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Lower", "-1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveVelocity", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveGearRatio", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.RevoluteJointFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionLinearTolerance", "1e+010" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionAngularTolerance", "3.14159" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.UserData", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.ContactDistance", "0.01" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Upper", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Lower", "-3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.PrismaticJointFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionLinearTolerance", "1e+010" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionAngularTolerance", "3.14159" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.UserData", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.YAngle", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ZAngle", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.SphericalJointFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ProjectionLinearTolerance", "1e+010" )
DEFINE_REPX_DEFAULT_PROPERTY("THEEND", "false" )

View File

@@ -0,0 +1,273 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Length", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Mass", "1000" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Speed", "10" )
DEFINE_REPX_DEFAULT_PROPERTY("PxBoxGeometry.HalfExtents", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphereGeometry.Radius", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Scale", "1 1 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Rotation", "0 0 0 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.ConvexMesh", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Scale", "1 1 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Rotation", "0 0 0 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.MeshFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.TriangleMesh", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightField", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightScale", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.RowScale", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.ColumnScale", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightFieldFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DynamicFriction", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.StaticFriction", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DynamicFrictionV", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.StaticFrictionV", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DirOfAnisotropy", "1 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.FrictionCombineMode", "eAVERAGE" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.RestitutionCombineMode", "eAVERAGE" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.LocalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.SimulationFilterData", "0 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.QueryFilterData", "0 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.ContactOffset", "0.02" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.RestOffset", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Flags", "eSIMULATION_SHAPE|eSCENE_QUERY_SHAPE|eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ActorFlags", "eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.DominanceGroup", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.OwnerClient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ClientBehaviorBits", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.GlobalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ActorFlags", "eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.DominanceGroup", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.OwnerClient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ClientBehaviorBits", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.GlobalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.CMassLocalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Mass", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MassSpaceInertiaTensor", "1 1 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearVelocity", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularVelocity", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearDamping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularDamping", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MaxAngularVelocity", "7" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SleepThreshold", "0.005" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minPositionIters", "4" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minVelocityIters", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ContactReportThreshold", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.RigidDynamicFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ParentPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ChildPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetOrientation", "0 0 0 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetVelocity", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.InternalCompliance", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ExternalCompliance", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.yLimit", "0.78539816339" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.zLimit", "0.78539816339" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TangentialSpring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TangentialDamping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimitContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimitEnabled", "false" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.lower", "-0.78539816339" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.upper", "0.78539816339" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimitEnabled", "false" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimitContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ActorFlags", "eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.DominanceGroup", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.OwnerClient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ClientBehaviorBits", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.GlobalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.CMassLocalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Mass", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.MassSpaceInertiaTensor", "1 1 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.LinearVelocity", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.AngularVelocity", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.MaxProjectionIterations", "4" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SeparationTolerance", "0.1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.InternalDriveIterations", "4" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.ExternalDriveIterations", "4" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minPositionIters", "4" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minVelocityIters", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SleepThreshold", "0.005" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Actors.actor0", "8887040" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Actors.actor1", "8887456" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eX", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eY", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eZ", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eTWIST", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING1", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING2", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.ContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Value", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.ContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Upper", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Lower", "-1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.YAngle", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ZAngle", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DrivePosition", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.linear", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.angular", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionLinearTolerance", "1e+010" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionAngularTolerance", "3.14159" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Actors.actor0", "8887040" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Actors.actor1", "8887456" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionLinearTolerance", "1e+010" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionAngularTolerance", "3.14159" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Actors.actor0", "8887040" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Actors.actor1", "8887456" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MinDistance", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MaxDistance", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Tolerance", "0.025" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.DistanceJointFlags", "eMAX_DISTANCE_ENABLED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Actors.actor0", "8887040" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Actors.actor1", "8887456" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.ContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Upper", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Lower", "-1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveVelocity", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveGearRatio", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.RevoluteJointFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionLinearTolerance", "1e+010" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionAngularTolerance", "3.14159" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Actors.actor0", "8887040" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Actors.actor1", "8887456" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.ContactDistance", "0.01" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Upper", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Lower", "-3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.PrismaticJointFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionLinearTolerance", "1e+010" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionAngularTolerance", "3.14159" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Actors.actor0", "8887040" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Actors.actor1", "8887456" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.YAngle", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ZAngle", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.SphericalJointFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ProjectionLinearTolerance", "1e+010" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ActorFlags", "eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.DominanceGroup", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.OwnerClient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ClientBehaviorBits", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.MotionConstraintScaleBias.scale", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.MotionConstraintScaleBias.bias", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.GlobalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ExternalAcceleration", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.DampingCoefficient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.SolverFrequency", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.SleepLinearVelocity", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("THEEND", "false" )

View File

@@ -0,0 +1,312 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Length", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Mass", "1000" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Speed", "10" )
DEFINE_REPX_DEFAULT_PROPERTY("PxBoxGeometry.HalfExtents", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphereGeometry.Radius", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Scale", "1 1 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Rotation", "0 0 0 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.ConvexMesh", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Scale", "1 1 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Rotation", "0 0 0 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.MeshFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.TriangleMesh", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightField", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightScale", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.RowScale", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.ColumnScale", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightFieldFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DynamicFriction", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.StaticFriction", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.FrictionCombineMode", "eAVERAGE" )
DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.RestitutionCombineMode", "eAVERAGE" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.LocalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.SimulationFilterData", "0 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.QueryFilterData", "0 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.ContactOffset", "0.02" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.RestOffset", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Flags", "eSIMULATION_SHAPE|eSCENE_QUERY_SHAPE|eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ActorFlags", "eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.DominanceGroup", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.OwnerClient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ClientBehaviorBits", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.GlobalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ActorFlags", "eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.DominanceGroup", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.OwnerClient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ClientBehaviorBits", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.GlobalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.CMassLocalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Mass", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MassSpaceInertiaTensor", "1 1 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearVelocity", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularVelocity", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearDamping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularDamping", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MaxAngularVelocity", "7" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SleepThreshold", "0.005" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minPositionIters", "4" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minVelocityIters", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ContactReportThreshold", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.RigidDynamicFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ParentPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ChildPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetOrientation", "0 0 0 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetVelocity", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.InternalCompliance", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ExternalCompliance", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.yLimit", "0.78539816339" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.zLimit", "0.78539816339" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TangentialSpring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TangentialDamping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimitContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimitEnabled", "false" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.lower", "-0.78539816339" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.upper", "0.78539816339" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimitEnabled", "false" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimitContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ActorFlags", "eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.DominanceGroup", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.OwnerClient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ClientBehaviorBits", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.GlobalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.CMassLocalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Mass", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.MassSpaceInertiaTensor", "1 1 1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.LinearVelocity", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.AngularVelocity", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.MaxProjectionIterations", "4" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SeparationTolerance", "0.1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.InternalDriveIterations", "4" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.ExternalDriveIterations", "4" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minPositionIters", "4" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minVelocityIters", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SleepThreshold", "0.005" )
DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Actors.actor0", "8887040" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Actors.actor1", "8887456" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eX", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eY", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eZ", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eTWIST", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING1", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING2", "eLOCKED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.ContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Value", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.ContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Upper", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Lower", "-1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.YAngle", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ZAngle", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.ForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Flags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DrivePosition", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.linear", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.angular", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionLinearTolerance", "1e+010" )
DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionAngularTolerance", "3.14159" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Actors.actor0", "8887040" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Actors.actor1", "8887456" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionLinearTolerance", "1e+010" )
DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionAngularTolerance", "3.14159" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Actors.actor0", "8887040" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Actors.actor1", "8887456" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MinDistance", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MaxDistance", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Tolerance", "0.025" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.DistanceJointFlags", "eMAX_DISTANCE_ENABLED" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Actors.actor0", "8887040" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Actors.actor1", "8887456" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.ContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Upper", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Lower", "-1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveVelocity", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveForceLimit", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveGearRatio", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.RevoluteJointFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionLinearTolerance", "1e+010" )
DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionAngularTolerance", "3.14159" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Actors.actor0", "8887040" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Actors.actor1", "8887456" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.ContactDistance", "0.01" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Upper", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Lower", "-3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.PrismaticJointFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionLinearTolerance", "1e+010" )
DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionAngularTolerance", "3.14159" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Actors.actor0", "8887040" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Actors.actor1", "8887456" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.force", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.torque", "3.40282e+038" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ConstraintFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Restitution", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Spring", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ContactDistance", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.YAngle", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ZAngle", "1.5708" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.SphericalJointFlags", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ProjectionLinearTolerance", "1e+010" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.Name", "" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ActorFlags", "eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.DominanceGroup", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.OwnerClient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ClientBehaviorBits", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.MotionConstraintScaleBias.scale", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.MotionConstraintScaleBias.bias", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.GlobalPose", "0 0 0 1 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ExternalAcceleration", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.DampingCoefficient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.SolverFrequency", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.SleepLinearVelocity", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.InertiaScale", "1" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.FrictionCoefficient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.DragCoefficient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.CollisionMassScale", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ActorFlags", "eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.DominanceGroup", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.OwnerClient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ExternalAcceleration", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ParticleMass", "0.001" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.Restitution", "0.5" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.DynamicFriction", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.StaticFriction", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.SimulationFilterData", "0 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.MaxMotionDistance", "0.06" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.RestOffset", "0.004" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ContactOffset", "0.008" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.GridSize", "0.96" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ProjectionPlane", "0 0 1 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ParticleReadDataFlags", "ePOSITION_BUFFER|eFLAGS_BUFFER" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ParticleBaseFlags", "eCOLLISION_WITH_DYNAMIC_ACTORS|eENABLED|ePER_PARTICLE_REST_OFFSET|ePER_PARTICLE_COLLISION_CACHE_HINT")
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ActorFlags", "eVISUALIZATION" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.DominanceGroup", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.OwnerClient", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.Damping", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ExternalAcceleration", "0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ParticleMass", "0.001" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.Restitution", "0.5" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.DynamicFriction", "0.05" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.StaticFriction", "0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.SimulationFilterData", "0 0 0 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.MaxMotionDistance", "0.06" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.RestOffset", "0.004" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ContactOffset", "0.008" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.GridSize", "0.64" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ProjectionPlane", "0 0 1 0" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.Stiffness", "20" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.Viscosity", "6" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.RestParticleDistance", "0.02" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ParticleReadDataFlags", "ePOSITION_BUFFER|eFLAGS_BUFFER" )
DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ParticleBaseFlags", "eCOLLISION_WITH_DYNAMIC_ACTORS|eENABLED|ePER_PARTICLE_REST_OFFSET|ePER_PARTICLE_COLLISION_CACHE_HINT")
DEFINE_REPX_DEFAULT_PROPERTY("PxAggregate.SelfCollision", "false" )
DEFINE_REPX_DEFAULT_PROPERTY("THEEND", "false" )

View File

@@ -0,0 +1,173 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef SN_REPX_COLLECTION_H
#define SN_REPX_COLLECTION_H
#include "common/PxTolerancesScale.h"
#include "extensions/PxRepXSerializer.h"
namespace physx { namespace Sn {
struct XmlNode;
struct RepXCollectionItem
{
PxRepXObject liveObject;
XmlNode* descriptor;
RepXCollectionItem( PxRepXObject inItem = PxRepXObject(), XmlNode* inDescriptor = NULL )
: liveObject( inItem )
, descriptor( inDescriptor )
{
}
};
struct RepXDefaultEntry
{
const char* name;
const char* value;
RepXDefaultEntry( const char* pn, const char* val ) : name( pn ), value( val ){}
};
/**
* The result of adding an object to the collection.
*/
struct RepXAddToCollectionResult
{
enum Enum
{
Success,
SerializerNotFound,
InvalidParameters, //Null data passed in.
AlreadyInCollection
};
PxSerialObjectId collectionId;
Enum result;
RepXAddToCollectionResult( Enum inResult = Success, const PxSerialObjectId inId = 0 )
: collectionId( inId )
, result( inResult )
{
}
bool isValid() { return result == Success && collectionId != 0; }
};
/**
* A RepX collection contains a set of static data objects that can be transformed
* into live objects. It uses RepX serializer to do two transformations:
* live object <-> collection object (descriptor)
* collection object <-> file system.
*
* A live object is considered to be something live in the physics
* world such as a material or a rigidstatic.
*
* A collection object is a piece of data from which a live object
* of identical characteristics can be created.
*
* Clients need to pass PxCollection so that objects can resolve
* references. In addition, objects must be added in an order such that
* references can be resolved in the first place. So objects must be added
* to the collection *after* objects they are dependent upon.
*
* When deserializing from a file, the collection will allocate char*'s that will
* not be freed when the collection itself is freed. The user must be responsible
* for these character allocations.
*/
class RepXCollection
{
protected:
virtual ~RepXCollection(){}
public:
virtual void destroy() = 0;
/**
* Set the scale on this collection. The scale is saved with the collection.
*
* If the scale wasn't set, it will be invalid.
*/
virtual void setTolerancesScale( const PxTolerancesScale& inScale ) = 0;
/**
* Get the scale that was set at collection creation time or at load time.
* If this is a loaded file and the source data does not contain a scale
* this value will be invalid (PxTolerancesScale::isValid()).
*/
virtual PxTolerancesScale getTolerancesScale() const = 0;
/**
* Set the up vector on this collection. The up vector is saved with the collection.
*
* If the up vector wasn't set, it will be (0,0,0).
*/
virtual void setUpVector( const PxVec3& inUpVector ) = 0;
/**
* If the up vector wasn't set, it will be (0,0,0). Else this will be the up vector
* optionally set when the collection was created.
*/
virtual PxVec3 getUpVector() const = 0;
virtual const char* getVersion() = 0;
static const char* getLatestVersion();
//Necessary accessor functions for translation/upgrading.
virtual const RepXCollectionItem* begin() const = 0;
virtual const RepXCollectionItem* end() const = 0;
//Performs a deep copy of the repx node.
virtual XmlNode* copyRepXNode( const XmlNode* srcNode ) = 0;
virtual void addCollectionItem( RepXCollectionItem inItem ) = 0;
//Create a new repx node with this name. Its value is unset.
virtual XmlNode& createRepXNode( const char* name ) = 0;
virtual RepXCollection& createCollection( const char* inVersionStr ) = 0;
//Release this when finished.
virtual XmlReaderWriter& createNodeEditor() = 0;
virtual PxAllocatorCallback& getAllocator() = 0;
virtual bool instantiateCollection( PxRepXInstantiationArgs& inArgs, PxCollection& inPxCollection ) = 0;
virtual RepXAddToCollectionResult addRepXObjectToCollection( const PxRepXObject& inObject, PxCollection* inCollection, PxRepXInstantiationArgs& inArgs ) = 0;
/**
* Save this collection out to a file stream. Uses the RepX serialize to perform
* collection object->file conversions.
*
* /param[in] inStream Write-only stream to save collection out to.
*/
virtual void save( PxOutputStream& inStream ) = 0;
};
} }
#endif

View File

@@ -0,0 +1,561 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "PxPhysicsAPI.h"
#include "PxMetaDataObjects.h"
#include "SnPxStreamOperators.h"
#include "foundation/PxUtilities.h"
#include "SnXmlImpl.h"
#include "SnXmlSerializer.h"
#include "SnXmlDeserializer.h"
#include "SnRepXCoreSerializer.h"
using namespace physx::Sn;
namespace physx {
typedef PxReadOnlyPropertyInfo<PxPropertyInfoName::PxArticulationLink_InboundJoint, PxArticulationLink, PxArticulationJointReducedCoordinate *> TIncomingJointPropType;
//*************************************************************
// Actual RepXSerializer implementations for PxMaterial
//*************************************************************
PxMaterial* PxMaterialRepXSerializer::allocateObject( PxRepXInstantiationArgs& inArgs )
{
return inArgs.physics.createMaterial(0, 0, 0);
}
PxRepXObject PxShapeRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection )
{
PxProfileAllocatorWrapper wrapper( inAllocator.getAllocator() );
TReaderNameStack names( wrapper );
PxProfileArray<PxU32> contexts( wrapper );
bool hadError = false;
RepXVisitorReader<PxShape> theVisitor( names, contexts, inArgs, inReader, NULL, inAllocator, *inCollection, hadError );
PxArray<PxMaterial*> materials;
PxGeometry* geometry = NULL;
parseShape( theVisitor, geometry, materials );
if(hadError)
return PxRepXObject();
PxShape *theShape = inArgs.physics.createShape( *geometry, materials.begin(), PxTo16(materials.size()) );
switch(geometry->getType())
{
case PxGeometryType::eSPHERE :
static_cast<PxSphereGeometry*>(geometry)->~PxSphereGeometry();
break;
case PxGeometryType::ePLANE :
static_cast<PxPlaneGeometry*>(geometry)->~PxPlaneGeometry();
break;
case PxGeometryType::eCAPSULE :
static_cast<PxCapsuleGeometry*>(geometry)->~PxCapsuleGeometry();
break;
case PxGeometryType::eBOX :
static_cast<PxBoxGeometry*>(geometry)->~PxBoxGeometry();
break;
case PxGeometryType::eCONVEXCORE:
static_cast<PxConvexCoreGeometry*>(geometry)->~PxConvexCoreGeometry();
break;
case PxGeometryType::eCONVEXMESH :
static_cast<PxConvexMeshGeometry*>(geometry)->~PxConvexMeshGeometry();
break;
case PxGeometryType::eTRIANGLEMESH :
static_cast<PxTriangleMeshGeometry*>(geometry)->~PxTriangleMeshGeometry();
break;
case PxGeometryType::eHEIGHTFIELD :
static_cast<PxHeightFieldGeometry*>(geometry)->~PxHeightFieldGeometry();
break;
case PxGeometryType::eTETRAHEDRONMESH :
static_cast<PxTetrahedronMeshGeometry*>(geometry)->~PxTetrahedronMeshGeometry();
break;
case PxGeometryType::ePARTICLESYSTEM:
static_cast<PxParticleSystemGeometry*>(geometry)->~PxParticleSystemGeometry();
break;
case PxGeometryType::eCUSTOM :
static_cast<PxCustomGeometry*>(geometry)->~PxCustomGeometry();
break;
case PxGeometryType::eGEOMETRY_COUNT:
case PxGeometryType::eINVALID:
PX_ASSERT(0);
}
inAllocator.getAllocator().deallocate(geometry);
bool ret = readAllProperties( inArgs, inReader, theShape, inAllocator, *inCollection );
return ret ? PxCreateRepXObject(theShape) : PxRepXObject();
}
//*************************************************************
// Actual RepXSerializer implementations for PxTriangleMesh
//*************************************************************
template<typename TTriIndexElem>
inline void writeTriangle( MemoryBuffer& inTempBuffer, const Triangle<TTriIndexElem>& inTriangle )
{
inTempBuffer << inTriangle.mIdx0
<< " " << inTriangle.mIdx1
<< " " << inTriangle.mIdx2;
}
PxU32 materialAccess( const PxTriangleMesh* inMesh, PxU32 inIndex ) { return inMesh->getTriangleMaterialIndex( inIndex ); }
template<typename TDataType>
void writeDatatype( MemoryBuffer& inTempBuffer, const TDataType& inType ) { inTempBuffer << inType; }
void PxBVH33TriangleMeshRepXSerializer::objectToFileImpl( const PxBVH33TriangleMesh* mesh, PxCollection* /*inCollection*/, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& inArgs )
{
bool hasMatIndex = mesh->getTriangleMaterialIndex(0) != 0xffff;
PxU32 numVertices = mesh->getNbVertices();
const PxVec3* vertices = mesh->getVertices();
writeBuffer( inWriter, inTempBuffer, 2, vertices, numVertices, "Points", writePxVec3 );
bool isU16 = mesh->getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES ? true : false;
PxU32 triCount = mesh->getNbTriangles();
const void* indices = mesh->getTriangles();
if ( isU16 )
writeBuffer( inWriter, inTempBuffer, 2, reinterpret_cast<const Triangle<PxU16>* >( indices ), triCount, "Triangles", writeTriangle<PxU16> );
else
writeBuffer( inWriter, inTempBuffer, 2, reinterpret_cast<const Triangle<PxU32>* >( indices ), triCount, "Triangles", writeTriangle<PxU32> );
if ( hasMatIndex )
writeBuffer( inWriter, inTempBuffer, 6, mesh, materialAccess, triCount, "materialIndices", writeDatatype<PxU32> );
//Cooked stream
PxTriangleMeshDesc meshDesc;
meshDesc.points.count = numVertices;
meshDesc.points.data = vertices;
meshDesc.points.stride = sizeof(PxVec3);
meshDesc.triangles.count = triCount;
meshDesc.triangles.data = indices;
meshDesc.triangles.stride = isU16?3*sizeof(PxU16):3*sizeof(PxU32);
if(isU16)
{
meshDesc.triangles.stride = sizeof(PxU16)*3;
meshDesc.flags |= PxMeshFlag::e16_BIT_INDICES;
}
else
{
meshDesc.triangles.stride = sizeof(PxU32)*3;
}
if(hasMatIndex)
{
PxMaterialTableIndex* materialIndices = new PxMaterialTableIndex[triCount];
for(PxU32 i = 0; i < triCount; i++)
materialIndices[i] = mesh->getTriangleMaterialIndex(i);
meshDesc.materialIndices.data = materialIndices;
meshDesc.materialIndices.stride = sizeof(PxMaterialTableIndex);
}
if(inArgs.cooker != NULL)
{
TMemoryPoolManager theManager(mAllocator);
MemoryBuffer theTempBuf( &theManager );
theTempBuf.clear();
PxCookTriangleMesh( *inArgs.cooker, meshDesc, theTempBuf );
writeBuffer( inWriter, inTempBuffer, 16, theTempBuf.mBuffer, theTempBuf.mWriteOffset, "CookedData", writeDatatype<PxU8> );
}
delete []meshDesc.materialIndices.data;
}
PxRepXObject PxBVH33TriangleMeshRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* /*inCollection*/ )
{
//We can't do a simple inverse; we *have* to cook data to get a mesh.
PxTriangleMeshDesc theDesc;
readStridedBufferProperty<PxVec3>( inReader, "points", theDesc.points, inAllocator);
readStridedBufferProperty<Triangle<PxU32> >( inReader, "triangles", theDesc.triangles, inAllocator);
PxU32 triCount;
readStridedBufferProperty<PxMaterialTableIndex>( inReader, "materialIndices", theDesc.materialIndices, triCount, inAllocator);
PxStridedData cookedData;
cookedData.stride = sizeof(PxU8);
PxU32 dataSize;
readStridedBufferProperty<PxU8>( inReader, "CookedData", cookedData, dataSize, inAllocator);
TMemoryPoolManager theManager(inAllocator.getAllocator());
MemoryBuffer theTempBuf( &theManager );
// PxTriangleMesh* theMesh = NULL;
PxBVH33TriangleMesh* theMesh = NULL;
if(dataSize != 0)
{
theTempBuf.write(cookedData.data, dataSize*sizeof(PxU8));
// theMesh = inArgs.physics.createTriangleMesh( theTempBuf );
theMesh = static_cast<PxBVH33TriangleMesh*>(inArgs.physics.createTriangleMesh( theTempBuf ));
}
if(theMesh == NULL)
{
PX_ASSERT(inArgs.cooker);
theTempBuf.clear();
PxCookingParams params = *inArgs.cooker;
params.midphaseDesc = PxMeshMidPhase::eBVH33;
PxCookTriangleMesh( params, theDesc, theTempBuf );
// theMesh = inArgs.physics.createTriangleMesh( theTempBuf );
theMesh = static_cast<PxBVH33TriangleMesh*>(inArgs.physics.createTriangleMesh( theTempBuf ));
}
return PxCreateRepXObject( theMesh );
}
void PxBVH34TriangleMeshRepXSerializer::objectToFileImpl( const PxBVH34TriangleMesh* mesh, PxCollection* /*inCollection*/, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& inArgs )
{
bool hasMatIndex = mesh->getTriangleMaterialIndex(0) != 0xffff;
PxU32 numVertices = mesh->getNbVertices();
const PxVec3* vertices = mesh->getVertices();
writeBuffer( inWriter, inTempBuffer, 2, vertices, numVertices, "Points", writePxVec3 );
bool isU16 = mesh->getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES ? true : false;
PxU32 triCount = mesh->getNbTriangles();
const void* indices = mesh->getTriangles();
if ( isU16 )
writeBuffer( inWriter, inTempBuffer, 2, reinterpret_cast<const Triangle<PxU16>* >( indices ), triCount, "Triangles", writeTriangle<PxU16> );
else
writeBuffer( inWriter, inTempBuffer, 2, reinterpret_cast<const Triangle<PxU32>* >( indices ), triCount, "Triangles", writeTriangle<PxU32> );
if ( hasMatIndex )
writeBuffer( inWriter, inTempBuffer, 6, mesh, materialAccess, triCount, "materialIndices", writeDatatype<PxU32> );
//Cooked stream
PxTriangleMeshDesc meshDesc;
meshDesc.points.count = numVertices;
meshDesc.points.data = vertices;
meshDesc.points.stride = sizeof(PxVec3);
meshDesc.triangles.count = triCount;
meshDesc.triangles.data = indices;
meshDesc.triangles.stride = isU16?3*sizeof(PxU16):3*sizeof(PxU32);
if(isU16)
{
meshDesc.triangles.stride = sizeof(PxU16)*3;
meshDesc.flags |= PxMeshFlag::e16_BIT_INDICES;
}
else
{
meshDesc.triangles.stride = sizeof(PxU32)*3;
}
if(hasMatIndex)
{
PxMaterialTableIndex* materialIndices = new PxMaterialTableIndex[triCount];
for(PxU32 i = 0; i < triCount; i++)
materialIndices[i] = mesh->getTriangleMaterialIndex(i);
meshDesc.materialIndices.data = materialIndices;
meshDesc.materialIndices.stride = sizeof(PxMaterialTableIndex);
}
if(inArgs.cooker != NULL)
{
TMemoryPoolManager theManager(mAllocator);
MemoryBuffer theTempBuf( &theManager );
theTempBuf.clear();
PxCookTriangleMesh( *inArgs.cooker, meshDesc, theTempBuf );
writeBuffer( inWriter, inTempBuffer, 16, theTempBuf.mBuffer, theTempBuf.mWriteOffset, "CookedData", writeDatatype<PxU8> );
}
delete []meshDesc.materialIndices.data;
}
PxRepXObject PxBVH34TriangleMeshRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* /*inCollection*/ )
{
//We can't do a simple inverse; we *have* to cook data to get a mesh.
PxTriangleMeshDesc theDesc;
readStridedBufferProperty<PxVec3>( inReader, "points", theDesc.points, inAllocator);
readStridedBufferProperty<Triangle<PxU32> >( inReader, "triangles", theDesc.triangles, inAllocator);
PxU32 triCount;
readStridedBufferProperty<PxMaterialTableIndex>( inReader, "materialIndices", theDesc.materialIndices, triCount, inAllocator);
PxStridedData cookedData;
cookedData.stride = sizeof(PxU8);
PxU32 dataSize;
readStridedBufferProperty<PxU8>( inReader, "CookedData", cookedData, dataSize, inAllocator);
TMemoryPoolManager theManager(inAllocator.getAllocator());
MemoryBuffer theTempBuf( &theManager );
// PxTriangleMesh* theMesh = NULL;
PxBVH34TriangleMesh* theMesh = NULL;
if(dataSize != 0)
{
theTempBuf.write(cookedData.data, dataSize*sizeof(PxU8));
// theMesh = inArgs.physics.createTriangleMesh( theTempBuf );
theMesh = static_cast<PxBVH34TriangleMesh*>(inArgs.physics.createTriangleMesh( theTempBuf ));
}
if(theMesh == NULL)
{
PX_ASSERT(inArgs.cooker);
theTempBuf.clear();
PxCookingParams params = *inArgs.cooker;
params.midphaseDesc = PxMeshMidPhase::eBVH34;
PxCookTriangleMesh( params, theDesc, theTempBuf );
// theMesh = inArgs.physics.createTriangleMesh( theTempBuf );
theMesh = static_cast<PxBVH34TriangleMesh*>(inArgs.physics.createTriangleMesh( theTempBuf ));
}
return PxCreateRepXObject(theMesh);
}
//*************************************************************
// Actual RepXSerializer implementations for PxHeightField
//*************************************************************
void PxHeightFieldRepXSerializer::objectToFileImpl( const PxHeightField* inHeightField, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& /*inArgs*/)
{
PxHeightFieldDesc theDesc;
theDesc.nbRows = inHeightField->getNbRows();
theDesc.nbColumns = inHeightField->getNbColumns();
theDesc.format = inHeightField->getFormat();
theDesc.samples.stride = inHeightField->getSampleStride();
theDesc.samples.data = NULL;
theDesc.convexEdgeThreshold = inHeightField->getConvexEdgeThreshold();
theDesc.flags = inHeightField->getFlags();
PxU32 theCellCount = inHeightField->getNbRows() * inHeightField->getNbColumns();
PxU32 theSampleStride = sizeof( PxHeightFieldSample );
PxU32 theSampleBufSize = theCellCount * theSampleStride;
PxHeightFieldSample* theSamples = reinterpret_cast< PxHeightFieldSample*> ( inTempBuffer.mManager->allocate( theSampleBufSize ) );
inHeightField->saveCells( theSamples, theSampleBufSize );
theDesc.samples.data = theSamples;
writeAllProperties( &theDesc, inWriter, inTempBuffer, *inCollection );
writeStridedBufferProperty<PxHeightFieldSample>( inWriter, inTempBuffer, "samples", theDesc.samples, theDesc.nbRows * theDesc.nbColumns, 6, writeHeightFieldSample);
inTempBuffer.mManager->deallocate( reinterpret_cast<PxU8*>(theSamples) );
}
PxRepXObject PxHeightFieldRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection )
{
PX_ASSERT(inArgs.cooker);
PxHeightFieldDesc theDesc;
readAllProperties( inArgs, inReader, &theDesc, inAllocator, *inCollection );
//Now read the data...
PxU32 count = 0; //ignored becaues numRows and numColumns tells the story
readStridedBufferProperty<PxHeightFieldSample>( inReader, "samples", theDesc.samples, count, inAllocator);
PxHeightField* retval = PxCreateHeightField( theDesc, inArgs.physics.getPhysicsInsertionCallback() );
return PxCreateRepXObject(retval);
}
//*************************************************************
// Actual RepXSerializer implementations for PxConvexMesh
//*************************************************************
void PxConvexMeshRepXSerializer::objectToFileImpl( const PxConvexMesh* mesh, PxCollection* /*inCollection*/, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& inArgs )
{
writeBuffer( inWriter, inTempBuffer, 2, mesh->getVertices(), mesh->getNbVertices(), "points", writePxVec3 );
if(inArgs.cooker != NULL)
{
//Cache cooked Data
PxConvexMeshDesc theDesc;
theDesc.points.data = mesh->getVertices();
theDesc.points.stride = sizeof(PxVec3);
theDesc.points.count = mesh->getNbVertices();
theDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX;
TMemoryPoolManager theManager(mAllocator);
MemoryBuffer theTempBuf( &theManager );
PxCookConvexMesh( *inArgs.cooker, theDesc, theTempBuf );
writeBuffer( inWriter, inTempBuffer, 16, theTempBuf.mBuffer, theTempBuf.mWriteOffset, "CookedData", writeDatatype<PxU8> );
}
}
//Conversion from scene object to descriptor.
PxRepXObject PxConvexMeshRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* /*inCollection*/)
{
PxConvexMeshDesc theDesc;
readStridedBufferProperty<PxVec3>( inReader, "points", theDesc.points, inAllocator);
theDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX;
PxStridedData cookedData;
cookedData.stride = sizeof(PxU8);
PxU32 dataSize;
readStridedBufferProperty<PxU8>( inReader, "CookedData", cookedData, dataSize, inAllocator);
TMemoryPoolManager theManager(inAllocator.getAllocator());
MemoryBuffer theTempBuf( &theManager );
PxConvexMesh* theMesh = NULL;
if(dataSize != 0)
{
theTempBuf.write(cookedData.data, dataSize*sizeof(PxU8));
theMesh = inArgs.physics.createConvexMesh( theTempBuf );
}
if(theMesh == NULL)
{
PX_ASSERT(inArgs.cooker);
theTempBuf.clear();
PxCookConvexMesh( *inArgs.cooker, theDesc, theTempBuf );
theMesh = inArgs.physics.createConvexMesh( theTempBuf );
}
return PxCreateRepXObject(theMesh);
}
//*************************************************************
// Actual RepXSerializer implementations for PxRigidStatic
//*************************************************************
PxRigidStatic* PxRigidStaticRepXSerializer::allocateObject( PxRepXInstantiationArgs& inArgs )
{
return inArgs.physics.createRigidStatic( PxTransform(PxIdentity) );
}
//*************************************************************
// Actual RepXSerializer implementations for PxRigidDynamic
//*************************************************************
PxRigidDynamic* PxRigidDynamicRepXSerializer::allocateObject( PxRepXInstantiationArgs& inArgs )
{
return inArgs.physics.createRigidDynamic( PxTransform(PxIdentity) );
}
//*************************************************************
// Actual RepXSerializer implementations for PxArticulationReducedCoordinate
//*************************************************************
void PxArticulationReducedCoordinateRepXSerializer::objectToFileImpl(const PxArticulationReducedCoordinate* inObj, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& /*inArgs*/)
{
TNameStack nameStack(inTempBuffer.mManager->mWrapper);
Sn::TArticulationLinkLinkMap linkMap(inTempBuffer.mManager->mWrapper);
RepXVisitorWriter<PxArticulationReducedCoordinate> writer(nameStack, inWriter, inObj, inTempBuffer, *inCollection, &linkMap);
RepXPropertyFilter<RepXVisitorWriter<PxArticulationReducedCoordinate> > theOp(writer);
visitAllProperties<PxArticulationReducedCoordinate>(theOp);
}
PxArticulationReducedCoordinate* PxArticulationReducedCoordinateRepXSerializer::allocateObject(PxRepXInstantiationArgs& inArgs) { return inArgs.physics.createArticulationReducedCoordinate(); }
//*************************************************************
// Actual RepXSerializer implementations for PxAggregate
//*************************************************************
void PxAggregateRepXSerializer::objectToFileImpl( const PxAggregate* data, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& /*inArgs*/)
{
PxArticulationLink *link = NULL;
inWriter.addAndGotoChild( "Actors" );
for(PxU32 i = 0; i < data->getNbActors(); ++i)
{
PxActor* actor;
if(data->getActors(&actor, 1, i))
{
link = actor->is<PxArticulationLink>();
}
if(link && !link->getInboundJoint() )
{
writeProperty( inWriter, *inCollection, inTempBuffer, "PxArticulationRef", &link->getArticulation());
}
else if( !link )
{
PxSerialObjectId theId = 0;
theId = inCollection->getId( *actor );
if( theId == 0 )
theId = static_cast<uint64_t>(size_t(actor));
writeProperty( inWriter, *inCollection, inTempBuffer, "PxActorRef", theId );
}
}
inWriter.leaveChild( );
writeProperty( inWriter, *inCollection, inTempBuffer, "NumActors", data->getNbActors() );
writeProperty( inWriter, *inCollection, inTempBuffer, "MaxNbActors", data->getMaxNbActors() );
writeProperty(inWriter, *inCollection, inTempBuffer, "MaxNbShapes", data->getMaxNbShapes());
writeProperty( inWriter, *inCollection, inTempBuffer, "SelfCollision", data->getSelfCollision() );
writeAllProperties( data, inWriter, inTempBuffer, *inCollection );
}
PxRepXObject PxAggregateRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection )
{
PxU32 numActors;
readProperty( inReader, "NumActors", numActors );
PxU32 maxNbActors;
readProperty( inReader, "MaxNbActors", maxNbActors );
PxU32 maxNbShapes;
readProperty(inReader, "MaxNbShapes", maxNbShapes);
bool selfCollision;
bool ret = readProperty( inReader, "SelfCollision", selfCollision );
PxAggregate* theAggregate = inArgs.physics.createAggregate(maxNbActors, maxNbShapes, selfCollision);
ret &= readAllProperties( inArgs, inReader, theAggregate, inAllocator, *inCollection );
inReader.pushCurrentContext();
if ( inReader.gotoChild( "Actors" ) )
{
inReader.pushCurrentContext();
for( bool matSuccess = inReader.gotoFirstChild(); matSuccess;
matSuccess = inReader.gotoNextSibling() )
{
const char* actorType = inReader.getCurrentItemName();
if ( 0 == physx::Pxstricmp( actorType, "PxActorRef" ) )
{
PxActor *actor = NULL;
ret &= readReference<PxActor>( inReader, *inCollection, actor );
if(actor)
{
PxScene *currScene = actor->getScene();
if(currScene)
{
currScene->removeActor(*actor);
}
theAggregate->addActor(*actor);
}
}
else if ( 0 == physx::Pxstricmp( actorType, "PxArticulationRef" ) )
{
PxArticulationReducedCoordinate* articulation = NULL;
ret &= readReference<PxArticulationReducedCoordinate>( inReader, *inCollection, articulation );
if(articulation)
{
PxScene *currScene = articulation->getScene();
if(currScene)
{
currScene->removeArticulation(*articulation);
}
theAggregate->addArticulation(*articulation);
}
}
}
inReader.popCurrentContext();
inReader.leaveChild();
}
inReader.popCurrentContext();
return ret ? PxCreateRepXObject(theAggregate) : PxRepXObject();
}
}

View File

@@ -0,0 +1,121 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef SN_REPX_CORE_SERIALIZER_H
#define SN_REPX_CORE_SERIALIZER_H
#include "foundation/PxSimpleTypes.h"
#include "SnRepXSerializerImpl.h"
#if !PX_DOXYGEN
namespace physx
{
#endif
class XmlReader;
class XmlMemoryAllocator;
class XmlWriter;
class MemoryBuffer;
struct PX_DEPRECATED PxMaterialRepXSerializer : RepXSerializerImpl<PxMaterial>
{
PxMaterialRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl<PxMaterial>( inCallback ) {}
virtual PxMaterial* allocateObject( PxRepXInstantiationArgs& );
};
struct PX_DEPRECATED PxShapeRepXSerializer : public RepXSerializerImpl<PxShape>
{
PxShapeRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl<PxShape>( inCallback ) {}
virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* );
virtual PxShape* allocateObject( PxRepXInstantiationArgs& ) { return NULL; }
};
struct PX_DEPRECATED PxBVH33TriangleMeshRepXSerializer : public RepXSerializerImpl<PxBVH33TriangleMesh>
{
PxBVH33TriangleMeshRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl<PxBVH33TriangleMesh>( inCallback ) {}
virtual void objectToFileImpl( const PxBVH33TriangleMesh*, PxCollection*, XmlWriter&, MemoryBuffer&, PxRepXInstantiationArgs& );
virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* );
virtual PxBVH33TriangleMesh* allocateObject( PxRepXInstantiationArgs& ) { return NULL; }
};
struct PX_DEPRECATED PxBVH34TriangleMeshRepXSerializer : public RepXSerializerImpl<PxBVH34TriangleMesh>
{
PxBVH34TriangleMeshRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl<PxBVH34TriangleMesh>( inCallback ) {}
virtual void objectToFileImpl( const PxBVH34TriangleMesh*, PxCollection*, XmlWriter&, MemoryBuffer&, PxRepXInstantiationArgs& );
virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* );
virtual PxBVH34TriangleMesh* allocateObject( PxRepXInstantiationArgs& ) { return NULL; }
};
struct PX_DEPRECATED PxHeightFieldRepXSerializer : public RepXSerializerImpl<PxHeightField>
{
PxHeightFieldRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl<PxHeightField>( inCallback ) {}
virtual void objectToFileImpl( const PxHeightField*, PxCollection*, XmlWriter&, MemoryBuffer&, PxRepXInstantiationArgs& );
virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* );
virtual PxHeightField* allocateObject( PxRepXInstantiationArgs& ) { return NULL; }
};
struct PX_DEPRECATED PxConvexMeshRepXSerializer : public RepXSerializerImpl<PxConvexMesh>
{
PxConvexMeshRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl<PxConvexMesh>( inCallback ) {}
virtual void objectToFileImpl( const PxConvexMesh*, PxCollection*, XmlWriter&, MemoryBuffer&, PxRepXInstantiationArgs& );
virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* );
virtual PxConvexMesh* allocateObject( PxRepXInstantiationArgs& ) { return NULL; }
};
struct PX_DEPRECATED PxRigidStaticRepXSerializer : public RepXSerializerImpl<PxRigidStatic>
{
PxRigidStaticRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl<PxRigidStatic>( inCallback ) {}
virtual PxRigidStatic* allocateObject( PxRepXInstantiationArgs& );
};
struct PX_DEPRECATED PxRigidDynamicRepXSerializer : public RepXSerializerImpl<PxRigidDynamic>
{
PxRigidDynamicRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl<PxRigidDynamic>( inCallback ) {}
virtual PxRigidDynamic* allocateObject( PxRepXInstantiationArgs& );
};
struct PX_DEPRECATED PxArticulationReducedCoordinateRepXSerializer : public RepXSerializerImpl<PxArticulationReducedCoordinate>
{
PxArticulationReducedCoordinateRepXSerializer(PxAllocatorCallback& inCallback) : RepXSerializerImpl<PxArticulationReducedCoordinate>(inCallback) {}
virtual void objectToFileImpl(const PxArticulationReducedCoordinate*, PxCollection*, XmlWriter&, MemoryBuffer&, PxRepXInstantiationArgs&);
virtual PxArticulationReducedCoordinate* allocateObject(PxRepXInstantiationArgs&);
};
struct PX_DEPRECATED PxAggregateRepXSerializer : public RepXSerializerImpl<PxAggregate>
{
PxAggregateRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl<PxAggregate>( inCallback ) {}
virtual void objectToFileImpl( const PxAggregate*, PxCollection*, XmlWriter& , MemoryBuffer&, PxRepXInstantiationArgs& );
virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* );
virtual PxAggregate* allocateObject( PxRepXInstantiationArgs& ) { return NULL; }
};
#if !PX_DOXYGEN
} // namespace physx
#endif
#endif

View File

@@ -0,0 +1,90 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef SN_REPX_SERIALIZER_IMPL_H
#define SN_REPX_SERIALIZER_IMPL_H
#include "foundation/PxUserAllocated.h"
#include "SnXmlVisitorWriter.h"
#include "SnXmlVisitorReader.h"
namespace physx {
using namespace Sn;
/**
* The repx serializer impl takes the raw, untyped repx extension interface
* and implements the simpler functions plus does the reinterpret-casts required
* for any object to implement the serializer safely.
*/
template<typename TLiveType>
struct RepXSerializerImpl : public PxRepXSerializer, PxUserAllocated
{
protected:
RepXSerializerImpl( const RepXSerializerImpl& inOther );
RepXSerializerImpl& operator=( const RepXSerializerImpl& inOther );
public:
PxAllocatorCallback& mAllocator;
RepXSerializerImpl( PxAllocatorCallback& inAllocator )
: mAllocator( inAllocator )
{
}
virtual const char* getTypeName() { return PxTypeInfo<TLiveType>::name(); }
virtual void objectToFile( const PxRepXObject& inLiveObject, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& inArgs )
{
const TLiveType* theObj = reinterpret_cast<const TLiveType*>( inLiveObject.serializable );
objectToFileImpl( theObj, inCollection, inWriter, inTempBuffer, inArgs );
}
virtual PxRepXObject fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection )
{
TLiveType* theObj( allocateObject( inArgs ) );
if ( theObj )
if(fileToObjectImpl( theObj, inReader, inAllocator, inArgs, inCollection ))
return PxCreateRepXObject(theObj);
return PxRepXObject();
}
virtual void objectToFileImpl( const TLiveType* inObj, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& /*inArgs*/)
{
writeAllProperties( inObj, inWriter, inTempBuffer, *inCollection );
}
virtual bool fileToObjectImpl( TLiveType* inObj, XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection )
{
return readAllProperties( inArgs, inReader, inObj, inAllocator, *inCollection );
}
virtual TLiveType* allocateObject( PxRepXInstantiationArgs& inArgs ) = 0;
};
}
#endif

View File

@@ -0,0 +1,462 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "foundation/PxMemory.h"
#include "SnXmlImpl.h"
#include "SnXmlReader.h"
#include "SnXmlMemoryAllocator.h"
#include "SnRepXCollection.h"
#include "SnRepXUpgrader.h"
using namespace physx::profile;
namespace physx { namespace Sn {
#define DEFINE_REPX_DEFAULT_PROPERTY( name, val ) RepXDefaultEntry( name, val ),
static RepXDefaultEntry gRepX1_0Defaults[] = {
#include "SnRepX1_0Defaults.h"
};
static PxU32 gNumRepX1_0Default = sizeof( gRepX1_0Defaults ) / sizeof ( *gRepX1_0Defaults );
static RepXDefaultEntry gRepX3_1Defaults[] = {
#include "SnRepX3_1Defaults.h"
};
static PxU32 gNumRepX3_1Defaults = sizeof( gRepX3_1Defaults ) / sizeof ( *gRepX3_1Defaults );
static RepXDefaultEntry gRepX3_2Defaults[] = {
#include "SnRepX3_2Defaults.h"
};
static PxU32 gNumRepX3_2Defaults = sizeof( gRepX3_2Defaults ) / sizeof ( *gRepX3_2Defaults );
inline const char* nextPeriod( const char* str )
{
for( ++str; str && *str && *str != '.'; ++str ); //empty loop intentional
return str;
}
inline bool safeStrEq(const char* lhs, const char* rhs)
{
if (lhs == rhs)
return true;
//If they aren't equal, and one of them is null,
//then they can't be equal.
//This is assuming that the null char* is not equal to
//the empty "" char*.
if (!lhs || !rhs)
return false;
return ::strcmp(lhs, rhs) == 0;
}
typedef PxProfileHashMap<const char*, PxU32> TNameOffsetMap;
void setMissingPropertiesToDefault( XmlNode* topNode, XmlReaderWriter& editor, const RepXDefaultEntry* defaults, PxU32 numDefaults, TNameOffsetMap& map )
{
for ( XmlNode* child = topNode->mFirstChild; child != NULL; child = child->mNextSibling )
setMissingPropertiesToDefault( child, editor, defaults, numDefaults, map );
const TNameOffsetMap::Entry* entry( map.find( topNode->mName ) );
if ( entry )
{
XmlReaderWriter& theReader( editor );
theReader.setNode( *topNode );
char nameBuffer[512] = {0};
size_t nameLen = strlen( topNode->mName );
//For each default property entry for this node type.
for ( const RepXDefaultEntry* item = defaults + entry->second; Pxstrncmp( item->name, topNode->mName, nameLen ) == 0; ++item )
{
bool childAdded = false;
const char* nameStart = item->name + nameLen;
++nameStart;
theReader.pushCurrentContext();
const char* str = nameStart;
while( *str )
{
const char *period = nextPeriod( str );
size_t len = size_t(PxMin( period - str, ptrdiff_t(1023) )); //can't be too careful these days.
PxMemCopy( nameBuffer, str, PxU32(len) );
nameBuffer[len] = 0;
if ( theReader.gotoChild( nameBuffer ) == false )
{
childAdded = true;
theReader.addOrGotoChild( nameBuffer );
}
if (*period )
str = period + 1;
else
str = period;
}
if ( childAdded )
theReader.setCurrentItemValue( item->value );
theReader.popCurrentContext();
}
}
}
static void setMissingPropertiesToDefault( RepXCollection& collection, XmlReaderWriter& editor, const RepXDefaultEntry* defaults, PxU32 numDefaults )
{
PxProfileAllocatorWrapper wrapper( collection.getAllocator() );
//Release all strings at once, instead of piece by piece
XmlMemoryAllocatorImpl alloc( collection.getAllocator() );
//build a hashtable of the initial default value strings.
TNameOffsetMap nameOffsets( wrapper );
for ( PxU32 idx = 0; idx < numDefaults; ++idx )
{
const RepXDefaultEntry& item( defaults[idx] );
size_t nameLen = 0;
const char* periodPtr = nextPeriod (item.name);
for ( ; periodPtr && *periodPtr; ++periodPtr ) if( *periodPtr == '.' ) break;
if ( periodPtr == NULL || *periodPtr != '.' ) continue;
nameLen = size_t(periodPtr - item.name);
char* newMem = reinterpret_cast<char*>(alloc.allocate( PxU32(nameLen + 1) ));
PxMemCopy( newMem, item.name, PxU32(nameLen) );
newMem[nameLen] = 0;
if ( nameOffsets.find( newMem ) )
alloc.deallocate( reinterpret_cast<PxU8*>(newMem) );
else
nameOffsets.insert( newMem, idx );
}
//Run through each collection item, and recursively find it and its children
//If an object's name is in the hash map, check and add any properties that don't exist.
//else return.
for ( const RepXCollectionItem* item = collection.begin(), *end = collection.end(); item != end; ++ item )
{
RepXCollectionItem theItem( *item );
setMissingPropertiesToDefault( theItem.descriptor, editor, defaults, numDefaults, nameOffsets );
}
}
struct RecursiveTraversal
{
RecursiveTraversal(XmlReaderWriter& editor): mEditor(editor) {}
void traverse()
{
mEditor.pushCurrentContext();
updateNode();
for(bool exists = mEditor.gotoFirstChild(); exists; exists = mEditor.gotoNextSibling())
traverse();
mEditor.popCurrentContext();
}
virtual void updateNode() = 0;
virtual ~RecursiveTraversal() {}
XmlReaderWriter& mEditor;
protected:
RecursiveTraversal& operator=(const RecursiveTraversal&){return *this;}
};
RepXCollection& RepXUpgrader::upgrade10CollectionTo3_1Collection(RepXCollection& src)
{
XmlReaderWriter& editor( src.createNodeEditor() );
setMissingPropertiesToDefault(src, editor, gRepX1_0Defaults, gNumRepX1_0Default );
RepXCollection* dest = &src.createCollection("3.1.1");
for ( const RepXCollectionItem* item = src.begin(), *end = src.end(); item != end; ++ item )
{
//either src or dest could do the copy operation, it doesn't matter who does it.
RepXCollectionItem newItem( item->liveObject, src.copyRepXNode( item->descriptor ) );
editor.setNode( *const_cast<XmlNode*>( newItem.descriptor ) );
//Some old files have this name in their system.
editor.renameProperty( "MassSpaceInertia", "MassSpaceInertiaTensor" );
editor.renameProperty( "SleepEnergyThreshold", "SleepThreshold" );
if ( strstr( newItem.liveObject.typeName, "Joint" ) || strstr( newItem.liveObject.typeName, "joint" ) )
{
//Joints changed format a bit. old joints looked like:
/*
<Actor0 >1627536</Actor0>
<Actor1 >1628368</Actor1>
<LocalPose0 >0 0 0 1 0.5 0.5 0.5</LocalPose0>
<LocalPose1 >0 0 0 1 0.3 0.3 0.3</LocalPose1>*/
//New joints look like:
/*
<Actors >
<actor0 >58320336</actor0>
<actor1 >56353568</actor1>
</Actors>
<LocalPose >
<eACTOR0 >0 0 0 1 0.5 0.5 0.5</eACTOR0>
<eACTOR1 >0 0 0 1 0.3 0.3 0.3</eACTOR1>
</LocalPose>
*/
const char* actor0, *actor1, *lp0, *lp1;
editor.readAndRemoveProperty( "Actor0", actor0 );
editor.readAndRemoveProperty( "Actor1", actor1 );
editor.readAndRemoveProperty( "LocalPose0", lp0 );
editor.readAndRemoveProperty( "LocalPose1", lp1 );
editor.addOrGotoChild( "Actors" );
editor.writePropertyIfNotEmpty( "actor0", actor0 );
editor.writePropertyIfNotEmpty( "actor1", actor1 );
editor.leaveChild();
editor.addOrGotoChild( "LocalPose" );
editor.writePropertyIfNotEmpty( "eACTOR0", lp0 );
editor.writePropertyIfNotEmpty( "eACTOR1", lp1 );
editor.leaveChild();
}
//now desc owns the new node. Collections share a single allocation pool, however,
//which will get destroyed when all the collections referencing it are destroyed themselves.
//Data on nodes is shared between nodes, but the node structure itself is allocated.
dest->addCollectionItem( newItem );
}
editor.release();
src.destroy();
return *dest;
}
RepXCollection& RepXUpgrader::upgrade3_1CollectionTo3_2Collection(RepXCollection& src)
{
XmlReaderWriter& editor( src.createNodeEditor() );
setMissingPropertiesToDefault(src, editor, gRepX3_1Defaults, gNumRepX3_1Defaults );
RepXCollection* dest = &src.createCollection("3.2.0");
for ( const RepXCollectionItem* item = src.begin(), *end = src.end(); item != end; ++ item )
{
//either src or dest could do the copy operation, it doesn't matter who does it.
RepXCollectionItem newItem( item->liveObject, src.copyRepXNode( item->descriptor ) );
editor.setNode( *const_cast<XmlNode*>( newItem.descriptor ) );
if ( strstr( newItem.liveObject.typeName, "PxMaterial" ) )
{
editor.removeChild( "DynamicFrictionV" );
editor.removeChild( "StaticFrictionV" );
editor.removeChild( "dirOfAnisotropy" );
}
//now desc owns the new node. Collections share a single allocation pool, however,
//which will get destroyed when all the collections referencing it are destroyed themselves.
//Data on nodes is shared between nodes, but the node structure itself is allocated.
dest->addCollectionItem( newItem );
}
editor.release();
src.destroy();
return *dest;
}
RepXCollection& RepXUpgrader::upgrade3_2CollectionTo3_3Collection(RepXCollection& src)
{
XmlReaderWriter& editor( src.createNodeEditor() );
setMissingPropertiesToDefault(src, editor, gRepX3_2Defaults, gNumRepX3_2Defaults );
RepXCollection* dest = &src.createCollection("3.3.0");
struct RenameSpringToStiffness : public RecursiveTraversal
{
RenameSpringToStiffness(XmlReaderWriter& editor_): RecursiveTraversal(editor_) {}
void updateNode()
{
mEditor.renameProperty("Spring", "Stiffness");
mEditor.renameProperty("TangentialSpring", "TangentialStiffness");
}
};
struct UpdateArticulationSwingLimit : public RecursiveTraversal
{
UpdateArticulationSwingLimit(XmlReaderWriter& editor_): RecursiveTraversal(editor_) {}
void updateNode()
{
if(!Pxstricmp(mEditor.getCurrentItemName(), "yLimit") && !Pxstricmp(mEditor.getCurrentItemValue(), "0"))
mEditor.setCurrentItemValue("0.785398");
if(!Pxstricmp(mEditor.getCurrentItemName(), "zLimit") && !Pxstricmp(mEditor.getCurrentItemValue(), "0"))
mEditor.setCurrentItemValue("0.785398");
if(!Pxstricmp(mEditor.getCurrentItemName(), "TwistLimit"))
{
mEditor.gotoFirstChild();
PxReal lower = PxReal(strtod(mEditor.getCurrentItemValue(), NULL));
mEditor.gotoNextSibling();
PxReal upper = PxReal(strtod(mEditor.getCurrentItemValue(), NULL));
mEditor.leaveChild();
if(lower>=upper)
{
mEditor.writePropertyIfNotEmpty("lower", "-0.785398");
mEditor.writePropertyIfNotEmpty("upper", "0.785398");
}
}
}
};
for ( const RepXCollectionItem* item = src.begin(), *end = src.end(); item != end; ++ item )
{
//either src or dest could do the copy operation, it doesn't matter who does it.
RepXCollectionItem newItem( item->liveObject, src.copyRepXNode( item->descriptor ) );
if ( strstr( newItem.liveObject.typeName, "PxCloth" ) || strstr( newItem.liveObject.typeName, "PxClothFabric" ) )
{
PxGetFoundation().error(PxErrorCode::eDEBUG_WARNING, PX_FL, "Didn't suppot PxCloth upgrate from 3.2 to 3.3! ");
continue;
}
if ( strstr( newItem.liveObject.typeName, "PxParticleSystem" ) || strstr( newItem.liveObject.typeName, "PxParticleFluid" ) )
{
editor.setNode( *const_cast<XmlNode*>( newItem.descriptor ) );
editor.renameProperty( "PositionBuffer", "Positions" );
editor.renameProperty( "VelocityBuffer", "Velocities" );
editor.renameProperty( "RestOffsetBuffer", "RestOffsets" );
}
if(strstr(newItem.liveObject.typeName, "PxPrismaticJoint" )
|| strstr(newItem.liveObject.typeName, "PxRevoluteJoint")
|| strstr(newItem.liveObject.typeName, "PxSphericalJoint")
|| strstr(newItem.liveObject.typeName, "PxD6Joint")
|| strstr(newItem.liveObject.typeName, "PxArticulation"))
{
editor.setNode( *const_cast<XmlNode*>( newItem.descriptor ) );
RenameSpringToStiffness(editor).traverse();
}
if(strstr(newItem.liveObject.typeName, "PxArticulation"))
{
editor.setNode( *const_cast<XmlNode*>( newItem.descriptor ) );
UpdateArticulationSwingLimit(editor).traverse();
}
//now dest owns the new node. Collections share a single allocation pool, however,
//which will get destroyed when all the collections referencing it are destroyed themselves.
//Data on nodes is shared between nodes, but the node structure itself is allocated.
dest->addCollectionItem( newItem );
}
editor.release();
src.destroy();
return *dest;
}
RepXCollection& RepXUpgrader::upgrade3_3CollectionTo3_4Collection(RepXCollection& src)
{
RepXCollection* dest = &src.createCollection("3.4.0");
for ( const RepXCollectionItem* item = src.begin(), *end = src.end(); item != end; ++ item )
{
if(strstr(item->liveObject.typeName, "PxTriangleMesh"))
{
PxRepXObject newMeshRepXObj("PxBVH33TriangleMesh", item->liveObject.serializable, item->liveObject.id);
XmlNode* newMeshNode = src.copyRepXNode( item->descriptor );
newMeshNode->mName = "PxBVH33TriangleMesh";
RepXCollectionItem newMeshItem(newMeshRepXObj, newMeshNode);
dest->addCollectionItem( newMeshItem );
continue;
}
RepXCollectionItem newItem( item->liveObject, src.copyRepXNode( item->descriptor ) );
dest->addCollectionItem( newItem );
}
src.destroy();
return *dest;
}
RepXCollection& RepXUpgrader::upgrade3_4CollectionTo4_0Collection(RepXCollection& src)
{
RepXCollection* dest = &src.createCollection("4.0.0");
for (const RepXCollectionItem* item = src.begin(), *end = src.end(); item != end; ++item)
{
if (strstr(item->liveObject.typeName, "PxParticleFluid") ||
strstr(item->liveObject.typeName, "PxParticleSystem") ||
strstr(item->liveObject.typeName, "PxClothFabric") ||
strstr(item->liveObject.typeName, "PxCloth"))
{
continue;
}
RepXCollectionItem newItem(item->liveObject, src.copyRepXNode(item->descriptor));
dest->addCollectionItem(newItem);
}
src.destroy();
return *dest;
}
RepXCollection& RepXUpgrader::upgradeCollection(RepXCollection& src)
{
const char* srcVersion = src.getVersion();
if( safeStrEq( srcVersion, RepXCollection::getLatestVersion() ))
return src;
typedef RepXCollection& (*UPGRADE_FUNCTION)(RepXCollection& src);
struct Upgrade { const char* versionString; UPGRADE_FUNCTION upgradeFunction; };
static const Upgrade upgradeTable[] =
{
{ "1.0", upgrade10CollectionTo3_1Collection },
{ "3.1", NULL },
{ "3.1.1", upgrade3_1CollectionTo3_2Collection },
{ "3.2.0", upgrade3_2CollectionTo3_3Collection },
{ "3.3.0", NULL },
{ "3.3.1", NULL },
{ "3.3.2", NULL },
{ "3.3.3", NULL },
{ "3.3.4", upgrade3_3CollectionTo3_4Collection },
{ "3.4.0", NULL },
{ "3.4.1", NULL },
{ "3.4.2", upgrade3_4CollectionTo4_0Collection }
}; //increasing order and complete
const PxU32 upgradeTableSize = sizeof(upgradeTable)/sizeof(upgradeTable[0]);
PxU32 repxVersion = UINT16_MAX;
for (PxU32 i=0; i<upgradeTableSize; i++)
{
if( safeStrEq( srcVersion, upgradeTable[i].versionString ))
{
repxVersion = i;
break;
}
}
RepXCollection* dest = &src;
for( PxU32 j = repxVersion; j < upgradeTableSize; j++ )
{
if( upgradeTable[j].upgradeFunction )
dest = &(upgradeTable[j].upgradeFunction)(*dest);
}
return *dest;
}
} }

View File

@@ -0,0 +1,54 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef SN_REPX_UPGRADER_H
#define SN_REPX_UPGRADER_H
#include "foundation/PxSimpleTypes.h"
namespace physx { namespace Sn {
class RepXCollection;
class RepXUpgrader
{
public:
//If a new collection is created, the source collection is destroyed.
//Thus you only need to release the new collection.
//This holds for all of the upgrade functions.
//So be aware, that the argument to these functions may not be valid
//after they are called, but the return value always will be valid.
static RepXCollection& upgradeCollection( RepXCollection& src );
static RepXCollection& upgrade10CollectionTo3_1Collection( RepXCollection& src );
static RepXCollection& upgrade3_1CollectionTo3_2Collection( RepXCollection& src );
static RepXCollection& upgrade3_2CollectionTo3_3Collection( RepXCollection& src );
static RepXCollection& upgrade3_3CollectionTo3_4Collection( RepXCollection& src );
static RepXCollection& upgrade3_4CollectionTo4_0Collection( RepXCollection& src );
};
} }
#endif

View File

@@ -0,0 +1,256 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef SN_SIMPLE_XML_WRITER_H
#define SN_SIMPLE_XML_WRITER_H
#include "foundation/PxArray.h"
#include "SnXmlMemoryPoolStreams.h"
namespace physx { namespace Sn {
class SimpleXmlWriter
{
public:
struct STagWatcher
{
typedef SimpleXmlWriter TXmlWriterType;
TXmlWriterType& mWriter;
STagWatcher( const STagWatcher& inOther );
STagWatcher& operator-( const STagWatcher& inOther );
STagWatcher( TXmlWriterType& inWriter, const char* inTagName )
: mWriter( inWriter )
{
mWriter.beginTag( inTagName );
}
~STagWatcher() { mWriter.endTag(); }
protected:
STagWatcher& operator=(const STagWatcher&);
};
virtual ~SimpleXmlWriter(){}
virtual void beginTag( const char* inTagname ) = 0;
virtual void endTag() = 0;
virtual void addAttribute( const char* inName, const char* inValue ) = 0;
virtual void writeContentTag( const char* inTag, const char* inContent ) = 0;
virtual void addContent( const char* inContent ) = 0;
virtual PxU32 tabCount() = 0;
private:
SimpleXmlWriter& operator=(const SimpleXmlWriter&);
};
template<typename TStreamType>
class SimpleXmlWriterImpl : public SimpleXmlWriter
{
PxProfileAllocatorWrapper mWrapper;
TStreamType& mStream;
SimpleXmlWriterImpl( const SimpleXmlWriterImpl& inOther );
SimpleXmlWriterImpl& operator=( const SimpleXmlWriterImpl& inOther );
PxProfileArray<const char*> mTags;
bool mTagOpen;
PxU32 mInitialTagDepth;
public:
SimpleXmlWriterImpl( TStreamType& inStream, PxAllocatorCallback& inAllocator, PxU32 inInitialTagDepth = 0 )
: mWrapper( inAllocator )
, mStream( inStream )
, mTags( mWrapper )
, mTagOpen( false )
, mInitialTagDepth( inInitialTagDepth )
{
}
virtual ~SimpleXmlWriterImpl()
{
while( mTags.size() )
endTag();
}
PxU32 tabCount() { return mTags.size() + mInitialTagDepth; }
void writeTabs( PxU32 inSize )
{
inSize += mInitialTagDepth;
for ( PxU32 idx =0; idx < inSize; ++idx )
mStream << "\t";
}
void beginTag( const char* inTagname )
{
closeTag();
writeTabs(mTags.size());
mTags.pushBack( inTagname );
mStream << "<" << inTagname;
mTagOpen = true;
}
void addAttribute( const char* inName, const char* inValue )
{
PX_ASSERT( mTagOpen );
mStream << " " << inName << "=" << "\"" << inValue << "\"";
}
void closeTag(bool useNewline = true)
{
if ( mTagOpen )
{
mStream << " " << ">";
if (useNewline )
mStream << "\n";
}
mTagOpen = false;
}
void doEndOpenTag()
{
mStream << "</" << mTags.back() << ">" << "\n";
}
void endTag()
{
PX_ASSERT( mTags.size() );
if ( mTagOpen )
mStream << " " << "/>" << "\n";
else
{
writeTabs(mTags.size()-1);
doEndOpenTag();
}
mTagOpen = false;
mTags.popBack();
}
static bool IsNormalizableWhitespace(char c) { return c == 0x9 || c == 0xA || c == 0xD; }
static bool IsValidXmlCharacter(char c) { return IsNormalizableWhitespace(c) || c >= 0x20; }
void addContent( const char* inContent )
{
closeTag(false);
//escape xml
for( ; *inContent; inContent++ )
{
switch (*inContent)
{
case '<':
mStream << "&lt;";
break;
case '>':
mStream << "&gt;";
break;
case '&':
mStream << "&amp;";
break;
case '\'':
mStream << "&apos;";
break;
case '"':
mStream << "&quot;";
break;
default:
if (IsValidXmlCharacter(*inContent)) {
if (IsNormalizableWhitespace(*inContent))
{
char s[32];
Pxsnprintf(s, 32, "&#x%02X;", unsigned(*inContent));
mStream << s;
}
else
mStream << *inContent;
}
break;
}
}
}
void writeContentTag( const char* inTag, const char* inContent )
{
beginTag( inTag );
addContent( inContent );
doEndOpenTag();
mTags.popBack();
}
void insertXml( const char* inXml )
{
closeTag();
mStream << inXml;
}
};
struct BeginTag
{
const char* mTagName;
BeginTag( const char* inTagName )
: mTagName( inTagName ) { }
};
struct EndTag
{
EndTag() {}
};
struct Att
{
const char* mAttName;
const char* mAttValue;
Att( const char* inAttName, const char* inAttValue )
: mAttName( inAttName )
, mAttValue( inAttValue ) { }
};
struct Content
{
const char* mContent;
Content( const char* inContent )
: mContent( inContent ) { }
};
struct ContentTag
{
const char* mTagName;
const char* mContent;
ContentTag( const char* inTagName, const char* inContent )
: mTagName( inTagName )
, mContent( inContent ) { }
};
inline SimpleXmlWriter& operator<<( SimpleXmlWriter& inWriter, const BeginTag& inTag ) { inWriter.beginTag( inTag.mTagName ); return inWriter; }
inline SimpleXmlWriter& operator<<( SimpleXmlWriter& inWriter, const EndTag& inTag ) { PX_UNUSED(inTag); inWriter.endTag(); return inWriter; }
inline SimpleXmlWriter& operator<<( SimpleXmlWriter& inWriter, const Att& inTag ) { inWriter.addAttribute(inTag.mAttName, inTag.mAttValue); return inWriter; }
inline SimpleXmlWriter& operator<<( SimpleXmlWriter& inWriter, const Content& inTag ) { inWriter.addContent(inTag.mContent); return inWriter; }
inline SimpleXmlWriter& operator<<( SimpleXmlWriter& inWriter, const ContentTag& inTag ) { inWriter.writeContentTag(inTag.mTagName, inTag.mContent); return inWriter; }
inline void writeProperty( SimpleXmlWriter& inWriter, MemoryBuffer& tempBuffer, const char* inPropName )
{
PxU8 data = 0;
tempBuffer.write( &data, sizeof(PxU8) );
inWriter.writeContentTag( inPropName, reinterpret_cast<const char*>( tempBuffer.mBuffer ) );
tempBuffer.clear();
}
template<typename TDataType>
inline void writeProperty( SimpleXmlWriter& inWriter, MemoryBuffer& tempBuffer, const char* inPropName, TDataType inValue )
{
tempBuffer << inValue;
writeProperty( inWriter, tempBuffer, inPropName );
}
} }
#endif

View File

@@ -0,0 +1,193 @@
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 THE COPYRIGHT OWNER OR
// CONTRIBUTORS 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.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#ifndef SN_XML_DESERIALIZER_H
#define SN_XML_DESERIALIZER_H
#include "SnXmlVisitorReader.h"
namespace physx { namespace Sn {
//Definitions needed internally in the Serializer headers.
template<typename TTriIndexElem>
struct Triangle
{
TTriIndexElem mIdx0;
TTriIndexElem mIdx1;
TTriIndexElem mIdx2;
Triangle( TTriIndexElem inIdx0 = 0, TTriIndexElem inIdx1 = 0, TTriIndexElem inIdx2 = 0)
: mIdx0( inIdx0 )
, mIdx1( inIdx1 )
, mIdx2( inIdx2 )
{
}
};
struct XmlMemoryAllocateMemoryPoolAllocator
{
XmlMemoryAllocator* mAllocator;
XmlMemoryAllocateMemoryPoolAllocator( XmlMemoryAllocator* inAlloc ) : mAllocator( inAlloc ) {}
PxU8* allocate( PxU32 inSize ) { return mAllocator->allocate( inSize ); }
void deallocate( PxU8* inMem ) { mAllocator->deallocate( inMem ); }
};
inline bool isEmpty(const char *s)
{
while (*s != '\0')
{
if (!isspace(*s))
return false;
s++;
}
return true;
}
inline void strtoLong( Triangle<PxU32>& ioDatatype,const char*& ioData )
{
strto( ioDatatype.mIdx0, ioData );
strto( ioDatatype.mIdx1, ioData );
strto( ioDatatype.mIdx2, ioData );
}
inline void strtoLong( PxHeightFieldSample& ioDatatype,const char*& ioData )
{
PxU32 tempData;
strto( tempData, ioData );
if ( isBigEndian() )
{
PxU32& theItem(tempData);
PxU32 theDest = 0;
PxU8* theReadPtr( reinterpret_cast< PxU8* >( &theItem ) );
PxU8* theWritePtr( reinterpret_cast< PxU8* >( &theDest ) );
//A height field sample is a 16 bit number
//followed by two bytes.
//We write this out as a 32 bit integer, LE.
//Thus, on a big endian, we need to move the bytes
//around a bit.
//LE - 1 2 3 4
//BE - 4 3 2 1 - after convert from xml number
//Correct BE - 2 1 3 4, just like LE but with the 16 number swapped
theWritePtr[0] = theReadPtr[2];
theWritePtr[1] = theReadPtr[3];
theWritePtr[2] = theReadPtr[1];
theWritePtr[3] = theReadPtr[0];
theItem = theDest;
}
ioDatatype = *reinterpret_cast<PxHeightFieldSample*>( &tempData );
}
template<typename TDataType>
inline void readStridedFlagsProperty( XmlReader& ioReader, const char* inPropName, TDataType*& outData, PxU32& outStride, PxU32& outCount, XmlMemoryAllocator& inAllocator,
const PxU32ToName* inConversions)
{
const char* theSrcData;
outStride = sizeof( TDataType );
outData = NULL;
outCount = 0;
if ( ioReader.read( inPropName, theSrcData ) )
{
XmlMemoryAllocateMemoryPoolAllocator tempAllocator( &inAllocator );
MemoryBufferBase<XmlMemoryAllocateMemoryPoolAllocator> tempBuffer( &tempAllocator );
if ( theSrcData )
{
char* theStartData = const_cast< char*>( copyStr( &tempAllocator, theSrcData ) );
char* aData = strtok(theStartData, " \n");
while( aData )
{
TDataType tempValue;
stringToFlagsType( aData, inAllocator, tempValue, inConversions );
aData = strtok(NULL," \n");
tempBuffer.write( &tempValue, sizeof(TDataType) );
}
outData = reinterpret_cast< TDataType* >( tempBuffer.mBuffer );
outCount = tempBuffer.mWriteOffset / sizeof( TDataType );
tempAllocator.deallocate( reinterpret_cast<PxU8*>(theStartData) );
}
tempBuffer.releaseBuffer();
}
}
template<typename TDataType>
inline void readStridedBufferProperty( XmlReader& ioReader, const char* inPropName, TDataType*& outData, PxU32& outStride, PxU32& outCount, XmlMemoryAllocator& inAllocator)
{
const char* theSrcData;
outStride = sizeof( TDataType );
outData = NULL;
outCount = 0;
if ( ioReader.read( inPropName, theSrcData ) )
{
XmlMemoryAllocateMemoryPoolAllocator tempAllocator( &inAllocator );
MemoryBufferBase<XmlMemoryAllocateMemoryPoolAllocator> tempBuffer( &tempAllocator );
if ( theSrcData )
{
char* theStartData = const_cast< char*>( copyStr( &tempAllocator, theSrcData ) );
const char* theData = theStartData;
while( !isEmpty(theData) )
{
//These buffers are whitespace delimited.
TDataType theType;
strtoLong( theType, theData );
tempBuffer.write( &theType, sizeof(theType) );
}
outData = reinterpret_cast< TDataType* >( tempBuffer.mBuffer );
outCount = tempBuffer.mWriteOffset / sizeof( TDataType );
tempAllocator.deallocate( reinterpret_cast<PxU8*>(theStartData) );
}
tempBuffer.releaseBuffer();
}
}
template<typename TDataType>
inline void readStridedBufferProperty( XmlReader& ioReader, const char* inPropName, PxStridedData& ioData, PxU32& outCount, XmlMemoryAllocator& inAllocator)
{
TDataType* tempData = NULL;
readStridedBufferProperty<TDataType>( ioReader, inPropName, tempData, ioData.stride, outCount, inAllocator );
ioData.data = tempData;
}
template<typename TDataType>
inline void readStridedBufferProperty( XmlReader& ioReader, const char* inPropName, PxTypedBoundedData<const TDataType>& ioData, PxU32& outCount, XmlMemoryAllocator& inAllocator)
{
TDataType* tempData = NULL;
readStridedBufferProperty<TDataType>( ioReader, inPropName, tempData, ioData.stride, outCount, inAllocator );
ioData.data = reinterpret_cast<PxMaterialTableIndex*>( tempData );
}
template<typename TDataType>
inline void readStridedBufferProperty( XmlReader& ioReader, const char* inPropName, PxBoundedData& ioData, XmlMemoryAllocator& inAllocator)
{
return readStridedBufferProperty<TDataType>( ioReader, inPropName, ioData, ioData.count, inAllocator );
}
} }
#endif

Some files were not shown because too many files have changed in this diff Show More