diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Samples/SampleBase/SampleCharacterHelpers.cpp | |
| download | physx-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.cpp | 578 |
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; +} + + |