GpuParticleEmitterComponent.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. // Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Scene/Components/GpuParticleEmitterComponent.h>
  6. #include <AnKi/Scene/SceneNode.h>
  7. #include <AnKi/Scene/SceneGraph.h>
  8. #include <AnKi/Scene/Components/RenderComponent.h>
  9. #include <AnKi/Resource/ResourceManager.h>
  10. #include <AnKi/Shaders/Include/ParticleTypes.h>
  11. namespace anki
  12. {
  13. ANKI_SCENE_COMPONENT_STATICS(GpuParticleEmitterComponent)
  14. GpuParticleEmitterComponent::GpuParticleEmitterComponent(SceneNode* node)
  15. : SceneComponent(node, getStaticClassId())
  16. , m_node(node)
  17. {
  18. }
  19. GpuParticleEmitterComponent::~GpuParticleEmitterComponent()
  20. {
  21. }
  22. Error GpuParticleEmitterComponent::loadParticleEmitterResource(CString filename)
  23. {
  24. m_markedForUpdate = true;
  25. // Create the debug drawer
  26. if(!m_dbgImage.isCreated())
  27. {
  28. ANKI_CHECK(m_node->getSceneGraph().getResourceManager().loadResource("EngineAssets/ParticleEmitter.ankitex",
  29. m_dbgImage));
  30. }
  31. // Load particle props
  32. ANKI_CHECK(m_node->getSceneGraph().getResourceManager().loadResource(filename, m_particleEmitterResource));
  33. const ParticleEmitterProperties& inProps = m_particleEmitterResource->getProperties();
  34. m_maxParticleCount = inProps.m_maxNumOfParticles;
  35. // Create program
  36. ANKI_CHECK(
  37. m_node->getSceneGraph().getResourceManager().loadResource("Shaders/GpuParticlesSimulation.ankiprog", m_prog));
  38. const ShaderProgramResourceVariant* variant;
  39. m_prog->getOrCreateVariant(variant);
  40. m_grProg = variant->getProgram();
  41. m_workgroupSizeX = variant->getWorkgroupSizes()[0];
  42. // Create a UBO with the props
  43. {
  44. BufferInitInfo buffInit("GpuParticlesProps");
  45. buffInit.m_mapAccess = BufferMapAccessBit::WRITE;
  46. buffInit.m_usage = BufferUsageBit::UNIFORM_COMPUTE;
  47. buffInit.m_size = sizeof(GpuParticleEmitterProperties);
  48. m_propsBuff = m_node->getSceneGraph().getGrManager().newBuffer(buffInit);
  49. GpuParticleEmitterProperties* props =
  50. static_cast<GpuParticleEmitterProperties*>(m_propsBuff->map(0, MAX_PTR_SIZE, BufferMapAccessBit::WRITE));
  51. props->m_minGravity = inProps.m_particle.m_minGravity;
  52. props->m_minMass = inProps.m_particle.m_minMass;
  53. props->m_maxGravity = inProps.m_particle.m_maxGravity;
  54. props->m_maxMass = inProps.m_particle.m_maxMass;
  55. props->m_minForce = inProps.m_particle.m_minForceDirection * inProps.m_particle.m_minForceMagnitude;
  56. props->m_minLife = F32(inProps.m_particle.m_minLife);
  57. props->m_maxForce = inProps.m_particle.m_maxForceDirection * inProps.m_particle.m_maxForceMagnitude;
  58. props->m_maxLife = F32(inProps.m_particle.m_maxLife);
  59. props->m_minStartingPosition = inProps.m_particle.m_minStartingPosition;
  60. props->m_maxStartingPosition = inProps.m_particle.m_maxStartingPosition;
  61. props->m_particleCount = inProps.m_maxNumOfParticles;
  62. m_propsBuff->flush(0, MAX_PTR_SIZE);
  63. m_propsBuff->unmap();
  64. }
  65. // Create the particle buffer
  66. {
  67. BufferInitInfo buffInit("GpuParticles");
  68. buffInit.m_mapAccess = BufferMapAccessBit::WRITE;
  69. buffInit.m_usage = BufferUsageBit::ALL_STORAGE;
  70. buffInit.m_size = sizeof(GpuParticle) * m_maxParticleCount;
  71. m_particlesBuff = m_node->getSceneGraph().getGrManager().newBuffer(buffInit);
  72. GpuParticle* particle =
  73. static_cast<GpuParticle*>(m_particlesBuff->map(0, MAX_PTR_SIZE, BufferMapAccessBit::WRITE));
  74. const GpuParticle* end = particle + m_maxParticleCount;
  75. for(; particle < end; ++particle)
  76. {
  77. particle->m_life = -1.0f; // Force GPU to init the particle
  78. }
  79. m_particlesBuff->flush(0, MAX_PTR_SIZE);
  80. m_particlesBuff->unmap();
  81. }
  82. // Create the rand buffer
  83. {
  84. BufferInitInfo buffInit("GpuParticlesRand");
  85. buffInit.m_mapAccess = BufferMapAccessBit::WRITE;
  86. buffInit.m_usage = BufferUsageBit::ALL_STORAGE;
  87. buffInit.m_size = sizeof(U32) + MAX_RAND_FACTORS * sizeof(F32);
  88. m_randFactorsBuff = m_node->getSceneGraph().getGrManager().newBuffer(buffInit);
  89. F32* randFactors = static_cast<F32*>(m_randFactorsBuff->map(0, MAX_PTR_SIZE, BufferMapAccessBit::WRITE));
  90. *reinterpret_cast<U32*>(randFactors) = MAX_RAND_FACTORS;
  91. ++randFactors;
  92. const F32* randFactorsEnd = randFactors + MAX_RAND_FACTORS;
  93. for(; randFactors < randFactorsEnd; ++randFactors)
  94. {
  95. *randFactors = getRandomRange(0.0f, 1.0f);
  96. }
  97. m_randFactorsBuff->flush(0, MAX_PTR_SIZE);
  98. m_randFactorsBuff->unmap();
  99. }
  100. // Create the sampler
  101. {
  102. SamplerInitInfo sinit("GpuParticles");
  103. sinit.m_addressing = SamplingAddressing::CLAMP;
  104. m_nearestAnyClampSampler = m_node->getSceneGraph().getGrManager().newSampler(sinit);
  105. }
  106. // Find the extend of the particles
  107. if(inProps.m_emitterBoundingVolumeMin.x() >= inProps.m_emitterBoundingVolumeMax.x())
  108. {
  109. const Vec3 maxForce = inProps.m_particle.m_maxForceDirection * inProps.m_particle.m_maxForceMagnitude;
  110. const Vec3& maxGravity = inProps.m_particle.m_maxGravity;
  111. const F32 maxMass = inProps.m_particle.m_maxMass;
  112. const F32 maxLife = F32(inProps.m_particle.m_maxLife);
  113. const F32 dt = 1.0f / 30.0f;
  114. const Vec3 initialForce = maxGravity * maxMass + maxForce;
  115. const Vec3 initialAcceleration = initialForce / maxMass;
  116. const Vec3 velocity = initialAcceleration * dt;
  117. F32 life = maxLife;
  118. Vec3 pos(0.0f);
  119. while(life > dt)
  120. {
  121. pos = maxGravity * (dt * dt) + velocity * dt + pos;
  122. life -= dt;
  123. }
  124. m_emitterBoundingBoxLocal = Aabb(-Vec3(pos.getLength()), Vec3(pos.getLength()));
  125. }
  126. else
  127. {
  128. m_emitterBoundingBoxLocal = Aabb(inProps.m_emitterBoundingVolumeMin, inProps.m_emitterBoundingVolumeMax);
  129. }
  130. return Error::NONE;
  131. }
  132. void GpuParticleEmitterComponent::simulate(GenericGpuComputeJobQueueElementContext& ctx) const
  133. {
  134. if(ANKI_UNLIKELY(!m_particleEmitterResource.isCreated()))
  135. {
  136. return;
  137. }
  138. CommandBufferPtr& cmdb = ctx.m_commandBuffer;
  139. cmdb->bindShaderProgram(m_grProg);
  140. // Bind resources
  141. cmdb->bindStorageBuffer(1, 0, m_particlesBuff, 0, MAX_PTR_SIZE);
  142. cmdb->bindUniformBuffer(1, 1, m_propsBuff, 0, MAX_PTR_SIZE);
  143. cmdb->bindStorageBuffer(1, 2, m_randFactorsBuff, 0, MAX_PTR_SIZE);
  144. cmdb->bindSampler(1, 3, m_nearestAnyClampSampler);
  145. StagingGpuMemoryToken token;
  146. GpuParticleSimulationState* unis =
  147. static_cast<GpuParticleSimulationState*>(ctx.m_stagingGpuAllocator->allocateFrame(
  148. sizeof(GpuParticleSimulationState), StagingGpuMemoryType::UNIFORM, token));
  149. unis->m_viewProjMat = ctx.m_viewProjectionMatrix;
  150. unis->m_unprojectionParams = ctx.m_projectionMatrix.extractPerspectiveUnprojectionParams();
  151. unis->m_randomIndex = rand();
  152. unis->m_dt = F32(m_dt);
  153. unis->m_emitterPosition = m_worldPosition;
  154. unis->m_emitterRotation = m_worldRotation;
  155. unis->m_invViewRotation = Mat3x4(Vec3(0.0f), ctx.m_cameraTransform.getRotationPart());
  156. cmdb->bindUniformBuffer(1, 4, token.m_buffer, token.m_offset, token.m_range);
  157. // Dispatch
  158. const U32 workgroupCount = (m_maxParticleCount + m_workgroupSizeX - 1) / m_workgroupSizeX;
  159. cmdb->dispatchCompute(workgroupCount, 1, 1);
  160. }
  161. void GpuParticleEmitterComponent::draw(RenderQueueDrawContext& ctx) const
  162. {
  163. if(ANKI_UNLIKELY(!m_particleEmitterResource.isCreated()))
  164. {
  165. return;
  166. }
  167. CommandBufferPtr& cmdb = ctx.m_commandBuffer;
  168. if(!ctx.m_debugDraw)
  169. {
  170. // Program
  171. ShaderProgramPtr prog;
  172. m_particleEmitterResource->getRenderingInfo(ctx.m_key, prog);
  173. cmdb->bindShaderProgram(prog);
  174. // Resources
  175. static const Mat4 identity = Mat4::getIdentity();
  176. RenderComponent::allocateAndSetupUniforms(m_particleEmitterResource->getMaterial(), ctx,
  177. ConstWeakArray<Mat4>(&identity, 1),
  178. ConstWeakArray<Mat4>(&identity, 1), *ctx.m_stagingGpuAllocator);
  179. cmdb->bindStorageBuffer(0, 1, m_particlesBuff, 0, MAX_PTR_SIZE);
  180. StagingGpuMemoryToken token;
  181. Vec4* extraUniforms = static_cast<Vec4*>(
  182. ctx.m_stagingGpuAllocator->allocateFrame(sizeof(Vec4), StagingGpuMemoryType::UNIFORM, token));
  183. *extraUniforms = ctx.m_cameraTransform.getColumn(2);
  184. cmdb->bindUniformBuffer(0, 2, token.m_buffer, token.m_offset, token.m_range);
  185. // Draw
  186. cmdb->setLineWidth(8.0f);
  187. cmdb->drawArrays(PrimitiveTopology::LINES, m_maxParticleCount * 2);
  188. }
  189. else
  190. {
  191. const Vec4 tsl = (m_worldAabb.getMin() + m_worldAabb.getMax()) / 2.0f;
  192. const Vec4 scale = (m_worldAabb.getMax() - m_worldAabb.getMin()) / 2.0f;
  193. // Set non uniform scale. Add a margin to avoid flickering
  194. Mat3 nonUniScale = Mat3::getZero();
  195. nonUniScale(0, 0) = scale.x();
  196. nonUniScale(1, 1) = scale.y();
  197. nonUniScale(2, 2) = scale.z();
  198. const Mat4 mvp = ctx.m_viewProjectionMatrix * Mat4(tsl.xyz1(), Mat3::getIdentity() * nonUniScale, 1.0f);
  199. const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DEPTH_TEST_ON);
  200. if(enableDepthTest)
  201. {
  202. cmdb->setDepthCompareOperation(CompareOperation::LESS);
  203. }
  204. else
  205. {
  206. cmdb->setDepthCompareOperation(CompareOperation::ALWAYS);
  207. }
  208. m_node->getSceneGraph().getDebugDrawer().drawCubes(
  209. ConstWeakArray<Mat4>(&mvp, 1), Vec4(1.0f, 0.0f, 1.0f, 1.0f), 2.0f,
  210. ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), 2.0f,
  211. *ctx.m_stagingGpuAllocator, cmdb);
  212. m_node->getSceneGraph().getDebugDrawer().drawBillboardTextures(
  213. ctx.m_projectionMatrix, ctx.m_viewMatrix, ConstWeakArray<Vec3>(&m_worldPosition, 1), Vec4(1.0f),
  214. ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), m_dbgImage->getTextureView(),
  215. ctx.m_sampler, Vec2(0.75f), *ctx.m_stagingGpuAllocator, ctx.m_commandBuffer);
  216. // Restore state
  217. if(!enableDepthTest)
  218. {
  219. cmdb->setDepthCompareOperation(CompareOperation::LESS);
  220. }
  221. }
  222. }
  223. Error GpuParticleEmitterComponent::update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated)
  224. {
  225. if(ANKI_UNLIKELY(!m_particleEmitterResource.isCreated()))
  226. {
  227. updated = false;
  228. return Error::NONE;
  229. }
  230. updated = m_markedForUpdate;
  231. m_markedForUpdate = false;
  232. m_dt = crntTime - prevTime;
  233. return Error::NONE;
  234. }
  235. void GpuParticleEmitterComponent::setWorldTransform(const Transform& trf)
  236. {
  237. m_worldPosition = trf.getOrigin().xyz();
  238. m_worldRotation = trf.getRotation();
  239. m_worldAabb.setMin(m_worldPosition + m_emitterBoundingBoxLocal.getMin().xyz());
  240. m_worldAabb.setMax(m_worldPosition + m_emitterBoundingBoxLocal.getMax().xyz());
  241. m_markedForUpdate = true;
  242. }
  243. } // end namespace anki