Browse Source

Add the base of motion blur

Panagiotis Christopoulos Charitos 7 years ago
parent
commit
82867309fe

+ 1 - 0
samples/physics_playground/assets/dynamic-material.ankimtl

@@ -14,6 +14,7 @@
 	
 	<inputs>
 		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="prevMvp" builtin="PREVIOUS_MODEL_VIEW_PROJECTION_MATRIX"/>
 		<input shaderInput="rotationMat" builtin="ROTATION_MATRIX"/>
 		
 	

+ 1 - 0
samples/physics_playground/assets/sky-material.ankimtl

@@ -14,6 +14,7 @@
 	
 	<inputs>
 		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="prevMvp" builtin="PREVIOUS_MODEL_VIEW_PROJECTION_MATRIX"/>
 		<input shaderInput="rotationMat" builtin="ROTATION_MATRIX"/>
 		
 	

+ 1 - 0
samples/physics_playground/assets/walls-material.ankimtl

@@ -14,6 +14,7 @@
 	
 	<inputs>
 		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="prevMvp" builtin="PREVIOUS_MODEL_VIEW_PROJECTION_MATRIX"/>
 		<input shaderInput="rotationMat" builtin="ROTATION_MATRIX"/>
 		
 	

+ 18 - 11
shaders/GBufferCommonFrag.glsl

@@ -21,15 +21,20 @@ layout(location = 3) in mediump Vec3 in_bitangent;
 layout(location = 4) in mediump F32 in_distFromTheCamera; // Parallax
 layout(location = 5) in mediump Vec3 in_eyeTangentSpace; // Parallax
 layout(location = 6) in mediump Vec3 in_normalTangentSpace; // Parallax
+
+#	if VELOCITY
+layout(location = 7) in mediump Vec2 in_velocity; // Velocity
+#	endif
 #endif // PASS == PASS_GB_FS
 
 //
 // Output
 //
 #if PASS == PASS_GB_FS || PASS == PASS_EZ
-layout(location = 0) out Vec4 out_msRt0;
-layout(location = 1) out Vec4 out_msRt1;
-layout(location = 2) out Vec4 out_msRt2;
+layout(location = 0) out Vec4 out_gbuffer0;
+layout(location = 1) out Vec4 out_gbuffer1;
+layout(location = 2) out Vec4 out_gbuffer2;
+layout(location = 3) out Vec2 out_gbuffer3;
 #endif
 
 //
@@ -136,13 +141,14 @@ Vec2 computeTextureCoordParallax(in sampler2D heightMap, in Vec2 uv, in F32 heig
 }
 
 // Write the data to FAIs
-void writeRts(in Vec3 diffColor, // from 0 to 1
-	in Vec3 normal,
-	in Vec3 specularColor,
-	in F32 roughness,
-	in F32 subsurface,
-	in Vec3 emission,
-	in F32 metallic)
+void writeRts(Vec3 diffColor,
+	Vec3 normal,
+	Vec3 specularColor,
+	F32 roughness,
+	F32 subsurface,
+	Vec3 emission,
+	F32 metallic,
+	Vec2 velocity)
 {
 	GbufferInfo g;
 	g.m_diffuse = diffColor;
@@ -152,6 +158,7 @@ void writeRts(in Vec3 diffColor, // from 0 to 1
 	g.m_subsurface = subsurface;
 	g.m_emission = (emission.r + emission.g + emission.b) / 3.0;
 	g.m_metallic = metallic;
-	writeGBuffer(g, out_msRt0, out_msRt1, out_msRt2);
+	g.m_velocity = velocity;
+	writeGBuffer(g, out_gbuffer0, out_gbuffer1, out_gbuffer2, out_gbuffer3);
 }
 #endif // PASS == PASS_GB_FS

+ 16 - 0
shaders/GBufferCommonVert.glsl

@@ -50,6 +50,10 @@ layout(location = 3) out mediump Vec3 out_bitangent;
 layout(location = 4) out mediump F32 out_distFromTheCamera; // Parallax
 layout(location = 5) out mediump Vec3 out_eyeTangentSpace; // Parallax
 layout(location = 6) out mediump Vec3 out_normalTangentSpace; // Parallax
+
+#	if VELOCITY
+layout(location = 7) out mediump Vec2 out_velocity; // Velocity
+#	endif
 #endif
 
 //
@@ -131,3 +135,15 @@ void skinning()
 #	endif
 }
 #endif
+
+#if VELOCITY && PASS == PASS_GB_FS
+void velocity(Mat4 prevMvp)
+{
+	Vec2 crntNdc = gl_Position.xy / gl_Position.w;
+
+	Vec4 v4 = prevMvp * Vec4(g_position, 1.0);
+	Vec2 prevNdc = v4.xy / v4.w;
+
+	out_velocity = NDC_TO_UV(crntNdc - prevNdc);
+}
+#endif

