Răsfoiți Sursa

Added generalized 1D blending

BearishSun 9 ani în urmă
părinte
comite
0a0047b369

+ 13 - 29
Source/BansheeCore/Include/BsAnimation.h

@@ -69,32 +69,23 @@ namespace BansheeEngine
 		UINT32 stateIdx; /**< State index this clip belongs to in AnimationProxy structure. */
 	};
 
-	/** Defines a single animation clip in BlendSequentialInfo. */
-	struct BS_CORE_EXPORT BlendSequentialClipInfo
+	/** Represents an animation clip used in 1D blending. Each clip has a position on the number line. */
+	struct BS_CORE_EXPORT BlendClipInfo
 	{
-		BlendSequentialClipInfo() { }
+		BlendClipInfo() { }
 
 		HAnimationClip clip;
-		float fadeTime = 0.0f;
-		float startTime = 0.0f;
-		float endTime = 0.0f;
+		float position = 0.0f;
 	};
 
-	/** Defines a sequential blend where one animation clip is played after another, with an optional fade between them. */
-	struct BS_CORE_EXPORT BlendSequentialInfo
+	/** Defines a 1D blend where multiple animation clips are blended between each other using linear interpolation. */
+	struct BS_CORE_EXPORT Blend1DInfo
 	{
-		BlendSequentialInfo(UINT32 numClips);
-		~BlendSequentialInfo();
+		Blend1DInfo(UINT32 numClips);
+		~Blend1DInfo();
 
-		BlendSequentialClipInfo* clips;
 		UINT32 numClips;
-	};
-
-	/** Defines a 1D blend where two animation clips are blended between each other using linear interpolation. */
-	struct Blend1DInfo
-	{
-		HAnimationClip leftClip;
-		HAnimationClip rightClip;
+		BlendClipInfo* clips;
 	};
 
 	/** Defines a 2D blend where two animation clips are blended between each other using bilinear interpolation. */
@@ -214,20 +205,13 @@ namespace BansheeEngine
 		void blendAdditive(const HAnimationClip& clip, float weight, float fadeLength = 0.0f, UINT32 layer = 0);
 
 		/**
-		 * Plays a set of animation clips sequentially one after another, with an optional fade between them.
-		 *
-		 * @param[in]	info		Describes all animation clips to play.
-		 */
-		void blendSequential(const BlendSequentialInfo& info);
-
-		/**
-		 * Blend two animation clips between each other using linear interpolation. Unlike normal animations these
+		 * Blend multiple animation clips between each other using linear interpolation. Unlike normal animations these
 		 * animations are not advanced with the progress of time, and is instead expected the user manually changes the
 		 * @p t parameter.
 		 *
-		 * @param[in]	info	Information about the clips to blend.
-		 * @param[in]	t		Parameter that controls the blending, in range [0, 1]. t = 0 means left animation has full
-		 *						influence, t = 1 means right animation has full influence.
+		 * @param[in]	info	Information about the clips to blend. Clip positions must be sorted from lowest to highest.
+		 * @param[in]	t		Parameter that controls the blending. Range depends on the positions of the provided
+		 *						animation clips.
 		 */
 		void blend1D(const Blend1DInfo& info, float t);
 

+ 87 - 24
Source/BansheeCore/Source/BsAnimation.cpp

@@ -14,14 +14,14 @@ namespace BansheeEngine
 		: fadeDirection(0.0f), fadeTime(0.0f), fadeLength(0.0f), clip(clip), curveVersion(0), layerIdx(0), stateIdx(0)
 	{ }
 
-	BlendSequentialInfo::BlendSequentialInfo(UINT32 numClips)
+	Blend1DInfo::Blend1DInfo(UINT32 numClips)
 		: clips(nullptr), numClips(numClips)
 	{
 		if (numClips > 0)
-			bs_newN<BlendSequentialClipInfo>(numClips);
+			clips = bs_newN<BlendClipInfo>(numClips);
 	}
 
