Browse Source

Morph shape evaluation

BearishSun 9 years ago
parent
commit
a69052d9c5

+ 32 - 5
Source/BansheeCore/Include/BsAnimation.h

@@ -30,8 +30,9 @@ namespace BansheeEngine
 		Clean = 0,
 		Value = 1 << 0,
 		Layout = 1 << 1,
-		Skeleton = 1 << 2,
-		Culling = 1 << 3
+		All = 1 << 2,
+		Culling = 1 << 3,
+		MorphWeights = 1 << 4
 	};
 
 	typedef Flags<AnimDirtyStateFlag> AnimDirtyState;
@@ -125,6 +126,13 @@ namespace BansheeEngine
 		String curveName;
 	};
 
+	/** Morph shape and its contribution to the final shape. */
+	struct MorphShapeInfo
+	{
+		SPtr<MorphShape> shape;
+		float weight;
+	};
+
 	/** Contains information about a scene object that is animated by a specific animation curve. */
 	struct AnimatedSceneObjectInfo
 	{
@@ -155,11 +163,12 @@ namespace BansheeEngine
 		 *									method completes clip info layout and state indices will be populated for 
 		 *									further use in the update*() methods.
 		 * @param[in]		sceneObjects	A list of scene objects that are influenced by specific animation curves.
+		 * @param[in]		morphShapes		Morph shapes used for per-vertex animation.
 		 *
 		 * @note	Should be called from the sim thread when the caller is sure the animation thread is not using it.
 		 */
 		void rebuild(const SPtr<Skeleton>& skeleton, const SkeletonMask& mask, Vector<AnimationClipInfo>& clipInfos, 
-			const Vector<AnimatedSceneObject>& sceneObjects);
+			const Vector<AnimatedSceneObject>& sceneObjects, const SPtr<MorphShapes>& morphShapes);
 
 		/** 
 		 * Rebuilds the internal proxy data according to the newly clips. This should be called whenever clips are added
@@ -169,10 +178,12 @@ namespace BansheeEngine
 		 *									completes clip info layout and state indices will be populated for further use 
 		 *									in the update*() methods.
 		 * @param[in]		sceneObjects	A list of scene objects that are influenced by specific animation curves.
+		 * * @param[in]		morphShapes		Morph shapes used for per-vertex animation.
 		 *
 		 * @note	Should be called from the sim thread when the caller is sure the animation thread is not using it.
 		 */
-		void rebuild(Vector<AnimationClipInfo>& clipInfos, const Vector<AnimatedSceneObject>& sceneObjects);
+		void rebuild(Vector<AnimationClipInfo>& clipInfos, const Vector<AnimatedSceneObject>& sceneObjects, 
+			const SPtr<MorphShapes>& morphShapes);
 
 		/** 
 		 * Updates the proxy data with new information about the clips. Caller must guarantee that clip layout didn't 
@@ -180,7 +191,13 @@ namespace BansheeEngine
 		 *
 		 * @note	Should be called from the sim thread when the caller is sure the animation thread is not using it.
 		 */