+ 17 - 4
shaders/GBufferGeneric.glslp

@@ -14,10 +14,12 @@
 #pragma anki mutator PARALLAX 0 1
 #pragma anki mutator EMISSIVE_TEX 0 1
 #pragma anki mutator BONES 0 1
+#pragma anki mutator VELOCITY 0 1
 
 #pragma anki input instanced Mat4 mvp
 #pragma anki input instanced Mat3 rotationMat "PASS == 0"
 #pragma anki input instanced Mat4 modelViewMat "PASS == 0 && PARALLAX == 1"
+#pragma anki input instanced Mat4 prevMvp "PASS == 0 && VELOCITY == 1"
 
 #pragma anki input const Vec3 diffColor "DIFFUSE_TEX == 0 && PASS == 0"
 #pragma anki input const Vec3 specColor "SPECULAR_TEX == 0 && PASS == 0"
@@ -49,6 +51,10 @@ void main()
 #	if PARALLAX
 	parallax(modelViewMat);
 #	endif
+
+#	if VELOCITY
+	velocity(prevMvp);
+#	endif
 #else
 	gl_Position = mvp * Vec4(g_position, 1.0);
 #endif
@@ -93,11 +99,18 @@ void main()
 	Vec3 emission = texture(emissiveTex, uv).rgb;
 #	endif
 
-	writeRts(diffColor, normal, specColor, roughness, subsurface, emission, metallic);
+#	if VELOCITY
+	Vec2 velocity = in_velocity;
+#	else
+	Vec2 velocity = Vec2(0.0);
+#	endif
+
+	writeRts(diffColor, normal, specColor, roughness, subsurface, emission, metallic, velocity);
 #elif PASS == PASS_EZ
-	out_msRt0 = Vec4(0.0);
-	out_msRt1 = Vec4(0.0);
-	out_msRt2 = Vec4(0.0);
+	out_gbuffer0 = Vec4(0.0);
+	out_gbuffer1 = Vec4(0.0);
+	out_gbuffer2 = Vec4(0.0);
+	out_gbuffer3 = Vec2(0.0);
 #endif
 }
 

+ 7 - 2
shaders/Pack.glsl

@@ -135,16 +135,19 @@ struct GbufferInfo
 	F32 m_metallic;
 	F32 m_subsurface;
 	F32 m_emission;
+	Vec2 m_velocity;
 };
 
 // Populate the G buffer
-void writeGBuffer(GbufferInfo g, out Vec4 rt0, out Vec4 rt1, out Vec4 rt2)
+void writeGBuffer(GbufferInfo g, out Vec4 rt0, out Vec4 rt1, out Vec4 rt2, out Vec2 rt3)
 {
 	rt0 = Vec4(g.m_diffuse, g.m_subsurface);
 	rt1 = Vec4(g.m_roughness, g.m_metallic, g.m_specular.x, 0.0);
 
 	Vec3 encNorm = signedOctEncode(g.m_normal);
 	rt2 = Vec4(encNorm.xy, g.m_emission / MAX_EMISSION, encNorm.z);
+
+	rt3 = g.m_velocity;
 }
 
 // Read from G-buffer
@@ -161,7 +164,7 @@ F32 readRoughnessFromGBuffer(sampler2D rt1, Vec2 uv)
 	return r;
 }
 
-// Read from the G buffer
+// Read part of the G-buffer
 void readGBuffer(sampler2D rt0, sampler2D rt1, sampler2D rt2, Vec2 uv, F32 lod, out GbufferInfo g)
 {
 	Vec4 comp = textureLod(rt0, uv, 0.0);
@@ -177,6 +180,8 @@ void readGBuffer(sampler2D rt0, sampler2D rt1, sampler2D rt2, Vec2 uv, F32 lod,
 	g.m_normal = signedOctDecode(comp.xyw);
 	g.m_emission = comp.z * MAX_EMISSION;
 
+	g.m_velocity = Vec2(FLT_MAX); // Put something random
+
 	// Fix roughness
 	g.m_roughness = g.m_roughness * (1.0 - MIN_ROUGHNESS) + MIN_ROUGHNESS;
 

+ 1 - 1
src/anki/renderer/Common.cpp

@@ -9,6 +9,6 @@ namespace anki
 {
 
 const Array<Format, GBUFFER_COLOR_ATTACHMENT_COUNT> MS_COLOR_ATTACHMENT_PIXEL_FORMATS = {
-	{Format::R8G8B8A8_UNORM, Format::R8G8B8A8_UNORM, Format::A2B10G10R10_UNORM_PACK32}};
+	{Format::R8G8B8A8_UNORM, Format::R8G8B8A8_UNORM, Format::A2B10G10R10_UNORM_PACK32, Format::R16G16_UNORM}};
 
 } // end namespace anki

+ 1 - 1
src/anki/renderer/Common.h

@@ -84,7 +84,7 @@ inline void computeLinearizeDepthOptimal(F32 near, F32 far, F32& a, F32& b)
 	b = far / near;
 }
 
