2
0
Эх сурвалжийг харах

Added bone filtering to animation

BearishSun 9 жил өмнө
parent
commit
6164237881

+ 2 - 0
Source/BansheeCore/CMakeSources.cmake

@@ -505,6 +505,7 @@ set(BS_BANSHEECORE_INC_ANIMATION
 	"Include/BsAnimationManager.h"
 	"Include/BsCurveCache.h"
 	"Include/BsAnimationUtility.h"
+	"Include/BsSkeletonMask.h"
 )
 
 set(BS_BANSHEECORE_SRC_ANIMATION
@@ -514,6 +515,7 @@ set(BS_BANSHEECORE_SRC_ANIMATION
 	"Source/BsAnimation.cpp"
 	"Source/BsAnimationManager.cpp"
 	"Source/BsAnimationUtility.cpp"
+	"Source/BsSkeletonMask.cpp"
 )
 
 source_group("Header Files\\Components" FILES ${BS_BANSHEECORE_INC_COMPONENTS})

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

@@ -6,6 +6,7 @@
 #include "BsCoreObject.h"
 #include "BsFlags.h"
 #include "BsSkeleton.h"
+#include "BsSkeletonMask.h"
 #include "BsVector2.h"
 
 namespace BansheeEngine
@@ -134,6 +135,7 @@ namespace BansheeEngine
 		 * whenever the animation skeleton changes.
 		 *
 		 * @param[in]		skeleton		New skeleton to assign to the proxy.
+		 * @param[in]		mask			Mask that filters which skeleton bones are enabled or disabled.
 		 * @param[in, out]	clipInfos		Potentially new clip infos that will be used for rebuilding the proxy. Once the
 		 *									method completes clip info layout and state indices will be populated for 
 		 *									further use in the update*() methods.
@@ -141,7 +143,7 @@ namespace BansheeEngine
 		 *
 		 * @note	Should be called from the sim thread when the caller is sure the animation thread is not using it.
 		 */
-		void rebuild(const SPtr<Skeleton>& skeleton, Vector<AnimationClipInfo>& clipInfos, 
+		void rebuild(const SPtr<Skeleton>& skeleton, const SkeletonMask& mask, Vector<AnimationClipInfo>& clipInfos, 
 			const Vector<AnimatedSceneObject>& sceneObjects);
 
 		/** 
@@ -188,6 +190,7 @@ namespace BansheeEngine
 		AnimationStateLayer* layers;
 		UINT32 numLayers;
 		SPtr<Skeleton> skeleton;
+		SkeletonMask skeletonMask;
 		UINT32 numSceneObjects;
 		AnimatedSceneObjectInfo* sceneObjectInfos;
 		Matrix4* sceneObjectTransforms;
@@ -216,6 +219,12 @@ namespace BansheeEngine
 		 */
 		void setSkeleton(const SPtr<Skeleton>& skeleton);
 
+		/** 
+		 * Sets a mask that allows certain bones from the skeleton to be disabled. Caller must ensure that the mask matches
+		 * the skeleton assigned to the animation.
+		 */
+		void setMask(const SkeletonMask& mask);
+
 		/** 
 		 * Changes the wrap mode for all active animations. Wrap mode determines what happens when animation reaches the 
 		 * first or last frame. 
@@ -404,6 +413,7 @@ namespace BansheeEngine
 		AnimDirtyState mDirty;
 
 		SPtr<Skeleton> mSkeleton;
+		SkeletonMask mSkeletonMask;
 		Vector<AnimationClipInfo> mClipInfos;
 		UnorderedMap<UINT64, AnimatedSceneObject> mSceneObjects;
 		Vector<float> mGenericCurveOutputs;

+ 8 - 2
Source/BansheeCore/Include/BsSkeleton.h

@@ -11,6 +11,8 @@
 
 namespace BansheeEngine
 {
+	class SkeletonMask;
+
 	/** @addtogroup Animation-Internal
 	 *  @{
 	 */
@@ -114,6 +116,7 @@ namespace BansheeEngine
 		 *
 		 * @param[out]	pose		Output pose containing the requested transforms. Must be pre-allocated with enough space
 		 *							to hold all the bone matrices of this skeleton.
+		 * @param[in]	mask		Mask that filters which skeleton bones are enabled or disabled.
 		 * @param[out]	localPose	Output pose containing the local transforms. Must be pre-allocated with enough space
 		 *							to hold all the bone data of this skeleton.
 		 * @param[in]	clip		Clip to evaluate.
@@ -123,7 +126,8 @@ namespace BansheeEngine
 		 * @note	It is more efficient to use the other getPose overload as sequential calls can benefit from animation
 		 *			evaluator cache.
 		 */
-		void getPose(Matrix4* pose, LocalSkeletonPose& localPose, const AnimationClip& clip, float time, bool loop = true);
+		void getPose(Matrix4* pose, LocalSkeletonPose& localPose, const SkeletonMask& mask, const AnimationClip& clip, 
+			float time, bool loop = true);
 
 		/** 
 		 * Outputs a skeleton pose containing required transforms for transforming the skeleton to the values specified by
@@ -131,12 +135,14 @@ namespace BansheeEngine
 		 *
 		 * @param[out]	pose		Output pose containing the requested transforms. Must be pre-allocated with enough space
 		 *							to hold all the bone matrices of this skeleton.
+		 * @param[in]	mask		Mask that filters which skeleton bones are enabled or disabled.
 		 * @param[out]	localPose	Output pose containing the local transforms. Must be pre-allocated with enough space
 		 *							to hold all the bone data of this skeleton.
 		 * @param[in]	layers		One or multiple layers, containing one or multiple animation states to evaluate.
 		 * @param[in]	numLayers	Number of layers in the @p layers array.
 		 */
-		void getPose(Matrix4* pose, LocalSkeletonPose& localPose, const AnimationStateLayer* layers, UINT32 numLayers);
+		void getPose(Matrix4* pose, LocalSkeletonPose& localPose, const SkeletonMask& mask, 
+			const AnimationStateLayer* layers, UINT32 numLayers);
 
 		/** Returns the total number of bones in the skeleton. */
 		UINT32 getNumBones() const { return mNumBones; }

+ 53 - 0
Source/BansheeCore/Include/BsSkeletonMask.h

@@ -0,0 +1,53 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsCorePrerequisites.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup Animation
+	 *  @{
+	 */
+
+	/** 
+	 * Contains a bitfield that determines which skeleton bones are enabled or disabled during skeletal animation. Use
+	 * SkeletonMaskBuilder to create a mask for a specific skeleton.
+	 */
+	class BS_CORE_EXPORT SkeletonMask
+	{
+	public:
+		SkeletonMask() {}
+		SkeletonMask(UINT32 numBones);
+
+		/** 
+		 * Checks is the bone at the specified index enabled. Caller is expected to know which skeleton is the skeleton
+		 * mask tied with, in order to determine the bone index. 
+		 */
+		bool isEnabled(UINT32 boneIdx) const;
+
+	private:
+		friend class SkeletonMaskBuilder;
+
+		Vector<bool> mIsDisabled;
+	};
+
+	/** Builds a SkeletonMask for a specific skeleton. */
+	class BS_CORE_EXPORT SkeletonMaskBuilder
+	{
+	public:
+		SkeletonMaskBuilder(const SPtr<Skeleton>& skeleton);
+
+		/** Enables or disables a bone with the specified name. */
+		void setBoneState(const String& name, bool enabled);
+
+		/** Teturns the built skeleton mask. */
+		SkeletonMask getMask() const { return mMask; }
+
+	private:
+		SPtr<Skeleton> mSkeleton;
+		SkeletonMask mMask;
+	};
+
+	/** @} */
+}

+ 10 - 3
Source/BansheeCore/Source/BsAnimation.cpp

@@ -109,10 +109,11 @@ namespace BansheeEngine
 		numGenericCurves = 0;
 	}
 
-	void AnimationProxy::rebuild(const SPtr<Skeleton>& skeleton, Vector<AnimationClipInfo>& clipInfos, 
-		const Vector<AnimatedSceneObject>& sceneObjects)
+	void AnimationProxy::rebuild(const SPtr<Skeleton>& skeleton, const SkeletonMask& mask, 
+		Vector<AnimationClipInfo>& clipInfos, const Vector<AnimatedSceneObject>& sceneObjects)
 	{
 		this->skeleton = skeleton;
+		this->skeletonMask = skeletonMask;
 
 		// Note: I could avoid having a separate allocation for LocalSkeletonPoses and use the same buffer as the rest
 		// of AnimationProxy
@@ -521,6 +522,12 @@ namespace BansheeEngine
 		mDirty |= AnimDirtyStateFlag::Skeleton;
 	}
 
+	void Animation::setMask(const SkeletonMask& mask)
+	{
+		mSkeletonMask = mask;
+		mDirty |= AnimDirtyStateFlag::Skeleton;
+	}
+
 	void Animation::setWrapMode(AnimWrapMode wrapMode)
 	{
 		mDefaultWrapMode = wrapMode;
@@ -1014,7 +1021,7 @@ namespace BansheeEngine
 			{
 				Vector<AnimatedSceneObject> animatedSOs = getAnimatedSOList();
 
-				mAnimProxy->rebuild(mSkeleton, mClipInfos, animatedSOs);
+				mAnimProxy->rebuild(mSkeleton, mSkeletonMask, mClipInfos, animatedSOs);
 				didFullRebuild = true;
 			}
 			else if (mDirty.isSet(AnimDirtyStateFlag::Layout))

+ 1 - 1
Source/BansheeCore/Source/BsAnimationManager.cpp

@@ -133,7 +133,7 @@ namespace BansheeEngine
 				}
 
 				// Animate bones
-				anim->skeleton->getPose(boneDst, anim->skeletonPose, anim->layers, anim->numLayers);
+				anim->skeleton->getPose(boneDst, anim->skeletonPose, anim->skeletonMask, anim->layers, anim->numLayers);
 
 				renderData.poseInfos[anim->id] = info;
 				curBoneIdx += numBones;

+ 18 - 9
Source/BansheeCore/Source/BsSkeleton.cpp

@@ -2,6 +2,7 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsSkeleton.h"
 #include "BsAnimationClip.h"
+#include "BsSkeletonMask.h"
 #include "BsSkeletonRTTI.h"
 
 namespace BansheeEngine
@@ -107,8 +108,8 @@ namespace BansheeEngine
 		return bs_shared_ptr<Skeleton>(rawPtr);
 	}
 
