Ver código fonte

Animation clip import functional

BearishSun 9 anos atrás
pai
commit
76ba8a4b32

+ 113 - 1
Source/BansheeCore/Include/BsAnimationClip.h

@@ -4,6 +4,9 @@
 
 
 #include "BsCorePrerequisites.h"
 #include "BsCorePrerequisites.h"
 #include "BsResource.h"
 #include "BsResource.h"
+#include "BsVector3.h"
+#include "BsQuaternion.h"
+#include "BsAnimationCurve.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -11,24 +14,133 @@ 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;
+	};
+
 	class BS_CORE_EXPORT AnimationClip : public Resource
 	class BS_CORE_EXPORT AnimationClip : public Resource
 	{
 	{
 	public:
 	public:
 		virtual ~AnimationClip() { }
 		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);
+
+		/** 
+		 * Registers a new curve used for animating rotation. 
+		 *
+		 * @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);
+
+		/** 
+		 * Registers a new curve used for animating scale. 
+		 *
+		 * @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);
+
+		/** 
+		 * Registers a new curve used for generic animation.
+		 *
+		 * @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.
+		 */
+		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.
+		 */
+		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.
+		 */
+		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.
+		 */
+		void removeGenericCurve(const String& name);
+
+		/** 
+		 * Creates an animation clip with no curves. After creation make sure to register some animation curves before
+		 * using it. 
+		 */
 		static HAnimationClip create();
 		static HAnimationClip create();
 
 
+		/** 
+		 * Creates an animation clip with specified curves.
+		 *
+		 * @param[in]	curves	Curves to initialize the animation with.
+		 */
+		static HAnimationClip create(const SPtr<AnimationCurves>& curves);
+
 	public: // ***** INTERNAL ******
 	public: // ***** INTERNAL ******
 		/** @name Internal
 		/** @name Internal
 		 *  @{
 		 *  @{
 		 */
 		 */
 
 
 		/** Creates a new AnimationClip without initializing it. Use create() for normal use. */
 		/** Creates a new AnimationClip without initializing it. Use create() for normal use. */
-		static SPtr<AnimationClip> _createPtr();
+		static SPtr<AnimationClip> _createPtr(const SPtr<AnimationCurves>& curves);
 
 
 		/** @} */
 		/** @} */
+
 	protected:
 	protected:
 		AnimationClip();
 		AnimationClip();
+		AnimationClip(const SPtr<AnimationCurves>& curves);
+
+		/** 
+		 * Contains all the animation curves in the clip. It's important this field is immutable so it may be used on other
+		 * threads. This means any modifications to the field will require a brand new data structure to be generated and
+		 * all existing data copied (plus the modification).
+		 */
+		SPtr<AnimationCurves> mCurves;
 
 
 		/************************************************************************/
 		/************************************************************************/
 		/* 								SERIALIZATION                      		*/
 		/* 								SERIALIZATION                      		*/

+ 8 - 1
Source/BansheeCore/Include/BsAnimationClipRTTI.h

@@ -5,6 +5,7 @@
 #include "BsCorePrerequisites.h"
 #include "BsCorePrerequisites.h"
 #include "BsRTTIType.h"
 #include "BsRTTIType.h"
 #include "BsAnimationClip.h"
 #include "BsAnimationClip.h"
+#include "BsAnimationCurveRTTI.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -16,9 +17,15 @@ namespace BansheeEngine
 	class BS_CORE_EXPORT AnimationClipRTTI : public RTTIType <AnimationClip, Resource, AnimationClipRTTI>
 	class BS_CORE_EXPORT AnimationClipRTTI : public RTTIType <AnimationClip, Resource, AnimationClipRTTI>
 	{
 	{
 	private:
 	private:
-
+		BS_BEGIN_RTTI_MEMBERS
+			BS_RTTI_MEMBER_PLAIN_NAMED(positionCurves, mCurves->position, 0)
+			BS_RTTI_MEMBER_PLAIN_NAMED(rotationCurves, mCurves->rotation, 1)
+			BS_RTTI_MEMBER_PLAIN_NAMED(scaleCurves, mCurves->scale, 2)
+			BS_RTTI_MEMBER_PLAIN_NAMED(genericCurves, mCurves->generic, 3)
+		BS_END_RTTI_MEMBERS
 	public:
 	public:
 		AnimationClipRTTI()
 		AnimationClipRTTI()
+			:mInitMembers(this)
 		{
 		{
 			
 			
 		}
 		}

+ 12 - 3
Source/BansheeCore/Include/BsAnimationCurve.h

@@ -26,11 +26,12 @@ namespace BansheeEngine
 	 * spline can be evaluated at any time, and uses caching to speed up multiple sequential evaluations.
 	 * spline can be evaluated at any time, and uses caching to speed up multiple sequential evaluations.
 	 */
 	 */
 	template <class T>
 	template <class T>
-	class BS_CORE_EXPORT TAnimationCurve
+	class BS_CORE_EXPORT TAnimationCurve // Note: Curves are expected to be immutable for threading purposes
 	{
 	{
 	public:
 	public:
 		typedef TKeyframe<T> KeyFrame;
 		typedef TKeyframe<T> KeyFrame;
 
 
+		TAnimationCurve();
 		TAnimationCurve(const Vector<KeyFrame>& keyframes);
 		TAnimationCurve(const Vector<KeyFrame>& keyframes);
 
 
 		/**
 		/**
@@ -44,7 +45,7 @@ namespace BansheeEngine
 		 *								maintained.
 		 *								maintained.
 		 * @param[in]	loop			If true the curve will loop when it goes past the end or beggining. Otherwise the
 		 * @param[in]	loop			If true the curve will loop when it goes past the end or beggining. Otherwise the
 		 *								curve value will be clamped.
 		 *								curve value will be clamped.
-		 @return						Interpolated value from the curve at provided time.
+		 * @return						Interpolated value from the curve at provided time.
 		 */
 		 */
 		T evaluate(const AnimationInstanceData<T>& animInstance, bool loop = true);
 		T evaluate(const AnimationInstanceData<T>& animInstance, bool loop = true);
 
 
@@ -55,7 +56,7 @@ namespace BansheeEngine
 		 * @param[i]	time	Time to evaluate the curve at.		
 		 * @param[i]	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 
 		 * @param[in]	loop	If true the curve will loop when it goes past the end or beggining. Otherwise the curve 
 		 *						value will be clamped.
 		 *						value will be clamped.
-		 @return				Interpolated value from the curve at provided time.
+		 * @return				Interpolated value from the curve at provided time.
 		 */
 		 */
 		T evaluate(float time, bool loop = true);
 		T evaluate(float time, bool loop = true);
 
 
@@ -103,5 +104,13 @@ namespace BansheeEngine
 		float mLength;
 		float mLength;
 	};
 	};
 
 
+	/** An animation curve and its name. */
+	template <class T>
+	struct TNamedAnimationCurve
+	{
+		String name;
+		TAnimationCurve<T> curve;
+	};
+
 	/** @} */
 	/** @} */
 }
 }

+ 43 - 1
Source/BansheeCore/Include/BsAnimationCurveRTTI.h

@@ -99,7 +99,7 @@ namespace BansheeEngine
 		}
 		}
 
 
 		/** @copydoc RTTIPlainType::getDynamicSize */
 		/** @copydoc RTTIPlainType::getDynamicSize */