-const U GBUFFER_COLOR_ATTACHMENT_COUNT = 3;
+const U GBUFFER_COLOR_ATTACHMENT_COUNT = 4;
 
 /// Downsample and blur down to a texture with size DOWNSCALE_BLUR_DOWN_TO
 const U DOWNSCALE_BLUR_DOWN_TO = 32;

+ 1 - 1
src/anki/renderer/Dbg.cpp

@@ -76,7 +76,7 @@ void Dbg::run(RenderPassWorkContext& rgraphCtx, const RenderingContext& ctx)
 	dctx.m_cameraTransform = ctx.m_renderQueue->m_viewMatrix.getInverse();
 	dctx.m_stagingGpuAllocator = &m_r->getStagingGpuMemoryManager();
 	dctx.m_commandBuffer = cmdb;
-	dctx.m_key = RenderingKey(Pass::GB_FS, 0, 1);
+	dctx.m_key = RenderingKey(Pass::GB_FS, 0, 1, false, false);
 	dctx.m_debugDraw = true;
 	dctx.m_debugDrawFlags = m_debugDrawFlags;
 

+ 1 - 1
src/anki/renderer/Drawer.cpp

@@ -54,7 +54,7 @@ void RenderableDrawer::drawRange(Pass pass,
 	ctx.m_queueCtx.m_cameraTransform = ctx.m_queueCtx.m_viewMatrix.getInverse();
 	ctx.m_queueCtx.m_stagingGpuAllocator = &m_r->getStagingGpuMemoryManager();
 	ctx.m_queueCtx.m_commandBuffer = cmdb;
-	ctx.m_queueCtx.m_key = RenderingKey(pass, 0, 1);
+	ctx.m_queueCtx.m_key = RenderingKey(pass, 0, 1, false, false);
 	ctx.m_queueCtx.m_debugDraw = false;
 
 	for(; begin != end; ++begin)

+ 2 - 1
src/anki/renderer/GBuffer.cpp

@@ -39,7 +39,8 @@ Error GBuffer::initInternal(const ConfigSet& initializer)
 		m_r->getWidth(), m_r->getHeight(), GBUFFER_DEPTH_ATTACHMENT_PIXEL_FORMAT, "GBuffer depth");
 	m_depthRtDescr.bake();
 
-	static const char* rtNames[GBUFFER_COLOR_ATTACHMENT_COUNT] = {"GBuffer rt0", "GBuffer rt1", "GBuffer rt2"};
+	static const Array<const char*, GBUFFER_COLOR_ATTACHMENT_COUNT> rtNames = {
+		{"GBuffer rt0", "GBuffer rt1", "GBuffer rt2", "GBuffer rt3"}};
 	for(U i = 0; i < GBUFFER_COLOR_ATTACHMENT_COUNT; ++i)
 	{
 		m_colorRtDescrs[i] = m_r->create2DRenderTargetDescription(

+ 2 - 2
src/anki/renderer/Indirect.cpp

@@ -380,8 +380,8 @@ void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 	rgraphCtx.bindColorTextureAndSampler(
 		GBUFFER_RT2_BINDING.x(), GBUFFER_RT2_BINDING.y(), m_ctx.m_gbufferColorRts[2], m_r->getNearestSampler());
 
-	rgraphCtx.bindTextureAndSampler(0,
-		GBUFFER_COLOR_ATTACHMENT_COUNT,
+	rgraphCtx.bindTextureAndSampler(GBUFFER_DEPTH_BINDING.x(),
+		GBUFFER_DEPTH_BINDING.y(),
 		m_ctx.m_gbufferDepthRt,
 		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
 		m_r->getNearestSampler());

+ 1 - 0
src/anki/renderer/RenderQueue.h

@@ -22,6 +22,7 @@ public:
 	Mat4 m_viewMatrix;
 	Mat4 m_projectionMatrix;
 	Mat4 m_viewProjectionMatrix;
+	Mat4 m_previousViewProjectionMatrix;
 };
 
 /// Some options that can be used as hints in debug drawcalls.

+ 39 - 8
src/anki/resource/MaterialResource.cpp

@@ -26,7 +26,8 @@ static const Array<BuiltinVarInfo, U(BuiltinMaterialVariableId::COUNT) - 1> BUIL
 		{"NORMAL_MATRIX", ShaderVariableDataType::MAT3, true},
 		{"ROTATION_MATRIX", ShaderVariableDataType::MAT3, true},
 		{"CAMERA_ROTATION_MATRIX", ShaderVariableDataType::MAT3, false},
-		{"CAMERA_POSITION", ShaderVariableDataType::VEC3, false}}};
+		{"CAMERA_POSITION", ShaderVariableDataType::VEC3, false},
+		{"PREVIOUS_MODEL_VIEW_PROJECTION_MATRIX", ShaderVariableDataType::MAT4, true}}};
 
 MaterialVariable::MaterialVariable()
 {
@@ -117,7 +118,8 @@ Error MaterialResource::parseMutators(XmlElement mutatorsEl)
 			return Error::USER_DATA;
 		}
 
