1 /* Copyright (C) 2022 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #include "precompiled.h"
20 #include "ParticleRenderer.h"
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"
32 struct ParticleRendererInternals
35 CShaderTechniquePtr techAdd
;
36 CShaderTechniquePtr techSubtract
;
37 CShaderTechniquePtr techOverlay
;
38 CShaderTechniquePtr techMultiply
;
39 CShaderTechniquePtr techWireframe
;
40 std::vector
<CParticleEmitter
*> emitters
[CSceneRenderer::CULL_MAX
];
43 ParticleRenderer::ParticleRenderer()
45 m
= new ParticleRendererInternals();
49 ParticleRenderer::~ParticleRenderer()
54 void ParticleRenderer::Submit(int cullGroup
, CParticleEmitter
* emitter
)
56 m
->emitters
[cullGroup
].push_back(emitter
);
59 void ParticleRenderer::EndFrame()
61 for (std::vector
<CParticleEmitter
*>& cullGroupEmitters
: m
->emitters
)
62 cullGroupEmitters
.clear();
63 // this should leave the capacity unchanged, which is okay since it
64 // won't be very large or very variable
67 struct SortEmitterDistance
69 SortEmitterDistance(const CMatrix3D
& m
) : worldToCam(m
) { }
71 // TODO: if this is slow, we should pre-compute the distance for each emitter
73 bool operator()(CParticleEmitter
* const& a
, CParticleEmitter
* const& b
)
75 CVector3D posa
= a
->GetPosition();
76 CVector3D posb
= b
->GetPosition();
79 float dista
= worldToCam
.Transform(posa
).LengthSquared();
80 float distb
= worldToCam
.Transform(posb
).LengthSquared();
87 void ParticleRenderer::PrepareForRendering(const CShaderDefines
& context
)
89 PROFILE3("prepare particles");
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 if (!m
->techWireframe
)
95 m
->techAdd
= g_Renderer
.GetShaderManager().LoadEffect(str_particle_add
, context
);
96 m
->techSubtract
= g_Renderer
.GetShaderManager().LoadEffect(str_particle_subtract
, context
);
97 m
->techOverlay
= g_Renderer
.GetShaderManager().LoadEffect(str_particle_overlay
, context
);
98 m
->techMultiply
= g_Renderer
.GetShaderManager().LoadEffect(str_particle_multiply
, context
);
99 CShaderDefines contextWithWireframe
= context
;
100 contextWithWireframe
.Add(str_MODE_WIREFRAME
, str_1
);
101 m
->techWireframe
= g_Renderer
.GetShaderManager().LoadEffect(str_particle_solid
, contextWithWireframe
);
106 for (std::vector
<CParticleEmitter
*>& cullGroupEmitters
: m
->emitters
)
108 PROFILE("update emitters");
109 for (CParticleEmitter
* emitter
: cullGroupEmitters
)
111 emitter
->UpdateArrayData(m
->frameNumber
);
112 emitter
->PrepareForRendering();
116 CMatrix3D worldToCamera
;
117 g_Renderer
.GetSceneRenderer().GetViewCamera().GetOrientation().GetInverse(worldToCamera
);
118 for (std::vector
<CParticleEmitter
*>& cullGroupEmitters
: m
->emitters
)
120 // Sort back-to-front by distance from camera
121 PROFILE("sort emitters");
122 std::stable_sort(cullGroupEmitters
.begin(), cullGroupEmitters
.end(), SortEmitterDistance(worldToCamera
));
125 // TODO: should batch by texture here when possible, maybe
128 void ParticleRenderer::Upload(
129 Renderer::Backend::IDeviceCommandContext
* deviceCommandContext
)
131 for (std::vector
<CParticleEmitter
*>& cullGroupEmitters
: m
->emitters
)
132 for (CParticleEmitter
* emitter
: cullGroupEmitters
)
133 emitter
->UploadData(deviceCommandContext
);
136 void ParticleRenderer::RenderParticles(
137 Renderer::Backend::IDeviceCommandContext
* deviceCommandContext
,
138 int cullGroup
, bool wireframe
)
140 CShaderTechnique
* lastTech
= nullptr;
141 for (CParticleEmitter
* emitter
: m
->emitters
[cullGroup
])
143 CShaderTechnique
* currentTech
= nullptr;
146 currentTech
= m
->techWireframe
.get();
150 switch (emitter
->m_Type
->m_BlendMode
)
152 case CParticleEmitterType::BlendMode::ADD
: currentTech
= m
->techAdd
.get(); break;
153 case CParticleEmitterType::BlendMode::SUBTRACT
: currentTech
= m
->techSubtract
.get(); break;
154 case CParticleEmitterType::BlendMode::OVERLAY
: currentTech
= m
->techOverlay
.get(); break;
155 case CParticleEmitterType::BlendMode::MULTIPLY
: currentTech
= m
->techMultiply
.get(); break;
159 if (lastTech
!= currentTech
)
162 deviceCommandContext
->EndPass();
163 lastTech
= currentTech
;
164 deviceCommandContext
->SetGraphicsPipelineState(lastTech
->GetGraphicsPipelineState());
165 deviceCommandContext
->BeginPass();
167 Renderer::Backend::IShaderProgram
* shader
= lastTech
->GetShader();
168 const CMatrix3D transform
=
169 g_Renderer
.GetSceneRenderer().GetViewCamera().GetViewProjection();
170 const CMatrix3D modelViewMatrix
=
171 g_Renderer
.GetSceneRenderer().GetViewCamera().GetOrientation().GetInverse();
172 deviceCommandContext
->SetUniform(
173 shader
->GetBindingSlot(str_transform
), transform
.AsFloatArray());
174 deviceCommandContext
->SetUniform(
175 shader
->GetBindingSlot(str_modelViewMatrix
), modelViewMatrix
.AsFloatArray());
177 emitter
->Bind(deviceCommandContext
, lastTech
->GetShader());
178 emitter
->RenderArray(deviceCommandContext
);
182 deviceCommandContext
->EndPass();
185 void ParticleRenderer::RenderBounds(int cullGroup
)
187 for (const CParticleEmitter
* emitter
: m
->emitters
[cullGroup
])
189 const CBoundingBoxAligned bounds
=
190 emitter
->m_Type
->CalculateBounds(emitter
->GetPosition(), emitter
->GetParticleBounds());
191 g_Renderer
.GetDebugRenderer().DrawBoundingBox(bounds
, CColor(0.0f
, 1.0f
, 0.0f
, 1.0f
), true);