GpuParticleEmitterComponent.cpp 9.7 KB

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