// // 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-2019 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 "PxImmediateMode.h" #include "../../lowleveldynamics/src/DyBodyCoreIntegrator.h" #include "../../lowleveldynamics/src/DyContactPrep.h" #include "../../lowleveldynamics/src/DyCorrelationBuffer.h" #include "../../lowleveldynamics/src/DyConstraintPrep.h" #include "../../lowleveldynamics/src/DySolverControl.h" #include "../../lowleveldynamics/src/DySolverContext.h" #include "../../lowlevel/common/include/collision/PxcContactMethodImpl.h" #include "../../lowleveldynamics/src/DyTGSContactPrep.h" #include "GuPersistentContactManifold.h" #include "NpConstraint.h" // PT: just for Dy::DY_ARTICULATION_MAX_SIZE #include "../../lowleveldynamics/include/DyFeatherstoneArticulation.h" #include "../../lowlevel/common/include/utils/PxcScratchAllocator.h" using namespace physx; using namespace Dy; using namespace immediate; void immediate::PxConstructSolverBodies(const PxRigidBodyData* inRigidData, PxSolverBodyData* outSolverBodyData, const PxU32 nbBodies, const PxVec3& gravity, const PxReal dt) { for(PxU32 a=0; a mLinks; // No dynamic array because we want to store the pointer to these items in ArticulationLink, and we don't want to deal with remapping PxsBodyCore mBodyCores[DY_ARTICULATION_MAX_SIZE]; Dy::ArticulationJointCore mArticulationJointCore[DY_ARTICULATION_MAX_SIZE]; PxArticulationFlags mFlags; // PT: PX-1399. Stored in Dy::ArticulationCore for retained mode. bool mImmDirty; private: void initJointCore(Dy::ArticulationJointCore& core, const PxFeatherstoneArticulationLinkData& data); }; #define MAX_NUM_PARTITIONS 32u class RigidBodyClassification { PX_NOCOPY(RigidBodyClassification) PxU8* PX_RESTRICT mBodies; const PxU32 mBodySize; const PxU32 mBodyStride; const PxU32 mBodyCount; public: RigidBodyClassification(PxU8* PX_RESTRICT bodies, PxU32 bodyCount, PxU32 bodyStride) : mBodies(bodies), mBodySize(bodyCount*bodyStride), mBodyStride(bodyStride), mBodyCount(bodyCount) { } //Returns true if it is a dynamic-dynamic constraint; false if it is a dynamic-static or dynamic-kinematic constraint PX_FORCE_INLINE bool classifyConstraint(const PxSolverConstraintDesc& desc, uintptr_t& indexA, uintptr_t& indexB, bool& activeA, bool& activeB, PxU32& bodyAProgress, PxU32& bodyBProgress) const { indexA = uintptr_t(reinterpret_cast(desc.bodyA) - mBodies) / mBodyStride; indexB = uintptr_t(reinterpret_cast(desc.bodyB) - mBodies) / mBodyStride; activeA = indexA < mBodyCount; activeB = indexB < mBodyCount; bodyAProgress = desc.bodyA->solverProgress; bodyBProgress = desc.bodyB->solverProgress; return activeA && activeB; } PX_FORCE_INLINE void reserveSpaceForStaticConstraints(PxU32* numConstraintsPerPartition) { for (PxU32 a = 0; a < mBodySize; a += mBodyStride) { PxSolverBody& body = *reinterpret_cast(mBodies + a); body.solverProgress = 0; for (PxU32 b = 0; b < body.maxSolverFrictionProgress; ++b) { PxU32 partId = PxMin(PxU32(body.maxSolverNormalProgress + b), MAX_NUM_PARTITIONS); numConstraintsPerPartition[partId]++; } } } PX_FORCE_INLINE void storeProgress(const PxSolverConstraintDesc& desc, const PxU32 bodyAProgress, const PxU32 bodyBProgress, const PxU16 availablePartition) { desc.bodyA->solverProgress = bodyAProgress; desc.bodyA->maxSolverNormalProgress = PxMax(desc.bodyA->maxSolverNormalProgress, availablePartition); desc.bodyB->solverProgress = bodyBProgress; desc.bodyB->maxSolverNormalProgress = PxMax(desc.bodyB->maxSolverNormalProgress, availablePartition); } PX_FORCE_INLINE PxU32 getStaticContactWriteIndex(const PxSolverConstraintDesc& desc, bool activeA, bool activeB) { if (activeA) return PxU32(desc.bodyA->maxSolverNormalProgress + desc.bodyA->maxSolverFrictionProgress++); else if (activeB) return PxU32(desc.bodyB->maxSolverNormalProgress + desc.bodyB->maxSolverFrictionProgress++); return 0xffffffff; } PX_FORCE_INLINE void recordStaticConstraint(const PxSolverConstraintDesc& desc, bool& activeA, bool& activeB) const { if (activeA) { desc.bodyA->maxSolverFrictionProgress++; } if (activeB) { desc.bodyB->maxSolverFrictionProgress++; } } PX_FORCE_INLINE void zeroBodies() { for (PxU32 a = 0; a < mBodySize; a += mBodyStride) { PxSolverBody& body = *reinterpret_cast(mBodies + a); body.solverProgress = 0; body.maxSolverFrictionProgress = 0; body.maxSolverNormalProgress = 0; } } PX_FORCE_INLINE void afterClassification() { for (PxU32 a = 0; a < mBodySize; a += mBodyStride) { PxSolverBody& body = *reinterpret_cast(mBodies + a); body.solverProgress = 0; //Keep the dynamic constraint count but bump the static constraint count back to 0. //This allows us to place the static constraints in the appropriate place when we see them //because we know the maximum index for the dynamic constraints... body.maxSolverFrictionProgress = 0; } } }; class ExtendedRigidBodyClassification { PX_NOCOPY(ExtendedRigidBodyClassification) PxU8* PX_RESTRICT mBodies; const PxU32 mBodyCount; const PxU32 mBodySize; const PxU32 mStride; Dy::ArticulationV** PX_RESTRICT mArticulations; const PxU32 mNumArticulations; public: ExtendedRigidBodyClassification(PxU8* PX_RESTRICT bodies, PxU32 numBodies, PxU32 stride, Dy::ArticulationV** articulations, PxU32 numArticulations) : mBodies(bodies), mBodyCount(numBodies), mBodySize(numBodies*stride), mStride(stride), mArticulations(articulations), mNumArticulations(numArticulations) { } //Returns true if it is a dynamic-dynamic constriant; false if it is a dynamic-static or dynamic-kinematic constraint PX_FORCE_INLINE bool classifyConstraint(const PxSolverConstraintDesc& desc, uintptr_t& indexA, uintptr_t& indexB, bool& activeA, bool& activeB, PxU32& bodyAProgress, PxU32& bodyBProgress) const { bool hasStatic = false; if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) { indexA = uintptr_t(reinterpret_cast(desc.bodyA) - mBodies) / mStride; activeA = indexA < mBodyCount; hasStatic = !activeA;//desc.bodyADataIndex == 0; bodyAProgress = activeA ? desc.bodyA->solverProgress : 0; } else { Dy::ArticulationV* articulationA = desc.articulationA; indexA = mBodyCount; //bodyAProgress = articulationA->getFsDataPtr()->solverProgress; bodyAProgress = articulationA->solverProgress; activeA = true; } if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) { indexB = uintptr_t(reinterpret_cast(desc.bodyB) - mBodies) / mStride; activeB = indexB < mBodyCount; hasStatic = hasStatic || !activeB; bodyBProgress = activeB ? desc.bodyB->solverProgress : 0; } else { Dy::ArticulationV* articulationB = desc.articulationB; indexB = mBodyCount; activeB = true; bodyBProgress = articulationB->solverProgress; } return !hasStatic; } PX_FORCE_INLINE void recordStaticConstraint(const PxSolverConstraintDesc& desc, bool& activeA, bool& activeB) { if (activeA) { if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) desc.bodyA->maxSolverFrictionProgress++; else { Dy::ArticulationV* articulationA = desc.articulationA; articulationA->maxSolverFrictionProgress++; } } if (activeB) { if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) desc.bodyB->maxSolverFrictionProgress++; else { Dy::ArticulationV* articulationB = desc.articulationB; articulationB->maxSolverFrictionProgress++; } } } PX_FORCE_INLINE PxU32 getStaticContactWriteIndex(const PxSolverConstraintDesc& desc, bool activeA, bool activeB) { if (activeA) { if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) { return PxU32(desc.bodyA->maxSolverNormalProgress + desc.bodyA->maxSolverFrictionProgress++); } else { Dy::ArticulationV* articulationA = desc.articulationA; return PxU32(articulationA->maxSolverNormalProgress + articulationA->maxSolverFrictionProgress++); } } else if (activeB) { if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) { return PxU32(desc.bodyB->maxSolverNormalProgress + desc.bodyB->maxSolverFrictionProgress++); } else { Dy::ArticulationV* articulationB = desc.articulationB; return PxU32(articulationB->maxSolverNormalProgress + articulationB->maxSolverFrictionProgress++); } } return 0xffffffff; } PX_FORCE_INLINE void storeProgress(const PxSolverConstraintDesc& desc, const PxU32 bodyAProgress, const PxU32 bodyBProgress, const PxU16 availablePartition) { if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) { desc.bodyA->solverProgress = bodyAProgress; desc.bodyA->maxSolverNormalProgress = PxMax(desc.bodyA->maxSolverNormalProgress, availablePartition); } else { Dy::ArticulationV* articulationA = desc.articulationA; articulationA->solverProgress = bodyAProgress; articulationA->maxSolverNormalProgress = PxMax(articulationA->maxSolverNormalProgress, availablePartition); } if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) { desc.bodyB->solverProgress = bodyBProgress; desc.bodyB->maxSolverNormalProgress = PxMax(desc.bodyB->maxSolverNormalProgress, availablePartition); } else { Dy::ArticulationV* articulationB = desc.articulationB; articulationB->solverProgress = bodyBProgress; articulationB->maxSolverNormalProgress = PxMax(articulationB->maxSolverNormalProgress, availablePartition); } } PX_FORCE_INLINE void reserveSpaceForStaticConstraints(PxU32* numConstraintsPerPartition) { for (PxU32 a = 0; a < mBodySize; a += mStride) { PxSolverBody& body = *reinterpret_cast(mBodies + a); body.solverProgress = 0; for (PxU32 b = 0; b < body.maxSolverFrictionProgress; ++b) { PxU32 partId = PxMin(PxU32(body.maxSolverNormalProgress + b), MAX_NUM_PARTITIONS); numConstraintsPerPartition[partId]++; } } for (PxU32 a = 0; a < mNumArticulations; ++a) { Dy::ArticulationV* articulation = mArticulations[a]; articulation->solverProgress = 0; for (PxU32 b = 0; b < articulation->maxSolverFrictionProgress; ++b) { PxU32 partId = PxMin(PxU32(articulation->maxSolverNormalProgress + b), MAX_NUM_PARTITIONS); numConstraintsPerPartition[partId]++; } } } PX_FORCE_INLINE void zeroBodies() { for (PxU32 a = 0; a < mBodySize; a += mStride) { PxSolverBody& body = *reinterpret_cast(mBodies + a); body.solverProgress = 0; body.maxSolverFrictionProgress = 0; body.maxSolverNormalProgress = 0; } for (PxU32 a = 0; a < mNumArticulations; ++a) { Dy::ArticulationV* articulation = mArticulations[a]; articulation->solverProgress = 0; articulation->maxSolverFrictionProgress = 0; articulation->maxSolverNormalProgress = 0; } } PX_FORCE_INLINE void afterClassification() { for (PxU32 a = 0; a < mBodySize; a += mStride) { PxSolverBody& body = *reinterpret_cast(mBodies + a); body.solverProgress = 0; //Keep the dynamic constraint count but bump the static constraint count back to 0. //This allows us to place the static constraints in the appropriate place when we see them //because we know the maximum index for the dynamic constraints... body.maxSolverFrictionProgress = 0; } for (PxU32 a = 0; a < mNumArticulations; ++a) { Dy::ArticulationV* articulation = mArticulations[a]; articulation->solverProgress = 0; articulation->maxSolverFrictionProgress = 0; } } }; template void classifyConstraintDesc(const PxSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, PxU32* numConstraintsPerPartition) { const PxSolverConstraintDesc* _desc = descs; const PxU32 numConstraintsMin1 = numConstraints - 1; PxMemZero(numConstraintsPerPartition, sizeof(PxU32) * (MAX_NUM_PARTITIONS+1)); for (PxU32 i = 0; i < numConstraints; ++i, _desc++) { const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); Ps::prefetchLine(_desc[prefetchOffset].constraint); Ps::prefetchLine(_desc[prefetchOffset].bodyA); Ps::prefetchLine(_desc[prefetchOffset].bodyB); Ps::prefetchLine(_desc + 8); uintptr_t indexA, indexB; bool activeA, activeB; PxU32 partitionsA, partitionsB; const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, activeA, activeB, partitionsA, partitionsB); if (notContainsStatic) { PxU32 availablePartition; { const PxU32 combinedMask = (~partitionsA & ~partitionsB); availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); if (availablePartition == MAX_NUM_PARTITIONS) { //Write to overflow partition... numConstraintsPerPartition[availablePartition]++; classification.storeProgress(*_desc, partitionsA, partitionsB, PxU16(MAX_NUM_PARTITIONS)); continue; } const PxU32 partitionBit = (1u << availablePartition); partitionsA |= partitionBit; partitionsB |= partitionBit; } numConstraintsPerPartition[availablePartition]++; availablePartition++; classification.storeProgress(*_desc, partitionsA, partitionsB, PxU16(availablePartition)); } else { //Just count the number of static constraints and store in maxSolverFrictionProgress... classification.recordStaticConstraint(*_desc, activeA, activeB); } } classification.reserveSpaceForStaticConstraints(numConstraintsPerPartition); } template void writeConstraintDesc(const PxSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, PxU32* accumulatedConstraintsPerPartition, PxSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDesc) { const PxSolverConstraintDesc* _desc = descs; const PxU32 numConstraintsMin1 = numConstraints - 1; for (PxU32 i = 0; i < numConstraints; ++i, _desc++) { const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); Ps::prefetchLine(_desc[prefetchOffset].constraint); Ps::prefetchLine(_desc[prefetchOffset].bodyA); Ps::prefetchLine(_desc[prefetchOffset].bodyB); Ps::prefetchLine(_desc + 8); uintptr_t indexA, indexB; bool activeA, activeB; PxU32 partitionsA, partitionsB; const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, activeA, activeB, partitionsA, partitionsB); if (notContainsStatic) { PxU32 availablePartition; { const PxU32 combinedMask = (~partitionsA & ~partitionsB); availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); if (availablePartition == MAX_NUM_PARTITIONS) { eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = *_desc; continue; } const PxU32 partitionBit = (1u << availablePartition); partitionsA |= partitionBit; partitionsB |= partitionBit; } classification.storeProgress(*_desc, partitionsA, partitionsB, PxU16(availablePartition+1)); eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = *_desc; } else { //Just count the number of static constraints and store in maxSolverFrictionProgress... //KS - TODO - handle registration of static contacts with articulations here! PxU32 index = classification.getStaticContactWriteIndex(*_desc, activeA, activeB); eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[index]++] = *_desc; } } } PX_FORCE_INLINE bool isArticulationConstraint(const PxSolverConstraintDesc& desc) { return desc.linkIndexA != PxSolverConstraintDesc::NO_LINK || desc.linkIndexB != PxSolverConstraintDesc::NO_LINK; } template PxU32 BatchConstraints(const PxSolverConstraintDesc* solverConstraintDescs, const PxU32 nbConstraints, PxConstraintBatchHeader* outBatchHeaders, PxSolverConstraintDesc* outOrderedConstraintDescs, Classification& classification) { PxU32 constraintsPerPartition[MAX_NUM_PARTITIONS + 1]; classification.zeroBodies(); classifyConstraintDesc(solverConstraintDescs, nbConstraints, classification, constraintsPerPartition); PxU32 accumulation = 0; for (PxU32 a = 0; a < MAX_NUM_PARTITIONS + 1; ++a) { PxU32 count = constraintsPerPartition[a]; constraintsPerPartition[a] = accumulation; accumulation += count; } classification.afterClassification(); writeConstraintDesc(solverConstraintDescs, nbConstraints, classification, constraintsPerPartition, outOrderedConstraintDescs); PxU32 numHeaders = 0; PxU32 currentPartition = 0; PxU32 maxJ = nbConstraints == 0 ? 0 : constraintsPerPartition[0]; const PxU32 maxBatchPartition = MAX_NUM_PARTITIONS; for (PxU32 a = 0; a < nbConstraints;) { PxConstraintBatchHeader& header = outBatchHeaders[numHeaders++]; header.startIndex = a; PxU32 loopMax = PxMin(maxJ - a, 4u); PxU16 j = 0; if (loopMax > 0) { j = 1; PxSolverConstraintDesc& desc = outOrderedConstraintDescs[a]; if(isArticulationConstraint(desc)) loopMax = 1; if (currentPartition < maxBatchPartition) { for (; j < loopMax && desc.constraintLengthOver16 == outOrderedConstraintDescs[a + j].constraintLengthOver16 && !isArticulationConstraint(outOrderedConstraintDescs[a + j]); ++j); } header.stride = j; header.constraintType = desc.constraintLengthOver16; } if (maxJ == (a + j) && maxJ != nbConstraints) { currentPartition++; maxJ = constraintsPerPartition[currentPartition]; } a += j; } return numHeaders; } } PxU32 immediate::PxBatchConstraints(const PxSolverConstraintDesc* solverConstraintDescs, const PxU32 nbConstraints, PxSolverBody* solverBodies, const PxU32 nbBodies, PxConstraintBatchHeader* outBatchHeaders, PxSolverConstraintDesc* outOrderedConstraintDescs, Dy::ArticulationV** articulations, const PxU32 nbArticulations) { if(!nbArticulations) { RigidBodyClassification classification(reinterpret_cast(solverBodies), nbBodies, sizeof(PxSolverBody)); return BatchConstraints(solverConstraintDescs, nbConstraints, outBatchHeaders, outOrderedConstraintDescs, classification); } else { ExtendedRigidBodyClassification classification(reinterpret_cast(solverBodies), nbBodies, sizeof(PxSolverBody), articulations, nbArticulations); return BatchConstraints(solverConstraintDescs, nbConstraints, outBatchHeaders, outOrderedConstraintDescs, classification); } } bool immediate::PxCreateContactConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxSolverContactDesc* contactDescs, PxConstraintAllocator& allocator, const PxReal invDt, const PxReal bounceThreshold, const PxReal frictionOffsetThreshold, const PxReal correlationDistance) { PX_ASSERT(invDt > 0.f && PxIsFinite(invDt)); PX_ASSERT(bounceThreshold < 0.f); PX_ASSERT(frictionOffsetThreshold > 0.f); PX_ASSERT(correlationDistance > 0.f); Dy::SolverConstraintPrepState::Enum state = Dy::SolverConstraintPrepState::eUNBATCHABLE; Dy::CorrelationBuffer cb; PxU32 currentContactDescIdx = 0; for (PxU32 i = 0; i < nbHeaders; ++i) { PxConstraintBatchHeader& batchHeader = batchHeaders[i]; if (batchHeader.stride == 4) { PxU32 totalContacts = contactDescs[currentContactDescIdx].numContacts + contactDescs[currentContactDescIdx + 1].numContacts + contactDescs[currentContactDescIdx + 2].numContacts + contactDescs[currentContactDescIdx + 3].numContacts; if (totalContacts <= 64) { state = Dy::createFinalizeSolverContacts4(cb, contactDescs + currentContactDescIdx, invDt, bounceThreshold, frictionOffsetThreshold, correlationDistance, 0.f, allocator); } } if (state == Dy::SolverConstraintPrepState::eUNBATCHABLE) { Cm::SpatialVectorF Z[Dy::DY_ARTICULATION_MAX_SIZE]; for (PxU32 a = 0; a < batchHeader.stride; ++a) { Dy::createFinalizeSolverContacts(contactDescs[currentContactDescIdx + a], cb, invDt, bounceThreshold, frictionOffsetThreshold, correlationDistance, 0.f, allocator, Z); } } PxU8 type = *contactDescs[currentContactDescIdx].desc->constraint; if (type == DY_SC_TYPE_STATIC_CONTACT) { //Check if any block of constraints is classified as type static (single) contact constraint. //If they are, iterate over all constraints grouped with it and switch to "dynamic" contact constraint //type if there's a dynamic contact constraint in the group. for (PxU32 c = 1; c < batchHeader.stride; ++c) { if (*contactDescs[currentContactDescIdx + c].desc->constraint == DY_SC_TYPE_RB_CONTACT) { type = DY_SC_TYPE_RB_CONTACT; break; } } } batchHeader.constraintType = type; currentContactDescIdx += batchHeader.stride; } return true; } bool immediate::PxCreateJointConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, const PxReal dt, const PxReal invDt) { PX_ASSERT(dt > 0.f); PX_ASSERT(invDt > 0.f && PxIsFinite(invDt)); Dy::SolverConstraintPrepState::Enum state = Dy::SolverConstraintPrepState::eUNBATCHABLE; PxU32 currentDescIdx = 0; for (PxU32 i = 0; i < nbHeaders; ++i) { PxConstraintBatchHeader& batchHeader = batchHeaders[i]; PxU8 type = DY_SC_TYPE_BLOCK_1D; if (batchHeader.stride == 4) { PxU32 totalRows = 0; PxU32 maxRows = 0; bool batchable = true; for (PxU32 a = 0; a < batchHeader.stride; ++a) { if (jointDescs[currentDescIdx + a].numRows == 0) { batchable = false; break; } totalRows += jointDescs[currentDescIdx + a].numRows; maxRows = PxMax(maxRows, jointDescs[currentDescIdx + a].numRows); } if (batchable) { state = Dy::setupSolverConstraint4 (jointDescs + currentDescIdx, dt, invDt, totalRows, allocator, maxRows); } } if (state == Dy::SolverConstraintPrepState::eUNBATCHABLE) { type = DY_SC_TYPE_RB_1D; Cm::SpatialVectorF Z[Dy::DY_ARTICULATION_MAX_SIZE]; for (PxU32 a = 0; a < batchHeader.stride; ++a) { // PT: TODO: And "isExtended" is already computed in Dy::ConstraintHelper::setupSolverConstraint PxSolverConstraintDesc& desc = *jointDescs[currentDescIdx + a].desc; const bool isExtended = desc.linkIndexA != PxSolverConstraintDesc::NO_LINK || desc.linkIndexB != PxSolverConstraintDesc::NO_LINK; if(isExtended) type = DY_SC_TYPE_EXT_1D; Dy::ConstraintHelper::setupSolverConstraint(jointDescs[currentDescIdx + a], allocator, dt, invDt, Z); } } batchHeader.constraintType = type; currentDescIdx += batchHeader.stride; } return true; } template static bool PxCreateJointConstraintsWithShadersT(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, ParamsT* params, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, const PxReal dt, const PxReal invDt) { Px1DConstraint allRows[Dy::MAX_CONSTRAINT_ROWS * 4]; //Runs shaders to fill in rows... PxU32 currentDescIdx = 0; for(PxU32 i=0; i(constraints[i]); npConstraint->updateConstants(); Sc::ConstraintCore& core = npConstraint->getScbConstraint().getScConstraint(); *prep = core.getPxConnector()->getPrep(); *constantBlock = core.getPxConnector()->getConstantBlock(); return core.getFlags() & PxConstraintFlag::eENABLE_EXTENDED_LIMITS; } }; return PxCreateJointConstraintsWithShadersT(batchHeaders, nbHeaders, constraints, jointDescs, allocator, dt, invDt); } bool immediate::PxCreateJointConstraintsWithImmediateShaders(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxImmediateConstraint* constraints, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, const PxReal dt, const PxReal invDt) { class immConstraintAdapter { public: static PX_FORCE_INLINE bool getData(PxImmediateConstraint* constraints, PxU32 i, PxConstraintSolverPrep* prep, const void** constantBlock) { const PxImmediateConstraint& ic = constraints[i]; *prep = ic.prep; *constantBlock = ic.constantBlock; return false; } }; return PxCreateJointConstraintsWithShadersT(batchHeaders, nbHeaders, constraints, jointDescs, allocator, dt, invDt); } /*static*/ PX_FORCE_INLINE bool PxIsZero(const PxSolverBody* bodies, PxU32 nbBodies) { for(PxU32 i=0; i 0); PX_ASSERT(nbVelocityIterations > 0); PX_ASSERT(PxIsZero(solverBodies, nbSolverBodies)); //Ensure that solver body velocities have been zeroed before solving //Stage 1: solve the position iterations... Dy::SolveBlockMethod* solveTable = Dy::getSolveBlockTable(); Dy::SolveBlockMethod* solveConcludeTable = Dy::getSolverConcludeBlockTable(); Dy::SolveWriteBackBlockMethod* solveWritebackTable = Dy::getSolveWritebackBlockTable(); Dy::SolverContext cache; cache.mThresholdStreamIndex = 0; cache.mThresholdStreamLength = 0xFFFFFFF; PX_ASSERT(nbPositionIterations > 0); PX_ASSERT(nbVelocityIterations > 0); Cm::SpatialVectorF Z[DY_ARTICULATION_MAX_SIZE]; Cm::SpatialVectorF deltaV[DY_ARTICULATION_MAX_SIZE]; cache.Z = Z; cache.deltaV = deltaV; struct Articulations { static PX_FORCE_INLINE void solveInternalConstraints(const float dt, const float invDt, const PxU32 nbSolverArticulations, Dy::ArticulationV** solverArticulations, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV, bool velIter) { for(PxU32 a=0;a(solverArticulations[a]); immArt->immSolveInternalConstraints(dt, invDt, Z, deltaV, 0.f, velIter, false); } } }; for(PxU32 i=nbPositionIterations; i>1; --i) { cache.doFriction = i <= 3; for(PxU32 a=0; a1; --i) { for(PxU32 a=0; a 0.f); PX_ASSERT(toleranceLength > 0.f); PX_ASSERT(contactDistance > 0.f); Gu::ContactBuffer contactBuffer; for (PxU32 i = 0; i < nbPairs; ++i) { contactBuffer.count = 0; PxGeometryType::Enum type0 = geom0[i]->getType(); PxGeometryType::Enum type1 = geom1[i]->getType(); const PxGeometry* tempGeom0 = geom0[i]; const PxGeometry* tempGeom1 = geom1[i]; PX_ALIGN(16, PxTransform) transform0 = pose0[i]; PX_ALIGN(16, PxTransform) transform1 = pose1[i]; const bool bSwap = type0 > type1; if (bSwap) { const PxGeometry* temp = tempGeom0; tempGeom0 = geom1[i]; tempGeom1 = temp; PxGeometryType::Enum tempType = type0; type0 = type1; type1 = tempType; transform1 = pose0[i]; transform0 = pose1[i]; } const Gu::GeometryUnion geomUnion0(*tempGeom0); const Gu::GeometryUnion geomUnion1(*tempGeom1); //Now work out which type of PCM we need... Gu::Cache& cache = static_cast(contactCache[i]); bool needsMultiManifold = type1 > PxGeometryType::eCONVEXMESH; Gu::NarrowPhaseParams params(contactDistance, meshContactMargin, toleranceLength); if (needsMultiManifold) { Gu::MultiplePersistentContactManifold multiManifold; if (cache.isMultiManifold()) { multiManifold.fromBuffer(reinterpret_cast(&cache.getMultipleManifold())); } else { multiManifold.initialize(); } cache.setMultiManifold(&multiManifold); //Do collision detection, then write manifold out... g_PCMContactMethodTable[type0][type1](geomUnion0, geomUnion1, transform0, transform1, params, cache, contactBuffer, NULL); const PxU32 size = (sizeof(Gu::MultiPersistentManifoldHeader) + multiManifold.mNumManifolds * sizeof(Gu::SingleManifoldHeader) + multiManifold.mNumTotalContacts * sizeof(Gu::CachedMeshPersistentContact)); PxU8* buffer = allocator.allocateCacheData(size); multiManifold.toBuffer(buffer); cache.setMultiManifold(buffer); } else { //Allocate the type of manifold we need again... Gu::PersistentContactManifold* oldManifold = NULL; if (cache.isManifold()) oldManifold = &cache.getManifold(); //Allocates and creates the PCM... createCache(cache, type0, type1, allocator); //Copy PCM from old to new manifold... if (oldManifold) { Gu::PersistentContactManifold& manifold = cache.getManifold(); manifold.mRelativeTransform = oldManifold->mRelativeTransform; manifold.mNumContacts = oldManifold->mNumContacts; manifold.mNumWarmStartPoints = oldManifold->mNumWarmStartPoints; manifold.mAIndice[0] = oldManifold->mAIndice[0]; manifold.mAIndice[1] = oldManifold->mAIndice[1]; manifold.mAIndice[2] = oldManifold->mAIndice[2]; manifold.mAIndice[3] = oldManifold->mAIndice[3]; manifold.mBIndice[0] = oldManifold->mBIndice[0]; manifold.mBIndice[1] = oldManifold->mBIndice[1]; manifold.mBIndice[2] = oldManifold->mBIndice[2]; manifold.mBIndice[3] = oldManifold->mBIndice[3]; PxMemCopy(manifold.mContactPoints, oldManifold->mContactPoints, sizeof(Gu::PersistentContact)*manifold.mNumContacts); } g_PCMContactMethodTable[type0][type1](geomUnion0, geomUnion1, transform0, transform1, params, cache, contactBuffer, NULL); } if (bSwap) { for (PxU32 a = 0; a < contactBuffer.count; ++a) { contactBuffer.contacts[a].normal = -contactBuffer.contacts[a].normal; } } if (contactBuffer.count != 0) { //Record this contact pair... contactRecorder.recordContacts(contactBuffer.contacts, contactBuffer.count, i); } } return true; } immArticulation::immArticulation(const PxFeatherstoneArticulationData& data) : FeatherstoneArticulation(this), mFlags (data.flags), mImmDirty (true) { // From Sc::ArticulationSim ctor, not sure why it's not done in FeatherstoneArticulation ctor setDirty(true); // PT: TODO: we only need the flags here, maybe drop the solver desc? getSolverDesc().initData(NULL, &mFlags); } immArticulation::~immArticulation() { } void immArticulation::initJointCore(Dy::ArticulationJointCore& core, const PxFeatherstoneArticulationLinkData& data) { // From Sc::ArticulationJointCore, this should once again just be done in a Dy::ArticulationJointCore ctor // =====> hmmm there is a ctor, but it initializes things to different values like PxArticulationJointType::eFIX.... // TODO: replicate ctor here { // TODO: in this one I initialized all data but I don't actually know which one is needed core.parentPose = data.inboundJoint.parentPose; core.childPose = data.inboundJoint.childPose; PX_ASSERT(core.parentPose.isValid()); PX_ASSERT(core.childPose.isValid()); core.dirtyFlag |= Dy::ArticulationJointCoreDirtyFlag::ePOSE; // TODO: is this used in the new articulations? core.targetPosition = PxQuat(PxIdentity); core.targetVelocity = PxVec3(0.f); core.driveType = PxArticulationJointDriveType::eTARGET; core.spring = 0.0f; core.damping = 0.0f; core.internalCompliance = 1.0f; core.externalCompliance = 1.0f; const PxU32* binP = reinterpret_cast(data.inboundJoint.targetPos); const PxU32* binV = reinterpret_cast(data.inboundJoint.targetVel); for(PxU32 i=0; iinit( bodyPose, data.inverseInertia, data.inverseMass, 0.0f, 0.0f, data.linearDamping, data.angularDamping, data.maxLinearVelocitySq, data.maxAngularVelocitySq); // PT: TODO: consider exposing all used data to immediate mode API (PX-1398) // bodyCore->maxPenBias = -1e32f; // <= this one is related to setMaxDepenetrationVelocity // bodyCore->linearVelocity = PxVec3(0.0f); // bodyCore->angularVelocity = PxVec3(0.0f); // bodyCore->linearVelocity = PxVec3(0.0f, 10.0f, 0.0f); // bodyCore->angularVelocity = PxVec3(0.0f, 10.0f, 0.0f); } /* PX_ASSERT((((index==0) && (joint == 0)) && (parent == 0)) || (((index!=0) && joint) && (parent && (parent->getArticulation() == this))));*/ // PT: TODO: add ctors everywhere ArticulationLink& link = mLinks.insert(); // void BodySim::postActorFlagChange(PxU32 oldFlags, PxU32 newFlags) bodyCore->disableGravity = data.disableGravity; link.bodyCore = bodyCore; link.children = 0; /* bool shouldSleep; bool currentlyAsleep; bool bodyReadyForSleep = body.checkSleepReadinessBesidesWakeCounter(); PxReal wakeCounter = getCore().getWakeCounter();*/ // const PxU32 parentIndex = data.mParent; const PxU32 parentIndex = getLinkIndex(data.parent); // const bool isRoot = parentIndex==0xffffffff; const bool isRoot = !data.parent; // const bool isRoot = isArticulationRootLink(data.mParent); if(!isRoot) /* if(parent)*/ { /* currentlyAsleep = !mBodies[0]->isActive(); shouldSleep = currentlyAsleep && bodyReadyForSleep; PxU32 parentIndex = findBodyIndex(*parent);*/ link.parent = parentIndex; link.pathToRoot = mLinks[parentIndex].pathToRoot | ArticulationBitField(1)<getCore().getCore(); mLinks[parentIndex].children |= ArticulationBitField(1)<> 32); const PxU32 depth = Ps::bitCount(low) + Ps::bitCount(high); PxU32 mMaxDepth = PxMax(depth, mMaxDepth); mImpl.setMaxDepth(mMaxDepth);*/ // mLLArticulation->setMaxDepth(mMaxDepth); /* if (currentlyAsleep && (!shouldSleep)) { for(PxU32 i=0; i < (mBodies.size() - 1); i++) mBodies[i]->internalWakeUpArticulationLink(wakeCounter); } body.setArticulation(this, wakeCounter, shouldSleep, index);*/ return index; } void immArticulation::complete() { // Based on Sc::ArticulationSim::checkResize() if(!mImmDirty) return; mImmDirty = false; setupLinks(mLinks.size(), const_cast(mLinks.begin())); } static bool gRegistration = false; void immediate::PxRegisterImmediateArticulations() { Dy::PxvRegisterArticulationsReducedCoordinate(); gRegistration = true; } Dy::ArticulationV* immediate::PxCreateFeatherstoneArticulation(const PxFeatherstoneArticulationData& data) { PX_ASSERT(gRegistration && "Please call PxRegisterImmediateArticulations() before creating immediate articulations."); void* memory = physx::shdfnd::AlignedAllocator<64>().allocate(sizeof(immArticulation), __FILE__, __LINE__); new (memory) immArticulation(data); return reinterpret_cast(memory); } void immediate::PxReleaseArticulation(Dy::ArticulationV* articulation) { if(!articulation) return; immArticulation* immArt = static_cast(articulation); immArt->~immArticulation(); physx::shdfnd::AlignedAllocator<64>().deallocate(articulation); } // PT: TODO: add test vs DY_ARTICULATION_MAX_SIZE Dy::ArticulationLinkHandle immediate::PxAddArticulationLink(Dy::ArticulationV* articulation, const PxFeatherstoneArticulationLinkData& data, bool isLastLink) { if(!articulation) return 0; immArticulation* immArt = static_cast(articulation); const PxU32 id = immArt->addLink(data); if(isLastLink) immArt->complete(); PX_ASSERT(!(size_t(articulation) & 63)); return size_t(articulation) | id; } PxArticulationCache* immediate::PxCreateArticulationCache(Dy::ArticulationV* articulation) { immArticulation *immArt = static_cast(articulation); immArt->complete(); const PxU32 totalDofs = immArt->getDofs(); const PxU32 jointCount = immArt->getBodyCount() - 1; PxU32 totalSize = sizeof(Cm::SpatialVector) * immArt->getBodyCount() //external force + sizeof(PxReal) * (6 + totalDofs) * ((1 + jointCount) * 6)//offset to end of dense jacobian (assuming free floating base) + sizeof(PxReal) * totalDofs * totalDofs //mass matrix + sizeof(PxReal) * totalDofs * 4 //jointVelocity, jointAcceleration, jointPosition, joint force + sizeof(PxArticulationRootLinkData); PxU8* tCache = reinterpret_cast(PX_ALLOC(totalSize, "Articulation cache")); PxMemZero(tCache, totalSize); PxArticulationCache* cache = reinterpret_cast(tCache); PxU32 offset = sizeof(PxArticulationCache); cache->externalForces = reinterpret_cast(tCache + offset); offset += sizeof(PxSpatialForce) * immArt->getBodyCount(); cache->denseJacobian = reinterpret_cast(tCache + offset); offset += sizeof(PxReal) * (6 + totalDofs) * ((1 + jointCount) * 6); //size of dense jacobian assuming free floating base link. cache->massMatrix = reinterpret_cast(tCache + offset); offset += sizeof(PxReal) *totalDofs * totalDofs; cache->jointVelocity = reinterpret_cast(tCache + offset); offset += sizeof(PxReal) * totalDofs; cache->jointAcceleration = reinterpret_cast(tCache + offset); offset += sizeof(PxReal) * totalDofs; cache->jointPosition = reinterpret_cast(tCache + offset); offset += sizeof(PxReal) * totalDofs; cache->jointForce = reinterpret_cast(tCache + offset); offset += sizeof(PxReal) * totalDofs; cache->rootLinkData = reinterpret_cast(tCache + offset); cache->coefficientMatrix = NULL; cache->lambda =NULL; const PxU32 scratchMemorySize = sizeof(Cm::SpatialVectorF) * immArt->getBodyCount() * 5 //motionVelocity, motionAccelerations, coriolisVectors, spatialZAVectors, externalAccels; + sizeof(Dy::SpatialMatrix) * immArt->getBodyCount() //compositeSpatialInertias; + sizeof(PxReal) * totalDofs * 5; //jointVelocity, jointAcceleration, jointForces, jointPositions, jointFrictionForces void* scratchMemory = PX_ALLOC(scratchMemorySize, "Cache scratch memory"); cache->scratchMemory = scratchMemory; cache->scratchAllocator = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(PxcScratchAllocator), "PxScrachAllocator"), PxcScratchAllocator)(); reinterpret_cast(cache->scratchAllocator)->setBlock(scratchMemory, scratchMemorySize); return cache; } void immediate::PxCopyInternalStateToArticulationCache(Dy::ArticulationV* articulation, PxArticulationCache& cache, PxArticulationCacheFlags flag) { immArticulation *immArt = static_cast(articulation); immArt->copyInternalStateToCache(cache, flag); } void immediate::PxApplyArticulationCache(Dy::ArticulationV* articulation, PxArticulationCache& cache, PxArticulationCacheFlags flag) { immArticulation *immArt = static_cast(articulation); immArt->applyCache(cache, flag); } void immediate::PxReleaseArticulationCache(PxArticulationCache& cache) { if (cache.scratchAllocator) { PxcScratchAllocator* scratchAlloc = reinterpret_cast(cache.scratchAllocator); scratchAlloc->~PxcScratchAllocator(); PX_FREE_AND_RESET(cache.scratchAllocator); } if (cache.scratchMemory) PX_FREE_AND_RESET(cache.scratchMemory); PX_FREE(&cache); } void immediate::PxComputeUnconstrainedVelocities(Dy::ArticulationV* articulation, const PxVec3& gravity, const PxReal dt) { if(!articulation) return; immArticulation* immArt = static_cast(articulation); immArt->complete(); immArt->immComputeUnconstrainedVelocities(dt, gravity); } void immediate::PxUpdateArticulationBodies(Dy::ArticulationV* articulation, const PxReal dt) { if(!articulation) return; FeatherstoneArticulation::updateBodies(static_cast(articulation), dt, true); } void immediate::PxComputeUnconstrainedVelocitiesTGS(Dy::ArticulationV* articulation, const PxVec3& gravity, const PxReal dt, const PxReal totalDt, const PxReal invDt, const PxReal invTotalDt) { if (!articulation) return; immArticulation* immArt = static_cast(articulation); immArt->complete(); immArt->immComputeUnconstrainedVelocitiesTGS(dt, totalDt, invDt, invTotalDt, gravity); } void immediate::PxUpdateArticulationBodiesTGS(Dy::ArticulationV* articulation, const PxReal dt) { if (!articulation) return; FeatherstoneArticulation::updateBodies(static_cast(articulation), dt, false); } Dy::ArticulationV* immediate::PxGetLinkArticulation(const Dy::ArticulationLinkHandle link) { return Dy::getArticulation(link); } PxU32 immediate::PxGetLinkIndex(const Dy::ArticulationLinkHandle link) { return Dy::getLinkIndex(link); } static void copyLinkData(PxLinkData& data, const immArticulation* immArt, PxU32 index) { data.pose = immArt->mBodyCores[index].body2World; // data.linearVelocity = immArt->mBodyCores[index].linearVelocity; // data.angularVelocity = immArt->mBodyCores[index].angularVelocity; const Cm::SpatialVectorF& velocity = immArt->getArticulationData().getMotionVelocity(index); data.linearVelocity = velocity.bottom; data.angularVelocity = velocity.top; } static PX_FORCE_INLINE const immArticulation* getFromLink(const Dy::ArticulationLinkHandle link, PxU32& index) { const Dy::ArticulationV* articulation = Dy::getArticulation(link); if(!articulation) return NULL; const immArticulation* immArt = static_cast(articulation); index = Dy::getLinkIndex(link); if(index>=immArt->mLinks.size()) return NULL; return immArt; } bool immediate::PxGetLinkData(const Dy::ArticulationLinkHandle link, PxLinkData& data) { PxU32 index; const immArticulation* immArt = getFromLink(link, index); if(!immArt) return false; copyLinkData(data, immArt, index); return true; } PxU32 immediate::PxGetAllLinkData(const Dy::ArticulationV* articulation, PxLinkData* data) { if(!articulation) return 0; const immArticulation* immArt = static_cast(articulation); const PxU32 nb = immArt->mLinks.size(); if(data) { for(PxU32 i=0;imBodyCores[index].inverseInertia; data.inverseMass = immArt->mBodyCores[index].inverseMass; data.linearDamping = immArt->mBodyCores[index].linearDamping; data.angularDamping = immArt->mBodyCores[index].angularDamping; data.maxLinearVelocitySq = immArt->mBodyCores[index].maxLinearVelocitySq; data.maxAngularVelocitySq = immArt->mBodyCores[index].maxAngularVelocitySq; data.disableGravity = immArt->mBodyCores[index].disableGravity!=0; return true; } bool immediate::PxSetMutableLinkData(Dy::ArticulationLinkHandle link, const PxMutableLinkData& data) { PxU32 index; immArticulation* immArt = const_cast(getFromLink(link, index)); if(!immArt) return false; immArt->mBodyCores[index].inverseInertia = data.inverseInertia; // See Sc::BodyCore::setInverseInertia immArt->mBodyCores[index].inverseMass = data.inverseMass; // See Sc::BodyCore::setInverseMass immArt->mBodyCores[index].linearDamping = data.linearDamping; // See Sc::BodyCore::setLinearDamping immArt->mBodyCores[index].angularDamping = data.angularDamping; // See Sc::BodyCore::setAngularDamping immArt->mBodyCores[index].maxLinearVelocitySq = data.maxLinearVelocitySq; // See Sc::BodyCore::setMaxLinVelSq immArt->mBodyCores[index].maxAngularVelocitySq = data.maxAngularVelocitySq; // See Sc::BodyCore::setMaxAngVelSq immArt->mBodyCores[index].disableGravity = data.disableGravity; // See BodySim::postActorFlagChange return true; } bool immediate::PxGetJointData(const Dy::ArticulationLinkHandle link, PxFeatherstoneArticulationJointData& data) { PxU32 index; const immArticulation* immArt = getFromLink(link, index); if(!immArt) return false; const Dy::ArticulationJointCore& core = immArt->mArticulationJointCore[index]; data.parentPose = core.parentPose; data.childPose = core.childPose; data.type = PxArticulationJointType::Enum(core.jointType); data.frictionCoefficient = core.frictionCoefficient; data.maxJointVelocity = core.maxJointVelocity; for(PxU32 i=0;i(getFromLink(link, index)); if(!immArt) return false; Dy::ArticulationJointCore& core = immArt->mArticulationJointCore[index]; bool dirty = false; if(!samePoses(core.parentPose, data.parentPose)) { core.parentPose = data.parentPose; core.dirtyFlag |= Dy::ArticulationJointCoreDirtyFlag::ePOSE; dirty = true; } if(!samePoses(core.childPose, data.childPose)) { core.childPose = data.childPose; core.dirtyFlag |= Dy::ArticulationJointCoreDirtyFlag::ePOSE; dirty = true; } if(core.jointType!=PxU8(data.type)) { core.jointType = PxU8(data.type); dirty = true; } core.frictionCoefficient = data.frictionCoefficient; core.maxJointVelocity = data.maxJointVelocity; for(PxU32 i=0;isetDirty(true); return true; } namespace physx { namespace Dy { void copyToSolverBodyDataStep(const PxVec3& linearVelocity, const PxVec3& angularVelocity, const PxReal invMass, const PxVec3& invInertia, const PxTransform& globalPose, const PxReal maxDepenetrationVelocity, const PxReal maxContactImpulse, const PxU32 nodeIndex, const PxReal reportThreshold, const PxReal maxAngVelSq, PxU32 lockFlags, bool isKinematic, PxTGSSolverBodyVel& solverVel, PxTGSSolverBodyTxInertia& solverBodyTxInertia, PxTGSSolverBodyData& solverBodyData); void integrateCoreStep(PxTGSSolverBodyVel& vel, PxTGSSolverBodyTxInertia& txInertia, const PxF32 dt); } } void immediate::PxConstructSolverBodiesTGS(const PxRigidBodyData* inRigidData, PxTGSSolverBodyVel* outSolverBodyVel, PxTGSSolverBodyTxInertia* outSolverBodyTxInertia, PxTGSSolverBodyData* outSolverBodyData, const PxU32 nbBodies, const PxVec3& gravity, const PxReal dt) { for (PxU32 a = 0; a(solverBodies), nbBodies, sizeof(PxTGSSolverBodyVel)); return BatchConstraints(solverConstraintDescs, nbConstraints, outBatchHeaders, outOrderedConstraintDescs, classification); } else { ExtendedRigidBodyClassification classification(reinterpret_cast(solverBodies), nbBodies, sizeof(PxTGSSolverBodyVel), articulations, nbArticulations); return BatchConstraints(solverConstraintDescs, nbConstraints, outBatchHeaders, outOrderedConstraintDescs, classification); } } bool immediate::PxCreateContactConstraintsTGS(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxTGSSolverContactDesc* contactDescs, PxConstraintAllocator& allocator, const PxReal invDt, const PxReal invTotalDt, const PxReal bounceThreshold, const PxReal frictionOffsetThreshold, const PxReal correlationDistance) { PX_ASSERT(invDt > 0.f && PxIsFinite(invDt)); PX_ASSERT(bounceThreshold < 0.f); PX_ASSERT(frictionOffsetThreshold > 0.f); PX_ASSERT(correlationDistance > 0.f); Dy::SolverConstraintPrepState::Enum state = Dy::SolverConstraintPrepState::eUNBATCHABLE; Dy::CorrelationBuffer cb; PxU32 currentContactDescIdx = 0; for (PxU32 i = 0; i < nbHeaders; ++i) { PxConstraintBatchHeader& batchHeader = batchHeaders[i]; if (batchHeader.stride == 4) { PxU32 totalContacts = contactDescs[currentContactDescIdx].numContacts + contactDescs[currentContactDescIdx + 1].numContacts + contactDescs[currentContactDescIdx + 2].numContacts + contactDescs[currentContactDescIdx + 3].numContacts; if (totalContacts <= 64) { state = Dy::createFinalizeSolverContacts4Step(cb, contactDescs + currentContactDescIdx, invDt, invTotalDt, bounceThreshold, frictionOffsetThreshold, correlationDistance, 0.f, allocator); } } if (state == Dy::SolverConstraintPrepState::eUNBATCHABLE) { for (PxU32 a = 0; a < batchHeader.stride; ++a) { Dy::createFinalizeSolverContactsStep(contactDescs[currentContactDescIdx + a], cb, invDt, invTotalDt, bounceThreshold, frictionOffsetThreshold, correlationDistance, allocator); } } PxU8 type = *contactDescs[currentContactDescIdx].desc->constraint; if (type == DY_SC_TYPE_STATIC_CONTACT) { //Check if any block of constraints is classified as type static (single) contact constraint. //If they are, iterate over all constraints grouped with it and switch to "dynamic" contact constraint //type if there's a dynamic contact constraint in the group. for (PxU32 c = 1; c < batchHeader.stride; ++c) { if (*contactDescs[currentContactDescIdx + c].desc->constraint == DY_SC_TYPE_RB_CONTACT) { type = DY_SC_TYPE_RB_CONTACT; break; } } } batchHeader.constraintType = type; currentContactDescIdx += batchHeader.stride; } return true; } bool immediate::PxCreateJointConstraintsTGS(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxTGSSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, const PxReal dt, const PxReal totalDt, const PxReal invDt, const PxReal invTotalDt, const PxReal lengthScale) { PX_ASSERT(dt > 0.f); PX_ASSERT(invDt > 0.f && PxIsFinite(invDt)); Dy::SolverConstraintPrepState::Enum state = Dy::SolverConstraintPrepState::eUNBATCHABLE; PxU32 currentDescIdx = 0; for (PxU32 i = 0; i < nbHeaders; ++i) { PxConstraintBatchHeader& batchHeader = batchHeaders[i]; PxU8 type = DY_SC_TYPE_BLOCK_1D; if (batchHeader.stride == 4) { PxU32 totalRows = 0; PxU32 maxRows = 0; bool batchable = true; for (PxU32 a = 0; a < batchHeader.stride; ++a) { if (jointDescs[currentDescIdx + a].numRows == 0) { batchable = false; break; } totalRows += jointDescs[currentDescIdx + a].numRows; maxRows = PxMax(maxRows, jointDescs[currentDescIdx + a].numRows); } if (batchable) { state = Dy::setupSolverConstraintStep4 (jointDescs + currentDescIdx, dt, totalDt, invDt, invTotalDt, totalRows, allocator, maxRows, lengthScale); } } if (state == Dy::SolverConstraintPrepState::eUNBATCHABLE) { type = DY_SC_TYPE_RB_1D; for (PxU32 a = 0; a < batchHeader.stride; ++a) { // PT: TODO: And "isExtended" is already computed in Dy::ConstraintHelper::setupSolverConstraint PxSolverConstraintDesc& desc = *jointDescs[currentDescIdx + a].desc; const bool isExtended = desc.linkIndexA != PxSolverConstraintDesc::NO_LINK || desc.linkIndexB != PxSolverConstraintDesc::NO_LINK; if (isExtended) type = DY_SC_TYPE_EXT_1D; Dy::setupSolverConstraintStep(jointDescs[currentDescIdx + a], allocator, dt, totalDt, invDt, invTotalDt, lengthScale); } } batchHeader.constraintType = type; currentDescIdx += batchHeader.stride; } return true; } template static bool PxCreateJointConstraintsWithShadersTGS_T(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, ParamsT* params, PxTGSSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, const PxReal dt, const PxReal totalDt, const PxReal invDt, const PxReal invTotalDt, const PxReal lengthScale) { Px1DConstraint allRows[Dy::MAX_CONSTRAINT_ROWS * 4]; //Runs shaders to fill in rows... PxU32 currentDescIdx = 0; for (PxU32 i = 0; i(constraints[i]); npConstraint->updateConstants(); Sc::ConstraintCore& core = npConstraint->getScbConstraint().getScConstraint(); *prep = core.getPxConnector()->getPrep(); *constantBlock = core.getPxConnector()->getConstantBlock(); return core.getFlags() & PxConstraintFlag::eENABLE_EXTENDED_LIMITS; } }; return PxCreateJointConstraintsWithShadersTGS_T(batchHeaders, nbBatchHeaders, constraints, jointDescs, allocator, dt, totalDt, invDt, invTotalDt, lengthScale); } bool immediate::PxCreateJointConstraintsWithImmediateShadersTGS(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxImmediateConstraint* constraints, PxTGSSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, const PxReal dt, const PxReal totalDt, const PxReal invDt, const PxReal invTotalDt, const PxReal lengthScale) { class immConstraintAdapter { public: static PX_FORCE_INLINE bool getData(PxImmediateConstraint* constraints, PxU32 i, PxConstraintSolverPrep* prep, const void** constantBlock) { const PxImmediateConstraint& ic = constraints[i]; *prep = ic.prep; *constantBlock = ic.constantBlock; return false; } }; return PxCreateJointConstraintsWithShadersTGS_T(batchHeaders, nbHeaders, constraints, jointDescs, allocator, dt, totalDt, invDt, invTotalDt, lengthScale); } void immediate::PxSolveConstraintsTGS(const PxConstraintBatchHeader* batchHeaders, const PxU32 nbBatchHeaders, const PxSolverConstraintDesc* solverConstraintDescs, PxTGSSolverBodyVel* solverBodies, PxTGSSolverBodyTxInertia* txInertias, const PxU32 nbSolverBodies, const PxU32 nbPositionIterations, const PxU32 nbVelocityIterations, const float dt, const float invDt, const PxU32 nbSolverArticulations, Dy::ArticulationV** solverArticulations) { PX_UNUSED(solverBodies); PX_UNUSED(nbSolverBodies); PX_ASSERT(nbPositionIterations > 0); PX_ASSERT(nbVelocityIterations > 0); //Stage 1: solve the position iterations... Dy::TGSSolveBlockMethod* solveTable = Dy::g_SolveTGSMethods; Dy::TGSSolveConcludeMethod* solveConcludeTable = Dy::g_SolveConcludeTGSMethods; Dy::TGSWriteBackMethod* writebackTable = Dy::g_WritebackTGSMethods; Dy::SolverContext cache; cache.mThresholdStreamIndex = 0; cache.mThresholdStreamLength = 0xFFFFFFF; PX_ASSERT(nbPositionIterations > 0); PX_ASSERT(nbVelocityIterations > 0); Cm::SpatialVectorF Z[DY_ARTICULATION_MAX_SIZE]; Cm::SpatialVectorF deltaV[DY_ARTICULATION_MAX_SIZE]; cache.Z = Z; cache.deltaV = deltaV; struct Articulations { static PX_FORCE_INLINE void solveInternalConstraints(const float dt, const float invDt, const PxU32 nbSolverArticulations, Dy::ArticulationV** solverArticulations, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV, const PxReal elapsedTime, bool velIter) { for (PxU32 a = 0; a(solverArticulations[a]); immArt->immSolveInternalConstraints(dt, invDt, Z, deltaV, elapsedTime, velIter, true); } } static PX_FORCE_INLINE void stepArticulations(const float dt, const PxU32 nbSolverArticulations, Dy::ArticulationV** solverArticulations, Cm::SpatialVectorF* deltaV) { for (PxU32 a = 0; a(solverArticulations[a]); immArt->recordDeltaMotion(immArt->getSolverDesc(), dt, deltaV); } } }; PxReal elapsedTime = 0.f; for (PxU32 i = nbPositionIterations; i>1; --i) { Articulations::solveInternalConstraints(dt, invDt, nbSolverArticulations, solverArticulations, Z, deltaV, elapsedTime, false); cache.doFriction = true; for (PxU32 a = 0; a1; --i) { Articulations::solveInternalConstraints(dt, invDt, nbSolverArticulations, solverArticulations, Z, deltaV, elapsedTime, true); for (PxU32 a = 0; a