-	BlendSequentialInfo::~BlendSequentialInfo()
+	Blend1DInfo::~Blend1DInfo()
 	{
 		if(clips != nullptr)
 			bs_deleteN(clips, numClips);
@@ -290,7 +290,7 @@ namespace BansheeEngine
 	}
 
 	Animation::Animation()
-		:mDefaultWrapMode(AnimWrapMode::Loop), mDefaultSpeed(1.0f), mDirty(AnimDirtyStateFlag::Skeleton)
+		: mDefaultWrapMode(AnimWrapMode::Loop), mDefaultSpeed(1.0f), mDirty(AnimDirtyStateFlag::Skeleton)
 	{
 		mId = AnimationManager::instance().registerAnimation(this);
 		mAnimProxy = bs_shared_ptr_new<AnimationProxy>(mId);
@@ -340,9 +340,9 @@ namespace BansheeEngine
 			clipInfo->state.speed = mDefaultSpeed;
 			clipInfo->state.weight = 1.0f;
 			clipInfo->state.wrapMode = mDefaultWrapMode;
-
-			mDirty |= AnimDirtyStateFlag::Value;
 		}
+
+		mDirty |= AnimDirtyStateFlag::Value;
 	}
 
 	void Animation::blendAdditive(const HAnimationClip& clip, float weight, float fadeLength, UINT32 layer)
@@ -379,22 +379,90 @@ namespace BansheeEngine
 
 	void Animation::blend1D(const Blend1DInfo& info, float t)
 	{
-		AnimationClipInfo* leftClipInfo = addClip(info.leftClip, (UINT32)-1, true);
-		if (leftClipInfo != nullptr)
+		if (info.numClips == 0)
+			return;
+
+		// Find valid range
+		float startPos = 0.0f;
+		float endPos = 0.0f;
+
+		for (UINT32 i = 0; i < info.numClips; i++)
+		{
+			startPos = std::min(startPos, info.clips[i].position);
+			endPos = std::min(endPos, info.clips[i].position);
+		}
+
+		float length = endPos - startPos;
+		if(Math::approxEquals(length, 0.0f) || info.numClips < 2)
+		{
+			play(info.clips[0].clip);
+			return;
+		}
+
+		// Clamp or loop time
+		bool loop = mDefaultWrapMode == AnimWrapMode::Loop;
+		if (t < startPos)
+		{
+			if (loop)
+				t = t - std::floor(t / length) * length;
+			else // Clamping
+				t = startPos;
+		}
+
+		if (t > endPos)
+		{
+			if (loop)
+				t = t - std::floor(t / length) * length;
+			else // Clamping
+				t = endPos;
+		}
+
+		// Find keys to blend between
+		UINT32 leftKey = 0;
+		UINT32 rightKey = 0;
+
+		INT32 start = 0;
+		INT32 searchLength = (INT32)info.numClips;
+
+		while (searchLength > 0)
 		{
-			leftClipInfo->state.time = 0.0f;
-			leftClipInfo->state.speed = 0.0f;
-			leftClipInfo->state.weight = 1.0f - t;
-			leftClipInfo->state.wrapMode = AnimWrapMode::Clamp;
+			INT32 half = searchLength >> 1;
+			INT32 mid = start + half;
+
+			if (t < info.clips[mid].position)
+			{
+				searchLength = half;
+			}
+			else
+			{
+				start = mid + 1;
+				searchLength -= half - 1;
+			}
 		}
 
-		AnimationClipInfo* rightClipInfo = addClip(info.rightClip, (UINT32)-1, false);
-		if(rightClipInfo != nullptr)
+		leftKey = start - 1;
+		rightKey = std::min(start, (INT32)info.numClips - 1);
+
+		float interpLength = info.clips[rightKey].position - info.clips[leftKey].position;
+		t = (t - info.clips[leftKey].position) / interpLength;
+
+		// Add clips and set weights
+		for(UINT32 i = 0; i < info.numClips; i++)
 		{
-			rightClipInfo->state.time = 0.0f;
-			rightClipInfo->state.speed = 0.0f;
-			rightClipInfo->state.weight = t;
-			rightClipInfo->state.wrapMode = AnimWrapMode::Clamp;
+			AnimationClipInfo* clipInfo = addClip(info.clips[i].clip, (UINT32)-1, i == 0);
+			if (clipInfo != nullptr)
+			{
+				clipInfo->state.time = 0.0f;
+				clipInfo->state.speed = 0.0f;
+				clipInfo->state.wrapMode = AnimWrapMode::Clamp;
+
+				if (i == leftKey)
+					clipInfo->state.weight = 1.0f - t;
+				else if (i == rightKey)
+					clipInfo->state.weight = t;
+				else
+					clipInfo->state.weight = 0.0f;
+			}
 		}
 
 		mDirty |= AnimDirtyStateFlag::Value;
@@ -483,14 +551,9 @@ namespace BansheeEngine
 					clipInfo->fadeLength = fadeLength;
 				}
 			}
