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 /APEX_1.4/shared/external/src/SkeletalAnim.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 'APEX_1.4/shared/external/src/SkeletalAnim.cpp')
| -rw-r--r-- | APEX_1.4/shared/external/src/SkeletalAnim.cpp | 1335 |
1 files changed, 1335 insertions, 0 deletions
diff --git a/APEX_1.4/shared/external/src/SkeletalAnim.cpp b/APEX_1.4/shared/external/src/SkeletalAnim.cpp new file mode 100644 index 00000000..2e969776 --- /dev/null +++ b/APEX_1.4/shared/external/src/SkeletalAnim.cpp @@ -0,0 +1,1335 @@ +/* + * Copyright (c) 2008-2015, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA CORPORATION and its licensors retain all intellectual property + * and proprietary rights in and to this software, 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. + */ + +#include <ApexUsingNamespace.h> +#include <PsFileBuffer.h> +#include <MeshImport.h> +#include <AutoGeometry.h> +#include <PsString.h> + +#include "SkeletalAnim.h" +#include "TriangleMesh.h" +#include <RenderDebugInterface.h> +#include "PsMathUtils.h" + +#include "PxInputDataFromPxFileBuf.h" + +#include <algorithm> + +namespace Samples +{ + +// ------------------------------------------------------------------- +void SkeletalBone::clear() +{ + name = ""; + id = -1; + pose = physx::PxTransform(physx::PxIdentity); + bindWorldPose = physx::PxMat44(physx::PxIdentity); + invBindWorldPose = physx::PxMat44(physx::PxIdentity); + currentWorldPose = physx::PxMat44(physx::PxIdentity); + + scale = physx::PxVec3(1.0f, 1.0f, 1.0f); + parent = -1; + firstChild = -1; + numChildren = 0; + firstVertex = -1; + boneOption = 0; + inflateConvex = 0.0f; + minimalBoneWeight = 0.4f; + numShapes = 0; + selected = false; + allowPrimitives = true; + dirtyParams = false; + manualShapes = false; + isRoot = false; + isRootLock = false; +} + +// ------------------------------------------------------------------- +void BoneKeyFrame::clear() +{ + relPose = physx::PxTransform(physx::PxIdentity); + time = 0.0f; + scale = physx::PxVec3(1.0f, 1.0f, 1.0f); +} + +// ------------------------------------------------------------------- +void BoneTrack::clear() +{ + firstFrame = -1; + numFrames = 0; +} + +// ------------------------------------------------------------------- +void SkeletalAnimation::clear() +{ + name = ""; + mBoneTracks.clear(); + minTime = 0.0f; + maxTime = 0.0f; +}; + +// ------------------------------------------------------------------- +SkeletalAnim::SkeletalAnim() +{ + clear(); +} + +// ------------------------------------------------------------------- +SkeletalAnim::~SkeletalAnim() +{ + clear(); +} + +// ------------------------------------------------------------------- +void SkeletalAnim::clear() +{ + ragdollMode = false; + + mBones.clear(); + mBones.resize(0); + + for (uint32_t i = 0; i < mAnimations.size(); i++) + { + delete mAnimations[i]; + } + + mAnimations.clear(); + mAnimations.resize(0); + + mKeyFrames.clear(); + mKeyFrames.resize(0); + + mSkinningMatrices.clear(); + mSkinningMatrices.resize(0); + mSkinningMatricesWorld.clear(); + mSkinningMatricesWorld.resize(0); + + mParent = NULL; +} + +// ------------------------------------------------------------------- +int SkeletalAnim::findBone(const std::string& name) +{ + for (uint32_t i = 0; i < mBones.size(); i++) + { + if (mBones[i].name == name) + { + return (int)i; + } + } + return -1; +} + +// ------------------------------------------------------------------- +void SkeletalAnim::interpolateBonePose(int animNr, int boneNr, float time, physx::PxTransform& pose, physx::PxVec3& scale) +{ + // the default + pose = physx::PxTransform(physx::PxIdentity); + scale = physx::PxVec3(1.0f, 1.0f, 1.0f); + + const std::vector<SkeletalAnimation*>& animations = mParent == NULL ? mAnimations : mParent->mAnimations; + const std::vector<BoneKeyFrame>& keyFrames = mParent == NULL ? mKeyFrames : mParent->mKeyFrames; + + if (animNr < 0 || animNr >= (int)animations.size()) + { + return; + } + if (boneNr < 0 || boneNr >= (int)animations[(uint32_t)animNr]->mBoneTracks.size()) + { + return; + } + + BoneTrack& t = animations[(uint32_t)animNr]->mBoneTracks[(uint32_t)boneNr]; + if (t.numFrames == 0) + { + return; + } + + // special cases + int frameNr = -1; + if (t.numFrames == 1) + { + frameNr = t.firstFrame; + } + else if (time <= keyFrames[(uint32_t)t.firstFrame].time) + { + frameNr = t.firstFrame; + } + else if (time >= keyFrames[uint32_t(t.firstFrame + t.numFrames - 1)].time) + { + frameNr = t.firstFrame + t.numFrames - 1; + } + + if (frameNr >= 0) + { + pose = keyFrames[(uint32_t)frameNr].relPose; + scale = keyFrames[(uint32_t)frameNr].scale; + return; + } + // binary search + uint32_t l = (uint32_t)t.firstFrame; + uint32_t r = uint32_t(t.firstFrame + t.numFrames - 1); + while (r > l + 1) + { + uint32_t m = (l + r) / 2; + if (keyFrames[m].time == time) + { + pose = keyFrames[m].relPose; + scale = keyFrames[m].scale; + return; + } + else if (keyFrames[m].time > time) + { + r = m; + } + else + { + l = m; + } + } + float dt = keyFrames[r].time - keyFrames[l].time; + // avoid singular case + if (dt == 0.0f) + { + pose = keyFrames[l].relPose; + scale = keyFrames[l].scale; + } + + // interpolation + float sr = (time - keyFrames[l].time) / dt; + float sl = 1.0f - sr; + + scale = keyFrames[l].scale * sl + keyFrames[r].scale * sr; + pose.p = keyFrames[l].relPose.p * sl + keyFrames[r].relPose.p * sr; + pose.q = physx::shdfnd::slerp(sr, keyFrames[l].relPose.q, keyFrames[r].relPose.q); +} + +// ------------------------------------------------------------------- +void SkeletalAnim::setBindPose() +{ + PX_ASSERT(mBones.size() == mSkinningMatrices.size()); + PX_ASSERT(mBones.size() == mSkinningMatricesWorld.size()); + for (uint32_t i = 0; i < mBones.size(); i++) + { + mSkinningMatrices[i] = physx::PxMat44(physx::PxIdentity); + mBones[i].currentWorldPose = mBones[i].bindWorldPose; + mSkinningMatricesWorld[i] = mBones[i].currentWorldPose; + } +} + +// ------------------------------------------------------------------- +void SkeletalAnim::setAnimPose(int animNr, float time, bool lockRootbone /* = false */) +{ + if (animNr >= 0) + { + for (uint32_t i = 0; i < mBones.size(); i++) + { + if (mBones[i].parent < 0) + { + setAnimPoseRec(animNr, (int)i, time, lockRootbone); + } + } + + PX_ASSERT(mBones.size() == mSkinningMatrices.size()); + PX_ASSERT(mBones.size() == mSkinningMatricesWorld.size()); + for (uint32_t i = 0; i < mBones.size(); i++) + { + SkeletalBone& b = mBones[i]; + mSkinningMatrices[i] = b.currentWorldPose * b.invBindWorldPose; + mSkinningMatricesWorld[i] = b.currentWorldPose; + } + } + else + { + for (uint32_t i = 0; i < mBones.size(); i++) + { + mSkinningMatrices[i] = physx::PxMat44(physx::PxIdentity); + mSkinningMatricesWorld[i] = mBones[i].bindWorldPose; + } + } +} + +// ------------------------------------------------------------------- +void SkeletalAnim::setBoneCollision(uint32_t boneNr, int option) +{ + if (mBones[boneNr].boneOption != option) + { + if (mBones[boneNr].boneOption == HACD::BO_COLLAPSE || option == HACD::BO_COLLAPSE) + { + // PH: Follow up the hierarchy until something is not set to collapse and mark all dirty + int current = mBones[boneNr].parent; + while (current != -1) + { + mBones[(uint32_t)current].dirtyParams = true; + if (mBones[(uint32_t)current].boneOption != HACD::BO_COLLAPSE) + { + break; + } + + if (mBones[(uint32_t)current].parent == current) + { + break; + } + + current = mBones[(uint32_t)current].parent; + } + } + mBones[boneNr].dirtyParams = true; + + // Find all children that collapse into this bone and mark them dirty + for (uint32_t i = 0; i < mBones.size(); i++) + { + // See whether boneNr is one of its parents + bool found = false; + uint32_t current = i; + while (current != (uint32_t)-1 && !found) + { + if (current == boneNr) + { + found = true; + } + + if (mBones[current].boneOption != HACD::BO_COLLAPSE) + { + break; + } + + if ((int)current == mBones[current].parent) + { + break; + } + + current = (uint32_t)mBones[current].parent; + } + if (found) + { + mBones[i].dirtyParams = true; + } + } + } + + mBones[boneNr].boneOption = option; +} + +// ------------------------------------------------------------------- +void SkeletalAnim::setAnimPoseRec(int animNr, int boneNr, float time, bool lockBoneTranslation) +{ + SkeletalBone& b = mBones[(uint32_t)boneNr]; + + { + physx::PxTransform keyPose; + physx::PxVec3 keyScale; + + // query the first frame instead of the current one if you want to lock this bone + float myTime = (lockBoneTranslation && b.isRootLock) ? 0.0f : time; + interpolateBonePose(animNr, boneNr, myTime, keyPose, keyScale); + + // todo: consider scale + physx::PxTransform combinedPose; + combinedPose.p = b.pose.p + keyPose.p; + combinedPose.q = b.pose.q * keyPose.q; + + if (b.parent < 0) + { + b.currentWorldPose = combinedPose; + } + else + { + b.currentWorldPose = mBones[(uint32_t)b.parent].currentWorldPose * combinedPose; + } + } + + const int* children = mParent == NULL ? &mChildren[0] : &mParent->mChildren[0]; + for (int i = b.firstChild; i < b.firstChild + b.numChildren; i++) + { + setAnimPoseRec(animNr, children[i], time, lockBoneTranslation); + } +} + +// ------------------------------------------------------------------- +bool SkeletalAnim::loadFromMeshImport(mimp::MeshSystemContainer* msc, std::string& error, bool onlyAddAnimation) +{ + bool ret = false; + + if (!onlyAddAnimation) + { + mBones.clear(); + mSkinningMatrices.clear(); + mSkinningMatricesWorld.clear(); + + for (unsigned int i = 0; i < mAnimations.size(); i++) + { + SkeletalAnimation* a = mAnimations[i]; + delete a; + } + mAnimations.clear(); + } + + bool addAnimation = true; + + if (msc) + { + mimp::MeshSystem* ms = mimp::gMeshImport->getMeshSystem(msc); + if (onlyAddAnimation && ms->mSkeletonCount > 0) + { + std::vector<int> overwriteBindPose(ms->mSkeletons[0]->mBoneCount, -1); + // figure out how those bones map to each other. + int numNotEqual = 0; + int numNonZeroMatches = 0; + for (int i = 0; i < ms->mSkeletons[0]->mBoneCount; i++) + { + mimp::MeshBone& bone = ms->mSkeletons[0]->mBones[i]; + for (size_t j = 0; j < mBones.size(); j++) + { + if (mBones[j].name.compare(ms->mSkeletons[0]->mBones[i].mName) == 0) + { + // found one, but let's see if the bind pose also matches more or less + physx::PxTransform inputPose; + inputPose.p = *(physx::PxVec3*)bone.mPosition; + inputPose.q = *(physx::PxQuat*)bone.mOrientation; + + + physx::PxTransform pose(inputPose); + + physx::PxTransform poseOld(mBones[j].pose); + + bool equal = (pose.p - poseOld.p).magnitude() <= ((0.5f * pose.p + 0.5f * poseOld.p).magnitude() * 0.01f); + + if (equal && !pose.p.isZero()) + { + numNonZeroMatches++; + } + + if (!equal && (inputPose.p.isZero() || numNonZeroMatches > 0)) + { + // identity, skip the new bone's bind pose + continue; + } + + if (!equal) + { + char buf[128]; + physx::shdfnd::snprintf(buf, 128, "Bone %d (%s) does not match bind pose\n", (int)i, ms->mSkeletons[0]->mBones[i].mName); + error.append(buf); + + numNotEqual++; + } + + overwriteBindPose[(uint32_t)i] = (int)j; + break; + } + } + } + + if (numNotEqual > 0) + { + error = std::string("Failed to load animation:\n") + error; + addAnimation = false; + } + else + { + // reset all bind poses exactly, now that we know they match pretty well + for (uint32_t i = 0; i < (uint32_t)ms->mSkeletons[0]->mBoneCount; i++) + { + mimp::MeshBone& bone = ms->mSkeletons[0]->mBones[i]; + + if (overwriteBindPose[i] != -1) + { + physx::PxTransform inputPose; + inputPose.p = *(physx::PxVec3*)bone.mPosition; + inputPose.q = *(physx::PxQuat*)bone.mOrientation; + physx::PxTransform pose(inputPose); + + mBones[(uint32_t)overwriteBindPose[i]].pose = pose; + } + } + } + } + else if (ms->mSkeletonCount) + { + mimp::MeshSkeleton* sk = ms->mSkeletons[0]; + + for (int i = 0; i < sk->mBoneCount; i++) + { + mimp::MeshBone& b = sk->mBones[i]; + SkeletalBone sb; + sb.clear(); + sb.name = b.mName; + sb.id = i; + sb.parent = b.mParentIndex; + sb.firstChild = 0; + sb.numChildren = 0; + sb.isRootLock = b.mParentIndex == 0; // lock the second bone in the hierarchy, first one is the scene root, not the anim root (for fbx files) + + PxQuatFromArray(sb.pose.q, b.mOrientation); + PxVec3FromArray(sb.pose.p, b.mPosition); + PxVec3FromArray(sb.scale, b.mScale); + + for (uint32_t bi = 0; bi < mBones.size(); bi++) + { + if (mBones[bi].name == b.mName) + { + if (error.empty()) + { + error = "Duplicated Bone Names, rename one:\n"; + } + + error.append(b.mName); + error.append("\n"); + } + } + + mBones.push_back(sb); + } + + ret = true; // allow loading a skeleton without animation + } + + if (ms->mAnimationCount && addAnimation) + { + for (unsigned int i = 0; i < ms->mAnimationCount; i++) + { + mimp::MeshAnimation* animation = ms->mAnimations[i]; + SkeletalAnimation* anim = new SkeletalAnimation; + anim->clear(); + if (ms->mAnimationCount == 1 && ms->mAssetName != NULL) + { + const char* lastDir = std::max(strrchr(ms->mAssetName, '/'), strrchr(ms->mAssetName, '\\')); + anim->name = lastDir != NULL ? lastDir + 1 : ms->mAssetName; + } + else + { + anim->name = animation->mName; + } + + size_t numBones = mBones.size(); + anim->mBoneTracks.resize(numBones); + for (uint32_t j = 0; j < numBones; j++) + { + anim->mBoneTracks[j].clear(); + } + + for (uint32_t j = 0; j < (uint32_t)animation->mTrackCount; j++) + { + mimp::MeshAnimTrack* track = animation->mTracks[j]; + std::string boneName = track->mName; + int boneNr = findBone(boneName); + if (boneNr >= 0 && boneNr < (int32_t)numBones) + { + anim->mBoneTracks[(uint32_t)boneNr].firstFrame = (int)mKeyFrames.size(); + anim->mBoneTracks[(uint32_t)boneNr].numFrames = track->mFrameCount; + + physx::PxTransform parent = mBones[(uint32_t)boneNr].pose; + + float ftime = 0; + + for (uint32_t k = 0; k < (uint32_t)track->mFrameCount; k++) + { + mimp::MeshAnimPose& pose = track->mPose[k]; + BoneKeyFrame frame; + frame.clear(); + + physx::PxTransform mat; + PxQuatFromArray(mat.q, pose.mQuat); + PxVec3FromArray(mat.p, pose.mPos); + + frame.time = ftime; + PxVec3FromArray(frame.scale, pose.mScale); + + frame.relPose.p = mat.p - parent.p; + frame.relPose.q = parent.q.getConjugate() * mat.q; + + mKeyFrames.push_back(frame); + + // eazymesh samples at 60 Hz, not 1s + ftime += track->mDtime / 200.f; + } + } + else if (!onlyAddAnimation) + { + // if onlyAddAnimation is set, the bone count does not have to match up, additional bones are just ignored + PX_ASSERT(0); + } + + } + + mAnimations.push_back(anim); + } + + ret = true; + } + + physx::PxTransform matId(physx::PxIdentity); + mSkinningMatrices.resize((uint32_t)mBones.size(), matId); + mSkinningMatricesWorld.resize((uint32_t)mBones.size(), matId); + init(!onlyAddAnimation); + } + + return ret; +} + +// ------------------------------------------------------------------- +bool SkeletalAnim::saveToMeshImport(mimp::MeshSystemContainer* msc) +{ +#if PX_WINDOWS_FAMILY == 0 + PX_UNUSED(msc); + return false; +#else + + if (msc == NULL) + { + return false; + } + + mimp::MeshSystem* ms = mimp::gMeshImport->getMeshSystem(msc); + if (ms == NULL) + { + return false; + } + + ms->mSkeletonCount = 1; + ms->mSkeletons = (mimp::MeshSkeleton**)::malloc(sizeof(mimp::MeshSkeleton*)); + ms->mSkeletons[0] = new mimp::MeshSkeleton; + + ms->mSkeletons[0]->mBoneCount = (int)mBones.size(); + ms->mSkeletons[0]->mBones = (mimp::MeshBone*)::malloc(sizeof(mimp::MeshBone) * mBones.size()); + for (size_t i = 0; i < mBones.size(); i++) + { + mimp::MeshBone& bone = ms->mSkeletons[0]->mBones[i]; + + size_t nameLen = mBones[i].name.length() + 1; + bone.mName = (char*)::malloc(sizeof(char) * nameLen); + strcpy_s((char*)bone.mName, nameLen, mBones[i].name.c_str()); + + (physx::PxQuat&)bone.mOrientation = mBones[i].pose.q; + + bone.mParentIndex = mBones[i].parent; + + (physx::PxVec3&)bone.mPosition = mBones[i].pose.p; + + (physx::PxVec3&)bone.mScale = mBones[i].scale; + } + + ms->mAnimationCount = (unsigned int)mAnimations.size(); + ms->mAnimations = (mimp::MeshAnimation**)::malloc(sizeof(mimp::MeshAnimation*) * mAnimations.size()); + for (unsigned int a = 0; a < ms->mAnimationCount; a++) + { + ms->mAnimations[a] = new mimp::MeshAnimation; + + PX_ASSERT(mAnimations[a] != NULL); + size_t nameLen = mAnimations[a]->name.length() + 1; + ms->mAnimations[a]->mName = (char*)::malloc(sizeof(char) * nameLen); + strcpy_s((char*)ms->mAnimations[a]->mName, nameLen, mAnimations[a]->name.c_str()); + + unsigned int trackCount = 0; + for (size_t i = 0; i < mBones.size(); i++) + { + trackCount += mAnimations[a]->mBoneTracks[i].numFrames > 0 ? 1 : 0; + } + + ms->mAnimations[a]->mTrackCount = (int32_t)trackCount; + ms->mAnimations[a]->mTracks = (mimp::MeshAnimTrack**)::malloc(sizeof(mimp::MeshAnimTrack*) * trackCount); + ms->mAnimations[a]->mDuration = 0.0f; + ms->mAnimations[a]->mDtime = 0.0f; + + unsigned int curTrack = 0; + for (size_t t = 0; t < mBones.size(); t++) + { + if (mAnimations[a]->mBoneTracks[t].numFrames <= 0) + { + continue; + } + + mimp::MeshAnimTrack* track = ms->mAnimations[a]->mTracks[curTrack++] = new mimp::MeshAnimTrack; + + track->mName = ms->mSkeletons[0]->mBones[t].mName; // just use the same name as the bone array already does + const unsigned int firstFrame = (uint32_t)mAnimations[a]->mBoneTracks[t].firstFrame; + track->mFrameCount = mAnimations[a]->mBoneTracks[t].numFrames; + ms->mAnimations[a]->mFrameCount = std::max(ms->mAnimations[a]->mFrameCount, track->mFrameCount); + + track->mPose = (mimp::MeshAnimPose*)::malloc(sizeof(mimp::MeshAnimPose) * track->mFrameCount); + + track->mDuration = 0.0f; + + for (int f = 0; f < track->mFrameCount; f++) + { + mimp::MeshAnimPose& pose = track->mPose[f]; + BoneKeyFrame& frame = mKeyFrames[firstFrame + f]; + + physx::PxTransform mat; + + mat.q = frame.relPose.q * mBones[t].pose.q; + mat.p = frame.relPose.p + mBones[t].pose.p; + + (physx::PxVec3&)pose.mScale = frame.scale; + (physx::PxVec3&)pose.mPos = mat.p; + + (physx::PxQuat&)pose.mQuat = mat.q; + + track->mDuration = std::max(track->mDuration, frame.time); + } + + track->mDtime = track->mDuration / (float)track->mFrameCount * 200.0f; + + ms->mAnimations[a]->mDuration = std::max(ms->mAnimations[a]->mDuration, track->mDuration); + ms->mAnimations[a]->mDtime = std::max(ms->mAnimations[a]->mDtime, track->mDtime); + } + } + + return true; +#endif +} + +// ------------------------------------------------------------------- +bool SkeletalAnim::initFrom(nvidia::apex::RenderMeshAssetAuthoring& mesh) +{ + PX_ASSERT(mesh.getPartCount() == 1); + + uint32_t numBones = 0; + for (uint32_t submeshIndex = 0; submeshIndex < mesh.getSubmeshCount(); submeshIndex++) + { + const nvidia::VertexBuffer& vb = mesh.getSubmesh(submeshIndex).getVertexBuffer(); + const nvidia::VertexFormat& vf = vb.getFormat(); + uint32_t bufferIndex = (uint32_t)vf.getBufferIndexFromID(vf.getSemanticID(nvidia::apex::RenderVertexSemantic::BONE_INDEX)); + + nvidia::apex::RenderDataFormat::Enum format; + const uint16_t* boneIndices = (const uint16_t*)vb.getBufferAndFormat(format, bufferIndex); + + unsigned int numBonesPerVertex = 0; + switch (format) + { + case nvidia::apex::RenderDataFormat::USHORT1: + numBonesPerVertex = 1; + break; + case nvidia::apex::RenderDataFormat::USHORT2: + numBonesPerVertex = 2; + break; + case nvidia::apex::RenderDataFormat::USHORT3: + numBonesPerVertex = 3; + break; + case nvidia::apex::RenderDataFormat::USHORT4: + numBonesPerVertex = 4; + break; + default: + break; + } + + if (boneIndices == NULL || numBonesPerVertex == 0) + { + return false; + } + + const unsigned int numElements = numBonesPerVertex * vb.getVertexCount(); + + for (unsigned int i = 0; i < numElements; i++) + { + numBones = std::max(numBones, boneIndices[i] + 1u); + } + } + + SkeletalBone initBone; + initBone.clear(); + mBones.resize(numBones, initBone); + + for (unsigned int i = 0; i < numBones; i++) + { + mBones[i].id = (int32_t)i; + } + + mSkinningMatrices.resize(numBones); + mSkinningMatricesWorld.resize(numBones); + + init(true); + + return numBones > 0; +} + +// ------------------------------------------------------------------- +bool SkeletalAnim::loadFromXML(const std::string& xmlFile, std::string& error) +{ + clear(); + + physx::PsFileBuffer fb(xmlFile.c_str(), physx::PxFileBuf::OPEN_READ_ONLY); + physx::PxInputDataFromPxFileBuf id(fb); + + if (!fb.isOpen()) + { + return false; + } + + physx::shdfnd::FastXml* fastXml = physx::shdfnd::createFastXml(this); + fastXml->processXml(id); + + int errorLineNumber = -1; + const char* xmlError = fastXml->getError(errorLineNumber); + if (xmlError != NULL) + { + char temp[1024]; + physx::shdfnd::snprintf(temp, 1024, "Xml parse error in %s on line %d:\n\n%s", xmlFile.c_str(), errorLineNumber, xmlError); + error = temp; + return false; + } + + fastXml->release(); + + physx::PxTransform matId(physx::PxIdentity); + mSkinningMatrices.resize((uint32_t)mBones.size(), matId); + mSkinningMatricesWorld.resize((uint32_t)mBones.size(), matId); + + init(true); + return true; +} + +// ------------------------------------------------------------------- +bool SkeletalAnim::loadFromParent(const SkeletalAnim* parent) +{ + if (parent == NULL) + { + return false; + } + + mParent = parent; + + mBones.resize(mParent->mBones.size()); + physx::PxTransform matId(physx::PxIdentity); + mSkinningMatrices.resize((uint32_t)mBones.size(), matId); + mSkinningMatricesWorld.resize((uint32_t)mBones.size(), matId); + for (uint32_t i = 0; i < mBones.size(); i++) + { + mBones[i] = mParent->mBones[i]; + } + + return true; +} + +// ------------------------------------------------------------------- +bool SkeletalAnim::saveToXML(const std::string& xmlFile) const +{ +#if PX_WINDOWS_FAMILY == 0 + PX_UNUSED(xmlFile); + return false; +#else + FILE* f = 0; + if (::fopen_s(&f, xmlFile.c_str(), "w") != 0) + { + return false; + } + + fprintf(f, "<skeleton>\n\n"); + + fprintf(f, " <bones>\n"); + for (uint32_t i = 0; i < mBones.size(); i++) + { + const SkeletalBone& bone = mBones[i]; + + float angle; + physx::PxVec3 axis; + bone.pose.q.toRadiansAndUnitAxis(angle, axis); + angle = bone.pose.q.getAngle(); + + fprintf(f, " <bone id = \"%i\" name = \"%s\">\n", bone.id, bone.name.c_str()); + fprintf(f, " <position x=\"%f\" y=\"%f\" z=\"%f\" />\n", bone.pose.p.x, bone.pose.p.y, bone.pose.p.z); + fprintf(f, " <rotation angle=\"%f\">\n", angle); + fprintf(f, " <axis x=\"%f\" y=\"%f\" z=\"%f\" />\n", axis.x, axis.y, axis.z); + fprintf(f, " </rotation>\n"); + fprintf(f, " <scale x=\"%f\" y=\"%f\" z=\"%f\" />\n", 1.0f, 1.0f, 1.0f); +// dont' use bone.scale.x, bone.scale.y, bone.scale.z because the length is baked into the bones + fprintf(f, " </bone>\n"); + } + fprintf(f, " </bones>\n\n"); + + fprintf(f, " <bonehierarchy>\n"); + for (uint32_t i = 0; i < mBones.size(); i++) + { + const SkeletalBone& bone = mBones[i]; + if (bone.parent < 0) + { + continue; + } + fprintf(f, " <boneparent bone=\"%s\" parent=\"%s\" />\n", bone.name.c_str(), mBones[(uint32_t)bone.parent].name.c_str()); + } + fprintf(f, " </bonehierarchy>\n\n"); + + fprintf(f, " <animations>\n"); + for (uint32_t i = 0; i < mAnimations.size(); i++) + { + const SkeletalAnimation* anim = mAnimations[i]; + + fprintf(f, " <animation name = \"%s\" length=\"%f\">\n", anim->name.c_str(), anim->maxTime); + fprintf(f, " <tracks>\n"); + + for (uint32_t j = 0; j < anim->mBoneTracks.size(); j++) + { + const BoneTrack& track = anim->mBoneTracks[j]; + if (track.numFrames == 0) + { + continue; + } + + fprintf(f, " <track bone = \"%s\">\n", mBones[j].name.c_str()); + fprintf(f, " <keyframes>\n"); + + for (int k = track.firstFrame; k < track.firstFrame + track.numFrames; k++) + { + const BoneKeyFrame& frame = mKeyFrames[(uint32_t)k]; + float angle; + physx::PxVec3 axis; + frame.relPose.q.toRadiansAndUnitAxis(angle, axis); + angle = frame.relPose.q.getAngle(); + + fprintf(f, " <keyframe time = \"%f\">\n", frame.time); + fprintf(f, " <translate x=\"%f\" y=\"%f\" z=\"%f\" />\n", frame.relPose.p.x, frame.relPose.p.y, frame.relPose.p.z); + fprintf(f, " <rotate angle=\"%f\">\n", angle); + fprintf(f, " <axis x=\"%f\" y=\"%f\" z=\"%f\" />\n", axis.x, axis.y, axis.z); + fprintf(f, " </rotate>\n"); + fprintf(f, " <scale x=\"%f\" y=\"%f\" z=\"%f\" />\n", frame.scale.x, frame.scale.y, frame.scale.z); + fprintf(f, " </keyframe>\n"); + } + fprintf(f, " </keyframes>\n"); + fprintf(f, " </track>\n"); + } + fprintf(f, " </tracks>\n"); + fprintf(f, " </animation>\n"); + } + fprintf(f, " </animations>\n"); + fprintf(f, "</skeleton>\n\n"); + + fclose(f); + + + return true; +#endif +} + +// ------------------------------------------------------------------- +void SkeletalAnim::init(bool firstTime) +{ + if (firstTime) + { + setupConnectivity(); + + // init bind poses + physx::PxVec3 oneOneOne(1.0f, 1.0f, 1.0f); + for (uint32_t i = 0; i < mBones.size(); i++) + { + if (mBones[i].parent < 0) + { + initBindPoses((int32_t)i, oneOneOne); + } + + // collapse finger and toes + if ( + mBones[i].name.find("finger") != std::string::npos || + mBones[i].name.find("Finger") != std::string::npos || + mBones[i].name.find("FINGER") != std::string::npos || + mBones[i].name.find("toe") != std::string::npos || + mBones[i].name.find("Toe") != std::string::npos || + mBones[i].name.find("TOE") != std::string::npos) + { + mBones[i].boneOption = 2; // this is collapse + } + } + } + + PX_ASSERT(mBones.size() == mSkinningMatrices.size()); + PX_ASSERT(mBones.size() == mSkinningMatricesWorld.size()); + for (uint32_t i = 0; i < mBones.size(); i++) + { + SkeletalBone& b = mBones[i]; + b.invBindWorldPose = b.bindWorldPose.inverseRT(); + b.currentWorldPose = mBones[i].bindWorldPose; + mSkinningMatrices[i] = physx::PxMat44(physx::PxIdentity); + mSkinningMatricesWorld[i] = b.currentWorldPose; + } + + // init time interval of animations + for (uint32_t i = 0; i < mAnimations.size(); i++) + { + SkeletalAnimation* a = mAnimations[i]; + bool first = true; + for (uint32_t j = 0; j < a->mBoneTracks.size(); j++) + { + BoneTrack& b = a->mBoneTracks[j]; + for (int k = b.firstFrame; k < b.firstFrame + b.numFrames; k++) + { + float time = mKeyFrames[(uint32_t)k].time; + if (first) + { + a->minTime = time; + a->maxTime = time; + first = false; + } + else + { + if (time < a->minTime) + { + a->minTime = time; + } + if (time > a->maxTime) + { + a->maxTime = time; + } + } + } + } + } +} + +// ------------------------------------------------------------------- +void SkeletalAnim::initBindPoses(int boneNr, const physx::PxVec3& scale) +{ + SkeletalBone& b = mBones[(uint32_t)boneNr]; + b.pose.p = b.pose.p.multiply(scale); + + physx::PxVec3 newScale = scale.multiply(b.scale); + + if (b.parent < 0) + { + b.bindWorldPose = b.pose; + } + else + { + b.bindWorldPose = mBones[(uint32_t)b.parent].bindWorldPose * b.pose; + } + + for (int i = b.firstChild; i < b.firstChild + b.numChildren; i++) + { + initBindPoses(mChildren[(uint32_t)i], newScale); + } +} + +// ------------------------------------------------------------------- +void SkeletalAnim::setupConnectivity() +{ + size_t i; + size_t numBones = mBones.size(); + for (i = 0; i < numBones; i++) + { + SkeletalBone& b = mBones[i]; + if (b.parent >= 0) + { + mBones[(uint32_t)b.parent].numChildren++; + } + } + int first = 0; + for (i = 0; i < numBones; i++) + { + mBones[i].firstChild = first; + first += mBones[i].numChildren; + } + mChildren.resize((uint32_t)first); + for (i = 0; i < numBones; i++) + { + if (mBones[i].parent < 0) + { + continue; + } + SkeletalBone& p = mBones[(uint32_t)mBones[i].parent]; + mChildren[(uint32_t)p.firstChild++] = (int)i; + } + for (i = 0; i < numBones; i++) + { + mBones[i].firstChild -= mBones[i].numChildren; + } +} + +// ------------------------------------------------------------------- +void SkeletalAnim::draw(nvidia::RenderDebugInterface* batcher) +{ + PX_ASSERT(batcher != NULL); + if (batcher == NULL) + { + return; + } + + using RENDER_DEBUG::DebugColors; + const uint32_t colorWhite = RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::White); + const uint32_t colorBlack = RENDER_DEBUG_IFACE(batcher)->getDebugColor(DebugColors::Black); + for (uint32_t i = 0; i < mBones.size(); i++) + { + SkeletalBone& bone = mBones[i]; + + uint32_t color = bone.selected ? colorWhite : colorBlack; + RENDER_DEBUG_IFACE(batcher)->setCurrentColor(color); + + if (bone.parent >= 0 /*&& mBones[bone.parent].parent >= 0*/) + { + SkeletalBone& parent = mBones[(uint32_t)bone.parent]; + + RENDER_DEBUG_IFACE(batcher)->debugLine(bone.currentWorldPose.getPosition(), parent.currentWorldPose.getPosition()); + } + } +} + +// ------------------------------------------------------------------- +void SkeletalAnim::copyFrom(const SkeletalAnim& anim) +{ + clear(); + + mBones.resize(anim.mBones.size()); + for (uint32_t i = 0; i < anim.mBones.size(); i++) + { + mBones[i] = anim.mBones[i]; + } + + mSkinningMatrices.resize(anim.mSkinningMatrices.size()); + for (uint32_t i = 0; i < anim.mSkinningMatrices.size(); i++) + { + mSkinningMatrices[i] = anim.mSkinningMatrices[i]; + } + + mSkinningMatricesWorld.resize(anim.mSkinningMatricesWorld.size()); + for (uint32_t i = 0; i < anim.mSkinningMatricesWorld.size(); i++) + { + mSkinningMatricesWorld[i] = anim.mSkinningMatricesWorld[i]; + } + + mChildren.resize(anim.mChildren.size()); + for (uint32_t i = 0; i < anim.mChildren.size(); i++) + { + mChildren[i] = anim.mChildren[i]; + } + + for (uint32_t i = 0; i < anim.mAnimations.size(); i++) + { + SkeletalAnimation* a = anim.mAnimations[i]; + SkeletalAnimation* na = new SkeletalAnimation(); + na->minTime = a->minTime; + na->maxTime = a->maxTime; + na->name = a->name; + na->mBoneTracks.resize(a->mBoneTracks.size()); + for (uint32_t j = 0; j < a->mBoneTracks.size(); j++) + { + na->mBoneTracks[j] = a->mBoneTracks[j]; + } + mAnimations.push_back(na); + } + + mKeyFrames.resize(anim.mKeyFrames.size()); + for (uint32_t i = 0; i < anim.mKeyFrames.size(); i++) + { + mKeyFrames[i] = anim.mKeyFrames[i]; + } +} + + +// ------------------------------------------------------------------- +void SkeletalAnim::clearShapeCount(int boneIndex) +{ + if (boneIndex < 0) + { + for (uint32_t i = 0; i < mBones.size(); i++) + { + mBones[i].numShapes = 0; + } + } + else + { + PX_ASSERT((uint32_t)boneIndex < mBones.size()); + mBones[(uint32_t)boneIndex].numShapes = 0; + } +} + +// ------------------------------------------------------------------- +void SkeletalAnim::incShapeCount(int boneIndex) +{ + if (boneIndex >= 0 && (uint32_t)boneIndex < mBones.size()) + { + mBones[(uint32_t)boneIndex].numShapes++; + } +} + +// ------------------------------------------------------------------- +void SkeletalAnim::decShapeCount(int boneIndex) +{ + if (boneIndex >= 0 && (uint32_t)boneIndex < mBones.size()) + { + PX_ASSERT(mBones[(uint32_t)boneIndex].numShapes > 0); + mBones[(uint32_t)boneIndex].numShapes--; + } +} +// ------------------------------------------------------------------- +bool SkeletalAnim::processElement(const char* elementName, const char* /*elementData*/, const physx::shdfnd::FastXml::AttributePairs& attr, int /*lineno*/) + +{ + static int activeBoneTrack = -1; + static BoneKeyFrame* activeKeyFrame; + static bool isAnimation = false; + + if (::strcmp(elementName, "skeleton") == 0) + { + // ok, a start + } + else if (::strcmp(elementName, "bones") == 0) + { + // the list of bones + } + else if (::strcmp(elementName, "bone") == 0) + { + PX_ASSERT(attr.getNbAttr() == 2); + PX_ASSERT(::strcmp(attr.getKey(0), "id") == 0); + PX_ASSERT(::strcmp(attr.getKey(1), "name") == 0); + SkeletalBone bone; + bone.clear(); + bone.id = atoi(attr.getValue(0)); + bone.name = attr.getValue(1); + mBones.push_back(bone); + } + else if (::strcmp(elementName, "position") == 0) + { + PX_ASSERT(attr.getNbAttr() == 3); + PX_ASSERT(::strcmp(attr.getKey(0), "x") == 0); + PX_ASSERT(::strcmp(attr.getKey(1), "y") == 0); + PX_ASSERT(::strcmp(attr.getKey(2), "z") == 0); + physx::PxVec3 pos; + pos.x = (float)atof(attr.getValue(0)); + pos.y = (float)atof(attr.getValue(1)); + pos.z = (float)atof(attr.getValue(2)); + mBones.back().pose.p = pos; + } + else if (::strcmp(elementName, "rotation") == 0) + { + PX_ASSERT(attr.getNbAttr() == 1); + PX_ASSERT(::strcmp(attr.getKey(0), "angle") == 0); + mBones.back().pose.q = physx::PxQuat((float)atof(attr.getValue(0))); + isAnimation = false; + } + else if (::strcmp(elementName, "axis") == 0 && !isAnimation) + { + PX_ASSERT(attr.getNbAttr() == 3); + PX_ASSERT(::strcmp(attr.getKey(0), "x") == 0); + PX_ASSERT(::strcmp(attr.getKey(1), "y") == 0); + PX_ASSERT(::strcmp(attr.getKey(2), "z") == 0); + physx::PxVec3 axis; + axis.x = (float)atof(attr.getValue(0)); + axis.y = (float)atof(attr.getValue(1)); + axis.z = (float)atof(attr.getValue(2)); + float angle = mBones.back().pose.q.getAngle(); + mBones.back().pose.q = physx::PxQuat(angle, axis); + } + else if (::strcmp(elementName, "scale") == 0) + { + PX_ASSERT(attr.getNbAttr() == 3); + PX_ASSERT(::strcmp(attr.getKey(0), "x") == 0); + PX_ASSERT(::strcmp(attr.getKey(1), "y") == 0); + PX_ASSERT(::strcmp(attr.getKey(2), "z") == 0); + physx::PxVec3 scale; + scale.x = (float)atof(attr.getValue(0)); + scale.y = (float)atof(attr.getValue(1)); + scale.z = (float)atof(attr.getValue(2)); + mBones.back().scale = scale; + } + else if (::strcmp(elementName, "bonehierarchy") == 0) + { + // ok + } + else if (::strcmp(elementName, "boneparent") == 0) + { + PX_ASSERT(attr.getNbAttr() == 2); + PX_ASSERT(::strcmp(attr.getKey(0), "bone") == 0); + PX_ASSERT(::strcmp(attr.getKey(1), "parent") == 0); + int child = findBone(attr.getValue(0)); + int parent = findBone(attr.getValue(1)); + if (child >= 0 && child < (int)mBones.size() && parent >= 0 && parent < (int)mBones.size()) + { + mBones[(uint32_t)child].parent = parent; + } + } + else if (::strcmp(elementName, "animations") == 0) + { + // ok + } + else if (::strcmp(elementName, "animation") == 0) + { + PX_ASSERT(attr.getNbAttr() == 2); + PX_ASSERT(::strcmp(attr.getKey(0), "name") == 0); + + SkeletalAnimation* anim = new SkeletalAnimation; + anim->clear(); + anim->name = attr.getValue(0); + anim->mBoneTracks.resize((uint32_t)mBones.size()); + + mAnimations.push_back(anim); + } + else if (::strcmp(elementName, "tracks") == 0) + { + // ok + } + else if (::strcmp(elementName, "track") == 0) + { + PX_ASSERT(attr.getNbAttr() == 1); + PX_ASSERT(::strcmp(attr.getKey(0), "bone") == 0); + activeBoneTrack = findBone(attr.getValue(0)); + if (activeBoneTrack >= 0 && activeBoneTrack < (int)mBones.size()) + { + mAnimations.back()->mBoneTracks[(uint32_t)activeBoneTrack].firstFrame = (int)(mKeyFrames.size()); + mAnimations.back()->mBoneTracks[(uint32_t)activeBoneTrack].numFrames = 0; + } + } + else if (::strcmp(elementName, "keyframes") == 0) + { + // ok + } + else if (::strcmp(elementName, "keyframe") == 0) + { + PX_ASSERT(attr.getNbAttr() == 1); + PX_ASSERT(::strcmp(attr.getKey(0), "time") == 0); + + mAnimations.back()->mBoneTracks[(uint32_t)activeBoneTrack].numFrames++; + + mKeyFrames.push_back(BoneKeyFrame()); + activeKeyFrame = &mKeyFrames.back(); + activeKeyFrame->clear(); + activeKeyFrame->time = (float)atof(attr.getValue(0)); + } + else if (::strcmp(elementName, "translate") == 0) + { + PX_ASSERT(attr.getNbAttr() == 3); + PX_ASSERT(::strcmp(attr.getKey(0), "x") == 0); + PX_ASSERT(::strcmp(attr.getKey(1), "y") == 0); + PX_ASSERT(::strcmp(attr.getKey(2), "z") == 0); + activeKeyFrame->relPose.p.x = (float)atof(attr.getValue(0)); + activeKeyFrame->relPose.p.y = (float)atof(attr.getValue(1)); + activeKeyFrame->relPose.p.z = (float)atof(attr.getValue(2)); + } + else if (::strcmp(elementName, "rotate") == 0) + { + PX_ASSERT(attr.getNbAttr() == 1); + PX_ASSERT(::strcmp(attr.getKey(0), "angle") == 0); + activeKeyFrame->relPose.q = physx::PxQuat((float)atof(attr.getValue(0))); + isAnimation = true; + } + else if (::strcmp(elementName, "axis") == 0 && isAnimation) + { + PX_ASSERT(attr.getNbAttr() == 3); + PX_ASSERT(::strcmp(attr.getKey(0), "x") == 0); + PX_ASSERT(::strcmp(attr.getKey(1), "y") == 0); + PX_ASSERT(::strcmp(attr.getKey(2), "z") == 0); + physx::PxVec3 axis; + axis.x = (float)atof(attr.getValue(0)); + axis.y = (float)atof(attr.getValue(1)); + axis.z = (float)atof(attr.getValue(2)); + axis.normalize(); + float angle = activeKeyFrame->relPose.q.getAngle(); + physx::PxQuat quat(angle, axis); + activeKeyFrame->relPose.q = quat; + } + else + { + // always break here, at least in debug mode + PX_ALWAYS_ASSERT(); + } + + return true; +} + +} // namespace Samples |