Browse Source

Some work

Panagiotis Christopoulos Charitos 6 years ago
parent
commit
423241e14e

+ 100 - 3
shaders/GpuParticles.glslp

@@ -5,15 +5,112 @@
 
 // This shader does a particle simulation
 
-#pragma anki input const UVec3 WORKGROUP_SIZE
+#pragma anki input const U32 WORKGROUP_SIZE_X
 
 #pragma anki start comp
 
-layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y, local_size_z = WORKGROUP_SIZE.z) in;
+#include <shaders/glsl_cpp_common/GpuParticles.h>
+#include <shaders/Common.glsl>
+
+layout(local_size_x = WORKGROUP_SIZE_X, local_size_y = 1, local_size_z = 1) in;
+
+layout(set = 0, binding = 0) uniform texture2D u_depthMap;
+
+layout(set = 1, binding = 0) buffer ssbo_
+{
+	GpuParticle u_particles[];
+};
+
+layout(set = 1, binding = 1) buffer ubo_
+{
+	GpuParticleEmitterProperties u_props;
+};
+
+layout(set = 1, binding = 2) uniform texture2D u_noiseTex;
+layout(set = 1, binding = 3) uniform sampler u_nearestAnyRepeatSampler;
+
+layout(std140, push_constant) pc_
+{
+	Mat4 u_viewProjMat;
+	Vec2 u_randomUv;
+	F32 u_dt;
+	F32 u_padding0;
+};
+
+const F32 noiseTexSizeX = F32(textureSize(u_noiseTex).x);
+
+F32 genRandomFactor()
+{
+	const Vec2 uv = u_randomUv + Vec2(gl_GlobalInvocationID.x) / noiseTexSizeX;
+	const F32 r = textureLod(u_noiseTex, u_nearestAnyRepeatSampler, uv).r;
+	return saturate(r);
+}
+
+void initParticle(out GpuParticle p)
+{
+	const F32 randFactor = genRandomFactor();
+
+	p.m_newWorldPosition = mix(u_props.m_minStartingPosition, u_props.m_maxStartingPosition, randFactor);
+	p.m_oldWorldPosition = p.m_newWorldPosition;
+
+	p.m_mass = mix(u_props.m_minMass, u_props.m_maxMass, randFactor);
+	p.m_life = mix(u_props.m_minLife, u_props.m_maxLife, randFactor);
+	p.m_acceleration = mix(u_props.m_minGravity, u_props.m_maxGravity, randFactor);
+
+	// Calculate the initial velocity
+	const Vec3 initialForce = mix(u_props.m_minForce, u_props.m_maxForce, randFactor);
+	const Vec3 totalForce = (p.m_acceleration * p.m_mass) + initialForce;
+	const Vec3 acceleration = totalForce / p.m_mass;
+	p.m_velocity = acceleration * u_dt;
+}
 
 void main()
 {
-	// TODO
+	const u32 particleIdx = gl_GlobalInvocationID.x;
+	if(particleIdx >= u_particleCount)
+	{
+		return;
+	}
+
+	GpuParticle particle = u_particles[particleIdx];
+	const F32 dt = u_dt;
+
+	// Check if it's dead
+	if(particle.m_life - dt <= 0.0)
+	{
+		// Dead, revive
+		initParticle(particle);
+	}
+	else
+	{
+		// Simulate
+
+		const Vec3 xp = particle.m_oldWorldPosition;
+		const Vec3 xc = particle.m_acceleration * (u_dt * u_dt) + u_particles[particleIdx].m_velocity * u_dt + xp;
+
+		// Project the point
+		const Vec4 proj4 = u_viewProjMat * Vec4(xc, 1.0);
+		const Vec3 proj3 = proj4.xyz / proj4.w;
+		if(proj3.xy > Vec2(-1.0) && proj3.xy < Vec2(1.0))
+		{
+			// It's visible, test against the depth buffer
+
+			const F32 refDepth = textureLod(u_depthMap, u_nearestAnyRepeatSampler, NDC_TO_UV(proj3.xy), 0.0).r;
+			const F32 testDepth = proj3.z;
+
+			if(testDepth >= refDepth)
+			{
+				// Collides, change its direction
+				p.m_velocity = -p.m_velocity;
+			}
+		}
+
+		particle.m_oldWorldPosition = particle.m_newWorldPosition;
+		particle.m_newWorldPosition = xc;
+	}
+
+	// Write back the particle
+	u_particles[particleIdx] = particle;
 }
 
 #pragma anki end

+ 50 - 0
shaders/glsl_cpp_common/GpuParticles.h

