Ver código fonte

Added a way to set and retrieve animation curves to AnimationClip in C#

BearishSun 9 anos atrás
pai
commit
b5b2b76c83

+ 31 - 60
Source/BansheeCore/Include/BsAnimationClip.h

@@ -19,38 +19,12 @@ namespace BansheeEngine
 	/** A set of animation curves representing translation/rotation/scale and generic animation. */
 	struct AnimationCurves
 	{
-		Vector<TNamedAnimationCurve<Vector3>> position;
-		Vector<TNamedAnimationCurve<Quaternion>> rotation;
-		Vector<TNamedAnimationCurve<Vector3>> scale;
-		Vector<TNamedAnimationCurve<float>> generic;
-	};
-
-	/** Types of curves in an AnimationClip. */
-	enum class CurveType
-	{
-		Position,
-		Rotation,
-		Scale,
-		Generic
-	};
-
-	/** 
-	 * Contains animation curves for translation/rotation/scale of scene objects/skeleton bones, as well as curves for
-	 * generic property animation. 
-	 */
-	class BS_CORE_EXPORT AnimationClip : public Resource
-	{
-	public:
-		virtual ~AnimationClip() { }
-
 		/** 
 		 * Registers a new curve used for animating position. 
 		 *
 		 * @param[in]	name		Unique name of the curve. This name will be used mapping the curve to the relevant bone
 		 *							in a skeleton, if any.
 		 * @param[in]	curve		Curve to add to the clip.
-		 *
-		 * @note Adding a new curve to a clip is a relatively slow operations and shouldn't be done at runtime.
 		 */
 		void addPositionCurve(const String& name, const TAnimationCurve<Vector3>& curve);
 
@@ -60,8 +34,6 @@ namespace BansheeEngine
 		 * @param[in]	name		Unique name of the curve. This name will be used mapping the curve to the relevant bone
 		 *							in a skeleton, if any.
 		 * @param[in]	curve		Curve to add to the clip.
-		 *
-		 * @note Adding a new curve to a clip is a relatively slow operations and shouldn't be done at runtime.
 		 */
 		void addRotationCurve(const String& name, const TAnimationCurve<Quaternion>& curve);
 
@@ -71,8 +43,6 @@ namespace BansheeEngine
 		 * @param[in]	name		Unique name of the curve. This name will be used mapping the curve to the relevant bone
 		 *							in a skeleton, if any.
 		 * @param[in]	curve		Curve to add to the clip.
-		 *
-		 * @note Adding a new curve to a clip is a relatively slow operations and shouldn't be done at runtime.
 		 */
 		void addScaleCurve(const String& name, const TAnimationCurve<Vector3>& curve);
 
@@ -82,53 +52,54 @@ namespace BansheeEngine
 		 * @param[in]	name		Unique name of the curve. This can be used for retrieving the value of the curve
 		 *							from animation.
 		 * @param[in]	curve		Curve to add to the clip.
-		 *
-		 * @note Adding a new curve to a clip is a relatively slow operations and shouldn't be done at runtime.
 		 */
 		void addGenericCurve(const String& name, const TAnimationCurve<float>& curve);
 
-		/**
-		 * Removes an existing curve from the clip.
-		 *
-		 * @param[in]	name	Name of the curve to remove.
-		 *
-		 * @note Removing curve from a clip is a relatively slow operations and shouldn't be done at runtime.
-		 */
+		/** Removes an existing curve from the clip. */
 		void removePositionCurve(const String& name);
 
-		/**
-		 * Removes an existing curve from the clip.
-		 *
-		 * @param[in]	name	Name of the curve to remove.
-		 *
-		 * @note Removing curve from a clip is a relatively slow operations and shouldn't be done at runtime.
-		 */
+		/** Removes an existing curve from the clip. */
 		void removeRotationCurve(const String& name);
 
-		/**
-		 * Removes an existing curve from the clip.
-		 *
-		 * @param[in]	name	Name of the curve to remove.
-		 *
-		 * @note Removing curve from a clip is a relatively slow operations and shouldn't be done at runtime.
-		 */
+		/** Removes an existing curve from the clip. */
 		void removeScaleCurve(const String& name);
 
-		/**
-		 * Removes an existing curve from the clip.
-		 *
-		 * @param[in]	name	Name of the curve to remove.
-		 *
-		 * @note Removing curve from a clip is a relatively slow operations and shouldn't be done at runtime.
-		 */
+		/** Removes an existing curve from the clip. */
 		void removeGenericCurve(const String& name);
 
+		Vector<TNamedAnimationCurve<Vector3>> position;
+		Vector<TNamedAnimationCurve<Quaternion>> rotation;
+		Vector<TNamedAnimationCurve<Vector3>> scale;
+		Vector<TNamedAnimationCurve<float>> generic;
+	};
+
+	/** Types of curves in an AnimationClip. */
+	enum class CurveType
+	{
+		Position,
+		Rotation,
+		Scale,
+		Generic
+	};
+
+	/** 
+	 * Contains animation curves for translation/rotation/scale of scene objects/skeleton bones, as well as curves for
+	 * generic property animation. 
+	 */
+	class BS_CORE_EXPORT AnimationClip : public Resource
+	{
+	public:
+		virtual ~AnimationClip() { }
+
 		/** 
 		 * Returns all curves stored in the animation. Returned value will not be updated if the animation clip curves are
 		 * added or removed. Caller must monitor for changes and retrieve a new set of curves when that happens.
 		 */
 		SPtr<AnimationCurves> getCurves() const { return mCurves; }
 
+		/** Assigns a new set of curves to be used by the animation. The clip will store a copy of this object.*/
+		void setCurves(const AnimationCurves& curves);
+
 		/**
 		 * Maps skeleton bone names to animation clip names, and returns a set of indices that can be easily used for
 		 * locating an animation curve based on the bone index.

+ 15 - 1
Source/BansheeCore/Include/BsAnimationCurve.h

@@ -60,6 +60,17 @@ namespace BansheeEngine
 		 */
 		T evaluate(float time, bool loop = true) const;
 
+		/**
+		 * Evaluate the animation curve at the specified time and returns a new keyframe containing the evaluated value
+		 * and tangents.
+		 *
+		 * @param[in]	time	Time to evaluate the curve at.		
+		 * @param[in]	loop	If true the curve will loop when it goes past the end or beggining. Otherwise the curve 
+		 *						value will be clamped.
+		 * @return				Keyframe containing the interpolated value and tangents at provided time.
+		 */
+		KeyFrame evaluateKey(float time, bool loop = true) const;
+
 		/** 
 		 * Splits a piece of the animation curve into a separate animation curve. 
 		 *
@@ -120,7 +131,7 @@ namespace BansheeEngine
 		 * @param[in]	t		Curve time to interpolate the keys at.
 		 * @return				Interpolated key value.
 		 */
-		KeyFrame evaluateKey(const KeyFrame& lhs, const KeyFrame& rhs, float time);
+		KeyFrame evaluateKey(const KeyFrame& lhs, const KeyFrame& rhs, float time) const;
 
 		/** 
 		 * Evaluates a value at the cached curve. Caller must ensure the request time falls within the cached curve range.
@@ -132,6 +143,9 @@ namespace BansheeEngine
 		 */
 		T evaluateCache(float time, const TCurveCache<T>& animInstance) const;
 
+		/** Clamps the time argument to valid range. */
+		void clampTime(float& time, bool loop) const;
+
 		static const UINT32 CACHE_LOOKAHEAD;
 
 		Vector<KeyFrame> mKeyframes;

+ 66 - 140
Source/BansheeCore/Source/BsAnimationClip.cpp

@@ -7,199 +7,125 @@
 
 namespace BansheeEngine
 {
-	AnimationClip::AnimationClip()
-		: Resource(false), mVersion(0), mCurves(bs_shared_ptr_new<AnimationCurves>()), mIsAdditive(false)
+
+	void AnimationCurves::addPositionCurve(const String& name, const TAnimationCurve<Vector3>& curve)
 	{
+		auto iterFind = std::find_if(position.begin(), position.end(), [&](auto x) { return x.name == name; });
 
+		if (iterFind != position.end())
+			iterFind->curve = curve;
+		else
+			position.push_back({ name, curve });
 	}
 
-	AnimationClip::AnimationClip(const SPtr<AnimationCurves>& curves, bool isAdditive)
-		: Resource(false), mVersion(0), mCurves(curves), mIsAdditive(isAdditive)
+	void AnimationCurves::addRotationCurve(const String& name, const TAnimationCurve<Quaternion>& curve)
 	{
+		auto iterFind = std::find_if(rotation.begin(), rotation.end(), [&](auto x) { return x.name == name; });
 
+		if (iterFind != rotation.end())
+			iterFind->curve = curve;
+		else
+			rotation.push_back({ name, curve });
 	}
 
-	HAnimationClip AnimationClip::create(bool isAdditive)
+	void AnimationCurves::addScaleCurve(const String& name, const TAnimationCurve<Vector3>& curve)
 	{
-		return static_resource_cast<AnimationClip>(gResources()._createResourceHandle(_createPtr(nullptr, isAdditive)));
-	}
+		auto iterFind = std::find_if(scale.begin(), scale.end(), [&](auto x) { return x.name == name; });
 
-	HAnimationClip AnimationClip::create(const SPtr<AnimationCurves>& curves, bool isAdditive)
-	{
-		return static_resource_cast<AnimationClip>(gResources()._createResourceHandle(_createPtr(curves, isAdditive)));
+		if (iterFind != scale.end())
+			iterFind->curve = curve;
+		else
+			scale.push_back({ name, curve });
 	}
 
-	SPtr<AnimationClip> AnimationClip::createEmpty()
+	void AnimationCurves::addGenericCurve(const String& name, const TAnimationCurve<float>& curve)
 	{
-		AnimationClip* rawPtr = new (bs_alloc<AnimationClip>()) AnimationClip();
-
-		SPtr<AnimationClip> newClip = bs_core_ptr<AnimationClip>(rawPtr);
-		newClip->_setThisPtr(newClip);
+		auto iterFind = std::find_if(generic.begin(), generic.end(), [&](auto x) { return x.name == name; });
 
-		return newClip;
+		if (iterFind != generic.end())
+			iterFind->curve = curve;
+		else
+			generic.push_back({ name, curve });
 	}
 
-	SPtr<AnimationClip> AnimationClip::_createPtr(const SPtr<AnimationCurves>& curves, bool isAdditive)
+	void AnimationCurves::removePositionCurve(const String& name)
 	{
-		AnimationClip* rawPtr = new (bs_alloc<AnimationClip>()) AnimationClip(curves, isAdditive);
-
-		SPtr<AnimationClip> newClip = bs_core_ptr<AnimationClip>(rawPtr);
-		newClip->_setThisPtr(newClip);
-		newClip->initialize();
+		auto iterFind = std::find_if(position.begin(), position.end(), [&](auto x) { return x.name == name; });
 
-		return newClip;
+		if (iterFind != position.end())
+			position.erase(iterFind);
 	}
 
-	void AnimationClip::addPositionCurve(const String& name, const TAnimationCurve<Vector3>& curve)
+	void AnimationCurves::removeRotationCurve(const String& name)
 	{
-		SPtr<AnimationCurves> newCurves = bs_shared_ptr_new<AnimationCurves>();
-		newCurves->rotation = mCurves->rotation;
-		newCurves->scale = mCurves->scale;
-		newCurves->generic = mCurves->generic;
-
-		for(auto& entry : mCurves->position)
-		{
-			if (entry.name != name)
-				newCurves->position.push_back(entry);
-		}
-
-		newCurves->position.push_back({ name, curve });
-		mCurves = newCurves;
+		auto iterFind = std::find_if(rotation.begin(), rotation.end(), [&](auto x) { return x.name == name; });
 
-		buildNameMapping();
-		mVersion++;
+		if (iterFind != rotation.end())
+			rotation.erase(iterFind);
 	}
 
-	void AnimationClip::addRotationCurve(const String& name, const TAnimationCurve<Quaternion>& curve)
+	void AnimationCurves::removeScaleCurve(const String& name)
 	{
-		SPtr<AnimationCurves> newCurves = bs_shared_ptr_new<AnimationCurves>();
-		newCurves->position = mCurves->position;
-		newCurves->scale = mCurves->scale;
-		newCurves->generic = mCurves->generic;
-
-		for (auto& entry : mCurves->rotation)
-		{
-			if (entry.name != name)
-				newCurves->rotation.push_back(entry);
-		}
-
-		newCurves->rotation.push_back({ name, curve });
-		mCurves = newCurves;
+		auto iterFind = std::find_if(scale.begin(), scale.end(), [&](auto x) { return x.name == name; });
 
-		buildNameMapping();
-		mVersion++;
+		if (iterFind != scale.end())
+			scale.erase(iterFind);
 	}
 
-	void AnimationClip::addScaleCurve(const String& name, const TAnimationCurve<Vector3>& curve)
+	void AnimationCurves::removeGenericCurve(const String& name)
 	{
-		SPtr<AnimationCurves> newCurves = bs_shared_ptr_new<AnimationCurves>();
-		newCurves->position = mCurves->position;
-		newCurves->rotation = mCurves->rotation;
-		newCurves->generic = mCurves->generic;
-
-		for (auto& entry : mCurves->scale)
-		{
-			if (entry.name != name)
-				newCurves->scale.push_back(entry);
-		}
-
-		newCurves->scale.push_back({ name, curve });
-		mCurves = newCurves;
+		auto iterFind = std::find_if(generic.begin(), generic.end(), [&](auto x) { return x.name == name; });
 
-		buildNameMapping();
-		mVersion++;
+		if (iterFind != generic.end())
+			generic.erase(iterFind);
 	}
 
-	void AnimationClip::addGenericCurve(const String& name, const TAnimationCurve<float>& curve)
+	AnimationClip::AnimationClip()
+		: Resource(false), mVersion(0), mCurves(bs_shared_ptr_new<AnimationCurves>()), mIsAdditive(false)
 	{
-		SPtr<AnimationCurves> newCurves = bs_shared_ptr_new<AnimationCurves>();
-		newCurves->position = mCurves->position;
-		newCurves->rotation = mCurves->rotation;
-		newCurves->scale = mCurves->scale;
 
-		for (auto& entry : mCurves->generic)
-		{
-			if (entry.name != name)
-				newCurves->generic.push_back(entry);
-		}
-
-		mCurves = newCurves;
-
-		buildNameMapping();
-		mVersion++;
 	}
 
-	void AnimationClip::removePositionCurve(const String& name)
+	AnimationClip::AnimationClip(const SPtr<AnimationCurves>& curves, bool isAdditive)
+		: Resource(false), mVersion(0), mCurves(curves), mIsAdditive(isAdditive)
 	{
-		SPtr<AnimationCurves> newCurves = bs_shared_ptr_new<AnimationCurves>();
-		newCurves->rotation = mCurves->rotation;
-		newCurves->scale = mCurves->scale;
-		newCurves->generic = mCurves->generic;
 
-		for (auto& entry : mCurves->position)
-		{
-			if (entry.name != name)
-				newCurves->position.push_back(entry);
-		}
-
-		mCurves = newCurves;
+	}
 
-		buildNameMapping();
-		mVersion++;
+	HAnimationClip AnimationClip::create(bool isAdditive)
+	{
+		return static_resource_cast<AnimationClip>(gResources()._createResourceHandle(_createPtr(nullptr, isAdditive)));
 	}
 
-	void AnimationClip::removeRotationCurve(const String& name)
+	HAnimationClip AnimationClip::create(const SPtr<AnimationCurves>& curves, bool isAdditive)
 	{
-		SPtr<AnimationCurves> newCurves = bs_shared_ptr_new<AnimationCurves>();
-		newCurves->position = mCurves->position;
-		newCurves->scale = mCurves->scale;
-		newCurves->generic = mCurves->generic;
+		return static_resource_cast<AnimationClip>(gResources()._createResourceHandle(_createPtr(curves, isAdditive)));
+	}
 
-		for (auto& entry : mCurves->rotation)
-		{
-			if (entry.name != name)
-				newCurves->rotation.push_back(entry);
-		}
+	SPtr<AnimationClip> AnimationClip::createEmpty()
+	{
+		AnimationClip* rawPtr = new (bs_alloc<AnimationClip>()) AnimationClip();
 
-		mCurves = newCurves;
+		SPtr<AnimationClip> newClip = bs_core_ptr<AnimationClip>(rawPtr);
+		newClip->_setThisPtr(newClip);
 
-		buildNameMapping();
-		mVersion++;
+		return newClip;
 	}
 
-	void AnimationClip::removeScaleCurve(const String& name)
+	SPtr<AnimationClip> AnimationClip::_createPtr(const SPtr<AnimationCurves>& curves, bool isAdditive)
 	{
-		SPtr<AnimationCurves> newCurves = bs_shared_ptr_new<AnimationCurves>();
-		newCurves->position = mCurves->position;
-		newCurves->rotation = mCurves->rotation;
-		newCurves->generic = mCurves->generic;
-
-		for (auto& entry : mCurves->scale)
-		{
-			if (entry.name != name)
-				newCurves->scale.push_back(entry);
-		}
+		AnimationClip* rawPtr = new (bs_alloc<AnimationClip>()) AnimationClip(curves, isAdditive);
 
-		mCurves = newCurves;
+		SPtr<AnimationClip> newClip = bs_core_ptr<AnimationClip>(rawPtr);
+		newClip->_setThisPtr(newClip);
+		newClip->initialize();
 
-		buildNameMapping();
-		mVersion++;
+		return newClip;
 	}
 
-	void AnimationClip::removeGenericCurve(const String& name)
+	void AnimationClip::setCurves(const AnimationCurves& curves)
 	{
-		SPtr<AnimationCurves> newCurves = bs_shared_ptr_new<AnimationCurves>();
-		newCurves->position = mCurves->position;
-		newCurves->rotation = mCurves->rotation;
-		newCurves->scale = mCurves->scale;
-
-		for (auto& entry : mCurves->generic)
-		{
-			if (entry.name != name)
-				newCurves->generic.push_back(entry);
-		}
-
-		mCurves = newCurves;
+		*mCurves = curves;
 
 		buildNameMapping();
 		mVersion++;

+ 43 - 18
Source/BansheeCore/Source/BsAnimationCurve.cpp

@@ -256,23 +256,7 @@ namespace BansheeEngine
 		if (mKeyframes.size() == 0)
 			return T();
 
-		// Clamp to start or loop
-		if (time < mStart)
-		{
-			if (loop)
-				time = time - std::floor(time / mLength) * mLength;
-			else // Clamping
-				time = mStart;
-		}
-
-		// Clamp to end or loop
-		if (time > mEnd)
-		{
-			if (loop)
-				time = time - std::floor(time / mLength) * mLength;
-			else // Clamping
-				time = mEnd;
-		}
+		clampTime(time, loop);
 
 		UINT32 leftKeyIdx;
 		UINT32 rightKeyIdx;
@@ -308,6 +292,25 @@ namespace BansheeEngine
 		return output;
 	}
 
+	template <class T>
+	TKeyframe<T> TAnimationCurve<T>::evaluateKey(float time, bool loop) const
+	{
+		if (mKeyframes.size() == 0)
+			return TKeyframe<T>();
+
+		clampTime(time, loop);
+
+		UINT32 leftKeyIdx;
+		UINT32 rightKeyIdx;
+
+		findKeys(time, leftKeyIdx, rightKeyIdx);
+
+		const KeyFrame& leftKey = mKeyframes[leftKeyIdx];
+		const KeyFrame& rightKey = mKeyframes[rightKeyIdx];
+
+		return evaluateKey(leftKey, rightKey, time);
+	}
+
 	template <class T>
 	T TAnimationCurve<T>::evaluateCache(float time, const TCurveCache<T>& cache) const
 	{
@@ -409,7 +412,7 @@ namespace BansheeEngine
 	}
 
 	template <class T>
-	TKeyframe<T> TAnimationCurve<T>::evaluateKey(const KeyFrame& lhs, const KeyFrame& rhs, float time)
+	TKeyframe<T> TAnimationCurve<T>::evaluateKey(const KeyFrame& lhs, const KeyFrame& rhs, float time) const
 	{
 		float length = rhs.time - lhs.time;
 		float t;
@@ -505,6 +508,28 @@ namespace BansheeEngine
 			mKeyframes[i].value = getDiff(mKeyframes[i].value, refKey.value);
 	}
 
+	template <class T>
+	void TAnimationCurve<T>::clampTime(float& time, bool loop) const
+	{
+		// Clamp to start or loop
+		if (time < mStart)
+		{
+			if (loop)
+				time = time - std::floor(time / mLength) * mLength;
+			else // Clamping
+				time = mStart;
+		}
+
+		// Clamp to end or loop
+		if (time > mEnd)
+		{
+			if (loop)
+				time = time - std::floor(time / mLength) * mLength;
+			else // Clamping
+				time = mEnd;
+		}
+	}
+
 	template class TAnimationCurve<Vector3>;
 	template class TAnimationCurve<Quaternion>;
 	template class TAnimationCurve<float>;

+ 1 - 1
Source/BansheeEditor/Source/BsEditorUtility.cpp

@@ -87,7 +87,7 @@ namespace BansheeEngine
 		}
 
 		if (gotOneMesh)
-			return center / count;
+			return center / (float)count;
 
 		return Vector3::ZERO;
 	}