-
-			mDirty |= AnimDirtyStateFlag::Value;
 		}
-	}
 
-	void Animation::blendSequential(const BlendSequentialInfo& info)
-	{
-		// TODO
+		mDirty |= AnimDirtyStateFlag::Value;
 	}
 
 	void Animation::stop(UINT32 layer)

+ 0 - 3
Source/BansheeEngine/Include/BsCAnimation.h

@@ -42,9 +42,6 @@ namespace BansheeEngine
 		/** @copydoc Animation::blendAdditive */
 		void blendAdditive(const HAnimationClip& clip, float weight, float fadeLength = 0.0f, UINT32 layer = 0);
 
-		/** @copydoc Animation::blendSequential */
-		void blendSequential(const BlendSequentialInfo& info);
-
 		/** @copydoc Animation::blend1D */
 		void blend1D(const Blend1DInfo& info, float t);
 

+ 0 - 6
Source/BansheeEngine/Source/BsCAnimation.cpp

@@ -53,12 +53,6 @@ namespace BansheeEngine
 			mInternal->play(clip);
 	}
 
-	void CAnimation::blendSequential(const BlendSequentialInfo& info)
-	{
-		if (mInternal != nullptr)
-			mInternal->blendSequential(info);
-	}
-
 	void CAnimation::blend1D(const Blend1DInfo& info, float t)
 	{
 		if (mInternal != nullptr)

+ 7 - 27
Source/MBansheeEngine/Animation/Animation.cs

@@ -25,22 +25,12 @@ namespace BansheeEngine
     }
 
     /// <summary>
-    /// Defines a single animation clip in <see cref="BlendSequentialInfo"/>.
+    /// Represents an animation clip used in 1D blending. Each clip has a position on the number line.
     /// </summary>
-    public class BlendSequentialClipInfo
+    public class BlendClipInfo
     {
         public AnimationClip clip;
-        public float fadeTime = 0.0f;
-        public float startTime = 0.0f;
-        public float endTime = 0.0f;
-    }
-
-    /// <summary>
-    /// Defines a sequential blend where one animation clip is played after another, with an optional fade between them.
-    /// </summary>
-    public class BlendSequentialInfo
-    {
-        public BlendSequentialClipInfo[] clips;
+        public float position;
     }
 
     /// <summary>
@@ -48,8 +38,7 @@ namespace BansheeEngine
     /// </summary>
     public class Blend1DInfo
     {
-        public AnimationClip leftClip;
-        public AnimationClip rightClip;
+        public BlendClipInfo[] clips;
     }
 
     /// <summary>
@@ -218,21 +207,12 @@ namespace BansheeEngine
         }
 
         /// <summary>
-        /// Plays a set of animation clips sequentially one after another, with an optional fade between them.
-        /// </summary>
-        /// <param name="info">Describes all animation clips to play.</param>
-        public void BlendSequential(BlendSequentialInfo info)
-        {
-            if (_native != null)
-                _native.BlendSequential(info);
-        }
-
-        /// <summary>
-        /// Blend two animation clips between each other using linear interpolation. Unlike normal animations these
+        /// Blends multiple animation clips between each other using linear interpolation. Unlike normal animations these
         /// animations are not advanced with the progress of time, and is instead expected the user manually changes the
         /// <see cref="t"/> parameter.
         /// </summary>