-		void updateValues(const Vector<AnimationClipInfo>& clipInfos);
+		void updateClipInfos(const Vector<AnimationClipInfo>& clipInfos);
+
+		/** 
+		 * Updates the proxy data with new weights used for morph shapes. Caller must ensure the weights are ordered so
+		 * they match with the morph shapes provided to the last rebuild() call.
+		 */
+		void updateMorphShapeWeights(const Vector<float>& weights);
 
 		/**
 		 * Updates the proxy data with new scene object transforms. Caller must guarantee that clip layout didn't 
@@ -202,6 +219,8 @@ namespace BansheeEngine
 		void clear();
 
 		UINT64 id;
+
+		// Skeletal animation
 		AnimationStateLayer* layers;
 		UINT32 numLayers;
 		SPtr<Skeleton> skeleton;
@@ -210,6 +229,12 @@ namespace BansheeEngine
 		AnimatedSceneObjectInfo* sceneObjectInfos;
 		Matrix4* sceneObjectTransforms;
 
+		// Morph shape animation
+		MorphShapeInfo* morphShapeInfos;
+		UINT32 numMorphShapes;
+		UINT32 numMorphVertices;
+		bool morphShapeWeightsDirty;
+
 		// Culling
 		AABox mBounds;
 		bool mCullEnabled;
@@ -476,6 +501,8 @@ namespace BansheeEngine
 
 		SPtr<Skeleton> mSkeleton;
 		SkeletonMask mSkeletonMask;
+		SPtr<MorphShapes> mMorphShapes;
+		Vector<float> mMorphShapeWeights;
 		Vector<AnimationClipInfo> mClipInfos;
 		UnorderedMap<UINT64, AnimatedSceneObject> mSceneObjects;
 		Vector<float> mGenericCurveOutputs;

+ 21 - 3
Source/BansheeCore/Include/BsAnimationManager.h

@@ -6,6 +6,7 @@
 #include "BsModule.h"
 #include "BsCoreThread.h"
 #include "BsConvexVolume.h"
+#include "BsVertexDataDesc.h"
 
 namespace BansheeEngine
 {
@@ -18,7 +19,7 @@ namespace BansheeEngine
 	/** Contains skeleton poses for all animations evaluated on a single frame. */
 	struct RendererAnimationData
 	{
-		/** Contains data about a calculated skeleton pose. */
+		/** Contains meta-data about a calculated skeleton pose. Actual data maps to the @p transforms buffer. */
 		struct PoseInfo
 		{
 			UINT64 animId;
@@ -26,8 +27,24 @@ namespace BansheeEngine
 			UINT32 numBones;
 		};
 
-		/** Maps animation ID to a pose information structure, containing its global joint transforms. */
-		UnorderedMap<UINT64, PoseInfo> poseInfos;
+		/** Contains data about a calculated morph shape. */
+		struct MorphShapeInfo
+		{
+			SPtr<MeshData> meshData;
+			UINT32 version;
+		};
+
+		/** Contains meta-data about where calculated animation data is stored. */
+		struct AnimInfo
+		{
+			PoseInfo poseInfo;
+			MorphShapeInfo morphShapeInfo;
+		};
+
+		/**
+		 * Maps animation ID to a animation information structure, which points to relevant skeletal or morph shape data. 
+		 */
+		UnorderedMap<UINT64, AnimInfo> infos;
 
 		/** Global joint transforms for all skeletons in the scene. */
 		Vector<Matrix4> transforms;
@@ -107,6 +124,7 @@ namespace BansheeEngine
 
 		bool mWorkerRunning;
 		SPtr<Task> mAnimationWorker;
+		SPtr<VertexDataDesc> mBlendShapeVertexDesc;
 
 		// Animation thread
 		Vector<SPtr<AnimationProxy>> mProxies;

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

@@ -376,6 +376,7 @@ namespace BansheeEngine
 	class Camera;
 	class CameraCore;
 	class MorphShapes;
+	class MorphShape;
 	// Asset import
 	class SpecificImporter;
 	class Importer;

+ 54 - 0
Source/BansheeCore/Include/BsMeshUtility.h

@@ -10,6 +10,20 @@ namespace BansheeEngine
 	 *  @{
 	 */
 
+	/** Normal packed in a 32-bit structure. */
+	union PackedNormal
+	{
+		struct
+		{
+			UINT8 x;
+			UINT8 y;
+			UINT8 z;
+			UINT8 w;
+		};
+
+		UINT32 packed;
+	};
+
 	/** Performs various operations on mesh geometry. */
 	class BS_CORE_EXPORT MeshUtility
 	{
@@ -119,6 +133,46 @@ namespace BansheeEngine
 		 */
 		static void clip3D(UINT8* vertices, UINT8* uvs, UINT32 numTris, UINT32 vertexStride, const Vector<Plane>& clipPlanes,
 			const std::function<void(Vector3*, Vector2*, UINT32)>& writeCallback);
+
+		/** 
+		 * Encodes normals from 32-bit float format into 4D 8-bit packed format. 
+		 *
+		 * @param[in]	source			Buffer containing data to encode. Must have @p count entries.
+		 * @param[out]	destination		Buffer to output the data to. Must have @p count entries, each 32-bits.
+		 * @param[in]	count			Number of entries in the @p source and @p destination arrays.
+		 * @param[in]	stride			Distance between two entries in the @p destination buffer, in bytes.
+		 */
+		static void packNormals(Vector3* source, UINT8* destination, UINT32 count, UINT32 stride);
+
+		/** 
+		 * Encodes normals from 32-bit float format into 4D 8-bit packed format. 
+		 *
+		 * @param[in]	source			Buffer containing data to encode. Must have @p count entries.
+		 * @param[out]	destination		Buffer to output the data to. Must have @p count entries, each 32-bits.
+		 * @param[in]	count			Number of entries in the @p source and @p destination arrays.
+		 * @param[in]	stride			Distance between two entries in the @p destination buffer, in bytes.
+		 */
+		static void packNormals(Vector4* source, UINT8* destination, UINT32 count, UINT32 stride);
+
+		/** 
+		 * Decodes normals from 4D 8-bit packed format into a 32-bit float format. 
+		 *
+		 * @param[in]	source			Buffer containing data to encode. Must have @p count entries, each 32-bits.
+		 * @param[out]	destination		Buffer to output the data to. Must have @p count entries.
+		 * @param[in]	count			Number of entries in the @p source and @p destination arrays.
+		 * @param[in]	stride			Distance between two entries in the @p source buffer, in bytes.
+		 */
+		static void unpackNormals(UINT8* source, Vector3* destination, UINT32 count, UINT32 stride);
+
+		/** 
+		 * Decodes normals from 4D 8-bit packed format into a 32-bit float format. 
+		 *
+		 * @param[in]	source			Buffer containing data to encode. Must have @p count entries, each 32-bits.
+		 * @param[out]	destination		Buffer to output the data to. Must have @p count entries.
+		 * @param[in]	count			Number of entries in the @p source and @p destination arrays.
+		 * @param[in]	stride			Distance between two entries in the @p source buffer, in bytes.
+		 */
+		static void unpackNormals(UINT8* source, Vector4* destination, UINT32 count, UINT32 stride);
 	};
 
 	/** @} */

+ 6 - 2
Source/BansheeCore/Include/BsMorphShapes.h

@@ -71,14 +71,18 @@ namespace BansheeEngine
 		/** Returns the morph shape at the specified index. */
 		SPtr<MorphShape> getShape(UINT32 idx) const { return mShapes[idx]; }
 
+		/** Returns the number of vertices per morph shape. */
+		UINT32 getNumVertices() const { return mNumVertices; }
+
 		/** Creates a new set of morph shapes. */
-		static SPtr<MorphShapes> create(const Vector<SPtr<MorphShape>>& shapes);
+		static SPtr<MorphShapes> create(const Vector<SPtr<MorphShape>>& shapes, UINT32 numVertices);
 
 	private:
 		MorphShapes();
-		MorphShapes(const Vector<SPtr<MorphShape>>& shapes);
+		MorphShapes(const Vector<SPtr<MorphShape>>& shapes, UINT32 numVertices);
 
 		Vector<SPtr<MorphShape>> mShapes;
+		UINT32 mNumVertices;
 
 		/************************************************************************/
 		/* 								SERIALIZATION                      		*/

+ 2 - 1
Source/BansheeCore/Include/BsMorphShapesRTTI.h

@@ -48,7 +48,8 @@ namespace BansheeEngine
 	private:
 		BS_BEGIN_RTTI_MEMBERS
 			BS_RTTI_MEMBER_REFLPTR_ARRAY(mShapes, 0)
-			BS_END_RTTI_MEMBERS
+			BS_RTTI_MEMBER_PLAIN(mNumVertices, 1)
+		BS_END_RTTI_MEMBERS
 
 	public:
 		MorphShapesRTTI()

+ 0 - 40
Source/BansheeCore/Include/BsRendererMeshData.h

@@ -224,46 +224,6 @@ namespace BansheeEngine
 		RendererMeshData(UINT32 numVertices, UINT32 numIndices, VertexLayout layout, IndexType indexType = IT_32BIT);
 		RendererMeshData(const SPtr<MeshData>& meshData);
 
-		/** 
-		 * Encodes normals from 32-bit float format into 4D 8-bit packed format. 
-		 *
-		 * @param[in]	source			Buffer containing data to encode. Must have @p count entries.
-		 * @param[out]	destination		Buffer to output the data to. Must have @p count entries, each 32-bits.
-		 * @param[in]	count			Number of entries in the @p source and @p destination arrays.
-		 * @param[in]	stride			Distance between two entries in the @p destination buffer, in bytes.
-		 */
-		void packNormals(Vector3* source, UINT8* destination, UINT32 count, UINT32 stride);
-
-		/** 
-		 * Encodes normals from 32-bit float format into 4D 8-bit packed format. 
-		 *
-		 * @param[in]	source			Buffer containing data to encode. Must have @p count entries.
-		 * @param[out]	destination		Buffer to output the data to. Must have @p count entries, each 32-bits.
-		 * @param[in]	count			Number of entries in the @p source and @p destination arrays.
-		 * @param[in]	stride			Distance between two entries in the @p destination buffer, in bytes.
-		 */
-		void packNormals(Vector4* source, UINT8* destination, UINT32 count, UINT32 stride);
-
-		/** 
-		 * Decodes normals from 4D 8-bit packed format into a 32-bit float format. 
-		 *
-		 * @param[in]	source			Buffer containing data to encode. Must have @p count entries, each 32-bits.
-		 * @param[out]	destination		Buffer to output the data to. Must have @p count entries.
-		 * @param[in]	count			Number of entries in the @p source and @p destination arrays.
-		 * @param[in]	stride			Distance between two entries in the @p source buffer, in bytes.
-		 */
-		void unpackNormals(UINT8* source, Vector3* destination, UINT32 count, UINT32 stride);
-
-		/** 
-		 * Decodes normals from 4D 8-bit packed format into a 32-bit float format. 
-		 *
-		 * @param[in]	source			Buffer containing data to encode. Must have @p count entries, each 32-bits.
-		 * @param[out]	destination		Buffer to output the data to. Must have @p count entries.
-		 * @param[in]	count			Number of entries in the @p source and @p destination arrays.
-		 * @param[in]	stride			Distance between two entries in the @p source buffer, in bytes.
-		 */
-		void unpackNormals(UINT8* source, Vector4* destination, UINT32 count, UINT32 stride);
-
 		SPtr<MeshData> mMeshData;
 	};
 

+ 82 - 20
Source/BansheeCore/Source/BsAnimation.cpp

@@ -5,6 +5,7 @@
 #include "BsAnimationClip.h"
 #include "BsAnimationUtility.h"
 #include "BsSceneObject.h"
+#include "BsMorphShapes.h"
 
 namespace BansheeEngine
 {
@@ -33,7 +34,8 @@ namespace BansheeEngine
 
 	AnimationProxy::AnimationProxy(UINT64 id)
 		: id(id), layers(nullptr), numLayers(0), numSceneObjects(0), sceneObjectInfos(nullptr)
-		, sceneObjectTransforms(nullptr), mCullEnabled(true), numGenericCurves(0), genericCurveOutputs(nullptr)
+		, sceneObjectTransforms(nullptr), morphShapeInfos(nullptr), numMorphShapes(0), numMorphVertices(0)
+		, morphShapeWeightsDirty(false), mCullEnabled(true), numGenericCurves(0), genericCurveOutputs(nullptr)
 	{ }
 
 	AnimationProxy::~AnimationProxy()
@@ -99,6 +101,11 @@ namespace BansheeEngine
 			layer.~AnimationStateLayer();
 		}
 
+		for(UINT32 i = 0; i < numMorphShapes; i++)
+		{
+			morphShapeInfos[i].shape.~SPtr<MorphShape>();
+		}
+
 		// All of the memory is part of the same buffer, so we only need to free the first element
 		bs_free(layers);
 		layers = nullptr;
@@ -111,7 +118,8 @@ namespace BansheeEngine
 	}
 
 	void AnimationProxy::rebuild(const SPtr<Skeleton>& skeleton, const SkeletonMask& mask, 
-		Vector<AnimationClipInfo>& clipInfos, const Vector<AnimatedSceneObject>& sceneObjects)
+		Vector<AnimationClipInfo>& clipInfos, const Vector<AnimatedSceneObject>& sceneObjects, 
+		const SPtr<MorphShapes>& morphShapes)
 	{
 		this->skeleton = skeleton;
 		this->skeletonMask = mask;
@@ -127,10 +135,11 @@ namespace BansheeEngine
 		else
 			sceneObjectPose = LocalSkeletonPose();
 
-		rebuild(clipInfos, sceneObjects);
+		rebuild(clipInfos, sceneObjects, morphShapes);
 	}
 
