Browse Source

Scene object transform modifications can now modify skeleton bones (for IK and similar)

BearishSun 9 years ago
parent
commit
9620459208

+ 11 - 1
Source/BansheeCore/Include/BsAnimation.h

@@ -112,11 +112,12 @@ namespace BansheeEngine
 	/** Contains information about a scene object that is animated by a specific animation curve. */
 	struct AnimatedSceneObjectInfo
 	{
-		UINT64 id;
+		UINT64 id; /**< Instance ID of the scene object. */
 		INT32 boneIdx; /**< Bone from which to access the transform. If -1 then no bone mapping is present. */
 		INT32 layerIdx; /**< If no bone mapping, layer on which the animation containing the referenced curve is in. */
 		INT32 stateIdx; /**< If no bone mapping, animation state containing the referenced curve. */
 		AnimationCurveMapping curveIndices; /**< Indices of the curves used for the transform. */
+		UINT32 hash; /**< Hash value of the scene object's transform. */
 	};
 
 	/** Represents a copy of the Animation data for use specifically on the animation thread. */
@@ -164,6 +165,14 @@ namespace BansheeEngine
 		 */
 		void updateValues(const Vector<AnimationClipInfo>& clipInfos);
 
+		/**
+		 * Updates the proxy data with new scene object transforms. Caller must guarantee that clip layout didn't 
+		 * change since the last call to rebuild().
+		 *
+		 * @note	Should be called from the sim thread when the caller is sure the animation thread is not using it.
+		 */
+		void updateTransforms(const Vector<AnimatedSceneObject>& sceneObjects);
+
 		/** 
 		 * Updates the proxy data with new clip times. Caller must guarantee that clip layout didn't change since the last
 		 * call to rebuild().
@@ -181,6 +190,7 @@ namespace BansheeEngine
 		SPtr<Skeleton> skeleton;
 		UINT32 numSceneObjects;
 		AnimatedSceneObjectInfo* sceneObjectInfos;
+		Matrix4* sceneObjectTransforms;
 
 		// Evaluation results
 		LocalSkeletonPose skeletonPose;

+ 126 - 34
Source/BansheeCore/Source/BsAnimation.cpp

@@ -30,7 +30,8 @@ namespace BansheeEngine
 	}
 
 	AnimationProxy::AnimationProxy(UINT64 id)
-		:id(id), layers(nullptr), numLayers(0), numSceneObjects(0), sceneObjectInfos(nullptr), genericCurveOutputs(nullptr)
+		: id(id), layers(nullptr), numLayers(0), numSceneObjects(0), sceneObjectInfos(nullptr)
+		, sceneObjectTransforms(nullptr), genericCurveOutputs(nullptr)
 	{ }
 
 	AnimationProxy::~AnimationProxy()
@@ -101,6 +102,7 @@ namespace BansheeEngine
 		layers = nullptr;
 		genericCurveOutputs = nullptr;
 		sceneObjectInfos = nullptr;
+		sceneObjectTransforms = nullptr;
 
 		numLayers = 0;
 		numSceneObjects = 0;
@@ -188,6 +190,31 @@ namespace BansheeEngine
 				numGenCurves += (UINT32)curves->generic.size();
 			}
 
+			UINT32* mappedBoneIndices = (UINT32*)bs_frame_alloc(sizeof(UINT32) * numSceneObjects);
+			for (UINT32 i = 0; i < numSceneObjects; i++)
+				mappedBoneIndices[i] = -1;
+
+			UINT32 numBoneMappedSOs = 0;
+			if (skeleton != nullptr)
+			{
+				for (UINT32 i = 0; i < numSceneObjects; i++)
+				{
+					for (UINT32 j = 0; j < numBones; j++)
+					{
+						if (sceneObjects[i].so.isDestroyed(true))
+							continue;
+
+						if (skeleton->getBoneInfo(j).name == sceneObjects[i].curveName)
+						{
+							mappedBoneIndices[i] = j;
+
+							numBoneMappedSOs++;
+							break;
+						}
+					}
+				}
+			}
+
 			UINT32 numBoneMappings = numBones * numClips;
 			UINT32 layersSize = sizeof(AnimationStateLayer) * numLayers;
 			UINT32 clipsSize = sizeof(AnimationState) * numClips;
@@ -198,9 +225,10 @@ namespace BansheeEngine
 			UINT32 genCacheSize = numGenCurves * sizeof(TCurveCache<float>);
 			UINT32 genericCurveOutputSize = numGenCurves * sizeof(float);
 			UINT32 sceneObjectIdsSize = numSceneObjects * sizeof(AnimatedSceneObjectInfo);
+			UINT32 sceneObjectTransformsSize = numBoneMappedSOs * sizeof(Matrix4);
 
 			UINT8* data = (UINT8*)bs_alloc(layersSize + clipsSize + boneMappingSize + posCacheSize + rotCacheSize + 
-				scaleCacheSize + genCacheSize + genericCurveOutputSize + sceneObjectIdsSize);
+				scaleCacheSize + genCacheSize + genericCurveOutputSize + sceneObjectIdsSize + sceneObjectTransformsSize);
 
 			layers = (AnimationStateLayer*)data;
 			memcpy(layers, tempLayers.data(), layersSize);
@@ -248,6 +276,12 @@ namespace BansheeEngine
 			sceneObjectInfos = (AnimatedSceneObjectInfo*)data;
 			data += sceneObjectIdsSize;
 
+			sceneObjectTransforms = (Matrix4*)data;
+			for (UINT32 i = 0; i < numBoneMappedSOs; i++)
+				sceneObjectTransforms[i] = Matrix4::IDENTITY;
+
+			data += sceneObjectTransformsSize;
+
 			UINT32 curLayerIdx = 0;
 			UINT32 curStateIdx = 0;
 
@@ -350,46 +384,52 @@ namespace BansheeEngine
 				assert(layer.numStates > 0);
 			}
 
+			UINT32 boneIdx = 0;
 			for(UINT32 i = 0; i < numSceneObjects; i++)
 			{
+				HSceneObject so = sceneObjects[i].so;
 				AnimatedSceneObjectInfo& soInfo = sceneObjectInfos[i];
-				soInfo.id = sceneObjects[i].so.getInstanceId();
-				soInfo.boneIdx = -1;
+				soInfo.id = so.getInstanceId();
+				soInfo.boneIdx = mappedBoneIndices[i];
 
-				// Check if the scene object maps to a bone (in which case we the system can just re-use the skeleton pose)
-				if (skeleton != nullptr)
-				{
-					for(UINT32 j = 0; j < numBones; j++)
-					{
-						if(skeleton->getBoneInfo(j).name == sceneObjects[i].curveName)
-						{
-							soInfo.boneIdx = j;
-							break;
-						}
-					}
-				}
+				bool isSOValid = !so.isDestroyed(true);
+				if (isSOValid)
+					soInfo.hash = so->getTransformHash();
+				else
+					soInfo.hash = 0;
 
 				// If no bone mapping, find curves directly
 				if(soInfo.boneIdx == -1)
 				{
 					soInfo.curveIndices = { (UINT32)-1, (UINT32)-1, (UINT32)-1 };
 
-					for(auto& clipInfo : clipInfos)
+					if (isSOValid)
 					{
-						soInfo.layerIdx = clipInfo.layerIdx;
-						soInfo.stateIdx = clipInfo.stateIdx;
-
-						bool isClipValid = clipInfo.clip.isLoaded();
-						if (isClipValid)
+						for (auto& clipInfo : clipInfos)
 						{
-							// Note: If there are multiple clips with the relevant curve name, we only use the first
+							soInfo.layerIdx = clipInfo.layerIdx;
+							soInfo.stateIdx = clipInfo.stateIdx;
 
-							clipInfo.clip->getCurveMapping(sceneObjects[i].curveName, soInfo.curveIndices);
-							break;
+							bool isClipValid = clipInfo.clip.isLoaded();
+							if (isClipValid)
+							{
+								// Note: If there are multiple clips with the relevant curve name, we only use the first
+
+								clipInfo.clip->getCurveMapping(sceneObjects[i].curveName, soInfo.curveIndices);
+								break;
+							}
 						}
 					}
 				}
+				else
+				{
+					// No need to check if SO is valid, if it has a bone connection it must be
+					sceneObjectTransforms[boneIdx] = so->getWorldTfrm();
+					boneIdx++;
+				}
 			}
+
+			bs_frame_free(mappedBoneIndices);
 		}
 		bs_frame_clear();
 	}
@@ -406,6 +446,28 @@ namespace BansheeEngine
 		}
 	}
 
+	void AnimationProxy::updateTransforms(const Vector<AnimatedSceneObject>& sceneObjects)
+	{
+		UINT32 boneIdx = 0;
+		for (UINT32 i = 0; i < numSceneObjects; i++)
+		{
+			HSceneObject so = sceneObjects[i].so;
+			if (so.isDestroyed(true))
+			{
+				sceneObjectInfos[i].hash = 0;
+				continue;
+			}
+
+			sceneObjectInfos[i].hash = so->getTransformHash();
+
+			if (sceneObjectInfos[i].boneIdx == -1)
+				continue;
+
+			sceneObjectTransforms[boneIdx] = sceneObjects[i].so->getWorldTfrm();
+			boneIdx++;
+		}
+	}
+
 	void AnimationProxy::updateTime(const Vector<AnimationClipInfo>& clipInfos)
 	{
 		for (auto& clipInfo : clipInfos)
@@ -889,26 +951,56 @@ namespace BansheeEngine
 		}
 		else
 		{
-			if (mDirty.isSet(AnimDirtyStateFlag::Skeleton))
+			auto getAnimatedSOList = [&]()
 			{
 				Vector<AnimatedSceneObject> animatedSO(mSceneObjects.size());
 				UINT32 idx = 0;
-				for(auto& entry : mSceneObjects)
+				for (auto& entry : mSceneObjects)
 					animatedSO[idx++] = entry.second;
 
-				mAnimProxy->rebuild(mSkeleton, mClipInfos, animatedSO);
+				return animatedSO;
+			};
+
+			bool didFullRebuild = false;
+			if (mDirty.isSet(AnimDirtyStateFlag::Skeleton))
+			{
+				Vector<AnimatedSceneObject> animatedSOs = getAnimatedSOList();
+
+				mAnimProxy->rebuild(mSkeleton, mClipInfos, animatedSOs);
+				didFullRebuild = true;
 			}
 			else if (mDirty.isSet(AnimDirtyStateFlag::Layout))
 			{
-				Vector<AnimatedSceneObject> animatedSO(mSceneObjects.size());
-				UINT32 idx = 0;
-				for (auto& entry : mSceneObjects)
-					animatedSO[idx++] = entry.second;
+				Vector<AnimatedSceneObject> animatedSOs = getAnimatedSOList();
 
-				mAnimProxy->rebuild(mClipInfos, animatedSO);
+				mAnimProxy->rebuild(mClipInfos, animatedSOs);
+				didFullRebuild = true;
 			}
 			else if (mDirty.isSet(AnimDirtyStateFlag::Value))
 				mAnimProxy->updateValues(mClipInfos);
+
+			// Check if there are dirty transforms
+			if(!didFullRebuild)
+			{
+				UINT32 numSceneObjects = (UINT32)mSceneObjects.size();
+				for (UINT32 i = 0; i < numSceneObjects; i++)
+				{
+					UINT32 hash;
+
+					HSceneObject so = mSceneObjects[i].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;
+					}
+				}
+			}
 		}
 
 		mDirty = AnimDirtyState();
@@ -926,7 +1018,7 @@ namespace BansheeEngine
 				continue;
 
 			HSceneObject so = iterFind->second.so;
-			if (so.isDestroyed())
+			if (so.isDestroyed(true))
 				continue;
 
 			if(soInfo.boneIdx != -1)

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

@@ -118,6 +118,21 @@ namespace BansheeEngine
 				info.numBones = numBones;
 
 				Matrix4* boneDst = renderData.transforms.data() + curBoneIdx;
+
+				// Copy transforms from mapped scene objects
+				UINT32 boneTfrmIdx = 0;
+				for(UINT32 i = 0; i < anim->numSceneObjects; i++)
+				{
+					const AnimatedSceneObjectInfo& soInfo = anim->sceneObjectInfos[i];
+
+					if (soInfo.boneIdx == -1)
+						continue;
+
+					boneDst[soInfo.boneIdx] = anim->sceneObjectTransforms[boneTfrmIdx];
+					boneTfrmIdx++;
+				}
+
+				// Animate bones
 				anim->skeleton->getPose(boneDst, anim->skeletonPose, anim->layers, anim->numLayers);
 
 				renderData.poseInfos[anim->id] = info;

+ 1 - 1
Source/BansheeD3D11RenderAPI/Source/BsD3D11RenderWindow.cpp

@@ -605,7 +605,7 @@ namespace BansheeEngine
 		SAFE_RELEASE(pDXGIDevice);
 
 		if (FAILED(hr))
-			BS_EXCEPT(RenderingAPIException, "Unable to create swap chain");
+			BS_EXCEPT(RenderingAPIException, "Unable to create swap chain. Error code: " + toString(hr));
 
 		BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_SwapChain);
 	}