Browse Source

Morph shape animation (WIP)

BearishSun 9 years ago
parent
commit
76dee9fbf8

+ 230 - 0
Data/Raw/Editor/Includes/SelectionBase.bslinc

@@ -0,0 +1,230 @@
+Parameters =
+{
+	mat4x4			matWorldViewProj;
+	float4			selColor;
+	StructBuffer 	boneMatrices;
+};
+
+Technique : base("SelectionBase") =
+{
+	Language = "HLSL11";
+	
+	Pass =
+	{
+		Fill = WIRE;
+		DepthBias = 0.00001f;
+		
+		Target =
+		{
+			Blend = true;
+			Color = { SRCA, SRCIA, ADD };
+		};
+
+		Fragment =
+		{
+			float4 selColor;
+
+			float4 main(in float4 inPos : SV_Position) : SV_Target
+			{
+				return selColor;
+			}
+		};
+	};
+};
+
+Technique 
+#if USE_BLEND_SHAPES
+	#if USE_SKELETON
+	 : base("SelectionSkinnedMorph")
+	#else
+	 : base("SelectionMorph")
+	#endif
+#else
+	#if USE_SKELETON
+	 : base("SelectionSkinned")
+	#else
+	 : base("Selection")
+	#endif
+#endif
+ : inherits("SelectionBase") = 
+{
+	Language = "HLSL11";
+
+	Vertex =
+	{
+		struct VertexInput
+		{
+			float3 position : POSITION;
+			
+			#if USE_SKELETON
+				uint4 blendIndices : BLENDINDICES;
+				float4 blendWeights : BLENDWEIGHT;
+			#endif
+			
+			#if USE_BLEND_SHAPES
+				float3 deltaPosition : POSITION1;
+				float4 deltaNormal : NORMAL1;
+			#endif
+		};	
+	
+		float4x4 matWorldViewProj;
+	
+#if USE_SKELETON
+		StructuredBuffer<float4> boneMatrices;
+		
+		float3x4 getBoneMatrix(uint idx)
+		{
+			float4 row0 = boneMatrices[idx * 3 + 0];
+			float4 row1 = boneMatrices[idx * 3 + 1];
+			float4 row2 = boneMatrices[idx * 3 + 2];
+			
+			return float3x4(row0, row1, row2);
+		}
+		
+		float3x4 getBlendMatrix(VertexInput input)
+		{
+			float3x4 result = input.blendWeights.x * getBoneMatrix(input.blendIndices.x);
+			result += input.blendWeights.y * getBoneMatrix(input.blendIndices.y);
+			result += input.blendWeights.z * getBoneMatrix(input.blendIndices.z);
+			result += input.blendWeights.w * getBoneMatrix(input.blendIndices.w);
+			
+			return result;
+		}			
+#endif
+		
+		void main(VertexInput input, out float4 oPosition : SV_Position)
+		{
+#if USE_BLEND_SHAPES
+			float4 position = float4(input.position + input.deltaPosition, 1.0f);
+#else
+			float4 position = float4(input.position, 1.0f);
+#endif
+		
+#if USE_SKELETON
+			float3x4 blendMatrix = getBlendMatrix(input);
+			position = float4(mul(blendMatrix, position), 1.0f);
+#endif
+
+			oPosition = mul(matWorldViewProj, position);
+		}
+	};
+};
+
+Technique : base("SelectionBase") =
+{
+	Language = "GLSL";
+	
+	Pass =
+	{
+		Fill = WIRE;
+		DepthBias = 0.00001f;
+		
+		Target =
+		{
+			Blend = true;
+			Color = { SRCA, SRCIA, ADD };
+		};
+		
+		Fragment =
+		{
+			uniform vec4 selColor;
+			out vec4 fragColor;
+
+			void main()
+			{
+				fragColor = selColor;
+			}
+		};
+	};
+};
+
+Technique
+#if USE_BLEND_SHAPES
+	#if USE_SKELETON
+	 : base("SelectionSkinnedMorph")
+	#else
+	 : base("SelectionMorph")
+	#endif
+#else
+	#if USE_SKELETON
+	 : base("SelectionSkinned")
+	#else
+	 : base("Selection")
+	#endif
+#endif
+ : inherits("SelectionBase") = 
+{
+	Language = "GLSL";
+	
+	Vertex =
+	{
+		uniform mat4 matWorldViewProj;
+
+		in vec3 bs_position;
+	
+		#if USE_SKELETON
+			in uvec4 bs_blendindices;
+			in vec4 bs_blendweights;
+		#endif
+			
+		#if USE_BLEND_SHAPES
+			in vec3 bs_position1;
+			in vec4 bs_normal1;
+		#endif
+		
+		out gl_PerVertex
+		{
+			vec4 gl_Position;
+		};
+		
+#if USE_SKELETON
+		uniform samplerBuffer boneMatrices;
+		
+		void getBoneMatrix(uint idx, out mat4x3 result)
+		{
+			mat3x4 temp;
+		
+			temp[0] = texelFetch(boneMatrices, idx * 3 + 0);
+			temp[1] = texelFetch(boneMatrices, idx * 3 + 1);
+			temp[2] = texelFetch(boneMatrices, idx * 3 + 2);
+			
+			result = transpose(temp);				
+		}
+		
+		void getBlendMatrix(out mat4x3 result)
+		{
+			mat4x3 boneMatrix;
+			
+			getBoneMatrix(bs_blendindices.x, out boneMatrix);
+			result = bs_blendweights.x * boneMatrix;
+			
+			getBoneMatrix(bs_blendindices.y, out boneMatrix);
+			result += bs_blendweights.y * boneMatrix;
+			
+			getBoneMatrix(bs_blendindices.z, out boneMatrix);
+			result += bs_blendweights.z * boneMatrix;
+			
+			getBoneMatrix(bs_blendindices.w, out boneMatrix);
+			result += bs_blendweights.w * boneMatrix;
+		}
+#endif
+		
+		void main()
+		{
+#if USE_BLEND_SHAPES
+			vec4 position = vec4(bs_position + bs_position1, 1.0f);
+#else
+			vec4 position = vec4(bs_position, 1.0f);
+#endif		
+		
+#if USE_SKELETON
+			mat3x4 blendMatrix;
+			getBlendMatrix(blendMatrix);
+			
+			position = vec4(blendMatrix * position, 1.0f);
+#endif
+
+			gl_Position = matWorldViewProj * position;
+		}
+	};
+};

+ 2 - 2
Data/Raw/Editor/Shaders/Selection.bsl

