// // 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 "NpArticulationReducedCoordinate.h" #include "CmPhysXCommon.h" #include "DyFeatherstoneArticulation.h" #include "ScArticulationSim.h" #include "ScConstraintSim.h" #include "PsAlignedMalloc.h" #include "PsFoundation.h" #include "PsPool.h" #include "PxPvdDataStream.h" #include "extensions/PxJoint.h" using namespace physx; class LLReducedArticulationPool : public Ps::Pool > { public: LLReducedArticulationPool() {} }; // PX_SERIALIZATION NpArticulationReducedCoordinate* NpArticulationReducedCoordinate::createObject(PxU8*& address, PxDeserializationContext& context) { NpArticulationReducedCoordinate* obj = new (address) NpArticulationReducedCoordinate(PxBaseFlag::eIS_RELEASABLE); address += sizeof(NpArticulationReducedCoordinate); obj->importExtraData(context); obj->resolveReferences(context); return obj; } void NpArticulationReducedCoordinate::preExportDataReset() { //for now, no support for loop joint serialization Ps::Array emptyLoopJoints; PxMemCopy(&mLoopJoints, &emptyLoopJoints, sizeof(Ps::Array)); } //~PX_SERIALIZATION NpArticulationReducedCoordinate::NpArticulationReducedCoordinate() : NpArticulationTemplate(PxConcreteType::eARTICULATION_REDUCED_COORDINATE, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) { } void NpArticulationReducedCoordinate::setArticulationFlags(PxArticulationFlags flags) { NP_WRITE_CHECK(mImpl.getOwnerScene()); mImpl.getScbArticulation().setArticulationFlags(flags); } void NpArticulationReducedCoordinate::setArticulationFlag(PxArticulationFlag::Enum flag, bool value) { NP_WRITE_CHECK(mImpl.getOwnerScene()); PxArticulationFlags flags = mImpl.getScbArticulation().getArticulationFlags(); if(value) flags |= flag; else flags &= (~flag); mImpl.getScbArticulation().setArticulationFlags(flags); } PxArticulationFlags NpArticulationReducedCoordinate::getArticulationFlags() const { NP_READ_CHECK(mImpl.getOwnerScene()); return mImpl.getScbArticulation().getArticulationFlags(); } PxU32 NpArticulationReducedCoordinate::getDofs() const { NP_READ_CHECK(mImpl.getOwnerScene()); return mImpl.mArticulation.getScArticulation().getDofs(); } PxArticulationCache* NpArticulationReducedCoordinate::createCache() const { PX_CHECK_AND_RETURN_NULL(mImpl.getAPIScene(), "PxArticulation::createCache: object must be in a scene"); NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads PxArticulationCache* cache = mImpl.mArticulation.getScArticulation().createCache(); cache->version = mImpl.mCacheVersion; return cache; } PxU32 NpArticulationReducedCoordinate::getCacheDataSize() const { PX_CHECK_AND_RETURN_NULL(mImpl.getAPIScene(), "PxArticulation::getCacheDataSize: object must be in a scene"); NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads return mImpl.mArticulation.getScArticulation().getCacheDataSize(); } void NpArticulationReducedCoordinate::zeroCache(PxArticulationCache& cache) { NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads return mImpl.mArticulation.getScArticulation().zeroCache(cache); } void NpArticulationReducedCoordinate::applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag, bool autowake) { PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::applyCache: object must be in a scene"); PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::applyCache : cache is invalid, articulation configuration has changed! ") //if we try to do a bulk op when sim is running, return with error if (static_cast(getScene())->getSimulationStage() != Sc::SimulationStage::eCOMPLETE) { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "NpArticulation::applyCache() not allowed while simulation is running."); return; } mImpl.mArticulation.getScArticulation().applyCache(cache, flag); if ((flag & PxArticulationCache::ePOSITION)) { const PxU32 linkCount = mImpl.mArticulationLinks.size(); for (PxU32 i = 0; i < linkCount; ++i) { NpArticulationLink* link = mImpl.mArticulationLinks[i]; //in the lowlevel articulation, we have already updated bodyCore's body2World const PxTransform internalPose = link->getScbBodyFast().getScBody().getBody2World(); link->getScbBodyFast().setBody2World(internalPose, false); } } if ((flag & PxArticulationCache::ePOSITION) || (flag & PxArticulationCache::eVELOCITY)) { const PxU32 linkCount = mImpl.mArticulationLinks.size(); for (PxU32 i = 0; i < linkCount; ++i) { NpArticulationLink* link = mImpl.mArticulationLinks[i]; //in the lowlevel articulation, we have already updated bodyCore's linear/angular velocity const PxVec3 internalLinVel = link->getScbBodyFast().getScBody().getLinearVelocity(); const PxVec3 internalAngVel = link->getScbBodyFast().getScBody().getAngularVelocity(); link->getScbBodyFast().setLinearVelocity(internalLinVel); link->getScbBodyFast().setAngularVelocity(internalAngVel); } } mImpl.wakeUpInternal(false, autowake); } void NpArticulationReducedCoordinate::copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const { PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::copyInternalStateToCache: object must be in a scene"); PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::applyCache : cache is invalid, articulation configuration has changed! ") mImpl.mArticulation.getScArticulation().copyInternalStateToCache(cache, flag); } void NpArticulationReducedCoordinate::releaseCache(PxArticulationCache& cache) const { PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::releaseCache: object must be in a scene"); NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads mImpl.mArticulation.getScArticulation().releaseCache(cache); } void NpArticulationReducedCoordinate::packJointData(const PxReal* maximum, PxReal* reduced) const { PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::packJointData: object must be in a scene"); NP_READ_CHECK(mImpl.getOwnerScene()); mImpl.mArticulation.getScArticulation().packJointData(maximum, reduced); } void NpArticulationReducedCoordinate::unpackJointData(const PxReal* reduced, PxReal* maximum) const { PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::unpackJointData: object must be in a scene"); NP_READ_CHECK(mImpl.getOwnerScene()); mImpl.mArticulation.getScArticulation().unpackJointData(reduced, maximum); } void NpArticulationReducedCoordinate::commonInit() const { PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::commonInit: object must be in a scene"); NP_READ_CHECK(mImpl.getOwnerScene()); mImpl.mArticulation.getScArticulation().commonInit(); } void NpArticulationReducedCoordinate::computeGeneralizedGravityForce(PxArticulationCache& cache) const { PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeGeneralisedGravityForce: object must be in a scene"); NP_READ_CHECK(mImpl.getOwnerScene()); PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeGeneralisedGravityForce : cache is invalid, articulation configuration has changed! "); mImpl.mArticulation.getScArticulation().computeGeneralizedGravityForce(cache); } void NpArticulationReducedCoordinate::computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) const { PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeCoriolisAndCentrifugalForce: object must be in a scene"); NP_READ_CHECK(mImpl.getOwnerScene()); PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeCoriolisAndCentrifugalForce : cache is invalid, articulation configuration has changed! "); mImpl.mArticulation.getScArticulation().computeCoriolisAndCentrifugalForce(cache); } void NpArticulationReducedCoordinate::computeGeneralizedExternalForce(PxArticulationCache& cache) const { PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeGeneralizedExternalForce: object must be in a scene"); NP_READ_CHECK(mImpl.getOwnerScene()); PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeGeneralizedExternalForce : cache is invalid, articulation configuration has changed! "); mImpl.mArticulation.getScArticulation().computeGeneralizedExternalForce(cache); } void NpArticulationReducedCoordinate::computeJointAcceleration(PxArticulationCache& cache) const { PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeJointAcceleration: object must be in a scene"); NP_READ_CHECK(mImpl.getOwnerScene()); PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeJointAcceleration : cache is invalid, articulation configuration has changed! "); mImpl.mArticulation.getScArticulation().computeJointAcceleration(cache); } void NpArticulationReducedCoordinate::computeJointForce(PxArticulationCache& cache) const { PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeJointForce: object must be in a scene"); NP_READ_CHECK(mImpl.getOwnerScene()); PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeJointForce : cache is invalid, articulation configuration has changed! "); mImpl.mArticulation.getScArticulation().computeJointForce(cache); } void NpArticulationReducedCoordinate::computeDenseJacobian(PxArticulationCache& cache, PxU32& nRows, PxU32& nCols) const { PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeDenseJacobian: object must be in a scene"); NP_READ_CHECK(mImpl.getOwnerScene()); PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeDenseJacobian : cache is invalid, articulation configuration has changed! "); mImpl.mArticulation.getScArticulation().computeDenseJacobian(cache, nRows, nCols); } void NpArticulationReducedCoordinate::computeCoefficientMatrix(PxArticulationCache& cache) const { PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeCoefficientMatrix: object must be in a scene"); NP_READ_CHECK(mImpl.getOwnerScene()); PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeCoefficientMatrix : cache is invalid, articulation configuration has changed! "); for (PxU32 i = 0; i < mLoopJoints.size(); ++i) { static_cast(mLoopJoints[i]->getConstraint())->updateConstants(); } mImpl.mArticulation.getScArticulation().computeCoefficientMatrix(cache); } bool NpArticulationReducedCoordinate::computeLambda(PxArticulationCache& cache, PxArticulationCache& initialState, const PxReal* const jointTorque, const PxU32 maxIter) const { if (!mImpl.getAPIScene()) { physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxArticulation::computeLambda : object must be in a scened!"); return false; } NP_READ_CHECK(mImpl.getOwnerScene()); if (cache.version != mImpl.mCacheVersion) { physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxArticulation::computeLambda : cache is invalid, articulation configuration has changed!"); return false; } return mImpl.mArticulation.getScArticulation().computeLambda(cache, initialState, jointTorque, getScene()->getGravity(), maxIter); } void NpArticulationReducedCoordinate::computeGeneralizedMassMatrix(PxArticulationCache& cache) const { PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeGeneralizedMassMatrix: object must be in a scene"); NP_READ_CHECK(mImpl.getOwnerScene()); PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeGeneralizedMassMatrix : cache is invalid, articulation configuration has changed! "); mImpl.mArticulation.getScArticulation().computeGeneralizedMassMatrix(cache); } void NpArticulationReducedCoordinate::addLoopJoint(PxJoint* joint) { NP_WRITE_CHECK(mImpl.getOwnerScene()); #if PX_CHECKED PxRigidActor* actor0; PxRigidActor* actor1; joint->getActors(actor0, actor1); PxArticulationLink* link0 = NULL; PxArticulationLink* link1 = NULL; if(actor0) link0 = actor0->is(); if(actor1) link1 = actor1->is(); PX_CHECK_AND_RETURN((link0 || link1), "PxArticulation::addLoopJoint : at least one of the PxRigidActors need to be PxArticulationLink! "); PxArticulationBase* base0 = NULL; PxArticulationBase* base1 = NULL; if (link0) base0 = &link0->getArticulation(); if (link1) base1 = &link1->getArticulation(); PX_CHECK_AND_RETURN((base0 == this || base1 == this), "PxArticulation::addLoopJoint : at least one of the PxArticulationLink belongs to this articulation! "); #endif const PxU32 size = mLoopJoints.size(); if (size >= mLoopJoints.capacity()) { mLoopJoints.reserve(size * 2 + 1); } mLoopJoints.pushBack(joint); Scb::Articulation& scbArt = mImpl.getScbArticulation(); Sc::ArticulationSim* scArtSim = scbArt.getScArticulation().getSim(); NpConstraint* constraint = static_cast(joint->getConstraint()); Sc::ConstraintSim* cSim = constraint->getScbConstraint().getScConstraint().getSim(); if(scArtSim) scArtSim->addLoopConstraint(cSim); } void NpArticulationReducedCoordinate::removeLoopJoint(PxJoint* joint) { NP_WRITE_CHECK(mImpl.getOwnerScene()); mLoopJoints.findAndReplaceWithLast(joint); Scb::Articulation& scbArt = mImpl.getScbArticulation(); Sc::ArticulationSim* scArtSim = scbArt.getScArticulation().getSim(); NpConstraint* constraint = static_cast(joint->getConstraint()); Sc::ConstraintSim* cSim = constraint->getScbConstraint().getScConstraint().getSim(); scArtSim->removeLoopConstraint(cSim); } PxU32 NpArticulationReducedCoordinate::getNbLoopJoints() const { NP_READ_CHECK(mImpl.getOwnerScene()); return mLoopJoints.size(); } PxU32 NpArticulationReducedCoordinate::getLoopJoints(PxJoint** userBuffer, PxU32 bufferSize, PxU32 startIndex) const { NP_READ_CHECK(mImpl.getOwnerScene()); return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mLoopJoints.begin(), mLoopJoints.size()); } PxU32 NpArticulationReducedCoordinate::getCoefficientMatrixSize() const { NP_READ_CHECK(mImpl.getOwnerScene()); return mImpl.mArticulation.getScArticulation().getCoefficientMatrixSize(); } void NpArticulationReducedCoordinate::teleportRootLink(const PxTransform& pose, bool autowake) { PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulationReducedCoordinate::teleportRootLink: object must be in a scene"); PX_CHECK_AND_RETURN(pose.isValid(), "PxArticulationReducedCoordinate::teleportRootLink pose is not valid."); NP_WRITE_CHECK(mImpl.getOwnerScene()); NpArticulationLink* root = mImpl.mArticulationLinks[0]; root->setGlobalPoseInternal(pose, autowake); } PxSpatialVelocity NpArticulationReducedCoordinate::getLinkVelocity(const PxU32 linkId) { PX_CHECK_AND_RETURN_VAL(mImpl.getAPIScene(), "PxArticulationReducedCoordinate::getLinkVelocity: object must be in a scene", PxSpatialVelocity()); PX_CHECK_AND_RETURN_VAL(linkId < 64, "PxArticulationReducedCoordinate::getLinkVelocity index is not valid.", PxSpatialVelocity()); NP_READ_CHECK(mImpl.getOwnerScene()); return mImpl.mArticulation.getScArticulation().getLinkVelocity(linkId); } PxSpatialVelocity NpArticulationReducedCoordinate::getLinkAcceleration(const PxU32 linkId) { PX_CHECK_AND_RETURN_VAL(mImpl.getAPIScene(), "PxArticulationReducedCoordinate::getLinkAcceleration: object must be in a scene", PxSpatialVelocity()); PX_CHECK_AND_RETURN_VAL(linkId < 64, "PxArticulationReducedCoordinate::getLinkAcceleration index is not valid.", PxSpatialVelocity()); NP_READ_CHECK(mImpl.getOwnerScene()); return mImpl.mArticulation.getScArticulation().getLinkAcceleration(linkId); } NpArticulationReducedCoordinate::~NpArticulationReducedCoordinate() { NpFactory::getInstance().onArticulationRelease(this); } PxArticulationJointBase* NpArticulationReducedCoordinate::createArticulationJoint(PxArticulationLink& parent, const PxTransform& parentFrame, PxArticulationLink& child, const PxTransform& childFrame) { return NpFactory::getInstance().createNpArticulationJointRC(static_cast(parent), parentFrame, static_cast(child), childFrame); } void NpArticulationReducedCoordinate::releaseArticulationJoint(PxArticulationJointBase* joint) { NpFactory::getInstance().releaseArticulationJointRCToPool(*static_cast(joint)); } void PxArticulationImpl::recomputeLinkIDs() { PX_CHECK_AND_RETURN(getAPIScene(), "PxArticulation::recomputeLinkIDs: object must be in a scene"); NP_WRITE_CHECK(getOwnerScene()); if (!mArticulation.isBuffering()) { Sc::ArticulationSim* scArtSim = getScbArticulation().getScArticulation().getSim(); if (scArtSim) { physx::NpArticulationLink*const* links = getLinks(); const PxU32 nbLinks = getNbLinks(); for (PxU32 i = 1; i < nbLinks; ++i) { physx::NpArticulationLink* link = links[i]; PxU32 cHandle = scArtSim->findBodyIndex(*link->getScbBodyFast().getScBody().getSim()); link->setLLIndex(cHandle); } } } }