// // 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 "PxConstraint.h" #include "NpConstraint.h" #include "NpPhysics.h" #include "NpRigidDynamic.h" #include "NpRigidStatic.h" #include "NpArticulationLink.h" #include "ScbConstraint.h" #include "ScbNpDeps.h" using namespace physx; void NpConstraint::setConstraintFunctions(PxConstraintConnector& n, const PxConstraintShaderTable& shaders) { mConstraint.getScConstraint().setConstraintFunctions(n, shaders); //update mConnectorArray, since mActor0 or mActor1 should be in external reference bool bNeedUpdate = false; if(mActor0) { NpActor& npActor = NpActor::getFromPxActor(*mActor0); if(npActor.findConnector(NpConnectorType::eConstraint, this) == 0xffffffff) { bNeedUpdate = true; npActor.addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 0: Constraint already added"); } } if(mActor1) { NpActor& npActor = NpActor::getFromPxActor(*mActor1); if(npActor.findConnector(NpConnectorType::eConstraint, this) == 0xffffffff) { bNeedUpdate = true; npActor.addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 1: Constraint already added"); } } if(bNeedUpdate) { NpScene* newScene = getSceneFromActors(mActor0, mActor1); NpScene* oldScene = getNpScene(); if (oldScene != newScene) { if (oldScene) { oldScene->removeFromConstraintList(*this); oldScene->getScene().removeConstraint(getScbConstraint()); } if (newScene) { newScene->addToConstraintList(*this); newScene->getScene().addConstraint(getScbConstraint()); } } } } NpConstraint::NpConstraint(PxRigidActor* actor0, PxRigidActor* actor1, PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize) : PxConstraint(PxConcreteType::eCONSTRAINT, PxBaseFlag::eOWNS_MEMORY) , mActor0 (actor0) , mActor1 (actor1) , mConstraint (connector, shaders, dataSize) , mIsDirty (true) { mConstraint.setFlags(shaders.flag); if(actor0) NpActor::getFromPxActor(*actor0).addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 0: Constraint already added"); if(actor1) NpActor::getFromPxActor(*actor1).addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 1: Constraint already added"); NpScene* s = getSceneFromActors(actor0, actor1); if (s) { s->addToConstraintList(*this); s->getScene().addConstraint(mConstraint); } } NpConstraint::~NpConstraint() { if(getBaseFlags()&PxBaseFlag::eOWNS_MEMORY) mConstraint.getPxConnector()->onConstraintRelease(); NpFactory::getInstance().onConstraintRelease(this); } void NpConstraint::release() { NpScene* npScene = getNpScene(); NP_WRITE_CHECK(npScene); NpPhysics::getInstance().notifyDeletionListenersUserRelease(this, NULL); if(mActor0) NpActor::getFromPxActor(*mActor0).removeConnector(*mActor0, NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 0: Constraint already added"); if(mActor1) NpActor::getFromPxActor(*mActor1).removeConnector(*mActor1, NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 1: Constraint already added"); if (npScene) { npScene->removeFromConstraintList(*this); npScene->getScene().removeConstraint(getScbConstraint()); } mConstraint.destroy(); } // PX_SERIALIZATION void NpConstraint::resolveReferences(PxDeserializationContext& context) { context.translatePxBase(mActor0); context.translatePxBase(mActor1); } NpConstraint* NpConstraint::createObject(PxU8*& address, PxDeserializationContext& context) { NpConstraint* obj = new (address) NpConstraint(PxBaseFlags(0)); address += sizeof(NpConstraint); obj->importExtraData(context); obj->resolveReferences(context); return obj; } // ~PX_SERIALIZATION PxScene* NpConstraint::getScene() const { return getNpScene(); } void NpConstraint::getActors(PxRigidActor*& actor0, PxRigidActor*& actor1) const { NP_READ_CHECK(getNpScene()); actor0 = mActor0; actor1 = mActor1; } void NpConstraint::setActors(PxRigidActor* actor0, PxRigidActor* actor1) { NP_WRITE_CHECK(getNpScene()); PX_CHECK_AND_RETURN((actor0 && !actor0->is()) || (actor1 && !actor1->is()), "PxConstraint: at least one actor must be non-static"); PX_SIMD_GUARD; if(mActor0) NpActor::getFromPxActor(*mActor0).removeConnector(*mActor0, NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 0: Constraint already added"); if(mActor1) NpActor::getFromPxActor(*mActor1).removeConnector(*mActor1, NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 1: Constraint already added"); if(actor0) NpActor::getFromPxActor(*actor0).addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 0: Constraint already added"); if(actor1) NpActor::getFromPxActor(*actor1).addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 1: Constraint already added"); mActor0 = actor0; mActor1 = actor1; NpScene* newScene = getSceneFromActors(actor0, actor1); NpScene* oldScene = getNpScene(); if (oldScene != newScene) { if (oldScene) { oldScene->removeFromConstraintList(*this); oldScene->getScene().removeConstraint(getScbConstraint()); } getScbConstraint().setBodies(getScbRigidObject(actor0), getScbRigidObject(actor1)); if (newScene) { newScene->addToConstraintList(*this); newScene->getScene().addConstraint(getScbConstraint()); } } else getScbConstraint().setBodies(getScbRigidObject(actor0), getScbRigidObject(actor1)); } void NpConstraint::markDirty() { mIsDirty = true; } void NpConstraint::setFlags(PxConstraintFlags flags) { NP_WRITE_CHECK(getNpScene()); // check for eBROKEN which is a read only flag PX_CHECK_AND_RETURN(!(flags & PxConstraintFlag::eBROKEN), "PxConstraintFlag::eBROKEN is a read only flag"); PX_CHECK_AND_RETURN(!(flags & PxConstraintFlag::eGPU_COMPATIBLE), "PxConstraintFlag::eGPU_COMPATIBLE is an internal flag and is illegal to set via the API"); PX_SIMD_GUARD; mConstraint.setFlags(flags); } PxConstraintFlags NpConstraint::getFlags() const { NP_READ_CHECK(getNpScene()); return mConstraint.getFlags(); } void NpConstraint::setFlag(PxConstraintFlag::Enum flag, bool value) { NP_WRITE_CHECK(getNpScene()); // check for eBROKEN which is a read only flag PX_CHECK_AND_RETURN(flag != PxConstraintFlag::eBROKEN, "PxConstraintFlag::eBROKEN is a read only flag"); PX_CHECK_AND_RETURN(flag != PxConstraintFlag::eGPU_COMPATIBLE, "PxConstraintFlag::eGPU_COMPATIBLE is an internal flag and is illegal to set via the API"); PX_SIMD_GUARD; PxConstraintFlags f = mConstraint.getFlags(); mConstraint.setFlags(value ? f|flag : f&~flag); } void NpConstraint::getForce(PxVec3& linear, PxVec3& angular) const { NP_READ_CHECK(getNpScene()); mConstraint.getForce(linear, angular); } void NpConstraint::updateConstants() { if(!mIsDirty) return; //ML - we can't just set dirty to false because this fails with constraints that were created while the scene was buffering! if(mConstraint.updateConstants(mConstraint.getPxConnector()->prepareData())) mIsDirty = false; #if PX_SUPPORT_PVD Scb::Scene* scbScene = mConstraint.getScbSceneForAPI(); //Changed to use the visual scenes update system which respects //the debugger's connection type flag. if( scbScene && !scbScene->isPhysicsBuffering() ) scbScene->getScenePvdClient().updatePvdProperties( &mConstraint ); #endif } void NpConstraint::setBreakForce(PxReal linear, PxReal angular) { NP_WRITE_CHECK(getNpScene()); PX_SIMD_GUARD; mConstraint.setBreakForce(linear, angular); } void NpConstraint::getBreakForce(PxReal& linear, PxReal& angular) const { NP_READ_CHECK(getNpScene()); PX_SIMD_GUARD; mConstraint.getBreakForce(linear, angular); } void NpConstraint::setMinResponseThreshold(PxReal threshold) { PX_CHECK_AND_RETURN(PxIsFinite(threshold) && threshold>=0, "PxConstraint::setMinResponseThreshold: threshold must be non-negative"); NP_WRITE_CHECK(getNpScene()); PX_SIMD_GUARD; mConstraint.setMinResponseThreshold(threshold); } PxReal NpConstraint::getMinResponseThreshold() const { NP_READ_CHECK(getNpScene()); PX_SIMD_GUARD; return mConstraint.getMinResponseThreshold(); } bool NpConstraint::isValid() const { NP_READ_CHECK(getNpScene()); bool isValid0 = mActor0 && !mActor0->is(); bool isValid1 = mActor1 && !mActor1->is(); return isValid0 || isValid1; } void* NpConstraint::getExternalReference(PxU32& typeID) { NP_READ_CHECK(getNpScene()); PxConstraintConnector* connector = mConstraint.getPxConnector(); return connector->getExternalReference(typeID); } void NpConstraint::comShift(PxRigidActor* actor) { PX_ASSERT(actor == mActor0 || actor == mActor1); PxConstraintConnector* connector = mConstraint.getPxConnector(); if(actor == mActor0) connector->onComShift(0); if(actor == mActor1) connector->onComShift(1); } void NpConstraint::actorDeleted(PxRigidActor* actor) { // the actor cannot be deleted without also removing it from the scene, // which means that the joint will also have been removed from the scene, // so we can just reset the actor here. PX_ASSERT(actor == mActor0 || actor == mActor1); if(actor == mActor0) mActor0 = 0; else mActor1 = 0; } NpScene* NpConstraint::getNpScene() const { return mConstraint.getScbSceneForAPI() ? static_cast(mConstraint.getScbSceneForAPI()->getPxScene()) : NULL; } Scb::RigidObject* NpConstraint::getScbRigidObject(PxRigidActor* a) { if(!a) return NULL; PxType type = a->getConcreteType(); if (type == PxConcreteType::eRIGID_DYNAMIC) return &(static_cast(a)->getScbBodyFast()); else if (type == PxConcreteType::eARTICULATION_LINK) return &(static_cast(a)->getScbBodyFast()); else { PX_ASSERT(type == PxConcreteType::eRIGID_STATIC); return &(static_cast(a)->getScbRigidStaticFast()); } } void physx::NpConstraintGetRigidObjectsFromScb(const Scb::Constraint&c, Scb::RigidObject*&b0, Scb::RigidObject*&b1) { const size_t offset = size_t(&(reinterpret_cast(0)->getScbConstraint())); const NpConstraint* np = reinterpret_cast(reinterpret_cast(&c)-offset); PxRigidActor* a0, * a1; np->getActors(a0, a1); b0 = NpConstraint::getScbRigidObject(a0); b1 = NpConstraint::getScbRigidObject(a1); } NpScene* NpConstraint::getSceneFromActors() const { return getSceneFromActors(mActor0, mActor1); } PX_FORCE_INLINE NpScene* NpConstraint::getSceneFromActors(const PxRigidActor* actor0, const PxRigidActor* actor1) { NpScene* s0 = NULL; NpScene* s1 = NULL; if (actor0 && (!(actor0->getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))) s0 = static_cast(actor0->getScene()); if (actor1 && (!(actor1->getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))) s1 = static_cast(actor1->getScene()); #if PX_CHECKED if ((s0 && s1) && (s0 != s1)) Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Adding constraint to scene: Actors belong to different scenes, undefined behavior expected!"); #endif if ((!actor0 || s0) && (!actor1 || s1)) return s0 ? s0 : s1; else return NULL; }