@@ -52,7 +52,7 @@ Technique : inherits("SelectionBase") =
 Technique : inherits("SelectionBase") = 
 {
 	Language = "HLSL11";
-	Tags = { "Animated" };
+	Tags = { "Skinned" };
 	
 	Vertex =
 	{
@@ -152,7 +152,7 @@ Technique : inherits("SelectionBase") =
 Technique : inherits("SelectionBase") = 
 {
 	Language = "GLSL";
-	Tags = { "Animated" };
+	Tags = { "Skinned" };
 	
 	Vertex =
 	{

+ 46 - 1
Data/Raw/Engine/Includes/DeferredBasePass.bslinc

@@ -66,7 +66,30 @@ Technique
 	Language = "HLSL11";
 };
 
-Technique : base("DeferredBasePassCommon") =
+Technique
+ : base("DeferredBasePassMorph")
+ : inherits("GBuffer")
+ : inherits("PerCameraData")
+ : inherits("PerObjectData")
+ : inherits("MorphVertexInput")
+ : inherits("DeferredBasePassCommon") =
+{
+	Language = "HLSL11";
+};
+
+Technique
+ : base("DeferredBasePassSkinnedMorph")
+ : inherits("GBuffer")
+ : inherits("PerCameraData")
+ : inherits("PerObjectData")
+ : inherits("SkinnedMorphVertexInput")
+ : inherits("DeferredBasePassCommon") =
+{
+	Language = "HLSL11";
+};
+
+Technique 
+	: base("DeferredBasePassCommon") =
 {
 	Language = "GLSL";
 	
@@ -122,4 +145,26 @@ Technique
  : inherits("DeferredBasePassCommon") =
 {
 	Language = "GLSL";
+};
+
+Technique
+ : base("DeferredBasePassMorph")
+ : inherits("GBuffer")
+ : inherits("PerCameraData")
+ : inherits("PerObjectData")
+ : inherits("MorphVertexInput")
+ : inherits("DeferredBasePassCommon") =
+{
+	Language = "GLSL";
+};
+
+Technique
+ : base("DeferredBasePassSkinnedMorph")
+ : inherits("GBuffer")
+ : inherits("PerCameraData")
+ : inherits("PerObjectData")
+ : inherits("SkinnedMorphVertexInput")
+ : inherits("DeferredBasePassCommon") =
+{
+	Language = "GLSL";
 };

+ 49 - 4
Data/Raw/Engine/Includes/NormalVertexInput.bslinc

@@ -1,4 +1,9 @@
-Technique : base("NormalVertexInput") =
+Technique
+#if USE_BLEND_SHAPES
+	 : base("MorphVertexInput") =
+#else
+	 : base("NormalVertexInput") =
+#endif
 {
 	Language = "HLSL11";
 	
@@ -13,6 +18,11 @@ Technique : base("NormalVertexInput") =
 				
 				float3 tangentToWorldZ : NORMAL; // Note: Half-precision could be used
 				float4 tangentToWorldX : TANGENT; // Note: Half-precision could be used
+				
+				#if USE_BLEND_SHAPES
+					float3 deltaPosition : POSITION1;
+					float4 deltaNormal : NORMAL1;
+				#endif
 			};
 		};
 
@@ -38,6 +48,13 @@ Technique : base("NormalVertexInput") =
 			{
 				float3 normal = input.normal * 2.0f - 1.0f;
 				float4 tangent = input.tangent * 2.0f - 1.0f;
+				
+				#if USE_BLEND_SHAPES
+					float3 deltaNormal = input.deltaNormal.xyz * 2.0f - 1.0f;
+					normal = normalize(normal + deltaNormal * input.deltaNormal.w);
+					tangent = normalize(tangent - dot(tangent, normal) * normal);
+				#endif
+				
 				float3 bitangent = cross(normal, tangent.xyz) * tangent.w;
 				tangentSign = tangent.w * gWorldDeterminantSign;
 				
@@ -64,7 +81,13 @@ Technique : base("NormalVertexInput") =
 			
 			float4 getVertexWorldPosition(VertexInput input, VertexIntermediate intermediate)
 			{
-				return mul(gMatWorld, float4(input.position, 1));
+				#if USE_BLEND_SHAPES
+					float4 position = float4(input.position + input.deltaPosition, 1.0f);
+				#else
+					float4 position = float4(input.position, 1.0f);
+				#endif			
+			
+				return mul(gMatWorld, position);
 			}
 			
 			void populateVertexOutput(VertexInput input, VertexIntermediate intermediate, inout VStoFS result)
@@ -78,7 +101,12 @@ Technique : base("NormalVertexInput") =
 	};
 };
 
-Technique : base("NormalVertexInput") =
+Technique
+#if USE_BLEND_SHAPES
+	 : base("MorphVertexInput") =
+#else
+	 : base("NormalVertexInput") =
+#endif
 {
 	Language = "GLSL";
 	
@@ -98,6 +126,11 @@ Technique : base("NormalVertexInput") =
 			in vec4 bs_tangent;
 			in vec2 bs_texcoord0;
 		
+			#if USE_BLEND_SHAPES
+				in vec3 bs_position1;
+				in vec4 bs_normal1;
+			#endif
+		
 			struct VertexIntermediate
 			{
 				vec3 worldNormal;
@@ -124,6 +157,12 @@ Technique : base("NormalVertexInput") =
 				vec3 normal = bs_normal * 2.0f - 1.0f;
 				vec4 tangent = bs_tangent * 2.0f - 1.0f;
 			
+				#if USE_BLEND_SHAPES
+					vec3 deltaNormal = bs_normal1.xyz * 2.0f - 1.0f;
+					normal = normalize(normal + deltaNormal * bs_normal1.w);
+					tangent = normalize(tangent - dot(tangent, normal) * normal);
+				#endif
+			
 				float tangentSign;
 				mat3 tangentToLocal;
 				getTangentToLocal(normal, tangent, tangentSign, tangentToLocal);
@@ -135,7 +174,13 @@ Technique : base("NormalVertexInput") =
 			
 			void getVertexWorldPosition(out vec4 result)
 			{
-				result = gMatWorld * vec4(bs_position, 1);
+				#if USE_BLEND_SHAPES
+					vec4 position = vec4(bs_position + bs_position1, 1.0f);
+				#else
+					vec4 position = vec4(bs_position, 1.0f);
+				#endif
+			
+				result = gMatWorld * position;
 			}
 			
 			void populateVertexOutput(VertexIntermediate intermediate)

+ 15 - 4
Data/Raw/Engine/Includes/SkinnedVertexInput.bslinc

@@ -3,7 +3,12 @@ Parameters =
 	StructBuffer boneMatrices : auto("BoneMatrices");
 };
 
-Technique : base("SkinnedVertexInput") =
+Technique 
+#if USE_BLEND_SHAPES
+	: base("SkinnedMorphVertexInput") =
+#else
+	: base("SkinnedVertexInput") =
+#endif
 {
 	Language = "HLSL11";
 
@@ -75,7 +80,8 @@ Technique : base("SkinnedVertexInput") =
 				float3 tangent = input.tangent.xyz * 2.0f - 1.0f;
 				
 				#if USE_BLEND_SHAPES
-					normal = normalize(normal + input.deltaNormal * input.deltaNormal.w);
+					float3 deltaNormal = input.deltaNormal.xyz * 2.0f - 1.0f;
+					normal = normalize(normal + deltaNormal * input.deltaNormal.w);
 					tangent = normalize(tangent - dot(tangent, normal) * normal);
 				#endif
 				
@@ -130,7 +136,11 @@ Technique : base("SkinnedVertexInput") =
 	};
 };
 
-Technique : base("SkinnedVertexInput") =
+#if USE_BLEND_SHAPES
+	: base("SkinnedMorphVertexInput") =
+#else
+	: base("SkinnedVertexInput") =
+#endif
 {
 	Language = "GLSL";
 	
@@ -209,7 +219,8 @@ Technique : base("SkinnedVertexInput") =
 				vec3 tangent = bs_tangent.xyz * 2.0f - 1.0f;
 				
 				#if USE_BLEND_SHAPES
-					normal = normalize(normal + bs_normal1 * bs_normal1.w);
+					vec3 deltaNormal = bs_normal1.xyz * 2.0f - 1.0f;
+					normal = normalize(normal + deltaNormal * bs_normal1.w);
 					tangent = normalize(tangent - dot(tangent, normal) * normal);
 				#endif
 				

+ 61 - 0
Data/Raw/Engine/Includes/Surface.bslinc

@@ -0,0 +1,61 @@
+Technique 
+ : inherits("DeferredBasePass")
+ : inherits("Surface") =
+{
+	Language = "HLSL11";
+};
+
+Technique 
+ : inherits("DeferredBasePassSkinned")
+ : inherits("Surface") =
+{
+	Language = "HLSL11";
+	Tags = { "Skinned" };
+};
+
+Technique 
+ : inherits("DeferredBasePassMorph")
+ : inherits("Surface") =
+{
+	Language = "HLSL11";
+	Tags = { "Morph" };
+};
+
+Technique 
+ : inherits("DeferredBasePassSkinnedMorph")
+ : inherits("Surface") =
+{
+	Language = "HLSL11";
+	Tags = { "SkinnedMorph" };
+};
+
+Technique 
+ : inherits("DeferredBasePass")
+ : inherits("Surface") =
+{
+	Language = "GLSL";
+};
+
+Technique 
+ : inherits("DeferredBasePassSkinned")
+ : inherits("Surface") =
+{
+	Language = "GLSL";
+	Tags = { "Skinned" };
+};
+
+Technique 
+ : inherits("DeferredBasePassMorph")
+ : inherits("Surface") =
+{
+	Language = "GLSL";
+	Tags = { "Morph" };
+};
+
+Technique 
+ : inherits("DeferredBasePassSkinnedMorph")
+ : inherits("Surface") =
+{
+	Language = "GLSL";
+	Tags = { "SkinnedMorph" };
+};

+ 3 - 31
Data/Raw/Engine/Shaders/Default.bsl

@@ -1,6 +1,6 @@
 #include "$ENGINE$\DeferredBasePass.bslinc"
 
-Technique : base("DiffuseCommon") =
+Technique : base("Surface") =
 {
 	Language = "HLSL11";
 	
@@ -26,22 +26,7 @@ Technique : base("DiffuseCommon") =
 	};
 };
 
-Technique 
- : inherits("DeferredBasePass")
- : inherits("DiffuseCommon") =
-{
-	Language = "HLSL11";
-};
-
-Technique 
- : inherits("DeferredBasePassSkinned")
- : inherits("DiffuseCommon") =
-{
-	Language = "HLSL11";
-	Tags = { "Animated" };
-};
-
-Technique : base("DiffuseCommon") =
+Technique : base("Surface") =
 {
 	Language = "GLSL";
 	
@@ -66,17 +51,4 @@ Technique : base("DiffuseCommon") =
 	};
 };
 
-Technique 
- : inherits("DeferredBasePass")
- : inherits("DiffuseCommon") =
-{
-	Language = "GLSL";
-};
-
-Technique 
- : inherits("DeferredBasePassSkinned")
- : inherits("DiffuseCommon") =
-{
-	Language = "GLSL";
-	Tags = { "Animated" };
-};
+#include "$ENGINE$\Surface.bslinc"

+ 3 - 31
Data/Raw/Engine/Shaders/Diffuse.bsl

@@ -9,7 +9,7 @@ Parameters =
 	Texture2D	gNormalTex = "normal";
 };
 