+ 16 - 1
Source/MBansheeEngine/Animation/AnimationClip.cs

@@ -10,16 +10,31 @@ namespace BansheeEngine
      */
 
     /// <summary>
-    /// Contains animation curves for translation/rotation/scale of scene objects/skeleton bones, as well as curves for
+    /// Contains animation curves for translation/rotation/scale of scene object/skeleton bones, as well as curves for
     /// generic property animation.
     /// </summary>
     public class AnimationClip : Resource
     {
+        /// <summary>
+        /// A set of all curves stored in the animation clip.
+        /// </summary>
+        public AnimationCurves Curves
+        {
+            get { return Internal_GetAnimationCurves(mCachedPtr); }
+            set { Internal_SetAnimationCurves(mCachedPtr, value); }
+        }
+
         /// <summary>
         /// Constructor for internal use by the runtime.
         /// </summary>
         private AnimationClip()
         { }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern AnimationCurves Internal_GetAnimationCurves(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetAnimationCurves(IntPtr thisPtr, AnimationCurves curves);
     }
 
     /** @} */

+ 74 - 0
Source/MBansheeEngine/Animation/AnimationCurve.cs

@@ -36,6 +36,12 @@ namespace BansheeEngine
     /// </summary>
     public class AnimationCurve : ScriptObject
     {
+        /// <summary>
+        /// Constructor for internal runtime use only.
+        /// </summary>
+        private AnimationCurve()
+        { }
+
         /// <summary>
         /// Creates a new animation curve.
         /// </summary>
@@ -78,4 +84,72 @@ namespace BansheeEngine
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern float Internal_Evaluate(IntPtr thisPtr, float time, bool loop);
     }
+    
+    /// <summary>
+    /// A set of animation curves for a 3D vector paired with a name.
+    /// </summary>
+    public class NamedVector3Curve
+    {
+        /// <summary>
+        /// Constructs a new named animation curve.
+        /// </summary>
+        /// <param name="name">Name of the curve.</param>
+        /// <param name="x">Curve representing the x axis of the vector.</param>
+        /// <param name="y">Curve representing the y axis of the vector.</param>
+        /// <param name="z">Curve representing the z axis of the vector.</param>
+        public NamedVector3Curve(string name, AnimationCurve x, AnimationCurve y, AnimationCurve z)
+        {
+            Name = name;
+            X = x;
+            Y = y;
+            Z = z;
+        }
+
+        /// <summary>
+        /// Name of the curve.
+        /// </summary>
+        public string Name;
+
+        /// <summary>
+        /// Animation curve for the x axis.
+        /// </summary>
+        public AnimationCurve X;
+
+        /// <summary>
+        /// Animation curve for the y axis.
+        /// </summary>
+        public AnimationCurve Y;
+
+        /// <summary>
+        /// Animation curve for the z axis.
+        /// </summary>
+        public AnimationCurve Z;
+    }
+
+    /// <summary>
+    /// An animatio curve for a single floating point value paired with a name.
+    /// </summary>
+    public class NamedFloatCurve
+    {
+        /// <summary>
+        /// Constructs a new named animation curve.
+        /// </summary>
+        /// <param name="name">Name of the curve.</param>
+        /// <param name="curve">Curve representing the floating point values.</param>
+        public NamedFloatCurve(string name, AnimationCurve curve)
+        {
+            Name = name;
+            Curve = curve;
+        }
+
+        /// <summary>
+        /// Name of the curve.
+        /// </summary>
+        public string Name;
+
+        /// <summary>
+        /// Animation curve.
+        /// </summary>
+        public AnimationCurve Curve;
+    }
 }