-		static UINT32 getDynamicSize(const TKeyframe<T>& data)
+		static UINT32 getDynamicSize(const TAnimationCurve<T>& data)
 		{
 		{
 			UINT64 dataSize = sizeof(UINT32) + sizeof(UINT32);
 			UINT64 dataSize = sizeof(UINT32) + sizeof(UINT32);
 			dataSize += rttiGetElemSize(data.mStart);
 			dataSize += rttiGetElemSize(data.mStart);
@@ -112,6 +112,48 @@ namespace BansheeEngine
 			return (UINT32)dataSize;
 			return (UINT32)dataSize;
 		}
 		}
 	};
 	};
+
+	template<class T> struct RTTIPlainType<TNamedAnimationCurve<T>>
+	{
+		enum { id = TID_NamedAnimationCurve }; enum { hasDynamicSize = 1 };
+
+		/** @copydoc RTTIPlainType::toMemory */
+		static void toMemory(const TNamedAnimationCurve<T>& data, char* memory)
+		{
+			UINT32 size = sizeof(UINT32);
+			char* memoryStart = memory;
+			memory += sizeof(UINT32);
+
+			memory = rttiWriteElem(data.name, memory, size);
+			memory = rttiWriteElem(data.curve, memory, size);
+
+			memcpy(memoryStart, &size, sizeof(UINT32));
+		}
+
+		/** @copydoc RTTIPlainType::fromMemory */
+		static UINT32 fromMemory(TNamedAnimationCurve<T>& data, char* memory)
+		{
+			UINT32 size = 0;
+			memory = rttiReadElem(size, memory);
+
+			memory = rttiReadElem(data.name, memory);
+			memory = rttiReadElem(data.curve, memory);
+
+			return size;
+		}
+
+		/** @copydoc RTTIPlainType::getDynamicSize */
+		static UINT32 getDynamicSize(const TNamedAnimationCurve<T>& data)
+		{
+			UINT64 dataSize = sizeof(UINT32);
+			dataSize += rttiGetElemSize(data.name);
+			dataSize += rttiGetElemSize(data.curve);
+
+			assert(dataSize <= std::numeric_limits<UINT32>::max());
+
+			return (UINT32)dataSize;
+		}
+	};
 	
 	
 	/** @} */
 	/** @} */
 	/** @endcond */
 	/** @endcond */