-Technique : base("DiffuseCommon") =
+Technique : base("Surface") =
 {
 	Language = "HLSL11";
 	
@@ -44,22 +44,7 @@ Technique : base("DiffuseCommon") =
 	};
 };
 
-Technique 
- : inherits("DeferredBasePass")
- : inherits("DiffuseCommon") =
-{
-	Language = "HLSL11";
-};
-
-Technique 
- : inherits("DeferredBasePassSkinned")
- : inherits("DiffuseCommon") =
-{
-	Language = "HLSL11";
-	Tags = { "Animated" };
-};
-
-Technique : base("DiffuseCommon") =
+Technique : base("Surface") =
 {
 	Language = "GLSL";
 	
@@ -90,17 +75,4 @@ Technique : base("DiffuseCommon") =
 	};
 };
 
-Technique 
- : inherits("DeferredBasePass")
- : inherits("DiffuseCommon") =
-{
-	Language = "GLSL";
-};
-
-Technique 
- : inherits("DeferredBasePassSkinned")
- : inherits("DiffuseCommon") =
-{
-	Language = "GLSL";
-	Tags = { "Animated" };
-};
+#include "$ENGINE$\Surface.bslinc"

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

@@ -39,7 +39,9 @@ namespace BansheeEngine
 	static StringID RPS_ViewDir = "ViewDir";
 
 	/** Technique tags. */
-	static StringID RTag_Animated = "Animated";
+	static StringID RTag_Skinned = "Skinned";
+	static StringID RTag_Morph = "Morph";
+	static StringID RTag_SkinnedMorph = "SkinnedMorph";
 
 	/**	Set of options that can be used for controlling the renderer. */	
 	struct BS_CORE_EXPORT CoreRendererOptions

+ 3 - 0
Source/BansheeCore/Include/BsMesh.h

@@ -302,6 +302,9 @@ namespace BansheeEngine
 		/** Returns a skeleton that can be used for animating the mesh. */
 		SPtr<Skeleton> getSkeleton() const { return mSkeleton; }
 
