LCOV - code coverage report
Current view: top level - source/renderer - ParticleRenderer.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 9 86 10.5 %
Date: 2023-01-19 00:18:29 Functions: 6 14 42.9 %

          Line data    Source code
       1             : /* Copyright (C) 2022 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "ParticleRenderer.h"
      21             : 
      22             : #include "graphics/ParticleEmitter.h"
      23             : #include "graphics/ShaderDefines.h"
      24             : #include "graphics/ShaderManager.h"
      25             : #include "graphics/TextureManager.h"
      26             : #include "ps/CStrInternStatic.h"
      27             : #include "ps/Profile.h"
      28             : #include "renderer/DebugRenderer.h"
      29             : #include "renderer/Renderer.h"
      30             : #include "renderer/SceneRenderer.h"
      31             : 
      32          12 : struct ParticleRendererInternals
      33             : {
      34             :     int frameNumber;
      35             :     CShaderTechniquePtr techAdd;
      36             :     CShaderTechniquePtr techSubtract;
      37             :     CShaderTechniquePtr techOverlay;
      38             :     CShaderTechniquePtr techMultiply;
      39             :     CShaderTechniquePtr techWireframe;
      40             :     std::vector<CParticleEmitter*> emitters[CSceneRenderer::CULL_MAX];
      41             : };
      42             : 
      43           6 : ParticleRenderer::ParticleRenderer()
      44             : {
      45           6 :     m = new ParticleRendererInternals();
      46           6 :     m->frameNumber = 0;
      47           6 : }
      48             : 
      49          12 : ParticleRenderer::~ParticleRenderer()
      50             : {
      51           6 :     delete m;
      52           6 : }
      53             : 
      54           0 : void ParticleRenderer::Submit(int cullGroup, CParticleEmitter* emitter)
      55             : {
      56           0 :     m->emitters[cullGroup].push_back(emitter);
      57           0 : }
      58             : 
      59           0 : void ParticleRenderer::EndFrame()
      60             : {
      61           0 :     for (std::vector<CParticleEmitter*>& cullGroupEmitters : m->emitters)
      62           0 :         cullGroupEmitters.clear();
      63             :     // this should leave the capacity unchanged, which is okay since it
      64             :     // won't be very large or very variable
      65           0 : }
      66             : 
      67             : struct SortEmitterDistance
      68             : {
      69           0 :     SortEmitterDistance(const CMatrix3D& m) : worldToCam(m) { }
      70             : 
      71             :     // TODO: if this is slow, we should pre-compute the distance for each emitter
      72             : 
      73           0 :     bool operator()(CParticleEmitter* const& a, CParticleEmitter* const& b)
      74             :     {
      75           0 :         CVector3D posa = a->GetPosition();
      76           0 :         CVector3D posb = b->GetPosition();
      77           0 :         if (posa == posb)
      78           0 :             return false;
      79           0 :         float dista = worldToCam.Transform(posa).LengthSquared();
      80           0 :         float distb = worldToCam.Transform(posb).LengthSquared();
      81           0 :         return distb < dista;
      82             :     }
      83             : 
      84             :     CMatrix3D worldToCam;
      85             : };
      86             : 
      87           0 : void ParticleRenderer::PrepareForRendering(const CShaderDefines& context)
      88             : {
      89           0 :     PROFILE3("prepare particles");
      90             : 
      91             :     // Can't load the shader in the constructor because it's called before the
      92             :     // renderer initialisation is complete, so load it the first time through here
      93           0 :     if (!m->techWireframe)
      94             :     {
      95           0 :         m->techAdd = g_Renderer.GetShaderManager().LoadEffect(str_particle_add, context);
      96           0 :         m->techSubtract = g_Renderer.GetShaderManager().LoadEffect(str_particle_subtract, context);
      97           0 :         m->techOverlay = g_Renderer.GetShaderManager().LoadEffect(str_particle_overlay, context);
      98           0 :         m->techMultiply = g_Renderer.GetShaderManager().LoadEffect(str_particle_multiply, context);
      99           0 :         CShaderDefines contextWithWireframe = context;
     100           0 :         contextWithWireframe.Add(str_MODE_WIREFRAME, str_1);
     101           0 :         m->techWireframe = g_Renderer.GetShaderManager().LoadEffect(str_particle_solid, contextWithWireframe);
     102             :     }
     103             : 
     104           0 :     ++m->frameNumber;
     105             : 
     106           0 :     for (std::vector<CParticleEmitter*>& cullGroupEmitters : m->emitters)
     107             :     {
     108           0 :         PROFILE("update emitters");
     109           0 :         for (CParticleEmitter* emitter : cullGroupEmitters)
     110             :         {
     111           0 :             emitter->UpdateArrayData(m->frameNumber);
     112           0 :             emitter->PrepareForRendering();
     113             :         }
     114             :     }
     115             : 
     116           0 :     CMatrix3D worldToCamera;
     117           0 :     g_Renderer.GetSceneRenderer().GetViewCamera().GetOrientation().GetInverse(worldToCamera);
     118           0 :     for (std::vector<CParticleEmitter*>& cullGroupEmitters : m->emitters)
     119             :     {
     120             :         // Sort back-to-front by distance from camera
     121           0 :         PROFILE("sort emitters");
     122           0 :         std::stable_sort(cullGroupEmitters.begin(), cullGroupEmitters.end(), SortEmitterDistance(worldToCamera));
     123             :     }
     124             : 
     125             :     // TODO: should batch by texture here when possible, maybe
     126           0 : }
     127             : 
     128           0 : void ParticleRenderer::Upload(
     129             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     130             : {
     131           0 :     for (std::vector<CParticleEmitter*>& cullGroupEmitters : m->emitters)
     132           0 :         for (CParticleEmitter* emitter : cullGroupEmitters)
     133           0 :             emitter->UploadData(deviceCommandContext);
     134           0 : }
     135             : 
     136           0 : void ParticleRenderer::RenderParticles(
     137             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     138             :     int cullGroup, bool wireframe)
     139             : {
     140           0 :     CShaderTechnique* lastTech = nullptr;
     141           0 :     for (CParticleEmitter* emitter : m->emitters[cullGroup])
     142             :     {
     143           0 :         CShaderTechnique* currentTech = nullptr;
     144           0 :         if (wireframe)
     145             :         {
     146           0 :             currentTech = m->techWireframe.get();
     147             :         }
     148             :         else
     149             :         {
     150           0 :             switch (emitter->m_Type->m_BlendMode)
     151             :             {
     152           0 :             case CParticleEmitterType::BlendMode::ADD: currentTech = m->techAdd.get(); break;
     153           0 :             case CParticleEmitterType::BlendMode::SUBTRACT: currentTech = m->techSubtract.get(); break;
     154           0 :             case CParticleEmitterType::BlendMode::OVERLAY: currentTech = m->techOverlay.get(); break;
     155           0 :             case CParticleEmitterType::BlendMode::MULTIPLY: currentTech = m->techMultiply.get(); break;
     156             :             }
     157             :         }
     158           0 :         ENSURE(currentTech);
     159           0 :         if (lastTech != currentTech)
     160             :         {
     161           0 :             if (lastTech)
     162           0 :                 deviceCommandContext->EndPass();
     163           0 :             lastTech = currentTech;
     164           0 :             deviceCommandContext->SetGraphicsPipelineState(lastTech->GetGraphicsPipelineState());
     165           0 :             deviceCommandContext->BeginPass();
     166             : 
     167           0 :             Renderer::Backend::IShaderProgram* shader = lastTech->GetShader();
     168             :             const CMatrix3D transform =
     169           0 :                 g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
     170             :             const CMatrix3D modelViewMatrix =
     171           0 :                 g_Renderer.GetSceneRenderer().GetViewCamera().GetOrientation().GetInverse();
     172           0 :             deviceCommandContext->SetUniform(
     173           0 :                 shader->GetBindingSlot(str_transform), transform.AsFloatArray());
     174           0 :             deviceCommandContext->SetUniform(
     175           0 :                 shader->GetBindingSlot(str_modelViewMatrix), modelViewMatrix.AsFloatArray());
     176             :         }
     177           0 :         emitter->Bind(deviceCommandContext, lastTech->GetShader());
     178           0 :         emitter->RenderArray(deviceCommandContext);
     179             :     }
     180             : 
     181           0 :     if (lastTech)
     182           0 :         deviceCommandContext->EndPass();
     183           0 : }
     184             : 
     185           0 : void ParticleRenderer::RenderBounds(int cullGroup)
     186             : {
     187           0 :     for (const CParticleEmitter* emitter : m->emitters[cullGroup])
     188             :     {
     189             :         const CBoundingBoxAligned bounds =
     190           0 :             emitter->m_Type->CalculateBounds(emitter->GetPosition(), emitter->GetParticleBounds());
     191           0 :         g_Renderer.GetDebugRenderer().DrawBoundingBox(bounds, CColor(0.0f, 1.0f, 0.0f, 1.0f), true);
     192             :     }
     193           3 : }

Generated by: LCOV version 1.13