-	void AnimationProxy::rebuild(Vector<AnimationClipInfo>& clipInfos, const Vector<AnimatedSceneObject>& sceneObjects)
+	void AnimationProxy::rebuild(Vector<AnimationClipInfo>& clipInfos, const Vector<AnimatedSceneObject>& sceneObjects, 
+		const SPtr<MorphShapes>& morphShapes)
 	{
 		clear();
 
@@ -245,6 +254,17 @@ namespace BansheeEngine
 				}
 			}
 
+			if (morphShapes != nullptr)
+			{
+				numMorphShapes = morphShapes->getNumShapes();
+				numMorphVertices = morphShapes->getNumVertices();
+			}
+			else
+			{
+				numMorphShapes = 0;
+				numMorphVertices = 0;
+			}
+
 			UINT32 numBoneMappings = numBones * numClips;
 			UINT32 layersSize = sizeof(AnimationStateLayer) * numLayers;
 			UINT32 clipsSize = sizeof(AnimationState) * numClips;
@@ -256,9 +276,11 @@ namespace BansheeEngine
 			UINT32 genericCurveOutputSize = numGenericCurves * sizeof(float);
 			UINT32 sceneObjectIdsSize = numSceneObjects * sizeof(AnimatedSceneObjectInfo);
 			UINT32 sceneObjectTransformsSize = numBoneMappedSOs * sizeof(Matrix4);