+ 38 - 0
Source/MBansheeEngine/Animation/AnimationCurves.cs

@@ -0,0 +1,38 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+
+namespace BansheeEngine
+{
+    /** @addtogroup Animation
+     *  @{
+     */
+
+    /// <summary>
+    /// Contains a set of animation curves used within an <see cref="AnimationClip"/>.
+    /// </summary>
+    public class AnimationCurves
+    {
+        /// <summary>
+        /// Curves for animating a scene object's position.
+        /// </summary>
+        public NamedVector3Curve[] PositionCurves;
+
+        /// <summary>
+        /// Curves for animating a scene object's rotation (in euler angles).
+        /// </summary>
+        public NamedVector3Curve[] RotationCurves;
+
+        /// <summary>
+        /// Curves for animating a scene object's scale.
+        /// </summary>
+        public NamedVector3Curve[] ScaleCurves;
+
+        /// <summary>
+        /// Curves for animating generic component properties.
+        /// </summary>
+        public NamedFloatCurve[] FloatCurves;
+    }
+
+    /** @} */
+}

+ 1 - 0
Source/MBansheeEngine/MBansheeEngine.csproj

@@ -46,6 +46,7 @@
     <Compile Include="Animation\Animation.cs" />
     <Compile Include="Animation\AnimationClip.cs" />
     <Compile Include="Animation\AnimationCurve.cs" />
