// // 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. #ifndef PX_PHYSICS_NP_RIGIDBODY_TEMPLATE #define PX_PHYSICS_NP_RIGIDBODY_TEMPLATE #include "NpRigidActorTemplate.h" #include "ScbBody.h" #include "NpPhysics.h" #include "NpShape.h" #include "NpScene.h" namespace physx { PX_INLINE PxVec3 invertDiagInertia(const PxVec3& m) { return PxVec3( m.x == 0.0f ? 0.0f : 1.0f/m.x, m.y == 0.0f ? 0.0f : 1.0f/m.y, m.z == 0.0f ? 0.0f : 1.0f/m.z); } #if PX_ENABLE_DEBUG_VISUALIZATION /* given the diagonal of the body space inertia tensor, and the total mass this returns the body space AABB width, height and depth of an equivalent box */ PX_INLINE PxVec3 getDimsFromBodyInertia(const PxVec3& inertiaMoments, PxReal mass) { const PxVec3 inertia = inertiaMoments * (6.0f/mass); return PxVec3( PxSqrt(PxAbs(- inertia.x + inertia.y + inertia.z)), PxSqrt(PxAbs(+ inertia.x - inertia.y + inertia.z)), PxSqrt(PxAbs(+ inertia.x + inertia.y - inertia.z))); } #endif template class NpRigidBodyTemplate : public NpRigidActorTemplate { private: typedef NpRigidActorTemplate RigidActorTemplateClass; public: // PX_SERIALIZATION NpRigidBodyTemplate(PxBaseFlags baseFlags) : RigidActorTemplateClass(baseFlags), mBody(PxEmpty) {} //~PX_SERIALIZATION virtual ~NpRigidBodyTemplate(); //--------------------------------------------------------------------------------- // PxRigidActor implementation //--------------------------------------------------------------------------------- // The rule is: If an API method is used somewhere in here, it has to be redeclared, else GCC whines virtual PxTransform getGlobalPose() const = 0; //--------------------------------------------------------------------------------- // PxRigidBody implementation //--------------------------------------------------------------------------------- // Center of mass pose virtual PxTransform getCMassLocalPose() const; // Mass virtual void setMass(PxReal mass); virtual PxReal getMass() const; virtual PxReal getInvMass() const; virtual void setMassSpaceInertiaTensor(const PxVec3& m); virtual PxVec3 getMassSpaceInertiaTensor() const; virtual PxVec3 getMassSpaceInvInertiaTensor() const; // Velocity virtual PxVec3 getLinearVelocity() const; virtual PxVec3 getAngularVelocity() const; virtual bool attachShape(PxShape& shape); //--------------------------------------------------------------------------------- // Miscellaneous //--------------------------------------------------------------------------------- NpRigidBodyTemplate(PxType concreteType, PxBaseFlags baseFlags, const PxActorType::Enum type, const PxTransform& bodyPose); PX_FORCE_INLINE const Scb::Body& getScbBodyFast() const { return mBody; } // PT: important: keep returning an address here (else update prefetch in SceneQueryManager::addShapes) PX_FORCE_INLINE Scb::Body& getScbBodyFast() { return mBody; } // PT: important: keep returning an address here (else update prefetch in SceneQueryManager::addShapes) PX_FORCE_INLINE Scb::Actor& getScbActorFast() { return mBody; } PX_FORCE_INLINE const Scb::Actor& getScbActorFast() const { return mBody; } // Flags virtual void setRigidBodyFlag(PxRigidBodyFlag::Enum, bool value); virtual void setRigidBodyFlags(PxRigidBodyFlags inFlags); PX_FORCE_INLINE PxRigidBodyFlags getRigidBodyFlagsFast() const { return getScbBodyFast().getFlags(); } virtual PxRigidBodyFlags getRigidBodyFlags() const { NP_READ_CHECK(NpActor::getOwnerScene(*this)); return getRigidBodyFlagsFast(); } virtual void setMinCCDAdvanceCoefficient(PxReal advanceCoefficient); virtual PxReal getMinCCDAdvanceCoefficient() const; virtual void setMaxDepenetrationVelocity(PxReal maxDepenVel); virtual PxReal getMaxDepenetrationVelocity() const; virtual void setMaxContactImpulse(PxReal maxDepenVel); virtual PxReal getMaxContactImpulse() const; virtual PxU32 getInternalIslandNodeIndex() const; protected: void setCMassLocalPoseInternal(const PxTransform&); void addSpatialForce(const PxVec3* force, const PxVec3* torque, PxForceMode::Enum mode); void clearSpatialForce(PxForceMode::Enum mode, bool force, bool torque); void setSpatialForce(const PxVec3* force, const PxVec3* torque, PxForceMode::Enum mode); PX_INLINE void updateBody2Actor(const PxTransform& newBody2Actor); PX_FORCE_INLINE void setRigidBodyFlagsInternal(const PxRigidBodyFlags& currentFlags, const PxRigidBodyFlags& newFlags); #if PX_ENABLE_DEBUG_VISUALIZATION public: void visualize(Cm::RenderOutput& out, NpScene* scene); #endif PX_FORCE_INLINE bool isKinematic() const { return (APIClass::getConcreteType() == PxConcreteType::eRIGID_DYNAMIC) && (getScbBodyFast().getFlags() & PxRigidBodyFlag::eKINEMATIC); } protected: Scb::Body mBody; }; template NpRigidBodyTemplate::NpRigidBodyTemplate(PxType concreteType, PxBaseFlags baseFlags, PxActorType::Enum type, const PxTransform& bodyPose) : RigidActorTemplateClass(concreteType, baseFlags) , mBody(type, bodyPose) { } template NpRigidBodyTemplate::~NpRigidBodyTemplate() { } namespace { PX_FORCE_INLINE static bool isSimGeom(PxGeometryType::Enum t) { return t != PxGeometryType::eTRIANGLEMESH && t != PxGeometryType::ePLANE && t != PxGeometryType::eHEIGHTFIELD; } } template bool NpRigidBodyTemplate::attachShape(PxShape& shape) { NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); PX_CHECK_AND_RETURN_VAL(!(shape.getFlags() & PxShapeFlag::eSIMULATION_SHAPE) || isSimGeom(shape.getGeometryType()) || isKinematic(), "attachShape: Triangle mesh, heightfield or plane geometry shapes configured as eSIMULATION_SHAPE are not supported for non-kinematic PxRigidDynamic instances.", false); return RigidActorTemplateClass::attachShape(shape); } template void NpRigidBodyTemplate::setCMassLocalPoseInternal(const PxTransform& body2Actor) { //the point here is to change the mass distribution w/o changing the actors' pose in the world PxTransform newBody2World = getGlobalPose() * body2Actor; mBody.setBody2World(newBody2World, true); mBody.setBody2Actor(body2Actor); RigidActorTemplateClass::updateShaderComs(); } template PxTransform NpRigidBodyTemplate::getCMassLocalPose() const { NP_READ_CHECK(NpActor::getOwnerScene(*this)); return getScbBodyFast().getBody2Actor(); } template void NpRigidBodyTemplate::setMass(PxReal mass) { NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); PX_CHECK_AND_RETURN(PxIsFinite(mass), "PxRigidDynamic::setMass: invalid float"); PX_CHECK_AND_RETURN(mass>=0, "PxRigidDynamic::setMass: mass must be non-negative!"); PX_CHECK_AND_RETURN(this->getType() != PxActorType::eARTICULATION_LINK || mass > 0.0f, "PxRigidDynamic::setMassSpaceInertiaTensor: components must be > 0 for articualtions"); getScbBodyFast().setInverseMass(mass > 0.0f ? 1.0f/mass : 0.0f); } template PxReal NpRigidBodyTemplate::getMass() const { NP_READ_CHECK(NpActor::getOwnerScene(*this)); const PxReal invMass = mBody.getInverseMass(); return invMass > 0.0f ? 1.0f/invMass : 0.0f; } template PxReal NpRigidBodyTemplate::getInvMass() const { NP_READ_CHECK(NpActor::getOwnerScene(*this)); return mBody.getInverseMass(); } template void NpRigidBodyTemplate::setMassSpaceInertiaTensor(const PxVec3& m) { PX_CHECK_AND_RETURN(m.isFinite(), "PxRigidDynamic::setMassSpaceInertiaTensor: invalid inertia"); PX_CHECK_AND_RETURN(m.x>=0.0f && m.y>=0.0f && m.z>=0.0f, "PxRigidDynamic::setMassSpaceInertiaTensor: components must be non-negative"); PX_CHECK_AND_RETURN(this->getType() != PxActorType::eARTICULATION_LINK || (m.x > 0.0f && m.y > 0.0f && m.z > 0.0f), "PxRigidDynamic::setMassSpaceInertiaTensor: components must be > 0 for articualtions"); NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); mBody.setInverseInertia(invertDiagInertia(m)); } template PxVec3 NpRigidBodyTemplate::getMassSpaceInertiaTensor() const { NP_READ_CHECK(NpActor::getOwnerScene(*this)); return invertDiagInertia(mBody.getInverseInertia()); } template PxVec3 NpRigidBodyTemplate::getMassSpaceInvInertiaTensor() const { NP_READ_CHECK(NpActor::getOwnerScene(*this)); return mBody.getInverseInertia(); } template PxVec3 NpRigidBodyTemplate::getLinearVelocity() const { NP_READ_CHECK(NpActor::getOwnerScene(*this)); return mBody.getLinearVelocity(); } template PxVec3 NpRigidBodyTemplate::getAngularVelocity() const { NP_READ_CHECK(NpActor::getOwnerScene(*this)); return mBody.getAngularVelocity(); } template void NpRigidBodyTemplate::addSpatialForce(const PxVec3* force, const PxVec3* torque, PxForceMode::Enum mode) { PX_ASSERT(!(mBody.getFlags() & PxRigidBodyFlag::eKINEMATIC)); switch (mode) { case PxForceMode::eFORCE: { PxVec3 linAcc, angAcc; if (force) { linAcc = (*force) * mBody.getInverseMass(); force = &linAcc; } if (torque) { angAcc = mBody.getGlobalInertiaTensorInverse() * (*torque); torque = &angAcc; } mBody.addSpatialAcceleration(force, torque); } break; case PxForceMode::eACCELERATION: mBody.addSpatialAcceleration(force, torque); break; case PxForceMode::eIMPULSE: { PxVec3 linVelDelta, angVelDelta; if (force) { linVelDelta = ((*force) * mBody.getInverseMass()); force = &linVelDelta; } if (torque) { angVelDelta = (mBody.getGlobalInertiaTensorInverse() * (*torque)); torque = &angVelDelta; } mBody.addSpatialVelocity(force, torque); } break; case PxForceMode::eVELOCITY_CHANGE: mBody.addSpatialVelocity(force, torque); break; } } template void NpRigidBodyTemplate::setSpatialForce(const PxVec3* force, const PxVec3* torque, PxForceMode::Enum mode) { PX_ASSERT(!(mBody.getFlags() & PxRigidBodyFlag::eKINEMATIC)); switch (mode) { case PxForceMode::eFORCE: { PxVec3 linAcc, angAcc; if (force) { linAcc = (*force) * mBody.getInverseMass(); force = &linAcc; } if (torque) { angAcc = mBody.getGlobalInertiaTensorInverse() * (*torque); torque = &angAcc; } mBody.setSpatialAcceleration(force, torque); } break; case PxForceMode::eACCELERATION: mBody.setSpatialAcceleration(force, torque); break; case PxForceMode::eIMPULSE: { PxVec3 linVelDelta, angVelDelta; if (force) { linVelDelta = ((*force) * mBody.getInverseMass()); force = &linVelDelta; } if (torque) { angVelDelta = (mBody.getGlobalInertiaTensorInverse() * (*torque)); torque = &angVelDelta; } mBody.addSpatialVelocity(force, torque); } break; case PxForceMode::eVELOCITY_CHANGE: mBody.addSpatialVelocity(force, torque); break; } } template void NpRigidBodyTemplate::clearSpatialForce(PxForceMode::Enum mode, bool force, bool torque) { PX_ASSERT(!(mBody.getFlags() & PxRigidBodyFlag::eKINEMATIC)); switch (mode) { case PxForceMode::eFORCE: case PxForceMode::eACCELERATION: mBody.clearSpatialAcceleration(force, torque); break; case PxForceMode::eIMPULSE: case PxForceMode::eVELOCITY_CHANGE: mBody.clearSpatialVelocity(force, torque); break; } } #if PX_ENABLE_DEBUG_VISUALIZATION template void NpRigidBodyTemplate::visualize(Cm::RenderOutput& out, NpScene* scene) { RigidActorTemplateClass::visualize(out, scene); if (mBody.getActorFlags() & PxActorFlag::eVISUALIZATION) { Scb::Scene& scbScene = scene->getScene(); const PxReal scale = scbScene.getVisualizationParameter(PxVisualizationParameter::eSCALE); //visualize actor frames const PxReal actorAxes = scale * scbScene.getVisualizationParameter(PxVisualizationParameter::eACTOR_AXES); if (actorAxes != 0.0f) out << getGlobalPose() << Cm::DebugBasis(PxVec3(actorAxes)); const PxReal bodyAxes = scale * scbScene.getVisualizationParameter(PxVisualizationParameter::eBODY_AXES); if (bodyAxes != 0.0f) out << mBody.getBody2World() << Cm::DebugBasis(PxVec3(bodyAxes)); const PxReal linVelocity = scale * scbScene.getVisualizationParameter(PxVisualizationParameter::eBODY_LIN_VELOCITY); if (linVelocity != 0.0f) { out << 0xffffff << PxMat44(PxIdentity) << Cm::DebugArrow(mBody.getBody2World().p, mBody.getLinearVelocity() * linVelocity, 0.2f * linVelocity); } const PxReal angVelocity = scale * scbScene.getVisualizationParameter(PxVisualizationParameter::eBODY_ANG_VELOCITY); if (angVelocity != 0.0f) { out << 0x000000 << PxMat44(PxIdentity) << Cm::DebugArrow(mBody.getBody2World().p, mBody.getAngularVelocity() * angVelocity, 0.2f * angVelocity); } } } #endif PX_FORCE_INLINE void updateDynamicSceneQueryShapes(NpShapeManager& shapeManager, Sq::SceneQueryManager& sqManager, const PxRigidActor& actor) { shapeManager.markAllSceneQueryForUpdate(sqManager, actor); sqManager.get(Sq::PruningIndex::eDYNAMIC).invalidateTimestamp(); } template PX_FORCE_INLINE void NpRigidBodyTemplate::setRigidBodyFlagsInternal(const PxRigidBodyFlags& currentFlags, const PxRigidBodyFlags& newFlags) { PxRigidBodyFlags filteredNewFlags = newFlags; //Test to ensure we are not enabling both CCD and kinematic state on a body. This is unsupported if((filteredNewFlags & PxRigidBodyFlag::eENABLE_CCD) && (filteredNewFlags & PxRigidBodyFlag::eKINEMATIC)) { physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "RigidBody::setRigidBodyFlag: kinematic bodies with CCD enabled are not supported! CCD will be ignored."); filteredNewFlags &= PxRigidBodyFlags(~PxRigidBodyFlag::eENABLE_CCD); } if ((filteredNewFlags & PxRigidBodyFlag::eENABLE_CCD) && (filteredNewFlags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD)) { physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "RigidBody::setRigidBodyFlag: eENABLE_CCD can't be raised as the same time as eENABLE_SPECULATIVE_CCD! eENABLE_SPECULATIVE_CCD will be ignored."); filteredNewFlags &= PxRigidBodyFlags(~PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD); } Scb::Body& body = getScbBodyFast(); NpScene* scene = NpActor::getAPIScene(*this); Sc::Scene* scScene = scene ? &scene->getScene().getScScene() : NULL; const bool isKinematic = currentFlags & PxRigidBodyFlag::eKINEMATIC; const bool willBeKinematic = filteredNewFlags & PxRigidBodyFlag::eKINEMATIC; const bool kinematicSwitchingToDynamic = isKinematic && (!willBeKinematic); const bool dynamicSwitchingToKinematic = (!isKinematic) && willBeKinematic; if(kinematicSwitchingToDynamic) { NpShapeManager& shapeManager = this->getShapeManager(); PxU32 nbShapes = shapeManager.getNbShapes(); NpShape*const* shapes = shapeManager.getShapes(); bool hasTriangleMesh = false; for(PxU32 i=0;igetFlags() & PxShapeFlag::eSIMULATION_SHAPE) && (shapes[i]->getGeometryTypeFast()==PxGeometryType::eTRIANGLEMESH || shapes[i]->getGeometryTypeFast()==PxGeometryType::ePLANE || shapes[i]->getGeometryTypeFast()==PxGeometryType::eHEIGHTFIELD)) { hasTriangleMesh = true; break; } } if(hasTriangleMesh) { physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "RigidBody::setRigidBodyFlag: dynamic meshes/planes/heightfields are not supported!"); return; } PxTransform bodyTarget; if ((currentFlags & PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES) && body.getKinematicTarget(bodyTarget) && scene) { updateDynamicSceneQueryShapes(shapeManager, scene->getSceneQueryManagerFast(), *this); } if(scScene) { scScene->decreaseNumKinematicsCounter(); scScene->increaseNumDynamicsCounter(); } body.clearSimStateDataForPendingInsert(); } else if (dynamicSwitchingToKinematic) { if (this->getType() != PxActorType::eARTICULATION_LINK) { body.transitionSimStateDataForPendingInsert(); } else { //We're an articulation, raise an issue physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "RigidBody::setRigidBodyFlag: kinematic articulation links are not supported!"); return; } if(scScene) { scScene->decreaseNumDynamicsCounter(); scScene->increaseNumKinematicsCounter(); } } const bool kinematicSwitchingUseTargetForSceneQuery = isKinematic && willBeKinematic && ((currentFlags & PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES) != (filteredNewFlags & PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES)); if (kinematicSwitchingUseTargetForSceneQuery) { PxTransform bodyTarget; if (body.getKinematicTarget(bodyTarget) && scene) { updateDynamicSceneQueryShapes(this->getShapeManager(), scene->getSceneQueryManagerFast(), *this); } } body.setFlags(filteredNewFlags); } template void NpRigidBodyTemplate::setRigidBodyFlag(PxRigidBodyFlag::Enum flag, bool value) { NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); Scb::Body& body = getScbBodyFast(); const PxRigidBodyFlags currentFlags = body.getFlags(); const PxRigidBodyFlags newFlags = value ? currentFlags | flag : currentFlags & (~PxRigidBodyFlags(flag)); setRigidBodyFlagsInternal(currentFlags, newFlags); } template void NpRigidBodyTemplate::setRigidBodyFlags(PxRigidBodyFlags inFlags) { NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); Scb::Body& body = getScbBodyFast(); const PxRigidBodyFlags currentFlags = body.getFlags(); setRigidBodyFlagsInternal(currentFlags, inFlags); } template void NpRigidBodyTemplate::setMinCCDAdvanceCoefficient(PxReal minCCDAdvanceCoefficient) { NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); mBody.setMinCCDAdvanceCoefficient(minCCDAdvanceCoefficient); } template PxReal NpRigidBodyTemplate::getMinCCDAdvanceCoefficient() const { NP_READ_CHECK(NpActor::getOwnerScene(*this)); return mBody.getMinCCDAdvanceCoefficient(); } template void NpRigidBodyTemplate::setMaxDepenetrationVelocity(PxReal maxDepenVel) { PX_CHECK_AND_RETURN(maxDepenVel > 0.0f, "PxRigidDynamic::setMaxDepenetrationVelocity: maxDepenVel must be greater than zero."); NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); mBody.setMaxPenetrationBias(-maxDepenVel); } template PxReal NpRigidBodyTemplate::getMaxDepenetrationVelocity() const { NP_READ_CHECK(NpActor::getOwnerScene(*this)); return -mBody.getMaxPenetrationBias(); } template void NpRigidBodyTemplate::setMaxContactImpulse(const PxReal maxImpulse) { PX_CHECK_AND_RETURN(maxImpulse >= 0.f, "NpRigidBody::setMaxImpulse: impulse limit must be greater than or equal to zero."); NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); mBody.setMaxContactImpulse(maxImpulse); } template PxReal NpRigidBodyTemplate::getMaxContactImpulse() const { NP_READ_CHECK(NpActor::getOwnerScene(*this)); return mBody.getMaxContactImpulse(); } template PxU32 NpRigidBodyTemplate::getInternalIslandNodeIndex() const { NP_READ_CHECK(NpActor::getOwnerScene(*this)); return mBody.getInternalIslandNodeIndex(); } } #endif