Browse Source

Root motion import implemented

BearishSun 9 years ago
parent
commit
9a6ee5a831

+ 3 - 0
Source/BansheeCore/Include/BsAnimation.h

@@ -463,6 +463,9 @@ namespace BansheeEngine
 		/** Returns the unique ID for this animation object. */
 		UINT64 _getId() const { return mId; }
 
+		/** Checks if any currently set animation clips perform animation of the root bone. */
+		bool _getAnimatesRoot() const;
+
 		/** @} */
 	private:
 		friend class AnimationManager;

+ 3 - 0
Source/BansheeCore/Include/BsAnimationClip.h

@@ -143,6 +143,9 @@ namespace BansheeEngine
 		 */
 		SPtr<RootMotion> getRootMotion() const { return mRootMotion; }
 
+		/** Checks if animation clip has root motion curves separate from the normal animation curves. */
+		bool hasRootMotion() const;
+
 		/**
 		 * Maps skeleton bone names to animation curve names, and returns a set of indices that can be easily used for
 		 * locating an animation curve based on the bone index.

+ 35 - 0
Source/BansheeCore/Source/BsAnimation.cpp

@@ -1080,6 +1080,41 @@ namespace BansheeEngine
 		return output;
 	}
 
+	bool Animation::_getAnimatesRoot() const
+	{
+		if (mSkeleton == nullptr)
+			return false;
+
+		UINT32 rootBoneIdx = mSkeleton->getRootBoneIndex();
+		if (rootBoneIdx == (UINT32)-1)
+			return false;
+
+		String rootBoneName = mSkeleton->getBoneInfo(rootBoneIdx).name;
+		for (auto& entry : mClipInfos)
+		{
+			if (entry.clip.isLoaded())
+			{
+				HAnimationClip clip = entry.clip;
+				if(!clip->hasRootMotion())
+				{
+					AnimationCurveMapping mapping;
+					clip->getCurveMapping(rootBoneName, mapping);
+
+					if (mapping.position != (UINT32)-1)
+						return true;
+
+					if (mapping.rotation != (UINT32)-1)
+						return true;
+
+					if (mapping.scale != (UINT32)-1)
+						return true;
+				}
+			}
+		}
+
+		return false;
+	}
+
 	void Animation::getListenerResources(Vector<HResource>& resources)
 	{
 		for (auto& entry : mClipInfos)

+ 6 - 0
Source/BansheeCore/Source/BsAnimationClip.cpp

@@ -146,6 +146,12 @@ namespace BansheeEngine
 		mVersion++;
 	}
 
+	bool AnimationClip::hasRootMotion() const
+	{
+		return mRootMotion != nullptr && 
+			(mRootMotion->position.getNumKeyFrames() > 0 || mRootMotion->rotation.getNumKeyFrames() > 0);
+	}
+
 	void AnimationClip::calculateLength()
 	{
 		mLength = 0.0f;

+ 8 - 1
Source/BansheeEngine/Source/BsRenderable.cpp

@@ -471,14 +471,21 @@ namespace BansheeEngine
 		if (curHash != _getLastModifiedHash() || force)
 		{
 			// If skinned animation, don't include own transform since that will be handled by root bone animation
+			bool ignoreOwnTransform;
 			if (mAnimType == RenderableAnimType::Skinned || mAnimType == RenderableAnimType::SkinnedMorph)
+				ignoreOwnTransform = mAnimation->_getAnimatesRoot();
+			else
+				ignoreOwnTransform = false;
+
+			if (ignoreOwnTransform)
 			{
 				// Note: Technically we're checking child's hash but using parent's transform. Ideally we check the parent's
 				// hash to reduce the number of required updates.
 				HSceneObject parentSO = so->getParent();
 				if (parentSO != nullptr)
 				{
-					Matrix4 transformNoScale = Matrix4::TRS(parentSO->getWorldPosition(), parentSO->getWorldRotation(), Vector3::ONE);
+					Matrix4 transformNoScale = Matrix4::TRS(parentSO->getWorldPosition(), parentSO->getWorldRotation(),
+						Vector3::ONE);
 					setTransform(parentSO->getWorldTfrm(), transformNoScale);
 				}
 				else

+ 6 - 2
Source/BansheeFBXImporter/Include/BsFBXImportData.h

@@ -13,6 +13,8 @@
 
 namespace BansheeEngine
 {
+	struct RootMotion;
+
 	/** @addtogroup FBX
 	 *  @{
 	 */