+ 3 - 1
Source/BansheeCore/Include/BsCorePrerequisites.h

@@ -361,6 +361,7 @@ namespace BansheeEngine
 	class AudioClipImportOptions;
 	class AudioClipImportOptions;
 	class AnimationClip;
 	class AnimationClip;
 	template <class T> class TAnimationCurve;
 	template <class T> class TAnimationCurve;
+	struct AnimationCurves;
 	// Asset import
 	// Asset import
 	class SpecificImporter;
 	class SpecificImporter;
 	class Importer;
 	class Importer;
@@ -514,7 +515,8 @@ namespace BansheeEngine
 		TID_CAudioSource = 1114,
 		TID_CAudioSource = 1114,
 		TID_AnimationClip = 1115,
 		TID_AnimationClip = 1115,
 		TID_AnimationCurve = 1116,
 		TID_AnimationCurve = 1116,
-		TID_KeyFrame = 1117
+		TID_KeyFrame = 1117,
+		TID_NamedAnimationCurve = 1118
 	};
 	};
 }
 }
 
 

+ 1 - 1
Source/BansheeCore/Include/BsSkeleton.h

@@ -43,7 +43,7 @@ namespace BansheeEngine
 		UINT32 numBones;
 		UINT32 numBones;
 	};
 	};
 
 
-	class Skeleton
+	class Skeleton // Note: Must be immutable in order to be usable on multiple threads
 	{
 	{
 	public:
 	public:
 		Skeleton(BONE_DESC* bones, UINT32 numBones);
 		Skeleton(BONE_DESC* bones, UINT32 numBones);

+ 158 - 5
Source/BansheeCore/Source/BsAnimationClip.cpp

@@ -7,22 +7,28 @@
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
 	AnimationClip::AnimationClip()
 	AnimationClip::AnimationClip()
-		: Resource(false)
+		: Resource(false), mCurves(bs_shared_ptr_new<AnimationCurves>())
 	{
 	{
 		
 		
 	}
 	}
 
 
-	HAnimationClip AnimationClip::create()
+	AnimationClip::AnimationClip(const SPtr<AnimationCurves>& curves)
+		: Resource(false), mCurves(curves)
 	{
 	{
-		return static_resource_cast<AnimationClip>(gResources()._createResourceHandle(_createPtr()));
+
 	}
 	}
 
 
-	SPtr<AnimationClip> AnimationClip::_createPtr()
+	HAnimationClip AnimationClip::create()
 	{
 	{
 		SPtr<AnimationClip> newClip = createEmpty();
 		SPtr<AnimationClip> newClip = createEmpty();
 		newClip->initialize();
 		newClip->initialize();
 
 
-		return newClip;
+		return static_resource_cast<AnimationClip>(gResources()._createResourceHandle(newClip));
+	}
+
+	HAnimationClip AnimationClip::create(const SPtr<AnimationCurves>& curves)
+	{
+		return static_resource_cast<AnimationClip>(gResources()._createResourceHandle(_createPtr(curves)));
 	}
 	}
 
 
 	SPtr<AnimationClip> AnimationClip::createEmpty()
 	SPtr<AnimationClip> AnimationClip::createEmpty()
@@ -35,6 +41,153 @@ namespace BansheeEngine
 		return newClip;
 		return newClip;
 	}
 	}
 
 