@@ -0,0 +1,50 @@
+// Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <shaders/glsl_cpp_common/Common.h>
+
+ANKI_BEGIN_NAMESPACE
+
+// Particle emitter properties
+struct GpuParticleEmitterProperties
+{
+	Vec3 m_minGravity;
+	F32 m_minMass;
+
+	Vec3 m_maxGravity;
+	F32 m_maxMass;
+
+	Vec3 m_minForce;
+	F32 m_minLife;
+
+	Vec3 m_maxForce;
+	F32 m_maxLife;
+
+	Vec3 m_minStartingPosition;
+	F32 m_padding0;
+
+	Vec3 m_maxStartingPosition;
+	U32 m_particleCount;
+};
+
+// GPU particle state
+struct GpuParticle
+{
+	Vec3 m_oldWorldPosition;
+	F32 m_mass;
+
+	Vec3 m_newWorldPosition;
+	F32 m_life;
+
+	Vec3 m_acceleration;
+	F32 m_padding1;
+
+	Vec3 m_velocity;
+	F32 m_padding2;
+};
+
+ANKI_END_NAMESPACE

+ 5 - 1
src/anki/renderer/GenericCompute.cpp

@@ -39,13 +39,17 @@ void GenericCompute::run(RenderPassWorkContext& rgraphCtx)
 {
 	ANKI_ASSERT(m_runCtx.m_ctx->m_renderQueue->m_genericGpuComputeJobs.getSize() > 0);
 
+	GenericGpuComputeJobQueueElementContext elementCtx;
+	elementCtx.m_commandBuffer = rgraphCtx.m_commandBuffer;
+	elementCtx.m_stagingGpuAllocator = &m_r->getStagingGpuMemoryManager();
+
 	// Bind some state
 	rgraphCtx.bindTexture(0, 0, m_r->getDepthDownscale().getHiZRt(), TextureSubresourceInfo());
 
 	for(const GenericGpuComputeJobQueueElement& element : m_runCtx.m_ctx->m_renderQueue->m_genericGpuComputeJobs)
 	{
 		ANKI_ASSERT(element.m_callback);
-		element.m_callback(element.m_userData);
+		element.m_callback(elementCtx, element.m_userData);
 	}
 }
 

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

@@ -66,8 +66,17 @@ public:
 static_assert(
 	std::is_trivially_destructible<RenderableQueueElement>::value == true, "Should be trivially destructible");
 
+/// Context that contains variables for the GenericGpuComputeJobQueueElement.
+class GenericGpuComputeJobQueueElementContext final
+{
+public:
+	CommandBufferPtr m_commandBuffer;
+	StagingGpuMemoryManager* m_stagingGpuAllocator ANKI_DEBUG_CODE(= nullptr);
+};
+
 /// Callback for GenericGpuComputeJobQueueElement.
-using GenericGpuComputeJobQueueElementCallback = void (*)(const void* userData);
+using GenericGpuComputeJobQueueElementCallback = void (*)(
+	GenericGpuComputeJobQueueElementContext& ctx, const void* userData);
 
 /// It has enough info to execute generic compute on the GPU.
 class GenericGpuComputeJobQueueElement final

+ 108 - 0
src/anki/scene/GpuParticleEmitterNode.cpp

@@ -4,8 +4,116 @@
 // http://www.anki3d.org/LICENSE
 
 #include <anki/scene/GpuParticleEmitterNode.h>
