GpuParticleEmitterComponent.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  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->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->unmap();
  79. }
  80. // Create the rand buffer
  81. {
  82. BufferInitInfo buffInit("GpuParticlesRand");
  83. buffInit.m_mapAccess = BufferMapAccessBit::WRITE;
  84. buffInit.m_usage = BufferUsageBit::ALL_STORAGE;
  85. buffInit.m_size = sizeof(U32) + MAX_RAND_FACTORS * sizeof(F32);
  86. m_randFactorsBuff = m_node->getSceneGraph().getGrManager().newBuffer(buffInit);
  87. F32* randFactors = static_cast<F32*>(m_randFactorsBuff->map(0, MAX_PTR_SIZE, BufferMapAccessBit::WRITE));
  88. *reinterpret_cast<U32*>(randFactors) = MAX_RAND_FACTORS;
  89. ++randFactors;
  90. const F32* randFactorsEnd = randFactors + MAX_RAND_FACTORS;
  91. for(; randFactors < randFactorsEnd; ++randFactors)
  92. {
  93. *randFactors = getRandomRange(0.0f, 1.0f);
  94. }
  95. m_randFactorsBuff->unmap();
  96. }
  97. // Create the sampler
  98. {
  99. SamplerInitInfo sinit("GpuParticles");
  100. sinit.m_addressing = SamplingAddressing::CLAMP;
  101. m_nearestAnyClampSampler = m_node->getSceneGraph().getGrManager().newSampler(sinit);
  102. }
  103. // Find the extend of the particles
  104. if(inProps.m_emitterBoundingVolumeMin.x() >= inProps.m_emitterBoundingVolumeMax.x())
  105. {
  106. const Vec3 maxForce = inProps.m_particle.m_maxForceDirection * inProps.m_particle.m_maxForceMagnitude;
  107. const Vec3& maxGravity = inProps.m_particle.m_maxGravity;
  108. const F32 maxMass = inProps.m_particle.m_maxMass;
  109. const F32 maxLife = F32(inProps.m_particle.m_maxLife);
  110. const F32 dt = 1.0f / 30.0f;
  111. const Vec3 initialForce = maxGravity * maxMass + maxForce;
  112. const Vec3 initialAcceleration = initialForce / maxMass;
  113. const Vec3 velocity = initialAcceleration * dt;
  114. F32 life = maxLife;
  115. Vec3 pos(0.0f);
  116. while(life > dt)
  117. {
  118. pos = maxGravity * (dt * dt) + velocity * dt + pos;
  119. life -= dt;
  120. }
  121. m_emitterBoundingBoxLocal = Aabb(-Vec3(pos.getLength()), Vec3(pos.getLength()));
  122. }
  123. else
  124. {
  125. m_emitterBoundingBoxLocal = Aabb(inProps.m_emitterBoundingVolumeMin, inProps.m_emitterBoundingVolumeMax);
  126. }
  127. return Error::NONE;
  128. }
  129. void GpuParticleEmitterComponent::simulate(GenericGpuComputeJobQueueElementContext& ctx) const
  130. {
  131. if(ANKI_UNLIKELY(!m_particleEmitterResource.isCreated()))
  132. {
  133. return;
  134. }
  135. CommandBufferPtr& cmdb = ctx.m_commandBuffer;
  136. cmdb->bindShaderProgram(m_grProg);
  137. // Bind resources
  138. cmdb->bindStorageBuffer(1, 0, m_particlesBuff, 0, MAX_PTR_SIZE);
  139. cmdb->bindUniformBuffer(1, 1, m_propsBuff, 0, MAX_PTR_SIZE);
  140. cmdb->bindStorageBuffer(1, 2, m_randFactorsBuff, 0, MAX_PTR_SIZE);
  141. cmdb->bindSampler(1, 3, m_nearestAnyClampSampler);
  142. StagingGpuMemoryToken token;
  143. GpuParticleSimulationState* unis =
  144. static_cast<GpuParticleSimulationState*>(ctx.m_stagingGpuAllocator->allocateFrame(
  145. sizeof(GpuParticleSimulationState), StagingGpuMemoryType::UNIFORM, token));
  146. unis->m_viewProjMat = ctx.m_viewProjectionMatrix;
  147. unis->m_unprojectionParams = ctx.m_projectionMatrix.extractPerspectiveUnprojectionParams();
  148. unis->m_randomIndex = rand();
  149. unis->m_dt = F32(m_dt);
  150. unis->m_emitterPosition = m_worldPosition;
  151. unis->m_emitterRotation = m_worldRotation;
  152. unis->m_invViewRotation = Mat3x4(Vec3(0.0f), ctx.m_cameraTransform.getRotationPart());
  153. cmdb->bindUniformBuffer(1, 4, token.m_buffer, token.m_offset, token.m_range);
  154. // Dispatch
  155. const U32 workgroupCount = (m_maxParticleCount + m_workgroupSizeX - 1) / m_workgroupSizeX;
  156. cmdb->dispatchCompute(workgroupCount, 1, 1);
  157. }
  158. void GpuParticleEmitterComponent::draw(RenderQueueDrawContext& ctx) const
  159. {
  160. if(ANKI_UNLIKELY(!m_particleEmitterResource.isCreated()))
  161. {
  162. return;
  163. }
  164. CommandBufferPtr& cmdb = ctx.m_commandBuffer;
  165. if(!ctx.m_debugDraw)
  166. {
  167. // Program
  168. ShaderProgramPtr prog;
  169. m_particleEmitterResource->getRenderingInfo(ctx.m_key, prog);
  170. cmdb->bindShaderProgram(prog);
  171. // Resources
  172. static const Mat4 identity = Mat4::getIdentity();
  173. RenderComponent::allocateAndSetupUniforms(m_particleEmitterResource->getMaterial(), ctx,
  174. ConstWeakArray<Mat4>(&identity, 1),
  175. ConstWeakArray<Mat4>(&identity, 1), *ctx.m_stagingGpuAllocator);
  176. cmdb->bindStorageBuffer(0, 1, m_particlesBuff, 0, MAX_PTR_SIZE);
  177. StagingGpuMemoryToken token;
  178. Vec4* extraUniforms = static_cast<Vec4*>(
  179. ctx.m_stagingGpuAllocator->allocateFrame(sizeof(Vec4), StagingGpuMemoryType::UNIFORM, token));
  180. *extraUniforms = ctx.m_cameraTransform.getColumn(2);
  181. cmdb->bindUniformBuffer(0, 2, token.m_buffer, token.m_offset, token.m_range);
  182. // Draw
  183. cmdb->setLineWidth(8.0f);
  184. cmdb->drawArrays(PrimitiveTopology::LINES, m_maxParticleCount * 2);
  185. }
  186. else
  187. {
  188. const Vec4 tsl = (m_worldAabb.getMin() + m_worldAabb.getMax()) / 2.0f;
  189. const Vec4 scale = (m_worldAabb.getMax() - m_worldAabb.getMin()) / 2.0f;
  190. // Set non uniform scale. Add a margin to avoid flickering
  191. Mat3 nonUniScale = Mat3::getZero();
  192. nonUniScale(0, 0) = scale.x();
  193. nonUniScale(1, 1) = scale.y();
  194. nonUniScale(2, 2) = scale.z();
  195. const Mat4 mvp = ctx.m_viewProjectionMatrix * Mat4(tsl.xyz1(), Mat3::getIdentity() * nonUniScale, 1.0f);
  196. const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DEPTH_TEST_ON);
  197. if(enableDepthTest)
  198. {
  199. cmdb->setDepthCompareOperation(CompareOperation::LESS);
  200. }
  201. else
  202. {
  203. cmdb->setDepthCompareOperation(CompareOperation::ALWAYS);
  204. }
  205. m_node->getSceneGraph().getDebugDrawer().drawCubes(
  206. ConstWeakArray<Mat4>(&mvp, 1), Vec4(1.0f, 0.0f, 1.0f, 1.0f), 2.0f,
  207. ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), 2.0f,
  208. *ctx.m_stagingGpuAllocator, cmdb);
  209. m_node->getSceneGraph().getDebugDrawer().drawBillboardTextures(
  210. ctx.m_projectionMatrix, ctx.m_viewMatrix, ConstWeakArray<Vec3>(&m_worldPosition, 1), Vec4(1.0f),
  211. ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), m_dbgImage->getTextureView(),
  212. ctx.m_sampler, Vec2(0.75f), *ctx.m_stagingGpuAllocator, ctx.m_commandBuffer);
  213. // Restore state
  214. if(!enableDepthTest)
  215. {
  216. cmdb->setDepthCompareOperation(CompareOperation::LESS);
  217. }
  218. }
  219. }
  220. Error GpuParticleEmitterComponent::update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated)
  221. {
  222. if(ANKI_UNLIKELY(!m_particleEmitterResource.isCreated()))
  223. {
  224. updated = false;
  225. return Error::NONE;
  226. }
  227. updated = m_markedForUpdate;
  228. m_markedForUpdate = false;
  229. m_dt = crntTime - prevTime;
  230. return Error::NONE;
  231. }
  232. void GpuParticleEmitterComponent::setWorldTransform(const Transform& trf)
  233. {
  234. m_worldPosition = trf.getOrigin().xyz();
  235. m_worldRotation = trf.getRotation();
  236. m_worldAabb.setMin(m_worldPosition + m_emitterBoundingBoxLocal.getMin().xyz());
  237. m_worldAabb.setMax(m_worldPosition + m_emitterBoundingBoxLocal.getMax().xyz());
  238. m_markedForUpdate = true;
  239. }
  240. } // end namespace anki