Pārlūkot izejas kodu

Users can now manually manipulate animated skeleton bones through scene object transforms

BearishSun 9 gadi atpakaļ
vecāks
revīzija
2a084db072

+ 1 - 0
Source/BansheeCore/Include/BsSkeleton.h

@@ -91,6 +91,7 @@ namespace BansheeEngine
 		Vector3* positions; /**< Local bone positions at specific animation time. */
 		Quaternion* rotations; /**< Local bone rotations at specific animation time. */
 		Vector3* scales; /**< Local bone scales at specific animation time. */
+		bool* hasOverride; /**< True if the bone transform was overriden externally (local pose was ignored). */
 		UINT32 numBones; /**< Number of bones in the pose. */
 	};
 

+ 66 - 29
Source/BansheeCore/Source/BsAnimation.cpp

@@ -421,6 +421,19 @@ namespace BansheeEngine
 				assert(layer.numStates > 0);
 			}
 
+			Matrix4 invRootTransform(BsIdentity);
+			for (UINT32 i = 0; i < numSceneObjects; i++)
+			{
+				if(sceneObjects[i].curveName.empty())
+				{
+					HSceneObject so = sceneObjects[i].so;
+					if (!so.isDestroyed(true))
+						invRootTransform = so->getWorldTfrm().inverseAffine();
+
+					break;
+				}				
+			}
+
 			UINT32 boneIdx = 0;
 			for(UINT32 i = 0; i < numSceneObjects; i++)
 			{
@@ -466,7 +479,7 @@ namespace BansheeEngine
 				else
 				{
 					// No need to check if SO is valid, if it has a bone connection it must be
-					sceneObjectTransforms[boneIdx] = so->getWorldTfrm();
+					sceneObjectTransforms[boneIdx] = so->getWorldTfrm() * invRootTransform;
 					boneIdx++;
 				}
 			}
@@ -490,6 +503,19 @@ namespace BansheeEngine
 
 	void AnimationProxy::updateTransforms(const Vector<AnimatedSceneObject>& sceneObjects)
 	{
+		Matrix4 invRootTransform(BsIdentity);
+		for (UINT32 i = 0; i < numSceneObjects; i++)
+		{
+			if (sceneObjects[i].curveName.empty())
+			{
+				HSceneObject so = sceneObjects[i].so;
+				if (!so.isDestroyed(true))
+					invRootTransform = so->getWorldTfrm().inverseAffine();
+
+				break;
+			}
+		}
+
 		UINT32 boneIdx = 0;
 		for (UINT32 i = 0; i < numSceneObjects; i++)
 		{
@@ -505,7 +531,7 @@ namespace BansheeEngine
 			if (sceneObjectInfos[i].boneIdx == -1)
 				continue;
 
-			sceneObjectTransforms[boneIdx] = sceneObjects[i].so->getWorldTfrm();
+			sceneObjectTransforms[boneIdx] = sceneObjects[i].so->getWorldTfrm() * invRootTransform;
 			boneIdx++;
 		}
 	}
@@ -1036,23 +1062,23 @@ namespace BansheeEngine
 			mDirty.unset(AnimDirtyStateFlag::Culling);
 		}
 
+		auto getAnimatedSOList = [&]()
+		{
+			Vector<AnimatedSceneObject> animatedSO(mSceneObjects.size());
+			UINT32 idx = 0;
+			for (auto& entry : mSceneObjects)
+				animatedSO[idx++] = entry.second;
+
+			return animatedSO;
+		};
+
+		bool didFullRebuild = false;
 		if((UINT32)mDirty == 0) // Clean
 		{
 			mAnimProxy->updateTime(mClipInfos);
 		}
 		else
 		{
-			auto getAnimatedSOList = [&]()
-			{
-				Vector<AnimatedSceneObject> animatedSO(mSceneObjects.size());
-				UINT32 idx = 0;
-				for (auto& entry : mSceneObjects)
-					animatedSO[idx++] = entry.second;
-
-				return animatedSO;
-			};
-
-			bool didFullRebuild = false;
 			if (mDirty.isSet(AnimDirtyStateFlag::Skeleton))
 			{
 				Vector<AnimatedSceneObject> animatedSOs = getAnimatedSOList();
@@ -1069,27 +1095,35 @@ namespace BansheeEngine
 			}
 			else if (mDirty.isSet(AnimDirtyStateFlag::Value))
 				mAnimProxy->updateValues(mClipInfos);
+		}
 