-        /// <param name="info">Information about the clips to blend.</param>
+        /// <param name="info">Information about the clips to blend. Clip positions must be sorted from lowest to highest.
+        ///                    </param>
         /// <param name="t">Parameter that controls the blending, in range [0, 1]. t = 0 means left animation has full
         ///                 influence, t = 1 means right animation has full influence.</param>
         public void Blend1D(Blend1DInfo info, float t)

+ 0 - 11
Source/MBansheeEngine/Animation/Interop/NativeAnimation.cs

@@ -41,14 +41,6 @@ namespace BansheeEngine
             Internal_BlendAdditive(mCachedPtr, clipPtr, weight, fadeLength, layer);
         }
 
-        public void BlendSequential(BlendSequentialInfo info)
-        {
-            if (info == null)
-                return;
-
-            Internal_BlendSequential(mCachedPtr, info);
-        }
-
         public void Blend1D(Blend1DInfo info, float t)
         {
             if (info == null)
@@ -136,9 +128,6 @@ namespace BansheeEngine
         private static extern void Internal_BlendAdditive(IntPtr thisPtr, IntPtr clipPtr, float weight, float fadeLength,
             int layer);
 
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_BlendSequential(IntPtr thisPtr, BlendSequentialInfo info);
-
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_Blend1D(IntPtr thisPtr, Blend1DInfo info, float t);
 

+ 2 - 2
Source/SBansheeEditor/Include/BsScriptImportOptions.h

@@ -218,10 +218,10 @@ namespace BansheeEngine
 	};
 
 	/** Helper class for dealing with AnimationSplitInfo structure. */
-	class ScriptAnimationSplitInfo : public ScriptObject<ScriptAnimationSplitInfo>
+	class BS_SCR_BED_EXPORT ScriptAnimationSplitInfo : public ScriptObject<ScriptAnimationSplitInfo>
 	{
 	public:
-		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "AnimationSplitInfo")
+		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "AnimationSplitInfo")
 
 		/** Converts managed split info to its native counterpart. */
 		static AnimationSplitInfo fromManaged(MonoObject* object);

+ 6 - 28
Source/SBansheeEngine/Include/BsScriptAnimation.h

@@ -40,7 +40,6 @@ namespace BansheeEngine
 
 		static void internal_Play(ScriptAnimation* thisPtr, ScriptAnimationClip* clip);
 		static void internal_BlendAdditive(ScriptAnimation* thisPtr, ScriptAnimationClip* clip, float weight, float fadeLength, UINT32 layer);
-		static void internal_BlendSequential(ScriptAnimation* thisPtr, MonoObject* info);
 		static void internal_Blend1D(ScriptAnimation* thisPtr, MonoObject* info, float t);
 		static void internal_Blend2D(ScriptAnimation* thisPtr, MonoObject* info, Vector2* t);
 		static void internal_CrossFade(ScriptAnimation* thisPtr, ScriptAnimationClip* clip, float fadeLength);
@@ -53,43 +52,23 @@ namespace BansheeEngine
 		static void internal_SetState(ScriptAnimation* thisPtr, ScriptAnimationClip* clip, AnimationClipState* state);
 	};
 
-	/** Helper class for dealing with BlendSequentialInfo structure. */
-	class ScriptBlendSequentialInfo : public ScriptObject<ScriptBlendSequentialInfo>
-	{
-	public:
-		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "BlendSequentialInfo")
-
-		/** Converts managed split info to its native counterpart. */
-		static BlendSequentialInfo fromManaged(MonoObject* object);
-
-	private:
-		ScriptBlendSequentialInfo(MonoObject* instance);
-
-		/************************************************************************/
-		/* 								CLR HOOKS						   		*/
-		/************************************************************************/
-		static MonoField* clipsField;
-	};
-
 	/** Helper class for dealing with BlendSequentialClipInfo structure. */
