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: 0 77 0.0 %
Date: 2022-03-08 13:03:03 Functions: 0 8 0.0 %

          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           0 : 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           0 : ParticleRenderer::ParticleRenderer()
      44             : {
      45           0 :     m = new ParticleRendererInternals();
      46           0 :     m->frameNumber = 0;
      47           0 : }
      48             : 
      49           0 : ParticleRenderer::~ParticleRenderer()
      50             : {
      51           0 :     delete m;
      52           0 : }
      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 (int cullGroup = 0; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
      62           0 :         m->emitters[cullGroup].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             :             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 (int cullGroup = 0; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
     107             :     {
     108           0 :         PROFILE("update emitters");
     109           0 :         for (size_t i = 0; i < m->emitters[cullGroup].size(); ++i)
     110             :         {
     111           0 :             CParticleEmitter* emitter = m->emitters[cullGroup][i];
     112           0 :             emitter->UpdateArrayData(m->frameNumber);
     113           0 :             emitter->PrepareForRendering();
     114             :         }
     115             :     }
     116             : 
     117           0 :     for (int cullGroup = 0; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
     118             :     {
     119             :         // Sort back-to-front by distance from camera
     120           0 :         PROFILE("sort emitters");
     121           0 :         CMatrix3D worldToCam;
     122           0 :         g_Renderer.GetSceneRenderer().GetViewCamera().GetOrientation().GetInverse(worldToCam);
     123           0 :         std::stable_sort(m->emitters[cullGroup].begin(), m->emitters[cullGroup].end(), SortEmitterDistance(worldToCam));
     124             :     }
     125             : 
     126             :     // TODO: should batch by texture here when possible, maybe
     127           0 : }
     128             : 
     129           0 : void ParticleRenderer::RenderParticles(
     130             :     Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
     131             :     int cullGroup, bool wireframe)
     132             : {
     133           0 :     CShaderTechnique* lastTech = nullptr;
     134           0 :     for (CParticleEmitter* emitter : m->emitters[cullGroup])
     135             :     {
     136           0 :         CShaderTechnique* currentTech = nullptr;
     137           0 :         if (wireframe)
     138             :         {
     139           0 :             currentTech = m->techWireframe.get();
     140             :         }
     141             :         else
     142             :         {
     143           0 :             switch (emitter->m_Type->m_BlendMode)
     144             :             {
     145           0 :             case CParticleEmitterType::BlendMode::ADD: currentTech = m->techAdd.get(); break;
     146           0 :             case CParticleEmitterType::BlendMode::SUBTRACT: currentTech = m->techSubtract.get(); break;
     147           0 :             case CParticleEmitterType::BlendMode::OVERLAY: currentTech = m->techOverlay.get(); break;
     148           0 :             case CParticleEmitterType::BlendMode::MULTIPLY: currentTech = m->techMultiply.get(); break;
     149             :             }
     150             :         }
     151           0 :         ENSURE(currentTech);
     152           0 :         if (lastTech != currentTech)
     153             :         {
     154           0 :             lastTech = currentTech;
     155           0 :             lastTech->BeginPass();
     156           0 :             deviceCommandContext->SetGraphicsPipelineState(lastTech->GetGraphicsPipelineStateDesc());
     157             : 
     158           0 :             const CShaderProgramPtr& shader = lastTech->GetShader();
     159           0 :             shader->Uniform(str_transform, g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection());
     160           0 :             shader->Uniform(str_modelViewMatrix, g_Renderer.GetSceneRenderer().GetViewCamera().GetOrientation().GetInverse());
     161             :         }
     162           0 :         emitter->Bind(deviceCommandContext, lastTech->GetShader());
     163           0 :         emitter->RenderArray(deviceCommandContext, lastTech->GetShader());
     164             :     }
     165             : 
     166           0 :     if (lastTech)
     167           0 :         lastTech->EndPass();
     168             : 
     169           0 :     CVertexBuffer::Unbind(deviceCommandContext);
     170           0 : }
     171             : 
     172           0 : void ParticleRenderer::RenderBounds(int cullGroup)
     173             : {
     174           0 :     for (const CParticleEmitter* emitter : m->emitters[cullGroup])
     175             :     {
     176           0 :         const CBoundingBoxAligned bounds =
     177           0 :             emitter->m_Type->CalculateBounds(emitter->GetPosition(), emitter->GetParticleBounds());
     178           0 :         g_Renderer.GetDebugRenderer().DrawBoundingBox(bounds, CColor(0.0f, 1.0f, 0.0f, 1.0f), true);
     179             :     }
     180           0 : }

Generated by: LCOV version 1.13