+    <Compile Include="Animation\AnimationCurves.cs" />
     <Compile Include="Animation\Interop\NativeAnimation.cs" />
     <Compile Include="Audio\Audio.cs" />
     <Compile Include="Audio\AudioClip.cs" />

+ 2 - 0
Source/SBansheeEngine/Include/BsScriptAnimationClip.h

@@ -29,6 +29,8 @@ namespace BansheeEngine
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
+		static MonoObject* internal_GetAnimationCurves(ScriptAnimationClip* thisPtr);
+		static void internal_SetAnimationCurves(ScriptAnimationClip* thisPtr, MonoObject* curves);
 	};
 
 	/** @} */

+ 7 - 0
Source/SBansheeEngine/Include/BsScriptAnimationCurve.h

@@ -4,6 +4,7 @@
 
 #include "BsScriptEnginePrerequisites.h"
 #include "BsScriptObject.h"
+#include "BsAnimationCurve.h"
 
 namespace BansheeEngine
 {
@@ -27,6 +28,12 @@ namespace BansheeEngine
 	public:
 		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "AnimationCurve")
 
+		/** Returns the internal native curve stored in this wrapper. */
+		SPtr<TAnimationCurve<float>> getInternal() const { return mCurve; }
+
+		/** Creates a new managed object wrapping the provided curve. */
+		static MonoObject* create(const TAnimationCurve<float>& curve);
+
 	private:
 		ScriptAnimationCurve(MonoObject* instance, const SPtr<TAnimationCurve<float>>& curve);
 

