Browse Source

Add motion blur to skins

Panagiotis Christopoulos Charitos 5 years ago
parent
commit
c69d0f4f82

+ 1 - 0
README.md

@@ -89,6 +89,7 @@ This code repository contains **3 sample projects** that are built by default (`
 - `sponza`: The Crytek's Sponza scene
 - `sponza`: The Crytek's Sponza scene
 - `simple_scene`: A simple scene
 - `simple_scene`: A simple scene
 - `physics_playground`: A scene with programmer's art and some physics interactions
 - `physics_playground`: A scene with programmer's art and some physics interactions
+- `skeletal_animation`: A simple scene with an animated skin
 
 
 You can try running them and interacting with them. To run sponza, for example, execute the binary from any working
 You can try running them and interacting with them. To run sponza, for example, execute the binary from any working
 directory.
 directory.

+ 6 - 0
samples/common/Framework.cpp

@@ -63,6 +63,12 @@ Error SampleApp::userMainLoop(Bool& quit)
 		setDisplayDeveloperConsole(!getDisplayDeveloperConsole());
 		setDisplayDeveloperConsole(!getDisplayDeveloperConsole());
 	}
 	}
 
 
+	if(in.getKey(KeyCode::Y) == 1)
+	{
+		renderer.setCurrentDebugRenderTarget(
+			(renderer.getCurrentDebugRenderTarget() == "GBuffer_velocity") ? "" : "GBuffer_velocity");
+	}
+
 	if(in.getKey(KeyCode::U) == 1)
 	if(in.getKey(KeyCode::U) == 1)
 	{
 	{
 		renderer.setCurrentDebugRenderTarget((renderer.getCurrentDebugRenderTarget() == "SSGI") ? "" : "SSGI");
 		renderer.setCurrentDebugRenderTarget((renderer.getCurrentDebugRenderTarget() == "SSGI") ? "" : "SSGI");

+ 2 - 0
samples/skeletal_animation/Main.cpp

@@ -33,6 +33,8 @@ public:
 
 
 		getMainRenderer().getOffscreenRenderer().getVolumetricFog().setFogParticleColor(Vec3(1.0f, 0.9f, 0.9f));
 		getMainRenderer().getOffscreenRenderer().getVolumetricFog().setFogParticleColor(Vec3(1.0f, 0.9f, 0.9f));
 		getMainRenderer().getOffscreenRenderer().getVolumetricFog().setParticleDensity(2.0f);
 		getMainRenderer().getOffscreenRenderer().getVolumetricFog().setParticleDensity(2.0f);
+
+		getMainRenderer().getOffscreenRenderer().getBloom().setThreshold(5.0f);
 		return Error::NONE;
 		return Error::NONE;
 	}
 	}
 
 

+ 20 - 0
samples/skeletal_animation/assets/scene.lua

@@ -11,6 +11,26 @@ trf:setRotation(rot)
 trf:setScale(1.000000)
 trf:setScale(1.000000)
 node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
 node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
 
 
+node = scene:newGlobalIlluminationProbeNode("Cube")
+comp = node:getSceneNodeBase():getGlobalIlluminationProbeComponent()
+comp:setBoundingBox(Vec4.new(-9.643279, -9.643279, -9.643279, 0), Vec4.new(9.643279, 9.643279, 9.643279, 0))
+trf = Transform.new()
+trf:setOrigin(Vec4.new(0.000000, 11.057286, 0.000000, 0))
+rot = Mat3x4.new()
+rot:setAll(1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000)
+trf:setRotation(rot)
+trf:setScale(1.000000)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
+
+node = scene:newReflectionProbeNode("Cube.001", Vec4.new(-9.271889, -9.271889, -9.271889, 0), Vec4.new(9.271889, 9.271888, 9.271889, 0))
+trf = Transform.new()
+trf:setOrigin(Vec4.new(0.000000, 11.057286, 0.000000, 0))
+rot = Mat3x4.new()
+rot:setAll(1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000)
+trf:setRotation(rot)
+trf:setScale(1.000000)
+node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
+
 node = scene:newModelNode("room", "assets/room_room.ankimdl")
 node = scene:newModelNode("room", "assets/room_room.ankimdl")
 trf = Transform.new()
 trf = Transform.new()
 trf:setOrigin(Vec4.new(0.000000, 11.142166, 0.000000, 0))
 trf:setOrigin(Vec4.new(0.000000, 11.142166, 0.000000, 0))

+ 2 - 2
shaders/GBufferCommon.glsl

@@ -47,7 +47,7 @@ layout(location = 5) out Vec3 out_eyeTangentSpace;
 layout(location = 6) out Vec3 out_normalTangentSpace;
 layout(location = 6) out Vec3 out_normalTangentSpace;
 #		endif
 #		endif
 
 
-#		if ANKI_VELOCITY
+#		if ANKI_VELOCITY || ANKI_BONES
 layout(location = 7) out Vec2 out_velocity;
 layout(location = 7) out Vec2 out_velocity;
 #		endif
 #		endif
 #	endif // ANKI_PASS == PASS_GB
 #	endif // ANKI_PASS == PASS_GB
@@ -68,7 +68,7 @@ layout(location = 5) in Vec3 in_eyeTangentSpace;
 layout(location = 6) in Vec3 in_normalTangentSpace;
 layout(location = 6) in Vec3 in_normalTangentSpace;
 #	endif
 #	endif
 
 
-#	if ANKI_VELOCITY
+#	if ANKI_VELOCITY || ANKI_BONES
 layout(location = 7) in Vec2 in_velocity;
 layout(location = 7) in Vec2 in_velocity;
 #	endif
 #	endif
 #endif
 #endif

+ 27 - 5
shaders/GBufferGeneric.ankiprog

@@ -124,6 +124,11 @@ layout(set = 0, binding = 9, row_major, std140) readonly buffer b_ankiBoneTransf
 {
 {
 	Mat4 u_ankiBoneTransforms[];
 	Mat4 u_ankiBoneTransforms[];
 };
 };
+
+layout(set = 0, binding = 10, row_major, std140) readonly buffer b_ankiPrevFrameBoneTransforms
+{
+	Mat4 u_ankiPrevFrameBoneTransforms[];
+};
 #endif
 #endif
 
 
 #if ANKI_INSTANCE_COUNT == 1
 #if ANKI_INSTANCE_COUNT == 1
@@ -137,6 +142,7 @@ layout(set = 0, binding = 9, row_major, std140) readonly buffer b_ankiBoneTransf
 // Globals (always in local space)
 // Globals (always in local space)
 Vec3 g_position = in_position;
 Vec3 g_position = in_position;
 #if ANKI_PASS == PASS_GB
 #if ANKI_PASS == PASS_GB
+Vec3 g_prevPosition = in_position;
 Vec2 g_uv = in_uv;
 Vec2 g_uv = in_uv;
 Vec3 g_normal = in_normal;
 Vec3 g_normal = in_normal;
 Vec4 g_tangent = in_tangent;
 Vec4 g_tangent = in_tangent;
@@ -147,16 +153,19 @@ Vec4 g_tangent = in_tangent;
 void skinning()
 void skinning()
 {
 {
 	Mat4 skinMat = u_ankiBoneTransforms[in_boneIndices[0]] * in_boneWeights[0];
 	Mat4 skinMat = u_ankiBoneTransforms[in_boneIndices[0]] * in_boneWeights[0];
+	Mat4 prevSkinMat = u_ankiPrevFrameBoneTransforms[in_boneIndices[0]] * in_boneWeights[0];
 	ANKI_UNROLL for(U32 i = 1; i < 4; ++i)
 	ANKI_UNROLL for(U32 i = 1; i < 4; ++i)
 	{
 	{
 		skinMat += u_ankiBoneTransforms[in_boneIndices[i]] * in_boneWeights[i];
 		skinMat += u_ankiBoneTransforms[in_boneIndices[i]] * in_boneWeights[i];
+		prevSkinMat += u_ankiPrevFrameBoneTransforms[in_boneIndices[i]] * in_boneWeights[i];
 	}
 	}
 
 
-	g_position = (skinMat * Vec4(g_position, 1.0)).xyz;
 #	if ANKI_PASS == PASS_GB
 #	if ANKI_PASS == PASS_GB
+	g_prevPosition = (prevSkinMat * Vec4(g_position, 1.0)).xyz;
 	g_tangent.xyz = (skinMat * Vec4(g_tangent.xyz, 0.0)).xyz;
 	g_tangent.xyz = (skinMat * Vec4(g_tangent.xyz, 0.0)).xyz;
 	g_normal = (skinMat * Vec4(g_normal, 0.0)).xyz;
 	g_normal = (skinMat * Vec4(g_normal, 0.0)).xyz;
 #	endif
 #	endif
+	g_position = (skinMat * Vec4(g_position, 1.0)).xyz;
 }
 }
 #endif
 #endif
 
 
@@ -192,10 +201,23 @@ void parallax()
 }
 }
 #endif
 #endif
 
 
-#if ANKI_VELOCITY && ANKI_PASS == PASS_GB
+#if(ANKI_VELOCITY || ANKI_BONES) && ANKI_PASS == PASS_GB
 void velocity()
 void velocity()
 {
 {
-	const Vec4 v4 = u_ankiPerInstance[INSTANCE_ID].m_ankiPreviousMvp * Vec4(g_position, 1.0);
+#	if ANKI_BONES
+	const Vec3 prevLocalPos = g_prevPosition;
+#	else
+	const Vec3 prevLocalPos = g_position;
+#	endif
+
+#	if ANKI_VELOCITY
+	const Mat4 mvp = u_ankiPerInstance[INSTANCE_ID].m_ankiPreviousMvp;
+#	else
+	const Mat4 mvp = u_ankiPerInstance[INSTANCE_ID].m_ankiMvp;
+#	endif
+
+	const Vec4 v4 = mvp * Vec4(prevLocalPos, 1.0);
+
 	const Vec2 prevNdc = v4.xy / v4.w;
 	const Vec2 prevNdc = v4.xy / v4.w;
 
 
 	const Vec2 crntNdc = gl_Position.xy / gl_Position.w;
 	const Vec2 crntNdc = gl_Position.xy / gl_Position.w;
@@ -218,7 +240,7 @@ void main()
 	parallax();
 	parallax();
 #	endif
 #	endif
 
 
-#	if ANKI_VELOCITY
+#	if ANKI_VELOCITY || ANKI_BONES
 	velocity();
 	velocity();
 #	endif
 #	endif
 #else
 #else
@@ -362,7 +384,7 @@ void main()
 	const Vec3 emission = u_ankiPerDraw.m_emission;
 	const Vec3 emission = u_ankiPerDraw.m_emission;
 #	endif
 #	endif
 
 
-#	if ANKI_VELOCITY
+#	if ANKI_VELOCITY || ANKI_BONES
 	const Vec2 velocity = in_velocity;
 	const Vec2 velocity = in_velocity;
 #	else
 #	else
 	const Vec2 velocity = Vec2(-1.0);
 	const Vec2 velocity = Vec2(-1.0);

+ 22 - 2
src/anki/renderer/Bloom.h

@@ -42,6 +42,26 @@ public:
 		return m_runCtx.m_upscaleRt;
 		return m_runCtx.m_upscaleRt;
 	}
 	}
 
 
+	F32 getThreshold() const
+	{
+		return m_exposure.m_threshold;
+	}
+
+	void setThreshold(F32 t)
+	{
+		m_exposure.m_threshold = t;
+	}
+
+	F32 getScale() const
+	{
+		return m_exposure.m_scale;
+	}
+
+	void setScale(F32 t)
+	{
+		m_exposure.m_scale = t;
+	}
+
 private:
 private:
 	static constexpr Format RT_PIXEL_FORMAT = Format::A2B10G10R10_UNORM_PACK32;
 	static constexpr Format RT_PIXEL_FORMAT = Format::A2B10G10R10_UNORM_PACK32;
 
 
@@ -53,8 +73,8 @@ private:
 		ShaderProgramResourcePtr m_prog;
 		ShaderProgramResourcePtr m_prog;
 		ShaderProgramPtr m_grProg;
 		ShaderProgramPtr m_grProg;
 
 
-		F32 m_threshold = 10.0; ///< How bright it is
-		F32 m_scale = 1.0;
+		F32 m_threshold = 10.0f; ///< How bright it is
+		F32 m_scale = 1.0f;
 		U32 m_width = 0;
 		U32 m_width = 0;
 		U32 m_height = 0;
 		U32 m_height = 0;
 
 

+ 5 - 0
src/anki/renderer/GBuffer.h

@@ -23,6 +23,7 @@ public:
 	{
 	{
 		registerDebugRenderTarget("GBuffer_normals");
 		registerDebugRenderTarget("GBuffer_normals");
 		registerDebugRenderTarget("GBuffer_albedo");
 		registerDebugRenderTarget("GBuffer_albedo");
+		registerDebugRenderTarget("GBuffer_velocity");
 	}
 	}
 
 
 	~GBuffer();
 	~GBuffer();
@@ -52,6 +53,10 @@ public:
 		{
 		{
 			handle = m_colorRts[2];
 			handle = m_colorRts[2];
 		}
 		}
+		else if(rtName == "GBuffer_velocity")
+		{
+			handle = m_colorRts[3];
+		}
 		else
 		else
 		{
 		{
 			ANKI_ASSERT(!"See file");
 			ANKI_ASSERT(!"See file");

+ 14 - 4
src/anki/resource/MaterialResource.cpp

@@ -386,18 +386,28 @@ Error MaterialResource::findBuiltinMutators()
 			{
 			{
 				if(block.m_set != m_descriptorSetIdx)
 				if(block.m_set != m_descriptorSetIdx)
 				{
 				{
-					ANKI_RESOURCE_LOGE("The set of u_ankiBoneTransforms should be %u", m_descriptorSetIdx);
+					ANKI_RESOURCE_LOGE("The set of b_ankiBoneTransforms should be %u", m_descriptorSetIdx);
 					return Error::USER_DATA;
 					return Error::USER_DATA;
 				}
 				}
 
 
 				m_boneTrfsBinding = block.m_binding;
 				m_boneTrfsBinding = block.m_binding;
-				break;
+			}
+			else if(block.m_name.getBegin() == CString("b_ankiPrevFrameBoneTransforms"))
+			{
+				if(block.m_set != m_descriptorSetIdx)
+				{
+					ANKI_RESOURCE_LOGE("The set of b_ankiPrevFrameBoneTransforms should be %u", m_descriptorSetIdx);
+					return Error::USER_DATA;
+				}
+
+				m_prevFrameBoneTrfsBinding = block.m_binding;
 			}
 			}
 		}
 		}
 
 
-		if(m_boneTrfsBinding == MAX_U32)
+		if(m_boneTrfsBinding == MAX_U32 || m_prevFrameBoneTrfsBinding == MAX_U32)
 		{
 		{
-			ANKI_RESOURCE_LOGE("The program is using the %s mutator but b_ankiBoneTransforms was not found",
+			ANKI_RESOURCE_LOGE("The program is using the %s mutator but b_ankiBoneTransforms or "
+							   "b_ankiPrevFrameBoneTransforms was not found",
 				BUILTIN_MUTATOR_NAMES[BuiltinMutatorId::BONES].cstr());
 				BUILTIN_MUTATOR_NAMES[BuiltinMutatorId::BONES].cstr());
 			return Error::NONE;
 			return Error::NONE;
 		}
 		}

+ 6 - 0
src/anki/resource/MaterialResource.h

@@ -340,6 +340,11 @@ public:
 		return m_boneTrfsBinding;
 		return m_boneTrfsBinding;
 	}
 	}
 
 
+	U32 getPrevFrameBoneTransformsBinding() const
+	{
+		return m_prevFrameBoneTrfsBinding;
+	}
+
 	U32 getUniformsBinding() const
 	U32 getUniformsBinding() const
 	{
 	{
 		ANKI_ASSERT(m_uboBinding != MAX_U32);
 		ANKI_ASSERT(m_uboBinding != MAX_U32);
@@ -367,6 +372,7 @@ private:
 	U32 m_uboIdx = MAX_U32; ///< The b_ankiMaterial UBO inside the binary.
 	U32 m_uboIdx = MAX_U32; ///< The b_ankiMaterial UBO inside the binary.
 	U32 m_uboBinding = MAX_U32;
 	U32 m_uboBinding = MAX_U32;
 	U32 m_boneTrfsBinding = MAX_U32;
 	U32 m_boneTrfsBinding = MAX_U32;
+	U32 m_prevFrameBoneTrfsBinding = MAX_U32;
 
 
 	/// Matrix of variants.
 	/// Matrix of variants.
 	mutable Array5d<MaterialVariant, U(Pass::COUNT), MAX_LOD_COUNT, MAX_INSTANCE_GROUPS, 2, 2> m_variantMatrix;
 	mutable Array5d<MaterialVariant, U(Pass::COUNT), MAX_LOD_COUNT, MAX_INSTANCE_GROUPS, 2, 2> m_variantMatrix;

+ 1 - 0
src/anki/resource/ModelResource.cpp

@@ -51,6 +51,7 @@ void ModelPatch::getRenderingDataSub(
 		inf.m_program = variant.getShaderProgram();
 		inf.m_program = variant.getShaderProgram();
 
 
 		inf.m_boneTransformsBinding = m_mtl->getBoneTransformsBinding();
 		inf.m_boneTransformsBinding = m_mtl->getBoneTransformsBinding();
+		inf.m_prevFrameBoneTransformsBinding = m_mtl->getPrevFrameBoneTransformsBinding();
 	}
 	}
 
 
 	// Vertex attributes & bindings
 	// Vertex attributes & bindings

+ 1 - 0
src/anki/resource/ModelResource.h

@@ -80,6 +80,7 @@ public:
 	IndexType m_indexType;
 	IndexType m_indexType;
 
 
 	U32 m_boneTransformsBinding;
 	U32 m_boneTransformsBinding;
+	U32 m_prevFrameBoneTransformsBinding;
 };
 };
 
 
 /// Model patch interface class. Its very important class and it binds the material with the mesh
 /// Model patch interface class. Its very important class and it binds the material with the mesh

+ 15 - 3
src/anki/scene/ModelNode.cpp

@@ -187,10 +187,15 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 		if(m_model->getSkeleton())
 		if(m_model->getSkeleton())
 		{
 		{
 			const SkinComponent& skinc = getComponentAt<SkinComponent>(0);
 			const SkinComponent& skinc = getComponentAt<SkinComponent>(0);
-			StagingGpuMemoryToken token;
+			const U32 boneCount = skinc.getBoneTransforms().getSize();
+			StagingGpuMemoryToken token, tokenPrev;
 			void* trfs = ctx.m_stagingGpuAllocator->allocateFrame(
 			void* trfs = ctx.m_stagingGpuAllocator->allocateFrame(
-				skinc.getBoneTransforms().getSize() * sizeof(Mat4), StagingGpuMemoryType::STORAGE, token);
-			memcpy(trfs, &skinc.getBoneTransforms()[0], skinc.getBoneTransforms().getSize() * sizeof(Mat4));
+				boneCount * sizeof(Mat4), StagingGpuMemoryType::STORAGE, token);
+			memcpy(trfs, &skinc.getBoneTransforms()[0], boneCount * sizeof(Mat4));
+
+			trfs = ctx.m_stagingGpuAllocator->allocateFrame(
+				boneCount * sizeof(Mat4), StagingGpuMemoryType::STORAGE, tokenPrev);
+			memcpy(trfs, &skinc.getPreviousFrameBoneTransforms()[0], boneCount * sizeof(Mat4));
 
 
 			ANKI_ASSERT(modelInf.m_boneTransformsBinding < MAX_U32);
 			ANKI_ASSERT(modelInf.m_boneTransformsBinding < MAX_U32);
 			cmdb->bindStorageBuffer(patch.getMaterial()->getDescriptorSetIndex(),
 			cmdb->bindStorageBuffer(patch.getMaterial()->getDescriptorSetIndex(),
@@ -198,6 +203,13 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 				token.m_buffer,
 				token.m_buffer,
 				token.m_offset,
 				token.m_offset,
 				token.m_range);
 				token.m_range);
+
+			ANKI_ASSERT(modelInf.m_prevFrameBoneTransformsBinding < MAX_U32);
+			cmdb->bindStorageBuffer(patch.getMaterial()->getDescriptorSetIndex(),
+				modelInf.m_prevFrameBoneTransformsBinding,
+				tokenPrev.m_buffer,
+				tokenPrev.m_offset,
+				tokenPrev.m_range);
 		}
 		}
 
 
 		// Program
 		// Program

+ 14 - 10
src/anki/scene/components/SkinComponent.cpp

@@ -19,19 +19,16 @@ SkinComponent::SkinComponent(SceneNode* node, SkeletonResourcePtr skeleton)
 {
 {
 	ANKI_ASSERT(node);
 	ANKI_ASSERT(node);
 
 
-	m_boneTrfs.create(m_node->getAllocator(), m_skeleton->getBones().getSize());
-	m_animationTrfs.create(m_node->getAllocator(), m_skeleton->getBones().getSize());
-
-	for(U32 i = 0; i < m_boneTrfs.getSize(); ++i)
-	{
-		m_boneTrfs[i].setIdentity();
-		m_animationTrfs[i] = {Vec3(0.0f), Quat::getIdentity(), 1.0f};
-	}
+	m_boneTrfs[0].create(m_node->getAllocator(), m_skeleton->getBones().getSize(), Mat4::getIdentity());
+	m_boneTrfs[1].create(m_node->getAllocator(), m_skeleton->getBones().getSize(), Mat4::getIdentity());
+	m_animationTrfs.create(
+		m_node->getAllocator(), m_skeleton->getBones().getSize(), {Vec3(0.0f), Quat::getIdentity(), 1.0f});
 }
 }
 
 
 SkinComponent::~SkinComponent()
 SkinComponent::~SkinComponent()
 {
 {
-	m_boneTrfs.destroy(m_node->getAllocator());
+	m_boneTrfs[0].destroy(m_node->getAllocator());
+	m_boneTrfs[1].destroy(m_node->getAllocator());
 	m_animationTrfs.destroy(m_node->getAllocator());
 	m_animationTrfs.destroy(m_node->getAllocator());
 }
 }
 
 
@@ -158,6 +155,9 @@ Error SkinComponent::update(SceneNode& node, Second prevTime, Second crntTime, B
 
 
 	if(updated)
 	if(updated)
 	{
 	{
+		m_prevBoneTrfs = m_crntBoneTrfs;
+		m_crntBoneTrfs = m_crntBoneTrfs ^ 1;
+
 		// Walk the bone hierarchy to add additional transforms
 		// Walk the bone hierarchy to add additional transforms
 		visitBones(m_skeleton->getRootBone(), Mat4::getIdentity(), bonesAnimated, minExtend, maxExtend);
 		visitBones(m_skeleton->getRootBone(), Mat4::getIdentity(), bonesAnimated, minExtend, maxExtend);
 
 
@@ -165,6 +165,10 @@ Error SkinComponent::update(SceneNode& node, Second prevTime, Second crntTime, B
 		m_boneBoundingVolume.setMin(minExtend - E);
 		m_boneBoundingVolume.setMin(minExtend - E);
 		m_boneBoundingVolume.setMax(maxExtend + E);
 		m_boneBoundingVolume.setMax(maxExtend + E);
 	}
 	}
+	else
+	{
+		m_prevBoneTrfs = m_crntBoneTrfs;
+	}
 
 
 	m_absoluteTime += dt;
 	m_absoluteTime += dt;
 
 
@@ -186,7 +190,7 @@ void SkinComponent::visitBones(
 		outMat = parentTrf * bone.getTransform();
 		outMat = parentTrf * bone.getTransform();
 	}
 	}
 
 
-	m_boneTrfs[bone.getIndex()] = outMat * bone.getVertexTransform();
+	m_boneTrfs[m_crntBoneTrfs][bone.getIndex()] = outMat * bone.getVertexTransform();
 
 
 	// Update volume
 	// Update volume
 	const Vec4 bonePos = outMat * Vec4(0.0f, 0.0f, 0.0f, 1.0f);
 	const Vec4 bonePos = outMat * Vec4(0.0f, 0.0f, 0.0f, 1.0f);

+ 11 - 3
src/anki/scene/components/SkinComponent.h

@@ -9,6 +9,7 @@
 #include <anki/resource/Forward.h>
 #include <anki/resource/Forward.h>
 #include <anki/collision/Aabb.h>
 #include <anki/collision/Aabb.h>
 #include <anki/util/Forward.h>
 #include <anki/util/Forward.h>
+#include <anki/util/WeakArray.h>
 #include <anki/Math.h>
 #include <anki/Math.h>
 
 
 namespace anki
 namespace anki
@@ -49,9 +50,14 @@ public:
 
 
 	void playAnimation(U32 track, AnimationResourcePtr anim, const AnimationPlayInfo& info);
 	void playAnimation(U32 track, AnimationResourcePtr anim, const AnimationPlayInfo& info);
 
 
-	const DynamicArray<Mat4>& getBoneTransforms() const
+	ConstWeakArray<Mat4> getBoneTransforms() const
 	{
 	{
-		return m_boneTrfs;
+		return m_boneTrfs[m_crntBoneTrfs];
+	}
+
+	ConstWeakArray<Mat4> getPreviousFrameBoneTransforms() const
+	{
+		return m_boneTrfs[m_prevBoneTrfs];
 	}
 	}
 
 
 	const SkeletonResourcePtr& getSkeleronResource() const
 	const SkeletonResourcePtr& getSkeleronResource() const
@@ -86,11 +92,13 @@ private:
 
 
 	SceneNode* m_node;
 	SceneNode* m_node;
 	SkeletonResourcePtr m_skeleton;
 	SkeletonResourcePtr m_skeleton;
-	DynamicArray<Mat4> m_boneTrfs;
+	Array<DynamicArray<Mat4>, 2> m_boneTrfs;
 	DynamicArray<Trf> m_animationTrfs;
 	DynamicArray<Trf> m_animationTrfs;
 	Aabb m_boneBoundingVolume{Vec3(-1.0f), Vec3(1.0f)};
 	Aabb m_boneBoundingVolume{Vec3(-1.0f), Vec3(1.0f)};
 	Array<Track, MAX_ANIMATION_TRACKS> m_tracks;
 	Array<Track, MAX_ANIMATION_TRACKS> m_tracks;
 	Second m_absoluteTime = 0.0;
 	Second m_absoluteTime = 0.0;
+	U8 m_crntBoneTrfs = 0;
+	U8 m_prevBoneTrfs = 1;
 
 
 	void visitBones(const Bone& bone,
 	void visitBones(const Bone& bone,
 		const Mat4& parentTrf,
 		const Mat4& parentTrf,