-	class ScriptBlendSequentialClipInfo : public ScriptObject<ScriptBlendSequentialClipInfo>
+	class ScriptBlendClipInfo : public ScriptObject<ScriptBlendClipInfo>
 	{
 	public:
-		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "BlendSequentialClipInfo")
+		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "BlendClipInfo")
 
 		/** Converts managed split info to its native counterpart. */
-		static BlendSequentialClipInfo fromManaged(MonoObject* object);
+		static BlendClipInfo fromManaged(MonoObject* object);
 
 	private:
-		ScriptBlendSequentialClipInfo(MonoObject* instance);
+		ScriptBlendClipInfo(MonoObject* instance);
 
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
 		static MonoField* clipField;
-		static MonoField* startTimeField;
-		static MonoField* endTimeField;
-		static MonoField* fadeTimeField;
+		static MonoField* positionField;
 	};
 
 	/** Helper class for dealing with Blend1DInfo structure. */
@@ -107,8 +86,7 @@ namespace BansheeEngine
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
-		static MonoField* leftClipField;
-		static MonoField* rightClipField;
+		static MonoField* clipsField;
 	};
 
 	/** Helper class for dealing with Blend2DInfo structure. */

+ 19 - 66
Source/SBansheeEngine/Source/BsScriptAnimation.cpp

@@ -29,7 +29,6 @@ namespace BansheeEngine
 
 		metaData.scriptClass->addInternalCall("Internal_Play", &ScriptAnimation::internal_Play);
 		metaData.scriptClass->addInternalCall("Internal_BlendAdditive", &ScriptAnimation::internal_BlendAdditive);
-		metaData.scriptClass->addInternalCall("Internal_BlendSequential", &ScriptAnimation::internal_BlendSequential);
 		metaData.scriptClass->addInternalCall("Internal_Blend1D", &ScriptAnimation::internal_Blend1D);
 		metaData.scriptClass->addInternalCall("Internal_Blend2D", &ScriptAnimation::internal_Blend2D);
 		metaData.scriptClass->addInternalCall("Internal_CrossFade", &ScriptAnimation::internal_CrossFade);
@@ -81,12 +80,6 @@ namespace BansheeEngine
 		thisPtr->getInternal()->blendAdditive(nativeClip, weight, fadeLength, layer);
 	}
 
-	void ScriptAnimation::internal_BlendSequential(ScriptAnimation* thisPtr, MonoObject* info)
-	{
-		BlendSequentialInfo nativeInfo = ScriptBlendSequentialInfo::fromManaged(info);
-		thisPtr->getInternal()->blendSequential(nativeInfo);
-	}
-
 	void ScriptAnimation::internal_Blend1D(ScriptAnimation* thisPtr, MonoObject* info, float t)
 	{
 		Blend1DInfo nativeInfo = ScriptBlend1DInfo::fromManaged(info);
@@ -141,54 +134,22 @@ namespace BansheeEngine
 		thisPtr->getInternal()->setState(nativeClip, *state);
 	}
 
-	MonoField* ScriptBlendSequentialInfo::clipsField = nullptr;
+	MonoField* ScriptBlendClipInfo::clipField = nullptr;
+	MonoField* ScriptBlendClipInfo::positionField = nullptr;
 
-	ScriptBlendSequentialInfo::ScriptBlendSequentialInfo(MonoObject* instance)
+	ScriptBlendClipInfo::ScriptBlendClipInfo(MonoObject* instance)
 		:ScriptObject(instance)
 	{ }
 