+ 83 - 0
Source/SBansheeEngine/Include/BsScriptAnimationCurves.h

@@ -0,0 +1,83 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "BsScriptObject.h"
+#include "BsAnimationCurve.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup ScriptInteropEngine
+	 *  @{
+	 */
+
+	/**	Interop class between C++ & CLR for AnimationCurves. */
+	class BS_SCR_BE_EXPORT ScriptAnimationCurves : public ScriptObject<ScriptAnimationCurves>
+	{
+	public:
+		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "AnimationCurves")
+
+		/** Converts managed animation curves its native counterpart. */
+		static SPtr<AnimationCurves> toNative(MonoObject* object);
+
+		/** Converts native animation curves to its managed counterpart. */
+		static MonoObject* toManaged(const SPtr<AnimationCurves>& curves);
+	private:
+		ScriptAnimationCurves(MonoObject* instance);
+
+		/************************************************************************/
+		/* 								CLR HOOKS						   		*/
+		/************************************************************************/
+		static MonoField* sPositionCurvesField;
+		static MonoField* sRotationCurvesField;
+		static MonoField* sScaleCurvesField;
+		static MonoField* sFloatCurvesField;
+	};
+
+	/**	Interop class between C++ & CLR for NamedVector3Curve. */
+	class BS_SCR_BE_EXPORT ScriptNamedVector3Curve : public ScriptObject<ScriptNamedVector3Curve>
+	{
+	public:
+		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "NamedVector3Curve")
+
+		/** Converts managed 3D vector animation curve its native counterpart. */
+		static TNamedAnimationCurve<Vector3> toNative(MonoObject* object);
+
+		/** Converts native 3D vector animation curve to its managed counterpart. */
+		static MonoObject* toManaged(const TNamedAnimationCurve<Vector3>& curve);
+	private:
+		ScriptNamedVector3Curve(MonoObject* instance);
+
+		/************************************************************************/
+		/* 								CLR HOOKS						   		*/
+		/************************************************************************/
+		static MonoField* sNameField;
+		static MonoField* sXCurveField;
+		static MonoField* sYCurveField;
+		static MonoField* sZCurveField;
+	};
+
+	/**	Interop class between C++ & CLR for NamedFloatCurve. */
+	class BS_SCR_BE_EXPORT ScriptNamedFloatCurve : public ScriptObject<ScriptNamedFloatCurve>
+	{
+	public:
+		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "NamedFloatCurve")
+
+		/** Converts managed float animation curve its native counterpart. */
+		static TNamedAnimationCurve<float> toNative(MonoObject* object);
+
+		/** Converts native float animation curve to its managed counterpart. */
+		static MonoObject* toManaged(const TNamedAnimationCurve<float>& curve);
+	private:
+		ScriptNamedFloatCurve(MonoObject* instance);
+
+		/************************************************************************/
+		/* 								CLR HOOKS						   		*/
+		/************************************************************************/
+		static MonoField* sNameField;
+		static MonoField* sCurveField;
+	};
+
+	/** @} */
+}