+			UINT32 morphShapeSize = numMorphShapes * sizeof(MorphShapeInfo);
 
 			UINT8* data = (UINT8*)bs_alloc(layersSize + clipsSize + boneMappingSize + posCacheSize + rotCacheSize + 
-				scaleCacheSize + genCacheSize + genericCurveOutputSize + sceneObjectIdsSize + sceneObjectTransformsSize);
+				scaleCacheSize + genCacheSize + genericCurveOutputSize + sceneObjectIdsSize + sceneObjectTransformsSize +
+				morphShapeSize);
 
 			layers = (AnimationStateLayer*)data;
 			memcpy(layers, tempLayers.data(), layersSize);
@@ -312,6 +334,18 @@ namespace BansheeEngine
 
 			data += sceneObjectTransformsSize;
 
+			morphShapeInfos = (MorphShapeInfo*)data;
+			for (UINT32 i = 0; i < numMorphShapes; i++)
+			{
+				new (&morphShapeInfos[i].shape) SPtr<MorphShape>();
+
+				morphShapeInfos[i].shape = morphShapes->getShape(i);
+				morphShapeInfos[i].weight = 0.0f;
+			}
+
+			morphShapeWeightsDirty = true;
+			data += morphShapeSize;
+
 			UINT32 curLayerIdx = 0;
 			UINT32 curStateIdx = 0;
 
@@ -490,7 +524,7 @@ namespace BansheeEngine
 		bs_frame_clear();
 	}
 
-	void AnimationProxy::updateValues(const Vector<AnimationClipInfo>& clipInfos)
+	void AnimationProxy::updateClipInfos(const Vector<AnimationClipInfo>& clipInfos)
 	{
 		for(auto& clipInfo : clipInfos)
 		{
@@ -505,6 +539,20 @@ namespace BansheeEngine
 		}
 	}
 
+	void AnimationProxy::updateMorphShapeWeights(const Vector<float>& weights)
+	{
+		UINT32 numWeights = (UINT32)weights.size();
+		for(UINT32 i = 0; i < numMorphShapes; i++)
+		{
+			if (i < numWeights)
+				morphShapeInfos[i].weight = weights[i];
+			else
+				morphShapeInfos[i].weight = 0.0f;
+		}
+
+		morphShapeWeightsDirty = true;
+	}
+
 	void AnimationProxy::updateTransforms(const Vector<AnimatedSceneObject>& sceneObjects)
 	{
 		Matrix4 invRootTransform(BsIdentity);
@@ -553,7 +601,7 @@ namespace BansheeEngine
 	}
 
 	Animation::Animation()
-		: mDefaultWrapMode(AnimWrapMode::Loop), mDefaultSpeed(1.0f), mCull(true), mDirty(AnimDirtyStateFlag::Skeleton)
+		: mDefaultWrapMode(AnimWrapMode::Loop), mDefaultSpeed(1.0f), mCull(true), mDirty(AnimDirtyStateFlag::All)
 		, mGenericCurveValuesValid(false)
 	{
 		mId = AnimationManager::instance().registerAnimation(this);
@@ -568,25 +616,37 @@ namespace BansheeEngine
 	void Animation::setSkeleton(const SPtr<Skeleton>& skeleton)
 	{
 		mSkeleton = skeleton;
-		mDirty |= AnimDirtyStateFlag::Skeleton;
+		mDirty |= AnimDirtyStateFlag::All;
 	}
 
 	void Animation::setMorphShapes(const SPtr<MorphShapes>& morphShapes)
 	{
-		BS_EXCEPT(NotImplementedException, "TODO");
-		// TODO
+		mMorphShapes = morphShapes;
+
+		UINT32 numShapes;
+		if (mMorphShapes != nullptr)
+			numShapes = mMorphShapes->getNumShapes();
+		else
+			numShapes = 0;
+
+		mMorphShapeWeights.assign(numShapes, 0.0f);
+		mDirty |= AnimDirtyStateFlag::Layout;
 	}
 
 	void Animation::setMorphShapeWeight(UINT32 idx, float weight)
 	{
-		BS_EXCEPT(NotImplementedException, "TODO");
-		// TODO
+		UINT32 numShapes = (UINT32)mMorphShapeWeights.size();
+		if (idx >= numShapes)
+			return;
+
+		mMorphShapeWeights[idx] = weight;
+		mDirty |= AnimDirtyStateFlag::MorphWeights;
 	}
 
 	void Animation::setMask(const SkeletonMask& mask)
 	{
 		mSkeletonMask = mask;
-		mDirty |= AnimDirtyStateFlag::Skeleton;
+		mDirty |= AnimDirtyStateFlag::All;
 	}
 
 	void Animation::setWrapMode(AnimWrapMode wrapMode)
@@ -1091,14 +1151,14 @@ namespace BansheeEngine
 		AnimatedSceneObject animSo = { so, curve };
 		mSceneObjects[so.getInstanceId()] = animSo;
 
-		mDirty |= AnimDirtyStateFlag::Skeleton;
+		mDirty |= AnimDirtyStateFlag::All;
 	}
 
 	void Animation::unmapSceneObject(const HSceneObject& so)
 	{
 		mSceneObjects.erase(so.getInstanceId());
 
-		mDirty |= AnimDirtyStateFlag::Skeleton;
+		mDirty |= AnimDirtyStateFlag::All;
 	}
 
 	bool Animation::getGenericCurveValue(UINT32 curveIdx, float& value)