-	void ScriptBlendSequentialInfo::initRuntimeData()
-	{
-		clipsField = metaData.scriptClass->getField("clips");
-	}
-
-	BlendSequentialInfo ScriptBlendSequentialInfo::fromManaged(MonoObject* instance)
-	{
-		MonoArray* managedClipsArray = (MonoArray*)clipsField->getValueBoxed(instance);
-		if (managedClipsArray == nullptr)
-			return BlendSequentialInfo(0);
-
-		ScriptArray clipsArray(managedClipsArray);
-
-		UINT32 numClips = clipsArray.size();
-		BlendSequentialInfo output(numClips);
-		
-		for (UINT32 i = 0; i < numClips; i++)
-			output.clips[i] = ScriptBlendSequentialClipInfo::fromManaged(clipsArray.get<MonoObject*>(i));
-
-		return output;
-	}
-
-	MonoField* ScriptBlendSequentialClipInfo::clipField = nullptr;
-	MonoField* ScriptBlendSequentialClipInfo::startTimeField = nullptr;
-	MonoField* ScriptBlendSequentialClipInfo::endTimeField = nullptr;
-	MonoField* ScriptBlendSequentialClipInfo::fadeTimeField = nullptr;
-
-	ScriptBlendSequentialClipInfo::ScriptBlendSequentialClipInfo(MonoObject* instance)
-		:ScriptObject(instance)
-	{ }
-
-	void ScriptBlendSequentialClipInfo::initRuntimeData()
+	void ScriptBlendClipInfo::initRuntimeData()
 	{
 		clipField = metaData.scriptClass->getField("clip");
-		startTimeField = metaData.scriptClass->getField("startTime");
-		endTimeField = metaData.scriptClass->getField("endTime");
-		fadeTimeField = metaData.scriptClass->getField("fadeTime");
+		positionField = metaData.scriptClass->getField("position");
 	}
 
-	BlendSequentialClipInfo ScriptBlendSequentialClipInfo::fromManaged(MonoObject* instance)
+	BlendClipInfo ScriptBlendClipInfo::fromManaged(MonoObject* instance)
 	{
-		BlendSequentialClipInfo output;
+		BlendClipInfo output;
 
 		MonoObject* managedAnimClip = clipField->getValueBoxed(instance);
 		if(managedAnimClip)
@@ -197,15 +158,12 @@ namespace BansheeEngine
 			output.clip = clip->getHandle();
 		}
 		
-		startTimeField->getValue(instance, &output.startTime);
-		endTimeField->getValue(instance, &output.endTime);
-		fadeTimeField->getValue(instance, &output.fadeTime);
+		positionField->getValue(instance, &output.position);
 
 		return output;
 	}
 
-	MonoField* ScriptBlend1DInfo::leftClipField = nullptr;
-	MonoField* ScriptBlend1DInfo::rightClipField = nullptr;
+	MonoField* ScriptBlend1DInfo::clipsField = nullptr;
 
 	ScriptBlend1DInfo::ScriptBlend1DInfo(MonoObject* instance)
 		:ScriptObject(instance)
@@ -213,27 +171,22 @@ namespace BansheeEngine
 
 	void ScriptBlend1DInfo::initRuntimeData()
 	{
-		leftClipField = metaData.scriptClass->getField("leftClip");
-		rightClipField = metaData.scriptClass->getField("rightClip");
+		clipsField = metaData.scriptClass->getField("clips");
 	}
 
 	Blend1DInfo ScriptBlend1DInfo::fromManaged(MonoObject* instance)
 	{
-		Blend1DInfo output;
+		MonoArray* managedClipsArray = (MonoArray*)clipsField->getValueBoxed(instance);
+		if (managedClipsArray == nullptr)
+			return Blend1DInfo(0);
 
-		MonoObject* managedLeftClip = leftClipField->getValueBoxed(instance);
-		if (managedLeftClip != nullptr)
-		{
-			ScriptAnimationClip* clip = ScriptAnimationClip::toNative(managedLeftClip);
-			output.leftClip = clip->getHandle();
-		}
+		ScriptArray clipsArray(managedClipsArray);
 
-		MonoObject* managedRightClip = rightClipField->getValueBoxed(instance);
-		if (managedRightClip != nullptr)
-		{
-			ScriptAnimationClip* clip = ScriptAnimationClip::toNative(managedRightClip);
-			output.rightClip = clip->getHandle();
-		}
+		UINT32 numClips = clipsArray.size();
+		Blend1DInfo output(numClips);
+
+		for (UINT32 i = 0; i < numClips; i++)
+			output.clips[i] = ScriptBlendClipInfo::fromManaged(clipsArray.get<MonoObject*>(i));
 
 		return output;
 	}