+ 15 - 2
Source/SBansheeEngine/Source/BsScriptAnimationClip.cpp

@@ -1,7 +1,7 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsScriptAnimationClip.h"
-#include "BsScriptResourceManager.h"
+#include "BsScriptAnimationCurves.h"
 #include "BsScriptMeta.h"
 #include "BsMonoClass.h"
 
@@ -15,11 +15,24 @@ namespace BansheeEngine
 
 	void ScriptAnimationClip::initRuntimeData()
 	{
-		
+		metaData.scriptClass->addInternalCall("Internal_GetAnimationCurves", &ScriptAnimationClip::internal_GetAnimationCurves);
+		metaData.scriptClass->addInternalCall("Internal_SetAnimationCurves", &ScriptAnimationClip::internal_SetAnimationCurves);
 	}
 	
 	MonoObject* ScriptAnimationClip::createInstance()
 	{
 		return metaData.scriptClass->createInstance();
 	}
+
+	MonoObject* ScriptAnimationClip::internal_GetAnimationCurves(ScriptAnimationClip* thisPtr)
+	{
+		SPtr<AnimationCurves> curves = thisPtr->getHandle()->getCurves();
+		return ScriptAnimationCurves::toManaged(curves);
+	}
+
+	void ScriptAnimationClip::internal_SetAnimationCurves(ScriptAnimationClip* thisPtr, MonoObject* curves)
+	{
+		SPtr<AnimationCurves> nativeCurves = ScriptAnimationCurves::toNative(curves);
+		thisPtr->getHandle()->setCurves(*nativeCurves);
+	}
 }

+ 11 - 0
Source/SBansheeEngine/Source/BsScriptAnimationCurve.cpp

@@ -31,6 +31,17 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_Evaluate", &ScriptAnimationCurve::internal_Evaluate);
 	}
 