@@ -118,14 +120,16 @@ namespace BansheeEngine
 	/** All information required for creating an animation clip. */
 	struct FBXAnimationClipData
 	{
-		FBXAnimationClipData(const String& name, bool isAdditive, UINT32 sampleRate, const SPtr<AnimationCurves>& curves)
-			:name(name), isAdditive(isAdditive), sampleRate(sampleRate), curves(curves)
+		FBXAnimationClipData(const String& name, bool isAdditive, UINT32 sampleRate, const SPtr<AnimationCurves>& curves, 
+			const SPtr<RootMotion>& rootMotion)
+			:name(name), isAdditive(isAdditive), sampleRate(sampleRate), curves(curves), rootMotion(rootMotion)
 		{ }
 
 		String name;
 		bool isAdditive;
 		UINT32 sampleRate;
 		SPtr<AnimationCurves> curves;
+		SPtr<RootMotion> rootMotion;
 	};
 
 	/**	Imported mesh data. */

+ 1 - 1
Source/BansheeFBXImporter/Include/BsFBXImporter.h

@@ -108,7 +108,7 @@ namespace BansheeEngine
 
 		/** Converts FBX animation clips into engine-ready animation curve format. */
 		void convertAnimations(const Vector<FBXAnimationClip>& clips, const Vector<AnimationSplitInfo>& splits, 
-			Vector<FBXAnimationClipData>& output);
+			const SPtr<Skeleton>& skeleton, bool importRootMotion, Vector<FBXAnimationClipData>& output);
 
 		/** 
 		 * Removes identical sequential keyframes for the provided set of curves. The keyframe must be identical over all

+ 60 - 16
Source/BansheeFBXImporter/Source/BsFBXImporter.cpp

@@ -183,7 +183,8 @@ namespace BansheeEngine
 			Vector<ImportedAnimationEvents> events = meshImportOptions->getAnimationEvents();
 			for(auto& entry : animationClips)
 			{
-				SPtr<AnimationClip> clip = AnimationClip::_createPtr(entry.curves, entry.isAdditive, entry.sampleRate);
+				SPtr<AnimationClip> clip = AnimationClip::_createPtr(entry.curves, entry.isAdditive, entry.sampleRate, 
+					entry.rootMotion);
 				
 				for(auto& eventsEntry : events)
 				{
@@ -247,7 +248,7 @@ namespace BansheeEngine
 		if (!importedScene.clips.empty())
 		{
 			Vector<AnimationSplitInfo> splits = meshImportOptions->getAnimationClipSplits();
-			convertAnimations(importedScene.clips, splits, animation);
+			convertAnimations(importedScene.clips, splits, skeleton, meshImportOptions->getImportRootMotion(), animation);
 		}
 
 		// TODO - Later: Optimize mesh: Remove bad and degenerate polygons, weld nearby vertices, optimize for vertex cache
@@ -634,20 +635,29 @@ namespace BansheeEngine
 	}
 
 	void FBXImporter::convertAnimations(const Vector<FBXAnimationClip>& clips, const Vector<AnimationSplitInfo>& splits,
-		Vector<FBXAnimationClipData>& output)
+		const SPtr<Skeleton>& skeleton, bool importRootMotion, Vector<FBXAnimationClipData>& output)
 	{
 		UnorderedSet<String> names;
 
+		String rootBoneName;
+		if (skeleton == nullptr)
+			importRootMotion = false;
+		else
+		{
+			UINT32 rootBoneIdx = skeleton->getRootBoneIndex();
+			if (rootBoneIdx == (UINT32)-1)
+				importRootMotion = false;
+			else
+				rootBoneName = skeleton->getBoneInfo(rootBoneIdx).name;
+		}
+
 		bool isFirstClip = true;
 		for (auto& clip : clips)
 		{
 			SPtr<AnimationCurves> curves = bs_shared_ptr_new<AnimationCurves>();
+			SPtr<RootMotion> rootMotion;
 
-			/************************************************************************/
-			/* 							BONE ANIMATIONS                      		*/
-			/************************************************************************/
-
-			// Offset animations so they start at time 0
+			// Find offset so animations start at time 0
 			float animStart = std::numeric_limits<float>::infinity();
 
 			for (auto& bone : clip.boneAnimations)