+		/** Returns an object containing all shapes used for morph animation, if any are available. */
+		SPtr<MorphShapes> getMorphShapes() const { return mMorphShapes; }
+
 		/**
 		 * Updates a part of the current mesh with the provided data.
 		 *

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

@@ -276,7 +276,7 @@ namespace BansheeEngine
 				if (iterFind != renderData.infos.end())
 					animInfo.morphShapeInfo = iterFind->second.morphShapeInfo;
 				else
-					animInfo.morphShapeInfo.version = 0;
+					animInfo.morphShapeInfo.version = 1; // 0 is considered invalid version
 
 				if(anim->morphShapeWeightsDirty)
 				{
@@ -348,7 +348,7 @@ namespace BansheeEngine
 				hasAnimInfo = true;
 			}
 			else
-				animInfo.morphShapeInfo.version = 0;
+				animInfo.morphShapeInfo.version = 1;
 
 			if (hasAnimInfo)
 				newAnimInfos[anim->id] = animInfo;

+ 5 - 6
Source/BansheeEditor/Include/BsSelectionRenderer.h

@@ -80,13 +80,12 @@ namespace BansheeEngine
 
 		// Immutable
 		SPtr<MaterialCore> mMaterial;
-		SPtr<GpuParamsSetCore> mParams[2];
-		GpuParamMat4Core mMatWorldViewProj[2];
-		GpuParamColorCore mColor[2];
-		GpuParamBufferCore mBoneMatrices;
+		SPtr<GpuParamsSetCore> mParams[4];
+		GpuParamMat4Core mMatWorldViewProj[4];
+		GpuParamColorCore mColor[4];
+		GpuParamBufferCore mBoneMatrices[4];
 
-		UINT32 mDefaultTechniqueIdx;
-		UINT32 mAnimatedTechniqueIdx;
+		UINT32 mTechniqueIndices[4];
 
 		static const Color SELECTION_COLOR;
 	};

+ 40 - 43
Source/BansheeEditor/Source/BsSelectionRenderer.cpp

@@ -21,6 +21,8 @@
 #include "BsAnimationManager.h"
 #include "BsSkeleton.h"
 #include "BsGpuBuffer.h"
+#include "BsRenderer.h"
+#include "BsRenderableElement.h"
 
 using namespace std::placeholders;
 
@@ -93,11 +95,29 @@ namespace BansheeEngine
 	void SelectionRendererCore::initialize(const SPtr<MaterialCore>& mat)
 	{
 		THROW_IF_NOT_CORE_THREAD;
+		
+		constexpr int numTechniques = sizeof(mTechniqueIndices) / sizeof(mTechniqueIndices[0]);
+		static_assert(numTechniques == (int)RenderableAnimType::Count, "Number of techniques doesn't match the number of possible animation types.");
 
-		mDefaultTechniqueIdx = mat->getDefaultTechnique();
-		mAnimatedTechniqueIdx = mat->findTechnique(RTag_Animated);
-
-		assert(mDefaultTechniqueIdx < 2 && mAnimatedTechniqueIdx < 2);
+		for(UINT32 i = 0; i < numTechniques; i++)
+		{
+			RenderableAnimType animType = (RenderableAnimType)i;
+			switch (animType)
+			{
+				case RenderableAnimType::Skinned:
+					mTechniqueIndices[i] = mat->findTechnique(RTag_Skinned);
+					break;
+				case RenderableAnimType::Morph:
+					mTechniqueIndices[i] = mat->findTechnique(RTag_Morph);
+					break;
+				case RenderableAnimType::SkinnedMorph:
+					mTechniqueIndices[i] = mat->findTechnique(RTag_SkinnedMorph);
+					break;
+				default:
+					mTechniqueIndices[i] = mat->getDefaultTechnique();
+					break;
+			}
+		}
 
 		mMaterial = mat;
 
@@ -107,13 +127,11 @@ namespace BansheeEngine
 
 			SPtr<GpuParamsCore> vertParams = mParams[i]->getGpuParams(GPT_VERTEX_PROGRAM);
 			vertParams->getParam("matWorldViewProj", mMatWorldViewProj[i]);
+			vertParams->getBufferParam("boneMatrices", mBoneMatrices[i]);
 
 			SPtr<GpuParamsCore> fragParams = mParams[i]->getGpuParams(GPT_FRAGMENT_PROGRAM);
 			fragParams->getParam("selColor", mColor[i]);
 		}
-
-		SPtr<GpuParamsCore> vertParams = mParams[mAnimatedTechniqueIdx]->getGpuParams(GPT_VERTEX_PROGRAM);
-		vertParams->getBufferParam("boneMatrices", mBoneMatrices);
 	}
 
 	void SelectionRendererCore::updateData(const SPtr<CameraCore>& camera, const Vector<SPtr<RenderableCore>>& objects)
@@ -142,6 +160,8 @@ namespace BansheeEngine
 		const RendererAnimationData& animData = AnimationManager::instance().getRendererData();
 		Matrix4 viewProjMat = mCamera->getProjectionMatrixRS() * mCamera->getViewMatrix();
 
+		SPtr<Renderer> renderer = gRenderer();
+
 		for (auto& renderable : mObjects)
 		{
 			SPtr<MeshCore> mesh = renderable->getMesh();
@@ -149,50 +169,27 @@ namespace BansheeEngine
 				continue;
 
 			Matrix4 worldViewProjMat = viewProjMat * renderable->getTransform();
-			UINT32 techniqueIdx = renderable->isAnimated() ? mAnimatedTechniqueIdx : mDefaultTechniqueIdx;
+			UINT32 techniqueIdx = mTechniqueIndices[(int)renderable->getAnimType()];
 
 			mMatWorldViewProj[techniqueIdx].set(worldViewProjMat);
 			mColor[techniqueIdx].set(SELECTION_COLOR);
-
-			if(renderable->isAnimated())
-			{
-				SPtr<Skeleton> skeleton = mesh->getSkeleton();
-				UINT32 numBones = skeleton != nullptr ? skeleton->getNumBones() : 0;
-
-				if (numBones > 0)
-				{
-					// Note: If I had access to them I could just use the buffers from the Renderer instead of creating a
-					// new one every frame
-					SPtr<GpuBufferCore> buffer = GpuBufferCore::create(numBones * 3, 0, GBT_STANDARD, BF_32X4F, GBU_DYNAMIC);
-
-					auto iterFind = animData.infos.find(renderable->getAnimationId());
-					if (iterFind != animData.infos.end())
-					{
-						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++)
-						{
-							const Matrix4& transform = animData.transforms[poseInfo.startIdx + j];
-							memcpy(dest, &transform, 12 * sizeof(float)); // Assuming row-major format
-
-							dest += 12 * sizeof(float);
-						}
-
-						buffer->unlock();
-					}
-
-					mBoneMatrices.set(buffer);
-				}
-			}
-
+			
 			gRendererUtility().setPass(mMaterial, 0, techniqueIdx);
 			gRendererUtility().setPassParams(mParams[techniqueIdx], 0);
 
 			UINT32 numSubmeshes = mesh->getProperties().getNumSubMeshes();
+			UINT32 renderableId = renderable->getRendererId();
+
+			for(UINT32 i = 0; i < numSubmeshes; i++)
+			{
+				const RenderableElement* renderableElem = renderer->getRenderableElem(renderableId, i);
+				if (renderableElem == nullptr)
+					continue;
 
-			for (UINT32 i = 0; i < numSubmeshes; i++)
-				gRendererUtility().draw(mesh, mesh->getProperties().getSubMesh(i));
+				mBoneMatrices[techniqueIdx].set(renderableElem->boneMatrixBuffer);
+
+				gRendererUtility().drawMorph(mesh, mesh->getProperties().getSubMesh(i), renderableElem->morphShapeBuffer);
+			}
 		}
 	}
 }

+ 16 - 2
Source/BansheeEngine/Include/BsRenderable.h

@@ -22,6 +22,16 @@ namespace BansheeEngine
 		Everything = 0x02
 	};
 
+	/** Type of animation that can be applied to a renderable object. */
+	enum class RenderableAnimType
+	{
+		None,
+		Skinned,
+		Morph,
+		SkinnedMorph,
+		Count // Keep at end
+	};
+
 	/**
 	 * Renderable represents any visible object in the scene. It has a mesh, bounds and a set of materials. Renderer will
 	 * render any Renderable objects visible by a camera.
@@ -151,6 +161,7 @@ namespace BansheeEngine
 		Matrix4 mTransform;
 		Matrix4 mTransformNoScale;
 		bool mIsActive;
+		RenderableAnimType mAnimType;
 	};
 
 	/** @} */