@@ -1168,22 +1228,24 @@ namespace BansheeEngine
 		}
 		else
 		{
-			if (mDirty.isSet(AnimDirtyStateFlag::Skeleton))
+			if (mDirty.isSet(AnimDirtyStateFlag::All))
 			{
 				Vector<AnimatedSceneObject> animatedSOs = getAnimatedSOList();
 
-				mAnimProxy->rebuild(mSkeleton, mSkeletonMask, mClipInfos, animatedSOs);
+				mAnimProxy->rebuild(mSkeleton, mSkeletonMask, mClipInfos, animatedSOs, mMorphShapes);
 				didFullRebuild = true;
 			}
 			else if (mDirty.isSet(AnimDirtyStateFlag::Layout))
 			{
 				Vector<AnimatedSceneObject> animatedSOs = getAnimatedSOList();
 
-				mAnimProxy->rebuild(mClipInfos, animatedSOs);
+				mAnimProxy->rebuild(mClipInfos, animatedSOs, mMorphShapes);
 				didFullRebuild = true;
 			}
-			else if (mDirty.isSet(AnimDirtyStateFlag::Value))
-				mAnimProxy->updateValues(mClipInfos);
+			else if(mDirty.isSet(AnimDirtyStateFlag::Value))
+
+			if (mDirty.isSet(AnimDirtyStateFlag::MorphWeights))
+				mAnimProxy->updateMorphShapeWeights(mMorphShapeWeights);
 		}
 
 		// Check if there are dirty transforms

+ 113 - 6
Source/BansheeCore/Source/BsAnimationManager.cpp

@@ -7,6 +7,9 @@
 #include "BsTime.h"
 #include "BsCoreSceneManager.h"
 #include "BsCamera.h"
+#include "BsMorphShapes.h"
+#include "BsMeshData.h"
+#include "BsMeshUtility.h"
 
 namespace BansheeEngine
 {
@@ -16,6 +19,10 @@ namespace BansheeEngine
 		, mPoseWriteBufferIdx(0), mDataReadyCount(0), mDataReady(false)
 	{
 		mAnimationWorker = Task::create("Animation", std::bind(&AnimationManager::evaluateAnimation, this));
+
+		mBlendShapeVertexDesc = VertexDataDesc::create();
+		mBlendShapeVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION, 1, 1);
+		mBlendShapeVertexDesc->addVertElem(VET_UBYTE4_NORM, VES_NORMAL, 1, 1);
 	}
 
 	void AnimationManager::setPaused(bool paused)
@@ -118,9 +125,10 @@ namespace BansheeEngine
 		RendererAnimationData& renderData = mAnimData[mPoseWriteBufferIdx];
 		mPoseWriteBufferIdx = (mPoseWriteBufferIdx + 1) % CoreThread::NUM_SYNC_BUFFERS;
 
-		renderData.poseInfos.clear();
 		renderData.transforms.resize(totalNumBones);
 
+		UnorderedMap<UINT64, RendererAnimationData::AnimInfo> newAnimInfos;
+
 		UINT32 curBoneIdx = 0;
 		for(auto& anim : mProxies)
 		{
@@ -140,14 +148,18 @@ namespace BansheeEngine
 					continue;
 			}
 
+			RendererAnimationData::AnimInfo animInfo;
+			bool hasAnimInfo = false;
+
+			// Evaluate skeletal animation
 			if (anim->skeleton != nullptr)
 			{
 				UINT32 numBones = anim->skeleton->getNumBones();
 
-				RendererAnimationData::PoseInfo info;
-				info.animId = anim->id;
-				info.startIdx = curBoneIdx;
-				info.numBones = numBones;
+				RendererAnimationData::PoseInfo& poseInfo = animInfo.poseInfo;
+				poseInfo.animId = anim->id;
+				poseInfo.startIdx = curBoneIdx;
+				poseInfo.numBones = numBones;
 
 				memset(anim->skeletonPose.hasOverride, 0, sizeof(bool) * anim->skeletonPose.numBones);
 				Matrix4* boneDst = renderData.transforms.data() + curBoneIdx;
@@ -169,8 +181,15 @@ namespace BansheeEngine
 				// Animate bones
 				anim->skeleton->getPose(boneDst, anim->skeletonPose, anim->skeletonMask, anim->layers, anim->numLayers);
 
-				renderData.poseInfos[anim->id] = info;
 				curBoneIdx += numBones;
+				hasAnimInfo = true;
+			}
+			else
+			{
+				RendererAnimationData::PoseInfo& poseInfo = animInfo.poseInfo;
+				poseInfo.animId = anim->id;
+				poseInfo.startIdx = 0;
+				poseInfo.numBones = 0;
 			}
 
 			// Reset mapped SO transform
