// // 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-2021 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. // **************************************************************************** // This snippet demonstrates the use of immediate articulations. // **************************************************************************** #include "PxPhysicsAPI.h" #include "PxImmediateMode.h" #include "PsArray.h" #include "PsHashSet.h" #include "PsHashMap.h" #include "ExtConstraintHelper.h" #include "../snippetutils/SnippetUtils.h" #include "../snippetcommon/SnippetPrint.h" #include "../snippetcommon/SnippetPVD.h" #define USE_TGS 0 //#define PRINT_TIMINGS #define TEST_IMMEDIATE_JOINTS //Enables whether we want persistent state caching (contact cache, friction caching) or not. Avoiding persistency results in one-shot collision detection and zero friction //correlation but simplifies code by not longer needing to cache persistent pairs. #define WITH_PERSISTENCY 1 //Toggles whether we batch constraints or not. Constraint batching is an optional process which can improve performance by grouping together independent constraints. These independent constraints //can be solved in parallel by using multiple lanes of SIMD registers. #define BATCH_CONTACTS 1 using namespace physx; using namespace shdfnd; using namespace immediate; static const PxVec3 gGravity(0.0f, -9.81f, 0.0f); static const float gContactDistance = 0.1f; static const float gMeshContactMargin = 0.01f; static const float gToleranceLength = 1.0f; static const float gBounceThreshold = -2.0f; static const float gFrictionOffsetThreshold = 0.04f; static const float gCorrelationDistance = 0.025f; static const float gBoundsInflation = 0.02f; static const float gStaticFriction = 0.5f; static const float gDynamicFriction = 0.5f; static const float gRestitution = 0.0f; static const float gMaxDepenetrationVelocity = 10.0f; static const float gMaxContactImpulse = FLT_MAX; static const float gLinearDamping = 0.1f; static const float gAngularDamping = 0.05f; static const float gMaxLinearVelocity = 100.0f; static const float gMaxAngularVelocity = 100.0f; static const float gJointFrictionCoefficient = 0.05f; static const PxU32 gNbIterPos = 4; static const PxU32 gNbIterVel = 1; static bool gPause = false; static bool gOneFrame = false; static bool gDrawBounds = false; static PxU32 gSceneIndex = 2; #if WITH_PERSISTENCY struct PersistentContactPair { PersistentContactPair() { reset(); } PxCache cache; PxU8* frictions; PxU32 nbFrictions; PX_FORCE_INLINE void reset() { cache = PxCache(); frictions = NULL; nbFrictions = 0; } }; #endif class BlockBasedAllocator { struct AllocationPage { static const PxU32 PageSize = 32 * 1024; PxU8 mPage[PageSize]; PxU32 currentIndex; AllocationPage() : currentIndex(0){} PxU8* allocate(const PxU32 size) { PxU32 alignedSize = (size + 15)&(~15); if ((currentIndex + alignedSize) < PageSize) { PxU8* ret = &mPage[currentIndex]; currentIndex += alignedSize; return ret; } return NULL; } }; AllocationPage* currentPage; Array mAllocatedBlocks; PxU32 mCurrentIndex; public: BlockBasedAllocator() : currentPage(NULL), mCurrentIndex(0) { } virtual PxU8* allocate(const PxU32 byteSize) { if (currentPage) { PxU8* data = currentPage->allocate(byteSize); if (data) return data; } if (mCurrentIndex < mAllocatedBlocks.size()) { currentPage = mAllocatedBlocks[mCurrentIndex++]; currentPage->currentIndex = 0; return currentPage->allocate(byteSize); } currentPage = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(AllocationPage), PX_DEBUG_EXP("AllocationPage")), AllocationPage)(); mAllocatedBlocks.pushBack(currentPage); mCurrentIndex = mAllocatedBlocks.size(); return currentPage->allocate(byteSize); } void release() { for (PxU32 a = 0; a < mAllocatedBlocks.size(); ++a) PX_FREE(mAllocatedBlocks[a]); mAllocatedBlocks.clear(); currentPage = NULL; mCurrentIndex = 0; } void reset() { currentPage = NULL; mCurrentIndex = 0; } virtual ~BlockBasedAllocator() { release(); } }; class TestCacheAllocator : public PxCacheAllocator { BlockBasedAllocator mAllocator[2]; PxU32 currIdx; public: TestCacheAllocator() : currIdx(0) { } virtual PxU8* allocateCacheData(const PxU32 byteSize) { return mAllocator[currIdx].allocate(byteSize); } void release() { currIdx = 1 - currIdx; mAllocator[currIdx].release(); } void reset() { currIdx = 1 - currIdx; mAllocator[currIdx].reset(); } virtual ~TestCacheAllocator(){} }; class TestConstraintAllocator : public PxConstraintAllocator { BlockBasedAllocator mConstraintAllocator; BlockBasedAllocator mFrictionAllocator[2]; PxU32 currIdx; public: TestConstraintAllocator() : currIdx(0) { } virtual PxU8* reserveConstraintData(const PxU32 byteSize){ return mConstraintAllocator.allocate(byteSize); } virtual PxU8* reserveFrictionData(const PxU32 byteSize){ return mFrictionAllocator[currIdx].allocate(byteSize); } void release() { currIdx = 1 - currIdx; mConstraintAllocator.release(); mFrictionAllocator[currIdx].release(); } virtual ~TestConstraintAllocator() {} }; struct IDS { PX_FORCE_INLINE IDS(PxU32 id0, PxU32 id1) : mID0(id0), mID1(id1) {} PxU32 mID0; PxU32 mID1; PX_FORCE_INLINE bool operator == (const IDS& other) const { return mID0 == other.mID0 && mID1 == other.mID1; } }; PX_FORCE_INLINE uint32_t hash(const IDS& p) { return shdfnd::hash(uint64_t(p.mID0)|(uint64_t(p.mID1)<<32)); } struct MassProps { PxVec3 mInvInertia; float mInvMass; }; static void computeMassProps(MassProps& props, const PxGeometry& geometry, float mass) { if(mass!=0.0f) { PxMassProperties inertia(geometry); inertia = inertia * (mass/inertia.mass); PxQuat orient; const PxVec3 diagInertia = PxMassProperties::getMassSpaceInertia(inertia.inertiaTensor, orient); props.mInvMass = 1.0f/inertia.mass; props.mInvInertia.x = diagInertia.x == 0.0f ? 0.0f : 1.0f/diagInertia.x; props.mInvInertia.y = diagInertia.y == 0.0f ? 0.0f : 1.0f/diagInertia.y; props.mInvInertia.z = diagInertia.z == 0.0f ? 0.0f : 1.0f/diagInertia.z; } else { props.mInvMass = 0.0f; props.mInvInertia = PxVec3(0.0f); } } #ifdef TEST_IMMEDIATE_JOINTS struct MyJointData : Ext::JointData { PxU32 mActors[2]; PxTransform mLocalFrames[2]; void initInvMassScale() { invMassScale.linear0 = 1.0f; invMassScale.angular0 = 1.0f; invMassScale.linear1 = 1.0f; invMassScale.angular1 = 1.0f; } }; #endif class ImmediateScene { PX_NOCOPY(ImmediateScene) public: ImmediateScene(); ~ImmediateScene(); void reset(); PxU32 createActor(const PxGeometry& geometry, const PxTransform& pose, const MassProps* massProps=NULL, Dy::ArticulationLinkHandle link=0); void createGroundPlane() { createActor(PxPlaneGeometry(), PxTransformFromPlaneEquation(PxPlane(0.0f, 1.0f, 0.0f, 0.0f))); } void createScene(); #ifdef TEST_IMMEDIATE_JOINTS void createSphericalJoint(PxU32 id0, PxU32 id1, const PxTransform& localFrame0, const PxTransform& localFrame1, const PxTransform* pose0=NULL, const PxTransform* pose1=NULL); #endif void updateArticulations(float dt); void updateBounds(); void broadPhase(); void narrowPhase(); void buildSolverBodyData(float dt); void buildSolverConstraintDesc(); void createContactConstraints(float dt, float invDt, float lengthScale, PxU32 nbPositionIterations); void solveAndIntegrate(float dt); TestCacheAllocator* mCacheAllocator; TestConstraintAllocator* mConstraintAllocator; // PT: TODO: revisit this basic design once everything works Array mGeoms; Array mPoses; Array mBounds; class ImmediateActor { public: ImmediateActor() {} ~ImmediateActor() {} enum Type { eSTATIC, eDYNAMIC, eLINK, }; Type mType; PxU32 mCollisionGroup; MassProps mMassProps; PxVec3 mLinearVelocity; PxVec3 mAngularVelocity; Dy::ArticulationLinkHandle mLink; }; Array mActors; #if USE_TGS Array mSolverBodyData; Array mSolverBodies; Array mSolverBodyTxInertias; #else Array mSolverBodyData; Array mSolverBodies; #endif Array mArticulations; #ifdef TEST_IMMEDIATE_JOINTS Array mJointData; #endif Array mBroadphasePairs; HashSet mFilteredPairs; struct ContactPair { PxU32 mID0; PxU32 mID1; PxU32 mNbContacts; PxU32 mStartContactIndex; }; // PT: we use separate arrays here because the immediate mode API expects an array of Gu::ContactPoint Array mContactPairs; Array mContactPoints; #if WITH_PERSISTENCY HashMap mPersistentPairs; #endif Array mSolverConstraintDesc; #if BATCH_CONTACTS Array mOrderedSolverConstraintDesc; #endif Array mHeaders; Array mContactForces; Array mMotionLinearVelocity; // Persistent to avoid runtime allocations but could be managed on the stack Array mMotionAngularVelocity; // Persistent to avoid runtime allocations but could be managed on the stack PxU32 mNbStaticActors; PxU32 mNbArticulationLinks; PX_FORCE_INLINE void disableCollision(PxU32 i, PxU32 j) { if(i>j) swap(i, j); mFilteredPairs.insert(IDS(i, j)); } PX_FORCE_INLINE bool isCollisionDisabled(PxU32 i, PxU32 j) const { if(i>j) swap(i, j); return mFilteredPairs.contains(IDS(i, j)); } Dy::ArticulationLinkHandle mMotorLink; }; ImmediateScene::ImmediateScene() : mNbStaticActors (0), mNbArticulationLinks(0), mMotorLink (0) { mCacheAllocator = new TestCacheAllocator; mConstraintAllocator = new TestConstraintAllocator; } ImmediateScene::~ImmediateScene() { reset(); PX_DELETE_AND_RESET(mConstraintAllocator); PX_DELETE_AND_RESET(mCacheAllocator); } void ImmediateScene::reset() { mGeoms.clear(); mPoses.clear(); mBounds.clear(); mActors.clear(); mSolverBodyData.clear(); mSolverBodies.clear(); #if USE_TGS mSolverBodyTxInertias.clear(); #endif mBroadphasePairs.clear(); mFilteredPairs.clear(); mContactPairs.clear(); mContactPoints.clear(); mSolverConstraintDesc.clear(); #if BATCH_CONTACTS mOrderedSolverConstraintDesc.clear(); #endif mHeaders.clear(); mContactForces.clear(); mMotionLinearVelocity.clear(); mMotionAngularVelocity.clear(); const PxU32 size = mArticulations.size(); for(PxU32 i=0;i& articulations) { PxFeatherstoneArticulationData data; data.flags = fixBase ? PxArticulationFlag::eFIX_BASE : PxArticulationFlag::Enum(0); Dy::ArticulationV* immArt = PxCreateFeatherstoneArticulation(data); articulations.pushBack(immArt); return immArt; } static void setupCommonLinkData(PxFeatherstoneArticulationLinkData& data, Dy::ArticulationLinkHandle parent, const PxTransform& pose, const MassProps& massProps) { data.parent = parent; data.pose = pose; data.inverseMass = massProps.mInvMass; data.inverseInertia = massProps.mInvInertia; data.linearDamping = gLinearDamping; data.angularDamping = gAngularDamping; data.maxLinearVelocitySq = gMaxLinearVelocity * gMaxLinearVelocity; data.maxAngularVelocitySq = gMaxAngularVelocity * gMaxAngularVelocity; data.inboundJoint.frictionCoefficient = gJointFrictionCoefficient; } #ifdef TEST_IMMEDIATE_JOINTS void ImmediateScene::createSphericalJoint(PxU32 id0, PxU32 id1, const PxTransform& localFrame0, const PxTransform& localFrame1, const PxTransform* pose0, const PxTransform* pose1) { const bool isStatic0 = mActors[id0].mType == ImmediateActor::eSTATIC; const bool isStatic1 = mActors[id1].mType == ImmediateActor::eSTATIC; MyJointData jointData; jointData.mActors[0] = id0; jointData.mActors[1] = id1; jointData.mLocalFrames[0] = localFrame0; jointData.mLocalFrames[1] = localFrame1; if(isStatic0) jointData.c2b[0] = pose0->getInverse().transformInv(localFrame0); else jointData.c2b[0] = localFrame0; if(isStatic1) jointData.c2b[1] = pose1->getInverse().transformInv(localFrame1); else jointData.c2b[1] = localFrame1; jointData.initInvMassScale(); mJointData.pushBack(jointData); disableCollision(id0, id1); } #endif void ImmediateScene::createScene() { mMotorLink = 0; const PxU32 index = gSceneIndex; if(index==0) { // Box stack { const PxVec3 extents(0.5f, 0.5f, 0.5f); const PxBoxGeometry boxGeom(extents); MassProps massProps; computeMassProps(massProps, boxGeom, 1.0f); // for(PxU32 i=0;i<8;i++) // createBox(extents, PxTransform(PxVec3(0.0f, extents.y + float(i)*extents.y*2.0f, 0.0f)), 1.0f); PxU32 size = 8; // PxU32 size = 2; // PxU32 size = 1; float y = extents.y; float x = 0.0f; while(size) { for(PxU32 i=0;i(currentGeom); extents = boxGeom.halfExtents; } break; case PxGeometryType::eSPHERE: case PxGeometryType::eCAPSULE: case PxGeometryType::eCONVEXMESH: case PxGeometryType::eTRIANGLEMESH: case PxGeometryType::eHEIGHTFIELD: case PxGeometryType::eGEOMETRY_COUNT: case PxGeometryType::eINVALID: PX_ASSERT(0); break; } extents += PxVec3(gBoundsInflation); mBounds[i] = PxBounds3::basisExtent(center, PxMat33(currentPose.q), extents); } } void ImmediateScene::broadPhase() { // PT: in this snippet we simply do a brute-force O(n^2) broadphase between all actors mBroadphasePairs.clear(); const PxU32 nbActors = mActors.size(); for(PxU32 i=0; i::Entry* e = mPersistentPairs.find(IDS(i, j)); if(e) { PersistentContactPair& persistentData = const_cast(e->second); //No collision detection performed at all so clear contact cache and friction data persistentData.reset(); } } #endif } } } void ImmediateScene::narrowPhase() { class ContactRecorder : public PxContactRecorder { public: ContactRecorder(ImmediateScene* scene, PxU32 id0, PxU32 id1) : mScene(scene), mID0(id0), mID1(id1), mHasContacts(false) {} virtual bool recordContacts(const Gu::ContactPoint* contactPoints, const PxU32 nbContacts, const PxU32 /*index*/) { { ImmediateScene::ContactPair pair; pair.mID0 = mID0; pair.mID1 = mID1; pair.mNbContacts = nbContacts; pair.mStartContactIndex = mScene->mContactPoints.size(); mScene->mContactPairs.pushBack(pair); mHasContacts = true; } for(PxU32 i=0; imContactPoints.pushBack(point); } return true; } ImmediateScene* mScene; PxU32 mID0; PxU32 mID1; bool mHasContacts; }; mCacheAllocator->reset(); mConstraintAllocator->release(); mContactPairs.resize(0); mContactPoints.resize(0); const PxU32 nbPairs = mBroadphasePairs.size(); for(PxU32 i=0;i(const_cast(&pair)); desc.constraintLengthOver16 = PxSolverConstraintDesc::eCONTACT_CONSTRAINT; } #ifdef TEST_IMMEDIATE_JOINTS for(PxU32 i=0; i(const_cast(&jointData)); desc.constraintLengthOver16 = PxSolverConstraintDesc::eJOINT_CONSTRAINT; } #endif } #ifdef TEST_IMMEDIATE_JOINTS // PT: this is copied from PxExtensions, it's the solver prep function for spherical joints static PxU32 SphericalJointSolverPrep(Px1DConstraint* constraints, PxVec3& body0WorldOffset, PxU32 /*maxConstraints*/, PxConstraintInvMassScale& invMassScale, const void* constantBlock, const PxTransform& bA2w, const PxTransform& bB2w, bool /*useExtendedLimits*/, PxVec3& cA2wOut, PxVec3& cB2wOut) { const MyJointData& data = *reinterpret_cast(constantBlock); PxTransform cA2w, cB2w; Ext::joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w); if(cB2w.q.dot(cA2w.q)<0.0f) cB2w.q = -cB2w.q; /* if(data.jointFlags & PxSphericalJointFlag::eLIMIT_ENABLED) { PxQuat swing, twist; Ps::separateSwingTwist(cA2w.q.getConjugate() * cB2w.q, swing, twist); PX_ASSERT(PxAbs(swing.x)<1e-6f); // PT: TODO: refactor with D6 joint code PxVec3 axis; PxReal error; const PxReal pad = data.limit.isSoft() ? 0.0f : data.limit.contactDistance; const Cm::ConeLimitHelperTanLess coneHelper(data.limit.yAngle, data.limit.zAngle, pad); const bool active = coneHelper.getLimit(swing, axis, error); if(active) 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(); } #endif void setupDesc(PxSolverContactDesc& contactDesc, const ImmediateScene::ImmediateActor* actors, PxSolverBodyData* solverBodyData, const PxU32 id, const bool aorb) { PxTransform& bodyFrame = aorb ? contactDesc.bodyFrame1 : contactDesc.bodyFrame0; PxSolverConstraintPrepDescBase::BodyState& bodyState = aorb ? contactDesc.bodyState1 : contactDesc.bodyState0; const PxSolverBodyData*& data = aorb ? contactDesc.data1 : contactDesc.data0; Dy::ArticulationLinkHandle link = actors[id].mLink; if(link) { PxLinkData linkData; bool status = PxGetLinkData(link, linkData); PX_ASSERT(status); PX_UNUSED(status); data = NULL; bodyFrame = linkData.pose; bodyState = PxSolverConstraintPrepDescBase::eARTICULATION; } else { data = &solverBodyData[id]; bodyFrame = solverBodyData[id].body2World; bodyState = actors[id].mType == ImmediateScene::ImmediateActor::eDYNAMIC ? PxSolverConstraintPrepDescBase::eDYNAMIC_BODY : PxSolverConstraintPrepDescBase::eSTATIC_BODY; } } void setupDesc(PxTGSSolverContactDesc& contactDesc, const ImmediateScene::ImmediateActor* actors, PxTGSSolverBodyTxInertia* txInertias, PxTGSSolverBodyData* solverBodyData, PxTransform* poses, const PxU32 id, const bool aorb) { PxTransform& bodyFrame = aorb ? contactDesc.bodyFrame1 : contactDesc.bodyFrame0; PxSolverConstraintPrepDescBase::BodyState& bodyState = aorb ? contactDesc.bodyState1 : contactDesc.bodyState0; const PxTGSSolverBodyData*& data = aorb ? contactDesc.bodyData1 : contactDesc.bodyData0; const PxTGSSolverBodyTxInertia*& txI = aorb ? contactDesc.body1TxI : contactDesc.body0TxI; Dy::ArticulationLinkHandle link = actors[id].mLink; if(link) { PxLinkData linkData; bool status = PxGetLinkData(link, linkData); PX_ASSERT(status); PX_UNUSED(status); data = NULL; txI = NULL; bodyFrame = linkData.pose; bodyState = PxSolverConstraintPrepDescBase::eARTICULATION; } else { data = &solverBodyData[id]; txI = &txInertias[id]; bodyFrame = poses[id]; bodyState = actors[id].mType == ImmediateScene::ImmediateActor::eDYNAMIC ? PxSolverConstraintPrepDescBase::eDYNAMIC_BODY : PxSolverConstraintPrepDescBase::eSTATIC_BODY; } } void setupJointDesc(PxSolverConstraintPrepDesc& jointDesc, const ImmediateScene::ImmediateActor* actors, PxSolverBodyData* solverBodyData, const PxU32 bodyDataIndex, const bool aorb) { if(!aorb) jointDesc.data0 = &solverBodyData[bodyDataIndex]; else jointDesc.data1 = &solverBodyData[bodyDataIndex]; PxTransform& bodyFrame = aorb ? jointDesc.bodyFrame1 : jointDesc.bodyFrame0; PxSolverConstraintPrepDescBase::BodyState& bodyState = aorb ? jointDesc.bodyState1 : jointDesc.bodyState0; if(actors[bodyDataIndex].mLink) { PxLinkData linkData; bool status = PxGetLinkData(actors[bodyDataIndex].mLink, linkData); PX_ASSERT(status); PX_UNUSED(status); bodyFrame = linkData.pose; bodyState = PxSolverConstraintPrepDescBase::eARTICULATION; } else { //This may seem redundant but the bodyFrame is not defined by the bodyData object when using articulations. // PT: TODO: this is a bug in the immediate mode snippet if(actors[bodyDataIndex].mType == ImmediateScene::ImmediateActor::eSTATIC) { bodyFrame = PxTransform(PxIdentity); bodyState = PxSolverConstraintPrepDescBase::eSTATIC_BODY; } else { bodyFrame = solverBodyData[bodyDataIndex].body2World; bodyState = PxSolverConstraintPrepDescBase::eDYNAMIC_BODY; } } } void setupJointDesc(PxTGSSolverConstraintPrepDesc& jointDesc, const ImmediateScene::ImmediateActor* actors, PxTGSSolverBodyTxInertia* txInertias, PxTGSSolverBodyData* solverBodyData, PxTransform* poses, const PxU32 bodyDataIndex, const bool aorb) { if(!aorb) { jointDesc.bodyData0 = &solverBodyData[bodyDataIndex]; jointDesc.body0TxI = &txInertias[bodyDataIndex]; } else { jointDesc.bodyData1 = &solverBodyData[bodyDataIndex]; jointDesc.body1TxI = &txInertias[bodyDataIndex]; } PxTransform& bodyFrame = aorb ? jointDesc.bodyFrame1 : jointDesc.bodyFrame0; PxSolverConstraintPrepDescBase::BodyState& bodyState = aorb ? jointDesc.bodyState1 : jointDesc.bodyState0; if(actors[bodyDataIndex].mLink) { PxLinkData linkData; bool status = PxGetLinkData(actors[bodyDataIndex].mLink, linkData); PX_ASSERT(status); PX_UNUSED(status); bodyFrame = linkData.pose; bodyState = PxSolverConstraintPrepDescBase::eARTICULATION; } else { //This may seem redundant but the bodyFrame is not defined by the bodyData object when using articulations. // PT: TODO: this is a bug in the immediate mode snippet if(actors[bodyDataIndex].mType == ImmediateScene::ImmediateActor::eSTATIC) { bodyFrame = PxTransform(PxIdentity); bodyState = PxSolverConstraintPrepDescBase::eSTATIC_BODY; } else { bodyFrame = poses[bodyDataIndex]; bodyState = PxSolverConstraintPrepDescBase::eDYNAMIC_BODY; } } } void ImmediateScene::createContactConstraints(float dt, float invDt, float lengthScale, const PxU32 nbPosIterations) { //Only referenced if using TGS solver PX_UNUSED(lengthScale); PX_UNUSED(nbPosIterations); #if USE_TGS const float stepDt = dt/float(nbPosIterations); const float stepInvDt = invDt*float(nbPosIterations); #endif #if BATCH_CONTACTS mHeaders.resize(mSolverConstraintDesc.size()); const PxU32 nbBodies = mActors.size() - mNbStaticActors; mOrderedSolverConstraintDesc.resize(mSolverConstraintDesc.size()); Array& orderedDescs = mOrderedSolverConstraintDesc; #if USE_TGS const PxU32 nbContactHeaders = physx::immediate::PxBatchConstraintsTGS( mSolverConstraintDesc.begin(), mContactPairs.size(), mSolverBodies.begin(), nbBodies, mHeaders.begin(), orderedDescs.begin(), mArticulations.begin(), mArticulations.size()); //2 batch the joints... const PxU32 nbJointHeaders = physx::immediate::PxBatchConstraintsTGS( mSolverConstraintDesc.begin() + mContactPairs.size(), mJointData.size(), mSolverBodies.begin(), nbBodies, mHeaders.begin() + nbContactHeaders, orderedDescs.begin() + mContactPairs.size(), mArticulations.begin(), mArticulations.size()); #else //1 batch the contacts const PxU32 nbContactHeaders = physx::immediate::PxBatchConstraints(mSolverConstraintDesc.begin(), mContactPairs.size(), mSolverBodies.begin(), nbBodies, mHeaders.begin(), orderedDescs.begin(), mArticulations.begin(), mArticulations.size()); //2 batch the joints... const PxU32 nbJointHeaders = physx::immediate::PxBatchConstraints( mSolverConstraintDesc.begin() + mContactPairs.size(), mJointData.size(), mSolverBodies.begin(), nbBodies, mHeaders.begin() + nbContactHeaders, orderedDescs.begin() + mContactPairs.size(), mArticulations.begin(), mArticulations.size()); #endif const PxU32 totalHeaders = nbContactHeaders + nbJointHeaders; mHeaders.forceSize_Unsafe(totalHeaders); #else Array& orderedDescs = mSolverConstraintDesc; const PxU32 nbContactHeaders = mContactPairs.size(); #ifdef TEST_IMMEDIATE_JOINTS const PxU32 nbJointHeaders = mJointData.size(); PX_ASSERT(nbContactHeaders+nbJointHeaders==mSolverConstraintDesc.size()); mHeaders.resize(nbContactHeaders+nbJointHeaders); #else PX_ASSERT(nbContactHeaders==mSolverConstraintDesc.size()); PX_UNUSED(dt); mHeaders.resize(nbContactHeaders); #endif // We are bypassing the constraint batching so we create dummy PxConstraintBatchHeaders for(PxU32 i=0; i(constraintDesc.constraint); #if USE_TGS PxTGSSolverContactDesc& contactDesc = contactDescs[a]; setupDesc(contactDesc, mActors.begin(), mSolverBodyTxInertias.begin(), mSolverBodyData.begin(), mPoses.begin(), pair.mID0, false); setupDesc(contactDesc, mActors.begin(), mSolverBodyTxInertias.begin(), mSolverBodyData.begin(), mPoses.begin(), pair.mID1, true); contactDesc.body0 = constraintDesc.tgsBodyA; contactDesc.body1 = constraintDesc.tgsBodyB; contactDesc.torsionalPatchRadius = 0.0f; contactDesc.minTorsionalPatchRadius = 0.0f; #else PxSolverContactDesc& contactDesc = contactDescs[a]; setupDesc(contactDesc, mActors.begin(), mSolverBodyData.begin(), pair.mID0, false); setupDesc(contactDesc, mActors.begin(), mSolverBodyData.begin(), pair.mID1, true); contactDesc.body0 = constraintDesc.bodyA; contactDesc.body1 = constraintDesc.bodyB; #endif contactDesc.contactForces = &mContactForces[pair.mStartContactIndex]; contactDesc.contacts = &mContactPoints[pair.mStartContactIndex]; contactDesc.numContacts = pair.mNbContacts; #if WITH_PERSISTENCY const HashMap::Entry* e = mPersistentPairs.find(IDS(pair.mID0, pair.mID1)); PX_ASSERT(e); { PersistentContactPair& pcp = const_cast(e->second); contactDesc.frictionPtr = pcp.frictions; contactDesc.frictionCount = PxU8(pcp.nbFrictions); persistentPairs[a] = &pcp; } #else contactDesc.frictionPtr = NULL; contactDesc.frictionCount = 0; #endif contactDesc.disableStrongFriction = false; contactDesc.hasMaxImpulse = false; contactDesc.hasForceThresholds = false; contactDesc.shapeInteraction = NULL; contactDesc.restDistance = 0.0f; contactDesc.maxCCDSeparation = PX_MAX_F32; contactDesc.desc = &constraintDesc; contactDesc.invMassScales.angular0 = contactDesc.invMassScales.angular1 = contactDesc.invMassScales.linear0 = contactDesc.invMassScales.linear1 = 1.0f; } #if USE_TGS PxCreateContactConstraintsTGS(&header, 1, contactDescs, *mConstraintAllocator, stepInvDt, invDt, gBounceThreshold, gFrictionOffsetThreshold, gCorrelationDistance); #else PxCreateContactConstraints(&header, 1, contactDescs, *mConstraintAllocator, invDt, gBounceThreshold, gFrictionOffsetThreshold, gCorrelationDistance); #endif #if WITH_PERSISTENCY //Cache friction information... for (PxU32 a = 0; a < header.stride; ++a) { #if USE_TGS const PxTGSSolverContactDesc& contactDesc = contactDescs[a]; #else const PxSolverContactDesc& contactDesc = contactDescs[a]; #endif PersistentContactPair& pcp = *persistentPairs[a]; pcp.frictions = contactDesc.frictionPtr; pcp.nbFrictions = contactDesc.frictionCount; } #endif } #ifdef TEST_IMMEDIATE_JOINTS for(PxU32 i=0; i(constraintDesc.constraint); constraints[a].prep = SphericalJointSolverPrep; constraints[a].constantBlock = &jd; #if USE_TGS PxTGSSolverConstraintPrepDesc& jointDesc = jointDescs[a]; jointDesc.body0 = constraintDesc.tgsBodyA; jointDesc.body1 = constraintDesc.tgsBodyB; setupJointDesc(jointDesc, mActors.begin(), mSolverBodyTxInertias.begin(), mSolverBodyData.begin(), mPoses.begin(), constraintDesc.bodyADataIndex, false); setupJointDesc(jointDesc, mActors.begin(), mSolverBodyTxInertias.begin(), mSolverBodyData.begin(), mPoses.begin(), constraintDesc.bodyBDataIndex, true); #else PxSolverConstraintPrepDesc& jointDesc = jointDescs[a]; jointDesc.body0 = constraintDesc.bodyA; jointDesc.body1 = constraintDesc.bodyB; setupJointDesc(jointDesc, mActors.begin(), mSolverBodyData.begin(), constraintDesc.bodyADataIndex, false); setupJointDesc(jointDesc, mActors.begin(), mSolverBodyData.begin(), constraintDesc.bodyBDataIndex, true); #endif jointDesc.desc = &constraintDesc; jointDesc.writeback = NULL; jointDesc.linBreakForce = PX_MAX_F32; jointDesc.angBreakForce = PX_MAX_F32; jointDesc.minResponseThreshold = 0; jointDesc.disablePreprocessing = false; jointDesc.improvedSlerp = false; jointDesc.driveLimitsAreForces = false; jointDesc.invMassScales.angular0 = jointDesc.invMassScales.angular1 = jointDesc.invMassScales.linear0 = jointDesc.invMassScales.linear1 = 1.0f; } #if USE_TGS immediate::PxCreateJointConstraintsWithImmediateShadersTGS(&header, 1, constraints, jointDescs, *mConstraintAllocator, stepDt, dt, stepInvDt, invDt, lengthScale); #else immediate::PxCreateJointConstraintsWithImmediateShaders(&header, 1, constraints, jointDescs, *mConstraintAllocator, dt, invDt); #endif } } #endif } void ImmediateScene::solveAndIntegrate(float dt) { #ifdef PRINT_TIMINGS unsigned long long time0 = __rdtsc(); #endif const PxU32 totalNbActors = mActors.size(); const PxU32 nbDynamicActors = totalNbActors - mNbStaticActors - mNbArticulationLinks; const PxU32 nbDynamic = nbDynamicActors + mNbArticulationLinks; mMotionLinearVelocity.resize(nbDynamic); mMotionAngularVelocity.resize(nbDynamic); const PxU32 nbArticulations = mArticulations.size(); Dy::ArticulationV** articulations = mArticulations.begin(); #if USE_TGS const float stepDt = dt/float(gNbIterPos); immediate::PxSolveConstraintsTGS(mHeaders.begin(), mHeaders.size(), #if BATCH_CONTACTS mOrderedSolverConstraintDesc.begin(), #else mSolverConstraintDesc.begin(), #endif mSolverBodies.begin(), mSolverBodyTxInertias.begin(), nbDynamic, gNbIterPos, gNbIterVel, stepDt, 1.0f / stepDt, nbArticulations, articulations); #else PxMemZero(mSolverBodies.begin(), mSolverBodies.size() * sizeof(PxSolverBody)); PxSolveConstraints( mHeaders.begin(), mHeaders.size(), #if BATCH_CONTACTS mOrderedSolverConstraintDesc.begin(), #else mSolverConstraintDesc.begin(), #endif mSolverBodies.begin(), mMotionLinearVelocity.begin(), mMotionAngularVelocity.begin(), nbDynamic, gNbIterPos, gNbIterVel, dt, 1.0f/dt, nbArticulations, articulations); #endif #ifdef PRINT_TIMINGS unsigned long long time1 = __rdtsc(); #endif #if USE_TGS PxIntegrateSolverBodiesTGS(mSolverBodies.begin(), mSolverBodyTxInertias.begin(), mPoses.begin(), nbDynamicActors, dt); for (PxU32 i = 0; imGeoms.size() : 0; } const PxGeometryHolder* getGeoms() { if(!gScene || !gScene->mGeoms.size()) return NULL; return &gScene->mGeoms[0]; } const PxTransform* getGeomPoses() { if(!gScene || !gScene->mPoses.size()) return NULL; return &gScene->mPoses[0]; } PxU32 getNbArticulations() { return gScene ? gScene->mArticulations.size() : 0; } Dy::ArticulationV** getArticulations() { if(!gScene || !gScene->mArticulations.size()) return NULL; return &gScene->mArticulations[0]; } PxU32 getNbBounds() { if(!gDrawBounds) return 0; return gScene ? gScene->mBounds.size() : 0; } const PxBounds3* getBounds() { if(!gDrawBounds) return NULL; if(!gScene || !gScene->mBounds.size()) return NULL; return &gScene->mBounds[0]; } PxU32 getNbContacts() { return gScene ? gScene->mContactPoints.size() : 0; } const Gu::ContactPoint* getContacts() { if(!gScene || !gScene->mContactPoints.size()) return NULL; return &gScene->mContactPoints[0]; } /////////////////////////////////////////////////////////////////////////////// void initPhysics(bool /*interactive*/) { gFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, gAllocator, gErrorCallback); PxRegisterImmediateArticulations(); gScene = new ImmediateScene; gScene->createScene(); } void stepPhysics(bool /*interactive*/) { if(!gScene) return; if(gPause && !gOneFrame) return; gOneFrame = false; const float dt = 1.0f/60.0f; const float invDt = 60.0f; { #ifdef PRINT_TIMINGS unsigned long long time = __rdtsc(); #endif gScene->updateArticulations(dt); #ifdef PRINT_TIMINGS time = __rdtsc() - time; printf("updateArticulations: %d \n", time/1024); #endif } { #ifdef PRINT_TIMINGS unsigned long long time = __rdtsc(); #endif gScene->updateBounds(); #ifdef PRINT_TIMINGS time = __rdtsc() - time; printf("updateBounds: %d \n", time/1024); #endif } { #ifdef PRINT_TIMINGS unsigned long long time = __rdtsc(); #endif gScene->broadPhase(); #ifdef PRINT_TIMINGS time = __rdtsc() - time; printf("broadPhase: %d \n", time/1024); #endif } { #ifdef PRINT_TIMINGS unsigned long long time = __rdtsc(); #endif gScene->narrowPhase(); #ifdef PRINT_TIMINGS time = __rdtsc() - time; printf("narrowPhase: %d \n", time/1024); #endif } { #ifdef PRINT_TIMINGS unsigned long long time = __rdtsc(); #endif gScene->buildSolverBodyData(dt); #ifdef PRINT_TIMINGS time = __rdtsc() - time; printf("buildSolverBodyData: %d \n", time/1024); #endif } { #ifdef PRINT_TIMINGS unsigned long long time = __rdtsc(); #endif gScene->buildSolverConstraintDesc(); #ifdef PRINT_TIMINGS time = __rdtsc() - time; printf("buildSolverConstraintDesc: %d \n", time/1024); #endif } { #ifdef PRINT_TIMINGS unsigned long long time = __rdtsc(); #endif gScene->createContactConstraints(dt, invDt, 1.f, gNbIterPos); #ifdef PRINT_TIMINGS time = __rdtsc() - time; printf("createContactConstraints: %d \n", time/1024); #endif } { #ifdef PRINT_TIMINGS // unsigned long long time = __rdtsc(); #endif gScene->solveAndIntegrate(dt); #ifdef PRINT_TIMINGS // time = __rdtsc() - time; // printf("solveAndIntegrate: %d \n", time/1024); #endif } if(gScene->mMotorLink) { static float time = 0.0f; time += 0.1f; const float target = sinf(time) * 4.0f; // printf("target: %f\n", target); PxFeatherstoneArticulationJointData data; bool status = PxGetJointData(gScene->mMotorLink, data); PX_ASSERT(status); data.targetVel[PxArticulationAxis::eTWIST] = target; const PxVec3 boxExtents(0.5f, 0.1f, 0.5f); const float s = boxExtents.x*1.1f + fabsf(sinf(time))*0.5f; data.parentPose = PxTransform(PxVec3(0.0f, 0.0f, s)); data.childPose = PxTransform(PxVec3(0.0f, 0.0f, -s)); status = PxSetJointData(gScene->mMotorLink, data); PX_ASSERT(status); PX_UNUSED(status); } } void cleanupPhysics(bool /*interactive*/) { PX_DELETE_AND_RESET(gScene); PX_RELEASE(gFoundation); printf("SnippetImmediateArticulation done.\n"); } void keyPress(unsigned char key, const PxTransform& /*camera*/) { if(key=='b' || key=='B') gDrawBounds = !gDrawBounds; if(key=='p' || key=='P') gPause = !gPause; if(key=='o' || key=='O') { gPause = true; gOneFrame = true; } if(gScene) { if(key>=1 && key<=6) { gSceneIndex = key-1; gScene->reset(); gScene->createScene(); } if(key=='r' || key=='R') { gScene->reset(); gScene->createScene(); } } } int snippetMain(int, const char*const*) { printf("Immediate articulation snippet. Use these keys:\n"); printf(" P - enable/disable pause\n"); printf(" O - step simulation for one frame\n"); printf(" R - reset scene\n"); printf(" F1 to F6 - select scene\n"); printf("\n"); #ifdef RENDER_SNIPPET extern void renderLoop(); renderLoop(); #else static const PxU32 frameCount = 100; initPhysics(false); for(PxU32 i=0; i