@@ -174,8 +185,8 @@ namespace BansheeEngine
 		/**	Retrieves an ID that can be used for uniquely identifying this handler by the renderer. */
 		UINT32 getRendererId() const { return mRendererId; }
 
-		/** Checks is the mesh geometry rendered by this renderable animated using skeleton or blend shape animation. */
-		bool isAnimated() const { return mAnimationId != (UINT64)-1; }
+		/** Returns the type of animation influencing this renderable, if any. */
+		RenderableAnimType getAnimType() const { return mAnimType; }
 
 		/** Returns the identifier of the animation, if this object is animated using skeleton or blend shape animation. */
 		UINT64 getAnimationId() const { return mAnimationId; }
@@ -232,6 +243,9 @@ namespace BansheeEngine
 		/** @copydoc TRenderable::onMeshChanged */
 		void onMeshChanged() override;
 
+		/** Updates animation properties depending on the current mesh. */
+		void refreshAnimation();
+
 		/** @copydoc TRenderable::_markCoreDirty */
 		void _markCoreDirty(RenderableDirtyFlag flag = RenderableDirtyFlag::Everything) override;
 

+ 6 - 0
Source/BansheeEngine/Include/BsRenderableElement.h

@@ -23,6 +23,12 @@ namespace BansheeEngine
 
 		/**	Material to render the mesh with. */
 		SPtr<MaterialCore> material;
+
+		/** GPU buffer containing element's bone matrices, if it requires any. */
+		SPtr<GpuBufferCore> boneMatrixBuffer;
+
+		/** Vertex buffer containing element's morph shape vertices, if it has any. */
+		SPtr<VertexBufferCore> morphShapeBuffer;
 	};
 
 	/** @} */

+ 6 - 0
Source/BansheeEngine/Include/BsRenderer.h

@@ -58,6 +58,12 @@ namespace BansheeEngine
 		 * @note	Core thread.
 		 */
 		virtual void notifyLightRemoved(LightCore* light) { }
+
+		/**
+		 * Attempts to retrieve a renderable element for a renderable with the specified renderable ID, and a sub-mesh
+		 * index of the mesh. Returns null if element cannot be found. Returned value is transient and should be stored.
+		 */
+		virtual const RenderableElement* getRenderableElem(UINT32 id, UINT32 subMeshIdx) = 0;
 	};
 
 	/**	Provides easy access to Renderer. */

+ 13 - 0
Source/BansheeEngine/Include/BsRendererUtility.h

@@ -79,6 +79,19 @@ namespace BansheeEngine
 		 */
 		void draw(const SPtr<MeshCoreBase>& mesh, const SubMesh& subMesh, UINT32 numInstances = 1);
 