@@ -184,6 +203,7 @@ namespace BansheeEngine
 			// Update mapped scene objects
 			memset(anim->sceneObjectPose.hasOverride, 1, sizeof(bool) * anim->numSceneObjects);
 
+			// Update scene object transforms
 			for(UINT32 i = 0; i < anim->numSceneObjects; i++)
 			{
 				const AnimatedSceneObjectInfo& soInfo = anim->sceneObjectInfos[i];
@@ -231,6 +251,7 @@ namespace BansheeEngine
 				}
 			}
 
+			// Update generic curves
 			// Note: No blending for generic animations, just use first animation
 			if (anim->numLayers > 0 && anim->layers[0].numStates > 0)
 			{
@@ -247,8 +268,94 @@ namespace BansheeEngine
 					}
 				}
 			}
+
+			// Update morph shapes
+			if(anim->numMorphShapes > 0)
+			{
+				auto iterFind = renderData.infos.find(anim->id);
+				if (iterFind != renderData.infos.end())
+					animInfo.morphShapeInfo = iterFind->second.morphShapeInfo;
+				else
+					animInfo.morphShapeInfo.version = 0;
+
+				if(anim->morphShapeWeightsDirty)
+				{
+					SPtr<MeshData> meshData = bs_shared_ptr_new<MeshData>(anim->numMorphVertices, 0, mBlendShapeVertexDesc);
+
+					UINT8* bufferData = meshData->getData();
+					memset(bufferData, 0, meshData->getSize());
+
+					UINT32 tempDataSize = (sizeof(Vector3) + sizeof(float)) * anim->numMorphVertices;
+					UINT8* tempData = (UINT8*)bs_stack_alloc(tempDataSize);
+					memset(tempData, 0, tempDataSize);
+
+					Vector3* tempNormals = (Vector3*)tempData;
+					float* accumulatedWeight = (float*)(tempData + sizeof(Vector3) * anim->numMorphVertices);
+
+					UINT8* positions = meshData->getElementData(VES_POSITION, 1, 1);
+					UINT8* normals = meshData->getElementData(VES_NORMAL, 1, 1);
+
+					UINT32 stride = mBlendShapeVertexDesc->getVertexStride(1);
+
+					for(UINT32 i = 0; i < anim->numMorphShapes; i++)
+					{
+						const MorphShapeInfo& info = anim->morphShapeInfos[i];
+						float absWeight = Math::abs(info.weight);
+
+						if (absWeight < 0.0001f)
+							continue;
+
+						const Vector<MorphVertex>& morphVertices = info.shape->getVertices();
+						UINT32 numVertices = (UINT32)morphVertices.size();
+						for(UINT32 j = 0; j < numVertices; j++)
+						{
+							const MorphVertex& vertex = morphVertices[j];
+
+							Vector3* destPos = (Vector3*)(positions + vertex.sourceIdx * stride);
+							*destPos += vertex.deltaPosition * info.weight;
+
+							tempNormals[vertex.sourceIdx] += vertex.deltaNormal * info.weight;
+							accumulatedWeight[vertex.sourceIdx] += absWeight;
+						}
+					}
+
+					for(UINT32 i = 0; i < anim->numMorphVertices; i++)
+					{
+						PackedNormal* destNrm = (PackedNormal*)(normals + i * stride);
+
+						if (accumulatedWeight[i] > 0.0001f)
+						{
+							Vector3 normal = tempNormals[i] / accumulatedWeight[i];
+							normal /= 2.0f; // Accumulated normal is in range [-2, 2] but our normal packing method assumes [-1, 1] range
+
+							MeshUtility::packNormals(&normal, (UINT8*)destNrm, 1, stride);
+							destNrm->w = (UINT8)(std::min(1.0f, accumulatedWeight[i]) * 255.999f);
+						}
+						else
+						{
+							*destNrm = { 127, 127, 127, 0 };
+						}
+					}
+
+					bs_stack_free(tempData);
+
+					animInfo.morphShapeInfo.meshData = meshData;
+
+					animInfo.morphShapeInfo.version++;
+					anim->morphShapeWeightsDirty = false;
+				}
+
+				hasAnimInfo = true;
+			}
+			else
+				animInfo.morphShapeInfo.version = 0;
+
+			if (hasAnimInfo)
+				newAnimInfos[anim->id] = animInfo;
 		}
 
+		renderData.infos = newAnimInfos;
+
 		mDataReadyCount.fetch_add(1, std::memory_order_relaxed);
 
 		// Make sure the thread finishes writing skeletal pose and other evaluation outputs as they will be read by sim and

+ 62 - 0
Source/BansheeCore/Source/BsMeshUtility.cpp

@@ -1,6 +1,7 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsMeshUtility.h"
+#include "BsVector4.h"
 #include "BsVector3.h"
 #include "BsVector2.h"
 #include "BsPlane.h"
@@ -864,4 +865,65 @@ namespace BansheeEngine
 		TriangleClipper3D clipper;
 		clipper.clip(vertices, uvs, numTris, vertexStride, clipPlanes, writeCallback);
 	}
