5431 lines
198 KiB
C++
5431 lines
198 KiB
C++
// 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-2025 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 "foundation/PxMathUtils.h"
|
|
#include "CmConeLimitHelper.h"
|
|
#include "DySolverConstraint1D.h"
|
|
#include "DyFeatherstoneArticulation.h"
|
|
#include "PxsRigidBody.h"
|
|
#include "PxcConstraintBlockStream.h"
|
|
#include "DyArticulationContactPrep.h"
|
|
#include "DyDynamics.h"
|
|
#include "DyArticulationPImpl.h"
|
|
#include "DyFeatherstoneArticulationLink.h"
|
|
#include "DyFeatherstoneArticulationJointData.h"
|
|
#include "DySolverConstraint1DStep.h"
|
|
#include "DyTGSDynamics.h"
|
|
#include "DyConstraintPrep.h"
|
|
#include "common/PxProfileZone.h"
|
|
#include "PxsContactManager.h"
|
|
#include "DyContactPrep.h"
|
|
#include "DySolverContext.h"
|
|
#include "DyTGSContactPrep.h"
|
|
#include "DyCpuGpuArticulation.h"
|
|
#include "DyArticulationUtils.h"
|
|
|
|
#ifndef FEATURESTONE_DEBUG
|
|
#define FEATURESTONE_DEBUG 0
|
|
#endif
|
|
|
|
// we encode articulation link handles in the lower bits of the pointer, so the
|
|
// articulation has to be aligned, which in an aligned pool means we need to size it
|
|
// appropriately
|
|
|
|
namespace physx
|
|
{
|
|
namespace Dy
|
|
{
|
|
ArticulationData::~ArticulationData()
|
|
{
|
|
PX_FREE(mLinksData);
|
|
PX_FREE(mJointData);
|
|
PX_FREE(mPathToRootElements);
|
|
}
|
|
|
|
void ArticulationData::resizeLinkData(const PxU32 linkCount)
|
|
{
|
|
const PxU32 oldSize = mMotionVelocities.size();
|
|
mMotionVelocities.reserve(linkCount);
|
|
mMotionVelocities.forceSize_Unsafe(linkCount);
|
|
|
|
mSolverLinkSpatialDeltaVels.reserve(linkCount);
|
|
mSolverLinkSpatialDeltaVels.forceSize_Unsafe(linkCount);
|
|
|
|
mSolverLinkSpatialImpulses.reserve(linkCount);
|
|
mSolverLinkSpatialImpulses.forceSize_Unsafe(linkCount);
|
|
|
|
mMotionAccelerations.reserve(linkCount);
|
|
mMotionAccelerations.forceSize_Unsafe(linkCount);
|
|
|
|
mLinkIncomingJointForces.reserve(linkCount);
|
|
mLinkIncomingJointForces.forceSize_Unsafe(linkCount);
|
|
|
|
mMotionAccelerationsInternal.reserve(linkCount);
|
|
mMotionAccelerationsInternal.forceSize_Unsafe(linkCount);
|
|
|
|
mCorioliseVectors.reserve(linkCount);
|
|
mCorioliseVectors.forceSize_Unsafe(linkCount);
|
|
|
|
mZAForces.reserve(linkCount);
|
|
mZAForces.forceSize_Unsafe(linkCount);
|
|
|
|
mZAInternalForces.reserve(linkCount);
|
|
mZAInternalForces.forceSize_Unsafe(linkCount);
|
|
|
|
mNbStatic1DConstraints.reserve(linkCount);
|
|
mNbStatic1DConstraints.forceSize_Unsafe(linkCount);
|
|
|
|
mStatic1DConstraintStartIndex.reserve(linkCount);
|
|
mStatic1DConstraintStartIndex.forceSize_Unsafe(linkCount);
|
|
|
|
mNbStaticContactConstraints.reserve(linkCount);
|
|
mNbStaticContactConstraints.forceSize_Unsafe(linkCount);
|
|
|
|
mStaticContactConstraintStartIndex.reserve(linkCount);
|
|
mStaticContactConstraintStartIndex.forceSize_Unsafe(linkCount);
|
|
|
|
mDeltaMotionVector.reserve(linkCount);
|
|
mDeltaMotionVector.forceSize_Unsafe(linkCount);
|
|
|
|
mPreTransform.reserve(linkCount);
|
|
mPreTransform.forceSize_Unsafe(linkCount);
|
|
|
|
mResponseMatrixW.reserve(linkCount);
|
|
mResponseMatrixW.forceSize_Unsafe(linkCount);
|
|
|
|
mWorldSpatialArticulatedInertia.reserve(linkCount);
|
|
mWorldSpatialArticulatedInertia.forceSize_Unsafe(linkCount);
|
|
|
|
mWorldIsolatedSpatialArticulatedInertia.reserve(linkCount);
|
|
mWorldIsolatedSpatialArticulatedInertia.forceSize_Unsafe(linkCount);
|
|
|
|
mMasses.reserve(linkCount);
|
|
mMasses.forceSize_Unsafe(linkCount);
|
|
|
|
mInvStIs.reserve(linkCount);
|
|
mInvStIs.forceSize_Unsafe(linkCount);
|
|
|
|
/*mMotionMatrix.resize(linkCount);
|
|
|
|
mWorldMotionMatrix.reserve(linkCount);
|
|
mWorldMotionMatrix.forceSize_Unsafe(linkCount);*/
|
|
|
|
mAccumulatedPoses.reserve(linkCount);
|
|
mAccumulatedPoses.forceSize_Unsafe(linkCount);
|
|
|
|
mDeltaQ.reserve(linkCount);
|
|
mDeltaQ.forceSize_Unsafe(linkCount);
|
|
|
|
mPosIterMotionVelocities.reserve(linkCount);
|
|
mPosIterMotionVelocities.forceSize_Unsafe(linkCount);
|
|
|
|
mJointTransmittedForce.reserve(linkCount);
|
|
mJointTransmittedForce.forceSize_Unsafe(linkCount);
|
|
|
|
mRw.reserve(linkCount);
|
|
mRw.forceSize_Unsafe(linkCount);
|
|
|
|
mRelativeQuat.resize(linkCount);
|
|
|
|
if (oldSize < linkCount)
|
|
{
|
|
ArticulationLinkData* oldLinks = mLinksData;
|
|
ArticulationJointCoreData* oldJoints = mJointData;
|
|
|
|
mLinksData = PX_ALLOCATE(ArticulationLinkData, linkCount, "ArticulationLinkData");
|
|
mJointData = PX_ALLOCATE(ArticulationJointCoreData, linkCount, "ArticulationJointCoreData");
|
|
|
|
PxMemCopy(mLinksData, oldLinks, sizeof(ArticulationLinkData)*oldSize);
|
|
PxMemCopy(mJointData, oldJoints, sizeof(ArticulationJointCoreData)*oldSize);
|
|
|
|
PX_FREE(oldLinks);
|
|
PX_FREE(oldJoints);
|
|
|
|
const PxU32 newElems = (linkCount - oldSize);
|
|
|
|
PxMemZero(mLinksData + oldSize, sizeof(ArticulationLinkData) * newElems);
|
|
PxMemZero(mJointData + oldSize, sizeof(ArticulationJointCoreData) * newElems);
|
|
|
|
for (PxU32 linkID = oldSize; linkID < linkCount; ++linkID)
|
|
{
|
|
PX_PLACEMENT_NEW(mLinksData + linkID, ArticulationLinkData)();
|
|
PX_PLACEMENT_NEW(mJointData + linkID, ArticulationJointCoreData)();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ArticulationData::resizeJointData(const PxU32 dofs)
|
|
{
|
|
mJointAcceleration.reserve(dofs);
|
|
mJointAcceleration.forceSize_Unsafe(dofs);
|
|
|
|
mJointInternalAcceleration.reserve(dofs);
|
|
mJointInternalAcceleration.forceSize_Unsafe(dofs);
|
|
|
|
mJointVelocity.reserve(dofs);
|
|
mJointVelocity.forceSize_Unsafe(dofs);
|
|
|
|
mJointNewVelocity.reserve(dofs+3);
|
|
mJointNewVelocity.forceSize_Unsafe(dofs+3);
|
|
|
|
mJointPosition.reserve(dofs);
|
|
mJointPosition.forceSize_Unsafe(dofs);
|
|
|
|
mJointForce.reserve(dofs);
|
|
mJointForce.forceSize_Unsafe(dofs);
|
|
|
|
mJointTargetPositions.reserve(dofs);
|
|
mJointTargetPositions.forceSize_Unsafe(dofs);
|
|
|
|
mJointTargetVelocities.reserve(dofs);
|
|
mJointTargetVelocities.forceSize_Unsafe(dofs);
|
|
|
|
mMotionMatrix.resize(dofs);
|
|
|
|
mWorldMotionMatrix.reserve(dofs);
|
|
mWorldMotionMatrix.forceSize_Unsafe(dofs);
|
|
|
|
mJointAxis.reserve(dofs);
|
|
mJointAxis.forceSize_Unsafe(dofs);
|
|
|
|
mIsW.reserve(dofs);
|
|
mIsW.forceSize_Unsafe(dofs);
|
|
|
|
mDeferredQstZ.reserve(dofs);
|
|
mDeferredQstZ.forceSize_Unsafe(dofs);
|
|
|
|
qstZIc.reserve(dofs);
|
|
qstZIc.forceSize_Unsafe(dofs);
|
|
|
|
qstZIntIc.reserve(dofs);
|
|
qstZIntIc.forceSize_Unsafe(dofs);
|
|
|
|
mISInvStIS.reserve(dofs);
|
|
mISInvStIS.forceSize_Unsafe(dofs);
|
|
|
|
mPosIterJointVelocities.reserve(dofs);
|
|
mPosIterJointVelocities.forceSize_Unsafe(dofs);
|
|
|
|
PxMemZero(mJointAcceleration.begin(), sizeof(PxReal) * dofs);
|
|
PxMemZero(mJointVelocity.begin(), sizeof(PxReal) * dofs);
|
|
PxMemZero(mJointPosition.begin(), sizeof(PxReal) * dofs);
|
|
PxMemZero(mJointForce.begin(), sizeof(PxReal) * dofs);
|
|
PxMemZero(mJointTargetPositions.begin(), sizeof(PxReal) * dofs);
|
|
PxMemZero(mJointTargetVelocities.begin(), sizeof(PxReal) * dofs);
|
|
}
|
|
|
|
ArticulationLinkData& ArticulationData::getLinkData(PxU32 index) const
|
|
{
|
|
PX_ASSERT(index < mLinkCount);
|
|
return mLinksData[index];
|
|
}
|
|
|
|
FeatherstoneArticulation::FeatherstoneArticulation(void* userData)
|
|
: mUserData(userData), mContext(NULL), mUpdateSolverData(true),
|
|
mMaxDepth(0), mJcalcDirty(true)
|
|
{
|
|
mGPUDirtyFlags = 0;
|
|
mInternalErrorAccumulatorVelIter.reset();
|
|
mContactErrorAccumulatorVelIter.reset();
|
|
mInternalErrorAccumulatorPosIter.reset();
|
|
mContactErrorAccumulatorPosIter.reset();
|
|
}
|
|
|
|
FeatherstoneArticulation::~FeatherstoneArticulation()
|
|
{
|
|
}
|
|
|
|
void FeatherstoneArticulation::copyJointData(const ArticulationData& data, PxReal* toJointData, const PxReal* fromJointData)
|
|
{
|
|
const PxU32 dofCount = data.getDofs();
|
|
|
|
PxMemCopy(toJointData, fromJointData, sizeof(PxReal)*dofCount);
|
|
}
|
|
|
|
PxU32 FeatherstoneArticulation::countDofs()
|
|
{
|
|
const PxU32 linkCount = mArticulationData.getLinkCount();
|
|
PxU32 totalDofs = 0;
|
|
|
|
for (PxU32 linkID = 1; linkID < linkCount; ++linkID)
|
|
{
|
|
const ArticulationLink& link = mArticulationData.getLink(linkID);
|
|
ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID);
|
|
const PxU8 dof = jointDatum.countJointDofs(link.inboundJoint);
|
|
totalDofs += dof;
|
|
}
|
|
|
|
return totalDofs;
|
|
}
|
|
|
|
void FeatherstoneArticulation::configureDofs()
|
|
{
|
|
const PxU32 linkCount = mArticulationData.getLinkCount();
|
|
PxU32 totalDof = 0;
|
|
|
|
for(PxU32 linkID = 1; linkID < linkCount; ++linkID)
|
|
{
|
|
const ArticulationLink& link = mArticulationData.getLink(linkID);
|
|
ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID);
|
|
const PxU8 dof = jointDatum.configureJointDofs(link.inboundJoint, mArticulationData.mJointAxis.begin() + totalDof);
|
|
jointDatum.jointOffset = totalDof;
|
|
link.inboundJoint->jointOffset = totalDof;
|
|
totalDof += dof;
|
|
}
|
|
}
|
|
|
|
bool FeatherstoneArticulation::resize(const PxU32 linkCount)
|
|
{
|
|
if (mUpdateSolverData)
|
|
{
|
|
if (linkCount != mSolverDesc.linkCount)
|
|
{
|
|
mSolverDesc.acceleration = mAcceleration.begin();
|
|
mSolverDesc.articulation = this;
|
|
}
|
|
|
|
mUpdateSolverData = false;
|
|
|
|
if (linkCount != mSolverDesc.linkCount)
|
|
mArticulationData.resizeLinkData(linkCount);
|
|
|
|
return true;
|
|
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FeatherstoneArticulation::getDataSizes(PxU32 /*linkCount*/, PxU32& solverDataSize, PxU32& totalSize, PxU32& scratchSize)
|
|
{
|
|
solverDataSize = 0;
|
|
totalSize = 0;
|
|
scratchSize = 0;
|
|
}
|
|
|
|
void FeatherstoneArticulation::setupLinks(PxU32 nbLinks, Dy::ArticulationLink* links)
|
|
{
|
|
//if this is needed, we need to re-allocated the link data
|
|
resize(nbLinks);
|
|
|
|
mSolverDesc.links = links;
|
|
mSolverDesc.linkCount = PxTo8(nbLinks);
|
|
|
|
mArticulationData.mLinks = links;
|
|
mArticulationData.mLinkCount = PxTo8(nbLinks);
|
|
mArticulationData.mFlags = mSolverDesc.core ? &mSolverDesc.core->flags : mSolverDesc.flags; // PT: PX-1399
|
|
mArticulationData.mExternalAcceleration = mSolverDesc.acceleration;
|
|
mArticulationData.mArticulation = this;
|
|
|
|
//allocate memory for articulation data
|
|
setupDofs();
|
|
}
|
|
|
|
void FeatherstoneArticulation::setupDofs()
|
|
{
|
|
PxU32 totalDofs = countDofs();
|
|
|
|
const PxU32 existedTotalDofs = mArticulationData.getDofs();
|
|
|
|
if(totalDofs != existedTotalDofs)
|
|
{
|
|
mArticulationData.resizeJointData(totalDofs + 1);
|
|
mArticulationData.setDofs(totalDofs);
|
|
}
|
|
configureDofs();
|
|
}
|
|
|
|
void FeatherstoneArticulation::allocatePathToRootElements(const PxU32 totalPathToRootElements)
|
|
{
|
|
if (mArticulationData.mNumPathToRootElements < totalPathToRootElements)
|
|
{
|
|
mArticulationData.mPathToRootElements = PX_ALLOCATE(PxU32, totalPathToRootElements, "PxU32");
|
|
mArticulationData.mNumPathToRootElements = totalPathToRootElements;
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::initPathToRoot()
|
|
{
|
|
Dy::ArticulationLink* links = mArticulationData.getLinks();
|
|
|
|
const PxU32 linkCount = mArticulationData.getLinkCount();
|
|
|
|
links[0].mPathToRootCount = 0;
|
|
links[0].mPathToRootStartIndex = 0;
|
|
|
|
PxU32 totalPathToRootCount = 1; //add on root
|
|
|
|
for (PxU32 linkID = 1; linkID < linkCount; ++linkID)
|
|
{
|
|
Dy::ArticulationLink& link = links[linkID];
|
|
|
|
PxU32 parent = link.parent;
|
|
PxU32 pathToRootCount = 1; // add myself to the path
|
|
while (parent != 0) // don't add the root
|
|
{
|
|
parent = links[parent].parent;
|
|
pathToRootCount++;
|
|
}
|
|
|
|
link.mPathToRootStartIndex = totalPathToRootCount;
|
|
link.mPathToRootCount = PxU16(pathToRootCount);
|
|
totalPathToRootCount += pathToRootCount;
|
|
}
|
|
|
|
allocatePathToRootElements(totalPathToRootCount);
|
|
|
|
PxU32* pathToRootElements = mArticulationData.getPathToRootElements();
|
|
|
|
pathToRootElements[0] = 0; //add on root index
|
|
|
|
for (PxU32 linkID = 1; linkID < linkCount; ++linkID)
|
|
{
|
|
Dy::ArticulationLink& link = links[linkID];
|
|
PxU32* pathToRoot = &pathToRootElements[link.mPathToRootStartIndex];
|
|
PxU32 numElements = link.mPathToRootCount;
|
|
|
|
pathToRoot[--numElements] = linkID;
|
|
PxU32 parent = links[linkID].parent;
|
|
while (parent != 0)
|
|
{
|
|
pathToRoot[--numElements] = parent;
|
|
parent = links[parent].parent;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::assignTendons(const PxU32 nbTendons, Dy::ArticulationSpatialTendon** tendons)
|
|
{
|
|
mArticulationData.mSpatialTendons = tendons;
|
|
mArticulationData.mNumSpatialTendons = nbTendons;
|
|
}
|
|
|
|
void FeatherstoneArticulation::assignTendons(const PxU32 nbTendons, Dy::ArticulationFixedTendon** tendons)
|
|
{
|
|
mArticulationData.mFixedTendons = tendons;
|
|
mArticulationData.mNumFixedTendons = nbTendons;
|
|
}
|
|
|
|
void FeatherstoneArticulation::assignMimicJoints(const PxU32 nbMimicJoints, Dy::ArticulationMimicJointCore** mimicJoints)
|
|
{
|
|
mArticulationData.mMimicJoints = mimicJoints;
|
|
mArticulationData.mNbMimicJoints = nbMimicJoints;
|
|
}
|
|
|
|
PxU32 FeatherstoneArticulation::getDofs() const
|
|
{
|
|
return mArticulationData.getDofs();
|
|
}
|
|
|
|
PxU32 FeatherstoneArticulation::getDof(const PxU32 linkID)
|
|
{
|
|
const ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID);
|
|
return jointDatum.nbDof;
|
|
}
|
|
|
|
bool FeatherstoneArticulation::applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag, bool& shouldWake)
|
|
{
|
|
return applyCacheToDest(mArticulationData, cache, mArticulationData.getJointVelocities(), mArticulationData.getJointPositions(),
|
|
mArticulationData.getJointForces(), mArticulationData.getJointTargetPositions(), mArticulationData.getJointTargetVelocities(), flag, shouldWake);
|
|
}
|
|
|
|
void FeatherstoneArticulation::copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag, const bool isGpuSimEnabled)
|
|
{
|
|
if (flag & PxArticulationCacheFlag::eVELOCITY)
|
|
{
|
|
copyJointData(mArticulationData, cache.jointVelocity, mArticulationData.getJointVelocities());
|
|
}
|
|
|
|
if (flag & PxArticulationCacheFlag::eACCELERATION)
|
|
{
|
|
copyJointData(mArticulationData, cache.jointAcceleration, mArticulationData.getJointAccelerations());
|
|
}
|
|
|
|
if (flag & PxArticulationCacheFlag::ePOSITION)
|
|
{
|
|
copyJointData(mArticulationData, cache.jointPosition, mArticulationData.getJointPositions());
|
|
}
|
|
|
|
if (flag & PxArticulationCacheFlag::eFORCE)
|
|
{
|
|
copyJointData(mArticulationData, cache.jointForce, mArticulationData.getJointForces());
|
|
}
|
|
|
|
if (flag & PxArticulationCacheFlag::eJOINT_TARGET_POSITIONS)
|
|
{
|
|
copyJointData(mArticulationData, cache.jointTargetPositions, mArticulationData.getJointTargetPositions());
|
|
}
|
|
|
|
if (flag & PxArticulationCacheFlag::eJOINT_TARGET_VELOCITIES)
|
|
{
|
|
copyJointData(mArticulationData, cache.jointTargetVelocities, mArticulationData.getJointTargetVelocities());
|
|
}
|
|
|
|
if (flag & PxArticulationCacheFlag::eLINK_VELOCITY)
|
|
{
|
|
const Cm::SpatialVectorF* vels = mArticulationData.getMotionVelocities();
|
|
const PxU32 numLinks = mArticulationData.getLinkCount();
|
|
for (PxU32 i = 0; i < numLinks; ++i)
|
|
{
|
|
const Cm::SpatialVectorF& vel = vels[i];
|
|
cache.linkVelocity[i].linear = vel.bottom;
|
|
cache.linkVelocity[i].angular = vel.top;
|
|
}
|
|
}
|
|
|
|
if (flag & PxArticulationCacheFlag::eLINK_ACCELERATION)
|
|
{
|
|
const PxU32 linkCount = mArticulationData.getLinkCount();
|
|
|
|
if(mArticulationData.getDt() == 0.0f)
|
|
{
|
|
PxMemZero(cache.linkAcceleration, sizeof(PxSpatialVelocity)*linkCount);
|
|
}
|
|
else if(isGpuSimEnabled)
|
|
{
|
|
const Cm::SpatialVectorF* linkMotionAccelerationsW = mArticulationData.mMotionAccelerations.begin();
|
|
|
|
//Iterate over all links and compute the acceleration for each link.
|
|
for (PxU32 i = 0; i < linkCount; ++i)
|
|
{
|
|
cache.linkAcceleration[i].linear = linkMotionAccelerationsW[i].bottom;
|
|
cache.linkAcceleration[i].angular = linkMotionAccelerationsW[i].top;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const PxReal invDt = 1.0f/mArticulationData.getDt();
|
|
|
|
const Cm::SpatialVectorF* linkMotionAccelerationsW = mArticulationData.mMotionAccelerations.begin();
|
|
const Cm::SpatialVectorF* linkSpatialDeltaVelsW = mArticulationData.mSolverLinkSpatialDeltaVels.begin();
|
|
|
|
//Iterate over all links and compute the acceleration for each link.
|
|
for (PxU32 i = 0; i < linkCount; ++i)
|
|
{
|
|
cache.linkAcceleration[i].linear = linkMotionAccelerationsW[i].bottom + linkSpatialDeltaVelsW[i].bottom*invDt;
|
|
cache.linkAcceleration[i].angular = linkMotionAccelerationsW[i].top + linkSpatialDeltaVelsW[i].top*invDt;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(flag & PxArticulationCacheFlag::eLINK_INCOMING_JOINT_FORCE)
|
|
{
|
|
const PxU32 linkCount = mArticulationData.getLinkCount();
|
|
if (mArticulationData.getDt() == 0.0f)
|
|
{
|
|
PxMemZero(cache.linkIncomingJointForce, sizeof(PxSpatialForce)*linkCount);
|
|
}
|
|
else if(isGpuSimEnabled)
|
|
{
|
|
//Calculation already completed on gpu.
|
|
const Cm::SpatialVectorF* incomingJointForces = mArticulationData.getLinkIncomingJointForces();
|
|
|
|
//Root links have no incoming joint.
|
|
cache.linkIncomingJointForce[0].force = PxVec3(PxZero);
|
|
cache.linkIncomingJointForce[0].torque = PxVec3(PxZero);
|
|
|
|
//Iterate over all links and get the incoming joint force for each link.
|
|
for (PxU32 i = 1; i < linkCount; ++i)
|
|
{
|
|
cache.linkIncomingJointForce[i].force = incomingJointForces[i].top;
|
|
cache.linkIncomingJointForce[i].torque = incomingJointForces[i].bottom;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const PxReal invDt = 1.0f/mArticulationData.getDt();
|
|
|
|
//Get everything we need.
|
|
const Cm::SpatialVectorF* linkZAForcesExtW = mArticulationData.mZAForces.begin();
|
|
const Cm::SpatialVectorF* linkZAForcesIntW = mArticulationData.mZAInternalForces.begin();
|
|
const Cm::SpatialVectorF* linkMotionAccelerationsW = mArticulationData.mMotionAccelerations.begin();
|
|
const SpatialMatrix* linkSpatialInertiasW = mArticulationData.mWorldSpatialArticulatedInertia.begin();
|
|
const Cm::SpatialVectorF* linkSpatialDeltaVelsW = mArticulationData.mSolverLinkSpatialDeltaVels.begin();
|
|
const Cm::SpatialVectorF* linkSpatialImpulsesW = mArticulationData.mSolverLinkSpatialImpulses.begin();
|
|
|
|
//Root links have no incoming joint.
|
|
cache.linkIncomingJointForce[0].force = PxVec3(PxZero);
|
|
cache.linkIncomingJointForce[0].torque = PxVec3(PxZero);
|
|
|
|
//Iterate over all links and compute the incoming joint force for each link.
|
|
for (PxU32 i = 1; i < linkCount; ++i)
|
|
{
|
|
const ArticulationLink& link = mArticulationData.getLink(i);
|
|
const ArticulationJointCore* joint = link.inboundJoint;
|
|
const PxTransform Gc = link.bodyCore->body2World;
|
|
const PxTransform Lc = joint->childPose;
|
|
const PxTransform GcLc = Gc*Lc;
|
|
const PxVec3 dW = Gc.rotate(Lc.p);
|
|
|
|
//Compute the force measured at the link.
|
|
const Cm::SpatialVectorF incomingJointForceAtLinkW =
|
|
linkSpatialInertiasW[i]*(linkMotionAccelerationsW[i] + linkSpatialDeltaVelsW[i]*invDt) +
|
|
(linkZAForcesExtW[i] + linkZAForcesIntW[i] + linkSpatialImpulsesW[i]*invDt);
|
|
|
|
//Compute the equivalent force measured at the joint.
|
|
const Cm::SpatialVectorF incomingJointForceAtJointW =
|
|
FeatherstoneArticulation::translateSpatialVector(-dW, incomingJointForceAtLinkW);
|
|
|
|
//Transform the force to the child joint frame.
|
|
cache.linkIncomingJointForce[i].force = GcLc.rotateInv(incomingJointForceAtJointW.top);
|
|
cache.linkIncomingJointForce[i].torque = GcLc.rotateInv(incomingJointForceAtJointW.bottom);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (flag & PxArticulationCacheFlag::eROOT_TRANSFORM)
|
|
{
|
|
const ArticulationLink& rLink = mArticulationData.getLink(0);
|
|
const PxsBodyCore& rBodyCore = *rLink.bodyCore;
|
|
const PxTransform& body2World = rBodyCore.body2World;
|
|
// PT:: tag: scalar transform*transform
|
|
cache.rootLinkData->transform = body2World * rBodyCore.getBody2Actor().getInverse();
|
|
}
|
|
|
|
if (flag & PxArticulationCacheFlag::eROOT_VELOCITIES)
|
|
{
|
|
const Cm::SpatialVectorF& vel = mArticulationData.getMotionVelocity(0);
|
|
cache.rootLinkData->worldLinVel = vel.bottom;
|
|
cache.rootLinkData->worldAngVel = vel.top;
|
|
|
|
const Cm::SpatialVectorF& accel = mArticulationData.getMotionAcceleration(0);
|
|
cache.rootLinkData->worldLinAccel = accel.bottom;
|
|
cache.rootLinkData->worldAngAccel = accel.top;
|
|
}
|
|
}
|
|
|
|
PxU32 FeatherstoneArticulation::getCacheDataSize(PxU32 totalDofs, PxU32 linkCount)
|
|
{
|
|
const PxU32 totalSize =
|
|
sizeof(PxSpatialForce) * linkCount // external force
|
|
+ sizeof(PxReal) * (6 + totalDofs) * (linkCount * 6) // Free floating base dofs = 6 + totalDofs, and each link (incl. base) velocity has 6 elements
|
|
// == offset to end of dense jacobian (assuming free floating base)
|
|
+ sizeof(PxReal) * (totalDofs + 6) * (totalDofs + 6) // mass matrix
|
|
+ sizeof(PxReal) * (totalDofs + 6) // Coriolis and centrifugal compensation force
|
|
+ sizeof(PxReal) * (totalDofs + 6) // gravity compensation force
|
|
+ sizeof(PxReal) * 6 * (totalDofs + 6) // centroidal momentum matrix
|
|
+ sizeof(PxReal) * 6 // centroidal momentum bias
|
|
+ sizeof(PxReal) * totalDofs * 6 // jointVelocity (PxArticulationCacheFlag::eVELOCITY)
|
|
// jointAcceleration (PxArticulationCacheFlag::eACCELERATION)
|
|
// jointPosition (PxArticulationCacheFlag::ePOSITION)
|
|
// joint force (PxArticulationCacheFlag::eFORCE)
|
|
// joint target positions (PxArticulationCacheFlag::eJOINT_TARGET_POSITIONS)
|
|
// joint target velocities (PxArticulationCacheFlag::eJOINT_TARGET_VELOCITIES)
|
|
+ sizeof(PxSpatialVelocity) * linkCount * 2 // link velocity, (PxArticulationCacheFlag::eLINK_VELOCITY)
|
|
// link acceleration (PxArticulationCacheFlag::eLINK_ACCELERATION)
|
|
+ sizeof(PxSpatialForce) * linkCount // link incoming joint forces (PxArticulationCacheFlag::eLINK_INCOMING_JOINT_FORCE)
|
|
+ sizeof(PxVec3) * linkCount * 2 // link force (PxArticulationCacheFlag::eLINK_FORCE)
|
|
// link torque (PxArticulationCacheFlag::eLINK_TORQUE)
|
|
+ sizeof(PxArticulationRootLinkData); // root link data (PxArticulationCacheFlag::eROOT_TRANSFORM, PxArticulationCacheFlag::eROOT_VELOCITIES)
|
|
|
|
return totalSize;
|
|
}
|
|
|
|
// AD: attention - some of the types here have 16B alignment requirements.
|
|
// But the size of PxArticulationCache is not necessarily a multiple of 16B.
|
|
PX_COMPILE_TIME_ASSERT(sizeof(Cm::SpatialVector)==sizeof(PxSpatialForce));
|
|
PxArticulationCache* FeatherstoneArticulation::createCache(PxU32 totalDofs, PxU32 linkCount)
|
|
{
|
|
const PxU32 pxArticulationCacheSize16BAligned = (sizeof(PxArticulationCache) + 15) & ~15;
|
|
|
|
const PxU32 totalSize = getCacheDataSize(totalDofs, linkCount) + pxArticulationCacheSize16BAligned;
|
|
|
|
PxU8* tCache = reinterpret_cast<PxU8*>(PX_ALLOC(totalSize, "Articulation cache"));
|
|
PX_ASSERT(((size_t)tCache & 15) == 0);
|
|
|
|
PxMemZero(tCache, totalSize);
|
|
|
|
PxArticulationCache* cache = reinterpret_cast<PxArticulationCache*>(tCache);
|
|
|
|
PxU32 offset = pxArticulationCacheSize16BAligned;
|
|
|
|
// the following code assumes that the size of PxSpatialForce and PxSpatialVelocity are multiples of 16B
|
|
PX_COMPILE_TIME_ASSERT((sizeof(PxSpatialForce) & 15) == 0);
|
|
PX_COMPILE_TIME_ASSERT((sizeof(PxSpatialVelocity) & 15) == 0);
|
|
|
|
// PT: filled in FeatherstoneArticulation::getGeneralizedExternalForce, size = mArticulationData.getLinkCount()
|
|
// 16B aligned
|
|
PX_ASSERT((offset & 15) == 0);
|
|
cache->externalForces = reinterpret_cast<PxSpatialForce*>(tCache + offset);
|
|
offset += sizeof(PxSpatialForce) * linkCount;
|
|
|
|
// PT: PxArticulationCacheFlag::eLINK_VELOCITY
|
|
// 16B aligned
|
|
PX_ASSERT((offset & 15) == 0);
|
|
cache->linkVelocity = reinterpret_cast<PxSpatialVelocity*>(tCache + offset);
|
|
offset += sizeof(PxSpatialVelocity) * linkCount;
|
|
|
|
// PT: PxArticulationCacheFlag::eLINK_ACCELERATION
|
|
// 16B aligned
|
|
PX_ASSERT((offset & 15) == 0);
|
|
cache->linkAcceleration = reinterpret_cast<PxSpatialVelocity*>(tCache + offset);
|
|
offset += sizeof(PxSpatialVelocity) * linkCount;
|
|
|
|
// PT: PxArticulationCacheFlag::eLINK_INCOMING_JOINT_FORCE
|
|
// 16B aligned
|
|
PX_ASSERT((offset & 15) == 0);
|
|
cache->linkIncomingJointForce = reinterpret_cast<PxSpatialForce*>(tCache + offset);
|
|
offset += sizeof(PxSpatialForce) * linkCount;
|
|
|
|
// PxArticulationCacheFlag::eLINK_FORCE
|
|
PX_ASSERT((offset & 15) == 0);
|
|
cache->linkForce = reinterpret_cast<PxVec3*>(tCache + offset);
|
|
offset += sizeof(PxVec3) * linkCount;
|
|
|
|
// PxArticulationCacheFlag::eLINK_TORQUE
|
|
cache->linkTorque = reinterpret_cast<PxVec3*>(tCache + offset);
|
|
offset += sizeof(PxVec3) * linkCount;
|
|
|
|
cache->denseJacobian = reinterpret_cast<PxReal*>(tCache + offset);
|
|
offset += sizeof(PxReal) * (6 + totalDofs) * (linkCount * 6); //size of dense jacobian assuming free floating base link.
|
|
|
|
cache->massMatrix = reinterpret_cast<PxReal*>(tCache + offset);
|
|
offset += sizeof(PxReal) * (totalDofs + 6) * (totalDofs + 6);
|
|
|
|
cache->coriolisForce = reinterpret_cast<PxReal*>(tCache + offset);
|
|
offset += sizeof(PxReal) * (totalDofs + 6);
|
|
|
|
cache->gravityCompensationForce = reinterpret_cast<PxReal*>(tCache + offset);
|
|
offset += sizeof(PxReal) * (totalDofs + 6);
|
|
|
|
cache->centroidalMomentumMatrix = reinterpret_cast<PxReal*>(tCache + offset);
|
|
offset += sizeof(PxReal) * 6 * (totalDofs + 6);
|
|
|
|
cache->centroidalMomentumBias = reinterpret_cast<PxReal*>(tCache + offset);
|
|
offset += sizeof(PxReal) * 6;
|
|
|
|
// PT: PxArticulationCacheFlag::eVELOCITY
|
|
cache->jointVelocity = reinterpret_cast<PxReal*>(tCache + offset);
|
|
offset += sizeof(PxReal) * totalDofs;
|
|
|
|
// PT: PxArticulationCacheFlag::eACCELERATION
|
|
cache->jointAcceleration = reinterpret_cast<PxReal*>(tCache + offset);
|
|
offset += sizeof(PxReal) * totalDofs;
|
|
|
|
// PT: PxArticulationCacheFlag::ePOSITION
|
|
cache->jointPosition = reinterpret_cast<PxReal*>(tCache + offset);
|
|
offset += sizeof(PxReal) * totalDofs;
|
|
|
|
// PT: PxArticulationCacheFlag::eFORCE
|
|
cache->jointForce = reinterpret_cast<PxReal*>(tCache + offset);
|
|
offset += sizeof(PxReal) * totalDofs;
|
|
|
|
// PxArticulationCacheFlag::eJOINT_TARGET_POSITIONS
|
|
cache->jointTargetPositions = reinterpret_cast<PxReal*>(tCache + offset);
|
|
offset += sizeof(PxReal) * totalDofs;
|
|
|
|
// PxArticulationCacheFlag::eJOINT_TARGET_VELOCITIES
|
|
cache->jointTargetVelocities = reinterpret_cast<PxReal*>(tCache + offset);
|
|
offset += sizeof(PxReal) * totalDofs;
|
|
|
|
// PT: PxArticulationCacheFlag::eROOT_TRANSFORM, PxArticulationCacheFlag::eROOT_VELOCITIES
|
|
cache->rootLinkData = reinterpret_cast<PxArticulationRootLinkData*>(tCache + offset);
|
|
PX_ASSERT((offset + sizeof(PxArticulationRootLinkData)) == totalSize);
|
|
|
|
cache->coefficientMatrix = NULL;
|
|
cache->lambda = NULL;
|
|
|
|
PxU32 scratchMemorySize =
|
|
sizeof(Cm::SpatialVectorF) * linkCount * 5 //motionVelocity, motionAccelerations, coriolisVectors, spatialZAVectors, externalAccels;
|
|
+ sizeof(Dy::SpatialMatrix) * linkCount //compositeSpatialInertias;
|
|
+ sizeof(PxVec3) * linkCount * 2 //linkForce, linkTorque
|
|
+ sizeof(PxReal) * totalDofs * 7; //jointVelocity, jointAcceleration, jointForces, jointPositions, jointFrictionForces, jointTargetPositions, jointTargetVelocities
|
|
|
|
scratchMemorySize = (scratchMemorySize+15)&~15;
|
|
|
|
void* scratchMemory = PX_ALLOC(scratchMemorySize, "Cache scratch memory");
|
|
cache->scratchMemory = scratchMemory;
|
|
|
|
PxcScratchAllocator* sa = PX_NEW(PxcScratchAllocator);
|
|
sa->setBlock(scratchMemory, scratchMemorySize);
|
|
cache->scratchAllocator = sa;
|
|
|
|
return cache;
|
|
}
|
|
|
|
static PX_FORCE_INLINE Mat33V loadPxMat33(const PxMat33& m)
|
|
{
|
|
return Mat33V(Vec3V_From_Vec4V(V4LoadU(&m.column0.x)),
|
|
Vec3V_From_Vec4V(V4LoadU(&m.column1.x)), V3LoadU(&m.column2.x));
|
|
}
|
|
|
|
static PX_FORCE_INLINE void storePxMat33(const Mat33V& src, PxMat33& dst)
|
|
{
|
|
V3StoreU(src.col0, dst.column0);
|
|
V3StoreU(src.col1, dst.column1);
|
|
V3StoreU(src.col2, dst.column2);
|
|
}
|
|
|
|
void FeatherstoneArticulation::transformInertia(const SpatialTransform& sTod, SpatialMatrix& spatialInertia)
|
|
{
|
|
#if 1
|
|
const SpatialTransform dTos = sTod.getTranspose();
|
|
|
|
Mat33V tL = loadPxMat33(spatialInertia.topLeft);
|
|
Mat33V tR = loadPxMat33(spatialInertia.topRight);
|
|
Mat33V bL = loadPxMat33(spatialInertia.bottomLeft);
|
|
|
|
Mat33V R = loadPxMat33(sTod.R);
|
|
Mat33V T = loadPxMat33(sTod.T);
|
|
|
|
Mat33V tl = M33MulM33(R, tL);
|
|
Mat33V tr = M33MulM33(R, tR);
|
|
Mat33V bl = M33Add(M33MulM33(T, tL), M33MulM33(R, bL));
|
|
Mat33V br = M33Add(M33MulM33(T, tR), M33MulM33(R, M33Trnsps(tL)));
|
|
|
|
Mat33V dR = loadPxMat33(dTos.R);
|
|
Mat33V dT = loadPxMat33(dTos.T);
|
|
|
|
tL = M33Add(M33MulM33(tl, dR), M33MulM33(tr, dT));
|
|
tR = M33MulM33(tr, dR);
|
|
bL = M33Add(M33MulM33(bl, dR), M33MulM33(br, dT));
|
|
|
|
bL = M33Scale(M33Add(bL, M33Trnsps(bL)), FHalf());
|
|
|
|
storePxMat33(tL, spatialInertia.topLeft);
|
|
storePxMat33(tR, spatialInertia.topRight);
|
|
storePxMat33(bL, spatialInertia.bottomLeft);
|
|
#else
|
|
const SpatialTransform dTos = sTod.getTranspose();
|
|
|
|
PxMat33 tl = sTod.R * spatialInertia.topLeft;
|
|
PxMat33 tr = sTod.R * spatialInertia.topRight;
|
|
PxMat33 bl = sTod.T * spatialInertia.topLeft + sTod.R * spatialInertia.bottomLeft;
|
|
PxMat33 br = sTod.T * spatialInertia.topRight + sTod.R * spatialInertia.getBottomRight();
|
|
|
|
spatialInertia.topLeft = tl * dTos.R + tr * dTos.T;
|
|
spatialInertia.topRight = tr * dTos.R;
|
|
spatialInertia.bottomLeft = bl * dTos.R + br * dTos.T;
|
|
|
|
//aligned inertia
|
|
spatialInertia.bottomLeft = (spatialInertia.bottomLeft + spatialInertia.bottomLeft.getTranspose()) * 0.5f;
|
|
#endif
|
|
}
|
|
|
|
void FeatherstoneArticulation::translateInertia(const PxMat33& sTod, SpatialMatrix& inertia)
|
|
{
|
|
#if 1
|
|
Mat33V sTodV = loadPxMat33(sTod);
|
|
Mat33V dTos = M33Trnsps(sTodV);
|
|
|
|
const Mat33V tL = loadPxMat33(inertia.topLeft);
|
|
const Mat33V tR = loadPxMat33(inertia.topRight);
|
|
const Mat33V bL = loadPxMat33(inertia.bottomLeft);
|
|
|
|
const Mat33V bl = M33Add(M33MulM33(sTodV, tL), bL);
|
|
const Mat33V br = M33Add(M33MulM33(sTodV, tR), M33Trnsps(tL));
|
|
const Mat33V bottomLeft = M33Add(bl, M33MulM33(br, dTos));
|
|
|
|
storePxMat33(M33Add(tL, M33MulM33(tR, dTos)), inertia.topLeft);
|
|
storePxMat33(M33Scale(M33Add(bottomLeft, M33Trnsps(bottomLeft)), FHalf()), inertia.bottomLeft);
|
|
#else
|
|
const PxMat33 dTos = sTod.getTranspose();
|
|
|
|
PxMat33 bl = sTod * inertia.topLeft + inertia.bottomLeft;
|
|
PxMat33 br = sTod * inertia.topRight + inertia.getBottomRight();
|
|
|
|
inertia.topLeft = inertia.topLeft + inertia.topRight * dTos;
|
|
inertia.bottomLeft = bl + br * dTos;
|
|
|
|
//aligned inertia - make it symmetrical! OPTIONAL!!!!
|
|
inertia.bottomLeft = (inertia.bottomLeft + inertia.bottomLeft.getTranspose()) * 0.5f;
|
|
#endif
|
|
}
|
|
|
|
void FeatherstoneArticulation::getImpulseResponse(
|
|
PxU32 linkID,
|
|
const Cm::SpatialVector& impulse,
|
|
Cm::SpatialVector& deltaVV) const
|
|
{
|
|
PX_ASSERT(impulse.pad0 == 0.f && impulse.pad1 == 0.f);
|
|
|
|
//impulse lin is contact normal, and ang is raxn. R is body2World, R(t) is world2Body
|
|
//| R(t), 0 |
|
|
//| R(t)*r, R(t)|
|
|
//r is the vector from center of mass to contact point
|
|
//p(impluse) = |n|
|
|
// |0|
|
|
|
|
Cm::SpatialVectorF deltaV = mArticulationData.getImpulseResponseMatrixWorld()[linkID].getLinkDeltaVImpulseResponse(reinterpret_cast<const Cm::SpatialVectorF&>(impulse));
|
|
|
|
deltaVV.linear = deltaV.bottom;
|
|
deltaVV.angular = deltaV.top;
|
|
}
|
|
|
|
void FeatherstoneArticulation::getImpulseResponse(
|
|
PxU32 linkID,
|
|
const Cm::SpatialVectorV& impulse,
|
|
Cm::SpatialVectorV& deltaVV) const
|
|
{
|
|
#if 0
|
|
const PxTransform& body2World = mArticulationData.getPreTransform(linkID);
|
|
|
|
QuatV rot = QuatVLoadU(&body2World.q.x);
|
|
|
|
Cm::SpatialVectorV impl(QuatRotateInv(rot, impulse.linear), QuatRotateInv(rot, impulse.angular));
|
|
|
|
//transform p(impluse) from world space to the local space of linkId
|
|
|
|
//Cm::SpatialVectorF impl(impulse.linear, impulse.angular);
|
|
Cm::SpatialVectorV deltaV = mArticulationData.getImpulseResponseMatrix()[linkID].getResponse(impl);
|
|
deltaVV.linear = QuatRotate(rot, deltaV.angular);
|
|
deltaVV.angular = QuatRotate(rot, deltaV.linear);
|
|
#else
|
|
Cm::SpatialVectorV deltaV = mArticulationData.getImpulseResponseMatrixWorld()[linkID].getLinkDeltaVImpulseResponse(impulse);
|
|
deltaVV.linear = deltaV.angular;
|
|
deltaVV.angular = deltaV.linear;
|
|
#endif
|
|
}
|
|
|
|
//This will return world space SpatialVectorV
|
|
Cm::SpatialVectorV FeatherstoneArticulation::getLinkVelocity(const PxU32 linkID) const
|
|
{
|
|
//This is in the world space
|
|
const Cm::SpatialVectorF& motionVelocity = mArticulationData.getMotionVelocity(linkID);
|
|
|
|
Cm::SpatialVectorV velocity;
|
|
velocity.linear = V3LoadA(motionVelocity.bottom);
|
|
velocity.angular = V3LoadA(motionVelocity.top);
|
|
|
|
return velocity;
|
|
}
|
|
|
|
Cm::SpatialVector FeatherstoneArticulation::getLinkScalarVelocity(const PxU32 linkID) const
|
|
{
|
|
//This is in the world space
|
|
const Cm::SpatialVectorF& motionVelocity = mArticulationData.getMotionVelocity(linkID);
|
|
|
|
return Cm::SpatialVector(motionVelocity.bottom, motionVelocity.top);
|
|
}
|
|
|
|
Cm::SpatialVectorV FeatherstoneArticulation::getLinkMotionVector(const PxU32 linkID) const
|
|
{
|
|
const Cm::SpatialVectorF& motionVector = mArticulationData.getDeltaMotionVector(linkID);
|
|
|
|
Cm::SpatialVectorV velocity;
|
|
velocity.linear = V3LoadU(motionVector.bottom);
|
|
velocity.angular = V3LoadU(motionVector.top);
|
|
|
|
return velocity;
|
|
}
|
|
|
|
//this is called by island gen to determine whether the articulation should be awake or sleep
|
|
Cm::SpatialVector FeatherstoneArticulation::getMotionVelocity(const PxU32 linkID) const
|
|
{
|
|
//This is in the world space
|
|
const Cm::SpatialVectorF& motionVelocity = mArticulationData.getPosIterMotionVelocities()[linkID];
|
|
return Cm::SpatialVector(motionVelocity.bottom, motionVelocity.top);
|
|
}
|
|
|
|
Cm::SpatialVector FeatherstoneArticulation::getMotionAcceleration(const PxU32 linkID, const bool isGpuSimEnabled) const
|
|
{
|
|
Cm::SpatialVector a = Cm::SpatialVector::zero();
|
|
if(mArticulationData.getDt() > 0.0f)
|
|
{
|
|
if(isGpuSimEnabled)
|
|
{
|
|
const Cm::SpatialVectorF& linkAccel = mArticulationData.mMotionAccelerations[linkID];
|
|
a = Cm::SpatialVector(linkAccel.bottom, linkAccel.top);
|
|
}
|
|
else
|
|
{
|
|
const PxReal invDt = 1.0f/mArticulationData.getDt();
|
|
const Cm::SpatialVectorF linkAccel =
|
|
mArticulationData.mMotionAccelerations[linkID] + mArticulationData.mSolverLinkSpatialDeltaVels[linkID]*invDt;
|
|
a = Cm::SpatialVector(linkAccel.bottom, linkAccel.top);
|
|
}
|
|
}
|
|
return a;
|
|
}
|
|
|
|
PxReal FeatherstoneArticulation::getLinkMaxPenBias(const PxU32 linkID) const
|
|
{
|
|
return mArticulationData.getLinkData(linkID).maxPenBias;
|
|
}
|
|
|
|
PxReal FeatherstoneArticulation::getCfm(const PxU32 linkID) const
|
|
{
|
|
return mArticulationData.getLink(linkID).cfm;
|
|
}
|
|
|
|
void PxcFsFlushVelocity(FeatherstoneArticulation& articulation, Cm::SpatialVectorF* deltaV)
|
|
{
|
|
PX_ASSERT(deltaV);
|
|
|
|
ArticulationData& data = articulation.mArticulationData;
|
|
const bool fixBase = data.getArticulationFlags() & PxArticulationFlag::eFIX_BASE;
|
|
Cm::SpatialVectorF* motionVelocities = data.getMotionVelocities();
|
|
//Cm::SpatialVectorF* deferredZ = data.getSpatialZAVectors();
|
|
ArticulationLink* links = data.getLinks();
|
|
ArticulationJointCoreData* jointData = data.getJointData();
|
|
|
|
//PxTransform* poses = data.getAccumulatedPoses();
|
|
//const PxTransform* poses = data.getPreTransform();
|
|
|
|
//This will be zero at the beginning of the frame
|
|
PxReal* jointNewVelocities = data.getJointNewVelocities();
|
|
|
|
if (fixBase)
|
|
{
|
|
deltaV[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f));
|
|
}
|
|
else
|
|
{
|
|
//ArticulationLink& link = links[0];
|
|
|
|
deltaV[0] = data.getBaseInvSpatialArticulatedInertiaW() * -data.getRootDeferredZ();
|
|
|
|
motionVelocities[0] += deltaV[0];
|
|
data.incrementSolverSpatialDeltaVel(0, deltaV[0]);
|
|
|
|
PX_ASSERT(motionVelocities[0].isFinite());
|
|
}
|
|
|
|
const PxU32 linkCount = data.getLinkCount();
|
|
|
|
for (PxU32 i = 1; i < linkCount; i++)
|
|
{
|
|
const ArticulationLink& tLink = links[i];
|
|
const ArticulationJointCoreData& tJointDatum = jointData[i];
|
|
|
|
const Cm::SpatialVectorF dV = propagateAccelerationW(
|
|
data.getRw(i), deltaV[tLink.parent],
|
|
data.getInvStIs(i), &data.getWorldMotionMatrix(tJointDatum.jointOffset),
|
|
&data.getIsW(tJointDatum.jointOffset), &data.getDeferredQstZ()[tJointDatum.jointOffset], tJointDatum.nbDof,
|
|
&jointNewVelocities[tJointDatum.jointOffset]);
|
|
|
|
deltaV[i] = dV;
|
|
motionVelocities[i] += dV;
|
|
data.incrementSolverSpatialDeltaVel(i, dV);
|
|
|
|
PX_ASSERT(motionVelocities[i].isFinite());
|
|
}
|
|
|
|
//PxMemZero(deferredZ, sizeof(Cm::SpatialVectorF)*linkCount);
|
|
PxMemZero(data.getDeferredQstZ(), sizeof(PxReal) * data.getDofs());
|
|
|
|
data.getRootDeferredZ() = Cm::SpatialVectorF::Zero();
|
|
}
|
|
|
|
// Apply substep external forces and joint actuation, propagate from leaves to root
|
|
// Gravity and external force/acceleration fullstep application happens in FeatherstoneArticulation::computeLinkStates
|
|
// Joint force fullstep application happens in FeatherstoneArticulation::computePropagateSpatialInertia_ZA_ZIc
|
|
void FeatherstoneArticulation::applyTgsSubstepForces(const ArticulationSolverDesc& desc, const PxReal stepDt,
|
|
Cm::SpatialVectorF* scratchExtForcesArticulatedYW)
|
|
{
|
|
FeatherstoneArticulation* articulation = desc.articulation;
|
|
ArticulationData& data = articulation->mArticulationData;
|
|
|
|
// inputs
|
|
const ArticulationLink* links = data.getLinks();
|
|
const PxU32 linkCount = data.getLinkCount();
|
|
const Cm::UnAlignedSpatialVector* jointDofMotionMatrixW = data.getWorldMotionMatrix();
|
|
const Cm::SpatialVectorF* jointDofISInvStISW = data.getISInvStIS();
|
|
const PxVec3* childToParentRW = data.getRw();
|
|
const ArticulationJointCoreData* jointData = data.getJointData();
|
|
const bool fixBase = data.getArticulationFlags() & PxArticulationFlag::eFIX_BASE;
|
|
const PxReal* jointForces = data.getJointForces();
|
|
const Cm::SpatialVector* linkExternalAcceleration = data.mExternalAcceleration; // we have converted to spatial forces here already, see FeatherstoneArticulation::computeLinkStates
|
|
|
|
// outputs
|
|
PxReal* jointDofQStY = data.getDeferredQstZ();
|
|
Cm::SpatialVectorF& rootDeferredZ = data.getRootDeferredZ();
|
|
data.mJointDirty = true;
|
|
|
|
// Scratch only temporarily needed until this function returns, passed in from outside to avoid allocating here.
|
|
// We set it to zero because it might be in a dirty state.
|
|
PX_ASSERT(scratchExtForcesArticulatedYW);
|
|
Cm::SpatialVectorF* extForcesArticulatedYW = scratchExtForcesArticulatedYW;
|
|
for(PxU32 linkID = 0; linkID < linkCount; linkID++)
|
|
{
|
|
extForcesArticulatedYW[linkID] = Cm::SpatialVectorF::Zero();
|
|
}
|
|
|
|
// Iterate from leaves to root, accumulating the substep forces in articulatedY up the tree
|
|
// This is like FeatherstoneArticulation::propagateImpulseW but with impulses on all links
|
|
const PxI32 startIndex = linkCount - 1;
|
|
for (PxI32 linkID = startIndex; linkID > 0; --linkID)
|
|
{
|
|
const ArticulationLink& link = links[linkID];
|
|
const Cm::SpatialVector& extForceStoredAsMotionVec = linkExternalAcceleration[linkID];
|
|
const Cm::SpatialVectorF isolatedYW(extForceStoredAsMotionVec.linear, extForceStoredAsMotionVec.angular); // order defined in FeatherstoneArticulation::computeLinkStates
|
|
const Cm::SpatialVectorF articulatedYW = isolatedYW * stepDt + extForcesArticulatedYW[linkID]; // could avoid adding this for leaf links
|
|
const ArticulationJointCoreData& jointDatum = jointData[linkID];
|
|
const PxU32 jointOffset = jointDatum.jointOffset;
|
|
const PxU8 dofCount = jointDatum.nbDof;
|
|
|
|
Cm::SpatialVectorF parentYW = articulatedYW;
|
|
for(PxU8 jointDof = 0; jointDof < dofCount; jointDof++)
|
|
{
|
|
const PxU32 jointIdx = jointOffset + jointDof;
|
|
const Cm::UnAlignedSpatialVector& sa = jointDofMotionMatrixW[jointIdx];
|
|
const Cm::SpatialVectorF& ISInvStISW = jointDofISInvStISW[jointIdx];
|
|
const PxReal jointForce = jointForces[jointIdx];
|
|
|
|
const PxReal stZY = jointForce * stepDt - sa.innerProduct(articulatedYW);
|
|
PX_ASSERT(PxIsFinite(stZY));
|
|
|
|
parentYW += ISInvStISW * stZY;
|
|
jointDofQStY[jointIdx] += stZY; // store as deferred impulse in joint space
|
|
}
|
|
|
|
const PxU32 parentLinkID = link.parent;
|
|
if(parentLinkID == 0 && fixBase)
|
|
continue; // no need to propagate to a fixed base
|
|
|
|
parentYW = FeatherstoneArticulation::translateSpatialVector(childToParentRW[linkID], parentYW);
|
|
extForcesArticulatedYW[parentLinkID] += parentYW; // add rather than assign because other children may have already contributed
|
|
}
|
|
|
|
// process the root if it's not fixed
|
|
if(!fixBase)
|
|
{
|
|
const Cm::SpatialVector& extForceStoredAsMotionVec = linkExternalAcceleration[0];
|
|
const Cm::SpatialVectorF isolatedYW(extForceStoredAsMotionVec.linear, extForceStoredAsMotionVec.angular); // order defined in FeatherstoneArticulation::computeLinkStates
|
|
const Cm::SpatialVectorF articulatedYW = isolatedYW * stepDt + extForcesArticulatedYW[0];
|
|
rootDeferredZ += articulatedYW;
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::recordDeltaMotion(const ArticulationSolverDesc& desc,
|
|
const PxReal dt, Cm::SpatialVectorF* deltaV, const PxReal /*totalInvDt*/)
|
|
{
|
|
PX_ASSERT(deltaV);
|
|
|
|
FeatherstoneArticulation* articulation = static_cast<FeatherstoneArticulation*>(desc.articulation);
|
|
ArticulationData& data = articulation->mArticulationData;
|
|
const PxU32 linkCount = data.getLinkCount();
|
|
|
|
const PxU32 flags = data.getArticulationFlags();
|
|
|
|
if (data.mJointDirty)
|
|
{
|
|
PxcFsFlushVelocity(*articulation, deltaV);
|
|
}
|
|
|
|
Cm::SpatialVectorF* deltaMotion = data.getDeltaMotionVector();
|
|
Cm::SpatialVectorF* posMotionVelocities = data.getPosIterMotionVelocities();
|
|
Cm::SpatialVectorF* motionVelocities = data.getMotionVelocities();
|
|
|
|
PxReal* jointPosition = data.getJointPositions();
|
|
PxReal* jointNewVelocities = data.getJointNewVelocities();
|
|
|
|
//data.mAccumulatedDt += dt;
|
|
data.setDt(dt);
|
|
|
|
const bool fixBase = flags & PxArticulationFlag::eFIX_BASE;
|
|
|
|
if (!fixBase)
|
|
{
|
|
Cm::SpatialVectorF& motionVelocity = motionVelocities[0];
|
|
PX_ASSERT(motionVelocity.top.isFinite());
|
|
PX_ASSERT(motionVelocity.bottom.isFinite());
|
|
|
|
const PxTransform preTrans = data.mAccumulatedPoses[0];
|
|
|
|
const PxVec3 lin = motionVelocity.bottom;
|
|
const PxVec3 ang = motionVelocity.top;
|
|
|
|
const PxVec3 newP = preTrans.p + lin * dt;
|
|
|
|
const PxTransform newPose = PxTransform(newP, PxExp(ang*dt) * preTrans.q);
|
|
|
|
//PxVec3 lin, ang;
|
|
/*calculateNewVelocity(newPose, data.mPreTransform[0],
|
|
1.f, lin, ang); */
|
|
|
|
data.mAccumulatedPoses[0] = newPose;
|
|
|
|
PxQuat dq = newPose.q * data.mPreTransform[0].q.getConjugate();
|
|
|
|
if (dq.w < 0.f)
|
|
dq = -dq;
|
|
|
|
data.mDeltaQ[0] = dq;
|
|
|
|
Cm::SpatialVectorF delta = motionVelocity * dt;
|
|
|
|
deltaMotion[0] += delta;
|
|
posMotionVelocities[0] += delta;
|
|
}
|
|
|
|
for (PxU32 linkID = 1; linkID < linkCount; linkID++)
|
|
{
|
|
ArticulationJointCoreData& jointDatum = data.getJointData(linkID);
|
|
|
|
const PxTransform newPose = articulation->propagateTransform(linkID, data.getLinks(), jointDatum, data.getMotionVelocities(),
|
|
dt, data.mAccumulatedPoses[data.getLink(linkID).parent], data.mAccumulatedPoses[linkID],
|
|
jointNewVelocities, jointPosition, &data.getMotionMatrix(jointDatum.jointOffset), &data.getWorldMotionMatrix(jointDatum.jointOffset));
|
|
|
|
//data.mDeltaQ[linkID] = data.mPreTransform[linkID].q.getConjugate() * newPose.q;
|
|
PxQuat dq = newPose.q * data.mPreTransform[linkID].q.getConjugate();
|
|
|
|
if(dq.w < 0.f)
|
|
dq = -dq;
|
|
|
|
data.mDeltaQ[linkID] = dq;
|
|
|
|
/*PxVec3 lin, ang;
|
|
calculateNewVelocity(newPose, data.mPreTransform[linkID],
|
|
1.f, lin, ang);*/
|
|
|
|
PxVec3 lin = (newPose.p - data.mPreTransform[linkID].p);
|
|
|
|
Cm::SpatialVectorF delta = motionVelocities[linkID] * dt;
|
|
|
|
//deltaMotion[linkID].top = ang;// motionVeloties[linkID].top * dt;
|
|
deltaMotion[linkID].top += delta.top;
|
|
deltaMotion[linkID].bottom = lin;// motionVeloties[linkID].top * dt;
|
|
posMotionVelocities[linkID] += delta;
|
|
|
|
//Record the new current pose
|
|
data.mAccumulatedPoses[linkID] = newPose;
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::deltaMotionToMotionVelocity(const ArticulationSolverDesc& desc, PxReal invDt)
|
|
{
|
|
FeatherstoneArticulation* articulation = static_cast<FeatherstoneArticulation*>(desc.articulation);
|
|
ArticulationData& data = articulation->mArticulationData;
|
|
const PxU32 linkCount = data.getLinkCount();
|
|
const Cm::SpatialVectorF* deltaMotion = data.getDeltaMotionVector();
|
|
|
|
for (PxU32 linkID = 0; linkID<linkCount; linkID++)
|
|
{
|
|
Cm::SpatialVectorF& v = data.getMotionVelocity(linkID);
|
|
|
|
Cm::SpatialVectorF delta = deltaMotion[linkID] * invDt;
|
|
|
|
v = delta;
|
|
|
|
desc.motionVelocity[linkID] = reinterpret_cast<Cm::SpatialVectorV&>(delta);
|
|
}
|
|
}
|
|
|
|
Cm::SpatialVectorV FeatherstoneArticulation::pxcFsGetVelocity(const PxU32 linkID, PxReal* jointDofSpeeds) const
|
|
{
|
|
const bool fixBase = mArticulationData.getArticulationFlags() & PxArticulationFlag::eFIX_BASE;
|
|
|
|
ArticulationLink* links = mArticulationData.getLinks();
|
|
|
|
Cm::SpatialVectorF deltaV(PxVec3(0.f), PxVec3(0.f));
|
|
|
|
if (!fixBase)
|
|
{
|
|
//deltaV = mArticulationData.mBaseInvSpatialArticulatedInertia * (-deferredZ[0]);
|
|
//DeferredZ now in world space!
|
|
deltaV = mArticulationData.mBaseInvSpatialArticulatedInertiaW * -mArticulationData.getRootDeferredZ();
|
|
}
|
|
|
|
const PxU32 startIndex = links[linkID].mPathToRootStartIndex;
|
|
const PxU32 elementCount = links[linkID].mPathToRootCount;
|
|
|
|
//Take care of the case where elementCount is 0.
|
|
const PxU32 elementCountMinusOne = (0 == elementCount) ? 0 : elementCount -1;
|
|
|
|
const PxU32* pathToRootElements = &mArticulationData.mPathToRootElements[startIndex];
|
|
|
|
//We don't need to compute the deltaJointDofSpeed for these links and joints.
|
|
for (PxU32 i = 0; i < elementCountMinusOne; ++i)
|
|
{
|
|
const PxU32 index = pathToRootElements[i];
|
|
PX_ASSERT(links[index].parent < index);
|
|
|
|
const PxU32 jointOffset = mArticulationData.getJointData(index).jointOffset;
|
|
const PxU32 dofCount = mArticulationData.getJointData(index).nbDof;
|
|
|
|
deltaV = propagateAccelerationW(
|
|
mArticulationData.getRw(index), deltaV,
|
|
mArticulationData.mInvStIs[index], &mArticulationData.mWorldMotionMatrix[jointOffset], &mArticulationData.mIsW[jointOffset],
|
|
&mArticulationData.mDeferredQstZ[jointOffset], dofCount,
|
|
NULL);
|
|
}
|
|
|
|
//The last link might need to compute the deltaJointDofSpeed.
|
|
PxReal deltaJointDofSpeeds[3] = {0, 0, 0};
|
|
PxReal* optionalDeltaJointDofSpeeds = jointDofSpeeds ? deltaJointDofSpeeds : NULL;
|
|
for (PxU32 i = elementCountMinusOne; i < elementCount; ++i)
|
|
{
|
|
const PxU32 index = pathToRootElements[i];
|
|
PX_ASSERT(links[index].parent < index);
|
|
|
|
const PxU32 jointOffset = mArticulationData.getJointData(index).jointOffset;
|
|
const PxU32 dofCount = mArticulationData.getJointData(index).nbDof;
|
|
|
|
deltaV = propagateAccelerationW(
|
|
mArticulationData.getRw(index), deltaV,
|
|
mArticulationData.mInvStIs[index], &mArticulationData.mWorldMotionMatrix[jointOffset], &mArticulationData.mIsW[jointOffset],
|
|
&mArticulationData.mDeferredQstZ[jointOffset], dofCount,
|
|
optionalDeltaJointDofSpeeds);
|
|
}
|
|
|
|
if(jointDofSpeeds)
|
|
{
|
|
const PxU32 jointOffset = mArticulationData.getJointData(linkID).jointOffset;
|
|
const PxU32 dofCount = mArticulationData.getJointData(linkID).nbDof;
|
|
for(PxU32 i = 0; i < dofCount; i++)
|
|
{
|
|
jointDofSpeeds[i] = mArticulationData.mJointNewVelocity[jointOffset + i] + deltaJointDofSpeeds[i];
|
|
}
|
|
}
|
|
const Cm::SpatialVectorF vel = mArticulationData.getMotionVelocity(linkID) + deltaV;
|
|
return Cm::SpatialVector(vel.bottom, vel.top);
|
|
}
|
|
|
|
void FeatherstoneArticulation::pxcFsGetVelocities(PxU32 linkID, PxU32 linkID1, Cm::SpatialVectorV& v0, Cm::SpatialVectorV& v1) const
|
|
{
|
|
{
|
|
const bool fixBase = mArticulationData.getArticulationFlags() & PxArticulationFlag::eFIX_BASE;
|
|
|
|
ArticulationLink* links = mArticulationData.getLinks();
|
|
|
|
Cm::SpatialVectorF deltaV(PxVec3(0.f), PxVec3(0.f));
|
|
|
|
if (!fixBase)
|
|
{
|
|
//deltaV = mArticulationData.mBaseInvSpatialArticulatedInertia * (-deferredZ[0]);
|
|
deltaV = mArticulationData.mBaseInvSpatialArticulatedInertiaW * (-mArticulationData.mRootDeferredZ);
|
|
}
|
|
|
|
const PxU32* pathToRootElements = mArticulationData.mPathToRootElements;
|
|
|
|
Dy::ArticulationLink& link0 = links[linkID];
|
|
Dy::ArticulationLink& link1 = links[linkID1];
|
|
|
|
const PxU32* pathToRoot0 = &pathToRootElements[link0.mPathToRootStartIndex];
|
|
const PxU32* pathToRoot1 = &pathToRootElements[link1.mPathToRootStartIndex];
|
|
|
|
const PxU32 numElems0 = link0.mPathToRootCount;
|
|
const PxU32 numElems1 = link1.mPathToRootCount;
|
|
|
|
PxU32 offset = 0;
|
|
while (pathToRoot0[offset] == pathToRoot1[offset])
|
|
{
|
|
const PxU32 index = pathToRoot0[offset++];
|
|
PX_ASSERT(links[index].parent < index);
|
|
if (offset >= numElems0 || offset >= numElems1)
|
|
break;
|
|
|
|
const PxU32 jointOffset = mArticulationData.getJointData(index).jointOffset;
|
|
const PxU32 dofCount = mArticulationData.getJointData(index).nbDof;
|
|
|
|
deltaV = propagateAccelerationW(
|
|
mArticulationData.getRw(index), deltaV,
|
|
mArticulationData.mInvStIs[index], &mArticulationData.mWorldMotionMatrix[jointOffset],
|
|
&mArticulationData.mIsW[jointOffset], &mArticulationData.mDeferredQstZ[jointOffset], dofCount,
|
|
NULL);
|
|
}
|
|
|
|
Cm::SpatialVectorF deltaV1 = deltaV;
|
|
|
|
for (PxU32 idx = offset; idx < numElems0; ++idx)
|
|
{
|
|
const PxU32 index = pathToRoot0[idx];
|
|
PX_ASSERT(links[index].parent < index);
|
|
|
|
const PxU32 jointOffset = mArticulationData.getJointData(index).jointOffset;
|
|
const PxU32 dofCount = mArticulationData.getJointData(index).nbDof;
|
|
|
|
deltaV = propagateAccelerationW(
|
|
mArticulationData.getRw(index), deltaV,
|
|
mArticulationData.mInvStIs[index],
|
|
&mArticulationData.mWorldMotionMatrix[jointOffset], &mArticulationData.mIsW[jointOffset],
|
|
&mArticulationData.mDeferredQstZ[jointOffset], dofCount,
|
|
NULL);
|
|
}
|
|
|
|
for (PxU32 idx = offset; idx < numElems1; ++idx)
|
|
{
|
|
const PxU32 index = pathToRoot1[idx];
|
|
PX_ASSERT(links[index].parent < index);
|
|
|
|
const PxU32 jointOffset = mArticulationData.getJointData(index).jointOffset;
|
|
const PxU32 dofCount = mArticulationData.getJointData(index).nbDof;
|
|
|
|
deltaV1 = propagateAccelerationW(
|
|
mArticulationData.getRw(index), deltaV1,
|
|
mArticulationData.mInvStIs[index], &mArticulationData.mWorldMotionMatrix[jointOffset],
|
|
&mArticulationData.mIsW[jointOffset], &mArticulationData.mDeferredQstZ[jointOffset], dofCount,
|
|
NULL);
|
|
}
|
|
|
|
Cm::SpatialVectorF vel = mArticulationData.getMotionVelocity(linkID) + deltaV;
|
|
|
|
v0 = Cm::SpatialVector(vel.bottom, vel.top);
|
|
|
|
Cm::SpatialVectorF vel1 = mArticulationData.getMotionVelocity(linkID1) + deltaV1;
|
|
|
|
v1 = Cm::SpatialVector(vel1.bottom, vel1.top);
|
|
}
|
|
}
|
|
|
|
/*Cm::SpatialVectorV FeatherstoneArticulation::pxcFsGetVelocity(PxU32 linkID)
|
|
{
|
|
|
|
Cm::SpatialVectorF& vel = mArticulationData.getMotionVelocity(linkID);
|
|
|
|
return Cm::SpatialVector(vel.bottom, vel.top);
|
|
}*/
|
|
|
|
Cm::SpatialVectorV FeatherstoneArticulation::pxcFsGetVelocityTGS(PxU32 linkID)
|
|
{
|
|
return getLinkVelocity(linkID);
|
|
}
|
|
|
|
//This is used in the solveExt1D, solveExtContact
|
|
void FeatherstoneArticulation::pxcFsApplyImpulse(PxU32 linkID, aos::Vec3V linkImpulseLinear, aos::Vec3V linkImpulseAngular, const PxReal* jointImpulse)
|
|
{
|
|
const ArticulationSolverDesc* desc = &mSolverDesc;
|
|
const ArticulationLink* links = desc->links;
|
|
ArticulationData& data = mArticulationData;
|
|
|
|
data.mJointDirty = true;
|
|
|
|
//impulse is in world space
|
|
Cm::SpatialVector impulse;
|
|
V4StoreA(Vec4V_From_Vec3V(linkImpulseAngular), &impulse.angular.x);
|
|
V4StoreA(Vec4V_From_Vec3V(linkImpulseLinear), &impulse.linear.x);
|
|
Cm::SpatialVectorF Z0(-impulse.linear, -impulse.angular);
|
|
|
|
for (PxU32 i = linkID; i; i = links[i].parent)
|
|
{
|
|
const PxU32 jointOffset = data.getJointData(i).jointOffset;
|
|
const PxU8 dofCount = data.getJointData(i).nbDof;
|
|
|
|
data.mSolverLinkSpatialImpulses[i] += Z0;
|
|
|
|
//Only apply the joint impulse to the inbound joint of linkID.
|
|
const PxReal* jointImpulseToApply = (linkID == i) ? jointImpulse : NULL;
|
|
|
|
Z0 = propagateImpulseW(
|
|
data.getRw(i),
|
|
Z0,
|
|
jointImpulseToApply, &data.mISInvStIS[jointOffset], &data.mWorldMotionMatrix[jointOffset], dofCount,
|
|
&data.mDeferredQstZ[jointOffset]);
|
|
}
|
|
|
|
|
|
data.mRootDeferredZ += Z0;
|
|
}
|
|
|
|
|
|
void FeatherstoneArticulation::pxcFsApplyImpulses(
|
|
PxU32 linkID1, const aos::Vec3V& linear1, const aos::Vec3V& angular1, const PxReal* jointImpulse1,
|
|
PxU32 linkID2, const aos::Vec3V& linear2, const aos::Vec3V& angular2, const PxReal* jointImpulse2)
|
|
{
|
|
if (0)
|
|
{
|
|
pxcFsApplyImpulse(linkID1, linear1, angular1, jointImpulse1);
|
|
pxcFsApplyImpulse(linkID2, linear2, angular2, jointImpulse2);
|
|
}
|
|
else
|
|
{
|
|
const ArticulationSolverDesc* desc = &mSolverDesc;
|
|
ArticulationData& data = mArticulationData;
|
|
data.mJointDirty = true;
|
|
ArticulationLink* links = static_cast<ArticulationLink*>(desc->links);
|
|
|
|
//impulse is in world space
|
|
Cm::SpatialVector impulse0;
|
|
V3StoreU(angular1, impulse0.angular);
|
|
V3StoreU(linear1, impulse0.linear);
|
|
|
|
Cm::SpatialVector impulse1;
|
|
V3StoreU(angular2, impulse1.angular);
|
|
V3StoreU(linear2, impulse1.linear);
|
|
|
|
Cm::SpatialVectorF Z1(-impulse0.linear, -impulse0.angular);
|
|
Cm::SpatialVectorF Z2(-impulse1.linear, -impulse1.angular);
|
|
|
|
ArticulationLink& link1 = links[linkID1];
|
|
ArticulationLink& link2 = links[linkID2];
|
|
|
|
const PxU32* pathToRoot1 = &mArticulationData.mPathToRootElements[link1.mPathToRootStartIndex];
|
|
const PxU32* pathToRoot2 = &mArticulationData.mPathToRootElements[link2.mPathToRootStartIndex];
|
|
|
|
const PxU32 numElems1 = link1.mPathToRootCount;
|
|
const PxU32 numElems2 = link2.mPathToRootCount;
|
|
|
|
//find the common link, work from one to that common, then the other to that common, then go from there upwards...
|
|
PxU32 offset = 0;
|
|
PxU32 commonLink = 0;
|
|
while (pathToRoot1[offset] == pathToRoot2[offset])
|
|
{
|
|
commonLink = pathToRoot1[offset++];
|
|
PX_ASSERT(links[commonLink].parent < commonLink);
|
|
if (offset >= numElems1 || offset >= numElems2)
|
|
break;
|
|
}
|
|
|
|
//The common link will either be linkID2, or its ancestors.
|
|
//The common link cannot be an index before either linkID2 or linkID
|
|
for (PxU32 i = linkID2; i != commonLink; i = links[i].parent)
|
|
{
|
|
const PxU32 jointOffset = mArticulationData.getJointData(i).jointOffset;
|
|
const PxU8 dofCount = mArticulationData.getJointData(i).nbDof;
|
|
|
|
//Note: linkID2 might be the common link. If this is the case, we will only apply
|
|
//jointImpulse2 when we propagate from the common link to the root.
|
|
//Watch out for that when we propagate from the common link.
|
|
const PxReal* jointImpulseToApply = (linkID2 == i) ? jointImpulse2 : NULL;
|
|
|
|
data.mSolverLinkSpatialImpulses[i] += Z2;
|
|
|
|
Z2 = propagateImpulseW(
|
|
mArticulationData.getRw(i),
|
|
Z2,
|
|
jointImpulseToApply, &data.mISInvStIS[jointOffset], &data.mWorldMotionMatrix[jointOffset], dofCount,
|
|
&data.mDeferredQstZ[jointOffset]);
|
|
}
|
|
|
|
for (PxU32 i = linkID1; i != commonLink; i = links[i].parent)
|
|
{
|
|
const PxU32 jointOffset = mArticulationData.getJointData(i).jointOffset;
|
|
const PxU8 dofCount = mArticulationData.getJointData(i).nbDof;
|
|
|
|
//Note: linkID1 might be the common link. If this is the case, we will only apply
|
|
//jointImpulse1 when we propagate from the common link to the root.
|
|
//Watch out for that when we propagate from the common link.
|
|
const PxReal* jointImpulseToApply = (linkID1 == i) ? jointImpulse1 : NULL;
|
|
|
|
data.mSolverLinkSpatialImpulses[i] += Z1;
|
|
|
|
Z1 = propagateImpulseW(
|
|
mArticulationData.getRw(i),
|
|
Z1,
|
|
jointImpulseToApply, &data.mISInvStIS[jointOffset], &data.mWorldMotionMatrix[jointOffset],dofCount,
|
|
&data.mDeferredQstZ[jointOffset]);
|
|
}
|
|
|
|
//If linkID1 (or linkID2) is the common link then we will not yet have applied
|
|
//jointImpulse1 (or jointImpulse2) to the inbound joint of the link.
|
|
//Work out how much joint impulse to apply to the inbound joint of the common link.
|
|
PxReal jointImpulseToApplyAtCommonLink[3] = {0, 0, 0};
|
|
if(((linkID1 == commonLink) && jointImpulse1) || ((linkID2 == commonLink) && jointImpulse2))
|
|
{
|
|
const PxU32 linkIndices[2] = {linkID1, linkID2};
|
|
const PxReal* jointImpulses[2]= {jointImpulse1, jointImpulse2};
|
|
const PxU32 dofCountAtCommonLink = mArticulationData.getJointData(commonLink).nbDof;
|
|
for(PxU32 k = 0; k < 2; k++)
|
|
{
|
|
const PxU32 linkId = linkIndices[k];
|
|
const PxReal* jointImpulse = jointImpulses[k];
|
|
if((linkId == commonLink) && jointImpulse)
|
|
{
|
|
for(PxU32 i = 0; i < dofCountAtCommonLink; i++)
|
|
{
|
|
jointImpulseToApplyAtCommonLink[i] += jointImpulse[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Cm::SpatialVectorF ZCommon = Z1 + Z2;
|
|
|
|
for (PxU32 i = commonLink; i; i = links[i].parent)
|
|
{
|
|
const PxU32 jointOffset = mArticulationData.getJointData(i).jointOffset;
|
|
const PxU8 dofCount = mArticulationData.getJointData(i).nbDof;
|
|
|
|
//Only apply a joint impulse to the inbound joint of commonLink.
|
|
//The joint impulse to apply to the inbound joint will only be non-zero if the common link
|
|
//is either linkID1 or linkID2.
|
|
const PxReal* jointImpulseToApply = (commonLink == i) ? jointImpulseToApplyAtCommonLink : NULL;
|
|
|
|
data.mSolverLinkSpatialImpulses[i] += ZCommon;
|
|
|
|
ZCommon = propagateImpulseW(
|
|
mArticulationData.getRw(i),
|
|
ZCommon,
|
|
jointImpulseToApply, &data.mISInvStIS[jointOffset], &data.mWorldMotionMatrix[jointOffset], dofCount,
|
|
&data.mDeferredQstZ[jointOffset]);
|
|
}
|
|
|
|
data.mRootDeferredZ += ZCommon;
|
|
}
|
|
}
|
|
|
|
//This version uses in updateBodies
|
|
PxQuat computeSphericalJointPositions(const PxQuat& relativeQuat,
|
|
const PxQuat& newRot, const PxQuat& pBody2WorldRot,
|
|
PxReal* jPositions, const Cm::UnAlignedSpatialVector* motionMatrix,
|
|
const PxU32 dofs);
|
|
|
|
PxQuat computeSphericalJointPositions(const PxQuat& relativeQuat,
|
|
const PxQuat& newRot, const PxQuat& pBody2WorldRot);
|
|
|
|
PxTransform FeatherstoneArticulation::propagateTransform(const PxU32 linkID, ArticulationLink* links,
|
|
ArticulationJointCoreData& jointDatum, Cm::SpatialVectorF* motionVelocities, const PxReal dt, const PxTransform& pBody2World,
|
|
const PxTransform& currentTransform, PxReal* jointVelocities, PxReal* jointPositions,
|
|
const Cm::UnAlignedSpatialVector* motionMatrix, const Cm::UnAlignedSpatialVector* /*worldMotionMatrix*/)
|
|
{
|
|
ArticulationLink& link = links[linkID];
|
|
|
|
const PxQuat relativeQuat = mArticulationData.mRelativeQuat[linkID];
|
|
|
|
ArticulationJointCore* joint = link.inboundJoint;
|
|
|
|
PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset];
|
|
PxReal* jPosition = &jointPositions[jointDatum.jointOffset];
|
|
|
|
PxQuat newParentToChild;
|
|
PxQuat newWorldQ;
|
|
PxVec3 r;
|
|
|
|
const PxVec3 childOffset = -joint->childPose.p;
|
|
const PxVec3 parentOffset = joint->parentPose.p;
|
|
|
|
switch (joint->jointType)
|
|
{
|
|
case PxArticulationJointType::ePRISMATIC:
|
|
{
|
|
PxReal tJointPosition = jPosition[0] + (jVelocity[0]) * dt;
|
|
|
|
const PxU32 dofId = link.inboundJoint->dofIds[0];
|
|
|
|
if (link.inboundJoint->motion[dofId] == PxArticulationMotion::eLIMITED)
|
|
{
|
|
if (tJointPosition < (link.inboundJoint->limits[dofId].low))
|
|
tJointPosition = link.inboundJoint->limits[dofId].low;
|
|
if (tJointPosition >(link.inboundJoint->limits[dofId].high))
|
|
tJointPosition = link.inboundJoint->limits[dofId].high;
|
|
}
|
|
|
|
jPosition[0] = tJointPosition;
|
|
|
|
newParentToChild = relativeQuat;
|
|
const PxVec3 e = newParentToChild.rotate(parentOffset);
|
|
const PxVec3 d = childOffset;
|
|
|
|
r = e + d + motionMatrix[0].bottom * tJointPosition;
|
|
break;
|
|
}
|
|
case PxArticulationJointType::eREVOLUTE:
|
|
case PxArticulationJointType::eREVOLUTE_UNWRAPPED:
|
|
{
|
|
PxReal tJointPosition = jPosition[0] + (jVelocity[0]) * dt;
|
|
|
|
/*PxU8 dofId = link.inboundJoint->dofIds[0];
|
|
if (link.inboundJoint->motion[dofId] == PxArticulationMotion::eLIMITED)
|
|
{
|
|
if (tJointPosition < (link.inboundJoint->limits[dofId].low))
|
|
tJointPosition = link.inboundJoint->limits[dofId].low;
|
|
if (tJointPosition >(link.inboundJoint->limits[dofId].high))
|
|
tJointPosition = link.inboundJoint->limits[dofId].high;
|
|
}*/
|
|
|
|
jPosition[0] = tJointPosition;
|
|
|
|
const PxVec3& u = motionMatrix[0].top;
|
|
|
|
PxQuat jointRotation = PxQuat(-tJointPosition, u);
|
|
if (jointRotation.w < 0) //shortest angle.
|
|
jointRotation = -jointRotation;
|
|
|
|
newParentToChild = (jointRotation * relativeQuat).getNormalized();
|
|
|
|
const PxVec3 e = newParentToChild.rotate(parentOffset);
|
|
const PxVec3 d = childOffset;
|
|
r = e + d;
|
|
|
|
PX_ASSERT(r.isFinite());
|
|
|
|
break;
|
|
}
|
|
case PxArticulationJointType::eSPHERICAL:
|
|
{
|
|
Cm::SpatialVectorF worldVel = motionVelocities[linkID];
|
|
|
|
const PxTransform oldTransform = currentTransform;
|
|
|
|
PxVec3 worldAngVel = worldVel.top;
|
|
//PxVec3 worldAngVel = motionVelocities[linkID].top;
|
|
|
|
PxReal dist = worldAngVel.normalize() * dt;
|
|
|
|
if (dist > 1e-6f)
|
|
newWorldQ = PxQuat(dist, worldAngVel) * oldTransform.q;
|
|
else
|
|
newWorldQ = oldTransform.q;
|
|
|
|
//newWorldQ = Ps::exp(worldAngVel*dt) * oldTransform.q;
|
|
|
|
//PxVec3 axis;
|
|
|
|
newParentToChild = computeSphericalJointPositions(mArticulationData.mRelativeQuat[linkID], newWorldQ,
|
|
pBody2World.q);
|
|
|
|
PxQuat jointRotation = newParentToChild * relativeQuat.getConjugate();
|
|
|
|
if(jointRotation.w < 0.0f)
|
|
jointRotation = -jointRotation;
|
|
|
|
/*PxVec3 axis = jointRotation.getImaginaryPart();
|
|
for (PxU32 i = 0; i < jointDatum.dof; ++i)
|
|
{
|
|
PxVec3 sa = mArticulationData.getMotionMatrix(jointDatum.jointOffset + i).top;
|
|
PxReal angle = -compAng(sa.dot(axis), jointRotation.w);
|
|
jPosition[i] = angle;
|
|
}*/
|
|
|
|
PxVec3 axis; PxReal angle;
|
|
jointRotation.toRadiansAndUnitAxis(angle, axis);
|
|
axis *= angle;
|
|
for (PxU32 i = 0; i < jointDatum.nbDof; ++i)
|
|
{
|
|
PxVec3 sa = mArticulationData.getMotionMatrix(jointDatum.jointOffset + i).top;
|
|
PxReal ang = -sa.dot(axis);
|
|
jPosition[i] = ang;
|
|
}
|
|
const PxVec3 e = newParentToChild.rotate(parentOffset);
|
|
const PxVec3 d = childOffset;
|
|
r = e + d;
|
|
|
|
PX_ASSERT(r.isFinite());
|
|
|
|
break;
|
|
}
|
|
case PxArticulationJointType::eFIX:
|
|
{
|
|
//this is fix joint so joint don't have velocity
|
|
newParentToChild = relativeQuat;
|
|
|
|
const PxVec3 e = newParentToChild.rotate(parentOffset);
|
|
const PxVec3 d = childOffset;
|
|
|
|
r = e + d;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
PxTransform cBody2World;
|
|
cBody2World.q = (pBody2World.q * newParentToChild.getConjugate()).getNormalized();
|
|
cBody2World.p = pBody2World.p + cBody2World.q.rotate(r);
|
|
|
|
PX_ASSERT(cBody2World.isSane());
|
|
|
|
return cBody2World;
|
|
}
|
|
|
|
const PxQuat& FeatherstoneArticulation::getDeltaQ(PxU32 linkID) const
|
|
{
|
|
return mArticulationData.mDeltaQ[linkID];
|
|
}
|
|
|
|
Cm::SpatialVectorF FeatherstoneArticulation::propagateVelocityW(
|
|
const PxVec3& parentToChild, const Cm::SpatialVectorF& parentLinkDeltaV,
|
|
const Dy::SpatialMatrix& spatialInertia, const Cm::SpatialVectorF& Z,
|
|
const PxReal* jointDofImpulses, const InvStIs& invStIs, const Cm::UnAlignedSpatialVector* motionMatrixW, const PxU32 dofCount,
|
|
PxReal* jointVelocity)
|
|
{
|
|
const Cm::SpatialVectorF pDeltaV = translateSpatialVector(-parentToChild, parentLinkDeltaV); //parent velocity change
|
|
|
|
// -s_i^T * [I_i^A * translated(vParent) + Z]
|
|
const Cm::SpatialVectorF temp = spatialInertia * pDeltaV + Z;
|
|
PxReal tJointDelta[6];
|
|
for (PxU32 ind = 0; ind < dofCount; ++ind)
|
|
{
|
|
const Cm::UnAlignedSpatialVector& sa = motionMatrixW[ind];
|
|
const PxReal jointDofImpulse = jointDofImpulses ? jointDofImpulses[ind] : 0.0f;
|
|
tJointDelta[ind] = jointDofImpulse - sa.innerProduct(temp);
|
|
}
|
|
|
|
//qdot = [1/ s_i^T * i_i^A * s_i] * { -[s_i^T * I_i^A * translated(vParent) + Z]}
|
|
//deltaV = qdot * s^T_i
|
|
Cm::SpatialVectorF jointSpatialDeltaV(PxVec3(0.f), PxVec3(0.f));
|
|
for (PxU32 ind = 0; ind < dofCount; ++ind)
|
|
{
|
|
PxReal jDelta = 0.f;
|
|
for (PxU32 ind2 = 0; ind2 < dofCount; ++ind2)
|
|
{
|
|
jDelta += invStIs.invStIs[ind2][ind] * tJointDelta[ind2];
|
|
}
|
|
|
|
if(jointVelocity)
|
|
jointVelocity[ind] += jDelta;
|
|
jointSpatialDeltaV.top += motionMatrixW[ind].top * jDelta;
|
|
jointSpatialDeltaV.bottom += motionMatrixW[ind].bottom * jDelta;
|
|
}
|
|
|
|
//deltaV = translated(parentLinkDeltaV) + qdot * s^T_i
|
|
return pDeltaV + jointSpatialDeltaV;
|
|
}
|
|
|
|
|
|
|
|
Cm::SpatialVectorF FeatherstoneArticulation::getDeltaVWithDeltaJV(const bool fixBase, const PxU32 linkID,
|
|
const ArticulationData& data, Cm::SpatialVectorF* Z,
|
|
PxReal* jointVelocities)
|
|
{
|
|
Cm::SpatialVectorF deltaV = Cm::SpatialVectorF::Zero();
|
|
if (!fixBase)
|
|
{
|
|
//velocity change
|
|
//SpatialMatrix inverseArticulatedInertia = hLinkDatum.spatialArticulatedInertia.getInverse();
|
|
const SpatialMatrix& inverseArticulatedInertia = data.mBaseInvSpatialArticulatedInertiaW;
|
|
deltaV = inverseArticulatedInertia * (-Z[0]);
|
|
}
|
|
|
|
ArticulationLink* links = data.getLinks();
|
|
const ArticulationLink& link = links[linkID];
|
|
const PxU32* pathToRoot = &data.mPathToRootElements[link.mPathToRootStartIndex];
|
|
const PxU32 numElems = link.mPathToRootCount;
|
|
|
|
for (PxU32 i = 0; i < numElems; ++i)
|
|
{
|
|
const PxU32 index = pathToRoot[i];
|
|
PX_ASSERT(links[index].parent < index);
|
|
ArticulationJointCoreData& tJointDatum = data.getJointData(index);
|
|
PxReal* jVelocity = &jointVelocities[tJointDatum.jointOffset];
|
|
deltaV = FeatherstoneArticulation::propagateVelocityW(
|
|
data.getRw(index), deltaV,
|
|
data.mWorldSpatialArticulatedInertia[index], Z[index],
|
|
NULL, data.mInvStIs[index], &data.mWorldMotionMatrix[tJointDatum.jointOffset], tJointDatum.nbDof,
|
|
jVelocity);
|
|
}
|
|
|
|
return deltaV;
|
|
}
|
|
|
|
void FeatherstoneArticulation::getZ(const PxU32 linkID,
|
|
const ArticulationData& data, Cm::SpatialVectorF* Z,
|
|
const Cm::SpatialVectorF& impulse)
|
|
{
|
|
ArticulationLink* links = data.getLinks();
|
|
|
|
//impulse need to be in linkID space!!!
|
|
Z[linkID] = -impulse;
|
|
|
|
for (PxU32 i = linkID; i; i = links[i].parent)
|
|
{
|
|
ArticulationLink& tLink = links[i];
|
|
const PxU32 jointOffset = data.getJointData(i).jointOffset;
|
|
const PxU8 dofCount = data.getJointData(i).nbDof;
|
|
Z[tLink.parent] = propagateImpulseW(
|
|
data.getRw(i),
|
|
Z[i],
|
|
NULL, &data.mISInvStIS[jointOffset], &data.mWorldMotionMatrix[jointOffset], dofCount,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
Cm::SpatialVectorF FeatherstoneArticulation::getImpulseResponseW(
|
|
const PxU32 linkID,
|
|
const ArticulationData& data,
|
|
const Cm::SpatialVectorF& impulse)
|
|
{
|
|
return data.getImpulseResponseMatrixWorld()[linkID].getLinkDeltaVImpulseResponse(impulse);
|
|
}
|
|
|
|
//This method use in impulse self response. The input impulse is in the link space
|
|
Cm::SpatialVectorF FeatherstoneArticulation::getImpulseResponseWithJ(
|
|
const PxU32 linkID,
|
|
const bool fixBase,
|
|
const ArticulationData& data,
|
|
Cm::SpatialVectorF* Z,
|
|
const Cm::SpatialVectorF& impulse,
|
|
PxReal* jointVelocites)
|
|
{
|
|
getZ(linkID, data, Z, impulse);
|
|
|
|
return getDeltaVWithDeltaJV(fixBase, linkID, data, Z, jointVelocites);
|
|
}
|
|
|
|
void FeatherstoneArticulation::saveVelocity(FeatherstoneArticulation* articulation, Cm::SpatialVectorF* deltaV)
|
|
{
|
|
ArticulationData& data = articulation->mArticulationData;
|
|
|
|
//update all links' motion velocity, joint delta velocity if there are contacts/constraints
|
|
if (data.mJointDirty)
|
|
{
|
|
PxcFsFlushVelocity(*articulation, deltaV);
|
|
}
|
|
|
|
const PxU32 linkCount = data.getLinkCount();
|
|
//copy motion velocites
|
|
Cm::SpatialVectorF* vels = data.getMotionVelocities();
|
|
Cm::SpatialVectorF* posVels = data.getPosIterMotionVelocities();
|
|
PxMemCopy(posVels, vels, sizeof(Cm::SpatialVectorF) * linkCount);
|
|
|
|
//copy joint velocities
|
|
const PxU32 dofs = data.getDofs();
|
|
|
|
PxReal* jPosVels = data.getPosIterJointVelocities();
|
|
|
|
const PxReal* jNewVels = data.getJointNewVelocities();
|
|
|
|
PxMemCopy(jPosVels, jNewVels, sizeof(PxReal) * dofs);
|
|
|
|
articulation->concludeInternalConstraints(false);
|
|
}
|
|
|
|
void FeatherstoneArticulation::saveVelocityTGS(FeatherstoneArticulation* articulation, PxReal invDtF32)
|
|
{
|
|
ArticulationData& data = articulation->mArticulationData;
|
|
|
|
//KS - we should not need to flush velocity because we will have already stepped the articulation with TGS
|
|
|
|
const PxU32 linkCount = data.getLinkCount();
|
|
Cm::SpatialVectorF* posVels = data.getPosIterMotionVelocities();
|
|
for (PxU32 i = 0; i < linkCount; ++i)
|
|
{
|
|
posVels[i] = posVels[i] * invDtF32;
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::getImpulseSelfResponse(
|
|
PxU32 linkID0,
|
|
PxU32 linkID1,
|
|
const Cm::SpatialVector& impulse0,
|
|
const Cm::SpatialVector& impulse1,
|
|
Cm::SpatialVector& deltaV0,
|
|
Cm::SpatialVector& deltaV1) const
|
|
{
|
|
FeatherstoneArticulation::getImpulseSelfResponse(mArticulationData.getLinks(),
|
|
const_cast<Dy::ArticulationData&>(mArticulationData), linkID0, reinterpret_cast<const Cm::SpatialVectorV&>(impulse0),
|
|
reinterpret_cast<Cm::SpatialVectorV&>(deltaV0), linkID1, reinterpret_cast<const Cm::SpatialVectorV&>(impulse1),
|
|
reinterpret_cast<Cm::SpatialVectorV&>(deltaV1));
|
|
}
|
|
|
|
void FeatherstoneArticulation::getImpulseResponseSlow(Dy::ArticulationLink* links,
|
|
ArticulationData& data,
|
|
PxU32 linkID0_,
|
|
const Cm::SpatialVector& impulse0,
|
|
Cm::SpatialVector& deltaV0,
|
|
PxU32 linkID1_,
|
|
const Cm::SpatialVector& impulse1,
|
|
Cm::SpatialVector& deltaV1)
|
|
{
|
|
const PxU32 linkCount = data.getLinkCount();
|
|
|
|
PX_ALLOCA(_stack, PxU32, linkCount);
|
|
PxU32* stack = _stack;
|
|
|
|
PxU32 i0, i1;//, ic;
|
|
|
|
PxU32 linkID0 = linkID0_;
|
|
PxU32 linkID1 = linkID1_;
|
|
|
|
for (i0 = linkID0, i1 = linkID1; i0 != i1;) // find common path
|
|
{
|
|
if (i0<i1)
|
|
i1 = links[i1].parent;
|
|
else
|
|
i0 = links[i0].parent;
|
|
}
|
|
|
|
PxU32 common = i0;
|
|
|
|
Cm::SpatialVectorF Z0(-impulse0.linear, -impulse0.angular);
|
|
Cm::SpatialVectorF Z1(-impulse1.linear, -impulse1.angular);
|
|
|
|
PxReal qstZ[192];
|
|
|
|
PxMemZero(qstZ, data.getDofs() * sizeof(PxReal));
|
|
|
|
//Z[linkID0] = Z0;
|
|
//Z[linkID1] = Z1;
|
|
|
|
for (i0 = 0; linkID0 != common; linkID0 = links[linkID0].parent)
|
|
{
|
|
const PxU32 jointOffset = data.getJointData(linkID0).jointOffset;
|
|
const PxU8 dofCount = data.getJointData(linkID0).nbDof;
|
|
Z0 = propagateImpulseW(
|
|
data.getRw(linkID0),
|
|
Z0,
|
|
NULL, &data.getWorldIsInvD(jointOffset), &data.getWorldMotionMatrix(jointOffset), dofCount,
|
|
&qstZ[jointOffset]);
|
|
stack[i0++] = linkID0;
|
|
}
|
|
|
|
for (i1 = i0; linkID1 != common; linkID1 = links[linkID1].parent)
|
|
{
|
|
const PxU32 jointOffset = data.getJointData(linkID1).jointOffset;
|
|
const PxU8 dofCount = data.getJointData(linkID1).nbDof;
|
|
Z1 = propagateImpulseW(
|
|
data.getRw(linkID1),
|
|
Z1,
|
|
NULL, &data.getWorldIsInvD(jointOffset), &data.getWorldMotionMatrix(jointOffset), dofCount,
|
|
&qstZ[jointOffset]);
|
|
stack[i1++] = linkID1;
|
|
}
|
|
|
|
Cm::SpatialVectorF ZZ = Z0 + Z1;
|
|
|
|
Cm::SpatialVectorF v = data.getImpulseResponseMatrixWorld()[common].getLinkDeltaVImpulseResponse(-ZZ);
|
|
|
|
Cm::SpatialVectorF dv1 = v;
|
|
for (PxU32 index = i1; (index--) > i0;)
|
|
{
|
|
//Dy::ArticulationLinkData& tLinkDatum = data.getLinkData(stack[index]);
|
|
const PxU32 id = stack[index];
|
|
const PxU32 jointOffset = data.getJointData(id).jointOffset;
|
|
const PxU32 dofCount = data.getJointData(id).nbDof;
|
|
dv1 = propagateAccelerationW(
|
|
data.getRw(id), dv1,
|
|
data.mInvStIs[id], &data.mWorldMotionMatrix[jointOffset], &data.mIsW[jointOffset], &qstZ[jointOffset], dofCount,
|
|
NULL);
|
|
}
|
|
|
|
Cm::SpatialVectorF dv0= v;
|
|
for (PxU32 index = i0; (index--) > 0;)
|
|
{
|
|
const PxU32 id = stack[index];
|
|
const PxU32 jointOffset = data.getJointData(id).jointOffset;
|
|
const PxU32 dofCount = data.getJointData(id).nbDof;
|
|
dv0 = propagateAccelerationW(
|
|
data.getRw(id), dv0, data.mInvStIs[id],
|
|
&data.mWorldMotionMatrix[jointOffset], &data.mIsW[jointOffset], &qstZ[jointOffset], dofCount,
|
|
NULL);
|
|
}
|
|
|
|
deltaV0.linear = dv0.bottom;
|
|
deltaV0.angular = dv0.top;
|
|
|
|
deltaV1.linear = dv1.bottom;
|
|
deltaV1.angular = dv1.top;
|
|
}
|
|
|
|
void FeatherstoneArticulation::getImpulseSelfResponse(ArticulationLink* links,
|
|
ArticulationData& data,
|
|
PxU32 linkID0,
|
|
const Cm::SpatialVectorV& impulse0,
|
|
Cm::SpatialVectorV& deltaV0,
|
|
PxU32 linkID1,
|
|
const Cm::SpatialVectorV& impulse1,
|
|
Cm::SpatialVectorV& deltaV1)
|
|
{
|
|
ArticulationLink& link = links[linkID1];
|
|
|
|
if (link.parent == linkID0)
|
|
{
|
|
PX_ASSERT(linkID0 == link.parent);
|
|
PX_ASSERT(linkID0 < linkID1);
|
|
|
|
//impulse is in world space
|
|
Cm::SpatialVectorF imp1;
|
|
V4StoreA(Vec4V_From_Vec3V(impulse1.angular), &imp1.bottom.x);
|
|
V4StoreA(Vec4V_From_Vec3V(impulse1.linear), &imp1.top.x);
|
|
|
|
Cm::SpatialVectorF imp0;
|
|
V4StoreA(Vec4V_From_Vec3V(impulse0.angular), &imp0.bottom.x);
|
|
V4StoreA(Vec4V_From_Vec3V(impulse0.linear), &imp0.top.x);
|
|
|
|
Cm::SpatialVectorF Z1W(-imp1.top, -imp1.bottom);
|
|
|
|
const PxU32 jointOffset1 = data.getJointData(linkID1).jointOffset;
|
|
const PxU8 dofCount1 = data.getJointData(linkID1).nbDof;
|
|
|
|
PxReal qstZ[3] = { 0.f, 0.f, 0.f };
|
|
const Cm::SpatialVectorF Z0W = propagateImpulseW(
|
|
data.getRw(linkID1),
|
|
Z1W,
|
|
NULL, &data.mISInvStIS[jointOffset1], &data.mWorldMotionMatrix[jointOffset1], dofCount1,
|
|
qstZ);
|
|
|
|
//in parent space
|
|
const Cm::SpatialVectorF impulseDifW = imp0 - Z0W;
|
|
|
|
//calculate velocity change start from the parent link to the root
|
|
const Cm::SpatialVectorF delV0W = FeatherstoneArticulation::getImpulseResponseW(linkID0, data, impulseDifW);
|
|
|
|
const Cm::SpatialVectorF delV1W = propagateAccelerationW(
|
|
data.getRw(linkID1), delV0W,
|
|
data.mInvStIs[linkID1], &data.mWorldMotionMatrix[jointOffset1], &data.mIsW[jointOffset1], qstZ, dofCount1,
|
|
NULL);
|
|
|
|
deltaV0.linear = Vec3V_From_Vec4V(V4LoadA(&delV0W.bottom.x));
|
|
deltaV0.angular = Vec3V_From_Vec4V(V4LoadA(&delV0W.top.x));
|
|
deltaV1.linear = Vec3V_From_Vec4V(V4LoadA(&delV1W.bottom.x));
|
|
deltaV1.angular = Vec3V_From_Vec4V(V4LoadA(&delV1W.top.x));
|
|
}
|
|
else
|
|
{
|
|
getImpulseResponseSlow(links, data, linkID0, reinterpret_cast<const Cm::SpatialVector&>(impulse0),
|
|
reinterpret_cast<Cm::SpatialVector&>(deltaV0), linkID1,
|
|
reinterpret_cast<const Cm::SpatialVector&>(impulse1), reinterpret_cast<Cm::SpatialVector&>(deltaV1));
|
|
}
|
|
}
|
|
|
|
struct ArticulationStaticConstraintSortPredicate
|
|
{
|
|
bool operator()(const PxSolverConstraintDesc& left, const PxSolverConstraintDesc& right) const
|
|
{
|
|
PxU32 linkIndexA = left.linkIndexA != PxSolverConstraintDesc::RIGID_BODY ? left.linkIndexA : left.linkIndexB;
|
|
PxU32 linkIndexB = right.linkIndexA != PxSolverConstraintDesc::RIGID_BODY ? right.linkIndexA : right.linkIndexB;
|
|
|
|
return linkIndexA < linkIndexB;
|
|
}
|
|
};
|
|
|
|
bool createFinalizeSolverContactsStep(PxTGSSolverContactDesc& contactDesc,
|
|
PxsContactManagerOutput& output,
|
|
ThreadContext& threadContext,
|
|
const PxReal invDtF32,
|
|
const PxReal invTotalDt,
|
|
const PxReal totalDt,
|
|
const PxReal stepDt,
|
|
const PxReal bounceThresholdF32,
|
|
const PxReal frictionOffsetThreshold,
|
|
const PxReal correlationDistance,
|
|
const PxReal biasCoefficient,
|
|
PxConstraintAllocator& constraintAllocator);
|
|
|
|
void FeatherstoneArticulation::prepareStaticConstraintsTGS(const PxReal stepDt, const PxReal totalDt, const PxReal invStepDt, const PxReal invTotalDt,
|
|
PxsContactManagerOutputIterator& outputs, Dy::ThreadContext& threadContext, PxReal correlationDist, PxReal bounceThreshold, PxReal frictionOffsetThreshold,
|
|
PxTGSSolverBodyData* solverBodyData, PxTGSSolverBodyTxInertia* txInertia, PxsConstraintBlockManager& blockManager,
|
|
Dy::ConstraintWriteback* constraintWritebackPool, const PxReal biasCoefficient, const PxReal lengthScale)
|
|
{
|
|
BlockAllocator blockAllocator(blockManager, threadContext.mConstraintBlockStream, threadContext.mFrictionPatchStreamPair, threadContext.mConstraintSize);
|
|
|
|
const PxTransform id(PxIdentity);
|
|
|
|
PxSort<PxSolverConstraintDesc, ArticulationStaticConstraintSortPredicate>(mStatic1DConstraints.begin(), mStatic1DConstraints.size(), ArticulationStaticConstraintSortPredicate());
|
|
PxSort<PxSolverConstraintDesc, ArticulationStaticConstraintSortPredicate>(mStaticContactConstraints.begin(), mStaticContactConstraints.size(), ArticulationStaticConstraintSortPredicate());
|
|
|
|
for (PxU32 i = 0; i < mStatic1DConstraints.size(); ++i)
|
|
{
|
|
PxSolverConstraintDesc& desc = mStatic1DConstraints[i];
|
|
|
|
PxU32 linkIndex = desc.linkIndexA != PxSolverConstraintDesc::RIGID_BODY ? desc.linkIndexA : desc.linkIndexB;
|
|
|
|
PX_ASSERT(desc.constraintType == DY_SC_TYPE_RB_1D);
|
|
const Constraint* constraint = reinterpret_cast<const Constraint*>(desc.constraint);
|
|
|
|
SolverConstraintShaderPrepDesc shaderPrepDesc;
|
|
PxTGSSolverConstraintPrepDesc prepDesc;
|
|
|
|
const PxConstraintSolverPrep solverPrep = constraint->solverPrep;
|
|
const void* constantBlock = constraint->constantBlock;
|
|
const PxU32 constantBlockByteSize = constraint->constantBlockSize;
|
|
const PxTransform& pose0 = (constraint->body0 ? constraint->body0->getPose() : id);
|
|
const PxTransform& pose1 = (constraint->body1 ? constraint->body1->getPose() : id);
|
|
const PxTGSSolverBodyVel* sbody0 = desc.tgsBodyA;
|
|
const PxTGSSolverBodyVel* sbody1 = desc.tgsBodyB;
|
|
PxTGSSolverBodyData* sbodyData0 = &solverBodyData[desc.linkIndexA != PxSolverConstraintDesc::RIGID_BODY ? 0 : desc.bodyADataIndex];
|
|
PxTGSSolverBodyData* sbodyData1 = &solverBodyData[desc.linkIndexB != PxSolverConstraintDesc::RIGID_BODY ? 0 : desc.bodyBDataIndex];
|
|
PxTGSSolverBodyTxInertia& txI0 = txInertia[desc.bodyADataIndex];
|
|
PxTGSSolverBodyTxInertia& txI1 = txInertia[desc.bodyBDataIndex];
|
|
|
|
shaderPrepDesc.constantBlock = constantBlock;
|
|
shaderPrepDesc.constantBlockByteSize = constantBlockByteSize;
|
|
shaderPrepDesc.constraint = constraint;
|
|
shaderPrepDesc.solverPrep = solverPrep;
|
|
|
|
prepDesc.desc = static_cast<PxSolverConstraintDesc*>(&desc);
|
|
prepDesc.bodyFrame0 = pose0;
|
|
prepDesc.bodyFrame1 = pose1;
|
|
prepDesc.body0 = sbody0;
|
|
prepDesc.body1 = sbody1;
|
|
prepDesc.body0TxI = &txI0;
|
|
prepDesc.body1TxI = &txI1;
|
|
prepDesc.bodyData0 = sbodyData0;
|
|
prepDesc.bodyData1 = sbodyData1;
|
|
prepDesc.linBreakForce = constraint->linBreakForce;
|
|
prepDesc.angBreakForce = constraint->angBreakForce;
|
|
prepDesc.writeback = &constraintWritebackPool[constraint->index];
|
|
setupConstraintFlags(prepDesc, constraint->flags);
|
|
prepDesc.minResponseThreshold = constraint->minResponseThreshold;
|
|
|
|
prepDesc.bodyState0 = desc.linkIndexA == PxSolverConstraintDesc::RIGID_BODY ? PxSolverContactDesc::eDYNAMIC_BODY : PxSolverContactDesc::eARTICULATION;
|
|
prepDesc.bodyState1 = desc.linkIndexB == PxSolverConstraintDesc::RIGID_BODY ? PxSolverContactDesc::eDYNAMIC_BODY : PxSolverContactDesc::eARTICULATION;
|
|
|
|
SetupSolverConstraintStep(shaderPrepDesc, prepDesc, blockAllocator, stepDt, totalDt, invStepDt, invTotalDt, lengthScale, biasCoefficient);
|
|
|
|
if (desc.constraint)
|
|
{
|
|
if (mArticulationData.mNbStatic1DConstraints[linkIndex] == 0)
|
|
mArticulationData.mStatic1DConstraintStartIndex[linkIndex] = i;
|
|
mArticulationData.mNbStatic1DConstraints[linkIndex]++;
|
|
}
|
|
else
|
|
{
|
|
//Shuffle down
|
|
mStatic1DConstraints.remove(i);
|
|
i--;
|
|
}
|
|
}
|
|
|
|
for (PxU32 i = 0; i < mStaticContactConstraints.size(); ++i)
|
|
{
|
|
PxSolverConstraintDesc& desc = mStaticContactConstraints[i];
|
|
|
|
PxU32 linkIndex = desc.linkIndexA != PxSolverConstraintDesc::RIGID_BODY ? desc.linkIndexA : desc.linkIndexB;
|
|
|
|
PX_ASSERT(desc.constraintType == DY_SC_TYPE_RB_CONTACT);
|
|
|
|
PxTGSSolverContactDesc blockDesc;
|
|
PxsContactManager* cm = reinterpret_cast<PxsContactManager*>(desc.constraint);
|
|
PxcNpWorkUnit& unit = cm->getWorkUnit();
|
|
PxsContactManagerOutput* cmOutput = &outputs.getContactManagerOutput(unit.mNpIndex);
|
|
|
|
PxTGSSolverBodyVel& b0 = *desc.tgsBodyA;
|
|
PxTGSSolverBodyVel& b1 = *desc.tgsBodyB;
|
|
|
|
PxTGSSolverBodyData& data0 = desc.linkIndexA != PxSolverConstraintDesc::RIGID_BODY ? solverBodyData[0] : solverBodyData[desc.bodyADataIndex];
|
|
PxTGSSolverBodyData& data1 = desc.linkIndexB != PxSolverConstraintDesc::RIGID_BODY ? solverBodyData[0] : solverBodyData[desc.bodyBDataIndex];
|
|
|
|
PxTGSSolverBodyTxInertia& txI0 = txInertia[desc.bodyADataIndex];
|
|
PxTGSSolverBodyTxInertia& txI1 = txInertia[desc.bodyBDataIndex];
|
|
|
|
blockDesc.bodyFrame0 = unit.mRigidCore0->body2World;
|
|
blockDesc.bodyFrame1 = unit.mRigidCore1->body2World;
|
|
blockDesc.shapeInteraction = cm->getShapeInteraction();
|
|
blockDesc.contactForces = cmOutput->contactForces;
|
|
blockDesc.desc = static_cast<PxSolverConstraintDesc*>(&desc);
|
|
blockDesc.body0 = &b0;
|
|
blockDesc.body1 = &b1;
|
|
blockDesc.body0TxI = &txI0;
|
|
blockDesc.body1TxI = &txI1;
|
|
blockDesc.bodyData0 = &data0;
|
|
blockDesc.bodyData1 = &data1;
|
|
blockDesc.hasForceThresholds = !!(unit.mFlags & PxcNpWorkUnitFlag::eFORCE_THRESHOLD);
|
|
blockDesc.disableStrongFriction = !!(unit.mFlags & PxcNpWorkUnitFlag::eDISABLE_STRONG_FRICTION);
|
|
blockDesc.bodyState0 = (unit.mFlags & PxcNpWorkUnitFlag::eARTICULATION_BODY0) ? PxSolverContactDesc::eARTICULATION : PxSolverContactDesc::eDYNAMIC_BODY;
|
|
blockDesc.bodyState1 = (unit.mFlags & PxcNpWorkUnitFlag::eARTICULATION_BODY1) ? PxSolverContactDesc::eARTICULATION : (unit.mFlags & PxcNpWorkUnitFlag::eHAS_KINEMATIC_ACTOR) ? PxSolverContactDesc::eKINEMATIC_BODY :
|
|
((unit.mFlags & PxcNpWorkUnitFlag::eDYNAMIC_BODY1) ? PxSolverContactDesc::eDYNAMIC_BODY : PxSolverContactDesc::eSTATIC_BODY);
|
|
//blockDesc.flags = unit.flags;
|
|
|
|
const PxReal maxImpulse0 = (unit.mFlags & PxcNpWorkUnitFlag::eARTICULATION_BODY0) ? static_cast<const PxsBodyCore*>(unit.mRigidCore0)->maxContactImpulse : data0.maxContactImpulse;
|
|
const PxReal maxImpulse1 = (unit.mFlags & PxcNpWorkUnitFlag::eARTICULATION_BODY1) ? static_cast<const PxsBodyCore*>(unit.mRigidCore1)->maxContactImpulse : data1.maxContactImpulse;
|
|
|
|
unit.setInvMassScaleFromDominance(blockDesc.invMassScales);
|
|
|
|
blockDesc.restDistance = unit.mRestDistance;
|
|
blockDesc.frictionPtr = unit.mFrictionDataPtr;
|
|
blockDesc.frictionCount = unit.mFrictionPatchCount;
|
|
blockDesc.maxCCDSeparation = PX_MAX_F32;
|
|
blockDesc.maxImpulse = PxMin(maxImpulse0, maxImpulse1);
|
|
blockDesc.torsionalPatchRadius = unit.mTorsionalPatchRadius;
|
|
blockDesc.minTorsionalPatchRadius = unit.mMinTorsionalPatchRadius;
|
|
blockDesc.offsetSlop = unit.mOffsetSlop;
|
|
|
|
createFinalizeSolverContactsStep(blockDesc, *cmOutput, threadContext,
|
|
invStepDt, invTotalDt, totalDt, stepDt, bounceThreshold, frictionOffsetThreshold,
|
|
correlationDist, biasCoefficient, blockAllocator);
|
|
|
|
getContactManagerConstraintDesc(*cmOutput, *cm, desc);
|
|
|
|
updateFrictionAnchorCountAndPosition(desc, *cmOutput, blockDesc);
|
|
|
|
unit.mFrictionDataPtr = blockDesc.frictionPtr;
|
|
unit.mFrictionPatchCount = blockDesc.frictionCount;
|
|
//KS - Don't track this for now!
|
|
//axisConstraintCount += blockDesc.axisConstraintCount;
|
|
|
|
if (desc.constraint)
|
|
{
|
|
if (mArticulationData.mNbStaticContactConstraints[linkIndex] == 0)
|
|
mArticulationData.mStaticContactConstraintStartIndex[linkIndex] = i;
|
|
mArticulationData.mNbStaticContactConstraints[linkIndex]++;
|
|
}
|
|
else
|
|
{
|
|
//Shuffle down
|
|
mStaticContactConstraints.remove(i);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::prepareStaticConstraints(const PxReal dt, const PxReal invDt, PxsContactManagerOutputIterator& outputs,
|
|
Dy::ThreadContext& threadContext, PxReal correlationDist, PxReal bounceThreshold, PxReal frictionOffsetThreshold,
|
|
PxReal ccdMaxSeparation, PxSolverBodyData* solverBodyData, PxsConstraintBlockManager& blockManager,
|
|
Dy::ConstraintWriteback* constraintWritebackPool)
|
|
{
|
|
BlockAllocator blockAllocator(blockManager, threadContext.mConstraintBlockStream, threadContext.mFrictionPatchStreamPair, threadContext.mConstraintSize);
|
|
|
|
const PxTransform id(PxIdentity);
|
|
|
|
Cm::SpatialVectorF* Z = threadContext.mZVector.begin();
|
|
|
|
PxSort<PxSolverConstraintDesc, ArticulationStaticConstraintSortPredicate>(mStatic1DConstraints.begin(), mStatic1DConstraints.size(), ArticulationStaticConstraintSortPredicate());
|
|
PxSort<PxSolverConstraintDesc, ArticulationStaticConstraintSortPredicate>(mStaticContactConstraints.begin(), mStaticContactConstraints.size(), ArticulationStaticConstraintSortPredicate());
|
|
|
|
for (PxU32 i = 0; i < mStatic1DConstraints.size(); ++i)
|
|
{
|
|
PxSolverConstraintDesc& desc = mStatic1DConstraints[i];
|
|
|
|
PxU32 linkIndex = desc.linkIndexA != PxSolverConstraintDesc::RIGID_BODY ? desc.linkIndexA : desc.linkIndexB;
|
|
|
|
PX_ASSERT(desc.constraintType == DY_SC_TYPE_RB_1D);
|
|
const Constraint* constraint = reinterpret_cast<const Constraint*>(desc.constraint);
|
|
|
|
SolverConstraintShaderPrepDesc shaderPrepDesc;
|
|
PxSolverConstraintPrepDesc prepDesc;
|
|
|
|
const PxConstraintSolverPrep solverPrep = constraint->solverPrep;
|
|
const void* constantBlock = constraint->constantBlock;
|
|
const PxU32 constantBlockByteSize = constraint->constantBlockSize;
|
|
const PxTransform& pose0 = (constraint->body0 ? constraint->body0->getPose() : id);
|
|
const PxTransform& pose1 = (constraint->body1 ? constraint->body1->getPose() : id);
|
|
const PxSolverBody* sbody0 = desc.bodyA;
|
|
const PxSolverBody* sbody1 = desc.bodyB;
|
|
PxSolverBodyData* sbodyData0 = &solverBodyData[desc.linkIndexA != PxSolverConstraintDesc::RIGID_BODY ? 0 : desc.bodyADataIndex];
|
|
PxSolverBodyData* sbodyData1 = &solverBodyData[desc.linkIndexB != PxSolverConstraintDesc::RIGID_BODY ? 0 : desc.bodyBDataIndex];
|
|
|
|
shaderPrepDesc.constantBlock = constantBlock;
|
|
shaderPrepDesc.constantBlockByteSize = constantBlockByteSize;
|
|
shaderPrepDesc.constraint = constraint;
|
|
shaderPrepDesc.solverPrep = solverPrep;
|
|
|
|
prepDesc.desc = &desc;
|
|
prepDesc.bodyFrame0 = pose0;
|
|
prepDesc.bodyFrame1 = pose1;
|
|
prepDesc.data0 = sbodyData0;
|
|
prepDesc.data1 = sbodyData1;
|
|
prepDesc.body0 = sbody0;
|
|
prepDesc.body1 = sbody1;
|
|
prepDesc.linBreakForce = constraint->linBreakForce;
|
|
prepDesc.angBreakForce = constraint->angBreakForce;
|
|
prepDesc.writeback = &constraintWritebackPool[constraint->index];
|
|
setupConstraintFlags(prepDesc, constraint->flags);
|
|
prepDesc.minResponseThreshold = constraint->minResponseThreshold;
|
|
|
|
SetupSolverConstraint(shaderPrepDesc, prepDesc, blockAllocator, dt, invDt);
|
|
|
|
if (desc.constraint)
|
|
{
|
|
if (mArticulationData.mNbStatic1DConstraints[linkIndex] == 0)
|
|
mArticulationData.mStatic1DConstraintStartIndex[linkIndex] = i;
|
|
mArticulationData.mNbStatic1DConstraints[linkIndex]++;
|
|
}
|
|
else
|
|
{
|
|
//Shuffle down
|
|
mStatic1DConstraints.remove(i);
|
|
i--;
|
|
}
|
|
}
|
|
|
|
for (PxU32 i = 0; i < mStaticContactConstraints.size(); ++i)
|
|
{
|
|
PxSolverConstraintDesc& desc = mStaticContactConstraints[i];
|
|
|
|
PxU32 linkIndex = desc.linkIndexA != PxSolverConstraintDesc::RIGID_BODY ? desc.linkIndexA : desc.linkIndexB;
|
|
|
|
PX_ASSERT(desc.constraintType == DY_SC_TYPE_RB_CONTACT);
|
|
|
|
PxSolverContactDesc blockDesc;
|
|
PxsContactManager* cm = reinterpret_cast<PxsContactManager*>(desc.constraint);
|
|
PxcNpWorkUnit& unit = cm->getWorkUnit();
|
|
PxsContactManagerOutput* cmOutput = &outputs.getContactManagerOutput(unit.mNpIndex);
|
|
|
|
PxSolverBodyData& data0 = desc.linkIndexA != PxSolverConstraintDesc::RIGID_BODY ? solverBodyData[0] : solverBodyData[desc.bodyADataIndex];
|
|
PxSolverBodyData& data1 = desc.linkIndexB != PxSolverConstraintDesc::RIGID_BODY ? solverBodyData[0] : solverBodyData[desc.bodyBDataIndex];
|
|
|
|
blockDesc.data0 = &data0;
|
|
blockDesc.data1 = &data1;
|
|
|
|
PxU8 flags = unit.mRigidCore0->mFlags;
|
|
if (unit.mRigidCore1)
|
|
flags |= PxU8(unit.mRigidCore1->mFlags);
|
|
|
|
blockDesc.bodyFrame0 = unit.mRigidCore0->body2World;
|
|
blockDesc.bodyFrame1 = unit.mRigidCore1 ? unit.mRigidCore1->body2World : id;
|
|
blockDesc.shapeInteraction = cm->getShapeInteraction();
|
|
blockDesc.contactForces = cmOutput->contactForces;
|
|
blockDesc.desc = &desc;
|
|
blockDesc.body0 = desc.bodyA;
|
|
blockDesc.body1 = desc.bodyB;
|
|
blockDesc.hasForceThresholds = !!(unit.mFlags & PxcNpWorkUnitFlag::eFORCE_THRESHOLD);
|
|
blockDesc.disableStrongFriction = !!(unit.mFlags & PxcNpWorkUnitFlag::eDISABLE_STRONG_FRICTION);
|
|
blockDesc.bodyState0 = (unit.mFlags & PxcNpWorkUnitFlag::eARTICULATION_BODY0) ? PxSolverContactDesc::eARTICULATION : PxSolverContactDesc::eDYNAMIC_BODY;
|
|
blockDesc.bodyState1 = (unit.mFlags & PxcNpWorkUnitFlag::eARTICULATION_BODY1) ? PxSolverContactDesc::eARTICULATION : (unit.mFlags & PxcNpWorkUnitFlag::eHAS_KINEMATIC_ACTOR) ? PxSolverContactDesc::eKINEMATIC_BODY :
|
|
((unit.mFlags & PxcNpWorkUnitFlag::eDYNAMIC_BODY1) ? PxSolverContactDesc::eDYNAMIC_BODY : PxSolverContactDesc::eSTATIC_BODY);
|
|
//blockDesc.flags = unit.flags;
|
|
|
|
unit.setInvMassScaleFromDominance(blockDesc.invMassScales);
|
|
|
|
blockDesc.restDistance = unit.mRestDistance;
|
|
blockDesc.frictionPtr = unit.mFrictionDataPtr;
|
|
blockDesc.frictionCount = unit.mFrictionPatchCount;
|
|
blockDesc.maxCCDSeparation = (flags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) ? ccdMaxSeparation : PX_MAX_F32;
|
|
blockDesc.offsetSlop = unit.mOffsetSlop;
|
|
|
|
createFinalizeSolverContacts(blockDesc, *cmOutput, threadContext, invDt, dt, bounceThreshold, frictionOffsetThreshold,
|
|
correlationDist, blockAllocator, Z);
|
|
|
|
getContactManagerConstraintDesc(*cmOutput, *cm, desc);
|
|
|
|
updateFrictionAnchorCountAndPosition(desc, *cmOutput, blockDesc);
|
|
|
|
unit.mFrictionDataPtr = blockDesc.frictionPtr;
|
|
unit.mFrictionPatchCount = blockDesc.frictionCount;
|
|
//KS - Don't track this for now!
|
|
//axisConstraintCount += blockDesc.axisConstraintCount;
|
|
|
|
if (desc.constraint)
|
|
{
|
|
if (mArticulationData.mNbStaticContactConstraints[linkIndex] == 0)
|
|
mArticulationData.mStaticContactConstraintStartIndex[linkIndex] = i;
|
|
mArticulationData.mNbStaticContactConstraints[linkIndex]++;
|
|
}
|
|
else
|
|
{
|
|
mStaticContactConstraints.remove(i);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void setupComplexLimit(ArticulationLink* links, ArticulationData& data, const PxU32 linkID,
|
|
const PxReal angle, const PxReal lowLimit, const PxReal highLimit, const PxVec3& axis, const PxReal cfm, ArticulationInternalConstraint& complexConstraint,
|
|
ArticulationInternalLimit& limit)
|
|
{
|
|
Cm::SpatialVectorV deltaVA, deltaVB;
|
|
FeatherstoneArticulation::getImpulseSelfResponse(links, data,
|
|
links[linkID].parent, Cm::SpatialVector(PxVec3(0), axis), deltaVA,
|
|
linkID, Cm::SpatialVector(PxVec3(0), -axis), deltaVB);
|
|
|
|
const Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA);
|
|
const Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB);
|
|
|
|
const PxReal r0 = deltaV0.angular.dot(axis);
|
|
const PxReal r1 = deltaV1.angular.dot(axis);
|
|
|
|
const PxReal unitResponse = r0 - r1;
|
|
|
|
const PxReal recipResponse = unitResponse > DY_ARTICULATION_MIN_RESPONSE ? 1.0f / (cfm + unitResponse) : 0.0f;
|
|
|
|
complexConstraint.row0 = Cm::UnAlignedSpatialVector(PxVec3(0), axis);
|
|
complexConstraint.row1 = Cm::UnAlignedSpatialVector(PxVec3(0), axis);
|
|
complexConstraint.deltaVA.top = unsimdRef(deltaVA).angular;
|
|
complexConstraint.deltaVA.bottom = unsimdRef(deltaVA).linear;
|
|
complexConstraint.deltaVB.top = unsimdRef(deltaVB).angular;
|
|
complexConstraint.deltaVB.bottom = unsimdRef(deltaVB).linear;
|
|
complexConstraint.recipResponse = recipResponse;
|
|
complexConstraint.response = unitResponse;
|
|
complexConstraint.isLinearConstraint = true;
|
|
limit.errorLow = angle - lowLimit;
|
|
limit.errorHigh = highLimit - angle;
|
|
limit.lowImpulse = 0.f;
|
|
limit.highImpulse = 0.f;
|
|
}
|
|
|
|
void FeatherstoneArticulation::setupInternalConstraintsRecursive(
|
|
ArticulationLink* links,
|
|
const PxU32 linkCount,
|
|
const bool fixBase,
|
|
ArticulationData& data,
|
|
const PxReal stepDt,
|
|
const PxReal dt,
|
|
const PxReal invDt,
|
|
const bool isTGSSolver,
|
|
const PxU32 linkID,
|
|
const PxReal maxForceScale
|
|
)
|
|
{
|
|
const ArticulationLink& link = links[linkID];
|
|
|
|
ArticulationJointCoreData& jointDatum = data.getJointData(linkID);
|
|
|
|
const ArticulationLink& pLink = links[link.parent];
|
|
|
|
const ArticulationJointCore& j = *link.inboundJoint;
|
|
|
|
//const bool jointDrive = (j.driveType != PxArticulationJointDriveType::eNONE);
|
|
|
|
bool hasFriction = j.frictionCoefficient > 0.f;
|
|
|
|
const PxReal fCoefficient = j.frictionCoefficient * stepDt;
|
|
|
|
|
|
const PxReal transmissionForce = data.getTransmittedForce(linkID).magnitude() * fCoefficient;
|
|
|
|
// PT:: tag: scalar transform*transform
|
|
const PxTransform cA2w = pLink.bodyCore->body2World.transform(j.parentPose);
|
|
const PxTransform cB2w = link.bodyCore->body2World.transform(j.childPose);
|
|
|
|
const PxU32 parent = link.parent;
|
|
|
|
const PxReal cfm = PxMax(link.cfm, pLink.cfm);
|
|
|
|
//Linear, then angular...
|
|
|
|
PxVec3 driveError(0.f);
|
|
PxVec3 angles(0.f);
|
|
PxVec3 row[3];
|
|
if (j.jointType == PxArticulationJointType::eSPHERICAL && jointDatum.nbDof > 1)
|
|
{
|
|
//It's a spherical joint. We can't directly work on joint positions with spherical joints, so we instead need to compute the quaternion
|
|
//and from that compute the joint error projected onto the DOFs. This will yield a rotation that is singularity-free, where the joint
|
|
//angles match the target joint angles provided provided the angles are within +/- Pi around each axis. Spherical joints do not support
|
|
//quaternion double cover cases/wide angles.
|
|
|
|
PxVec3 driveAxis(0.f);
|
|
|
|
bool hasAngularDrives = false;
|
|
|
|
PxU32 tmpDofId = 0;
|
|
|
|
for (PxU32 i = 0; i < PxArticulationAxis::eX; ++i)
|
|
{
|
|
if (j.motion[i] != PxArticulationMotion::eLOCKED)
|
|
{
|
|
const bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].driveType != PxArticulationDriveType::eNONE);
|
|
|
|
if (hasDrive)
|
|
{
|
|
const PxVec3 axis = data.mMotionMatrix[jointDatum.jointOffset + tmpDofId].top;
|
|
PxReal target = data.mJointTargetPositions[jointDatum.jointOffset + tmpDofId];
|
|
|
|
driveAxis += axis * target;
|
|
hasAngularDrives = true;
|
|
|
|
}
|
|
|
|
tmpDofId++;
|
|
}
|
|
}
|
|
|
|
{
|
|
PxQuat qB2qA = cA2w.q.getConjugate() * cB2w.q;
|
|
|
|
{
|
|
//Spherical joint drive calculation using 3x child-space Euler angles
|
|
if (hasAngularDrives)
|
|
{
|
|
PxReal angle = driveAxis.normalize();
|
|
|
|
if (angle < 1e-12f)
|
|
{
|
|
driveAxis = PxVec3(1.f, 0.f, 0.f);
|
|
angle = 0.f;
|
|
}
|
|
|
|
PxQuat targetQ = PxQuat(angle, driveAxis);
|
|
|
|
if (targetQ.dot(qB2qA) < 0.f)
|
|
targetQ = -targetQ;
|
|
|
|
driveError = -2.f * (targetQ.getConjugate() * qB2qA).getImaginaryPart();
|
|
}
|
|
|
|
for (PxU32 i = 0, tmpDof = 0; i < PxArticulationAxis::eX; ++i)
|
|
{
|
|
if (j.motion[i] != PxArticulationMotion::eLOCKED)
|
|
{
|
|
angles[i] = data.mJointPosition[j.jointOffset + tmpDof];
|
|
row[i] = data.mWorldMotionMatrix[jointDatum.jointOffset + tmpDof].top;
|
|
tmpDof++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (PxU32 i = 0; i < PxArticulationAxis::eX; ++i)
|
|
{
|
|
if (j.motion[i] != PxArticulationMotion::eLOCKED)
|
|
{
|
|
driveError[i] = data.mJointTargetPositions[j.jointOffset] - data.mJointPosition[j.jointOffset];
|
|
angles[i] = data.mJointPosition[j.jointOffset];
|
|
row[i] = data.mWorldMotionMatrix[jointDatum.jointOffset].top;
|
|
}
|
|
}
|
|
}
|
|
|
|
PxU32 dofId = 0;
|
|
|
|
for (PxU32 i = 0; i < PxArticulationAxis::eX; ++i)
|
|
{
|
|
if (j.motion[i] != PxArticulationMotion::eLOCKED)
|
|
{
|
|
//Impulse response vector and axes are common for all constraints on this axis besides locked axis!!!
|
|
const PxVec3 axis = row[i];
|
|
|
|
Cm::SpatialVectorV deltaVA, deltaVB;
|
|
FeatherstoneArticulation::getImpulseSelfResponse(links, data,
|
|
parent, Cm::SpatialVector(PxVec3(0), axis), deltaVA,
|
|
linkID, Cm::SpatialVector(PxVec3(0), -axis), deltaVB);
|
|
|
|
const Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA);
|
|
const Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB);
|
|
|
|
const PxReal r0 = deltaV0.angular.dot(axis);
|
|
const PxReal r1 = deltaV1.angular.dot(axis);
|
|
|
|
const PxReal unitResponse = r0 - r1;
|
|
|
|
const PxReal recipResponse = unitResponse <= 0.f ? 0.f : 1.0f / (unitResponse+cfm);
|
|
|
|
const PxU32 count = data.mInternalConstraints.size();
|
|
data.mInternalConstraints.forceSize_Unsafe(count + 1);
|
|
ArticulationInternalConstraint* constraints = &data.mInternalConstraints[count];
|
|
|
|
constraints->recipResponse = recipResponse;
|
|
constraints->response = unitResponse;
|
|
constraints->row0 = Cm::SpatialVectorF(PxVec3(0), axis);
|
|
constraints->row1 = Cm::SpatialVectorF(PxVec3(0), axis);
|
|
constraints->deltaVA.top = unsimdRef(deltaVA).angular;
|
|
constraints->deltaVA.bottom = unsimdRef(deltaVA).linear;
|
|
constraints->deltaVB.top = unsimdRef(deltaVB).angular;
|
|
constraints->deltaVB.bottom = unsimdRef(deltaVB).linear;
|
|
constraints->isLinearConstraint = false;
|
|
constraints->maxJointVelocity = j.maxJointVelocity[i];
|
|
|
|
constraints->accumulatedFrictionImpulse = 0.0f;
|
|
constraints->frictionMaxForce = hasFriction ? transmissionForce : 0.f;
|
|
|
|
constraints->dynamicFrictionEffort = j.frictionParams[i].dynamicFrictionEffort;
|
|
constraints->staticFrictionEffort = j.frictionParams[i].staticFrictionEffort;
|
|
constraints->viscousFrictionCoefficient = j.frictionParams[i].viscousFrictionCoefficient;
|
|
|
|
const bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].driveType != PxArticulationDriveType::eNONE);
|
|
constraints->driveImpulse = 0.0f;
|
|
constraints->driveMaxImpulse = j.drives[i].maxForce * maxForceScale;
|
|
constraints->envelope = j.drives[i].envelope;
|
|
constraints->externalJointForce = data.getJointForces()[jointDatum.jointOffset + dofId];
|
|
|
|
if(hasDrive)
|
|
{
|
|
constraints->setImplicitDriveDesc(
|
|
computeImplicitDriveParams(
|
|
j.drives[i].driveType, j.drives[i].stiffness, j.drives[i].damping,
|
|
isTGSSolver ? stepDt : dt, dt,
|
|
unitResponse, recipResponse, driveError[i], data.mJointTargetVelocities[j.jointOffset + dofId],
|
|
isTGSSolver));
|
|
}
|
|
else
|
|
{
|
|
constraints->setImplicitDriveDesc(ArticulationImplicitDriveDesc(PxZero));
|
|
}
|
|
|
|
if (j.motion[i] == PxArticulationMotion::eLIMITED)
|
|
{
|
|
const PxU32 limitCount = data.mInternalLimits.size();
|
|
data.mInternalLimits.forceSize_Unsafe(limitCount + 1);
|
|
ArticulationInternalLimit* limits = &data.mInternalLimits[limitCount];
|
|
|
|
const PxReal jPos = angles[i];
|
|
limits->errorHigh = j.limits[i].high - jPos;
|
|
limits->errorLow = jPos - j.limits[i].low;
|
|
limits->lowImpulse = 0.f;
|
|
limits->highImpulse = 0.f;
|
|
}
|
|
dofId++;
|
|
}
|
|
}
|
|
|
|
for (PxU32 i = PxArticulationAxis::eX; i < PxArticulationAxis::eCOUNT; ++i)
|
|
{
|
|
if (j.motion[i] != PxArticulationMotion::eLOCKED)
|
|
{
|
|
//Impulse response vector and axes are common for all constraints on this axis besides locked axis!!!
|
|
const PxVec3 axis = data.mWorldMotionMatrix[jointDatum.jointOffset + dofId].bottom;
|
|
const PxVec3 ang0 = (cA2w.p - pLink.bodyCore->body2World.p).cross(axis);
|
|
const PxVec3 ang1 = (cB2w.p - link.bodyCore->body2World.p).cross(axis);
|
|
|
|
Cm::SpatialVectorV deltaVA, deltaVB;
|
|
FeatherstoneArticulation::getImpulseSelfResponse(links, data,
|
|
links[linkID].parent, Cm::SpatialVector(axis, ang0), deltaVA,
|
|
linkID, Cm::SpatialVector(-axis, -ang1), deltaVB);
|
|
|
|
const Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA);
|
|
const Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB);
|
|
|
|
const PxReal r0 = deltaV0.linear.dot(axis) + deltaV0.angular.dot(ang0);
|
|
const PxReal r1 = deltaV1.linear.dot(axis) + deltaV1.angular.dot(ang1);
|
|
|
|
const PxReal unitResponse = r0 - r1;
|
|
|
|
//const PxReal recipResponse = unitResponse > DY_ARTICULATION_MIN_RESPONSE ? 1.0f / (unitResponse+cfm) : 0.0f;
|
|
const PxReal recipResponse = 1.0f / (unitResponse + cfm);
|
|
|
|
const PxU32 count = data.mInternalConstraints.size();
|
|
data.mInternalConstraints.forceSize_Unsafe(count + 1);
|
|
ArticulationInternalConstraint* constraints = &data.mInternalConstraints[count];
|
|
|
|
constraints->response = unitResponse;
|
|
constraints->recipResponse = recipResponse;
|
|
constraints->row0 = Cm::SpatialVectorF(axis, ang0);
|
|
constraints->row1 = Cm::SpatialVectorF(axis, ang1);
|
|
constraints->deltaVA.top = unsimdRef(deltaVA).angular;
|
|
constraints->deltaVA.bottom = unsimdRef(deltaVA).linear;
|
|
constraints->deltaVB.top = unsimdRef(deltaVB).angular;
|
|
constraints->deltaVB.bottom = unsimdRef(deltaVB).linear;
|
|
constraints->isLinearConstraint = true;
|
|
constraints->maxJointVelocity = j.maxJointVelocity[i];
|
|
|
|
constraints->accumulatedFrictionImpulse = 0.f;
|
|
constraints->frictionMaxForce = hasFriction ? transmissionForce : 0.f;
|
|
constraints->dynamicFrictionEffort = j.frictionParams[i].dynamicFrictionEffort;
|
|
constraints->staticFrictionEffort = j.frictionParams[i].staticFrictionEffort;
|
|
constraints->viscousFrictionCoefficient = j.frictionParams[i].viscousFrictionCoefficient;
|
|
|
|
|
|
const bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && (j.drives[i].envelope.maxEffort > 0.0f || j.drives[i].maxForce > 0.0f) && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f));
|
|
constraints->driveImpulse = 0.0f;
|
|
constraints->envelope = j.drives[i].envelope;
|
|
constraints->driveMaxImpulse = j.drives[i].maxForce * maxForceScale;
|
|
constraints->externalJointForce = data.getJointForces()[jointDatum.jointOffset + dofId];
|
|
if(hasDrive)
|
|
{
|
|
constraints->setImplicitDriveDesc(
|
|
computeImplicitDriveParams(
|
|
j.drives[i].driveType, j.drives[i].stiffness, j.drives[i].damping,
|
|
isTGSSolver ? stepDt : dt, dt,
|
|
unitResponse, recipResponse,
|
|
data.mJointTargetPositions[j.jointOffset + dofId] - data.mJointPosition[j.jointOffset + dofId],
|
|
data.mJointTargetVelocities[j.jointOffset + dofId],
|
|
isTGSSolver));
|
|
}
|
|
else
|
|
{
|
|
constraints->setImplicitDriveDesc(ArticulationImplicitDriveDesc(PxZero));
|
|
}
|
|
|
|
if (j.motion[i] == PxArticulationMotion::eLIMITED)
|
|
{
|
|
const PxU32 limitCount = data.mInternalLimits.size();
|
|
data.mInternalLimits.forceSize_Unsafe(limitCount + 1);
|
|
ArticulationInternalLimit* limits = &data.mInternalLimits[limitCount];
|
|
const PxReal jPos = data.mJointPosition[j.jointOffset + dofId];
|
|
limits->errorHigh = j.limits[i].high - jPos;
|
|
limits->errorLow = jPos - j.limits[i].low;
|
|
limits->lowImpulse = 0.f;
|
|
limits->highImpulse = 0.f;
|
|
}
|
|
dofId++;
|
|
}
|
|
}
|
|
|
|
if (jointDatum.dofLimitMask)
|
|
{
|
|
for (PxU32 dof = 0; dof < jointDatum.nbDof; ++dof)
|
|
{
|
|
PxU32 i = j.dofIds[dof];
|
|
if (j.motion[i] == PxArticulationMotion::eLOCKED)
|
|
{
|
|
const PxU32 count = data.mInternalConstraints.size();
|
|
data.mInternalConstraints.forceSize_Unsafe(count + 1);
|
|
ArticulationInternalConstraint* constraints = &data.mInternalConstraints[count];
|
|
|
|
const PxU32 limitCount = data.mInternalLimits.size();
|
|
data.mInternalLimits.forceSize_Unsafe(limitCount + 1);
|
|
ArticulationInternalLimit* limits = &data.mInternalLimits[limitCount];
|
|
|
|
const PxVec3 axis = row[i];
|
|
|
|
PxReal angle = angles[i];
|
|
|
|
//A locked axis is equivalent to a limit of 0
|
|
PxReal low = 0.f;
|
|
PxReal high = 0.f;
|
|
|
|
setupComplexLimit(links, data, linkID, angle,
|
|
low, high, axis, cfm, *constraints, *limits++);
|
|
}
|
|
}
|
|
}
|
|
|
|
const PxU32 numChildren = link.mNumChildren;
|
|
const PxU32 offset = link.mChildrenStartIndex;
|
|
for (PxU32 i = 0; i < numChildren; ++i)
|
|
{
|
|
const PxU32 child = offset + i;
|
|
setupInternalConstraintsRecursive(links, linkCount, fixBase, data, stepDt, dt, invDt, isTGSSolver, child, maxForceScale);
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::setupInternalSpatialTendonConstraintsRecursive(
|
|
ArticulationLink* links,
|
|
ArticulationAttachment* attachments,
|
|
const PxU32 attachmentCount,
|
|
const PxVec3& pAttachPoint,
|
|
const bool fixBase,
|
|
ArticulationData& data,
|
|
const PxReal stepDt,
|
|
const bool isTGSSolver,
|
|
const PxU32 attachmentID,
|
|
const PxReal stiffness,
|
|
const PxReal damping,
|
|
const PxReal limitStiffness,
|
|
const PxReal accumLength,
|
|
const PxU32 startLink,
|
|
const PxVec3& startAxis,
|
|
const PxVec3& startRaXn)
|
|
{
|
|
ArticulationAttachment& attachment = attachments[attachmentID];
|
|
|
|
ArticulationLink& cLink = links[attachment.linkInd];
|
|
|
|
const PxTransform cBody2World = cLink.bodyCore->body2World;
|
|
|
|
const PxVec3 rb = cBody2World.q.rotate(attachment.relativeOffset);
|
|
|
|
const PxVec3 cAttachPoint = cBody2World.p + rb;
|
|
|
|
const PxVec3 dif = pAttachPoint - cAttachPoint;
|
|
const PxReal distanceSq = dif.magnitudeSquared();
|
|
const PxReal distance = PxSqrt(distanceSq);
|
|
|
|
const PxReal u = distance * attachment.coefficient + accumLength;
|
|
|
|
const PxU32 childCount = attachment.childCount;
|
|
if (childCount)
|
|
{
|
|
for (ArticulationBitField children = attachment.children; children != 0; children &= (children - 1))
|
|
{
|
|
//index of child of link h on path to link linkID
|
|
const PxU32 child = PxLowestSetBit(children);
|
|
|
|
setupInternalSpatialTendonConstraintsRecursive(links, attachments, attachmentCount, cAttachPoint, fixBase, data, stepDt,
|
|
isTGSSolver, child, stiffness, damping, limitStiffness, u, startLink,
|
|
startAxis, startRaXn);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const PxVec3 axis = distance > 0.001f ? dif / distance : PxVec3(0.f);
|
|
|
|
const PxVec3 rbXn = rb.cross(axis);
|
|
|
|
Cm::SpatialVectorV deltaVA, deltaVB;
|
|
FeatherstoneArticulation::getImpulseSelfResponse(links, data,
|
|
startLink, Cm::SpatialVector(startAxis, startRaXn), deltaVA,
|
|
attachment.linkInd, Cm::SpatialVector(-axis, -rbXn), deltaVB);
|
|
|
|
const Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA);
|
|
const Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB);
|
|
|
|
const PxReal r0 = deltaV0.linear.dot(startAxis) + deltaV0.angular.dot(startRaXn);
|
|
const PxReal r1 = deltaV1.linear.dot(axis) + deltaV1.angular.dot(rbXn);
|
|
|
|
const PxReal unitResponse = (r0 - r1);
|
|
|
|
//const PxReal recipResponse = unitResponse > DY_ARTICULATION_MIN_RESPONSE ? 1.0f / (unitResponse + cfm) : 0.0f;
|
|
//const PxReal recipResponse = unitResponse > DY_ARTICULATION_MIN_RESPONSE ? 1.0f / (unitResponse) : 0.0f;
|
|
const PxReal recipResponse = 1.0f / (unitResponse + cLink.cfm);
|
|
|
|
const PxU32 count = data.mInternalSpatialTendonConstraints.size();
|
|
data.mInternalSpatialTendonConstraints.forceSize_Unsafe(count + 1);
|
|
|
|
ArticulationInternalTendonConstraint* constraint = &data.mInternalSpatialTendonConstraints[count];
|
|
attachment.mConstraintInd = PxU16(count);
|
|
|
|
constraint->row0 = Cm::SpatialVectorF(startAxis, startRaXn);
|
|
constraint->row1 = Cm::SpatialVectorF(axis, rbXn);
|
|
constraint->linkID0 = startLink;
|
|
constraint->linkID1 = attachment.linkInd;
|
|
constraint->recipResponse = recipResponse;
|
|
|
|
const PxReal a = stepDt * (stepDt*stiffness + damping);
|
|
const PxReal a2 = stepDt * (stepDt*limitStiffness + damping);
|
|
|
|
const PxReal x = unitResponse > 0.f ? 1.0f / (1.0f + a * unitResponse) : 0.f;
|
|
const PxReal x2 = unitResponse > 0.f ? 1.0f / (1.0f + a2 * unitResponse) : 0.f;
|
|
|
|
constraint->velMultiplier = -x * a;// * unitResponse;
|
|
//constraint->velMultiplier = -x * damping*stepDt;
|
|
|
|
constraint->impulseMultiplier = isTGSSolver ? 1.f : 1.f - x;
|
|
constraint->biasCoefficient = (-stiffness * x * stepDt);//*unitResponse;
|
|
constraint->appliedForce = 0.f;
|
|
|
|
constraint->accumulatedLength = u;// + u*0.2f;
|
|
constraint->restDistance = attachment.restLength;
|
|
constraint->lowLimit = attachment.lowLimit;
|
|
constraint->highLimit = attachment.highLimit;
|
|
|
|
constraint->limitBiasCoefficient = (-limitStiffness * x2 * stepDt);//*unitResponse;
|
|
constraint->limitImpulseMultiplier = isTGSSolver ? 1.f : 1.f - x2;
|
|
constraint->limitAppliedForce = 0.f;
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::updateSpatialTendonConstraintsRecursive(ArticulationAttachment* attachments, ArticulationData& data, const PxU32 attachmentID, PxReal accumLength,
|
|
const PxVec3& pAttachPoint)
|
|
{
|
|
ArticulationAttachment& attachment = attachments[attachmentID];
|
|
|
|
//const PxReal restDist = attachment.restDistance;
|
|
|
|
const PxTransform& cBody2World = data.getAccumulatedPoses()[attachment.linkInd];
|
|
|
|
const PxVec3 rb = cBody2World.q.rotate(attachment.relativeOffset);
|
|
|
|
const PxVec3 cAttachPoint = cBody2World.p + rb;
|
|
|
|
const PxVec3 dif = pAttachPoint - cAttachPoint;
|
|
const PxReal distanceSq = dif.magnitudeSquared();
|
|
const PxReal distance = PxSqrt(distanceSq);
|
|
|
|
const PxReal u = distance * attachment.coefficient + accumLength;
|
|
|
|
const PxU32 childCount = attachment.childCount;
|
|
if (childCount)
|
|
{
|
|
for (ArticulationBitField children = attachment.children; children != 0; children &= (children - 1))
|
|
{
|
|
//index of child of link h on path to link linkID
|
|
const PxU32 child = PxLowestSetBit(children);
|
|
|
|
updateSpatialTendonConstraintsRecursive(attachments, data, child, u, cAttachPoint);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PxU32 index = attachment.mConstraintInd;
|
|
ArticulationInternalTendonConstraint& constraint = data.mInternalSpatialTendonConstraints[index];
|
|
|
|
constraint.accumulatedLength = u;
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::setupInternalFixedTendonConstraintsRecursive(
|
|
ArticulationLink* links,
|
|
ArticulationTendonJoint* tendonJoints,
|
|
const bool fixBase,
|
|
ArticulationData& data,
|
|
const PxReal stepDt,
|
|
const bool isTGSSolver,
|
|
const PxU32 tendonJointID,
|
|
const PxReal stiffness,
|
|
const PxReal damping,
|
|
const PxReal limitStiffness,
|
|
const PxU32 startLink,
|
|
const PxVec3& startAxis,
|
|
const PxVec3& startRaXn)
|
|
{
|
|
ArticulationTendonJoint& tendonJoint = tendonJoints[tendonJointID];
|
|
|
|
ArticulationLink& cLink = links[tendonJoint.linkInd];
|
|
|
|
const PxTransform& cBody2World = cLink.bodyCore->body2World;
|
|
|
|
const PxReal cfm = PxMax(cLink.cfm, links[startLink].cfm);
|
|
|
|
ArticulationJointCoreData& jointDatum = data.getJointData(tendonJoint.linkInd);
|
|
ArticulationJointCore& joint = *cLink.inboundJoint;
|
|
|
|
PxU16 tendonJointAxis = tendonJoint.axis;
|
|
|
|
PX_ASSERT(joint.motion[tendonJointAxis] != PxArticulationMotion::eLOCKED);
|
|
|
|
//Cm::SpatialVector cImpulse;
|
|
PxU32 dofIndex = joint.invDofIds[tendonJointAxis];
|
|
|
|
tendonJoint.startJointOffset = PxTo16(jointDatum.jointOffset + dofIndex);
|
|
|
|
///*PxReal jointPose = jointPositions[jointDatum.jointOffset + dofIndex] * tendonJoint.coefficient;
|
|
|
|
//jointPose += accumulatedJointPose;*/
|
|
|
|
{
|
|
PxVec3 axis, rbXn;
|
|
if (tendonJointAxis < PxArticulationAxis::eX)
|
|
{
|
|
const PxVec3 tAxis = data.mWorldMotionMatrix[jointDatum.jointOffset + dofIndex].top;
|
|
axis = PxVec3(0.f);
|
|
rbXn = tAxis;
|
|
}
|
|
else
|
|
{
|
|
// PT:: tag: scalar transform*transform
|
|
const PxTransform cB2w = cBody2World.transform(joint.childPose);
|
|
const PxVec3 tAxis = data.mWorldMotionMatrix[jointDatum.jointOffset + dofIndex].bottom;
|
|
axis = tAxis;
|
|
rbXn = (cB2w.p - cBody2World.p).cross(axis);
|
|
}
|
|
|
|
Cm::SpatialVectorV deltaVA, deltaVB;
|
|
FeatherstoneArticulation::getImpulseSelfResponse(links, data,
|
|
startLink, Cm::SpatialVector(startAxis, startRaXn), deltaVA,
|
|
tendonJoint.linkInd, Cm::SpatialVector(-axis, -rbXn), deltaVB);
|
|
|
|
const Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA);
|
|
const Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB);
|
|
|
|
/*const PxU32 pLinkInd = cLink.parent;
|
|
printf("(%i, %i) deltaV1(%f, %f, %f, %f, %f, %f)\n",
|
|
pLinkInd, tendonJoint.linkInd, deltaV1.linear.x, deltaV1.linear.y, deltaV1.linear.z, deltaV1.angular.x,
|
|
deltaV1.angular.y, deltaV1.angular.z);*/
|
|
|
|
const PxReal r0 = deltaV0.linear.dot(startAxis) + deltaV0.angular.dot(startRaXn);
|
|
const PxReal r1 = deltaV1.linear.dot(axis) + deltaV1.angular.dot(rbXn);
|
|
|
|
const PxReal unitResponse = r0 - r1;
|
|
|
|
const PxReal recipResponse = 1.0f / (unitResponse + cfm);
|
|
|
|
const PxU32 count = data.mInternalFixedTendonConstraints.size();
|
|
data.mInternalFixedTendonConstraints.forceSize_Unsafe(count + 1);
|
|
|
|
ArticulationInternalTendonConstraint* constraint = &data.mInternalFixedTendonConstraints[count];
|
|
tendonJoint.mConstraintInd = PxU16(count);
|
|
|
|
constraint->row0 = Cm::UnAlignedSpatialVector(startAxis, startRaXn);
|
|
constraint->row1 = Cm::UnAlignedSpatialVector(axis, rbXn);
|
|
constraint->deltaVA = r0; //We only need to record the change in velocity projected onto the dof for this!
|
|
constraint->deltaVB = Cm::UnAlignedSpatialVector(deltaV1.angular, deltaV1.linear);
|
|
constraint->linkID0 = startLink;
|
|
constraint->linkID1 = tendonJoint.linkInd;
|
|
constraint->recipResponse = recipResponse;
|
|
|
|
const PxReal a = stepDt * (stepDt*stiffness + damping);
|
|
|
|
const PxReal a2 = stepDt * (stepDt*limitStiffness + damping);
|
|
|
|
PxReal x = unitResponse > 0.f ? 1.0f / (1.0f + a * unitResponse) : 0.f;
|
|
|
|
PxReal x2 = unitResponse > 0.f ? 1.0f / (1.0f + a2* unitResponse) : 0.f;
|
|
|
|
constraint->velMultiplier = -x * a;// * unitResponse;
|
|
|
|
constraint->impulseMultiplier = isTGSSolver ? 1.f : 1.f - x;
|
|
constraint->biasCoefficient = (-stiffness * x * stepDt);//*unitResponse;
|
|
constraint->appliedForce = 0.f;
|
|
//constraint->accumulatedLength = jointPose;
|
|
|
|
constraint->limitImpulseMultiplier = isTGSSolver ? 1.f : 1.f - x2;
|
|
constraint->limitBiasCoefficient = (-limitStiffness * x2 * stepDt);//*unitResponse;
|
|
constraint->limitAppliedForce = 0.f;
|
|
|
|
/*printf("(%i, %i) r0 %f, r1 %f cmf %f unitResponse %f recipResponse %f a %f x %f\n",
|
|
pLinkInd, tendonJoint.linkInd, r0, r1, cLink.cfm, unitResponse, recipResponse, a, x);*/
|
|
}
|
|
|
|
const PxU32 childCount = tendonJoint.childCount;
|
|
|
|
if (childCount)
|
|
{
|
|
for (ArticulationBitField children = tendonJoint.children; children != 0; children &= (children - 1))
|
|
{
|
|
//index of child of link h on path to link linkID
|
|
const PxU32 child = PxLowestSetBit(children);
|
|
|
|
setupInternalFixedTendonConstraintsRecursive(links, tendonJoints, fixBase, data, stepDt,
|
|
isTGSSolver, child, stiffness, damping, limitStiffness, startLink, startAxis, startRaXn);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::setupInternalConstraints(
|
|
ArticulationLink* links,
|
|
const PxU32 linkCount,
|
|
const bool fixBase,
|
|
ArticulationData& data,
|
|
PxReal stepDt,
|
|
PxReal dt,
|
|
PxReal invDt,
|
|
bool isTGSSolver)
|
|
{
|
|
data.mInternalConstraints.forceSize_Unsafe(0);
|
|
data.mInternalConstraints.reserve(data.getDofs());
|
|
|
|
data.mInternalLimits.forceSize_Unsafe(0);
|
|
data.mInternalLimits.reserve(data.getDofs());
|
|
|
|
const PxReal maxForceScale = data.getArticulationFlags() & PxArticulationFlag::eDRIVE_LIMITS_ARE_FORCES ? dt : 1.f;
|
|
|
|
|
|
const PxU32 numChildren = links[0].mNumChildren;
|
|
const PxU32 offset = links[0].mChildrenStartIndex;
|
|
for (PxU32 i = 0; i < numChildren; ++i)
|
|
{
|
|
const PxU32 child = offset + i;
|
|
|
|
setupInternalConstraintsRecursive(links, linkCount, fixBase, data, stepDt, dt, invDt, isTGSSolver, child, maxForceScale);
|
|
}
|
|
|
|
PxU32 totalNumAttachments = 0;
|
|
for (PxU32 i = 0; i < data.mNumSpatialTendons; ++i)
|
|
{
|
|
Dy::ArticulationSpatialTendon* tendon = data.mSpatialTendons[i];
|
|
totalNumAttachments += tendon->getNumAttachments();
|
|
}
|
|
|
|
data.mInternalSpatialTendonConstraints.forceSize_Unsafe(0);
|
|
data.mInternalSpatialTendonConstraints.reserve(totalNumAttachments);
|
|
|
|
for (PxU32 i = 0; i < data.mNumSpatialTendons; ++i)
|
|
{
|
|
Dy::ArticulationSpatialTendon* tendon = data.mSpatialTendons[i];
|
|
|
|
Dy::ArticulationAttachment* attachments = tendon->getAttachments();
|
|
|
|
ArticulationAttachment& pAttachment = attachments[0];
|
|
|
|
//const PxU32 childCount = pAttachment.childCount;
|
|
|
|
//PxReal scale = 1.f/PxReal(childCount);
|
|
|
|
const PxReal coefficient = pAttachment.coefficient;
|
|
|
|
const PxU32 startLink = pAttachment.linkInd;
|
|
ArticulationLink& pLink = links[startLink];
|
|
const PxTransform pBody2World = pLink.bodyCore->body2World;
|
|
const PxVec3 ra = pBody2World.q.rotate(pAttachment.relativeOffset);
|
|
const PxVec3 pAttachPoint = pBody2World.p + ra;
|
|
|
|
for (ArticulationAttachmentBitField children = pAttachment.children; children != 0; children &= (children - 1))
|
|
{
|
|
//index of child of link h on path to link linkID
|
|
const PxU32 child = PxLowestSetBit(children);
|
|
|
|
ArticulationAttachment& attachment = attachments[child];
|
|
ArticulationLink& cLink = links[attachment.linkInd];
|
|
const PxTransform cBody2World = cLink.bodyCore->body2World;
|
|
const PxVec3 rb = cBody2World.q.rotate(attachment.relativeOffset);
|
|
const PxVec3 cAttachPoint = cBody2World.p + rb;
|
|
|
|
const PxVec3 axis = (pAttachPoint - cAttachPoint).getNormalized();
|
|
const PxVec3 raXn = ra.cross(axis);
|
|
|
|
setupInternalSpatialTendonConstraintsRecursive(links, attachments, tendon->getNumAttachments(), pAttachPoint, fixBase, data, stepDt, isTGSSolver,
|
|
child, tendon->mStiffness, tendon->mDamping, tendon->mLimitStiffness, tendon->mOffset*coefficient, startLink,
|
|
axis, raXn);
|
|
}
|
|
}
|
|
|
|
PxU32 totalNumTendonJoints = 0;
|
|
for (PxU32 i = 0; i < data.mNumFixedTendons; ++i)
|
|
{
|
|
Dy::ArticulationFixedTendon* tendon = data.mFixedTendons[i];
|
|
totalNumTendonJoints += tendon->getNumJoints();
|
|
}
|
|
|
|
data.mInternalFixedTendonConstraints.forceSize_Unsafe(0);
|
|
data.mInternalFixedTendonConstraints.reserve(totalNumTendonJoints);
|
|
|
|
for (PxU32 i = 0; i < data.mNumFixedTendons; ++i)
|
|
{
|
|
ArticulationFixedTendon* tendon = data.mFixedTendons[i];
|
|
|
|
ArticulationTendonJoint* tendonJoints = tendon->getTendonJoints();
|
|
|
|
ArticulationTendonJoint& pTendonJoint = tendonJoints[0];
|
|
|
|
const PxU32 startLinkInd = pTendonJoint.linkInd;
|
|
ArticulationLink& pLink = links[startLinkInd];
|
|
const PxTransform& pBody2World = pLink.bodyCore->body2World;
|
|
|
|
for (ArticulationAttachmentBitField children = pTendonJoint.children; children != 0; children &= (children - 1))
|
|
{
|
|
//index of child of link h on path to link linkID
|
|
const PxU32 child = PxLowestSetBit(children);
|
|
|
|
ArticulationTendonJoint& cTendonJoint = tendonJoints[child];
|
|
|
|
ArticulationLink& cLink = links[cTendonJoint.linkInd];
|
|
|
|
ArticulationJointCore* joint = cLink.inboundJoint;
|
|
|
|
PX_ASSERT(joint != NULL);
|
|
|
|
ArticulationJointCoreData* jointDatum = &data.getJointData(cTendonJoint.linkInd);
|
|
|
|
PxU16 tendonJointAxis = cTendonJoint.axis;
|
|
|
|
PX_ASSERT(joint->motion[tendonJointAxis] != PxArticulationMotion::eLOCKED);
|
|
|
|
PxVec3 startAxis, raXn;
|
|
PxU32 dofIndex = joint->invDofIds[tendonJointAxis];
|
|
|
|
if (tendonJointAxis < PxArticulationAxis::eX)
|
|
{
|
|
const PxVec3 axis = data.mWorldMotionMatrix[jointDatum->jointOffset + dofIndex].top;
|
|
startAxis = PxVec3(0.f);
|
|
raXn = axis;
|
|
}
|
|
else
|
|
{
|
|
// PT:: tag: scalar transform*transform
|
|
const PxTransform cA2w = pBody2World.transform(joint->parentPose);
|
|
const PxVec3 axis = data.mWorldMotionMatrix[jointDatum->jointOffset + dofIndex].bottom;
|
|
const PxVec3 ang0 = (cA2w.p - pBody2World.p).cross(axis);
|
|
startAxis = axis;
|
|
raXn = ang0;
|
|
}
|
|
|
|
setupInternalFixedTendonConstraintsRecursive(links, tendonJoints, fixBase, data, stepDt, isTGSSolver,
|
|
child, tendon->mStiffness, tendon->mDamping, tendon->mLimitStiffness, startLinkInd, startAxis, raXn);
|
|
}
|
|
}
|
|
|
|
//Now set up the internal mimic joint constraints.
|
|
setupInternalMimicJointConstraints();
|
|
}
|
|
|
|
PxU32 FeatherstoneArticulation::setupSolverConstraints(
|
|
ArticulationLink* links,
|
|
const PxU32 linkCount,
|
|
const bool fixBase,
|
|
ArticulationData& data,
|
|
PxU32& acCount)
|
|
{
|
|
acCount = 0;
|
|
|
|
setupInternalConstraints(links, linkCount, fixBase, data, data.getDt(), data.getDt(), 1.f / data.getDt(), false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
PxU32 FeatherstoneArticulation::setupSolverConstraintsTGS(const ArticulationSolverDesc& articDesc,
|
|
PxReal dt,
|
|
PxReal invDt,
|
|
PxReal totalDt)
|
|
{
|
|
FeatherstoneArticulation* thisArtic = articDesc.articulation;
|
|
|
|
ArticulationLink* links = thisArtic->mArticulationData.getLinks();
|
|
const PxU32 linkCount = thisArtic->mArticulationData.getLinkCount();
|
|
const bool fixBase = thisArtic->mArticulationData.getArticulationFlags() & PxArticulationFlag::eFIX_BASE;
|
|
|
|
thisArtic->setupInternalConstraints(links, linkCount, fixBase, thisArtic->mArticulationData, dt, totalDt, invDt, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void FeatherstoneArticulation::teleportLinks(ArticulationData& data)
|
|
{
|
|
ArticulationLink* links = mArticulationData.getLinks();
|
|
|
|
ArticulationJointCoreData* jointData = mArticulationData.getJointData();
|
|
|
|
const PxReal* jointPositions = data.getJointPositions();
|
|
|
|
const PxU32 linkCount = mArticulationData.getLinkCount();
|
|
|
|
for (PxU32 linkID = 1; linkID < linkCount; ++linkID)
|
|
{
|
|
const ArticulationLink& link = links[linkID];
|
|
|
|
const ArticulationJointCoreData& jointDatum = jointData[linkID];
|
|
|
|
const ArticulationLink& pLink = links[link.parent];
|
|
const PxTransform pBody2World = pLink.bodyCore->body2World;
|
|
|
|
const ArticulationJointCore* joint = link.inboundJoint;
|
|
|
|
const PxReal* jPosition = &jointPositions[jointDatum.jointOffset];
|
|
|
|
PxQuat newParentToChild;
|
|
PxQuat newWorldQ;
|
|
PxVec3 r;
|
|
|
|
const PxVec3 childOffset = -joint->childPose.p;
|
|
const PxVec3 parentOffset = joint->parentPose.p;
|
|
|
|
const PxQuat relativeQuat = mArticulationData.mRelativeQuat[linkID];
|
|
|
|
switch (joint->jointType)
|
|
{
|
|
case PxArticulationJointType::ePRISMATIC:
|
|
{
|
|
newParentToChild = relativeQuat;
|
|
const PxVec3 e = newParentToChild.rotate(parentOffset);
|
|
const PxVec3 d = childOffset;
|
|
|
|
const PxVec3& u = data.mMotionMatrix[jointDatum.jointOffset].bottom;
|
|
|
|
r = e + d + u * jPosition[0];
|
|
break;
|
|
}
|
|
case PxArticulationJointType::eREVOLUTE:
|
|
case PxArticulationJointType::eREVOLUTE_UNWRAPPED:
|
|
{
|
|
const PxVec3& u = data.mMotionMatrix[jointDatum.jointOffset].top;
|
|
|
|
PxQuat jointRotation = PxQuat(-jPosition[0], u);
|
|
if (jointRotation.w < 0) //shortest angle.
|
|
jointRotation = -jointRotation;
|
|
|
|
newParentToChild = (jointRotation * relativeQuat).getNormalized();
|
|
|
|
const PxVec3 e = newParentToChild.rotate(parentOffset);
|
|
const PxVec3 d = childOffset;
|
|
r = e + d;
|
|
|
|
PX_ASSERT(r.isFinite());
|
|
|
|
break;
|
|
}
|
|
case PxArticulationJointType::eSPHERICAL:
|
|
{
|
|
PxQuat jointRotation(PxIdentity);
|
|
|
|
{
|
|
PxVec3 axis(0.f);
|
|
for (PxU32 d = 0; d < jointDatum.nbDof; ++d)
|
|
{
|
|
axis += data.mMotionMatrix[jointDatum.jointOffset + d].top * -jPosition[d];
|
|
}
|
|
PxReal angle = axis.normalize();
|
|
jointRotation = angle < 1e-10f ? PxQuat(PxIdentity) : PxQuat(angle, axis);
|
|
if(jointRotation.w < 0.f)
|
|
jointRotation = -jointRotation;
|
|
}
|
|
|
|
|
|
newParentToChild = (jointRotation * relativeQuat).getNormalized();
|
|
|
|
const PxVec3 e = newParentToChild.rotate(parentOffset);
|
|
const PxVec3 d = childOffset;
|
|
r = e + d;
|
|
break;
|
|
}
|
|
case PxArticulationJointType::eFIX:
|
|
{
|
|
//this is fix joint so joint don't have velocity
|
|
newParentToChild = relativeQuat;
|
|
|
|
const PxVec3 e = newParentToChild.rotate(parentOffset);
|
|
const PxVec3 d = childOffset;
|
|
|
|
r = e + d;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
PxTransform& body2World = link.bodyCore->body2World;
|
|
body2World.q = (pBody2World.q * newParentToChild.getConjugate()).getNormalized();
|
|
body2World.p = pBody2World.p + body2World.q.rotate(r);
|
|
|
|
PX_ASSERT(body2World.isSane());
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::computeLinkVelocities(ArticulationData& data)
|
|
{
|
|
ArticulationLink* links = data.getLinks();
|
|
const PxU32 linkCount = data.getLinkCount();
|
|
|
|
Cm::SpatialVectorF* motionVelocities = data.getMotionVelocities();
|
|
const PxReal* jointVelocities = data.mJointVelocity.begin();
|
|
|
|
// sync root motion vel:
|
|
const PxsBodyCore& rootBodyCore = *links[0].bodyCore;
|
|
motionVelocities[0].top = rootBodyCore.angularVelocity;
|
|
motionVelocities[0].bottom = rootBodyCore.linearVelocity;
|
|
|
|
for (PxU32 linkID = 1; linkID < linkCount; ++linkID)
|
|
{
|
|
const ArticulationLink& link = links[linkID];
|
|
|
|
PxsBodyCore& bodyCore = *link.bodyCore;
|
|
|
|
PxTransform body2World = bodyCore.body2World;
|
|
|
|
ArticulationLink& plink = links[link.parent];
|
|
const PxsBodyCore& pbodyCore = *plink.bodyCore;
|
|
|
|
Cm::SpatialVectorF parentVel(pbodyCore.angularVelocity, pbodyCore.linearVelocity);
|
|
|
|
PxTransform pBody2World = pbodyCore.body2World;
|
|
|
|
const PxVec3 rw = body2World.p - pBody2World.p;
|
|
|
|
Cm::SpatialVectorF vel = FeatherstoneArticulation::translateSpatialVector(-rw,parentVel);
|
|
|
|
if (jointVelocities)
|
|
{
|
|
ArticulationJointCoreData& jointDatum = data.getJointData(linkID);
|
|
const PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset];
|
|
|
|
Cm::UnAlignedSpatialVector deltaV = Cm::UnAlignedSpatialVector::Zero();
|
|
for (PxU32 ind = 0; ind < jointDatum.nbDof; ++ind)
|
|
{
|
|
deltaV += data.mMotionMatrix[jointDatum.jointOffset + ind] * jVelocity[ind];
|
|
}
|
|
|
|
vel.top += body2World.q.rotate(deltaV.top);
|
|
vel.bottom += body2World.q.rotate(deltaV.bottom);
|
|
}
|
|
|
|
bodyCore.linearVelocity = vel.bottom;
|
|
bodyCore.angularVelocity = vel.top;
|
|
motionVelocities[linkID] = vel;
|
|
}
|
|
}
|
|
|
|
// AD: needed because we define the templated function in a CPP.
|
|
template void FeatherstoneArticulation::jcalc<false>(ArticulationData& data);
|
|
template void FeatherstoneArticulation::jcalc<true>(ArticulationData& data);
|
|
|
|
template<bool immediateMode>
|
|
void FeatherstoneArticulation::jcalc(ArticulationData& data)
|
|
{
|
|
const ArticulationLink* links = data.getLinks();
|
|
ArticulationJointCoreData* jointData = data.getJointData();
|
|
const PxU32 linkCount = data.getLinkCount();
|
|
|
|
PxU32 totalDof = 0;
|
|
|
|
for (PxU32 linkID = 1; linkID < linkCount; ++linkID)
|
|
{
|
|
const ArticulationLink& link = links[linkID];
|
|
ArticulationJointCore* joint = link.inboundJoint;
|
|
ArticulationJointCoreData& jointDatum = jointData[linkID];
|
|
|
|
PX_CHECK_AND_RETURN(joint->jointType != PxArticulationJointType::eUNDEFINED, "FeatherstoneArticulation::jcalc application need to define valid joint type and motion");
|
|
|
|
|
|
// AD: only used in immediate mode because we don't have the llArticulation there to write directly.
|
|
if (immediateMode)
|
|
{
|
|
|
|
// compute joint dof
|
|
const PxU32 dof = jointDatum.configureJointDofs(joint, data.mJointAxis.begin() + totalDof);
|
|
|
|
|
|
PxReal* PX_RESTRICT jointTargetPositions = data.getJointTargetPositions();
|
|
PxReal* PX_RESTRICT jointTargetVelocities = data.getJointTargetVelocities();
|
|
|
|
for(PxU32 dofId = 0; dofId < dof; ++dofId)
|
|
{
|
|
PxU32 id = totalDof + dofId;
|
|
PxU32 llDofId = joint->dofIds[dofId];
|
|
jointTargetPositions[id] = joint->targetP[llDofId];
|
|
jointTargetVelocities[id] = joint->targetV[llDofId];
|
|
}
|
|
|
|
jointDatum.jointOffset = totalDof;
|
|
joint->jointOffset = totalDof;
|
|
totalDof += dof;
|
|
}
|
|
|
|
joint->setJointFrame(&data.mMotionMatrix[jointDatum.jointOffset], &data.mJointAxis[jointDatum.jointOffset],
|
|
mArticulationData.mRelativeQuat[linkID],
|
|
jointDatum.nbDof);
|
|
|
|
|
|
}
|
|
|
|
if(immediateMode)
|
|
{
|
|
if(totalDof != mArticulationData.getDofs())
|
|
{
|
|
mArticulationData.resizeJointData(totalDof);
|
|
}
|
|
mArticulationData.setDofs(totalDof);
|
|
}
|
|
}
|
|
|
|
//compute link's spatial inertia tensor
|
|
void FeatherstoneArticulation::computeSpatialInertia(ArticulationData& data)
|
|
{
|
|
for (PxU32 linkID = 0; linkID < data.getLinkCount(); ++linkID)
|
|
{
|
|
const ArticulationLink& link = data.getLink(linkID);
|
|
//ArticulationLinkData& linkDatum = data.getLinkData(linkID);
|
|
|
|
const PxsBodyCore& core = *link.bodyCore;
|
|
|
|
const PxVec3& ii = core.inverseInertia;
|
|
|
|
const PxReal m = core.inverseMass == 0.f ? 0.f : 1.0f / core.inverseMass;
|
|
|
|
SpatialMatrix& worldArticulatedInertia = data.mWorldSpatialArticulatedInertia[linkID];
|
|
|
|
//construct inertia matrix
|
|
const PxVec3 inertiaTensor = PxVec3(ii.x == 0.f ? 0.f : (1.f / ii.x), ii.y == 0.f ? 0.f : (1.f / ii.y), ii.z == 0.f ? 0.f : (1.f / ii.z));
|
|
|
|
PxMat33 rot(data.getLink(linkID).bodyCore->body2World.q);
|
|
|
|
worldArticulatedInertia.topLeft = PxMat33(PxZero);
|
|
worldArticulatedInertia.topRight = PxMat33::createDiagonal(PxVec3(m));
|
|
Cm::transformInertiaTensor(inertiaTensor, rot, worldArticulatedInertia.bottomLeft);
|
|
|
|
data.mWorldIsolatedSpatialArticulatedInertia[linkID] = worldArticulatedInertia.bottomLeft;
|
|
data.mMasses[linkID] = m;
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::computeZ(const ArticulationData& data,
|
|
const PxVec3& gravity, ScratchData& scratchData)
|
|
{
|
|
const Cm::SpatialVectorF* motionVelocities = scratchData.motionVelocities;
|
|
Cm::SpatialVectorF* spatialZAForces = scratchData.spatialZAVectors;
|
|
const Cm::SpatialVector* externalAccels = scratchData.externalAccels;
|
|
|
|
for (PxU32 linkID = 0; linkID < data.getLinkCount(); ++linkID)
|
|
{
|
|
ArticulationLink& link = data.getLink(linkID);
|
|
|
|
const PxsBodyCore& core = *link.bodyCore;
|
|
//const PxTransform& body2World = core.body2World;
|
|
|
|
const PxMat33& I = data.mWorldSpatialArticulatedInertia[linkID].bottomLeft;
|
|
|
|
//construct spatial zero acceleration
|
|
Cm::SpatialVectorF& z = spatialZAForces[linkID];
|
|
|
|
//Cm::SpatialVectorF v;
|
|
//v.top = motionVelocities[linkID].top;
|
|
//v.bottom = motionVelocities[linkID].bottom;
|
|
|
|
//KS - limit the magnitude of the angular velocity that contributes to the geometric term. This is a
|
|
//v^2 term and can become unstable if it is too large!
|
|
|
|
Cm::SpatialVectorF v = motionVelocities[linkID];
|
|
|
|
PxVec3 vA = v.top;
|
|
|
|
PxVec3 gravLinAccel(0.f);
|
|
if(!core.disableGravity)
|
|
gravLinAccel = -gravity;
|
|
|
|
PX_ASSERT(core.inverseMass != 0.f);
|
|
|
|
const PxReal m = 1.0f / core.inverseMass;
|
|
|
|
Cm::SpatialVectorF zTmp;
|
|
|
|
zTmp.top = (gravLinAccel * m);
|
|
zTmp.bottom = vA.cross(I * vA);
|
|
|
|
PX_ASSERT(zTmp.top.isFinite());
|
|
PX_ASSERT(zTmp.bottom.isFinite());
|
|
|
|
if (externalAccels)
|
|
{
|
|
const Cm::SpatialVector& externalAccel = externalAccels[linkID];
|
|
|
|
const PxVec3 exLinAccel = -externalAccel.linear;
|
|
const PxVec3 exAngAccel = -externalAccel.angular;
|
|
|
|
zTmp.top += (exLinAccel * m);
|
|
zTmp.bottom += I * exAngAccel;
|
|
}
|
|
|
|
z = zTmp;
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::computeZD(const ArticulationData& data,
|
|
const PxVec3& gravity, ScratchData& scratchData)
|
|
{
|
|
const Cm::SpatialVectorF* motionVelocities = scratchData.motionVelocities;
|
|
Cm::SpatialVectorF* spatialZAForces = scratchData.spatialZAVectors;
|
|
const Cm::SpatialVector* externalAccels = scratchData.externalAccels;
|
|
|
|
const PxReal dt = data.getDt();
|
|
const PxReal invDt = dt < 1e-6f ? PX_MAX_F32 : 1.f / data.mDt;
|
|
|
|
for (PxU32 linkID = 0; linkID < data.getLinkCount(); ++linkID)
|
|
{
|
|
ArticulationLink& link = data.getLink(linkID);
|
|
|
|
const PxsBodyCore& core = *link.bodyCore;
|
|
//const PxTransform& body2World = core.body2World;
|
|
|
|
const PxMat33& I = data.mWorldSpatialArticulatedInertia[linkID].bottomLeft;
|
|
|
|
//construct spatial zero acceleration
|
|
Cm::SpatialVectorF& z = spatialZAForces[linkID];
|
|
|
|
//Cm::SpatialVectorF v;
|
|
//v.top = motionVelocities[linkID].top;
|
|
//v.bottom = motionVelocities[linkID].bottom;
|
|
|
|
//KS - limit the magnitude of the angular velocity that contributes to the geometric term. This is a
|
|
//v^2 term and can become unstable if it is too large!
|
|
|
|
Cm::SpatialVectorF v = motionVelocities[linkID];
|
|
|
|
PxVec3 vA = v.top;
|
|
|
|
PxVec3 gravLinAccel(0.f);
|
|
if (!core.disableGravity)
|
|
gravLinAccel = -gravity;
|
|
|
|
PX_ASSERT(core.inverseMass != 0.f);
|
|
|
|
const PxReal m = 1.0f / core.inverseMass;
|
|
|
|
Cm::SpatialVectorF zTmp;
|
|
|
|
zTmp.top = (gravLinAccel * m);
|
|
zTmp.bottom = vA.cross(I * vA);
|
|
|
|
PX_ASSERT(zTmp.top.isFinite());
|
|
PX_ASSERT(zTmp.bottom.isFinite());
|
|
|
|
if (externalAccels)
|
|
{
|
|
const Cm::SpatialVector& externalAccel = externalAccels[linkID];
|
|
|
|
const PxVec3 exLinAccel = -externalAccel.linear;
|
|
const PxVec3 exAngAccel = -externalAccel.angular;
|
|
|
|
zTmp.top += (exLinAccel * m);
|
|
zTmp.bottom += I * exAngAccel;
|
|
}
|
|
|
|
if (core.linearDamping > 0.f || core.angularDamping > 0.f)
|
|
{
|
|
const PxReal linDamp = PxMin(core.linearDamping, invDt);
|
|
const PxReal angDamp = PxMin(core.angularDamping, invDt);
|
|
|
|
|
|
zTmp.top += (v.bottom * linDamp*m) - zTmp.top * linDamp*dt;
|
|
zTmp.bottom += I * (v.top* angDamp) - zTmp.bottom * angDamp*dt;
|
|
}
|
|
|
|
const PxReal maxAng = core.maxAngularVelocitySq;
|
|
const PxReal maxLin = core.maxLinearVelocitySq;
|
|
|
|
const PxReal angMag = v.top.magnitudeSquared();
|
|
const PxReal linMag = v.bottom.magnitudeSquared();
|
|
|
|
if (angMag > maxAng || linMag > maxLin)
|
|
{
|
|
if (angMag > maxAng)
|
|
{
|
|
const PxReal scale = 1.f - PxSqrt(maxAng) / PxSqrt(angMag);
|
|
const PxVec3 tmpaccelerationAng = (I * v.top)*scale;
|
|
zTmp.bottom += tmpaccelerationAng*invDt;
|
|
}
|
|
|
|
if (linMag > maxLin)
|
|
{
|
|
const PxReal scale = 1.f - (PxSqrt(maxLin) / PxSqrt(linMag));
|
|
const PxVec3 tmpaccelerationLin = (v.bottom*m*scale);
|
|
PX_UNUSED(tmpaccelerationLin);
|
|
zTmp.top += tmpaccelerationLin*invDt;
|
|
}
|
|
}
|
|
|
|
z = zTmp;
|
|
}
|
|
}
|
|
|
|
//compute coriolis and centrifugal term
|
|
void FeatherstoneArticulation::computeC(ArticulationData& data, ScratchData& scratchData)
|
|
{
|
|
const PxReal* jointVelocities = scratchData.jointVelocities;
|
|
Cm::SpatialVectorF* coriolisVectors = scratchData.coriolisVectors;
|
|
|
|
const PxU32 linkCount = data.getLinkCount();
|
|
|
|
coriolisVectors[0] = Cm::SpatialVectorF::Zero();
|
|
|
|
for (PxU32 linkID = 1; linkID < linkCount; ++linkID)
|
|
{
|
|
const ArticulationLink& link = data.getLink(linkID);
|
|
const ArticulationJointCoreData& jointDatum = data.getJointData(linkID);
|
|
|
|
const PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset];
|
|
Cm::SpatialVectorF& coriolis = coriolisVectors[linkID];
|
|
|
|
|
|
//const PxTransform& body2World = link.bodyCore->body2World;
|
|
//transform parent link's angular velocity into current link's body space
|
|
const PxVec3 pAngular = scratchData.motionVelocities[link.parent].top;
|
|
|
|
PxVec3 torque = pAngular.cross(pAngular.cross(data.getRw(linkID)));
|
|
|
|
//PX_ASSERT(parentAngular.magnitude() < 100.f);
|
|
|
|
PxVec3 force(0.f);
|
|
if (jointDatum.nbDof > 0)
|
|
{
|
|
Cm::SpatialVectorF relVel(PxVec3(0.f), PxVec3(0.f));
|
|
for (PxU32 ind = 0; ind < jointDatum.nbDof; ++ind)
|
|
{
|
|
//Clamp joint velocity used in coriolis terms to reduce chances of unstable feed-back loops
|
|
const PxReal jV = jVelocity[ind];
|
|
relVel.top += data.mWorldMotionMatrix[jointDatum.jointOffset + ind].top * jV;
|
|
relVel.bottom += data.mWorldMotionMatrix[jointDatum.jointOffset + ind].bottom * jV;
|
|
}
|
|
const PxVec3 aVec = relVel.top;
|
|
force = pAngular.cross(aVec);
|
|
|
|
//compute linear part
|
|
const PxVec3 lVel = relVel.bottom;
|
|
|
|
const PxVec3 temp1 = 2.f * pAngular.cross(lVel);
|
|
const PxVec3 temp2 = aVec.cross(lVel);
|
|
torque += temp1 + temp2;
|
|
}
|
|
|
|
PX_ASSERT(force.isFinite());
|
|
PX_ASSERT(torque.isFinite());
|
|
|
|
coriolis = Cm::SpatialVectorF(force, torque);//.rotateInv(body2World);
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::computeRelativeTransformC2P(
|
|
const ArticulationLink* links, const PxU32 linkCount, const ArticulationJointCoreData* jointCoreDatas,
|
|
const Cm::UnAlignedSpatialVector* jonitDofMotionMatrices,
|
|
PxTransform* linkAccumulatedPoses, PxVec3* linkRws, Cm::UnAlignedSpatialVector* jointDofMotionMatricesW)
|
|
{
|
|
linkAccumulatedPoses[0] = links[0].bodyCore->body2World;
|
|
|
|
for (PxU32 linkID = 1; linkID < linkCount; ++linkID)
|
|
{
|
|
const ArticulationLink& link = links[linkID];
|
|
const PxsBodyCore& bodyCore = *link.bodyCore;
|
|
const PxU32 jointOffset = jointCoreDatas[linkID].jointOffset;
|
|
const PxU32 dofCount = jointCoreDatas[linkID].nbDof;
|
|
|
|
const PxTransform& body2World = bodyCore.body2World;
|
|
|
|
const ArticulationLink& pLink = links[link.parent];
|
|
const PxsBodyCore& pBodyCore = *pLink.bodyCore;
|
|
const PxTransform& pBody2World = pBodyCore.body2World;
|
|
|
|
//const PxTransform tC2P = pBody2World.transformInv(body2World).getNormalized();
|
|
|
|
linkRws[linkID] = body2World.p - pBody2World.p;
|
|
|
|
const Cm::UnAlignedSpatialVector* motionMatrix = &jonitDofMotionMatrices[jointOffset];
|
|
Cm::UnAlignedSpatialVector* worldMotionMatrix = &jointDofMotionMatricesW[jointOffset];
|
|
|
|
for (PxU32 i = 0; i < dofCount; ++i)
|
|
{
|
|
const Cm::UnAlignedSpatialVector worldRow = motionMatrix[i].rotate(body2World);
|
|
worldMotionMatrix[i] = worldRow;
|
|
}
|
|
|
|
linkAccumulatedPoses[linkID] = body2World;
|
|
|
|
#if FEATURESTONE_DEBUG
|
|
{
|
|
//debug
|
|
PxMat33 pToC = c2p.getTranspose();
|
|
//parentToChild -rR
|
|
PxMat33 T2 = skewMatrixPR * pToC;
|
|
|
|
PX_ASSERT(SpatialMatrix::isTranspose(linkDatum.childToParent.T, T2));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::computeRelativeTransformC2B(ArticulationData& data)
|
|
{
|
|
ArticulationLink* links = data.getLinks();
|
|
ArticulationLinkData* linkData = data.getLinkData();
|
|
const PxU32 linkCount = data.getLinkCount();
|
|
|
|
const ArticulationLink& bLink = links[0];
|
|
const PxTransform& bBody2World = bLink.bodyCore->body2World;
|
|
|
|
for (PxU32 linkID = 1; linkID < linkCount; ++linkID)
|
|
{
|
|
const ArticulationLink& link = links[linkID];
|
|
ArticulationLinkData& linkDatum = linkData[linkID];
|
|
const PxsBodyCore& bodyCore = *link.bodyCore;
|
|
|
|
const PxTransform& body2World = bodyCore.body2World;
|
|
|
|
const PxVec3 rw = body2World.p - bBody2World.p;//body space of link i
|
|
|
|
//rotation matrix cToP's inverse is rotation matrix pToC
|
|
linkDatum.childToBase = rw;
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::getDenseJacobian(PxArticulationCache& cache, PxU32 & nRows, PxU32 & nCols)
|
|
{
|
|
//make sure motionMatrix has been set
|
|
//jcalc(mArticulationData);
|
|
initializeCommonData();
|
|
|
|
const PxU32 linkCount = mArticulationData.getLinkCount();
|
|
ArticulationLink* links = mArticulationData.getLinks();
|
|
|
|
#if 0 //Enable this if you want to compare results of this function with results of computeLinkVelocities().
|
|
|
|
if (mArticulationData.getDataDirty())
|
|
{
|
|
PxGetFoundation().error(PxErrorCode::eINVALID_OPERATION, PX_FL, "Articulation::getDenseJacobian(): commonInit need to be called first to initialize data!");
|
|
return;
|
|
}
|
|
|
|
PxcScratchAllocator* allocator = reinterpret_cast<PxcScratchAllocator*>(cache.scratchAllocator);
|
|
|
|
ScratchData scratchData;
|
|
PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData);
|
|
|
|
scratchData.jointVelocities = mArticulationData.getJointVelocities();
|
|
|
|
|
|
computeLinkVelocities(mArticulationData, scratchData);
|
|
const ArticulationLink& baseLink = links[0];
|
|
PxsBodyCore& core0 = *baseLink.bodyCore;
|
|
#endif
|
|
|
|
ArticulationLinkData* linkData = mArticulationData.getLinkData();
|
|
|
|
const PxU32 totalDofs = getDofs();
|
|
const bool fixBase = mArticulationData.getArticulationFlags() & PxArticulationFlag::eFIX_BASE;
|
|
//matrix dims is
|
|
nCols = (fixBase ? 0 : 6) + totalDofs;
|
|
nRows = (fixBase ? 0 : 6) + (linkCount - 1) * 6;
|
|
|
|
// auto jacobian = [&](PxU32 row, PxU32 col) -> PxReal & { return cache.denseJacobian[nCols * row + col]; } ;
|
|
#define jacobian(row, col) cache.denseJacobian[nCols * (row) + (col)]
|
|
|
|
PxU32 destRow = 0;
|
|
PxU32 destCol = 0;
|
|
|
|
if (!fixBase)
|
|
{
|
|
jacobian(0, 0) = 1.0f;
|
|
jacobian(0, 1) = 0.0f;
|
|
jacobian(0, 2) = 0.0f;
|
|
jacobian(0, 3) = 0.0f;
|
|
jacobian(0, 4) = 0.0f;
|
|
jacobian(0, 5) = 0.0f;
|
|
|
|
jacobian(1, 0) = 0.0f;
|
|
jacobian(1, 1) = 1.0f;
|
|
jacobian(1, 2) = 0.0f;
|
|
jacobian(1, 3) = 0.0f;
|
|
jacobian(1, 4) = 0.0f;
|
|
jacobian(1, 5) = 0.0f;
|
|
|
|
jacobian(2, 0) = 0.0f;
|
|
jacobian(2, 1) = 0.0f;
|
|
jacobian(2, 2) = 1.0f;
|
|
jacobian(2, 3) = 0.0f;
|
|
jacobian(2, 4) = 0.0f;
|
|
jacobian(2, 5) = 0.0f;
|
|
|
|
jacobian(3, 0) = 0.0f;
|
|
jacobian(3, 1) = 0.0f;
|
|
jacobian(3, 2) = 0.0f;
|
|
jacobian(3, 3) = 1.0f;
|
|
jacobian(3, 4) = 0.0f;
|
|
jacobian(3, 5) = 0.0f;
|
|
|
|
jacobian(4, 0) = 0.0f;
|
|
jacobian(4, 1) = 0.0f;
|
|
jacobian(4, 2) = 0.0f;
|
|
jacobian(4, 3) = 0.0f;
|
|
jacobian(4, 4) = 1.0f;
|
|
jacobian(4, 5) = 0.0f;
|
|
|
|
jacobian(5, 0) = 0.0f;
|
|
jacobian(5, 1) = 0.0f;
|
|
jacobian(5, 2) = 0.0f;
|
|
jacobian(5, 3) = 0.0f;
|
|
jacobian(5, 4) = 0.0f;
|
|
jacobian(5, 5) = 1.0f;
|
|
|
|
destRow += 6;
|
|
destCol += 6;
|
|
}
|
|
|
|
for (PxU32 linkID = 1; linkID < linkCount; ++linkID)//each iteration of this writes 6 rows in the matrix
|
|
{
|
|
const ArticulationLink& link = links[linkID];
|
|
ArticulationLinkData& linkDatum = linkData[linkID];
|
|
const PxsBodyCore& bodyCore = *link.bodyCore;
|
|
|
|
linkDatum.maxPenBias = bodyCore.maxPenBias;
|
|
|
|
const PxTransform& body2World = bodyCore.body2World;
|
|
|
|
const ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID);
|
|
const PxU32 parentLinkID = link.parent;
|
|
|
|
if (parentLinkID || !fixBase)
|
|
{
|
|
// VR: mArticulationData.getJointData(0) isn't initialized. parentLinkID can be 0 if fixBase is false.
|
|
//const ArticulationJointCoreData& parentJointDatum = mArticulationData.getJointData(parentLinkID);
|
|
//const PxU32 parentsFirstDestCol = parentJointDatum.jointOffset + (fixBase ? 0 : 6);
|
|
//const PxU32 parentsLastDestCol = parentsFirstDestCol + parentJointDatum.dof;
|
|
const PxU32 parentsLastDestCol = parentLinkID ?
|
|
mArticulationData.getJointData(parentLinkID).jointOffset + (fixBase ? 0 : 6) + mArticulationData.getJointData(parentLinkID).nbDof :
|
|
6;
|
|
|
|
// VR: With parentLinkID == 0 this expression has two unsigned integer overflows, but the result is still correct.
|
|
const PxU32 parentsDestRow = (fixBase ? 0 : 6) + (parentLinkID - 1) * 6;
|
|
|
|
for (PxU32 col = 0; col < parentsLastDestCol; col++)
|
|
{
|
|
//copy downward the 6 cols from parent
|
|
const PxVec3 parentAng(
|
|
jacobian(parentsDestRow + 3, col),
|
|
jacobian(parentsDestRow + 4, col),
|
|
jacobian(parentsDestRow + 5, col)
|
|
);
|
|
|
|
const PxVec3 parentAngxRw = parentAng.cross(mArticulationData.getRw(linkID));
|
|
|
|
jacobian(destRow + 0, col) = jacobian(parentsDestRow + 0, col) + parentAngxRw.x;
|
|
jacobian(destRow + 1, col) = jacobian(parentsDestRow + 1, col) + parentAngxRw.y;
|
|
jacobian(destRow + 2, col) = jacobian(parentsDestRow + 2, col) + parentAngxRw.z;
|
|
|
|
jacobian(destRow + 3, col) = parentAng.x;
|
|
jacobian(destRow + 4, col) = parentAng.y;
|
|
jacobian(destRow + 5, col) = parentAng.z;
|
|
}
|
|
|
|
for (PxU32 col = parentsLastDestCol; col < destCol; col++)
|
|
{
|
|
//fill with zeros.
|
|
jacobian(destRow + 0, col) = 0.0f;
|
|
jacobian(destRow + 1, col) = 0.0f;
|
|
jacobian(destRow + 2, col) = 0.0f;
|
|
|
|
jacobian(destRow + 3, col) = 0.0f;
|
|
jacobian(destRow + 4, col) = 0.0f;
|
|
jacobian(destRow + 5, col) = 0.0f;
|
|
}
|
|
}
|
|
|
|
//diagonal block:
|
|
for (PxU32 ind = 0; ind < jointDatum.nbDof; ++ind)
|
|
{
|
|
const Cm::UnAlignedSpatialVector& v = mArticulationData.mMotionMatrix[jointDatum.jointOffset + ind];
|
|
|
|
const PxVec3 ang = body2World.rotate(v.top);
|
|
const PxVec3 lin = body2World.rotate(v.bottom);
|
|
|
|
jacobian(destRow + 0, destCol) = lin.x;
|
|
jacobian(destRow + 1, destCol) = lin.y;
|
|
jacobian(destRow + 2, destCol) = lin.z;
|
|
|
|
jacobian(destRow + 3, destCol) = ang.x;
|
|
jacobian(destRow + 4, destCol) = ang.y;
|
|
jacobian(destRow + 5, destCol) = ang.z;
|
|
|
|
destCol++;
|
|
}
|
|
|
|
//above diagonal block: always zero
|
|
for (PxU32 col = destCol; col < nCols; col++)
|
|
{
|
|
jacobian(destRow + 0, col) = 0.0f;
|
|
jacobian(destRow + 1, col) = 0.0f;
|
|
jacobian(destRow + 2, col) = 0.0f;
|
|
|
|
jacobian(destRow + 3, col) = 0.0f;
|
|
jacobian(destRow + 4, col) = 0.0f;
|
|
jacobian(destRow + 5, col) = 0.0f;
|
|
}
|
|
|
|
destRow += 6;
|
|
}
|
|
#undef jacobian
|
|
|
|
#if 0 //Enable this if you want to compare results of this function with results of computeLinkVelocities().
|
|
|
|
PxReal * jointVels = mArticulationData.getJointVelocities();//size is totalDofs
|
|
|
|
PxReal * jointSpaceVelsVector = new PxReal[nCols];
|
|
PxReal * worldSpaceVelsVector = new PxReal[nRows];
|
|
|
|
PxU32 offset = 0;
|
|
|
|
//stack input:
|
|
|
|
if (!fixBase)
|
|
{
|
|
jointSpaceVelsVector[0] = core0.linearVelocity[0];
|
|
jointSpaceVelsVector[1] = core0.linearVelocity[1];
|
|
jointSpaceVelsVector[2] = core0.linearVelocity[2];
|
|
|
|
jointSpaceVelsVector[3] = core0.angularVelocity[0];
|
|
jointSpaceVelsVector[4] = core0.angularVelocity[1];
|
|
jointSpaceVelsVector[5] = core0.angularVelocity[2];
|
|
|
|
offset = 6;
|
|
}
|
|
|
|
for (PxU32 i = 0; i < totalDofs; i++)
|
|
jointSpaceVelsVector[i + offset] = jointVels[i];
|
|
|
|
//multiply:
|
|
|
|
for (PxU32 row = 0; row < nRows; row++)
|
|
{
|
|
worldSpaceVelsVector[row] = 0.0f;
|
|
for (PxU32 col = 0; col < nCols; col++)
|
|
{
|
|
worldSpaceVelsVector[row] += jacobian(row, col)*jointSpaceVelsVector[col];
|
|
}
|
|
}
|
|
|
|
//worldSpaceVelsVector should now contain the same result as scratchData.motionVelocities (except for swapped linear/angular vec3 order).
|
|
|
|
delete[] jointSpaceVelsVector;
|
|
delete[] worldSpaceVelsVector;
|
|
|
|
allocator->free(tempMemory);
|
|
#endif
|
|
}
|
|
|
|
void FeatherstoneArticulation::computeLinkStates(
|
|
const PxF32 dt, const PxReal invLengthScale, const PxVec3& gravity,
|
|
const bool fixBase,
|
|
const PxU32 linkCount,
|
|
const PxTransform* linkAccumulatedPosesW, Cm::SpatialVector* linkExternalAccelsW, const PxVec3* linkRsW, const Cm::UnAlignedSpatialVector* jointDofMotionMatricesW,
|
|
const Dy::ArticulationJointCoreData* jointCoreData, bool externalForcesEveryTgsIterationEnabled,
|
|
Dy::ArticulationLinkData *linkData, Dy::ArticulationLink* links,
|
|
Cm::SpatialVectorF* linkMotionAccelerationsW, Cm::SpatialVectorF* linkMotionVelocitiesW,
|
|
Cm::SpatialVectorF* linkZAExtForcesW, Cm::SpatialVectorF* linkZAIntForcesW, Cm::SpatialVectorF* linkCoriolisVectorsW,
|
|
PxMat33* linkIsolatedSpatialArticulatedInertiasW, PxF32* linkMasses, Dy::SpatialMatrix* linkSpatialArticulatedInertiasW,
|
|
PxReal* jointDofVelocities,
|
|
Cm::SpatialVectorF& rootPreMotionVelocityW, PxVec3& comW, PxF32& invSumMass)
|
|
{
|
|
const PxReal invDt = dt < 1e-6f ? PX_MAX_F32 : 1.f / dt;
|
|
|
|
//Initialise motion velocity, motion acceleration and coriolis vector of root link.
|
|
Cm::SpatialVectorF rootLinkVel;
|
|
{
|
|
const Dy::ArticulationLink& baseLink = links[0];
|
|
const PxsBodyCore& core0 = *baseLink.bodyCore;
|
|
rootLinkVel = fixBase ? Cm::SpatialVectorF::Zero() : Cm::SpatialVectorF(core0.angularVelocity, core0.linearVelocity);
|
|
linkMotionVelocitiesW[0] = rootLinkVel;
|
|
linkMotionAccelerationsW[0] = fixBase ? Cm::SpatialVectorF::Zero() : linkMotionAccelerationsW[0];
|
|
linkCoriolisVectorsW[0] = Cm::SpatialVectorF::Zero();
|
|
rootPreMotionVelocityW = rootLinkVel;
|
|
}
|
|
|
|
//Is it really necessary? It is already resolved as an internal cosntraint.
|
|
PxReal ratio = 1.f;
|
|
if (jointDofVelocities)
|
|
{
|
|
|
|
for (PxU32 linkID = 1; linkID < linkCount; ++linkID)
|
|
{
|
|
const ArticulationLink& link = links[linkID];
|
|
const ArticulationJointCoreData& jointDatum = jointCoreData[linkID];
|
|
const PxReal* jVelocity = &jointDofVelocities[jointDatum.jointOffset];
|
|
for (PxU32 ind = 0; ind < jointDatum.nbDof; ++ind)
|
|
{
|
|
const PxReal maxJVelocity = link.inboundJoint->maxJointVelocity[ind];
|
|
PxReal jVel = jVelocity[ind];
|
|
ratio = (jVel != 0.0f) ? PxMin(ratio, maxJVelocity / PxAbs(jVel)) : ratio;
|
|
}
|
|
}
|
|
}
|
|
|
|
PxReal sumMass = 0.f;
|
|
PxVec3 COM(0.f);
|
|
for (PxU32 linkID = 0; linkID < linkCount; ++linkID)
|
|
{
|
|
ArticulationLink& link = links[linkID];
|
|
ArticulationLinkData& linkDatum = linkData[linkID];
|
|
const PxsBodyCore& bodyCore = *link.bodyCore;
|
|
|
|
//Set the maxPemBias and cfm values from bodyCore
|
|
linkDatum.maxPenBias = bodyCore.maxPenBias;
|
|
link.cfm = (fixBase && linkID == 0) ? 0.f : bodyCore.cfmScale * invLengthScale;
|
|
|
|
//Read the inertia and mass from bodyCore
|
|
const PxVec3& ii = bodyCore.inverseInertia;
|
|
const PxVec3 inertiaTensor = PxVec3(ii.x == 0.f ? 0.f : (1.f / ii.x), ii.y == 0.f ? 0.f : (1.f / ii.y), ii.z == 0.f ? 0.f : (1.f / ii.z));
|
|
const PxReal invMass = bodyCore.inverseMass;
|
|
const PxReal m = invMass == 0.f ? 0.f : 1.0f / invMass;
|
|
|
|
//Compute the inertia matrix Iw = R * I * Rtranspose
|
|
//Compute the articulated inertia.
|
|
PxMat33 Iw; //R * I * Rtranspose
|
|
SpatialMatrix worldArticulatedInertia;
|
|
{
|
|
PxMat33 rot(linkAccumulatedPosesW[linkID].q);
|
|
Cm::transformInertiaTensor(inertiaTensor, rot, Iw);
|
|
worldArticulatedInertia.topLeft = PxMat33(PxZero);
|
|
worldArticulatedInertia.topRight = PxMat33::createDiagonal(PxVec3(m));
|
|
worldArticulatedInertia.bottomLeft = Iw;
|
|
}
|
|
|
|
//Set the articulated inertia, inertia and mass of the link.
|
|
linkSpatialArticulatedInertiasW[linkID] = worldArticulatedInertia;
|
|
linkIsolatedSpatialArticulatedInertiasW[linkID] = Iw;
|
|
linkMasses[linkID] = m;
|
|
|
|
//Accumulate the centre of mass.
|
|
sumMass += m;
|
|
COM += linkAccumulatedPosesW[linkID].p * m;
|
|
|
|
Cm::SpatialVectorF vel;
|
|
if (linkID != 0)
|
|
{
|
|
//Propagate spatial velocity of link parent to link's spatial velocity.
|
|
const Cm::SpatialVectorF pVel = linkMotionVelocitiesW[link.parent];
|
|
vel = FeatherstoneArticulation::translateSpatialVector(-linkRsW[linkID], pVel);
|
|
|
|
//Propagate joint dof velocities to the link's spatial velocity vector.
|
|
//Accumulate spatial forces that the joint applies to the link.
|
|
if (jointDofVelocities)
|
|
{
|
|
//The coriolis vector depends on the type of joint and the joint motion matrix.
|
|
//However, some terms in the coriolis vector are common to all joint types.
|
|
//Write down the term that is independent of the joint.
|
|
Cm::SpatialVectorF coriolisVector(PxVec3(PxZero), pVel.top.cross(pVel.top.cross(linkRsW[linkID])));
|
|
const ArticulationJointCoreData& jointDatum = jointCoreData[linkID];
|
|
if (jointDatum.nbDof)
|
|
{
|
|
//Compute the effect of the joint velocities on the link.
|
|
PxReal* jVelocity = &jointDofVelocities[jointDatum.jointOffset];
|
|
Cm::UnAlignedSpatialVector deltaV = Cm::UnAlignedSpatialVector::Zero();
|
|
for (PxU32 ind = 0; ind < jointDatum.nbDof; ++ind)
|
|
{
|
|
PxReal jVel = jVelocity[ind] * ratio;
|
|
deltaV += jointDofMotionMatricesW[jointDatum.jointOffset + ind] * jVel;
|
|
jVelocity[ind] = jVel;
|
|
}
|
|
|
|
//Add the effect of the joint velocities to the link.
|
|
vel.top += deltaV.top;
|
|
vel.bottom += deltaV.bottom;
|
|
|
|
//Compute the coriolis vector.
|
|
//mu(i) is the velocity arising from the joint motion: mu(i) = motionMatrix*qdot
|
|
//where qdot is the joint velocity.
|
|
//mu(i) is already computed and cached in deltaV.
|
|
//For the revolute joint we have:
|
|
//deltaV = {mu(i), mu(i) X d(i)}
|
|
//coriolis vector += {omega(i-1) X mu(i), 2*omega(i-1) X (mu(i) X d(i)) + mu(i) X (mu(i) X d(i))}
|
|
//For the prismatic joint we have:
|
|
//deltaV = {0, mu(i)}
|
|
//coriolis vector += {0, 2*omega(i-1) X mu(i)}
|
|
coriolisVector += Cm::SpatialVectorF(pVel.top.cross(deltaV.top), 2.0f*pVel.top.cross(deltaV.bottom) + deltaV.top.cross(deltaV.bottom));
|
|
}
|
|
//TODOGY - if jointVelocities is null we do not appear to set coriolisVectors[linkId] but we do set coriolisVectors[0]
|
|
linkCoriolisVectorsW[linkID] = coriolisVector;
|
|
}
|
|
|
|
//PX_ASSERT(vel.top.isFinite() && PxAbs(vel.top.x) < 10000.f && PxAbs(vel.top.y) < 10000.f && PxAbs(vel.top.z) < 10000.f);
|
|
//PX_ASSERT(vel.bottom.isFinite() && PxAbs(vel.bottom.x) < 10000.f && PxAbs(vel.bottom.y) < 10000.f && PxAbs(vel.bottom.z) < 10000.f);
|
|
linkMotionVelocitiesW[linkID] = vel;
|
|
}
|
|
else
|
|
{
|
|
vel = rootLinkVel;
|
|
//Note: we have already set motionVelocities[0] and coriolisVectors[0] so no need to set them again.
|
|
}
|
|
|
|
//Account for force arising from external accelerations
|
|
//Account for damping force arising from the velocity and from the velocity that will accumulate from the acceleration terms.
|
|
//Account for scaling force that will bring velocity back to the maximum allowed velocity if velocity exceed the maximum allowed.
|
|
//Example for the linear term:
|
|
//acceleration force = m*(g + extLinAccel)
|
|
//linVelNew = linVel + (g + extLinAccel)*dt
|
|
//damping force = -m*linVelNew*linDamp = -m*linVel*linDamp - m*(g + extLinAccel)*linDamp*dt
|
|
//scaling force = -m*linVel*linScale/dt with linScale = (1.0f - maxLinVel/linVel)
|
|
//Put it all together
|
|
// m*(g + extLinAccel)*(1 - linDamp*dt) - m*linVel*(linDamp + linScale/dt)
|
|
//Bit more reordering:
|
|
//m*[g + extLinAccel)*(1 - linDamp*dt) - linVel*(linDamp + linScale/dt)]
|
|
//Zero acceleration means we need to work against change:
|
|
//-m*[g + extLinAccel)*(1 - linDamp*dt) - linVel*(linDamp + linScale/dt)]
|
|
Cm::SpatialVectorF zExtForces;
|
|
Cm::SpatialVectorF zDamping;
|
|
{
|
|
const PxVec3 g = bodyCore.disableGravity ? PxVec3(PxZero) : gravity;
|
|
const PxVec3 extLinAccel = linkExternalAccelsW ? linkExternalAccelsW[linkID].linear : PxVec3(PxZero);
|
|
const PxF32 lindamp = bodyCore.linearDamping > 0.f ? PxMin(bodyCore.linearDamping, invDt) : 0.0f;
|
|
const PxF32 linscale = (vel.bottom.magnitudeSquared() > bodyCore.maxLinearVelocitySq) ? (1.0f - (PxSqrt(bodyCore.maxLinearVelocitySq)/PxSqrt(vel.bottom.magnitudeSquared()))): 0.0f;
|
|
zExtForces.top = (m * (lindamp * dt - 1.0f)) * (g + extLinAccel);
|
|
zDamping.top = (m * (lindamp + linscale * invDt)) * vel.bottom;
|
|
}
|
|
{
|
|
const PxVec3 extAngAccel = linkExternalAccelsW ? linkExternalAccelsW[linkID].angular : PxVec3(PxZero);
|
|
const PxF32 angdamp = bodyCore.angularDamping > 0.f ? PxMin(bodyCore.angularDamping, invDt) : 0.0f;
|
|
const PxF32 angscale = (vel.top.magnitudeSquared() > bodyCore.maxAngularVelocitySq) ? (1.0f - (PxSqrt(bodyCore.maxAngularVelocitySq)/PxSqrt(vel.top.magnitudeSquared()))) : 0.0f;
|
|
zExtForces.bottom = Iw * (extAngAccel * (angdamp * dt - 1.0f));
|
|
zDamping.bottom = Iw * (vel.top * (angdamp + angscale * invDt));
|
|
}
|
|
|
|
if(externalForcesEveryTgsIterationEnabled)
|
|
{
|
|
// We precompute the isolated z vector for gravity and ext forces here so we don't need to load inertias in the per-substep application.
|
|
// The ugly thing is that we start storing spatial forces into the spatial motion vector linkExternalAccelsW.
|
|
// Reusing linkExternalAccelsW is ok because at the end of the sim step they are anyways cleared or refreshed from velMod in Sc::ArticulationSim::clearAcceleration
|
|
// and inverse dynamics does not use it.
|
|
|
|
linkZAExtForcesW[linkID] = zDamping; // only apply damping once for the full step
|
|
linkExternalAccelsW[linkID].linear = zExtForces.top; // must use the same order (linear<=>top) again when picking this up in the substep application
|
|
linkExternalAccelsW[linkID].angular = zExtForces.bottom;
|
|
}
|
|
else
|
|
{
|
|
linkZAExtForcesW[linkID] = zExtForces + zDamping;
|
|
}
|
|
|
|
//Account for forces arising from internal accelerations.
|
|
//Note: Mirtich thesis introduces a single spatial zero acceleration force that contains an external [mass*gravity] term and the internal [omega X (Iw *omega)] term.
|
|
//In Mirtich it looks like this:
|
|
// [-m_i * g ]
|
|
// [omega_i * (I_i * omega_i) ]
|
|
//We split the spatial zero acceleration force into external (above) and internal (below).
|
|
//The sum of the two (external and internal) corresponds to Z_i in the Mirtich formulation.
|
|
// In this function, we're only computing the isolated zero acceleration Z_i but already store
|
|
// it in the Z_i^A (articulated) fields as they will be accumulated later.
|
|
const Cm::SpatialVectorF zInternal(PxVec3(0.f), vel.top.cross(Iw*vel.top));
|
|
linkZAIntForcesW[linkID] = zInternal;
|
|
}
|
|
|
|
PxReal invMass = 1.f / sumMass;
|
|
comW = COM * invMass;
|
|
invSumMass = invMass;
|
|
}
|
|
|
|
//compute all links velocities
|
|
void FeatherstoneArticulation::computeLinkVelocities(ArticulationData& data,
|
|
ScratchData& scratchData)
|
|
{
|
|
Cm::SpatialVectorF* coriolisVectors = scratchData.coriolisVectors;
|
|
ArticulationLink* links = data.getLinks();
|
|
ArticulationLinkData* linkData = data.getLinkData();
|
|
const PxU32 linkCount = data.getLinkCount();
|
|
const bool fixBase = data.getArticulationFlags() & PxArticulationFlag::eFIX_BASE;
|
|
|
|
//motion velocities has to be in world space to avoid numerical errors caused by space
|
|
Cm::SpatialVectorF* motionVelocities = scratchData.motionVelocities;
|
|
Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations;
|
|
|
|
PxReal* jointVelocities = scratchData.jointVelocities;
|
|
|
|
const ArticulationLink& baseLink = links[0];
|
|
ArticulationLinkData& baseLinkDatum = linkData[0];
|
|
|
|
PxsBodyCore& core0 = *baseLink.bodyCore;
|
|
|
|
baseLinkDatum.maxPenBias = core0.maxPenBias;
|
|
|
|
if (fixBase)
|
|
{
|
|
motionVelocities[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f));
|
|
motionAccelerations[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f));
|
|
}
|
|
else
|
|
{
|
|
motionVelocities[0] = Cm::SpatialVectorF(core0.angularVelocity, core0.linearVelocity);
|
|
|
|
//PX_ASSERT(core0.angularVelocity.isFinite() && PxAbs(core0.angularVelocity.x) < 10000.f && PxAbs(core0.angularVelocity.y) < 10000.f && PxAbs(core0.angularVelocity.z) < 10000.f);
|
|
//PX_ASSERT(core0.linearVelocity.isFinite() && PxAbs(core0.linearVelocity.x) < 10000.f && PxAbs(core0.linearVelocity.y) < 10000.f && PxAbs(core0.linearVelocity.z) < 10000.f);
|
|
}
|
|
|
|
coriolisVectors[0] = Cm::SpatialVectorF::Zero();
|
|
|
|
data.mRootPreMotionVelocity = motionVelocities[0];
|
|
|
|
//Is it really necessary? It is already resolved as an internal cosntraint.
|
|
//const PxU32 dofCount = data.mDofs;
|
|
PxReal ratio = 1.f;
|
|
if (jointVelocities)
|
|
{
|
|
|
|
for (PxU32 linkID = 1; linkID < linkCount; ++linkID)
|
|
{
|
|
const ArticulationLink& link = links[linkID];
|
|
ArticulationJointCoreData& jointDatum = data.getJointData(linkID);
|
|
PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset];
|
|
for (PxU32 ind = 0; ind < jointDatum.nbDof; ++ind)
|
|
{
|
|
const PxReal maxJVelocity = link.inboundJoint->maxJointVelocity[ind];
|
|
PxReal absJvel = PxAbs(jVelocity[ind]);
|
|
ratio = ratio * absJvel > maxJVelocity ? (maxJVelocity / absJvel) : ratio;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (PxU32 linkID = 1; linkID < linkCount; ++linkID)
|
|
{
|
|
const ArticulationLink& link = links[linkID];
|
|
ArticulationLinkData& linkDatum = linkData[linkID];
|
|
const PxsBodyCore& bodyCore = *link.bodyCore;
|
|
|
|
linkDatum.maxPenBias = bodyCore.maxPenBias;
|
|
const Cm::SpatialVectorF pVel = motionVelocities[link.parent];
|
|
Cm::SpatialVectorF vel = FeatherstoneArticulation::translateSpatialVector(-mArticulationData.getRw(linkID), pVel);
|
|
const PxTransform& body2World = bodyCore.body2World;
|
|
|
|
if (jointVelocities)
|
|
{
|
|
PxVec3 torque = pVel.top.cross(pVel.top.cross(data.getRw(linkID)));
|
|
ArticulationJointCoreData& jointDatum = data.getJointData(linkID);
|
|
PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset];
|
|
|
|
PxVec3 force(0.f);
|
|
if (jointDatum.nbDof)
|
|
{
|
|
Cm::UnAlignedSpatialVector deltaV = Cm::UnAlignedSpatialVector::Zero();
|
|
for (PxU32 ind = 0; ind < jointDatum.nbDof; ++ind)
|
|
{
|
|
PxReal jVel = jVelocity[ind] * ratio;
|
|
//deltaV += data.mWorldMotionMatrix[jointDatum.jointOffset + ind] * jVel;
|
|
deltaV += data.mMotionMatrix[jointDatum.jointOffset+ind].rotate(body2World) * jVel;
|
|
jVelocity[ind] = jVel;
|
|
}
|
|
|
|
vel.top += deltaV.top;
|
|
vel.bottom += deltaV.bottom;
|
|
|
|
const PxVec3 aVec = deltaV.top;
|
|
force = pVel.top.cross(aVec);
|
|
|
|
//compute linear part
|
|
const PxVec3 lVel = deltaV.bottom;
|
|
|
|
const PxVec3 temp1 = 2.f * pVel.top.cross(lVel);
|
|
const PxVec3 temp2 = aVec.cross(lVel);
|
|
torque += temp1 + temp2;
|
|
|
|
}
|
|
coriolisVectors[linkID] = Cm::SpatialVectorF(force, torque);
|
|
}
|
|
|
|
//PX_ASSERT(vel.top.isFinite() && PxAbs(vel.top.x) < 10000.f && PxAbs(vel.top.y) < 10000.f && PxAbs(vel.top.z) < 10000.f);
|
|
//PX_ASSERT(vel.bottom.isFinite() && PxAbs(vel.bottom.x) < 10000.f && PxAbs(vel.bottom.y) < 10000.f && PxAbs(vel.bottom.z) < 10000.f);
|
|
|
|
motionVelocities[linkID] = vel;
|
|
}
|
|
}
|
|
|
|
void solveExtContact(const PxSolverConstraintDesc& desc, Vec3V& linVel0, Vec3V& linVel1, Vec3V& angVel0, Vec3V& angVel1,
|
|
Vec3V& linImpulse0, Vec3V& linImpulse1, Vec3V& angImpulse0, Vec3V& angImpulse1, bool doFriction, Dy::ErrorAccumulator* contactErrorAccumulator);
|
|
|
|
void solveExt1D(const PxSolverConstraintDesc& desc, Vec3V& linVel0, Vec3V& linVel1, Vec3V& angVel0, Vec3V& angVel1,
|
|
Vec3V& li0, Vec3V& li1, Vec3V& ai0, Vec3V& ai1, bool isPositionIteration);
|
|
|
|
void solveExt1D(const PxSolverConstraintDesc& desc, Vec3V& linVel0, Vec3V& linVel1, Vec3V& angVel0, Vec3V& angVel1,
|
|
const Vec3V& linMotion0, const Vec3V& linMotion1, const Vec3V& angMotion0, const Vec3V& angMotion1,
|
|
const QuatV& rotA, const QuatV& rotB, const PxReal elapsedTimeF32, Vec3V& linImpulse0, Vec3V& linImpulse1, Vec3V& angImpulse0,
|
|
Vec3V& angImpulse1, bool isPositionIteration);
|
|
|
|
void solveExtContactStep(const PxSolverConstraintDesc& desc, Vec3V& linVel0, Vec3V& linVel1, Vec3V& angVel0, Vec3V& angVel1,
|
|
Vec3V& linDelta0, Vec3V& linDelta1, Vec3V& angDelta0, Vec3V& angDelta1, Vec3V& linImpulse0, Vec3V& linImpulse1, Vec3V& angImpulse0, Vec3V& angImpulse1,
|
|
bool doFriction, const PxReal minPenetration, const PxReal elapsedTimeF32, Dy::ErrorAccumulator* contactErrorAccumulator);
|
|
|
|
void solveStaticConstraint(const PxSolverConstraintDesc& desc, Cm::SpatialVectorF& linkV,
|
|
Cm::SpatialVectorF& impulse, Cm::SpatialVectorF& deltaV, const Cm::SpatialVectorF& motion,
|
|
const PxQuat& rot, bool isTGS, PxReal elapsedTime, const PxReal minPenetration, Dy::ErrorAccumulator* contactErrorAccumulator, bool isPositionIteration)
|
|
{
|
|
Vec3V linVel = V3LoadA(linkV.bottom);
|
|
Vec3V angVel = V3LoadA(linkV.top);
|
|
|
|
Vec3V linVel0, linVel1, angVel0, angVel1;
|
|
Vec3V li0 = V3Zero(), li1 = V3Zero(), ai0 = V3Zero(), ai1 = V3Zero();
|
|
|
|
if (isTGS)
|
|
{
|
|
PxQuat idt(PxIdentity);
|
|
Vec3V linMotion0, angMotion0, linMotion1, angMotion1;
|
|
QuatV rotA, rotB;
|
|
if (desc.linkIndexA != PxSolverConstraintDesc::RIGID_BODY)
|
|
{
|
|
linVel0 = linVel;
|
|
angVel0 = angVel;
|
|
linMotion0 = V3LoadA(motion.bottom);
|
|
angMotion0 = V3LoadA(motion.top);
|
|
rotA = QuatVLoadU(&rot.x);
|
|
rotB = QuatVLoadU(&idt.x);
|
|
linVel1 = angVel1 = linMotion1 = angMotion1 = V3Zero();
|
|
}
|
|
else
|
|
{
|
|
linVel1 = linVel;
|
|
angVel1 = angVel;
|
|
linMotion1 = V3LoadA(motion.bottom);
|
|
angMotion1 = V3LoadA(motion.top);
|
|
rotB = QuatVLoadU(&rot.x);
|
|
rotA = QuatVLoadU(&idt.x);
|
|
linVel0 = angVel0 = linMotion0 = angMotion0 = V3Zero();
|
|
}
|
|
|
|
if (*desc.constraint == DY_SC_TYPE_EXT_CONTACT)
|
|
{
|
|
Dy::solveExtContactStep(desc,
|
|
linVel0, linVel1, angVel0, angVel1,
|
|
linMotion0, linMotion1, angMotion0, angMotion1,
|
|
li0, li1, ai0, ai1,
|
|
true, minPenetration, elapsedTime, contactErrorAccumulator);
|
|
}
|
|
else
|
|
{
|
|
Dy::solveExt1D(desc,
|
|
linVel0, linVel1, angVel0, angVel1,
|
|
linMotion0, linMotion1, angMotion0, angMotion1,
|
|
rotA, rotB, elapsedTime,
|
|
li0, li1, ai0, ai1, isPositionIteration);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (desc.linkIndexA != PxSolverConstraintDesc::RIGID_BODY)
|
|
{
|
|
linVel0 = linVel;
|
|
angVel0 = angVel;
|
|
linVel1 = angVel1 = V3Zero();
|
|
}
|
|
else
|
|
{
|
|
linVel1 = linVel;
|
|
angVel1 = angVel;
|
|
linVel0 = angVel0 = V3Zero();
|
|
}
|
|
|
|
if (*desc.constraint == DY_SC_TYPE_EXT_CONTACT)
|
|
{
|
|
Dy::solveExtContact(desc, linVel0, linVel1, angVel0, angVel1, li0, li1, ai0, ai1, true, contactErrorAccumulator);
|
|
}
|
|
else
|
|
{
|
|
Dy::solveExt1D(desc, linVel0, linVel1, angVel0, angVel1, li0, li1, ai0, ai1, isPositionIteration);
|
|
}
|
|
}
|
|
|
|
Cm::SpatialVectorF newVel, newImp;
|
|
|
|
if (desc.linkIndexA != PxSolverConstraintDesc::RIGID_BODY)
|
|
{
|
|
V4StoreA(Vec4V_From_Vec3V(linVel0), &newVel.bottom.x);
|
|
V4StoreA(Vec4V_From_Vec3V(angVel0), &newVel.top.x);
|
|
|
|
V4StoreA(Vec4V_From_Vec3V(li0), &newImp.top.x);
|
|
V4StoreA(Vec4V_From_Vec3V(ai0), &newImp.bottom.x);
|
|
}
|
|
else
|
|
{
|
|
V4StoreA(Vec4V_From_Vec3V(linVel1), &newVel.bottom.x);
|
|
V4StoreA(Vec4V_From_Vec3V(angVel1), &newVel.top.x);
|
|
|
|
V4StoreA(Vec4V_From_Vec3V(li1), &newImp.top.x);
|
|
V4StoreA(Vec4V_From_Vec3V(ai1), &newImp.bottom.x);
|
|
}
|
|
|
|
deltaV.top += (newVel.top - linkV.top);
|
|
deltaV.bottom += (newVel.bottom - linkV.bottom);
|
|
linkV.top = newVel.top;
|
|
linkV.bottom = newVel.bottom;
|
|
impulse -= newImp;
|
|
}
|
|
|
|
// Forward declarations
|
|
void writeBackContact(const PxSolverConstraintDesc& desc, SolverContext& cache,
|
|
PxSolverBodyData& bd0, PxSolverBodyData& bd1);
|
|
void writeBackContact(const PxSolverConstraintDesc& desc, SolverContext* cache);
|
|
void writeBack1D(const PxSolverConstraintDesc& desc);
|
|
void writeBack1DStep(const PxSolverConstraintDesc& desc);
|
|
|
|
void FeatherstoneArticulation::writebackInternalConstraints(bool isTGS)
|
|
{
|
|
SolverContext context;
|
|
PxSolverBodyData data;
|
|
|
|
for (PxU32 i = 0; i < mStatic1DConstraints.size(); ++i)
|
|
{
|
|
PxSolverConstraintDesc& desc = mStatic1DConstraints[i];
|
|
|
|
PX_ASSERT(*desc.constraint == DY_SC_TYPE_EXT_1D);
|
|
|
|
if (isTGS)
|
|
{
|
|
writeBack1DStep(desc);
|
|
}
|
|
else
|
|
{
|
|
writeBack1D(desc);
|
|
}
|
|
}
|
|
|
|
for (PxU32 i = 0; i < mStaticContactConstraints.size(); ++i)
|
|
{
|
|
PxSolverConstraintDesc& desc = mStaticContactConstraints[i];
|
|
|
|
PX_ASSERT(*desc.constraint == DY_SC_TYPE_EXT_CONTACT);
|
|
if (isTGS)
|
|
{
|
|
writeBackContact(static_cast<PxSolverConstraintDesc&>(desc), NULL);
|
|
}
|
|
else
|
|
{
|
|
writeBackContact(desc, context, data, data);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Forward declarations
|
|
void concludeContact(const PxSolverConstraintDesc& desc);
|
|
void concludeContactStep(const PxSolverConstraintDesc& desc);
|
|
void conclude1D(const PxSolverConstraintDesc& desc);
|
|
void conclude1DStep(const PxSolverConstraintDesc& desc);
|
|
|
|
void FeatherstoneArticulation::concludeInternalConstraints(bool isTGS)
|
|
{
|
|
for (PxU32 i = 0; i < mStatic1DConstraints.size(); ++i)
|
|
{
|
|
PxSolverConstraintDesc& desc = mStatic1DConstraints[i];
|
|
|
|
PX_ASSERT(*desc.constraint == DY_SC_TYPE_EXT_1D);
|
|
if (isTGS)
|
|
{
|
|
conclude1DStep(desc);
|
|
}
|
|
else
|
|
{
|
|
conclude1D(desc);
|
|
}
|
|
}
|
|
|
|
for (PxU32 i = 0; i < mStaticContactConstraints.size(); ++i)
|
|
{
|
|
PxSolverConstraintDesc& desc = mStaticContactConstraints[i];
|
|
PX_ASSERT(*desc.constraint == DY_SC_TYPE_EXT_CONTACT);
|
|
|
|
if (isTGS)
|
|
{
|
|
concludeContactStep(desc);
|
|
}
|
|
else
|
|
{
|
|
concludeContact(desc);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct InternalConstraintSolverData
|
|
{
|
|
PxReal dt;
|
|
PxReal stepDt;
|
|
PxReal invStepDt;
|
|
PxReal elapsedTime;
|
|
PxReal erp;
|
|
bool isTGS;
|
|
bool isVelIter;
|
|
bool isResidualReportingActive;
|
|
bool isExternalForceEveryStep;
|
|
|
|
InternalConstraintSolverData(const PxReal dt_, const PxReal stepDt_, const PxReal invStepDt_, const PxReal elapsedTime_, const PxReal erp_,
|
|
const bool isTGS_, const bool isVelIter_, const bool isResidualReportingActive_,
|
|
const bool isExternalForcesEveryTgsIterationEnabled_)
|
|
: dt(dt_)
|
|
, stepDt(stepDt_)
|
|
, invStepDt(invStepDt_)
|
|
, elapsedTime(elapsedTime_)
|
|
, erp(erp_)
|
|
, isTGS(isTGS_)
|
|
, isVelIter(isVelIter_)
|
|
, isResidualReportingActive(isResidualReportingActive_)
|
|
, isExternalForceEveryStep(isExternalForcesEveryTgsIterationEnabled_)
|
|
{
|
|
}
|
|
|
|
PX_NOCOPY(InternalConstraintSolverData)
|
|
};
|
|
|
|
Cm::SpatialVectorF FeatherstoneArticulation::solveInternalJointConstraintRecursive(const InternalConstraintSolverData& data, const PxU32 linkID,
|
|
const Cm::SpatialVectorF& parentDeltaV, PxU32& dofId, PxU32& limitId)
|
|
{
|
|
//PxU32 linkID = stack[stackSize];
|
|
const ArticulationLink* links = mArticulationData.mLinks;
|
|
const ArticulationLink& link = links[linkID];
|
|
|
|
|
|
const ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID);
|
|
|
|
Cm::SpatialVectorF i1(PxVec3(0.f), PxVec3(0.f));
|
|
|
|
//We know the absolute parentDeltaV from the call to this function so no need to modify it.
|
|
Cm::SpatialVectorF parentV = parentDeltaV + mArticulationData.mMotionVelocities[link.parent];
|
|
|
|
Cm::SpatialVectorF parentVelContrib = propagateAccelerationW(
|
|
mArticulationData.getRw(linkID), parentDeltaV,
|
|
mArticulationData.mInvStIs[linkID], &mArticulationData.mWorldMotionMatrix[jointDatum.jointOffset],
|
|
&mArticulationData.mIsW[jointDatum.jointOffset], &mArticulationData.mDeferredQstZ[jointDatum.jointOffset], jointDatum.nbDof,
|
|
NULL);
|
|
|
|
Cm::SpatialVectorF childV = mArticulationData.mMotionVelocities[linkID] + parentVelContrib;
|
|
|
|
Cm::UnAlignedSpatialVector i0(PxVec3(0.f), PxVec3(0.f));
|
|
|
|
Cm::SpatialVectorF dv1 = parentVelContrib;
|
|
|
|
//Process internal constraints (parent/child limits/locks/drives)
|
|
for (PxU32 dof = 0; dof < jointDatum.nbDof; ++dof)
|
|
{
|
|
if (mArticulationData.mInternalConstraints.size() <= dofId)
|
|
continue;
|
|
|
|
ArticulationInternalConstraint& constraint = mArticulationData.mInternalConstraints[dofId++];
|
|
const PxReal jointPDelta = constraint.row1.innerProduct(mArticulationData.mDeltaMotionVector[linkID]) - constraint.row0.innerProduct(mArticulationData.mDeltaMotionVector[link.parent]);
|
|
|
|
// This jointV is just used to compute velocity-dependent forces (friction, limits, ...). It is not stored to the articulation joint velocities.
|
|
PxReal jointV = constraint.row1.innerProduct(childV) - constraint.row0.innerProduct(parentV);
|
|
|
|
PxReal frictionDeltaF = 0.0f;
|
|
|
|
bool newFrictionModel = constraint.staticFrictionEffort != 0.0f || constraint.viscousFrictionCoefficient != 0.0f;
|
|
const bool isPerStep = (data.isTGS && data.isExternalForceEveryStep && !(data.isVelIter));
|
|
const PxReal effectiveTimestep = isPerStep ? data.stepDt : data.dt;
|
|
|
|
// deprecated friction model
|
|
if (!newFrictionModel)
|
|
{
|
|
// Friction force is accumulated through all position iterations only for PGS
|
|
const PxReal appliedFriction = data.isTGS ? 0.0f : constraint.accumulatedFrictionImpulse;
|
|
const PxReal frictionForce = PxClamp(-jointV * constraint.recipResponse + appliedFriction,
|
|
-constraint.frictionMaxForce, constraint.frictionMaxForce);
|
|
constraint.accumulatedFrictionImpulse = frictionForce; // This is not used for TGS
|
|
|
|
frictionDeltaF = frictionForce - appliedFriction;
|
|
|
|
jointV += frictionDeltaF * constraint.response;
|
|
}
|
|
|
|
PxReal driveDeltaF = 0.0f;
|
|
|
|
if (constraint.envelope.maxEffort > 0.0f)
|
|
{
|
|
const PxReal appliedDriveImpulse = isPerStep ? 0.0f : constraint.driveImpulse;
|
|
const PxReal maxImpulse = constraint.envelope.maxEffort * effectiveTimestep;
|
|
const PxReal speedImpulseGradient = constraint.envelope.speedEffortGradient / effectiveTimestep;
|
|
const PxReal velocityDependentResistance = constraint.envelope.velocityDependentResistance * effectiveTimestep;
|
|
const PxReal externalJointImpulse = constraint.externalJointForce * effectiveTimestep;
|
|
|
|
const PxReal unclampedImpulse = (data.isTGS && data.isVelIter)
|
|
? appliedDriveImpulse
|
|
: computeDriveImpulse(appliedDriveImpulse, jointV, jointPDelta, data.elapsedTime, constraint.getImplicitDriveDesc());
|
|
|
|
const PxReal clampedImpulse = clampDriveImpulse(
|
|
jointV,
|
|
appliedDriveImpulse + externalJointImpulse,
|
|
unclampedImpulse + externalJointImpulse,
|
|
constraint.response,
|
|
constraint.envelope.maxActuatorVelocity,
|
|
maxImpulse,
|
|
speedImpulseGradient,
|
|
velocityDependentResistance
|
|
) - externalJointImpulse;
|
|
|
|
driveDeltaF = clampedImpulse - appliedDriveImpulse;
|
|
constraint.driveImpulse += driveDeltaF;
|
|
}
|
|
|
|
else
|
|
{
|
|
const PxReal unclampedImpulse = (data.isTGS && data.isVelIter) ? constraint.driveImpulse :
|
|
computeDriveImpulse(constraint.driveImpulse, jointV, jointPDelta, data.elapsedTime, constraint.getImplicitDriveDesc());
|
|
const PxReal clampedImpulse = PxClamp(unclampedImpulse, -constraint.driveMaxImpulse, constraint.driveMaxImpulse);
|
|
driveDeltaF = (clampedImpulse - constraint.driveImpulse);
|
|
constraint.driveImpulse = clampedImpulse;
|
|
}
|
|
jointV += driveDeltaF * constraint.response;
|
|
|
|
if (newFrictionModel)
|
|
{
|
|
const PxReal staticFrictionImpulse = constraint.staticFrictionEffort * effectiveTimestep;
|
|
const PxReal dynamicFrictionImpulse = constraint.dynamicFrictionEffort * effectiveTimestep;
|
|
const PxReal viscousFrictionCoefficient = constraint.viscousFrictionCoefficient * effectiveTimestep;
|
|
const PxReal accumulatedFrictionImpulse = isPerStep ? 0.0f : constraint.accumulatedFrictionImpulse;
|
|
|
|
const PxReal totalFrictionImpulse = computeFrictionImpulse(
|
|
accumulatedFrictionImpulse - jointV * constraint.recipResponse,
|
|
staticFrictionImpulse,
|
|
dynamicFrictionImpulse,
|
|
viscousFrictionCoefficient,
|
|
jointV);
|
|
|
|
frictionDeltaF = totalFrictionImpulse - accumulatedFrictionImpulse;
|
|
constraint.accumulatedFrictionImpulse += frictionDeltaF; // to keep track of accumulated impulse for velIter in TGS with isExternalForceEveryStep
|
|
jointV += frictionDeltaF * constraint.response;
|
|
}
|
|
|
|
|
|
//Where we will be next frame - we use this to compute error bias terms to correct limits and drives...
|
|
|
|
//printf("LinkID %i driveDeltaV = %f, jointV = %f\n", linkID, driveDeltaF, jointV);
|
|
|
|
PxReal posLimitDeltaF = 0.0f;
|
|
if (jointDatum.dofLimitMask & (1 << dof))
|
|
{
|
|
ArticulationInternalLimit& limit = mArticulationData.mInternalLimits[limitId++];
|
|
posLimitDeltaF = computeLimitImpulse(
|
|
data.stepDt, data.invStepDt, data.isVelIter,
|
|
constraint.response, constraint.recipResponse, data.erp,
|
|
limit.errorLow, limit.errorHigh,
|
|
jointPDelta,
|
|
limit.lowImpulse, limit.highImpulse, jointV);
|
|
}
|
|
|
|
PxReal velLimitDeltaF = 0.0f;
|
|
const PxReal maxJointVel =constraint.maxJointVelocity;
|
|
if (PxAbs(jointV) > maxJointVel)
|
|
{
|
|
PxReal newJointV = PxClamp(jointV, -maxJointVel, maxJointVel);
|
|
velLimitDeltaF = (newJointV - jointV) * constraint.recipResponse;
|
|
jointV = newJointV;
|
|
}
|
|
|
|
const PxReal deltaF = frictionDeltaF + driveDeltaF + posLimitDeltaF + velLimitDeltaF;
|
|
|
|
//Accumulate error even if it is zero because the increment of the counter affects the RMS value
|
|
if (data.isResidualReportingActive)
|
|
(data.isVelIter ? mInternalErrorAccumulatorVelIter : mInternalErrorAccumulatorPosIter).accumulateErrorLocal(deltaF, constraint.recipResponse);
|
|
|
|
if (deltaF != 0.f)
|
|
{
|
|
//impulse = true;
|
|
|
|
i0 += constraint.row0 * deltaF;
|
|
i1.top -= constraint.row1.top * deltaF;
|
|
i1.bottom -= constraint.row1.bottom * deltaF;
|
|
|
|
const Cm::UnAlignedSpatialVector deltaVP = constraint.deltaVA * (-deltaF);
|
|
const Cm::UnAlignedSpatialVector deltaVC = constraint.deltaVB * (-deltaF);
|
|
|
|
parentV += Cm::SpatialVectorF(deltaVP.top, deltaVP.bottom);
|
|
childV += Cm::SpatialVectorF(deltaVC.top, deltaVC.bottom);
|
|
|
|
dv1.top += deltaVC.top;
|
|
dv1.bottom += deltaVC.bottom;
|
|
}
|
|
}
|
|
|
|
//Cache the impulse arising from internal constraints.
|
|
//We'll subtract this from the total impulse applied later in this function.
|
|
const Cm::SpatialVectorF i1Internal = i1;
|
|
|
|
const Cm::SpatialVectorF& deltaMotion = mArticulationData.getDeltaMotionVector(linkID);
|
|
const PxQuat& deltaQ = getDeltaQ(linkID);
|
|
|
|
const PxU32 nbStatic1DConstraints = mArticulationData.mNbStatic1DConstraints[linkID];
|
|
PxU32 start1DIdx = mArticulationData.mStatic1DConstraintStartIndex[linkID];
|
|
for (PxU32 i = 0; i < nbStatic1DConstraints; ++i)
|
|
{
|
|
PxSolverConstraintDesc& desc = mStatic1DConstraints[start1DIdx++];
|
|
solveStaticConstraint(
|
|
desc, childV, i1, dv1, deltaMotion, deltaQ, data.isTGS, data.elapsedTime, data.isVelIter ? 0.f : -PX_MAX_F32,
|
|
data.isVelIter ? &mContactErrorAccumulatorVelIter : &mContactErrorAccumulatorPosIter, !data.isVelIter);
|
|
}
|
|
|
|
const PxU32 nbStaticContactConstraints = mArticulationData.mNbStaticContactConstraints[linkID];
|
|
PxU32 startContactIdx = mArticulationData.mStaticContactConstraintStartIndex[linkID];
|
|
for (PxU32 i = 0; i < nbStaticContactConstraints; ++i)
|
|
{
|
|
PxSolverConstraintDesc& desc = mStaticContactConstraints[startContactIdx++];
|
|
solveStaticConstraint(
|
|
desc, childV, i1, dv1, deltaMotion, deltaQ, data.isTGS, data.elapsedTime, data.isVelIter ? 0.f : -PX_MAX_F32,
|
|
data.isVelIter ? &mContactErrorAccumulatorVelIter : &mContactErrorAccumulatorPosIter, !data.isVelIter);
|
|
}
|
|
|
|
PxU32 numChildren = link.mNumChildren;
|
|
PxU32 offset = link.mChildrenStartIndex;
|
|
|
|
for(PxU32 i = 0; i < numChildren; ++i)
|
|
{
|
|
const PxU32 child = offset+i;
|
|
|
|
Cm::SpatialVectorF childImp = solveInternalJointConstraintRecursive(data, child, dv1, dofId, limitId);
|
|
i1 += childImp;
|
|
|
|
if ((numChildren-i) > 1)
|
|
{
|
|
//Propagate the childImp to my dv1 so that the next constraint gets to see an updated velocity state based
|
|
//on the propagation of the child velocities
|
|
Cm::SpatialVectorF deltaV = mArticulationData.mResponseMatrixW[linkID].getLinkDeltaVImpulseResponse(-childImp);
|
|
dv1 += deltaV;
|
|
childV += deltaV;
|
|
|
|
}
|
|
}
|
|
|
|
Cm::SpatialVectorF propagatedImpulseAtParentW;
|
|
{
|
|
//const inputs
|
|
const PxVec3& r = mArticulationData.getRw(linkID);
|
|
const Cm::SpatialVectorF* jointDofISInvStISW = &mArticulationData.mISInvStIS[jointDatum.jointOffset];
|
|
const Cm::UnAlignedSpatialVector* jointDofMotionMatrixW = &mArticulationData.mWorldMotionMatrix[jointDatum.jointOffset];
|
|
const PxU8 nbDofs = jointDatum.nbDof;
|
|
|
|
//output
|
|
PxReal* jointDofDeferredQstZ = &mArticulationData.mDeferredQstZ[jointDatum.jointOffset];
|
|
propagatedImpulseAtParentW = propagateImpulseW(
|
|
r,
|
|
i1,
|
|
NULL, jointDofISInvStISW, jointDofMotionMatrixW, nbDofs,
|
|
jointDofDeferredQstZ);
|
|
}
|
|
|
|
//Accumulate the propagated impulse at the link.
|
|
//Don't forget to subtract the impulse arising from internal constraints.
|
|
//This can be used to compute the link's incoming joint force.
|
|
mArticulationData.mSolverLinkSpatialImpulses[linkID] += (i1 - i1Internal);
|
|
|
|
return Cm::SpatialVectorF(i0.top, i0.bottom) + propagatedImpulseAtParentW;
|
|
}
|
|
|
|
void FeatherstoneArticulation::solveInternalJointConstraints(const PxReal dt, const PxReal stepDt, const PxReal invStepDt, bool isVelIter, bool isTGS,
|
|
const PxReal elapsedTime, const PxReal biasCoefficient,
|
|
bool residualReportingActive, bool isExternalForcesEveryTgsIterationEnabled)
|
|
{
|
|
//const PxU32 count = mArticulationData.getLinkCount();
|
|
|
|
//const PxReal erp = isTGS ? 0.5f*biasCoefficient : biasCoefficient;
|
|
const PxReal erp = biasCoefficient;
|
|
|
|
const bool fixBase = mArticulationData.getArticulationFlags() & PxArticulationFlag::eFIX_BASE;
|
|
|
|
PxU32* static1DConstraintCounts = mArticulationData.mNbStatic1DConstraints.begin();
|
|
PxU32* static1DConstraintStarts = mArticulationData.mStatic1DConstraintStartIndex.begin();
|
|
|
|
PxU32* staticContactConstraintCounts = mArticulationData.mNbStaticContactConstraints.begin();
|
|
PxU32* staticContactConstraintStarts = mArticulationData.mStaticContactConstraintStartIndex.begin();
|
|
|
|
ArticulationLink* links = mArticulationData.getLinks();
|
|
Cm::SpatialVectorF* baseVelocities = mArticulationData.getMotionVelocities();
|
|
//PxTransform* transforms = mArticulationData.mPreTransform.begin();
|
|
|
|
//Cm::SpatialVectorF* deferredZ = mArticulationData.getSpatialZAVectors();
|
|
|
|
const PxReal minPenetration = isVelIter ? 0.f : -PX_MAX_F32;
|
|
|
|
Cm::SpatialVectorF rootLinkV;
|
|
{
|
|
Cm::SpatialVectorF rootLinkDeltaV(PxVec3(0.f), PxVec3(0.f));
|
|
|
|
if (!fixBase)
|
|
{
|
|
//Cm::SpatialVectorF temp = mArticulationData.getBaseInvSpatialArticulatedInertia() * (-deferredZ[0]);
|
|
//const PxTransform& body2World0 = transforms[0];
|
|
//rootLinkDeltaV = temp.rotate(body2World0);
|
|
//temp is now in world space!
|
|
rootLinkDeltaV = mArticulationData.getBaseInvSpatialArticulatedInertiaW() * -mArticulationData.mRootDeferredZ;
|
|
}
|
|
|
|
rootLinkV = rootLinkDeltaV + baseVelocities[0];
|
|
|
|
Cm::SpatialVectorF im0 = Cm::SpatialVectorF::Zero();
|
|
{
|
|
const PxU32 nbStatic1DConstraints = static1DConstraintCounts[0];
|
|
|
|
if (nbStatic1DConstraints)
|
|
{
|
|
const Cm::SpatialVectorF& deltaMotion = mArticulationData.getDeltaMotionVector(0);
|
|
const PxQuat& deltaQ = getDeltaQ(0);
|
|
|
|
PxU32 startIdx = static1DConstraintStarts[0];
|
|
for (PxU32 i = 0; i < nbStatic1DConstraints; ++i)
|
|
{
|
|
PxSolverConstraintDesc& desc = mStatic1DConstraints[startIdx++];
|
|
|
|
solveStaticConstraint(desc, rootLinkV, im0, rootLinkDeltaV, deltaMotion, deltaQ, isTGS, elapsedTime, minPenetration,
|
|
isVelIter ? &mContactErrorAccumulatorVelIter : &mContactErrorAccumulatorPosIter, !isVelIter);
|
|
}
|
|
|
|
//Impulses and deferredZ are now in world space, not link space!
|
|
/*im0.top = transforms[0].rotateInv(im0.top);
|
|
im0.bottom = transforms[0].rotateInv(im0.bottom);*/
|
|
}
|
|
|
|
const PxU32 nbStaticContactConstraints = staticContactConstraintCounts[0];
|
|
|
|
if (nbStaticContactConstraints)
|
|
{
|
|
const Cm::SpatialVectorF& deltaMotion = mArticulationData.getDeltaMotionVector(0);
|
|
const PxQuat& deltaQ = getDeltaQ(0);
|
|
|
|
PxU32 startIdx = staticContactConstraintStarts[0];
|
|
for (PxU32 i = 0; i < nbStaticContactConstraints; ++i)
|
|
{
|
|
PxSolverConstraintDesc& desc = mStaticContactConstraints[startIdx++];
|
|
|
|
solveStaticConstraint(desc, rootLinkV, im0, rootLinkDeltaV, deltaMotion, deltaQ, isTGS, elapsedTime, minPenetration,
|
|
isVelIter ? &mContactErrorAccumulatorVelIter : &mContactErrorAccumulatorPosIter, !isVelIter);
|
|
}
|
|
|
|
//Impulses and deferredZ are now in world space, not link space!
|
|
/*im0.top = transforms[0].rotateInv(im0.top);
|
|
im0.bottom = transforms[0].rotateInv(im0.bottom);*/
|
|
}
|
|
}
|
|
|
|
//Store the constant that will be used by every dof and limit encountered.
|
|
const InternalConstraintSolverData data(dt, stepDt, invStepDt, elapsedTime, erp, isTGS, isVelIter, residualReportingActive, isExternalForcesEveryTgsIterationEnabled);
|
|
|
|
//Increment dofId and limitId as each is encountered to make sure we stay in sync with the ordering of
|
|
//ArticulationData::mInternalConstraints and ArticulationData::mInternalLimits.
|
|
PxU32 dofId = 0;
|
|
PxU32 limitId = 0;
|
|
|
|
const PxU32 numChildren = links[0].mNumChildren;
|
|
const PxU32 offset = links[0].mChildrenStartIndex;
|
|
|
|
for (PxU32 i = 0; i < numChildren; ++i)
|
|
{
|
|
const PxU32 child = offset + i;
|
|
|
|
Cm::SpatialVectorF imp = solveInternalJointConstraintRecursive(data, child, rootLinkDeltaV, dofId, limitId);
|
|
|
|
im0 += imp;
|
|
|
|
//There's an impulse, we have to work out how it impacts our velocity (only if required (we have more children to traverse))!
|
|
if (!fixBase && (numChildren - 1) != 0)
|
|
{
|
|
//Impulses and deltaVs are all now in world space
|
|
rootLinkDeltaV += mArticulationData.getBaseInvSpatialArticulatedInertiaW() * (-imp);
|
|
}
|
|
}
|
|
|
|
mArticulationData.mRootDeferredZ += im0;
|
|
mArticulationData.mJointDirty = true;
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::solveInternalSpatialTendonConstraints(bool isTGS)
|
|
{
|
|
if (mArticulationData.mInternalSpatialTendonConstraints.size() == 0)
|
|
return;
|
|
|
|
if (isTGS)
|
|
{
|
|
//Update the error terms in the tendons recursively...
|
|
const PxU32 nbTendons = mArticulationData.mNumSpatialTendons;
|
|
|
|
for (PxU32 i = 0; i < nbTendons; ++i)
|
|
{
|
|
Dy::ArticulationSpatialTendon* tendon = mArticulationData.mSpatialTendons[i];
|
|
|
|
Dy::ArticulationAttachment& attachment = tendon->getAttachment(0);
|
|
|
|
//const PxU32 childCount = attachment.childCount;
|
|
|
|
//PxReal scale = 1.f / PxReal(childCount);
|
|
|
|
PxReal coefficient = attachment.coefficient;
|
|
|
|
const PxU32 startLink = tendon->getAttachment(0).linkInd;
|
|
const PxTransform pBody2World = mArticulationData.getAccumulatedPoses()[startLink];
|
|
const PxVec3 pAttachPoint = pBody2World.transform(tendon->getAttachment(0).relativeOffset);
|
|
|
|
Dy::ArticulationAttachment* attachments = tendon->getAttachments();
|
|
for (ArticulationAttachmentBitField children = attachments[0].children; children != 0; children &= (children - 1))
|
|
{
|
|
//index of child of link h on path to link linkID
|
|
const PxU32 child = PxLowestSetBit(children);
|
|
|
|
updateSpatialTendonConstraintsRecursive(attachments, mArticulationData, child, tendon->mOffset*coefficient, pAttachPoint);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (PxU32 i = 0; i < mArticulationData.mInternalSpatialTendonConstraints.size(); ++i)
|
|
{
|
|
ArticulationInternalTendonConstraint& constraint = mArticulationData.mInternalSpatialTendonConstraints[i];
|
|
|
|
const PxU32 parentID = constraint.linkID0;
|
|
const PxU32 linkID = constraint.linkID1;
|
|
|
|
//Cm::SpatialVectorF childDeltaP = mArticulationData.mDeltaMotionVector[linkID];
|
|
//Cm::SpatialVectorF parentDeltaP = mArticulationData.mDeltaMotionVector[parentID];
|
|
|
|
//PxReal deltaP = constraint.row1.innerProduct(childDeltaP) - constraint.row0.innerProduct(parentDeltaP);// + deltaErr;
|
|
|
|
Cm::SpatialVectorV childVel = pxcFsGetVelocity(linkID);
|
|
Cm::SpatialVectorV parentVel = pxcFsGetVelocity(parentID);
|
|
|
|
Cm::UnAlignedSpatialVector childV;
|
|
V3StoreU(childVel.angular, childV.top);
|
|
V3StoreU(childVel.linear, childV.bottom);
|
|
|
|
Cm::UnAlignedSpatialVector parentV;
|
|
V3StoreU(parentVel.angular, parentV.top);
|
|
V3StoreU(parentVel.linear, parentV.bottom);
|
|
|
|
PxReal error = constraint.restDistance - constraint.accumulatedLength;// + deltaP;
|
|
|
|
PxReal error2 = 0.f;
|
|
if (constraint.accumulatedLength > constraint.highLimit)
|
|
error2 = constraint.highLimit - constraint.accumulatedLength;
|
|
if (constraint.accumulatedLength < constraint.lowLimit)
|
|
error2 = constraint.lowLimit - constraint.accumulatedLength;
|
|
|
|
PxReal jointV = constraint.row1.innerProduct(childV) - constraint.row0.innerProduct(parentV);
|
|
|
|
PX_ASSERT(PxIsFinite(jointV));
|
|
|
|
PxReal unclampedForce = (jointV * constraint.velMultiplier + error * constraint.biasCoefficient) /** constraint.recipResponse*/
|
|
+ constraint.appliedForce * constraint.impulseMultiplier;
|
|
|
|
PxReal unclampedForce2 = (error2 * constraint.limitBiasCoefficient) + constraint.limitAppliedForce * constraint.limitImpulseMultiplier;
|
|
|
|
const PxReal deltaF = (unclampedForce - constraint.appliedForce) + (unclampedForce2 - constraint.limitAppliedForce);
|
|
|
|
constraint.appliedForce = unclampedForce;
|
|
constraint.limitAppliedForce = unclampedForce2;
|
|
|
|
//Accumulate error even if it is zero because the increment of the counter affects the RMS value
|
|
//Ignore tendons for now
|
|
//(isVelIter ? mInternalErrorAccumulatorVelIter : mInternalErrorAccumulatorPosIter).accumulateErrorLocal(deltaF, constraint.recipResponse);
|
|
|
|
if (deltaF != 0.f)
|
|
{
|
|
Cm::UnAlignedSpatialVector i0 = constraint.row0 * -deltaF;
|
|
Cm::UnAlignedSpatialVector i1 = constraint.row1 * deltaF;
|
|
pxcFsApplyImpulses(
|
|
parentID, V3LoadU(i0.top), V3LoadU(i0.bottom), NULL,
|
|
linkID, V3LoadU(i1.top), V3LoadU(i1.bottom), NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
PxVec3 FeatherstoneArticulation::calculateFixedTendonVelocityAndPositionRecursive(FixedTendonSolveData& solveData,
|
|
const Cm::SpatialVectorF& parentV, const Cm::SpatialVectorF& parentDeltaV, const PxU32 tendonJointID)
|
|
{
|
|
ArticulationTendonJoint& tendonJoint = solveData.tendonJoints[tendonJointID];
|
|
|
|
ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(tendonJoint.linkInd);
|
|
|
|
Cm::SpatialVectorF deltaV = propagateAccelerationW(
|
|
mArticulationData.getRw(tendonJoint.linkInd), parentDeltaV,
|
|
mArticulationData.mInvStIs[tendonJoint.linkInd],
|
|
&mArticulationData.mWorldMotionMatrix[jointDatum.jointOffset],
|
|
&mArticulationData.mIsW[jointDatum.jointOffset], &mArticulationData.mDeferredQstZ[jointDatum.jointOffset], jointDatum.nbDof,
|
|
NULL);
|
|
|
|
Cm::SpatialVectorF childV = mArticulationData.mMotionVelocities[tendonJoint.linkInd] + deltaV;
|
|
|
|
PxU32 index = tendonJoint.mConstraintInd;
|
|
ArticulationInternalTendonConstraint& constraint = mArticulationData.mInternalFixedTendonConstraints[index];
|
|
|
|
const PxU32 childCount = tendonJoint.childCount;
|
|
|
|
PxVec3 jointVError;
|
|
|
|
const PxReal jointV = constraint.row1.innerProduct(childV) - constraint.row0.innerProduct(parentV);
|
|
const PxReal jointP = mArticulationData.mJointPosition[tendonJoint.startJointOffset];
|
|
|
|
jointVError.x = jointV * tendonJoint.coefficient;
|
|
|
|
jointVError.y = jointP * tendonJoint.coefficient;
|
|
|
|
//printf("%i: jointPose = %f, jointV = %f, coefficient = %f\n", tendonJoint.linkInd, jointP, jointV, tendonJoint.coefficient);
|
|
|
|
jointVError.z = 1.f;
|
|
|
|
if (childCount)
|
|
{
|
|
for (ArticulationBitField children = tendonJoint.children; children != 0; children &= (children - 1))
|
|
{
|
|
//index of child of link h on path to link linkID
|
|
const PxU32 child = PxLowestSetBit(children);
|
|
jointVError += calculateFixedTendonVelocityAndPositionRecursive(solveData, childV, deltaV, child);
|
|
}
|
|
}
|
|
|
|
return jointVError;
|
|
}
|
|
|
|
Cm::SpatialVectorF FeatherstoneArticulation::solveFixedTendonConstraintsRecursive(FixedTendonSolveData& solveData,
|
|
const PxU32 tendonJointID)
|
|
{
|
|
ArticulationTendonJoint& tendonJoint = solveData.tendonJoints[tendonJointID];
|
|
|
|
ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(tendonJoint.linkInd);
|
|
|
|
PxU32 index = tendonJoint.mConstraintInd;
|
|
ArticulationInternalTendonConstraint& constraint = mArticulationData.mInternalFixedTendonConstraints[index];
|
|
|
|
const PxU32 childCount = tendonJoint.childCount;
|
|
|
|
PxReal jointV = solveData.rootVel;
|
|
|
|
// calculate current accumulated tendon length from parent accumulated length
|
|
|
|
const PxReal lengthError = solveData.error;
|
|
PxReal limitError = solveData.limitError;
|
|
|
|
// the constraint bias coefficients need to flip signs together with the tendon joint's coefficient
|
|
// in order for the constraint force to point into the correct direction:
|
|
const PxReal coefficientSign = tendonJoint.recipCoefficient;// PxSign(tendonJoint.coefficient);
|
|
const PxReal biasCoefficient = constraint.biasCoefficient;
|
|
const PxReal limitBiasCoefficient = constraint.limitBiasCoefficient;
|
|
|
|
PxReal unclampedForce = ((jointV * constraint.velMultiplier + lengthError * biasCoefficient)*coefficientSign)
|
|
+ constraint.appliedForce * constraint.impulseMultiplier;
|
|
|
|
PxReal unclampedForce2 = (limitError * limitBiasCoefficient * coefficientSign)
|
|
+ constraint.limitAppliedForce * constraint.limitImpulseMultiplier;
|
|
|
|
const PxReal deltaF = ((unclampedForce - constraint.appliedForce) + (unclampedForce2 - constraint.limitAppliedForce));
|
|
|
|
//Ignore tendons for now
|
|
//(isVelIter ? mInternalErrorAccumulatorVelIter : mInternalErrorAccumulatorPosIter).accumulateErrorLocal(deltaF, constraint.recipResponse);
|
|
|
|
constraint.appliedForce = unclampedForce;
|
|
constraint.limitAppliedForce = unclampedForce2;
|
|
|
|
solveData.rootImp += deltaF;
|
|
|
|
Cm::SpatialVectorF impulse(constraint.row1.top * -deltaF, constraint.row1.bottom * -deltaF);
|
|
const Cm::SpatialVectorF YInt = impulse;
|
|
if (childCount)
|
|
{
|
|
for (ArticulationBitField children = tendonJoint.children; children != 0; children &= (children - 1))
|
|
{
|
|
//index of child of link h on path to link linkID
|
|
const PxU32 child = PxLowestSetBit(children);
|
|
|
|
Cm::SpatialVectorF propagatedImpulse = solveFixedTendonConstraintsRecursive(solveData, child);
|
|
|
|
|
|
impulse.top += propagatedImpulse.top;
|
|
impulse.bottom += propagatedImpulse.bottom;
|
|
}
|
|
}
|
|
|
|
|
|
mArticulationData.mSolverLinkSpatialImpulses[tendonJoint.linkInd] += impulse - YInt;
|
|
|
|
return propagateImpulseW(
|
|
mArticulationData.mRw[tendonJoint.linkInd],
|
|
impulse,
|
|
NULL, &mArticulationData.mISInvStIS[jointDatum.jointOffset], &mArticulationData.mWorldMotionMatrix[jointDatum.jointOffset], jointDatum.nbDof,
|
|
&mArticulationData.mDeferredQstZ[jointDatum.jointOffset]);
|
|
}
|
|
|
|
void FeatherstoneArticulation::solveInternalFixedTendonConstraints(bool isTGS)
|
|
{
|
|
PX_UNUSED(isTGS);
|
|
if (mArticulationData.mInternalFixedTendonConstraints.size() == 0)
|
|
return;
|
|
|
|
{
|
|
//Update the error terms in the tendons recursively...
|
|
const PxU32 nbTendons = mArticulationData.mNumFixedTendons;
|
|
|
|
ArticulationLink* links = mArticulationData.getLinks();
|
|
|
|
for (PxU32 i = 0; i < nbTendons; ++i)
|
|
{
|
|
Dy::ArticulationFixedTendon* tendon = mArticulationData.mFixedTendons[i];
|
|
|
|
ArticulationTendonJoint* tendonJoints = tendon->getTendonJoints();
|
|
|
|
Dy::ArticulationTendonJoint& pTendonJoint = tendonJoints[0];
|
|
|
|
//const PxU32 childCount = pTendonJoint.childCount;
|
|
|
|
const PxU32 startLink = pTendonJoint.linkInd;
|
|
|
|
Cm::SpatialVectorV parentVel = pxcFsGetVelocity(startLink);
|
|
Cm::SpatialVectorF parentV;
|
|
V3StoreU(parentVel.angular, parentV.top);
|
|
V3StoreU(parentVel.linear, parentV.bottom);
|
|
|
|
Cm::SpatialVectorF Z(PxVec3(0.f), PxVec3(0.f));
|
|
|
|
Cm::SpatialVectorF parentDeltaV = parentV - mArticulationData.mMotionVelocities[startLink];
|
|
|
|
PxVec3 velError(0.f);
|
|
|
|
for (ArticulationAttachmentBitField children = pTendonJoint.children; children != 0; children &= (children - 1))
|
|
{
|
|
//index of child of link h on path to link linkID
|
|
const PxU32 child = PxLowestSetBit(children);
|
|
|
|
FixedTendonSolveData solveData;
|
|
solveData.links = links;
|
|
solveData.erp = 1.f;
|
|
solveData.rootImp = 0.f;
|
|
solveData.error = tendon->mError;
|
|
solveData.tendonJoints = tendonJoints;
|
|
|
|
velError += calculateFixedTendonVelocityAndPositionRecursive(solveData, parentV, parentDeltaV, child);
|
|
}
|
|
|
|
const PxReal recipScale = velError.z == 0.f ? 0.f : 1.f / velError.z;
|
|
|
|
for (ArticulationAttachmentBitField children = pTendonJoint.children; children != 0; children &= (children - 1))
|
|
{
|
|
//index of child of link h on path to link linkID
|
|
const PxU32 child = PxLowestSetBit(children);
|
|
ArticulationTendonJoint& tendonJoint = tendonJoints[child];
|
|
|
|
ArticulationInternalTendonConstraint& constraint = mArticulationData.mInternalFixedTendonConstraints[tendonJoint.mConstraintInd];
|
|
|
|
const PxReal length = (velError.y + tendon->mOffset);
|
|
|
|
FixedTendonSolveData solveData;
|
|
solveData.links = links;
|
|
solveData.erp = 1.f;
|
|
solveData.rootImp = 0.f;
|
|
solveData.error = (length - tendon->mRestLength) * recipScale;
|
|
solveData.rootVel = velError.x*recipScale;
|
|
|
|
PxReal limitError = 0.f;
|
|
if (length < tendon->mLowLimit)
|
|
limitError = length - tendon->mLowLimit;
|
|
else if (length > tendon->mHighLimit)
|
|
limitError = length - tendon->mHighLimit;
|
|
solveData.limitError = limitError * recipScale;
|
|
solveData.tendonJoints = tendonJoints;
|
|
|
|
//KS - TODO - hook up offsets
|
|
Cm::SpatialVectorF propagatedImpulse = solveFixedTendonConstraintsRecursive(solveData, child);
|
|
|
|
propagatedImpulse.top += constraint.row0.top * solveData.rootImp;
|
|
propagatedImpulse.bottom += constraint.row0.bottom * solveData.rootImp;
|
|
|
|
Z += propagatedImpulse;
|
|
}
|
|
|
|
for (PxU32 linkID = pTendonJoint.linkInd; linkID; linkID = links[linkID].parent)
|
|
{
|
|
const PxU32 jointOffset = mArticulationData.getJointData(linkID).jointOffset;
|
|
const PxU8 dofCount = mArticulationData.getJointData(linkID).nbDof;
|
|
|
|
Z = propagateImpulseW(
|
|
mArticulationData.getRw(linkID),
|
|
Z,
|
|
NULL, &mArticulationData.mISInvStIS[jointOffset], &mArticulationData.mWorldMotionMatrix[jointOffset], dofCount,
|
|
&mArticulationData.mDeferredQstZ[jointOffset]);
|
|
}
|
|
|
|
mArticulationData.mRootDeferredZ += Z;
|
|
mArticulationData.mJointDirty = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FeatherstoneArticulation::solveInternalConstraints(const PxReal dt, const PxReal stepDt, const PxReal invStepDt, bool velocityIteration, bool isTGS,
|
|
const PxReal elapsedTime, const PxReal biasCoefficient,
|
|
bool residualReportingActive, bool isExternalForcesEveryTgsIterationEnabled)
|
|
{
|
|
if (velocityIteration)
|
|
{
|
|
mInternalErrorAccumulatorVelIter.reset();
|
|
mContactErrorAccumulatorVelIter.reset();
|
|
}
|
|
else
|
|
{
|
|
mInternalErrorAccumulatorPosIter.reset();
|
|
mContactErrorAccumulatorPosIter.reset();
|
|
}
|
|
|
|
solveInternalSpatialTendonConstraints(isTGS);
|
|
solveInternalFixedTendonConstraints(isTGS);
|
|
solveInternalMimicJointConstraints(stepDt, invStepDt, velocityIteration, isTGS, biasCoefficient);
|
|
solveInternalJointConstraints(dt, stepDt, invStepDt, velocityIteration, isTGS, elapsedTime, biasCoefficient, residualReportingActive, isExternalForcesEveryTgsIterationEnabled);
|
|
}
|
|
|
|
bool FeatherstoneArticulation::storeStaticConstraint(const PxSolverConstraintDesc& desc)
|
|
{
|
|
if (DY_STATIC_CONTACTS_IN_INTERNAL_SOLVER)
|
|
{
|
|
if (desc.constraintType == DY_SC_TYPE_RB_CONTACT)
|
|
mStaticContactConstraints.pushBack(desc);
|
|
else
|
|
mStatic1DConstraints.pushBack(desc);
|
|
}
|
|
return DY_STATIC_CONTACTS_IN_INTERNAL_SOLVER;
|
|
}
|
|
|
|
void FeatherstoneArticulation::setRootLinearVelocity(const PxVec3& velocity)
|
|
{
|
|
ArticulationLink& rLink = mArticulationData.getLink(0);
|
|
rLink.bodyCore->linearVelocity = velocity;
|
|
mGPUDirtyFlags |= ArticulationDirtyFlag::eDIRTY_ROOT_VELOCITIES;
|
|
computeLinkVelocities(mArticulationData);
|
|
}
|
|
|
|
void FeatherstoneArticulation::setRootAngularVelocity(const PxVec3& velocity)
|
|
{
|
|
ArticulationLink& rLink = mArticulationData.getLink(0);
|
|
rLink.bodyCore->angularVelocity = velocity;
|
|
mGPUDirtyFlags |= ArticulationDirtyFlag::eDIRTY_ROOT_VELOCITIES;
|
|
computeLinkVelocities(mArticulationData);
|
|
}
|
|
|
|
//This method is for user update the root link transform so we need to
|
|
//fix up other link's position. In this case, we should assume all joint
|
|
//velocity/pose is to be zero
|
|
void FeatherstoneArticulation::teleportRootLink()
|
|
{
|
|
//make sure motionMatrix has been set
|
|
//jcalc(mArticulationData);
|
|
|
|
const PxU32 linkCount = mArticulationData.getLinkCount();
|
|
ArticulationLink* links = mArticulationData.getLinks();
|
|
PxReal* jointPositions = mArticulationData.getJointPositions();
|
|
Cm::SpatialVectorF* motionVelocities = mArticulationData.getMotionVelocities();
|
|
|
|
for (PxU32 linkID = 1; linkID < linkCount; ++linkID)
|
|
{
|
|
ArticulationLink& link = links[linkID];
|
|
const PxTransform oldTransform = link.bodyCore->body2World;
|
|
|
|
ArticulationLink& pLink = links[link.parent];
|
|
const PxTransform pBody2World = pLink.bodyCore->body2World;
|
|
|
|
ArticulationJointCore* joint = link.inboundJoint;
|
|
ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID);
|
|
|
|
PxReal* jPosition = &jointPositions[jointDatum.jointOffset];
|
|
|
|
PxQuat newParentToChild;
|
|
PxQuat newWorldQ;
|
|
PxVec3 r;
|
|
|
|
const PxVec3 childOffset = -joint->childPose.p;
|
|
const PxVec3 parentOffset = joint->parentPose.p;
|
|
|
|
const PxQuat relativeQuat = mArticulationData.mRelativeQuat[linkID];
|
|
|
|
switch (joint->jointType)
|
|
{
|
|
case PxArticulationJointType::ePRISMATIC:
|
|
{
|
|
newParentToChild = relativeQuat;
|
|
const PxVec3 e = newParentToChild.rotate(parentOffset);
|
|
const PxVec3 d = childOffset;
|
|
|
|
const PxVec3& u = mArticulationData.mMotionMatrix[jointDatum.jointOffset].bottom;
|
|
|
|
r = e + d + u * jPosition[0];
|
|
break;
|
|
}
|
|
case PxArticulationJointType::eREVOLUTE:
|
|
case PxArticulationJointType::eREVOLUTE_UNWRAPPED:
|
|
{
|
|
const PxVec3& u = mArticulationData.mMotionMatrix[jointDatum.jointOffset].top;
|
|
|
|
PxQuat jointRotation = PxQuat(-jPosition[0], u);
|
|
if (jointRotation.w < 0) //shortest angle.
|
|
jointRotation = -jointRotation;
|
|
|
|
newParentToChild = (jointRotation * relativeQuat).getNormalized();
|
|
|
|
const PxVec3 e = newParentToChild.rotate(parentOffset);
|
|
const PxVec3 d = childOffset;
|
|
r = e + d;
|
|
|
|
/*PxVec3 worldAngVel = oldTransform.rotate(link.motionVelocity.top);
|
|
|
|
newWorldQ = PxExp(worldAngVel*dt) * oldTransform.q;
|
|
|
|
PxQuat newParentToChild2 = (newWorldQ.getConjugate() * joint->relativeQuat * pBody2World.q).getNormalized();
|
|
|
|
const PxVec3 e2 = newParentToChild2.rotate(parentOffset);
|
|
const PxVec3 d2 = childOffset;
|
|
r = e2 + d2;*/
|
|
|
|
PX_ASSERT(r.isFinite());
|
|
|
|
break;
|
|
}
|
|
case PxArticulationJointType::eSPHERICAL:
|
|
{
|
|
|
|
//PxVec3 angVel(joint->jointVelocity[0], joint->jointVelocity[1], joint->jointVelocity[2]);
|
|
//PxVec3 worldAngVel = pLink.bodyCore->angularVelocity + oldTransform.rotate(angVel);
|
|
|
|
PxVec3 worldAngVel = motionVelocities[linkID].top;
|
|
|
|
/*const PxReal eps = 0.001f;
|
|
const PxVec3 dif = worldAngVel - worldAngVel2;
|
|
PX_ASSERT(PxAbs(dif.x) < eps && PxAbs(dif.y) < eps && PxAbs(dif.z) < eps);*/
|
|
|
|
newWorldQ = PxExp(worldAngVel) * oldTransform.q;
|
|
|
|
newParentToChild = (newWorldQ.getConjugate() * relativeQuat * pBody2World.q).getNormalized();
|
|
|
|
const PxVec3 e = newParentToChild.rotate(parentOffset);
|
|
const PxVec3 d = childOffset;
|
|
r = e + d;
|
|
|
|
PX_ASSERT(r.isFinite());
|
|
break;
|
|
}
|
|
case PxArticulationJointType::eFIX:
|
|
{
|
|
//this is fix joint so joint don't have velocity
|
|
newParentToChild = relativeQuat;
|
|
|
|
const PxVec3 e = newParentToChild.rotate(parentOffset);
|
|
const PxVec3 d = childOffset;
|
|
|
|
r = e + d;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
PxTransform& body2World = link.bodyCore->body2World;
|
|
body2World.q = (pBody2World.q * newParentToChild.getConjugate()).getNormalized();
|
|
body2World.p = pBody2World.p + body2World.q.rotate(r);
|
|
|
|
PX_ASSERT(body2World.isSane());
|
|
}
|
|
}
|
|
|
|
PxU8* FeatherstoneArticulation::allocateScratchSpatialData(PxcScratchAllocator* allocator,
|
|
const PxU32 linkCount, ScratchData& scratchData, bool fallBackToHeap)
|
|
{
|
|
const PxU32 size = sizeof(Cm::SpatialVectorF) * linkCount;
|
|
const PxU32 totalSize = size * 4 + sizeof(Dy::SpatialMatrix) * linkCount;
|
|
|
|
PxU8* tempMemory = reinterpret_cast<PxU8*>(allocator->alloc(totalSize, fallBackToHeap));
|
|
|
|
scratchData.motionVelocities = reinterpret_cast<Cm::SpatialVectorF*>(tempMemory);
|
|
PxU32 offset = size;
|
|
scratchData.motionAccelerations = reinterpret_cast<Cm::SpatialVectorF*>(tempMemory + offset);
|
|
offset += size;
|
|
scratchData.coriolisVectors = reinterpret_cast<Cm::SpatialVectorF*>(tempMemory + offset);
|
|
offset += size;
|
|
scratchData.spatialZAVectors = reinterpret_cast<Cm::SpatialVectorF*>(tempMemory + offset);
|
|
offset += size;
|
|
scratchData.compositeSpatialInertias = reinterpret_cast<Dy::SpatialMatrix*>(tempMemory + offset);
|
|
|
|
return tempMemory;
|
|
}
|
|
|
|
/* void FeatherstoneArticulation::allocateScratchSpatialData(DyScratchAllocator& allocator,
|
|
const PxU32 linkCount, ScratchData& scratchData)
|
|
{
|
|
const PxU32 size = sizeof(Cm::SpatialVectorF) * linkCount;
|
|
const PxU32 totalSize = size * 5 + sizeof(Dy::SpatialMatrix) * linkCount;
|
|
|
|
PxU8* tempMemory = allocator.alloc<PxU8>(totalSize);
|
|
|
|
scratchData.motionVelocities = reinterpret_cast<Cm::SpatialVectorF*>(tempMemory);
|
|
PxU32 offset = size;
|
|
scratchData.motionAccelerations = reinterpret_cast<Cm::SpatialVectorF*>(tempMemory + offset);
|
|
offset += size;
|
|
scratchData.coriolisVectors = reinterpret_cast<Cm::SpatialVectorF*>(tempMemory + offset);
|
|
offset += size;
|
|
scratchData.spatialZAVectors = reinterpret_cast<Cm::SpatialVectorF*>(tempMemory + offset);
|
|
offset += size;
|
|
scratchData.externalAccels = reinterpret_cast<Cm::SpatialVector*>(tempMemory + offset);
|
|
offset += size;
|
|
scratchData.compositeSpatialInertias = reinterpret_cast<Dy::SpatialMatrix*>(tempMemory + offset);
|
|
|
|
}*/
|
|
|
|
}//namespace Dy
|
|
}
|