+
+		/**
+		 * Draws the specified mesh with an additional vertex buffer containing morph shape vertices.
+		 *
+		 * @param[in]	mesh			Mesh to draw.
+		 * @param[in]	subMesh			Portion of the mesh to draw.
+		 * @param[in]	morphVertices	Buffer containing the morph shape vertices. Will be bound to stream 1. Expected
+		 *								to contain the same number of vertices as the source mesh.
+		 *
+		 * @note	Core thread.
+		 */
+		void drawMorph(const SPtr<MeshCoreBase>& mesh, const SubMesh& subMesh, const SPtr<VertexBufferCore>& morphVertices);
+
 		/**
 		 * Blits contents of the provided texture into the currently bound render target. If the provided texture contains
 		 * multiple samples, they will be resolved.

+ 39 - 18
Source/BansheeEngine/Source/BsRenderable.cpp

@@ -27,7 +27,7 @@ namespace BansheeEngine
 	template<bool Core>
 	TRenderable<Core>::TRenderable()
 		: mLayer(1), mUseOverrideBounds(false), mPosition(BsZero), mTransform(BsIdentity), mTransformNoScale(BsIdentity)
-		, mIsActive(true)
+		, mIsActive(true), mAnimType(RenderableAnimType::None)
 	{
 		mMaterials.resize(1);
 	}
@@ -212,6 +212,7 @@ namespace BansheeEngine
 		dataPtr = rttiReadElem(mTransformNoScale, dataPtr);
 		dataPtr = rttiReadElem(mPosition, dataPtr);
 		dataPtr = rttiReadElem(mIsActive, dataPtr);
+		dataPtr = rttiReadElem(mAnimType, dataPtr);
 		dataPtr = rttiReadElem(mAnimationId, dataPtr);
 		dataPtr = rttiReadElem(dirtyFlags, dataPtr);
 
@@ -259,12 +260,7 @@ namespace BansheeEngine
 	void Renderable::setAnimation(const SPtr<Animation>& animation)
 	{
 		mAnimation = animation;
-
-		if (mAnimation != nullptr && mMesh.isLoaded(false))
-		{
-			mAnimation->setSkeleton(mMesh->getSkeleton());
-			mAnimation->setMorphShapes(mMesh->getMorphShapes());
-		}
+		refreshAnimation();
 
 		_markCoreDirty();
 	}
@@ -315,18 +311,40 @@ namespace BansheeEngine
 
 	void Renderable::onMeshChanged()
 	{
-		if(mAnimation != nullptr)
+		refreshAnimation();
+	}
+
+	void Renderable::refreshAnimation()
+	{
+		if (mAnimation == nullptr)
 		{
-			if (mMesh.isLoaded(false))
-			{
-				mAnimation->setSkeleton(mMesh->getSkeleton());
-				mAnimation->setMorphShapes(mMesh->getMorphShapes());
-			}
+			mAnimType = RenderableAnimType::None;
+			return;
+		}
+
+		if (mMesh.isLoaded(false))
+		{
+			SPtr<Skeleton> skeleton = mMesh->getSkeleton();
+			SPtr<MorphShapes> morphShapes = mMesh->getMorphShapes();
+
+			if (skeleton != nullptr && morphShapes != nullptr)
+				mAnimType = RenderableAnimType::SkinnedMorph;
+			else if (skeleton != nullptr)
+				mAnimType = RenderableAnimType::Skinned;
+			else if (morphShapes != nullptr)
+				mAnimType = RenderableAnimType::Morph;
 			else
-			{
-				mAnimation->setSkeleton(nullptr);
-				mAnimation->setMorphShapes(nullptr);
-			}
+				mAnimType = RenderableAnimType::None;
+
+			mAnimation->setSkeleton(mMesh->getSkeleton());
+			mAnimation->setMorphShapes(mMesh->getMorphShapes());
+		}
+		else
+		{
+			mAnimType = RenderableAnimType::None;
+
+			mAnimation->setSkeleton(nullptr);
+			mAnimation->setMorphShapes(nullptr);
 		}
 	}
 
@@ -335,7 +353,8 @@ namespace BansheeEngine
 		UINT32 curHash = so->getTransformHash();
 		if (curHash != _getLastModifiedHash() || force)
 		{
-			if (mAnimation != nullptr)
+			// If skinned animation, don't include own transform since that will be handled by root bone animation
+			if (mAnimType == RenderableAnimType::Skinned || mAnimType == RenderableAnimType::SkinnedMorph)
 			{
 				// Note: Technically we're checking child's hash but using parent's transform. Ideally we check the parent's
 				// hash to reduce the number of required updates.
@@ -391,6 +410,7 @@ namespace BansheeEngine
 			rttiGetElemSize(mTransformNoScale) +
 			rttiGetElemSize(mPosition) +
 			rttiGetElemSize(mIsActive) +
+			rttiGetElemSize(mAnimType) + 
 			rttiGetElemSize(animationId) + 
 			rttiGetElemSize(getCoreDirtyFlags()) +
 			sizeof(SPtr<MeshCore>) + 
@@ -407,6 +427,7 @@ namespace BansheeEngine
 		dataPtr = rttiWriteElem(mPosition, dataPtr);
 		dataPtr = rttiWriteElem(mIsActive, dataPtr);
 		dataPtr = rttiWriteElem(animationId, dataPtr);
+		dataPtr = rttiWriteElem(mAnimType, dataPtr);
 		dataPtr = rttiWriteElem(getCoreDirtyFlags(), dataPtr);
 
 		SPtr<MeshCore>* mesh = new (dataPtr) SPtr<MeshCore>();

+ 46 - 6
Source/BansheeEngine/Source/BsRendererUtility.cpp

@@ -165,6 +165,9 @@ namespace BansheeEngine
 			rapi.setRasterizerState(pass->getRasterizerState());
 		else
 			rapi.setRasterizerState(RasterizerStateCore::getDefault());
+
+		SPtr<VertexDeclarationCore> shaderDecl = pass->getVertexProgram()->getInputDeclaration();
+		rapi.setVertexDeclaration(shaderDecl);
 	}
 
 	void RendererUtility::setComputePass(const SPtr<MaterialCore>& material, UINT32 passIdx)
@@ -258,9 +261,7 @@ namespace BansheeEngine
 		RenderAPICore& rs = RenderAPICore::instance();
 		SPtr<VertexData> vertexData = mesh->getVertexData();
 
-		rs.setVertexDeclaration(vertexData->vertexDeclaration);
 		auto& vertexBuffers = vertexData->getBuffers();
-
 		if (vertexBuffers.size() > 0)
 		{
 			SPtr<VertexBufferCore> buffers[MAX_BOUND_VERTEX_BUFFERS];
@@ -284,19 +285,58 @@ namespace BansheeEngine
 			rs.setVertexBuffers(startSlot, buffers, endSlot - startSlot + 1);
 		}
 
-		rs.setDrawOperation(subMesh.drawOp);
-
 		SPtr<IndexBufferCore> indexBuffer = mesh->getIndexBuffer();
+		rs.setIndexBuffer(indexBuffer);
 
-		UINT32 indexCount = subMesh.indexCount;
+		rs.setDrawOperation(subMesh.drawOp);
 
-		rs.setIndexBuffer(indexBuffer);
+		UINT32 indexCount = subMesh.indexCount;
 		rs.drawIndexed(subMesh.indexOffset + mesh->getIndexOffset(), indexCount, mesh->getVertexOffset(), 
 			vertexData->vertexCount, numInstances);
 
 		mesh->_notifyUsedOnGPU();
 	}
 
+	void RendererUtility::drawMorph(const SPtr<MeshCoreBase>& mesh, const SubMesh& subMesh, 
+		const SPtr<VertexBufferCore>& morphVertices)
+	{
+		// Bind buffers and draw
+		RenderAPICore& rapi = RenderAPICore::instance();
+
+		SPtr<VertexData> vertexData = mesh->getVertexData();
+
+		auto& meshBuffers = vertexData->getBuffers();
+		SPtr<VertexBufferCore> allBuffers[MAX_BOUND_VERTEX_BUFFERS];
+
+		UINT32 endSlot = 0;
+		UINT32 startSlot = MAX_BOUND_VERTEX_BUFFERS;
+		for (auto iter = meshBuffers.begin(); iter != meshBuffers.end(); ++iter)
+		{
+			if (iter->first >= MAX_BOUND_VERTEX_BUFFERS)
+				BS_EXCEPT(InvalidParametersException, "Buffer index out of range");
+
+			startSlot = std::min(iter->first, startSlot);
+			endSlot = std::max(iter->first, endSlot);
+		}
+
+		for (auto iter = meshBuffers.begin(); iter != meshBuffers.end(); ++iter)
+			allBuffers[iter->first - startSlot] = iter->second;
+
+		allBuffers[1] = morphVertices;
+		rapi.setVertexBuffers(startSlot, allBuffers, endSlot - startSlot + 1);
+
+		SPtr<IndexBufferCore> indexBuffer = mesh->getIndexBuffer();
+		rapi.setIndexBuffer(indexBuffer);
+
+		rapi.setDrawOperation(subMesh.drawOp);
+
+		UINT32 indexCount = subMesh.indexCount;
+		rapi.drawIndexed(subMesh.indexOffset + mesh->getIndexOffset(), indexCount, mesh->getVertexOffset(),
+			vertexData->vertexCount, 1);
+
+		mesh->_notifyUsedOnGPU();
+	}
+
 	void RendererUtility::blit(const SPtr<TextureCore>& texture, const Rect2I& area)
 	{
 		auto& texProps = texture->getProperties();

+ 0 - 6
Source/MBansheeEngine/Rendering/Renderable.cs

@@ -164,12 +164,6 @@ namespace BansheeEngine
         /// <param name="animation">Component that was added</param>
         internal void RegisterAnimation(Animation animation)
         {
-            bool isMeshAnimated = serializableData.mesh != null && 
-                (serializableData.mesh.Skeleton != null || serializableData.mesh.MorphShapes != null);
-
-            if (!isMeshAnimated)
-                return;
-
             this.animation = animation;
             if (_native != null)
             {

+ 8 - 0
Source/RenderBeast/Include/BsObjectRendering.h

@@ -12,6 +12,8 @@
 
 namespace BansheeEngine
 {
+	struct RendererAnimationData;
+
 	/** @addtogroup RenderBeast
 	 *  @{
 	 */
