aboutsummaryrefslogtreecommitdiff
path: root/PhysX_3.4/Source/PhysXExtensions/src/ExtD6Joint.cpp
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Source/PhysXExtensions/src/ExtD6Joint.cpp
downloadphysx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz
physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip
Initial commit:
PhysX 3.4.0 Update @ 21294896 APEX 1.4.0 Update @ 21275617 [CL 21300167]
Diffstat (limited to 'PhysX_3.4/Source/PhysXExtensions/src/ExtD6Joint.cpp')
-rw-r--r--PhysX_3.4/Source/PhysXExtensions/src/ExtD6Joint.cpp564
1 files changed, 564 insertions, 0 deletions
diff --git a/PhysX_3.4/Source/PhysXExtensions/src/ExtD6Joint.cpp b/PhysX_3.4/Source/PhysXExtensions/src/ExtD6Joint.cpp
new file mode 100644
index 00000000..5fdad2e7
--- /dev/null
+++ b/PhysX_3.4/Source/PhysXExtensions/src/ExtD6Joint.cpp
@@ -0,0 +1,564 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2008-2016 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 "ExtD6Joint.h"
+#include "ExtConstraintHelper.h"
+#include "CmRenderOutput.h"
+#include "CmConeLimitHelper.h"
+#include "PxTolerancesScale.h"
+#include "CmUtils.h"
+#include "PxConstraint.h"
+
+#include "common/PxSerialFramework.h"
+
+using namespace physx;
+using namespace Ext;
+
+PxD6Joint* physx::PxD6JointCreate(PxPhysics& physics,
+ PxRigidActor* actor0, const PxTransform& localFrame0,
+ PxRigidActor* actor1, const PxTransform& localFrame1)
+{
+ PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxD6JointCreate: local frame 0 is not a valid transform");
+ PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxD6JointCreate: local frame 1 is not a valid transform");
+ PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxD6JointCreate: actors must be different");
+ PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is<PxRigidBody>()) || (actor1 && actor1->is<PxRigidBody>()), "PxD6JointCreate: at least one actor must be dynamic");
+
+ D6Joint *j;
+ PX_NEW_SERIALIZED(j,D6Joint)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1);
+ if(j->attach(physics, actor0, actor1))
+ return j;
+
+ PX_DELETE(j);
+ return NULL;
+}
+
+
+
+D6Joint::D6Joint(const PxTolerancesScale& scale,
+ PxRigidActor* actor0, const PxTransform& localFrame0,
+ PxRigidActor* actor1, const PxTransform& localFrame1)
+: D6JointT(PxJointConcreteType::eD6, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE)
+, mRecomputeMotion(true)
+, mRecomputeLimits(true)
+{
+ D6JointData* data = reinterpret_cast<D6JointData*>(PX_ALLOC(sizeof(D6JointData), "D6JointData"));
+ Cm::markSerializedMem(data, sizeof(D6JointData));
+ mData = data;
+
+ initCommonData(*data,actor0, localFrame0, actor1, localFrame1);
+ for(PxU32 i=0;i<6;i++)
+ data->motion[i] = PxD6Motion::eLOCKED;
+
+ data->twistLimit = PxJointAngularLimitPair(-PxPi/2, PxPi/2);
+ data->swingLimit = PxJointLimitCone(PxPi/2, PxPi/2);
+ data->linearLimit = PxJointLinearLimit(scale, PX_MAX_F32);
+ data->linearMinDist = 1e-6f*scale.length;
+
+ for(PxU32 i=0;i<PxD6Drive::eCOUNT;i++)
+ data->drive[i] = PxD6JointDrive();
+
+ data->drivePosition = PxTransform(PxIdentity);
+ data->driveLinearVelocity = PxVec3(0);
+ data->driveAngularVelocity = PxVec3(0);
+
+ data->projectionLinearTolerance = 1e10;
+ data->projectionAngularTolerance = PxPi;
+}
+
+PxD6Motion::Enum D6Joint::getMotion(PxD6Axis::Enum index) const
+{
+ return data().motion[index];
+}
+
+void D6Joint::setMotion(PxD6Axis::Enum index, PxD6Motion::Enum t)
+{
+ data().motion[index] = t;
+ mRecomputeMotion = true;
+ markDirty();
+}
+
+PxReal D6Joint::getTwist() const
+{
+ PxQuat q = getRelativeTransform().q, swing, twist;
+ Ps::separateSwingTwist(q, swing, twist);
+ if (twist.x < 0)
+ twist = -twist;
+ PxReal angle = twist.getAngle(); // angle is in range [0, 2pi]
+ return angle < PxPi ? angle : angle - PxTwoPi;
+}
+
+PxReal D6Joint::getSwingYAngle() const
+{
+ PxQuat q = getRelativeTransform().q, swing, twist;
+ Ps::separateSwingTwist(q, swing, twist);
+ if (swing.w < 0) // choose the shortest rotation
+ swing = -swing;
+
+ PxReal angle = 4 * PxAtan2(swing.y, 1 + swing.w); // tan (t/2) = sin(t)/(1+cos t), so this is the quarter angle
+ PX_ASSERT(angle>-PxPi && angle<=PxPi); // since |y| < w+1, the atan magnitude is < PI/4
+ return angle;
+}
+
+PxReal D6Joint::getSwingZAngle() const
+{
+ PxQuat q = getRelativeTransform().q, swing, twist;
+ Ps::separateSwingTwist(q, swing, twist);
+ if (swing.w < 0) // choose the shortest rotation
+ swing = -swing;
+
+ PxReal angle = 4 * PxAtan2(swing.z, 1 + swing.w); // tan (t/2) = sin(t)/(1+cos t), so this is the quarter angle
+ PX_ASSERT(angle>-PxPi && angle <= PxPi); // since |y| < w+1, the atan magnitude is < PI/4
+ return angle;
+}
+
+
+PxD6JointDrive D6Joint::getDrive(PxD6Drive::Enum index) const
+{
+ return data().drive[index];
+}
+
+void D6Joint::setDrive(PxD6Drive::Enum index, const PxD6JointDrive& d)
+{
+ PX_CHECK_AND_RETURN(d.isValid(), "PxD6Joint::setDrive: drive is invalid");
+
+ data().drive[index] = d;
+ mRecomputeMotion = true;
+ markDirty();
+}
+
+PxJointLinearLimit D6Joint::getLinearLimit() const
+{
+
+ return data().linearLimit;
+}
+
+void D6Joint::setLinearLimit(const PxJointLinearLimit& l)
+{
+ PX_CHECK_AND_RETURN(l.isValid(), "PxD6Joint::setLinearLimit: limit invalid");
+ data().linearLimit = l;
+ mRecomputeLimits = true;
+ markDirty();
+}
+
+PxJointAngularLimitPair D6Joint::getTwistLimit() const
+{
+ return data().twistLimit;
+}
+
+void D6Joint::setTwistLimit(const PxJointAngularLimitPair& l)
+{
+ PX_CHECK_AND_RETURN(l.isValid(), "PxD6Joint::setTwistLimit: limit invalid");
+ PX_CHECK_AND_RETURN(l.lower>-PxTwoPi && l.upper<PxTwoPi , "PxD6Joint::twist limit must be strictly -2*PI and 2*PI");
+ PX_CHECK_AND_RETURN(l.upper - l.lower < PxTwoPi, "PxD6Joint::twist limit range must be strictly less than 2*PI");
+
+ data().twistLimit = l;
+ mRecomputeLimits = true;
+ markDirty();
+}
+
+PxJointLimitCone D6Joint::getSwingLimit() const
+{
+ return data().swingLimit;
+}
+
+void D6Joint::setSwingLimit(const PxJointLimitCone& l)
+{
+ PX_CHECK_AND_RETURN(l.isValid(), "PxD6Joint::setSwingLimit: limit invalid");
+
+ data().swingLimit = l;
+ mRecomputeLimits = true;
+ markDirty();
+}
+
+PxTransform D6Joint::getDrivePosition() const
+{
+ return data().drivePosition;
+}
+
+void D6Joint::setDrivePosition(const PxTransform& pose)
+{
+ PX_CHECK_AND_RETURN(pose.isSane(), "PxD6Joint::setDrivePosition: pose invalid");
+ data().drivePosition = pose.getNormalized();
+ markDirty();
+}
+
+void D6Joint::getDriveVelocity(PxVec3& linear, PxVec3& angular) const
+{
+ linear = data().driveLinearVelocity;
+ angular = data().driveAngularVelocity;
+}
+
+void D6Joint::setDriveVelocity(const PxVec3& linear,
+ const PxVec3& angular)
+{
+ PX_CHECK_AND_RETURN(linear.isFinite() && angular.isFinite(), "PxD6Joint::setDriveVelocity: velocity invalid");
+ data().driveLinearVelocity = linear;
+ data().driveAngularVelocity = angular;
+ markDirty();
+}
+
+void D6Joint::setProjectionAngularTolerance(PxReal tolerance)
+{
+ PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0 && tolerance <= PxPi, "PxD6Joint::setProjectionAngularTolerance: tolerance invalid");
+ data().projectionAngularTolerance = tolerance;
+ markDirty();
+}
+
+PxReal D6Joint::getProjectionAngularTolerance() const
+{
+ return data().projectionAngularTolerance;
+}
+
+void D6Joint::setProjectionLinearTolerance(PxReal tolerance)
+{
+ PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0, "PxD6Joint::setProjectionLinearTolerance: invalid parameter");
+ data().projectionLinearTolerance = tolerance;
+ markDirty();
+}
+
+PxReal D6Joint::getProjectionLinearTolerance() const
+{
+ return data().projectionLinearTolerance;
+}
+
+
+
+void* D6Joint::prepareData()
+{
+ D6JointData& d = data();
+
+ if(mRecomputeLimits)
+ {
+ d.thSwingY = PxTan(d.swingLimit.yAngle/2);
+ d.thSwingZ = PxTan(d.swingLimit.zAngle/2);
+ d.thSwingPad = PxTan(d.swingLimit.contactDistance/2);
+
+ d.tqSwingY = PxTan(d.swingLimit.yAngle/4);
+ d.tqSwingZ = PxTan(d.swingLimit.zAngle/4);
+ d.tqSwingPad = PxTan(d.swingLimit.contactDistance/4);
+
+ d.tqTwistLow = PxTan(d.twistLimit.lower/4);
+ d.tqTwistHigh = PxTan(d.twistLimit.upper/4);
+ d.tqTwistPad = PxTan(d.twistLimit.contactDistance/4);
+ mRecomputeLimits = false;
+ }
+
+ if(mRecomputeMotion)
+ {
+ d.driving = 0;
+ d.limited = 0;
+ d.locked = 0;
+
+ for(PxU32 i=0;i<PxD6Axis::eCOUNT;i++)
+ {
+ if(d.motion[i] == PxD6Motion::eLIMITED)
+ d.limited |= 1<<i;
+ else if(d.motion[i] == PxD6Motion::eLOCKED)
+ d.locked |= 1<<i;
+ }
+
+ // a linear direction isn't driven if it's locked
+ if(active(PxD6Drive::eX) && d.motion[PxD6Axis::eX]!=PxD6Motion::eLOCKED) d.driving |= 1<< PxD6Drive::eX;
+ if(active(PxD6Drive::eY) && d.motion[PxD6Axis::eY]!=PxD6Motion::eLOCKED) d.driving |= 1<< PxD6Drive::eY;
+ if(active(PxD6Drive::eZ) && d.motion[PxD6Axis::eZ]!=PxD6Motion::eLOCKED) d.driving |= 1<< PxD6Drive::eZ;
+
+ // SLERP drive requires all angular dofs unlocked, and inhibits swing/twist
+
+ bool swing1Locked = d.motion[PxD6Axis::eSWING1] == PxD6Motion::eLOCKED;
+ bool swing2Locked = d.motion[PxD6Axis::eSWING2] == PxD6Motion::eLOCKED;
+ bool twistLocked = d.motion[PxD6Axis::eTWIST] == PxD6Motion::eLOCKED;
+
+ if(active(PxD6Drive::eSLERP) && !swing1Locked && !swing2Locked && !twistLocked)
+ d.driving |= 1<<PxD6Drive::eSLERP;
+ else
+ {
+ if(active(PxD6Drive::eTWIST) && !twistLocked)
+ d.driving |= 1<<PxD6Drive::eTWIST;
+ if(active(PxD6Drive::eSWING) && (!swing1Locked || !swing2Locked))
+ d.driving |= 1<< PxD6Drive::eSWING;
+ }
+
+ mRecomputeMotion = false;
+ }
+
+ this->D6JointT::prepareData();
+
+ return mData;
+}
+
+// Notes:
+/*
+
+This used to be in the linear drive model:
+
+ if(motion[PxD6Axis::eX+i] == PxD6Motion::eLIMITED)
+ {
+ if(data.driveLinearVelocity[i] < 0.0f && cB2cA.p[i] < -mLimits[PxD6Limit::eLINEAR].mValue ||
+ data.driveLinearVelocity[i] > 0.0f && cB2cA.p[i] > mLimits[PxD6Limit::eLINEAR].mValue)
+ continue;
+ }
+
+it doesn't seem like a good idea though, because it turns off drive altogether, despite the fact that positional
+drive might pull us back in towards the limit. Might be better to make the drive unilateral so it can only pull
+us in from the limit
+
+This used to be in angular locked:
+
+ // Angular locked
+ //TODO fix this properly.
+ if(PxAbs(cB2cA.q.x) < 0.0001f) cB2cA.q.x = 0;
+ if(PxAbs(cB2cA.q.y) < 0.0001f) cB2cA.q.y = 0;
+ if(PxAbs(cB2cA.q.z) < 0.0001f) cB2cA.q.z = 0;
+ if(PxAbs(cB2cA.q.w) < 0.0001f) cB2cA.q.w = 0;
+
+
+*/
+
+namespace
+{
+PxReal tanHalfFromSin(PxReal sin)
+{
+ return Ps::tanHalf(sin, 1 - sin*sin);
+}
+
+PxQuat truncate(const PxQuat& qIn, PxReal minCosHalfTol, bool& truncated)
+{
+ PxQuat q = qIn.w >= 0 ? qIn : -qIn;
+ truncated = q.w < minCosHalfTol;
+ if (!truncated)
+ return q;
+ PxVec3 v = q.getImaginaryPart().getNormalized() * PxSqrt(1 - minCosHalfTol * minCosHalfTol);
+ return PxQuat(v.x, v.y, v.z, minCosHalfTol);
+}
+
+// we decompose the quaternion as q1 * q2, where q1 is a rotation orthogonal to the unit axis, and q2 a rotation around it.
+// (so for example if 'axis' is the twist axis, this is separateSwingTwist).
+
+PxQuat project(const PxQuat& q, const PxVec3& axis, PxReal cosHalfTol, bool& truncated)
+{
+ PxReal a = q.getImaginaryPart().dot(axis);
+ PxQuat q2 = PxAbs(a) >= 1e-6f ? PxQuat(a*axis.x, a*axis.y, a*axis.z, q.w).getNormalized() : PxQuat(PxIdentity);
+ PxQuat q1 = q * q2.getConjugate();
+
+ PX_ASSERT(PxAbs(q1.getImaginaryPart().dot(q2.getImaginaryPart())) < 1e-6f);
+
+ return truncate(q1, cosHalfTol, truncated) * q2;
+}
+}
+
+namespace physx
+{
+// Here's how the angular part works:
+// * if no DOFs are locked, there's nothing to do.
+// * if all DOFs are locked, we just truncate the rotation
+// * if two DOFs are locked
+// * we decompose the rotation into swing * twist, where twist is a rotation around the free DOF and swing is a rotation around an axis orthogonal to the free DOF
+// * then we truncate swing
+// The case of one locked DOF is currently unimplemented, but one option would be:
+// * if one DOF is locked (the tricky case), we define the 'free' axis as follows (as the velocity solver prep function does)
+// TWIST: cB[0]
+// SWING1: cB[0].cross(cA[2])
+// SWING2: cB[0].cross(cA[1])
+// then, as above, we decompose into swing * free, and truncate the free rotation
+
+//export this in the physx namespace so we can unit test it
+PxQuat angularProject(PxU32 lockedDofs, const PxQuat& q, PxReal cosHalfTol, bool& truncated)
+{
+ PX_ASSERT(lockedDofs <= 7);
+ truncated = false;
+
+ switch (lockedDofs)
+ {
+ case 0: return q;
+ case 1: return q; // currently unimplemented
+ case 2: return q; // currently unimplemented
+ case 3: return project(q, PxVec3(0.0f, 0.0f, 1.0f), cosHalfTol, truncated);
+ case 4: return q; // currently unimplemented
+ case 5: return project(q, PxVec3(0.0f, 1.0f, 0.0f), cosHalfTol, truncated);
+ case 6: return project(q, PxVec3(1.0f, 0.0f, 0.0f), cosHalfTol, truncated);
+ case 7: return truncate(q, cosHalfTol, truncated);
+ default: return PxQuat(PxIdentity);
+ }
+}
+}
+
+namespace
+{
+void D6JointProject(const void* constantBlock,
+ PxTransform& bodyAToWorld,
+ PxTransform& bodyBToWorld,
+ bool projectToA)
+{
+ using namespace joint;
+ const D6JointData &data = *reinterpret_cast<const D6JointData*>(constantBlock);
+
+ PxTransform cA2w, cB2w, cB2cA, projected;
+ computeDerived(data, bodyAToWorld, bodyBToWorld, cA2w, cB2w, cB2cA);
+
+ PxVec3 v(data.locked & 1 ? cB2cA.p.x : 0,
+ data.locked & 2 ? cB2cA.p.y : 0,
+ data.locked & 4 ? cB2cA.p.z : 0);
+
+ bool linearTrunc, angularTrunc = false;
+ projected.p = truncateLinear(v, data.projectionLinearTolerance, linearTrunc) + (cB2cA.p - v);
+
+ projected.q = angularProject(data.locked >> 3, cB2cA.q, PxCos(data.projectionAngularTolerance / 2), angularTrunc);
+
+ if (linearTrunc || angularTrunc)
+ projectTransforms(bodyAToWorld, bodyBToWorld, cA2w, cB2w, projected, data, projectToA);
+}
+
+
+void D6JointVisualize(PxConstraintVisualizer &viz,
+ const void* constantBlock,
+ const PxTransform& body0Transform,
+ const PxTransform& body1Transform,
+ PxU32 /*flags*/)
+{
+ using namespace joint;
+
+ const PxU32 SWING1_FLAG = 1<<PxD6Axis::eSWING1,
+ SWING2_FLAG = 1<<PxD6Axis::eSWING2,
+ TWIST_FLAG = 1<<PxD6Axis::eTWIST;
+
+ const PxU32 ANGULAR_MASK = SWING1_FLAG | SWING2_FLAG | TWIST_FLAG;
+ const PxU32 LINEAR_MASK = 1<<PxD6Axis::eX | 1<<PxD6Axis::eY | 1<<PxD6Axis::eZ;
+
+ PX_UNUSED(ANGULAR_MASK);
+ PX_UNUSED(LINEAR_MASK);
+
+ const D6JointData & data = *reinterpret_cast<const D6JointData*>(constantBlock);
+
+ PxTransform cA2w = body0Transform * data.c2b[0];
+ PxTransform cB2w = body1Transform * data.c2b[1];
+
+ viz.visualizeJointFrames(cA2w, cB2w);
+
+ if(cA2w.q.dot(cB2w.q)<0)
+ cB2w.q = -cB2w.q;
+
+ PxTransform cB2cA = cA2w.transformInv(cB2w);
+
+ PxQuat swing, twist;
+ Ps::separateSwingTwist(cB2cA.q,swing,twist);
+
+ PxMat33 cA2w_m(cA2w.q), cB2w_m(cB2w.q);
+ PxVec3 bX = cB2w_m[0], aY = cA2w_m[1], aZ = cA2w_m[2];
+
+ if(data.limited&TWIST_FLAG)
+ {
+ PxReal tqPhi = Ps::tanHalf(twist.x, twist.w); // always support (-pi, +pi)
+ viz.visualizeAngularLimit(cA2w, data.twistLimit.lower, data.twistLimit.upper,
+ PxAbs(tqPhi) > data.tqTwistHigh + data.tqSwingPad);
+ }
+
+ bool swing1Limited = (data.limited & SWING1_FLAG)!=0, swing2Limited = (data.limited & SWING2_FLAG)!=0;
+
+ if(swing1Limited && swing2Limited)
+ {
+ PxVec3 tanQSwing = PxVec3(0, Ps::tanHalf(swing.z,swing.w), -Ps::tanHalf(swing.y,swing.w));
+ Cm::ConeLimitHelper coneHelper(data.tqSwingZ, data.tqSwingY, data.tqSwingPad);
+ viz.visualizeLimitCone(cA2w, data.tqSwingZ, data.tqSwingY,
+ !coneHelper.contains(tanQSwing));
+ }
+ else if(swing1Limited ^ swing2Limited)
+ {
+ PxTransform yToX = PxTransform(PxVec3(0), PxQuat(-PxPi/2, PxVec3(0,0,1.f)));
+ PxTransform zToX = PxTransform(PxVec3(0), PxQuat(PxPi/2, PxVec3(0,1.f,0)));
+
+ if(swing1Limited)
+ {
+ if(data.locked & SWING2_FLAG)
+ viz.visualizeAngularLimit(cA2w * yToX, -data.swingLimit.yAngle, data.swingLimit.yAngle,
+ PxAbs(Ps::tanHalf(swing.y, swing.w)) > data.tqSwingY - data.tqSwingPad);
+ else
+ viz.visualizeDoubleCone(cA2w * zToX, data.swingLimit.yAngle,
+ PxAbs(tanHalfFromSin(aZ.dot(bX)))> data.thSwingY - data.thSwingPad);
+ }
+ else
+ {
+ if(data.locked & SWING1_FLAG)
+ viz.visualizeAngularLimit(cA2w * zToX, -data.swingLimit.zAngle, data.swingLimit.zAngle,
+ PxAbs(Ps::tanHalf(swing.z, swing.w)) > data.tqSwingZ - data.tqSwingPad);
+ else
+ viz.visualizeDoubleCone(cA2w * yToX, data.swingLimit.zAngle,
+ PxAbs(tanHalfFromSin(aY.dot(bX)))> data.thSwingZ - data.thSwingPad);
+ }
+ }
+}
+}
+
+bool D6Joint::attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1)
+{
+ mPxConstraint = physics.createConstraint(actor0, actor1, *this, sShaders, sizeof(D6JointData));
+ return mPxConstraint!=NULL;
+}
+
+void D6Joint::exportExtraData(PxSerializationContext& stream)
+{
+ if(mData)
+ {
+ stream.alignData(PX_SERIAL_ALIGN);
+ stream.writeData(mData, sizeof(D6JointData));
+ }
+ stream.writeName(mName);
+}
+
+void D6Joint::importExtraData(PxDeserializationContext& context)
+{
+ if(mData)
+ mData = context.readExtraData<D6JointData, PX_SERIAL_ALIGN>();
+
+ context.readName(mName);
+}
+
+void D6Joint::resolveReferences(PxDeserializationContext& context)
+{
+ setPxConstraint(resolveConstraintPtr(context, getPxConstraint(), getConnector(), sShaders));
+}
+
+D6Joint* D6Joint::createObject(PxU8*& address, PxDeserializationContext& context)
+{
+ D6Joint* obj = new (address) D6Joint(PxBaseFlag::eIS_RELEASABLE);
+ address += sizeof(D6Joint);
+ obj->importExtraData(context);
+ obj->resolveReferences(context);
+ return obj;
+}
+
+// global function to share the joint shaders with API capture
+const PxConstraintShaderTable* Ext::GetD6JointShaderTable()
+{
+ return &D6Joint::getConstraintShaderTable();
+}
+
+//~PX_SERIALIZATION
+PxConstraintShaderTable Ext::D6Joint::sShaders = { Ext::D6JointSolverPrep, D6JointProject, D6JointVisualize, PxConstraintFlag::eGPU_COMPATIBLE };
+