+	SPtr<AnimationClip> AnimationClip::_createPtr(const SPtr<AnimationCurves>& curves)
+	{
+		AnimationClip* rawPtr = new (bs_alloc<AnimationClip>()) AnimationClip(curves);
+
+		SPtr<AnimationClip> newClip = bs_core_ptr<AnimationClip>(rawPtr);
+		newClip->_setThisPtr(newClip);
+		newClip->initialize();
+
+		return newClip;
+	}
+
+	void AnimationClip::addPositionCurve(const String& name, const TAnimationCurve<Vector3>& curve)
+	{
+		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;
+	}
+
+	void AnimationClip::addRotationCurve(const String& name, const TAnimationCurve<Quaternion>& curve)
+	{
+		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;
+	}
+
+	void AnimationClip::addScaleCurve(const String& name, const TAnimationCurve<Vector3>& curve)
+	{
+		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;
+	}
+
+	void AnimationClip::addGenericCurve(const String& name, const TAnimationCurve<float>& curve)
+	{
+		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);
+		}
+
+		newCurves->generic.push_back({ name, curve });
+
+		mCurves = newCurves;
+	}
+
+	void AnimationClip::removePositionCurve(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);
+		}
+
+		mCurves = newCurves;
+	}
+
+	void AnimationClip::removeRotationCurve(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);
+		}
+
+		mCurves = newCurves;
+	}
+
+	void AnimationClip::removeScaleCurve(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);
+		}
+
+		mCurves = newCurves;
+	}
+
+	void AnimationClip::removeGenericCurve(const String& name)
+	{
+		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;
+	}
+
 	RTTITypeBase* AnimationClip::getRTTIStatic()
 	RTTITypeBase* AnimationClip::getRTTIStatic()
 	{
 	{
 		return AnimationClipRTTI::instance();
 		return AnimationClipRTTI::instance();

+ 8 - 0
Source/BansheeCore/Source/BsAnimationCurve.cpp

@@ -1,6 +1,7 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsAnimationCurve.h"
 #include "BsAnimationCurve.h"
+#include "BsAnimationCurveRTTI.h"
 #include "BsVector3.h"
 #include "BsVector3.h"
 #include "BsQuaternion.h"
 #include "BsQuaternion.h"
 #include "BsMath.h"
 #include "BsMath.h"
@@ -10,6 +11,13 @@ namespace BansheeEngine
 	template <class T>
 	template <class T>
 	const UINT32 TAnimationCurve<T>::CACHE_LOOKAHEAD = 3;
 	const UINT32 TAnimationCurve<T>::CACHE_LOOKAHEAD = 3;
 
 
+	template <class T>
+	TAnimationCurve<T>::TAnimationCurve()
+		:mStart(0.0f), mEnd(0.0f), mLength(0.0f)
+	{
+		
+	}
+
 	template <class T>
 	template <class T>
 	TAnimationCurve<T>::TAnimationCurve(const Vector<KeyFrame>& keyframes)
 	TAnimationCurve<T>::TAnimationCurve(const Vector<KeyFrame>& keyframes)
 		:mKeyframes(keyframes)
 		:mKeyframes(keyframes)

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

@@ -50,7 +50,7 @@ namespace BansheeEngine
 		 * Reads the FBX file and outputs mesh data from the read file. Sub-mesh information will be output in @p subMeshes.
 		 * Reads the FBX file and outputs mesh data from the read file. Sub-mesh information will be output in @p subMeshes.
 		 */
 		 */
 		SPtr<RendererMeshData> importMeshData(const Path& filePath, SPtr<const ImportOptions> importOptions, 
 		SPtr<RendererMeshData> importMeshData(const Path& filePath, SPtr<const ImportOptions> importOptions, 
-			Vector<SubMesh>& subMeshes);
+			Vector<SubMesh>& subMeshes, UnorderedMap<String, SPtr<AnimationCurves>>& animationClips);
 
 
 		/**
 		/**
 		 * Loads the data from the file at the provided path into the provided FBX scene. Returns false if the file
 		 * Loads the data from the file at the provided path into the provided FBX scene. Returns false if the file
@@ -98,6 +98,9 @@ namespace BansheeEngine
 		/**	Converts a single FBX animation curve into an engine curve format, resampling it if necessary. */
 		/**	Converts a single FBX animation curve into an engine curve format, resampling it if necessary. */
 		void importCurve(FbxAnimCurve* fbxCurve, FBXImportOptions& importOptions, FBXAnimationCurve& curve, float start, float end);
 		void importCurve(FbxAnimCurve* fbxCurve, FBXImportOptions& importOptions, FBXAnimationCurve& curve, float start, float end);
 
 
+		/** Converts FBX animation clips into engine-ready animation curve format. */
+		void convertAnimations(const Vector<FBXAnimationClip>& clips, UnorderedMap<String, SPtr<AnimationCurves>>& output);
+
 		/**	Converts a set of curves containing rotation in euler angles into a set of curves using	quaternion rotation. */
 		/**	Converts a set of curves containing rotation in euler angles into a set of curves using	quaternion rotation. */
 		void eulerToQuaternionCurves(FBXAnimationCurve(&eulerCurves)[3], FBXAnimationCurve(&quatCurves)[4]);
 		void eulerToQuaternionCurves(FBXAnimationCurve(&eulerCurves)[3], FBXAnimationCurve(&quatCurves)[4]);
 
 

+ 104 - 3
Source/BansheeFBXImporter/Source/BsFBXImporter.cpp

@@ -17,6 +17,8 @@
 #include "BsRendererMeshData.h"
 #include "BsRendererMeshData.h"
 #include "BsMeshImportOptions.h"
 #include "BsMeshImportOptions.h"
 #include "BsPhysicsMesh.h"
 #include "BsPhysicsMesh.h"
+#include "BsAnimationCurve.h"
+#include "BsAnimationClip.h"
 #include "BsPhysics.h"
 #include "BsPhysics.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
@@ -113,7 +115,8 @@ namespace BansheeEngine
 	SPtr<Resource> FBXImporter::import(const Path& filePath, SPtr<const ImportOptions> importOptions)
 	SPtr<Resource> FBXImporter::import(const Path& filePath, SPtr<const ImportOptions> importOptions)
 	{
 	{
 		Vector<SubMesh> subMeshes;
 		Vector<SubMesh> subMeshes;
-		SPtr<RendererMeshData> rendererMeshData = importMeshData(filePath, importOptions, subMeshes);
+		UnorderedMap<String, SPtr<AnimationCurves>> dummy;
+		SPtr<RendererMeshData> rendererMeshData = importMeshData(filePath, importOptions, subMeshes, dummy);
 
 
 		const MeshImportOptions* meshImportOptions = static_cast<const MeshImportOptions*>(importOptions.get());
 		const MeshImportOptions* meshImportOptions = static_cast<const MeshImportOptions*>(importOptions.get());
 
 
@@ -132,7 +135,8 @@ namespace BansheeEngine
 	Vector<SubResourceRaw> FBXImporter::importAll(const Path& filePath, SPtr<const ImportOptions> importOptions)
 	Vector<SubResourceRaw> FBXImporter::importAll(const Path& filePath, SPtr<const ImportOptions> importOptions)
 	{
 	{
 		Vector<SubMesh> subMeshes;
 		Vector<SubMesh> subMeshes;
-		SPtr<RendererMeshData> rendererMeshData = importMeshData(filePath, importOptions, subMeshes);
+		UnorderedMap<String, SPtr<AnimationCurves>> animationClips;
+		SPtr<RendererMeshData> rendererMeshData = importMeshData(filePath, importOptions, subMeshes, animationClips);
 
 
 		const MeshImportOptions* meshImportOptions = static_cast<const MeshImportOptions*>(importOptions.get());
 		const MeshImportOptions* meshImportOptions = static_cast<const MeshImportOptions*>(importOptions.get());
 
 
@@ -166,7 +170,13 @@ namespace BansheeEngine
 				{
 				{
 					LOGWRN("Cannot generate a collision mesh as the physics module was not started.");
 					LOGWRN("Cannot generate a collision mesh as the physics module was not started.");
 				}
 				}
+			}
+
+			for(auto& entry : animationClips)
+			{
+				SPtr<AnimationClip> clip = AnimationClip::_createPtr(entry.second);
 
 
+				output.push_back({ toWString(entry.first), clip });
 			}
 			}
 		}
 		}
 
 
@@ -174,7 +184,7 @@ namespace BansheeEngine
 	}
 	}
 
 
 	SPtr<RendererMeshData> FBXImporter::importMeshData(const Path& filePath, SPtr<const ImportOptions> importOptions, 
 	SPtr<RendererMeshData> FBXImporter::importMeshData(const Path& filePath, SPtr<const ImportOptions> importOptions, 
-		Vector<SubMesh>& subMeshes)
+		Vector<SubMesh>& subMeshes, UnorderedMap<String, SPtr<AnimationCurves>>& animation)
 	{
 	{
 		FbxScene* fbxScene = nullptr;
 		FbxScene* fbxScene = nullptr;
 
 
@@ -210,6 +220,10 @@ namespace BansheeEngine
 
 
 		SPtr<RendererMeshData> rendererMeshData = generateMeshData(importedScene, fbxImportOptions, subMeshes);
 		SPtr<RendererMeshData> rendererMeshData = generateMeshData(importedScene, fbxImportOptions, subMeshes);
 
 
+		// Import animation clips
+		if (!importedScene.clips.empty())
+			convertAnimations(importedScene.clips, animation);
+
 		// TODO - Later: Optimize mesh: Remove bad and degenerate polygons, weld nearby vertices, optimize for vertex cache
 		// TODO - Later: Optimize mesh: Remove bad and degenerate polygons, weld nearby vertices, optimize for vertex cache
 
 
 		shutDownSdk();
 		shutDownSdk();
@@ -402,6 +416,93 @@ namespace BansheeEngine
 		scene.meshes = splitMeshes;
 		scene.meshes = splitMeshes;
 	}
 	}
 
 