@@ -85,6 +87,12 @@ namespace BansheeEngine
 		 */
 		void setPerCameraParams(const CameraShaderData& cameraData);
 
+		/** 
+		 * Updates any bone or morph vertex buffers used by the element. Should be called once every frame before rendering,
+		 * or whenever animation changes. 
+		 */
+		void updateAnimationBuffers(const BeastRenderableElement& element, const RendererAnimationData& animData);
+
 		/**
 		 * Updates object specific parameter buffers with new values. To be called whenever object specific values change.
 		 */

+ 3 - 0
Source/RenderBeast/Include/BsRenderBeast.h

@@ -117,6 +117,9 @@ namespace BansheeEngine
 		/** @copydoc Renderer::notifyRenderableRemoved */
 		void notifyRenderableRemoved(RenderableCore* renderable) override;
 
+		/** @copydoc Renderer::getRenderableElem */
+		const RenderableElement* getRenderableElem(UINT32 id, UINT32 subMeshIdx) override;
+
 		/** 
 		 * Updates (or adds) renderer specific data for the specified camera. Should be called whenever camera properties
 		 * change. 

+ 6 - 2
Source/RenderBeast/Include/BsRendererObject.h

@@ -4,6 +4,7 @@
 
 #include "BsRenderBeastPrerequisites.h"
 #include "BsRenderableElement.h"
+#include "BsRenderable.h"
 
 namespace BansheeEngine
 {
@@ -36,6 +37,9 @@ namespace BansheeEngine
 		/** Identifier of the animation running on the renderable's mesh. -1 if no animation. */
 		UINT64 animationId;
 
+		/** Type of animation applied to this element, if any. */
+		RenderableAnimType animType;
+
 		/** Index of the technique in the material to render the element with. */
 		UINT32 techniqueIdx;
 
@@ -44,8 +48,8 @@ namespace BansheeEngine
 		 */
 		MaterialParamBufferCore boneMatricesParam;
 
-		/** GPU buffer containing element's bone matrices, if it requires any. */
-		SPtr<GpuBufferCore> boneMatrixBuffer;
+		/** Version of the morph shape vertices in the buffer. */
+		mutable UINT32 morphShapeVersion;
 	};
 
 	 /** Contains information about a Renderable, used by the Renderer. */

+ 73 - 0
Source/RenderBeast/Source/BsObjectRendering.cpp

@@ -9,6 +9,8 @@
 #include "BsSkeleton.h"
 #include "BsGpuBuffer.h"
 #include "BsGpuParamsSet.h"
+#include "BsMorphShapes.h"
+#include "BsAnimationManager.h"
 
 namespace BansheeEngine
 {
@@ -74,6 +76,77 @@ namespace BansheeEngine
 				element.boneMatrixBuffer = buffer;
 			}
 		}
+
+		if (element.animType == RenderableAnimType::Morph || element.animType == RenderableAnimType::SkinnedMorph)
+		{
+			// Note: Morph vertices should be shared between all sub-meshes, so maybe it's better to create this buffer
+			// on a per-Renderable basis, rather than per-element?
+
+			SPtr<MorphShapes> morphShapes = element.mesh->getMorphShapes();
+
+			UINT32 vertexSize = sizeof(Vector3) + sizeof(Vector4);
+			UINT32 numVertices = morphShapes->getNumVertices();
+
+			SPtr<VertexBufferCore> vertexBuffer = VertexBufferCore::create(vertexSize, numVertices, GBU_DYNAMIC);
+
+			UINT32 totalSize = vertexSize * numVertices;
+			UINT8* dest = (UINT8*)vertexBuffer->lock(0, totalSize, GBL_WRITE_ONLY_DISCARD);
+			memset(dest, 0, totalSize);
+			vertexBuffer->unlock();
+
+			element.morphShapeBuffer = vertexBuffer;
+		}
+	}
+
+	void ObjectRenderer::updateAnimationBuffers(const BeastRenderableElement& element, const RendererAnimationData& animData)
+	{
+		if (element.animationId == (UINT64)-1)
+			return;
+
+		const RendererAnimationData::AnimInfo* animInfo = nullptr;
+
+		auto iterFind = animData.infos.find(element.animationId);
+		if (iterFind != animData.infos.end())
+			animInfo = &iterFind->second;
+
+		if (animInfo == nullptr)
+			return;
+
+		if (element.animType == RenderableAnimType::Skinned || element.animType == RenderableAnimType::SkinnedMorph)
+		{
+			const RendererAnimationData::PoseInfo& poseInfo = animInfo->poseInfo;
+
+			// Note: If multiple elements are using the same animation (not possible atm), this buffer should be shared by
+			// all such elements
+			SPtr<GpuBufferCore> boneMatrices = element.boneMatrixBuffer;
+
+			UINT8* dest = (UINT8*)boneMatrices->lock(0, poseInfo.numBones * 3 * sizeof(Vector4), GBL_WRITE_ONLY_DISCARD);
+			for (UINT32 j = 0; j < poseInfo.numBones; j++)
+			{
+				const Matrix4& transform = animData.transforms[poseInfo.startIdx + j];
+				memcpy(dest, &transform, 12 * sizeof(float)); // Assuming row-major format
+
+				dest += 12 * sizeof(float);
+			}
+
+			boneMatrices->unlock();
+		}
+
+		if (element.animType == RenderableAnimType::Morph || element.animType == RenderableAnimType::SkinnedMorph)
+		{
+			if (element.morphShapeVersion < animInfo->morphShapeInfo.version)
+			{
+				SPtr<MeshData> meshData = animInfo->morphShapeInfo.meshData;
+				SPtr<VertexBufferCore> vertexBuffer = element.morphShapeBuffer;
+
+				UINT32 bufferSize = meshData->getSize();
+				UINT8* data = meshData->getData();
+
+				vertexBuffer->writeData(0, bufferSize, data, BufferWriteType::Discard);
+
+				element.morphShapeVersion = animInfo->morphShapeInfo.version;
+			}
+		}
 	}
 
 	void ObjectRenderer::setParamFrameParams(float time)