+
+	void MeshUtility::packNormals(Vector3* source, UINT8* destination, UINT32 count, UINT32 stride)
+	{
+		UINT8* ptr = destination;
+		for (UINT32 i = 0; i < count; i++)
+		{
+			PackedNormal& packed = *(PackedNormal*)ptr;
+			packed.x = Math::clamp((int)(source[i].x * 127.5f + 127.5f), 0, 255);
+			packed.y = Math::clamp((int)(source[i].y * 127.5f + 127.5f), 0, 255);
+			packed.z = Math::clamp((int)(source[i].z * 127.5f + 127.5f), 0, 255);
+			packed.w = 128;
+
+			ptr += stride;
+		}
+	}
+
+	void MeshUtility::packNormals(Vector4* source, UINT8* destination, UINT32 count, UINT32 stride)
+	{
+		UINT8* ptr = destination;
+		for (UINT32 i = 0; i < count; i++)
+		{
+			PackedNormal& packed = *(PackedNormal*)ptr;
+			packed.x = Math::clamp((int)(source[i].x * 127.5f + 127.5f), 0, 255);
+			packed.y = Math::clamp((int)(source[i].y * 127.5f + 127.5f), 0, 255);
+			packed.z = Math::clamp((int)(source[i].z * 127.5f + 127.5f), 0, 255);
+			packed.w = Math::clamp((int)(source[i].w * 127.5f + 127.5f), 0, 255);
+
+			ptr += stride;
+		}
+	}
+
+	void MeshUtility::unpackNormals(UINT8* source, Vector3* destination, UINT32 count, UINT32 stride)
+	{
+		UINT8* ptr = source;
+		for (UINT32 i = 0; i < count; i++)
+		{
+			PackedNormal& packed = *(PackedNormal*)ptr;
+
+			destination[i].x = (packed.x * 2.0f - 1.0f);
+			destination[i].y = (packed.y * 2.0f - 1.0f);
+			destination[i].z = (packed.z * 2.0f - 1.0f);
+
+			ptr += stride;
+		}
+	}
+
+	void MeshUtility::unpackNormals(UINT8* source, Vector4* destination, UINT32 count, UINT32 stride)
+	{
+		UINT8* ptr = source;
+		for (UINT32 i = 0; i < count; i++)
+		{
+			PackedNormal& packed = *(PackedNormal*)ptr;
+
+			destination[i].x = (packed.x * 2.0f - 1.0f);
+			destination[i].y = (packed.y * 2.0f - 1.0f);
+			destination[i].z = (packed.z * 2.0f - 1.0f);
+			destination[i].w = (packed.w * 2.0f - 1.0f);
+
+			ptr += stride;
+		}
+	}
 }

+ 7 - 5
Source/BansheeCore/Source/BsMorphShapes.cpp

@@ -31,13 +31,15 @@ namespace BansheeEngine
 	MorphShapes::MorphShapes()
 	{ }
 
-	MorphShapes::MorphShapes(const Vector<SPtr<MorphShape>>& shapes)
-		:mShapes(shapes)
-	{ }
+	MorphShapes::MorphShapes(const Vector<SPtr<MorphShape>>& shapes, UINT32 numVertices)
+		:mShapes(shapes), mNumVertices(numVertices)
+	{
+
+	}
 
-	SPtr<MorphShapes> MorphShapes::create(const Vector<SPtr<MorphShape>>& shapes)
+	SPtr<MorphShapes> MorphShapes::create(const Vector<SPtr<MorphShape>>& shapes, UINT32 numVertices)
 	{
-		MorphShapes* raw = new (bs_alloc<MorphShapes>()) MorphShapes(shapes);
+		MorphShapes* raw = new (bs_alloc<MorphShapes>()) MorphShapes(shapes, numVertices);
 		return bs_shared_ptr(raw);
 	}
 

+ 5 - 78
Source/BansheeCore/Source/BsRendererMeshData.cpp

@@ -9,6 +9,7 @@
 #include "BsPixelUtil.h"
 #include "BsRendererManager.h"
 #include "BsCoreRenderer.h"
+#include "BsMeshUtility.h"
 
 namespace BansheeEngine
 {
@@ -58,7 +59,7 @@ namespace BansheeEngine
 		UINT8* normalSrc = mMeshData->getElementData(VES_NORMAL);
 		UINT32 stride = mMeshData->getVertexDesc()->getVertexStride(0);
 
-		unpackNormals(normalSrc, buffer, numElements, stride);
+		MeshUtility::unpackNormals(normalSrc, buffer, numElements, stride);
 	}
 
 	void RendererMeshData::setNormals(Vector3* buffer, UINT32 size)
@@ -72,7 +73,7 @@ namespace BansheeEngine
 		UINT8* normalDst = mMeshData->getElementData(VES_NORMAL);
 		UINT32 stride = mMeshData->getVertexDesc()->getVertexStride(0);
 
-		packNormals(buffer, normalDst, numElements, stride);
+		MeshUtility::packNormals(buffer, normalDst, numElements, stride);
 	}
 
 	void RendererMeshData::getTangents(Vector4* buffer, UINT32 size)
@@ -86,7 +87,7 @@ namespace BansheeEngine
 		UINT8* tangentSrc = mMeshData->getElementData(VES_TANGENT);
 		UINT32 stride = mMeshData->getVertexDesc()->getVertexStride(0);
 
-		unpackNormals(tangentSrc, buffer, numElements, stride);
+		MeshUtility::unpackNormals(tangentSrc, buffer, numElements, stride);
 	}
 
 	void RendererMeshData::setTangents(Vector4* buffer, UINT32 size)
@@ -100,7 +101,7 @@ namespace BansheeEngine
 		UINT8* tangentDst = mMeshData->getElementData(VES_TANGENT);
 		UINT32 stride = mMeshData->getVertexDesc()->getVertexStride(0);
 