-		if(mutatorName == "INSTANCE_COUNT" || mutatorName == "PASS" || mutatorName == "LOD" || mutatorName == "BONES")
+		if(mutatorName == "INSTANCE_COUNT" || mutatorName == "PASS" || mutatorName == "LOD" || mutatorName == "BONES"
+			|| mutatorName == "VELOCITY")
 		{
 			ANKI_RESOURCE_LOGE("Cannot list builtin mutator \"%s\"", &mutatorName[0]);
 			return Error::USER_DATA;
@@ -246,6 +248,27 @@ Error MaterialResource::parseMutators(XmlElement mutatorsEl)
 		++builtinMutatorCount;
 	}
 
+	m_velocityMutator = m_prog->tryFindMutator("VELOCITY");
+	if(m_velocityMutator)
+	{
+		if(m_velocityMutator->getValues().getSize() != 2)
+		{
+			ANKI_RESOURCE_LOGE("Mutator VELOCITY should have 2 values in the program");
+			return Error::USER_DATA;
+		}
+
+		for(U i = 0; i < m_velocityMutator->getValues().getSize(); ++i)
+		{
+			if(m_velocityMutator->getValues()[i] != I(i))
+			{
+				ANKI_RESOURCE_LOGE("Values of the VELOCITY mutator in the program are not the expected");
+				return Error::USER_DATA;
+			}
+		}
+
+		++builtinMutatorCount;
+	}
+
 	if(m_mutations.getSize() + builtinMutatorCount != m_prog->getMutators().getSize())
 	{
 		ANKI_RESOURCE_LOGE("Some mutatators are unacounted for");
@@ -493,7 +516,7 @@ Error MaterialResource::parseInputs(XmlElement inputsEl, Bool async)
 	return Error::NONE;
 }
 