+ 57 - 50
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -29,6 +29,7 @@
 #include "BsSkeleton.h"
 #include "BsGpuBuffer.h"
 #include "BsGpuParamsSet.h"
+#include "BsMeshData.h"
 
 using namespace std::placeholders;
 
@@ -138,7 +139,9 @@ namespace BansheeEngine
 				renElement.mesh = mesh;
 				renElement.subMesh = meshProps.getSubMesh(i);
 				renElement.renderableId = renderableId;
+				renElement.animType = renderable->getAnimType();
 				renElement.animationId = renderable->getAnimationId();
+				renElement.morphShapeVersion = 0;
 
 				renElement.material = renderable->getMaterial(i);
 				if (renElement.material == nullptr)
@@ -147,45 +150,62 @@ namespace BansheeEngine
 				if (renElement.material != nullptr && renElement.material->getShader() == nullptr)
 					renElement.material = nullptr;
 
+				// If no material use the default material
+				if (renElement.material == nullptr)
+					renElement.material = mDefaultMaterial->getMaterial();
+
+				// Determine which technique to use
+				static StringID techniqueIDLookup[4] = { StringID::NONE, RTag_Skinned, RTag_Morph, RTag_SkinnedMorph };
+				static_assert((UINT32)RenderableAnimType::Count == 4, "RenderableAnimType is expected to have four sequential entries.");
+				
+				UINT32 techniqueIdx = -1;
+				RenderableAnimType animType = renderable->getAnimType();
+				if(animType != RenderableAnimType::None)
+					techniqueIdx = renElement.material->findTechnique(techniqueIDLookup[(int)animType]);
+
+				if (techniqueIdx == (UINT32)-1)
+					techniqueIdx = renElement.material->getDefaultTechnique();
+
+				renElement.techniqueIdx = techniqueIdx;
+
 				// Validate mesh <-> shader vertex bindings
 				if (renElement.material != nullptr)
 				{
-					UINT32 numPasses = renElement.material->getNumPasses();
+					UINT32 numPasses = renElement.material->getNumPasses(techniqueIdx);
 					for (UINT32 j = 0; j < numPasses; j++)
 					{
-						SPtr<PassCore> pass = renElement.material->getPass(j);
+						SPtr<PassCore> pass = renElement.material->getPass(j, techniqueIdx);
 
 						SPtr<VertexDeclarationCore> shaderDecl = pass->getVertexProgram()->getInputDeclaration();
 						if (!vertexDecl->isCompatible(shaderDecl))
 						{
 							Vector<VertexElement> missingElements = vertexDecl->getMissingElements(shaderDecl);
 
-							StringStream wrnStream;
-							wrnStream << "Provided mesh is missing required vertex attributes to render with the provided shader. Missing elements: " << std::endl;
-
-							for (auto& entry : missingElements)
-								wrnStream << "\t" << toString(entry.getSemantic()) << entry.getSemanticIdx() << std::endl;
-
-							LOGWRN(wrnStream.str());
-							break;
+							// If using morph shapes ignore POSITION1 and NORMAL1 missing since we assign them from within the renderer
+							if(animType == RenderableAnimType::Morph || animType == RenderableAnimType::SkinnedMorph)
+							{
+								std::remove_if(missingElements.begin(), missingElements.end(), [](const VertexElement& x)
+								{
+									return (x.getSemantic() == VES_POSITION && x.getSemanticIdx() == 1) ||
+										(x.getSemantic() == VES_NORMAL && x.getSemanticIdx() == 1);
+								});
+							}
+
+							if (!missingElements.empty())
+							{
+								StringStream wrnStream;
+								wrnStream << "Provided mesh is missing required vertex attributes to render with the provided shader. Missing elements: " << std::endl;
+
+								for (auto& entry : missingElements)
+									wrnStream << "\t" << toString(entry.getSemantic()) << entry.getSemanticIdx() << std::endl;
+
+								LOGWRN(wrnStream.str());
+								break;
+							}
 						}
 					}
 				}
 
-				// If no material use the default material
-				if (renElement.material == nullptr)
-					renElement.material = mDefaultMaterial->getMaterial();
-
-				// Determine which technique to use
-				UINT32 techniqueIdx = -1;
-				if(renderable->isAnimated())
-					techniqueIdx = renElement.material->findTechnique(RTag_Animated);
-
-				if (techniqueIdx == (UINT32)-1)
-					techniqueIdx = renElement.material->getDefaultTechnique();
-
-				renElement.techniqueIdx = techniqueIdx;
-
 				// Generate or assigned renderer specific data for the material
 				Any materialInfo = renElement.material->getRendererData();
 				if(materialInfo.empty())
@@ -395,6 +415,17 @@ namespace BansheeEngine
 		updateCameraData(camera, true);
 	}
 
+	const RenderableElement* RenderBeast::getRenderableElem(UINT32 id, UINT32 subMeshIdx)
+	{
+		if (id >= (UINT32)mRenderables.size())
+			return nullptr;
+
+		if (subMeshIdx >= (UINT32)mRenderables[id].elements.size())
+			return nullptr;
+
+		return &mRenderables[id].elements[subMeshIdx];
+	}
+
 	SPtr<PostProcessSettings> RenderBeast::createPostProcessSettings() const
 	{
 		return bs_shared_ptr_new<StandardPostProcessSettings>();
@@ -551,31 +582,7 @@ namespace BansheeEngine
 				continue;
 
 			for (auto& element : mRenderables[i].elements)
-			{
-				if (element.animationId == (UINT64)-1)
-					continue;
-
-				// Note: If multiple elements are using the same animation (not possible atm), this buffer should be shared by
-				// all such elements
-				SPtr<GpuBufferCore> boneMatrices = element.boneMatrixBuffer;
-
-				auto iterFind = animData.infos.find(element.animationId);
-				if (iterFind != animData.infos.end())
-				{
-					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++)
-					{
-						const Matrix4& transform = animData.transforms[poseInfo.startIdx + j];
-						memcpy(dest, &transform, 12 * sizeof(float)); // Assuming row-major format
-
-						dest += 12 * sizeof(float);
-					}
-
-					boneMatrices->unlock();
-				}
-			}
+				mObjectRenderer->updateAnimationBuffers(element, animData);
 
 			// TODO - Also move per-object buffer updates here (will require worldViewProj matrix to be moved to a separate buffer (or a push constant))
 			// TODO - Before uploading bone matrices and per-object data, check if it has actually been changed since last frame (most objects will be static)
@@ -838,7 +845,7 @@ namespace BansheeEngine
 		else
 			setPassParams(element.params, nullptr, passIdx);
 
-		gRendererUtility().draw(element.mesh, element.subMesh);
+		gRendererUtility().drawMorph(element.mesh, element.subMesh, element.morphShapeBuffer);
 	}
 
 	void RenderBeast::refreshSamplerOverrides(bool force)