-			// Check if there are dirty transforms
-			if(!didFullRebuild)
+		// Check if there are dirty transforms
+		if (!didFullRebuild)
+		{
+			for (UINT32 i = 0; i < mAnimProxy->numSceneObjects; i++)
 			{
-				UINT32 numSceneObjects = (UINT32)mSceneObjects.size();
-				for (UINT32 i = 0; i < numSceneObjects; i++)
+				AnimatedSceneObjectInfo& soInfo = mAnimProxy->sceneObjectInfos[i];
+
+				auto iterFind = mSceneObjects.find(soInfo.id);
+				if (iterFind == mSceneObjects.end())
 				{
-					UINT32 hash;
+					assert(false); // Should never happen
+					continue;
+				}
 
-					HSceneObject so = mSceneObjects[i].so;
-					if (so.isDestroyed(true))
-						hash = 0;
-					else
-						hash = so->getTransformHash();
+				UINT32 hash;
 
-					if(hash != mAnimProxy->sceneObjectInfos[i].hash)
-					{
-						Vector<AnimatedSceneObject> animatedSOs = getAnimatedSOList();
-						mAnimProxy->updateTransforms(animatedSOs);
-						break;
-					}
+				HSceneObject so = iterFind->second.so;
+				if (so.isDestroyed(true))
+					hash = 0;
+				else
+					hash = so->getTransformHash();
+
+				if (hash != mAnimProxy->sceneObjectInfos[i].hash)
+				{
+					Vector<AnimatedSceneObject> animatedSOs = getAnimatedSOList();
+					mAnimProxy->updateTransforms(animatedSOs);
+					break;
 				}
 			}
 		}
@@ -1119,6 +1153,9 @@ namespace BansheeEngine
 
 			if(soInfo.boneIdx != -1)
 			{
+				if (mAnimProxy->skeletonPose.hasOverride[soInfo.boneIdx])
+					continue;
+
 				Vector3 position = mAnimProxy->skeletonPose.positions[soInfo.boneIdx];
 				Quaternion rotation = mAnimProxy->skeletonPose.rotations[soInfo.boneIdx];
 				Vector3 scale = mAnimProxy->skeletonPose.scales[soInfo.boneIdx];

+ 2 - 0
Source/BansheeCore/Source/BsAnimationManager.cpp

@@ -149,6 +149,7 @@ namespace BansheeEngine
 				info.startIdx = curBoneIdx;
 				info.numBones = numBones;
 
+				memset(anim->skeletonPose.hasOverride, 0, sizeof(bool) * anim->skeletonPose.numBones);
 				Matrix4* boneDst = renderData.transforms.data() + curBoneIdx;
 
 				// Copy transforms from mapped scene objects
@@ -161,6 +162,7 @@ namespace BansheeEngine
 						continue;
 
 					boneDst[soInfo.boneIdx] = anim->sceneObjectTransforms[boneTfrmIdx];
+					anim->skeletonPose.hasOverride[soInfo.boneIdx] = true;
 					boneTfrmIdx++;
 				}
 

+ 32 - 8
Source/BansheeCore/Source/BsSkeleton.cpp

@@ -8,13 +8,13 @@
 namespace BansheeEngine
 {
 	LocalSkeletonPose::LocalSkeletonPose()
-		: positions(nullptr), rotations(nullptr), scales(nullptr), numBones(0)
+		: positions(nullptr), rotations(nullptr), scales(nullptr), hasOverride(nullptr), numBones(0)
 	{ }
 
 	LocalSkeletonPose::LocalSkeletonPose(UINT32 numBones)
 		: numBones(numBones)
 	{
-		UINT32 elementSize = sizeof(Vector3) * 2 + sizeof(Quaternion);
+		UINT32 elementSize = sizeof(Vector3) * 2 + sizeof(Quaternion) + sizeof(bool);
 		UINT8* buffer = (UINT8*)bs_alloc(elementSize * numBones);
 
 		positions = (Vector3*)buffer;
@@ -24,10 +24,13 @@ namespace BansheeEngine
 		buffer += sizeof(Quaternion) * numBones;
 
 		scales = (Vector3*)buffer;
+		buffer += sizeof(Vector3) * numBones;
+
+		hasOverride = (bool*)buffer;
 	}
 
 	LocalSkeletonPose::LocalSkeletonPose(UINT32 numPos, UINT32 numRot, UINT32 numScale)
-		: numBones(0)
+		: numBones(0), hasOverride(nullptr)
 	{
 		UINT32 bufferSize = sizeof(Vector3) * numPos + sizeof(Quaternion) * numRot + sizeof(Vector3) * numScale;
 		UINT8* buffer = (UINT8*)bs_alloc(bufferSize);
@@ -42,11 +45,13 @@ namespace BansheeEngine
 	}
 
 	LocalSkeletonPose::LocalSkeletonPose(LocalSkeletonPose&& other)
-		: positions(other.positions), rotations(other.rotations), scales(other.scales), numBones(other.numBones)
+		: positions(other.positions), rotations(other.rotations), scales(other.scales), hasOverride(other.hasOverride)
+		, numBones(other.numBones)
 	{
 		other.positions = nullptr;
 		other.rotations = nullptr;
 		other.scales = nullptr;
+		other.hasOverride = nullptr;
 		other.numBones = 0;
 	}
 
@@ -66,11 +71,13 @@ namespace BansheeEngine
 			positions = other.positions;
 			rotations = other.rotations;
 			scales = other.scales;
+			hasOverride = other.hasOverride;
 			numBones = other.numBones;
 
 			other.positions = nullptr;
 			other.rotations = nullptr;
 			other.scales = nullptr;
+			other.hasOverride = nullptr;
 			other.numBones = 0;
 		}
 