-const MaterialVariant& MaterialResource::getOrCreateVariant(const RenderingKey& key_, Bool skinned) const
+const MaterialVariant& MaterialResource::getOrCreateVariant(const RenderingKey& key_) const
 {
 	RenderingKey key = key_;
 	key.m_lod = min<U>(m_lodCount - 1, key.m_lod);
@@ -503,18 +526,19 @@ const MaterialVariant& MaterialResource::getOrCreateVariant(const RenderingKey&
 		ANKI_ASSERT(key.m_instanceCount == 1);
 	}
 
-	ANKI_ASSERT(!skinned || m_bonesMutator);
+	ANKI_ASSERT(!key.m_skinned || m_bonesMutator);
+	ANKI_ASSERT(!key.m_velocity || m_velocityMutator);
 
 	key.m_instanceCount = 1 << getInstanceGroupIdx(key.m_instanceCount);
 
-	MaterialVariant& variant =
-		m_variantMatrix[U(key.m_pass)][key.m_lod][getInstanceGroupIdx(key.m_instanceCount)][skinned];
+	MaterialVariant& variant = m_variantMatrix[U(key.m_pass)][key.m_lod][getInstanceGroupIdx(key.m_instanceCount)]
+											  [key.m_skinned][key.m_velocity];
 	LockGuard<SpinLock> lock(m_variantMatrixMtx);
 
 	if(variant.m_variant == nullptr)
 	{
 		const U mutatorCount = m_mutations.getSize() + ((m_instanceMutator) ? 1 : 0) + ((m_passMutator) ? 1 : 0)
-							   + ((m_lodMutator) ? 1 : 0) + ((m_bonesMutator) ? 1 : 0);
+							   + ((m_lodMutator) ? 1 : 0) + ((m_bonesMutator) ? 1 : 0) + ((m_velocityMutator) ? 1 : 0);
 
 		DynamicArrayAuto<ShaderProgramResourceMutation> mutations(getTempAllocator());
 		mutations.create(mutatorCount);
@@ -548,7 +572,14 @@ const MaterialVariant& MaterialResource::getOrCreateVariant(const RenderingKey&
 		if(m_bonesMutator)
 		{
 			mutations[count].m_mutator = m_bonesMutator;
-			mutations[count].m_value = skinned != 0;
+			mutations[count].m_value = key.m_skinned != 0;
+			++count;
+		}
+
+		if(m_velocityMutator)
+		{
+			mutations[count].m_mutator = m_velocityMutator;
+			mutations[count].m_value = key.m_velocity != 0;
 			++count;
 		}
 

+ 4 - 2
src/anki/resource/MaterialResource.h

@@ -40,6 +40,7 @@ enum class BuiltinMaterialVariableId : U8
 	ROTATION_MATRIX,
 	CAMERA_ROTATION_MATRIX,
 	CAMERA_POSITION,
+	PREVIOUS_MODEL_VIEW_PROJECTION_MATRIX,
 	COUNT
 };
 
@@ -236,7 +237,7 @@ public:
 		return m_prog->isInstanced();
 	}
 
-	const MaterialVariant& getOrCreateVariant(const RenderingKey& key, Bool skinned = false) const;
+	const MaterialVariant& getOrCreateVariant(const RenderingKey& key) const;
 
 	const DynamicArray<MaterialVariable>& getVariables() const
 	{
@@ -265,11 +266,12 @@ private:
 	const ShaderProgramResourceMutator* m_passMutator = nullptr;
 	const ShaderProgramResourceMutator* m_instanceMutator = nullptr;
 	const ShaderProgramResourceMutator* m_bonesMutator = nullptr;
+	const ShaderProgramResourceMutator* m_velocityMutator = nullptr;
 
 	DynamicArray<ShaderProgramResourceMutation> m_mutations;
 
 	/// Matrix of variants.
-	mutable Array4d<MaterialVariant, U(Pass::COUNT), MAX_LOD_COUNT, MAX_INSTANCE_GROUPS, 2> m_variantMatrix;
+	mutable Array5d<MaterialVariant, U(Pass::COUNT), MAX_LOD_COUNT, MAX_INSTANCE_GROUPS, 2, 2> m_variantMatrix;
 	mutable SpinLock m_variantMatrixMtx;
 
 	DynamicArray<MaterialVariable> m_vars; ///< Non-const vars.

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

@@ -35,8 +35,9 @@ void ModelPatch::getRenderingDataSub(
 	{
 		RenderingKey mtlKey = key;
 		mtlKey.m_lod = min<U>(key.m_lod, m_mtl->getLodCount() - 1);
+		mtlKey.m_skinned = m_model->getSkeleton().isCreated();
 
-		const MaterialVariant& variant = m_mtl->getOrCreateVariant(mtlKey, m_model->getSkeleton().isCreated());
+		const MaterialVariant& variant = m_mtl->getOrCreateVariant(mtlKey);
 
 		inf.m_program = variant.getShaderProgram();
 	}

+ 1 - 1
src/anki/resource/ParticleEmitterResource.cpp

@@ -200,7 +200,7 @@ void ParticleEmitterResource::getRenderingInfo(U lod, ShaderProgramPtr& prog) co
 {
 	lod = min<U>(lod, m_lodCount - 1);
 
-	RenderingKey key(Pass::GB_FS, lod, 1);
+	RenderingKey key(Pass::GB_FS, lod, 1, false, false);
 	const MaterialVariant& variant = m_material->getOrCreateVariant(key);
 	prog = variant.getShaderProgram();
 }

+ 18 - 7
src/anki/resource/RenderingKey.h

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/resource/Common.h>
+#include <anki/util/Hash.h>
 
 namespace anki
 {
@@ -26,40 +27,50 @@ public:
 	Pass m_pass;
 	U8 m_lod;
 	U8 m_instanceCount;
+	Bool8 m_skinned;
+	Bool8 m_velocity;
 
-	RenderingKey(Pass pass, U8 lod, U instanceCount)
+	RenderingKey(Pass pass, U8 lod, U instanceCount, Bool8 skinned, Bool8 velocity)
 		: m_pass(pass)
 		, m_lod(lod)
 		, m_instanceCount(instanceCount)
+		, m_skinned(skinned)
+		, m_velocity(velocity)
 	{
 		ANKI_ASSERT(m_instanceCount <= MAX_INSTANCES && m_instanceCount != 0);
 		ANKI_ASSERT(m_lod <= MAX_LOD_COUNT);
 	}
 
 	RenderingKey()
-		: RenderingKey(Pass::GB_FS, 0, 1)
+		: RenderingKey(Pass::GB_FS, 0, 1, false, false)
 	{
 	}
 
 	RenderingKey(const RenderingKey& b)
-		: RenderingKey(b.m_pass, b.m_lod, b.m_instanceCount)
+		: RenderingKey(b.m_pass, b.m_lod, b.m_instanceCount, b.m_skinned, b.m_velocity)
 	{
 	}
+
+	Bool operator==(const RenderingKey& b) const
+	{
+		return m_pass == b.m_pass && m_lod == b.m_lod && m_instanceCount == b.m_instanceCount
+			   && m_skinned == b.m_skinned && m_velocity == b.m_velocity;
+	}
 };
 
 template<>
 constexpr Bool isPacked<RenderingKey>()
 {
-	return sizeof(RenderingKey) == 3;
+	return sizeof(RenderingKey) == 5;
 }
 
 /// The hash function
 class RenderingKeyHasher
 {
 public:
-	PtrSize operator()(const RenderingKey& key) const
+	U64 operator()(const RenderingKey& key) const
 	{
-		return U8(key.m_pass) | (key.m_lod << 8) | (key.m_instanceCount << 16);
+		return computeHash(&key, sizeof(key));
 	}
 };
 
@@ -69,7 +80,7 @@ class RenderingKeyEqual
 public:
 	Bool operator()(const RenderingKey& a, const RenderingKey& b) const
 	{
-		return a.m_pass == b.m_pass && a.m_lod == b.m_lod && a.m_instanceCount == b.m_instanceCount;
+		return a == b;
 	}
 };
 

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

@@ -4,7 +4,6 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/resource/ShaderProgramResource.h>
-#include <anki/resource/RenderingKey.h>
 #include <anki/resource/ResourceManager.h>
 #include <anki/util/Filesystem.h>
 #include <tinyexpr.h>

+ 0 - 3
src/anki/resource/ShaderProgramResource.h

@@ -16,9 +16,6 @@ struct te_variable;
 namespace anki
 {
 
-// Forward
-class RenderingKey;
-
 /// @addtogroup resource
 /// @{
 

+ 64 - 42
src/anki/scene/ModelNode.cpp

@@ -78,12 +78,38 @@ void ModelPatchNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<vo
 	// That will not work on multi-draw and instanced at the same time. Make sure that there is no multi-draw anywhere
 	ANKI_ASSERT(self.m_modelPatch->getSubMeshCount() == 1);
 
+	// Transforms
+	Array<Mat4, MAX_INSTANCES> trfs;
+	Array<Mat4, MAX_INSTANCES> prevTrfs;
+	const MoveComponent& movec = self.getParent()->getComponentAt<MoveComponent>(0);
+	trfs[0] = Mat4(movec.getWorldTransform());
+	prevTrfs[0] = Mat4(movec.getPreviousWorldTransform());
+	Bool moved = trfs[0] != prevTrfs[0]; // If at least one is moved then it's dynamic
+	for(U i = 1; i < userData.getSize(); ++i)
+	{
+		const ModelPatchNode& self2 = *static_cast<const ModelPatchNode*>(userData[i]);
+		const MoveComponent& movec = self2.getParent()->getComponentAt<MoveComponent>(0);
+		trfs[i] = Mat4(movec.getWorldTransform());
+		prevTrfs[i] = Mat4(movec.getPreviousWorldTransform());
+
+		moved = moved || (trfs[i] != prevTrfs[i]);
+	}
+
 	ModelRenderingInfo modelInf;
+	ctx.m_key.m_velocity = moved;
 	self.m_modelPatch->getRenderingDataSub(ctx.m_key, WeakArray<U8>(), modelInf);
 
 	// Program
 	cmdb->bindShaderProgram(modelInf.m_program);
 
+	// Uniforms
+	static_cast<const MaterialRenderComponent&>(self.getComponentAt<RenderComponent>(1))
+		.allocateAndSetupUniforms(self.m_modelPatch->getMaterial()->getDescriptorSetIndex(),
+			ctx,
+			ConstWeakArray<Mat4>(&trfs[0], userData.getSize()),
+			ConstWeakArray<Mat4>(&prevTrfs[0], userData.getSize()),
+			*ctx.m_stagingGpuAllocator);
+
 	// Set attributes
 	for(U i = 0; i < modelInf.m_vertexAttributeCount; ++i)
 	{
@@ -102,21 +128,6 @@ void ModelPatchNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<vo
 	// Index buffer
 	cmdb->bindIndexBuffer(modelInf.m_indexBuffer, 0, IndexType::U16);
 
-	// Uniforms
-	Array<Mat4, MAX_INSTANCES> trfs;
-	trfs[0] = Mat4(self.getParent()->getComponentAt<MoveComponent>(0).getWorldTransform());
-	for(U i = 1; i < userData.getSize(); ++i)
-	{
-		const ModelPatchNode& self2 = *static_cast<const ModelPatchNode*>(userData[i]);
-		trfs[i] = Mat4(self2.getParent()->getComponentAt<MoveComponent>(0).getWorldTransform());
-	}
-
-	static_cast<const MaterialRenderComponent&>(self.getComponentAt<RenderComponent>(1))
-		.allocateAndSetupUniforms(self.m_modelPatch->getMaterial()->getDescriptorSetIndex(),
-			ctx,
-			ConstWeakArray<Mat4>(&trfs[0], userData.getSize()),
-			*ctx.m_stagingGpuAllocator);
-
 	// Draw
 	cmdb->drawElements(PrimitiveTopology::TRIANGLES,
 		modelInf.m_indicesCountArray[0],
@@ -266,12 +277,50 @@ void ModelNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*>
 		// anywhere
 		ANKI_ASSERT(patch->getSubMeshCount() == 1);
 
+		// Transforms
+		Array<Mat4, MAX_INSTANCES> trfs;
+		Array<Mat4, MAX_INSTANCES> prevTrfs;
+		const MoveComponent& movec = self.getComponent<MoveComponent>();
+		trfs[0] = Mat4(movec.getWorldTransform());
+		prevTrfs[0] = Mat4(movec.getPreviousWorldTransform());
+		Bool moved = trfs[0] != prevTrfs[0];
+		for(U i = 1; i < userData.getSize(); ++i)
+		{
+			const ModelNode& self2 = *static_cast<const ModelNode*>(userData[i]);
+			const MoveComponent& movec = self2.getComponent<MoveComponent>();
+			trfs[i] = Mat4(movec.getWorldTransform());
+			prevTrfs[i] = Mat4(movec.getPreviousWorldTransform());
+
+			moved = moved || (trfs[i] != prevTrfs[i]);
+		}
+
+		// Bones storage
+		if(self.m_model->getSkeleton())
+		{
+			const SkinComponent& skinc = self.getComponentAt<SkinComponent>(0);
+			StagingGpuMemoryToken token;
+			void* trfs = ctx.m_stagingGpuAllocator->allocateFrame(
+				skinc.getBoneTransforms().getSize() * sizeof(Mat4), StagingGpuMemoryType::STORAGE, token);
+			memcpy(trfs, &skinc.getBoneTransforms()[0], skinc.getBoneTransforms().getSize() * sizeof(Mat4));
+
+			cmdb->bindStorageBuffer(0, 0, token.m_buffer, token.m_offset, token.m_range);
+		}
+
+		ctx.m_key.m_velocity = moved;
 		ModelRenderingInfo modelInf;
 		patch->getRenderingDataSub(ctx.m_key, WeakArray<U8>(), modelInf);
 
 		// Program
 		cmdb->bindShaderProgram(modelInf.m_program);
 
+		// Uniforms
+		static_cast<const MaterialRenderComponent&>(self.getComponent<RenderComponent>())
+			.allocateAndSetupUniforms(patch->getMaterial()->getDescriptorSetIndex(),
+				ctx,
+				ConstWeakArray<Mat4>(&trfs[0], userData.getSize()),
+				ConstWeakArray<Mat4>(&prevTrfs[0], userData.getSize()),
+				*ctx.m_stagingGpuAllocator);
+
 		// Set attributes
 		for(U i = 0; i < modelInf.m_vertexAttributeCount; ++i)
 		{
@@ -291,33 +340,6 @@ void ModelNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*>
 		// Index buffer
 		cmdb->bindIndexBuffer(modelInf.m_indexBuffer, 0, IndexType::U16);
 
-		// Uniforms
-		Array<Mat4, MAX_INSTANCES> trfs;
-		trfs[0] = Mat4(self.getComponent<MoveComponent>().getWorldTransform());
-		for(U i = 1; i < userData.getSize(); ++i)
-		{
-			const ModelNode& self2 = *static_cast<const ModelNode*>(userData[i]);
-			trfs[i] = Mat4(self2.getComponent<MoveComponent>().getWorldTransform());
-		}
-
-		static_cast<const MaterialRenderComponent&>(self.getComponent<RenderComponent>())
-			.allocateAndSetupUniforms(patch->getMaterial()->getDescriptorSetIndex(),
-				ctx,
-				ConstWeakArray<Mat4>(&trfs[0], userData.getSize()),
-				*ctx.m_stagingGpuAllocator);
-
-		// Bones storage
-		if(self.m_model->getSkeleton())
-		{
-			const SkinComponent& skinc = self.getComponentAt<SkinComponent>(0);
-			StagingGpuMemoryToken token;
-			void* trfs = ctx.m_stagingGpuAllocator->allocateFrame(
-				skinc.getBoneTransforms().getSize() * sizeof(Mat4), StagingGpuMemoryType::STORAGE, token);
-			memcpy(trfs, &skinc.getBoneTransforms()[0], skinc.getBoneTransforms().getSize() * sizeof(Mat4));
-
-			cmdb->bindStorageBuffer(0, 0, token.m_buffer, token.m_offset, token.m_range);
-		}
-
 		// Draw
 		cmdb->drawElements(PrimitiveTopology::TRIANGLES,
 			modelInf.m_indicesCountArray[0],

+ 1 - 0
src/anki/scene/ParticleEmitterNode.cpp

@@ -311,6 +311,7 @@ void ParticleEmitterNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArr
 			.allocateAndSetupUniforms(self.m_particleEmitterResource->getMaterial()->getDescriptorSetIndex(),
 				ctx,
 				trf,
+				trf,
 				*ctx.m_stagingGpuAllocator);
 
 		// Draw

+ 22 - 2
src/anki/scene/components/RenderComponent.cpp

@@ -33,10 +33,14 @@ MaterialRenderComponent::~MaterialRenderComponent()
 	m_vars.destroy(getAllocator());
 }
 
-void MaterialRenderComponent::allocateAndSetupUniforms(
-	U set, const RenderQueueDrawContext& ctx, ConstWeakArray<Mat4> transforms, StagingGpuMemoryManager& alloc) const
+void MaterialRenderComponent::allocateAndSetupUniforms(U set,
+	const RenderQueueDrawContext& ctx,
+	ConstWeakArray<Mat4> transforms,
+	ConstWeakArray<Mat4> prevTransforms,
+	StagingGpuMemoryManager& alloc) const
 {
 	ANKI_ASSERT(transforms.getSize() <= MAX_INSTANCES);
+	ANKI_ASSERT(prevTransforms.getSize() == transforms.getSize());
 
 	const MaterialVariant& variant = m_mtl->getOrCreateVariant(ctx.m_key);
 	const ShaderProgramResourceVariant& progVariant = variant.getShaderProgramResourceVariant();
@@ -196,6 +200,22 @@ void MaterialRenderComponent::allocateAndSetupUniforms(
 				progVariant.writeShaderBlockMemory(progvar, &mvp[0], transforms.getSize(), uniformsBegin, uniformsEnd);
 				break;
 			}
+			case BuiltinMaterialVariableId::PREVIOUS_MODEL_VIEW_PROJECTION_MATRIX:
+			{
+				ANKI_ASSERT(prevTransforms.getSize() > 0);
+
+				DynamicArrayAuto<Mat4> mvp(getFrameAllocator());
+				mvp.create(prevTransforms.getSize());
+
+				for(U i = 0; i < prevTransforms.getSize(); i++)
+				{
+					mvp[i] = ctx.m_previousViewProjectionMatrix * prevTransforms[i];
+				}
+
+				progVariant.writeShaderBlockMemory(
+					progvar, &mvp[0], prevTransforms.getSize(), uniformsBegin, uniformsEnd);
+				break;
+			}
 			case BuiltinMaterialVariableId::MODEL_VIEW_MATRIX:
 			{
 				ANKI_ASSERT(transforms.getSize() > 0);

+ 1 - 0
src/anki/scene/components/RenderComponent.h

@@ -123,6 +123,7 @@ public:
 	void allocateAndSetupUniforms(U set,
 		const RenderQueueDrawContext& ctx,
 		ConstWeakArray<Mat4> transforms,
+		ConstWeakArray<Mat4> prevTransforms,
 		StagingGpuMemoryManager& alloc) const;
 
 private:

+ 4 - 0
src/anki/util/Array.h

@@ -158,6 +158,10 @@ using Array3d = Array<Array<Array<T, K>, J>, I>;
 /// 4D Array. @code Array4d<X, 10, 2, 3, 4> a; @endcode is equivelent to @code X a[10][2][3][4]; @endcode
 template<typename T, PtrSize I, PtrSize J, PtrSize K, PtrSize L>
 using Array4d = Array<Array<Array<Array<T, L>, K>, J>, I>;
+
+/// 5D Array. @code Array5d<X, 10, 2, 3, 4, 5> a; @endcode is equivelent to @code X a[10][2][3][4][5]; @endcode
+template<typename T, PtrSize I, PtrSize J, PtrSize K, PtrSize L, PtrSize M>
+using Array5d = Array<Array<Array<Array<Array<T, M>, L>, K>, J>, I>;
 /// @}
 
 } // end namespace anki

+ 1 - 0
tools/scene/ExporterMaterial.cpp

@@ -22,6 +22,7 @@ const char* MATERIAL_TEMPLATE = R"(<?xml version="1.0" encoding="UTF-8" ?>
 	
 	<inputs>
 		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="prevMvp" builtin="PREVIOUS_MODEL_VIEW_PROJECTION_MATRIX"/>
 		<input shaderInput="rotationMat" builtin="ROTATION_MATRIX"/>
 		%parallaxInput%