@@ -677,9 +687,14 @@ namespace BansheeEngine
 					TAnimationCurve<Quaternion> rotation = AnimationUtility::offsetCurve(bone.rotation, -animStart);
 					TAnimationCurve<Vector3> scale = AnimationUtility::offsetCurve(bone.scale, -animStart);
 
-					curves->position.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, translation });
-					curves->rotation.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, rotation });
-					curves->scale.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, scale });
+					if(importRootMotion && bone.node->name == rootBoneName)
+						rootMotion = bs_shared_ptr_new<RootMotion>(translation, rotation);
+					else
+					{
+						curves->position.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, translation });
+						curves->rotation.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, rotation });
+						curves->scale.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, scale });
+					}
 				}
 
 				for (auto& anim : clip.blendShapeAnimations)
@@ -692,9 +707,14 @@ namespace BansheeEngine
 			{
 				for (auto& bone : clip.boneAnimations)
 				{
-					curves->position.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, bone.translation });
-					curves->rotation.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, bone.rotation });
-					curves->scale.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, bone.scale });
+					if (importRootMotion && bone.node->name == rootBoneName)
+						rootMotion = bs_shared_ptr_new<RootMotion>(bone.translation, bone.rotation);
+					else
+					{
+						curves->position.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, bone.translation });
+						curves->rotation.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, bone.rotation });
+						curves->scale.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, bone.scale });
+					}
 				}
 
 				for (auto& anim : clip.blendShapeAnimations)
@@ -710,6 +730,7 @@ namespace BansheeEngine
 				for(auto& split : splits)
 				{
 					SPtr<AnimationCurves> splitClipCurve = bs_shared_ptr_new<AnimationCurves>();
+					SPtr<RootMotion> splitRootMotion;
 
 					auto splitCurves = [&](auto& inCurves, auto& outCurves)
 					{
@@ -740,6 +761,28 @@ namespace BansheeEngine
 					splitCurves(curves->scale, splitClipCurve->scale);
 					splitCurves(curves->generic, splitClipCurve->generic);
 
+					if(rootMotion != nullptr)
+					{
+						auto splitCurve = [&](auto& inCurve, auto& outCurve)
+						{
+							UINT32 numFrames = inCurve.getNumKeyFrames();
+							if (numFrames > 0)
+							{
+								float startTime = split.startFrame * secondsPerFrame;
+								float endTime = split.endFrame * secondsPerFrame;
+
+								outCurve = inCurve.split(startTime, endTime);
+
+								if (split.isAdditive)
+									outCurve.makeAdditive();
+							}
+						};
+
+						splitRootMotion = bs_shared_ptr_new<RootMotion>();
+						splitCurve(rootMotion->position, splitRootMotion->position);
+						splitCurve(rootMotion->rotation, splitRootMotion->rotation);
+					}
+
 					// Search for a unique name
 					String name = split.name;
 					UINT32 attemptIdx = 0;
@@ -750,7 +793,8 @@ namespace BansheeEngine
 					}
 
 					names.insert(name);
-					output.push_back(FBXAnimationClipData(name, split.isAdditive, clip.sampleRate, splitClipCurve));
+					output.push_back(FBXAnimationClipData(name, split.isAdditive, clip.sampleRate, splitClipCurve,
+						splitRootMotion));
 				}
 			}
 			else
@@ -765,7 +809,7 @@ namespace BansheeEngine
 				}
 
 				names.insert(name);
-				output.push_back(FBXAnimationClipData(name, false, clip.sampleRate, curves));
+				output.push_back(FBXAnimationClipData(name, false, clip.sampleRate, curves, rootMotion));
 			}
 
 			isFirstClip = false;