+	MonoObject* ScriptAnimationCurve::create(const TAnimationCurve<float>& curve)
+	{
+		SPtr<TAnimationCurve<float>> curvePtr = bs_shared_ptr_new<TAnimationCurve<float>>();
+		*curvePtr = curve;
+
+		MonoObject* instance = metaData.scriptClass->createInstance();
+		new (bs_alloc<ScriptAnimationCurve>()) ScriptAnimationCurve(instance, curvePtr);
+
+		return instance;
+	}
+
 	void ScriptAnimationCurve::internal_Create(MonoObject* instance, MonoArray* keyFrames)
 	{
 		ScriptArray inArray(keyFrames);

+ 351 - 0
Source/SBansheeEngine/Source/BsScriptAnimationCurves.cpp

@@ -0,0 +1,351 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptAnimationCurves.h"
+#include "BsScriptAnimationCurve.h"
+#include "BsMonoUtil.h"
+#include "BsMonoClass.h"
+#include "BsMonoField.h"
+#include "BsAnimationCurve.h"
+#include "BsAnimationClip.h"
+#include "BsMath.h"
+#include "BsVector3.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	MonoField* ScriptAnimationCurves::sPositionCurvesField = nullptr;
+	MonoField* ScriptAnimationCurves::sRotationCurvesField = nullptr;
+	MonoField* ScriptAnimationCurves::sScaleCurvesField = nullptr;
+	MonoField* ScriptAnimationCurves::sFloatCurvesField = nullptr;
+
+	ScriptAnimationCurves::ScriptAnimationCurves(MonoObject* instance)
+		:ScriptObject(instance)
+	{ }
+
+	void ScriptAnimationCurves::initRuntimeData()
+	{
+		sPositionCurvesField = metaData.scriptClass->getField("PositionCurves");
+		sRotationCurvesField = metaData.scriptClass->getField("RotationCurves");
+		sScaleCurvesField = metaData.scriptClass->getField("ScaleCurves");
+		sFloatCurvesField = metaData.scriptClass->getField("FloatCurves");
+	}
+
+	TAnimationCurve<Quaternion> eulerAngleToQuaternionCurve(const TAnimationCurve<Vector3>& inCurve)
+	{
+		UINT32 numKeys = (UINT32)inCurve.getNumKeyFrames();
+		Vector<TKeyframe<Quaternion>> quatKeys(numKeys);
+		for (UINT32 j = 0; j < numKeys; j++)
+		{
+			// TODO - Not implemented. Convert euler angle rotation to quaternion.
+		}
+
+		return TAnimationCurve<Quaternion>(quatKeys);
+	}
+
+	TAnimationCurve<Vector3> quaternionToEulerAngleCurve(const TAnimationCurve<Quaternion>& inCurve)
+	{
+		UINT32 numKeys = (UINT32)inCurve.getNumKeyFrames();
+		Vector<TKeyframe<Vector3>> eulerKeys(numKeys);
+		for (UINT32 j = 0; j < numKeys; j++)
+		{
+			// TODO - Not implemented. Convert quaternion rotation to euler angles.
+		}
+
+		return TAnimationCurve<Vector3>(eulerKeys);
+	}
+
+	SPtr<AnimationCurves> ScriptAnimationCurves::toNative(MonoObject* instance)
+	{
+		SPtr<AnimationCurves> output = bs_shared_ptr_new<AnimationCurves>();
+
+		MonoArray* monoPosCurves;
+		sPositionCurvesField->getValue(instance, &monoPosCurves);
+
+		if (monoPosCurves != nullptr)
+		{
+			ScriptArray scriptCurves(monoPosCurves);
+			for(UINT32 i = 0; i < scriptCurves.size(); i++)
+			{
+				MonoObject* monoCurve = scriptCurves.get<MonoObject*>(i);
+				output->position.push_back(ScriptNamedVector3Curve::toNative(monoCurve));
+			}
+		}
+
+		MonoArray* monoRotCurves;
+		sRotationCurvesField->getValue(instance, &monoRotCurves);
+
+		if (monoRotCurves != nullptr)
+		{
+			ScriptArray scriptCurves(monoRotCurves);
+			for (UINT32 i = 0; i < scriptCurves.size(); i++)
+			{
+				MonoObject* monoCurve = scriptCurves.get<MonoObject*>(i);
+				TNamedAnimationCurve<Vector3> eulerRotation = ScriptNamedVector3Curve::toNative(monoCurve);
+
+				TNamedAnimationCurve<Quaternion> quatRotation;
+				quatRotation.name = eulerRotation.name;
+				quatRotation.curve = eulerAngleToQuaternionCurve(eulerRotation.curve);
+
+				output->rotation.push_back(quatRotation);
+			}
+		}
+
+		MonoArray* monoScaleCurves;
+		sScaleCurvesField->getValue(instance, &monoScaleCurves);
+
+		if (monoScaleCurves != nullptr)
+		{
+			ScriptArray scriptCurves(monoScaleCurves);
+			for (UINT32 i = 0; i < scriptCurves.size(); i++)
+			{
+				MonoObject* monoCurve = scriptCurves.get<MonoObject*>(i);
+				output->scale.push_back(ScriptNamedVector3Curve::toNative(monoCurve));
+			}
+		}
+
+		MonoArray* monoFloatCurves;
+		sFloatCurvesField->getValue(instance, &monoFloatCurves);
+
+		if (monoFloatCurves != nullptr)
+		{
+			ScriptArray scriptCurves(monoFloatCurves);
+			for (UINT32 i = 0; i < scriptCurves.size(); i++)
+			{
+				MonoObject* monoCurve = scriptCurves.get<MonoObject*>(i);
+				output->generic.push_back(ScriptNamedFloatCurve::toNative(monoCurve));
+			}
+		}
+
+		return output;
+	}
+
+	MonoObject* ScriptAnimationCurves::toManaged(const SPtr<AnimationCurves>& curves)
+	{
+		UINT32 numPosCurves = (UINT32)curves->position.size();
+		ScriptArray scriptPositionCurves = ScriptArray::create<ScriptNamedVector3Curve>(numPosCurves);
+
+		for(UINT32 i = 0; i < numPosCurves; i++)
+		{
+			MonoObject* monoCurve = ScriptNamedVector3Curve::toManaged(curves->position[i]);
+			scriptPositionCurves.set(i, monoCurve);
+		}
+
+		UINT32 numRotCurves = (UINT32)curves->rotation.size();
+		ScriptArray scriptRotationCurves = ScriptArray::create<ScriptNamedVector3Curve>(numRotCurves);
+
+		for (UINT32 i = 0; i < numRotCurves; i++)
+		{
+			TNamedAnimationCurve<Vector3> eulerRotationCurve;
+			eulerRotationCurve.name = curves->rotation[i].name;
+			eulerRotationCurve.curve = quaternionToEulerAngleCurve(curves->rotation[i].curve);
+
+			MonoObject* monoCurve = ScriptNamedVector3Curve::toManaged(eulerRotationCurve);
+			scriptRotationCurves.set(i, monoCurve);
+		}
+
+		UINT32 numScaleCurves = (UINT32)curves->scale.size();
+		ScriptArray scriptScaleCurves = ScriptArray::create<ScriptNamedVector3Curve>(numScaleCurves);
+
+		for (UINT32 i = 0; i < numScaleCurves; i++)
+		{
+			MonoObject* monoCurve = ScriptNamedVector3Curve::toManaged(curves->scale[i]);
+			scriptScaleCurves.set(i, monoCurve);
+		}
+
+		UINT32 numFloatCurves = (UINT32)curves->generic.size();
+		ScriptArray scriptFloatCurves = ScriptArray::create<ScriptNamedFloatCurve>(numFloatCurves);
+
+		for (UINT32 i = 0; i < numFloatCurves; i++)
+		{
+			MonoObject* monoCurve = ScriptNamedFloatCurve::toManaged(curves->generic[i]);
+			scriptFloatCurves.set(i, monoCurve);
+		}
+
+		MonoObject* instance = metaData.scriptClass->createInstance();
+		sPositionCurvesField->setValue(instance, scriptPositionCurves.getInternal());
+		sRotationCurvesField->setValue(instance, scriptRotationCurves.getInternal());
+		sScaleCurvesField->setValue(instance, scriptScaleCurves.getInternal());
+		sFloatCurvesField->setValue(instance, scriptFloatCurves.getInternal());
+
+		return instance;
+	}
+
+	MonoField* ScriptNamedVector3Curve::sNameField = nullptr;
+	MonoField* ScriptNamedVector3Curve::sXCurveField = nullptr;
+	MonoField* ScriptNamedVector3Curve::sYCurveField = nullptr;
+	MonoField* ScriptNamedVector3Curve::sZCurveField = nullptr;
+
+	ScriptNamedVector3Curve::ScriptNamedVector3Curve(MonoObject* instance)
+		:ScriptObject(instance)
+	{ }
+
+	void ScriptNamedVector3Curve::initRuntimeData()
+	{
+		sNameField = metaData.scriptClass->getField("Name");
+		sXCurveField = metaData.scriptClass->getField("X");
+		sYCurveField = metaData.scriptClass->getField("Y");
+		sZCurveField = metaData.scriptClass->getField("Z");
+	}
+
+	TNamedAnimationCurve<Vector3> ScriptNamedVector3Curve::toNative(MonoObject* instance)
+	{
+		TNamedAnimationCurve<Vector3> output;
+
+		MonoString* monoName = nullptr;
+		sNameField->getValue(instance, &monoName);
+
+		output.name = MonoUtil::monoToString(monoName);
+
+		// Convert from three separate floating point curves, to a Vector3 curve
+		MonoObject* monoCurves[3];
+		sXCurveField->getValue(instance, &monoCurves[0]);
+		sYCurveField->getValue(instance, &monoCurves[1]);
+		sZCurveField->getValue(instance, &monoCurves[2]);
+
+		SPtr<TAnimationCurve<float>> curves[3];
+
+		// Find unique keyframe times
+		Map<float, TKeyframe<Vector3>> keyFrames;
+		for(UINT32 i = 0; i < 3; i++)
+		{
+			if (monoCurves[i] == nullptr)
+			{
+				curves[i] = bs_shared_ptr_new<TAnimationCurve<float>>();
+				continue;
+			}
+
+			ScriptAnimationCurve* scriptCurve = ScriptAnimationCurve::toNative(monoCurves[i]);
+			curves[i] = scriptCurve->getInternal();
+
+			UINT32 numKeyFrames = curves[i]->getNumKeyFrames();
+			for (UINT32 j = 0; j < numKeyFrames; j++)
+			{
+				const TKeyframe<float>& keyFrame = curves[i]->getKeyFrame(j);
+
+				auto iterFind = keyFrames.find(keyFrame.time);
+				if (iterFind == keyFrames.end())
+				{
+					TKeyframe<Vector3> newKeyFrame;
+					newKeyFrame.time = keyFrame.time;
+
+					keyFrames.insert(std::make_pair(keyFrame.time, newKeyFrame));
+				}
+			}
+		}
+
+		// Populate keyframe values
+
+		Vector<TKeyframe<Vector3>> keyframeList(keyFrames.size());
+		for(auto& entry : keyFrames)
+		{
+			TKeyframe<Vector3>& keyFrame = entry.second;
+			
+			for(UINT32 j = 0; j < 3; j++)
+			{
+				TKeyframe<float> currentKey = curves[j]->evaluateKey(keyFrame.time, false);
+				keyFrame.value[j] = currentKey.value;
+				keyFrame.inTangent[j] = currentKey.inTangent;
+				keyFrame.outTangent[j] = currentKey.outTangent;
+			}
+
+			keyframeList.push_back(keyFrame);
+		}
+
+		output.curve = TAnimationCurve<Vector3>(keyframeList);
+		return output;
+	}
+
+	MonoObject* ScriptNamedVector3Curve::toManaged(const TNamedAnimationCurve<Vector3>& curve)
+	{
+		MonoString* monoString = MonoUtil::stringToMono(curve.name);
+
+		UINT32 numKeyFrames = curve.curve.getNumKeyFrames();
+		Vector<TKeyframe<float>> keyFrames[3];
+
+		for (UINT32 i = 0; i < numKeyFrames; i++)
+		{
+			const TKeyframe<Vector3>& key = curve.curve.getKeyFrame(i);
+
+			TKeyframe<float> newKey;
+			newKey.time = key.time;
+
+			for(UINT32 j = 0; j < 3; j++)
+			{
+				bool addNew = true;
+				if (i > 0)
+				{
+					const TKeyframe<float>& prevKey = keyFrames[j].back();
+
+					bool isEqual = Math::approxEquals(prevKey.value, key.value[j]) &&
+						Math::approxEquals(prevKey.outTangent, key.inTangent[j]);
+
+					addNew = !isEqual;
+				}
+
+				if (addNew)
+				{
+					newKey.value = key.value[j];
+					newKey.inTangent = key.inTangent[j];
+					newKey.outTangent = key.outTangent[j];
+
+					keyFrames[j].push_back(newKey);
+				}
+			}
+		}
+
+		TAnimationCurve<float> xCurve(keyFrames[0]);
+		TAnimationCurve<float> yCurve(keyFrames[1]);
+		TAnimationCurve<float> zCurve(keyFrames[2]);
+
+		MonoObject* monoXCurve = ScriptAnimationCurve::create(xCurve);
+		MonoObject* monoYCurve = ScriptAnimationCurve::create(yCurve);
+		MonoObject* monoZCurve = ScriptAnimationCurve::create(zCurve);
+
+		void* params[4] = { monoString, monoXCurve, monoYCurve, monoZCurve };
+		return metaData.scriptClass->createInstance("string, AnimationCurve, AnimationCurve, AnimationCurve", params);
+	}
+
+	MonoField* ScriptNamedFloatCurve::sNameField = nullptr;
+	MonoField* ScriptNamedFloatCurve::sCurveField = nullptr;
+
+	ScriptNamedFloatCurve::ScriptNamedFloatCurve(MonoObject* instance)
+		:ScriptObject(instance)
+	{ }
+
+	void ScriptNamedFloatCurve::initRuntimeData()
+	{
+		sNameField = metaData.scriptClass->getField("Name");
+		sCurveField = metaData.scriptClass->getField("Curve");
+	}
+
+	TNamedAnimationCurve<float> ScriptNamedFloatCurve::toNative(MonoObject* instance)
+	{
+		TNamedAnimationCurve<float> output;
+
+		MonoString* monoName = nullptr;
+		sNameField->getValue(instance, &monoName);
+
+		output.name = MonoUtil::monoToString(monoName);
+
+		MonoObject* monoCurve = nullptr;
+		sCurveField->getValue(instance, &monoCurve);
+
+		if(monoCurve != nullptr)
+		{
+			ScriptAnimationCurve* scriptCurve = ScriptAnimationCurve::toNative(monoCurve);
+			output.curve = *scriptCurve->getInternal();
+		}
+
+		return output;
+	}
+
+	MonoObject* ScriptNamedFloatCurve::toManaged(const TNamedAnimationCurve<float>& curve)
+	{
+		MonoString* monoString = MonoUtil::stringToMono(curve.name);
+		MonoObject* monoCurve = ScriptAnimationCurve::create(curve.curve);
+
+		void* params[2] = { monoString, monoCurve };
+		return metaData.scriptClass->createInstance("string, AnimationCurve", params);
+	}
+}