-		packNormals(buffer, tangentDst, numElements, stride);
+		MeshUtility::packNormals(buffer, tangentDst, numElements, stride);
 	}
 
 	void RendererMeshData::getColors(Color* buffer, UINT32 size)
@@ -374,78 +375,4 @@ namespace BansheeEngine
 
 		return vertexDesc;
 	}
-
-	union PackedNormal
-	{
-		struct
-		{
-			UINT8 x;
-			UINT8 y;
-			UINT8 z;
-			UINT8 w;
-		};
-
-		UINT32 packed;
-	};
-
-	void RendererMeshData::packNormals(Vector3* source, UINT8* destination, UINT32 count, UINT32 stride)
-	{
-		UINT8* ptr = destination;
-		for(UINT32 i = 0; i < count; i++)
-		{
-			PackedNormal& packed = *(PackedNormal*)ptr;
-			packed.x = Math::clamp((int)(source[i].x * 127.5f + 127.5f), 0, 255);
-			packed.y = Math::clamp((int)(source[i].y * 127.5f + 127.5f), 0, 255);
-			packed.z = Math::clamp((int)(source[i].z * 127.5f + 127.5f), 0, 255);
-			packed.w = 128;
-
-			ptr += stride;
-		}
-	}
-
-	void RendererMeshData::packNormals(Vector4* source, UINT8* destination, UINT32 count, UINT32 stride)
-	{
-		UINT8* ptr = destination;
-		for (UINT32 i = 0; i < count; i++)
-		{
-			PackedNormal& packed = *(PackedNormal*)ptr;
-			packed.x = Math::clamp((int)(source[i].x * 127.5f + 127.5f), 0, 255);
-			packed.y = Math::clamp((int)(source[i].y * 127.5f + 127.5f), 0, 255);
-			packed.z = Math::clamp((int)(source[i].z * 127.5f + 127.5f), 0, 255);
-			packed.w = Math::clamp((int)(source[i].w * 127.5f + 127.5f), 0, 255);
-
-			ptr += stride;
-		}
-	}
-
-	void RendererMeshData::unpackNormals(UINT8* source, Vector3* destination, UINT32 count, UINT32 stride)
-	{
-		UINT8* ptr = source;
-		for (UINT32 i = 0; i < count; i++)
-		{
-			PackedNormal& packed = *(PackedNormal*)ptr;
-
-			destination[i].x = (packed.x * 2.0f - 1.0f);
-			destination[i].y = (packed.y * 2.0f - 1.0f);
-			destination[i].z = (packed.z * 2.0f - 1.0f);
-
-			ptr += stride;
-		}
-	}
-
-	void RendererMeshData::unpackNormals(UINT8* source, Vector4* destination, UINT32 count, UINT32 stride)
-	{
-		UINT8* ptr = source;
-		for (UINT32 i = 0; i < count; i++)
-		{
-			PackedNormal& packed = *(PackedNormal*)ptr;
-
-			destination[i].x = (packed.x * 2.0f - 1.0f);
-			destination[i].y = (packed.y * 2.0f - 1.0f);
-			destination[i].z = (packed.z * 2.0f - 1.0f);
-			destination[i].w = (packed.w * 2.0f - 1.0f);
-
-			ptr += stride;
-		}
-	}
 }

+ 3 - 3
Source/BansheeEditor/Source/BsSelectionRenderer.cpp

@@ -165,10 +165,10 @@ namespace BansheeEngine
 					// new one every frame
 					SPtr<GpuBufferCore> buffer = GpuBufferCore::create(numBones * 3, 0, GBT_STANDARD, BF_32X4F, GBU_DYNAMIC);
 
-					auto iterFind = animData.poseInfos.find(renderable->getAnimationId());
-					if (iterFind != animData.poseInfos.end())
+					auto iterFind = animData.infos.find(renderable->getAnimationId());
+					if (iterFind != animData.infos.end())
 					{
-						const RendererAnimationData::PoseInfo& poseInfo = iterFind->second;
+						const RendererAnimationData::PoseInfo& poseInfo = iterFind->second.poseInfo;
 
 						UINT8* dest = (UINT8*)buffer->lock(0, poseInfo.numBones * 3 * sizeof(Vector4), GBL_WRITE_ONLY_DISCARD);
 						for (UINT32 j = 0; j < poseInfo.numBones; j++)

+ 1 - 1
Source/BansheeFBXImporter/Source/BsFBXImporter.cpp

@@ -413,7 +413,7 @@ namespace BansheeEngine
 		}
 
 		if (!allMorphShapes.empty())
-			return MorphShapes::create(allMorphShapes);
+			return MorphShapes::create(allMorphShapes, totalNumVertices);
 
 		return morphShapes;
 	}

+ 3 - 3
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -559,10 +559,10 @@ namespace BansheeEngine
 				// all such elements
 				SPtr<GpuBufferCore> boneMatrices = element.boneMatrixBuffer;
 
-				auto iterFind = animData.poseInfos.find(element.animationId);
-				if (iterFind != animData.poseInfos.end())
+				auto iterFind = animData.infos.find(element.animationId);
+				if (iterFind != animData.infos.end())
 				{
-					const RendererAnimationData::PoseInfo& poseInfo = iterFind->second;
+					const RendererAnimationData::PoseInfo& poseInfo = iterFind->second.poseInfo;
 
 					UINT8* dest = (UINT8*)boneMatrices->lock(0, poseInfo.numBones * 3 * sizeof(Vector4), GBL_WRITE_ONLY_DISCARD);
 					for (UINT32 j = 0; j < poseInfo.numBones; j++)