@@ -159,6 +166,9 @@ namespace BansheeEngine
 			localPose.scales[i] = Vector3::ONE;
 		}
 
+		// Note: For a possible performance improvement consider keeping an array of only active (non-disabled) bones and
+		// just iterate over them without mask checks. Possibly also a list of active curve mappings to avoid those checks
+		// as well.
 		for(UINT32 i = 0; i < numLayers; i++)
 		{
 			const AnimationStateLayer& layer = layers[i];
@@ -197,6 +207,8 @@ namespace BansheeEngine
 					{
 						const TAnimationCurve<Vector3>& curve = state.curves->position[mapping.position].curve;
 						localPose.positions[k] += curve.evaluate(state.time, state.positionCaches[k], state.loop) * normWeight;
+
+						localPose.hasOverride[k] = false;
 					}
 				}
 
@@ -210,6 +222,8 @@ namespace BansheeEngine
 					{
 						const TAnimationCurve<Vector3>& curve = state.curves->scale[mapping.scale].curve;
 						localPose.scales[k] *= curve.evaluate(state.time, state.scaleCaches[k], state.loop) * normWeight;
+
+						localPose.hasOverride[k] = false;
 					}
 				}
 
@@ -233,6 +247,7 @@ namespace BansheeEngine
 							value = Quaternion::lerp(normWeight, Quaternion::IDENTITY, value);
 
 							localPose.rotations[k] *= value;
+							localPose.hasOverride[k] = false;
 						}
 					}
 				}
@@ -253,6 +268,7 @@ namespace BansheeEngine
 								value = -value;
 							
 							localPose.rotations[k] += value;
+							localPose.hasOverride[k] = false;
 						}
 					}
 				}
@@ -260,6 +276,10 @@ namespace BansheeEngine
 		}
 
 		// Calculate local pose matrices
+		UINT32 isGlobalBytes = sizeof(bool) * mNumBones;
+		bool* isGlobal = (bool*)bs_stack_alloc(isGlobalBytes);
+		memset(isGlobal, 0, isGlobalBytes);
+
 		for(UINT32 i = 0; i < mNumBones; i++)
 		{
 			bool isAssigned = localPose.rotations[i].w != 0.0f;
@@ -268,14 +288,18 @@ namespace BansheeEngine
 			else
 				localPose.rotations[i].normalize();
 
+			if (localPose.hasOverride[i])
+			{
+				isGlobal[i] = true;
+				continue;
+			}
+
 			pose[i] = Matrix4::TRS(localPose.positions[i], localPose.rotations[i], localPose.scales[i]);
 		}
 
 		// Calculate global poses
-		UINT32 isGlobalBytes = sizeof(bool) * mNumBones;
-		bool* isGlobal = (bool*)bs_stack_alloc(isGlobalBytes);
-		memset(isGlobal, 0, isGlobalBytes);
-
+		// Note: For a possible performance improvement consider sorting bones in such order so that parents (and overrides)
+		// always come before children, we no isGlobal check is needed.
 		std::function<void(UINT32)> calcGlobal = [&](UINT32 boneIdx)
 		{
 			UINT32 parentBoneIdx = mBoneInfo[boneIdx].parent;