aboutsummaryrefslogtreecommitdiff
path: root/PhysX_3.4/Samples/SampleBase/SampleCharacterHelpers.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/Samples/SampleBase/SampleCharacterHelpers.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/Samples/SampleBase/SampleCharacterHelpers.cpp')
-rw-r--r--PhysX_3.4/Samples/SampleBase/SampleCharacterHelpers.cpp578
1 files changed, 578 insertions, 0 deletions
diff --git a/PhysX_3.4/Samples/SampleBase/SampleCharacterHelpers.cpp b/PhysX_3.4/Samples/SampleBase/SampleCharacterHelpers.cpp
new file mode 100644
index 00000000..9337ae01
--- /dev/null
+++ b/PhysX_3.4/Samples/SampleBase/SampleCharacterHelpers.cpp
@@ -0,0 +1,578 @@
+// 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 <stdio.h>
+
+#include "SampleCharacterHelpers.h"
+
+#include "AcclaimLoader.h"
+#include "PsMathUtils.h"
+#include "SampleAllocatorSDKClasses.h"
+#include "SampleArray.h"
+
+///////////////////////////////////////////////////////////////////////////////
+static Acclaim::Bone* getBoneFromName(Acclaim::ASFData &data, const char *name)
+{
+ // use a simple linear search -> probably we could use hash map if performance is an issue
+ for (PxU32 i = 0; i < data.mNbBones; i++)
+ {
+ if (strcmp(name, data.mBones[i].mName) == 0)
+ return &data.mBones[i];
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+inline PxQuat EulerAngleToQuat(const PxVec3 &rot)
+{
+ PxQuat qx(Ps::degToRad(rot.x), PxVec3(1.0f, 0.0f, 0.0f));
+ PxQuat qy(Ps::degToRad(rot.y), PxVec3(0.0f, 1.0f, 0.0f));
+ PxQuat qz(Ps::degToRad(rot.z), PxVec3(0.0f, 0.0f, 1.0f));
+ return qz * qy * qx;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static PxTransform computeBoneTransform(PxTransform &rootTransform, Acclaim::Bone &bone, PxVec3* boneFrameData)
+{
+ using namespace Acclaim;
+
+ //PxTransform rootTransform(PxVec3(0.0f), PxQuat(PxIdentity));
+ PxTransform parentTransform = (bone.mParent) ?
+ computeBoneTransform(rootTransform, *bone.mParent, boneFrameData) : rootTransform;
+
+ PxQuat qWorld = EulerAngleToQuat(bone.mAxis);
+ PxVec3 offset = bone.mLength * bone.mDirection;
+ PxQuat qDelta = PxQuat(PxIdentity);
+ PxVec3 boneData = boneFrameData[bone.mID-1];
+
+ if (bone.mDOF & BoneDOFFlag::eRX)
+ qDelta = PxQuat(Ps::degToRad(boneData.x), PxVec3(1.0f, 0.0f, 0.0f)) * qDelta;
+ if (bone.mDOF & BoneDOFFlag::eRY)
+ qDelta = PxQuat(Ps::degToRad(boneData.y), PxVec3(0.0f, 1.0f, 0.0f)) * qDelta;
+ if (bone.mDOF & BoneDOFFlag::eRZ)
+ qDelta = PxQuat(Ps::degToRad(boneData.z), PxVec3(0.0f, 0.0f, 1.0f)) * qDelta;
+
+ PxQuat boneOrientation = qWorld * qDelta * qWorld.getConjugate();
+
+ PxTransform boneTransform(boneOrientation.rotate(offset), boneOrientation);
+
+ return parentTransform.transform(boneTransform);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static PxTransform computeBoneTransformRest(Acclaim::Bone &bone)
+{
+ using namespace Acclaim;
+
+ PxTransform parentTransform = (bone.mParent) ?
+ computeBoneTransformRest(*bone.mParent) : PxTransform(PxVec3(0.0f), PxQuat(PxIdentity));
+
+ PxVec3 offset = bone.mLength * bone.mDirection;
+
+ PxTransform boneTransform(offset, PxQuat(PxIdentity));
+
+ return parentTransform.transform(boneTransform);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+Character::Character() :
+ mCurrentMotion(NULL),
+ mTargetMotion(NULL),
+ mBlendCounter(0),
+ mCharacterScale(1.0f),
+ mASFData(NULL),
+ mCharacterPose(PxVec3(0.0f), PxQuat(PxIdentity)),
+ mFrameTime(0.0f)
+{
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+int Character::addMotion(const char* amcFileName, PxU32 start, PxU32 end)
+{
+ Acclaim::AMCData AMCData;
+
+ if (Acclaim::readAMCData(amcFileName, *mASFData, AMCData) == false)
+ {
+ AMCData.release();
+ return -1;
+ }
+
+ if (AMCData.mNbFrames == 0)
+ {
+ AMCData.release();
+ return -1;
+ }
+
+ if (end >= AMCData.mNbFrames)
+ end = AMCData.mNbFrames - 1;
+
+ Motion* motion = SAMPLE_NEW(Motion)();
+
+ if (buildMotion(AMCData, *motion, start, end) == false)
+ {
+ SAMPLE_FREE(motion);
+ AMCData.release();
+ return -1;
+ }
+
+ mMotions.pushBack(motion);
+ mCurrentMotion = motion;
+
+ // set the frame counter to 0
+ mFrameTime = 0.0f;
+
+ AMCData.release();
+
+ return mMotions.size() - 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool Character::buildMotion(Acclaim::AMCData &amcData, Motion &motion, PxU32 start, PxU32 end)
+{
+ using namespace Acclaim;
+
+ if (mASFData == NULL)
+ return false;
+
+ motion.mNbFrames = end - start + 1;
+ motion.mMotionData = SAMPLE_NEW(MotionData)[motion.mNbFrames];
+
+ // compute bounds of all the motion data on normalized frame
+ PxBounds3 bounds = PxBounds3::empty();
+ for (PxU32 i = start; i < end; i++)
+ {
+ Acclaim::FrameData &frameData = amcData.mFrameData[i];
+
+ PxTransform rootTransform(PxVec3(0.0f), EulerAngleToQuat(frameData.mRootOrientation));
+
+ for (PxU32 j = 0; j < mASFData->mNbBones; j++)
+ {
+ PxTransform t = computeBoneTransform(rootTransform, mASFData->mBones[j], frameData.mBoneFrameData);
+ bounds.include(t.p);
+ }
+ }
+
+ Acclaim::FrameData& firstFrame = amcData.mFrameData[0];
+ Acclaim::FrameData& lastFrame = amcData.mFrameData[amcData.mNbFrames-1];
+
+ // compute direction vector
+ motion.mDistance = mCharacterScale * (lastFrame.mRootPosition - firstFrame.mRootPosition).magnitude();
+
+ PxVec3 firstPosition = firstFrame.mRootPosition;
+ PX_UNUSED(firstPosition);
+ PxVec3 firstAngles = firstFrame.mRootOrientation;
+ PxQuat firstOrientation = EulerAngleToQuat(PxVec3(0, firstAngles.y, 0));
+
+ for (PxU32 i = 0; i < motion.mNbFrames; i++)
+ {
+ Acclaim::FrameData& frameData = amcData.mFrameData[i+start];
+ MotionData &motionData = motion.mMotionData[i];
+
+ // normalize y-rot by computing inverse quat from first frame
+ // this makes all the motion aligned in the same (+ z) direction.
+ PxQuat currentOrientation = EulerAngleToQuat(frameData.mRootOrientation);
+ PxQuat qdel = firstOrientation.getConjugate() * currentOrientation;
+ PxTransform rootTransform(PxVec3(0.0f), qdel);
+
+ for (PxU32 j = 0; j < mNbBones; j++)
+ {
+ PxTransform boneTransform = computeBoneTransform(rootTransform, mASFData->mBones[j], frameData.mBoneFrameData);
+ motionData.mBoneTransform[j] = boneTransform;
+ }
+
+ //PxReal y = mCharacterScale * (frameData.mRootPosition.y - firstPosition.y) - bounds.minimum.y;
+ motionData.mRootTransform = PxTransform(PxVec3(0.0f, -bounds.minimum.y, 0.0f), PxQuat(PxIdentity));
+ }
+
+ // now make the motion cyclic by linear interpolating root position and joint angles
+ const PxU32 windowSize = 10;
+ for (PxU32 i = 0; i <= windowSize; i++)
+ {
+ PxU32 j = motion.mNbFrames - 1 - windowSize + i;
+
+ PxReal t = PxReal(i) / PxReal(windowSize);
+
+ MotionData& motion_i = motion.mMotionData[0];
+ MotionData& motion_j = motion.mMotionData[j];
+
+ // lerp root translation
+ PxVec3 blendedRootPos = (1.0f - t) * motion_j.mRootTransform.p + t * motion_i.mRootTransform.p;
+ for (PxU32 k = 0; k < mNbBones; k++)
+ {
+ PxVec3 pj = motion_j.mRootTransform.p + motion_j.mBoneTransform[k].p;
+ PxVec3 pi = motion_i.mRootTransform.p + motion_i.mBoneTransform[k].p;
+
+ PxVec3 p = (1.0f - t) * pj + t * pi;
+ motion_j.mBoneTransform[k].p = p - blendedRootPos;
+ }
+ motion_j.mRootTransform.p = blendedRootPos;
+ }
+
+ return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// we apply pose blending if there is transition from one motion to another
+bool
+Character::computeFramePose()
+{
+ PxU32 frameNo = PxU32(mFrameTime);
+ if (frameNo >= mCurrentMotion->mNbFrames)
+ return false;
+
+ MotionData& motionData = mCurrentMotion->mMotionData[frameNo];
+
+ // compute blended motion when a target motion is set
+ if (mTargetMotion)
+ {
+ // first pose from target motion data
+ MotionData& targetData = mTargetMotion->mMotionData[0];
+ PxReal t = PxReal(mBlendCounter) / 10.0f;
+
+ for (PxU32 i = 0; i < mNbBones; i++)
+ {
+ mCurrentBoneTransform[i].p = t * motionData.mBoneTransform[i].p +
+ (1.0f - t) * targetData.mBoneTransform[i].p;
+ mCurrentBoneTransform[i].q = motionData.mBoneTransform[i].q;
+ }
+ mCurrentRootTransform.p = t * motionData.mRootTransform.p +
+ (1.0f - t) * targetData.mRootTransform.p;
+ mCurrentRootTransform.q = targetData.mRootTransform.q;
+
+ }
+ else
+ {
+ for (PxU32 i = 0; i < mNbBones; i++)
+ mCurrentBoneTransform[i] = motionData.mBoneTransform[i];
+ mCurrentRootTransform = motionData.mRootTransform;
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool Character::create(const char *asfFileName, PxReal scale)
+{
+ mCharacterScale = scale;
+
+ if (readSetup(asfFileName) == false)
+ return false;
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+bool Character::faceToward(const PxVec3& targetDir, PxReal angleLimitPerFrame)
+{
+ PxVec3 oldDir = mCharacterPose.q.rotate(PxVec3(0,0,1));
+ PxVec3 up(0,1,0);
+ PxVec3 newDir = PxVec3(targetDir.x, 0, targetDir.z).getNormalized();
+ PxVec3 right = -1.0f * oldDir.cross(up);
+
+ PxReal cos = newDir.dot(oldDir);
+ PxReal sin = newDir.dot(right);
+ PxReal angle = atan2(sin, cos);
+
+ PxReal limit = angleLimitPerFrame * (PxPi / 180.0f);
+ if (angle > limit) angle = limit;
+ else if (angle < -limit) angle = -limit;
+
+ PxQuat qdel(angle, up);
+
+ mCharacterPose.q = qdel * mCharacterPose.q;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool
+Character::getFramePose(PxTransform &rootTransform, SampleArray<PxVec3> &positions, SampleArray<PxU32> &indexBuffers)
+{
+ if (mCurrentMotion == NULL)
+ return false;
+
+ PxU32 frameNo = PxU32(mFrameTime);
+ if (frameNo >= mCurrentMotion->mNbFrames)
+ return false;
+
+ positions.resize(mNbBones+1);
+
+ // copy precomputed bone position in local space
+ positions[0] = PxVec3(0.0f); // root position
+ for (PxU32 i = 0; i < mNbBones; i++)
+ positions[i+1] = mCurrentBoneTransform[i].p;
+
+ // copy capsule index data
+ indexBuffers.resize(mASFData->mNbBones * 2);
+ for (PxU32 i = 0; i < mASFData->mNbBones; i++)
+ {
+ Acclaim::Bone& bone = mASFData->mBones[i];
+ indexBuffers[i*2] = bone.mID;
+ indexBuffers[i*2+1] = (bone.mParent) ? bone.mParent->mID : 0;
+ }
+
+ // compute root transform
+ rootTransform = mCharacterPose.transform(mCurrentRootTransform);
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+bool Character::move(PxReal speed, bool jump, bool active )
+{
+ if (mCurrentMotion == NULL)
+ return false;
+
+ if (mBlendCounter > 0)
+ mBlendCounter--;
+
+ if (mTargetMotion && (mBlendCounter == 0))
+ {
+ mBlendCounter = 0;
+ mCurrentMotion = mTargetMotion;
+ mFrameTime = 0.0f;
+ mTargetMotion = NULL;
+ }
+
+ PxU32 nbFrames = mCurrentMotion->mNbFrames;
+ PxReal distance = mCurrentMotion->mDistance;
+
+ PxReal frameDelta = 0.0f;
+ const PxReal angleLimitPerFrame = 3.0f;
+
+ if (jump)
+ {
+ frameDelta = 1.0f;
+ }
+ else if (active && (mBlendCounter == 0))
+ {
+ // compute target orientation
+ PxVec3 dir = mTargetPosition - mCharacterPose.p;
+ dir.y = 0.0f;
+ PxReal curDistance = dir.magnitude();
+
+ if (curDistance > 0.01f)
+ faceToward(dir, angleLimitPerFrame);
+
+ frameDelta = speed * PxReal(nbFrames) * (curDistance / distance);
+ }
+ else
+ {
+ frameDelta = 0;
+ }
+
+ mCharacterPose.p = mTargetPosition;
+
+ mFrameTime += frameDelta;
+ PxU32 frameNo = PxU32(mFrameTime);
+
+ if (frameNo >= nbFrames)
+ {
+ if (jump)
+ mFrameTime = PxReal(nbFrames) - 1.0f;
+ else
+ mFrameTime = 0.0f;
+ }
+
+ // compute pose of all the bones at current frame (results are used by both getFramePose and Skin)
+ computeFramePose();
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool Character::readSetup(const char* asfFileName)
+{
+ if (mASFData) mASFData->release();
+
+ mASFData = new Acclaim::ASFData;
+
+ if (Acclaim::readASFData(asfFileName, *mASFData) == false)
+ {
+ delete mASFData;
+ mASFData = NULL;
+ return false;
+ }
+
+ mNbBones = mASFData->mNbBones;
+
+ // scale bone length
+ for (PxU32 i = 0; i < mASFData->mNbBones; i++)
+ {
+ Acclaim::Bone& bone = mASFData->mBones[i];
+
+ bone.mLength *= mCharacterScale;
+ }
+
+ return true;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+void Character::release()
+{
+ for (PxU32 i = 0; i < mMotions.size(); i++)
+ {
+ if (mMotions[i])
+ mMotions[i]->release();
+ }
+ if (mASFData) mASFData->release();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void Character::resetMotion(PxReal firstFrame)
+{
+ mFrameTime = firstFrame;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool Character::setGlobalPose(const PxTransform &transform)
+{
+ mCharacterPose.p = transform.p;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool Character::setGoalPosition(const PxVec3 pos)
+{
+ mGoalPosition = pos;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool Character::setMotion(PxU32 id, bool init)
+{
+ if (id >= mMotions.size())
+ return false;
+
+ if (init)
+ {
+ mCurrentMotion = mMotions[id];
+ mTargetMotion = NULL;
+
+ computeFramePose();
+ return true;
+ }
+
+ if (mCurrentMotion == NULL)
+ {
+ mCurrentMotion = mMotions[id];
+ return true;
+ }
+
+ if (mCurrentMotion == mMotions[id])
+ return true;
+
+ if (mTargetMotion == mMotions[id])
+ return true;
+
+ mTargetMotion = mMotions[id];
+ mBlendCounter = 10;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool Character::setTargetPosition(const PxVec3 pos)
+{
+ mTargetPosition = pos;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool Character::setForward(void)
+{
+ PxVec3 p = mCharacterPose.p;
+
+ PxVec3 dir = mGoalPosition - p;
+ dir.normalize();
+
+ dir = mCharacterPose.q.rotate(dir);
+
+ PxU32 nbFrames = mCurrentMotion->mNbFrames;
+ PxReal distance = mCurrentMotion->mDistance;
+
+ PxReal frameDelta = distance / PxReal(nbFrames);
+
+ mTargetPosition = p + frameDelta * dir;
+
+ return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+bool
+Skin::bindToCharacter(Character &character, SampleArray<PxVec4> &positions)
+{
+ // currently we just bind everything to the 'thorax' (between neck and clavicles).
+ // Modify this if you need to do more elaborate skin binding
+
+ mBindPos.resize(positions.size());
+ Acclaim::Bone* bone = getBoneFromName(*character.mASFData, "thorax");
+ if (bone == NULL)
+ return false;
+
+ PxTransform boneTransform = computeBoneTransformRest(*bone);
+ PxTransform boneTransformInv = boneTransform.getInverse();
+
+ mBoneID = bone->mID - 1;
+
+ for (PxU32 i = 0; i < positions.size(); i++)
+ {
+ mBindPos[i] = boneTransformInv.transform(
+ reinterpret_cast<const PxVec3&>(positions[i]));
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool
+Skin::computeNewPositions(Character &character, SampleArray<PxVec3> &particlePositions)
+{
+ if (character.mCurrentMotion == NULL)
+ return false;
+
+ PxTransform t = character.mCurrentBoneTransform[mBoneID];
+ particlePositions.resize(mBindPos.size());
+
+ for (PxU32 i = 0; i < mBindPos.size(); i++)
+ particlePositions[i] = t.transform(mBindPos[i]);
+
+ return true;
+}
+
+