+	void FBXImporter::convertAnimations(const Vector<FBXAnimationClip>& clips, 
+		UnorderedMap<String, SPtr<AnimationCurves>>& output)
+	{
+		for (auto& clip : clips)
+		{
+			SPtr<AnimationCurves> curves = bs_shared_ptr_new<AnimationCurves>();
+			output.insert(std::make_pair(clip.name, curves));
+
+			for (auto& bone : clip.boneAnimations)
+			{
+				// Translation curves
+				{
+					assert((bone.translation[0].keyframes.size() == bone.translation[1].keyframes.size()) &&
+						(bone.translation[0].keyframes.size() == bone.translation[2].keyframes.size()));
+
+					UINT32 numKeyframes = (UINT32)bone.translation[0].keyframes.size();
+					Vector <TKeyframe<Vector3>> keyFrames(numKeyframes);
+					for (UINT32 i = 0; i < numKeyframes; i++)
+					{
+						const FBXKeyFrame& keyFrameX = bone.translation[0].keyframes[i];
+						const FBXKeyFrame& keyFrameY = bone.translation[1].keyframes[i];
+						const FBXKeyFrame& keyFrameZ = bone.translation[2].keyframes[i];
+
+						keyFrames[i].value = Vector3(keyFrameX.value, keyFrameY.value, keyFrameZ.value);
+
+						assert((keyFrameX.time == keyFrameY.time) && (keyFrameX.time == keyFrameZ.time));
+						keyFrames[i].time = keyFrameX.time;
+						keyFrames[i].inTangent = Vector3(keyFrameX.inTangent, keyFrameY.inTangent, keyFrameZ.inTangent);
+						keyFrames[i].outTangent = Vector3(keyFrameX.outTangent, keyFrameY.outTangent, keyFrameZ.outTangent);
+					}
+
+					curves->position.push_back({ bone.node->name, keyFrames });
+				}
+
+				// Rotation curves
+				{
+					assert((bone.rotation[0].keyframes.size() == bone.rotation[1].keyframes.size()) &&
+						(bone.rotation[0].keyframes.size() == bone.rotation[2].keyframes.size()) &&
+						(bone.rotation[0].keyframes.size() == bone.rotation[3].keyframes.size()));
+
+					UINT32 numKeyframes = (UINT32)bone.rotation[0].keyframes.size();
+					Vector <TKeyframe<Quaternion>> keyFrames(numKeyframes);
+					for (UINT32 i = 0; i < numKeyframes; i++)
+					{
+						const FBXKeyFrame& keyFrameW = bone.rotation[0].keyframes[i];
+						const FBXKeyFrame& keyFrameX = bone.rotation[1].keyframes[i];
+						const FBXKeyFrame& keyFrameY = bone.rotation[2].keyframes[i];
+						const FBXKeyFrame& keyFrameZ = bone.rotation[3].keyframes[i];
+
+						keyFrames[i].value = Quaternion(keyFrameW.value, keyFrameX.value, keyFrameY.value, keyFrameZ.value);
+
+						assert((keyFrameX.time == keyFrameY.time) && (keyFrameX.time == keyFrameZ.time) && (keyFrameX.time == keyFrameW.time));
+						keyFrames[i].time = keyFrameX.time;
+						keyFrames[i].inTangent = Quaternion(keyFrameW.inTangent, keyFrameX.inTangent, keyFrameY.inTangent, keyFrameZ.inTangent);
+						keyFrames[i].outTangent = Quaternion(keyFrameW.outTangent, keyFrameX.outTangent, keyFrameY.outTangent, keyFrameZ.outTangent);
+					}
+
+					curves->rotation.push_back({ bone.node->name, keyFrames });
+				}
+
+				// Scale curves
+				{
+					assert((bone.scale[0].keyframes.size() == bone.scale[1].keyframes.size()) &&
+						(bone.scale[0].keyframes.size() == bone.scale[2].keyframes.size()));
+
+					UINT32 numKeyframes = (UINT32)bone.scale[0].keyframes.size();
+					Vector <TKeyframe<Vector3>> keyFrames(numKeyframes);
+					for (UINT32 i = 0; i < numKeyframes; i++)
+					{
+						const FBXKeyFrame& keyFrameX = bone.scale[0].keyframes[i];
+						const FBXKeyFrame& keyFrameY = bone.scale[1].keyframes[i];
+						const FBXKeyFrame& keyFrameZ = bone.scale[2].keyframes[i];
+
+						keyFrames[i].value = Vector3(keyFrameX.value, keyFrameY.value, keyFrameZ.value);
+
+						assert((keyFrameX.time == keyFrameY.time) && (keyFrameX.time == keyFrameZ.time));
+						keyFrames[i].time = keyFrameX.time;
+						keyFrames[i].inTangent = Vector3(keyFrameX.inTangent, keyFrameY.inTangent, keyFrameZ.inTangent);
+						keyFrames[i].outTangent = Vector3(keyFrameX.outTangent, keyFrameY.outTangent, keyFrameZ.outTangent);
+					}
+
+					curves->scale.push_back({ bone.node->name, keyFrames });
+				}
+			}
+		}
+	}
+
 	SPtr<RendererMeshData> FBXImporter::generateMeshData(const FBXImportScene& scene, const FBXImportOptions& options, Vector<SubMesh>& outputSubMeshes)
 	SPtr<RendererMeshData> FBXImporter::generateMeshData(const FBXImportScene& scene, const FBXImportOptions& options, Vector<SubMesh>& outputSubMeshes)
 	{
 	{
 		Matrix4 importScale = Matrix4::scaling(options.importScale);
 		Matrix4 importScale = Matrix4::scaling(options.importScale);

+ 62 - 0
Source/BansheeUtility/Include/BsRTTIType.h

@@ -87,6 +87,26 @@ namespace BansheeEngine
 																								\
 																								\
 	typedef META_NextEntry_##name
 	typedef META_NextEntry_##name
 
 
+ /** 
+  * Same as BS_RTTI_MEMBER_PLAIN_ARRAY, but allows you to specify separate names for the field name and the member variable.
+  */
+#define BS_RTTI_MEMBER_PLAIN_ARRAY_NAMED(name, field, id)													\
+	META_Entry_##name;																			\
+																								\
+	std::common_type<decltype(OwnerType::field)>::type::value_type& get##name(OwnerType* obj, UINT32 idx) { return obj->field[idx]; }					\
+	void set##name(OwnerType* obj, UINT32 idx, std::common_type<decltype(OwnerType::field)>::type::value_type& val) { obj->field[idx] = val; }		\
+	UINT32 getSize##name(OwnerType* obj) { return (UINT32)obj->field.size(); }																		\
+	void setSize##name(OwnerType* obj, UINT32 val) { obj->field.resize(val); }																		\
+																								\
+	struct META_NextEntry_##name{};																\
+	void META_InitPrevEntry(META_NextEntry_##name typeId)										\
+	{																							\
+		addPlainArrayField(#name, id, &MyType::get##name, &MyType::getSize##name, &MyType::set##name, &MyType::setSize##name);						\
+		META_InitPrevEntry(META_Entry_##name());												\
+	}																							\
+																								\
+	typedef META_NextEntry_##name
+
 /**
 /**
  * Registers a new member field in the RTTI type. The field references the @p name member in the owner class. 
  * Registers a new member field in the RTTI type. The field references the @p name member in the owner class. 
  * The type of the member must be a valid reflectable (non-pointer) type. Each field must specify a unique ID for @p id.
  * The type of the member must be a valid reflectable (non-pointer) type. Each field must specify a unique ID for @p id.
@@ -144,6 +164,27 @@ namespace BansheeEngine
 																								\
 																								\
 	typedef META_NextEntry_##name
 	typedef META_NextEntry_##name
 
 
+
+/** 
+ * Same as BS_RTTI_MEMBER_REFL_ARRAY, but allows you to specify separate names for the field name and the member variable. 
+ */
+#define BS_RTTI_MEMBER_REFL_ARRAY_NAMED(name, field, id)										\
+	META_Entry_##name;																			\
+																								\
+	std::common_type<decltype(OwnerType::field)>::type::value_type& get##name(OwnerType* obj, UINT32 idx) { return obj->field[idx]; }				\
+	void set##name(OwnerType* obj, UINT32 idx, std::common_type<decltype(OwnerType::field)>::type::value_type& val) { obj->field[idx] = val; }		\
+	UINT32 getSize##name(OwnerType* obj) { return (UINT32)obj->field.size(); }					\
+	void setSize##name(OwnerType* obj, UINT32 val) { obj->field.resize(val); }					\
+																								\
+	struct META_NextEntry_##name{};																\
+	void META_InitPrevEntry(META_NextEntry_##name typeId)										\
+	{																							\
+		addReflectableArrayField(#name, id, &MyType::get##name, &MyType::getSize##name, &MyType::set##name, &MyType::setSize##name);				\
+		META_InitPrevEntry(META_Entry_##name());												\
+	}																							\
+																								\
+	typedef META_NextEntry_##name
+
 /**
 /**
  * Registers a new member field in the RTTI type. The field references the @p name member in the owner class. 
  * Registers a new member field in the RTTI type. The field references the @p name member in the owner class. 
  * The type of the member must be a valid reflectable pointer type. Each field must specify a unique ID for @p id.
  * The type of the member must be a valid reflectable pointer type. Each field must specify a unique ID for @p id.
@@ -200,6 +241,27 @@ namespace BansheeEngine
 																								\
 																								\
 	typedef META_NextEntry_##name
 	typedef META_NextEntry_##name
 
 
+ /**
+  * Same as BS_RTTI_MEMBER_REFLPTR_ARRAY, but allows you to specify separate names for the field name and the member 
+  * variable. 
+  */
+#define BS_RTTI_MEMBER_REFLPTR_ARRAY_NAMED(name, field, id)										\
+	META_Entry_##name;																			\
+																								\
+	std::common_type<decltype(OwnerType::field)>::type::value_type get##name(OwnerType* obj, UINT32 idx) { return obj->field[idx]; }				\
+	void set##name(OwnerType* obj, UINT32 idx, std::common_type<decltype(OwnerType::field)>::type::value_type val) { obj->field[idx] = val; }		\
+	UINT32 getSize##name(OwnerType* obj) { return (UINT32)obj->field.size(); }					\
+	void setSize##name(OwnerType* obj, UINT32 val) { obj->field.resize(val); }					\
+																								\
+	struct META_NextEntry_##name{};																\
+	void META_InitPrevEntry(META_NextEntry_##name typeId)										\
+	{																							\
+		addReflectablePtrArrayField(#name, id, &MyType::get##name, &MyType::getSize##name, &MyType::set##name, &MyType::setSize##name);			\
+		META_InitPrevEntry(META_Entry_##name());												\
+	}																							\
+																								\
+	typedef META_NextEntry_##name
+
 /** Ends definitions for member fields with a RTTI type. Must follow BS_BEGIN_RTTI_MEMBERS. */
 /** Ends definitions for member fields with a RTTI type. Must follow BS_BEGIN_RTTI_MEMBERS. */
 #define BS_END_RTTI_MEMBERS																		\
 #define BS_END_RTTI_MEMBERS																		\
 	META_LastEntry;																				\
 	META_LastEntry;																				\