-	void Skeleton::getPose(Matrix4* pose, LocalSkeletonPose& localPose, const AnimationClip& clip, float time,
-		bool loop)
+	void Skeleton::getPose(Matrix4* pose, LocalSkeletonPose& localPose, const SkeletonMask& mask, 
+		const AnimationClip& clip, float time, bool loop)
 	{
 		bs_frame_mark();
 		{
@@ -139,13 +140,13 @@ namespace BansheeEngine
 
 			clip.getBoneMapping(*this, state.boneToCurveMapping);
 
-			getPose(pose, localPose, &layer, 1);
+			getPose(pose, localPose, mask, &layer, 1);
 		}
 		bs_frame_clear();
 	}
 
-	void Skeleton::getPose(Matrix4* pose, LocalSkeletonPose& localPose, const AnimationStateLayer* layers,
-		UINT32 numLayers)
+	void Skeleton::getPose(Matrix4* pose, LocalSkeletonPose& localPose, const SkeletonMask& mask, 
+		const AnimationStateLayer* layers, UINT32 numLayers)
 	{
 		// Note: If more performance is required this method could be optimized with vector instructions
 
@@ -188,8 +189,10 @@ namespace BansheeEngine
 
 				for (UINT32 k = 0; k < mNumBones; k++)
 				{
-					const AnimationCurveMapping& mapping = state.boneToCurveMapping[k];
+					if (!mask.isEnabled(k))
+						continue;
 
+					const AnimationCurveMapping& mapping = state.boneToCurveMapping[k];
 					if (mapping.position != (UINT32)-1)
 					{
 						const TAnimationCurve<Vector3>& curve = state.curves->position[mapping.position].curve;
@@ -199,8 +202,10 @@ namespace BansheeEngine
 
 				for (UINT32 k = 0; k < mNumBones; k++)
 				{
-					const AnimationCurveMapping& mapping = state.boneToCurveMapping[k];
+					if (!mask.isEnabled(k))
+						continue;
 
+					const AnimationCurveMapping& mapping = state.boneToCurveMapping[k];
 					if (mapping.scale != (UINT32)-1)
 					{
 						const TAnimationCurve<Vector3>& curve = state.curves->scale[mapping.scale].curve;
@@ -212,8 +217,10 @@ namespace BansheeEngine
 				{
 					for (UINT32 k = 0; k < mNumBones; k++)
 					{
-						const AnimationCurveMapping& mapping = state.boneToCurveMapping[k];
+						if (!mask.isEnabled(k))
+							continue;
 
+						const AnimationCurveMapping& mapping = state.boneToCurveMapping[k];
 						if (mapping.rotation != (UINT32)-1)
 						{
 							const TAnimationCurve<Quaternion>& curve = state.curves->rotation[mapping.rotation].curve;
@@ -229,8 +236,10 @@ namespace BansheeEngine
 				{
 					for (UINT32 k = 0; k < mNumBones; k++)
 					{
-						const AnimationCurveMapping& mapping = state.boneToCurveMapping[k];
+						if (!mask.isEnabled(k))
+							continue;
 
+						const AnimationCurveMapping& mapping = state.boneToCurveMapping[k];
 						if (mapping.rotation != (UINT32)-1)
 						{
 							const TAnimationCurve<Quaternion>& curve = state.curves->rotation[mapping.rotation].curve;

+ 36 - 0
Source/BansheeCore/Source/BsSkeletonMask.cpp

@@ -0,0 +1,36 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsSkeletonMask.h"
+#include "BsSkeleton.h"
+
+namespace BansheeEngine
+{
+	SkeletonMask::SkeletonMask(UINT32 numBones)
+		:mIsDisabled(numBones)
+	{ }
+
+	bool SkeletonMask::isEnabled(UINT32 boneIdx) const
+	{
+		if (boneIdx >= (UINT32)mIsDisabled.size())
+			return true;
+
+		return !mIsDisabled[boneIdx];
+	}
+
+	SkeletonMaskBuilder::SkeletonMaskBuilder(const SPtr<Skeleton>& skeleton)
+		:mSkeleton(skeleton), mMask(skeleton->getNumBones())
+	{ }
+
+	void SkeletonMaskBuilder::setBoneState(const String& name, bool enabled)
+	{
+		UINT32 numBones = mSkeleton->getNumBones();
+		for(UINT32 i = 0; i < numBones; i++)
+		{
+			if(mSkeleton->getBoneInfo(i).name == name)
+			{
+				mMask.mIsDisabled[i] = !enabled;
+				break;
+			}
+		}
+	}
+}