+#include <anki/scene/SceneGraph.h>
+#include <anki/scene/components/MoveComponent.h>
+#include <anki/scene/components/SpatialComponent.h>
+#include <anki/scene/components/GenericGpuComputeJobComponent.h>
+#include <anki/resource/ResourceManager.h>
+#include <shaders/glsl_cpp_common/GpuParticles.h>
 
 namespace anki
 {
 
+class GpuParticleEmitterNode::MoveFeedbackComponent : public SceneComponent
+{
+public:
+	MoveFeedbackComponent()
+		: SceneComponent(SceneComponentType::NONE)
+	{
+	}
+
+	ANKI_USE_RESULT Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
+	{
+		updated = false;
+
+		const MoveComponent& move = node.getComponent<MoveComponent>();
+		if(move.getTimestamp() == node.getGlobalTimestamp())
+		{
+			GpuParticleEmitterNode& mnode = static_cast<GpuParticleEmitterNode&>(node);
+			mnode.onMoveComponentUpdate(move);
+		}
+
+		return Error::NONE;
+	}
+};
+
+Error GpuParticleEmitterNode::init(const CString& filename)
+{
+	// Create program
+	ANKI_CHECK(getResourceManager().loadResource("shaders/GpuParticles.glslp", m_prog));
+	const ShaderProgramResourceVariant* variant;
+	m_prog->getOrCreateVariant(variant);
+	m_grProg = variant->getProgram();
+
+	// Load particle props
+	ANKI_CHECK(getResourceManager().loadResource(filename, m_emitterRsrc));
+
+	// Create a UBO with the props
+	BufferInitInfo buffInit;
+	buffInit.m_access = BufferMapAccessBit::WRITE;
+	buffInit.m_usage = BufferUsageBit::UNIFORM_COMPUTE;
+	buffInit.m_size = sizeof(GpuParticleEmitterProperties);
+	m_propsBuff = getSceneGraph().getGrManager().newBuffer(buffInit);
+	GpuParticleEmitterProperties* props =
+		static_cast<GpuParticleEmitterProperties*>(m_propsBuff->map(0, MAX_PTR_SIZE, BufferMapAccessBit::WRITE));
+
+	const ParticleEmitterProperties& inProps = m_emitterRsrc->getProperties();
+	props->m_minGravity = inProps.m_particle.m_minGravity;
+	props->m_minMass = inProps.m_particle.m_minMass;
+	props->m_maxGravity = inProps.m_particle.m_maxGravity;
+	props->m_maxMass = inProps.m_particle.m_maxMass;
+	props->m_minForce = inProps.m_particle.m_minForceDirection * inProps.m_particle.m_minForceMagnitude;
+	props->m_minLife = inProps.m_particle.m_minLife;
+	props->m_maxForce = inProps.m_particle.m_maxForceDirection * inProps.m_particle.m_maxForceMagnitude;
+	props->m_maxLife = inProps.m_particle.m_maxLife;
+	props->m_particleCount = inProps.m_maxNumOfParticles;
+
+	m_propsBuff->unmap();
+
+	// Create the particle buffer
+	buffInit.m_access = BufferMapAccessBit::WRITE;
+	buffInit.m_usage = BufferUsageBit::STORAGE_ALL;
+	buffInit.m_size = sizeof(GpuParticle) * inProps.m_maxNumOfParticles;
+	m_particlesBuff = getSceneGraph().getGrManager().newBuffer(buffInit);
+	GpuParticle* particle = static_cast<GpuParticle*>(m_particlesBuff->map(0, MAX_PTR_SIZE, BufferMapAccessBit::WRITE));
+	const GpuParticle* end = particle + inProps.m_maxNumOfParticles;
+	for(; particle < end; ++particle)
+	{
+		particle->m_life = -1.0f; // Force GPU to init the particle
+	}
+
+	m_particlesBuff->unmap();
+
+	// Find the extend of the particles
+	{
+		const Vec3 maxForce = inProps.m_particle.m_maxForceDirection * inProps.m_particle.m_maxForceMagnitude;
+		const Vec3& maxGravity = inProps.m_particle.m_maxGravity;
+		const F32 maxMass = inProps.m_particle.m_maxMass;
+		const F32 maxTime = inProps.m_particle.m_maxLife;
+
+		const Vec3 totalForce = maxGravity * maxMass + maxForce;
+		const Vec3 accelleration = totalForce / maxMass;
+		const Vec3 velocity = accelleration * maxTime;
+		m_maxDistanceAParticleCanGo = (velocity * maxTime).getLength();
+	}
+
+	// Create the components
+	newComponent<MoveComponent>();
+	newComponent<MoveFeedbackComponent>();
+	newComponent<SpatialComponent>(this, &m_spatialVolume);
+	GenericGpuComputeJobComponent* gpuComp = newComponent<GenericGpuComputeJobComponent>();
+	gpuComp->setCallback(
+		[](GenericGpuComputeJobQueueElementContext& ctx, const void* userData) {
+			static_cast<const GpuParticleEmitterNode*>(userData)->simulate(ctx.m_commandBuffer);
+		},
+		this);
+
+	return Error::NONE;
+}
+
+void GpuParticleEmitterNode::simulate(CommandBufferPtr& cmdb) const
+{
+	// TODO
+}
+
 } // end namespace anki

+ 19 - 3
src/anki/scene/GpuParticleEmitterNode.h

@@ -7,9 +7,7 @@
 
 #include <anki/scene/SceneNode.h>
 #include <anki/resource/ParticleEmitterResource.h>
-#include <anki/renderer/RenderQueue.h>
-#include <anki/collision/Obb.h>
-#include <anki/physics/Forward.h>
+#include <anki/collision/Aabb.h>
 
 namespace anki
 {
@@ -28,6 +26,24 @@ public:
 	ANKI_USE_RESULT Error init(const CString& filename);
 
 	ANKI_USE_RESULT Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
+
+private:
+	class MoveFeedbackComponent;
+
+	ShaderProgramResourcePtr m_prog;
+	ShaderProgramPtr m_grProg;
+
+	ParticleEmitterResourcePtr m_emitterRsrc;
+
+	BufferPtr m_propsBuff; ///< Constant buffer with particle properties.
+	BufferPtr m_particlesBuff; ///< Particles buffer.
+
+	Aabb m_spatialVolume = Aabb(Vec3(-1.0f), Vec3(1.0f));
+	F32 m_maxDistanceAParticleCanGo = -1.0f;
+
+	void onMoveComponentUpdate(const MoveComponent& movec);
+
+	void simulate(CommandBufferPtr& cmdb) const;
 };
 /// @}
 

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

@@ -6,6 +6,7 @@
 #pragma once
 
 #include <anki/scene/components/SceneComponent.h>
+#include <anki/renderer/RenderQueue.h>
 
 namespace anki
 {