1 /* Copyright (C) 2011 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/Profile.h"
27 #include "renderer/Renderer.h"
29 struct ParticleRendererInternals
32 CShaderTechniquePtr shader
;
33 CShaderTechniquePtr shaderSolid
;
34 std::vector
<CParticleEmitter
*> emitters
[CRenderer::CULL_MAX
];
37 ParticleRenderer::ParticleRenderer()
39 m
= new ParticleRendererInternals();
43 ParticleRenderer::~ParticleRenderer()
48 void ParticleRenderer::Submit(int cullGroup
, CParticleEmitter
* emitter
)
50 m
->emitters
[cullGroup
].push_back(emitter
);
53 void ParticleRenderer::EndFrame()
55 for (int cullGroup
= 0; cullGroup
< CRenderer::CULL_MAX
; ++cullGroup
)
56 m
->emitters
[cullGroup
].clear();
57 // this should leave the capacity unchanged, which is okay since it
58 // won't be very large or very variable
61 struct SortEmitterDistance
63 SortEmitterDistance(const CMatrix3D
& m
) : worldToCam(m
) { }
65 // TODO: if this is slow, we should pre-compute the distance for each emitter
67 bool operator()(CParticleEmitter
* const& a
, CParticleEmitter
* const& b
)
69 CVector3D posa
= a
->GetPosition();
70 CVector3D posb
= b
->GetPosition();
73 float dista
= worldToCam
.Transform(posa
).LengthSquared();
74 float distb
= worldToCam
.Transform(posb
).LengthSquared();
81 void ParticleRenderer::PrepareForRendering(const CShaderDefines
& context
)
83 PROFILE3("prepare particles");
85 // Can't load the shader in the constructor because it's called before the
86 // renderer initialisation is complete, so load it the first time through here
89 // Only construct the shaders when shaders are supported and enabled; otherwise
90 // RenderParticles will never be called so it's safe to leave the shaders as null
91 if (g_Renderer
.GetRenderPath() == CRenderer::RP_SHADER
)
93 m
->shader
= g_Renderer
.GetShaderManager().LoadEffect(str_particle
, context
, CShaderDefines());
94 m
->shaderSolid
= g_Renderer
.GetShaderManager().LoadEffect(str_particle_solid
, context
, CShaderDefines());
100 for (int cullGroup
= 0; cullGroup
< CRenderer::CULL_MAX
; ++cullGroup
)
102 PROFILE("update emitters");
103 for (size_t i
= 0; i
< m
->emitters
[cullGroup
].size(); ++i
)
105 CParticleEmitter
* emitter
= m
->emitters
[cullGroup
][i
];
106 emitter
->UpdateArrayData(m
->frameNumber
);
107 emitter
->PrepareForRendering();
111 for (int cullGroup
= 0; cullGroup
< CRenderer::CULL_MAX
; ++cullGroup
)
113 // Sort back-to-front by distance from camera
114 PROFILE("sort emitters");
115 CMatrix3D worldToCam
;
116 g_Renderer
.GetViewCamera().m_Orientation
.GetInverse(worldToCam
);
117 std::stable_sort(m
->emitters
[cullGroup
].begin(), m
->emitters
[cullGroup
].end(), SortEmitterDistance(worldToCam
));
120 // TODO: should batch by texture here when possible, maybe
123 void ParticleRenderer::RenderParticles(int cullGroup
, bool solidColor
)
125 CShaderTechniquePtr shader
= solidColor
? m
->shaderSolid
: m
->shader
;
127 std::vector
<CParticleEmitter
*>& emitters
= m
->emitters
[cullGroup
];
131 shader
->GetShader()->Uniform(str_transform
, g_Renderer
.GetViewCamera().GetViewProjection());
132 shader
->GetShader()->Uniform(str_modelViewMatrix
, g_Renderer
.GetViewCamera().GetOrientation().GetInverse());
138 for (size_t i
= 0; i
< emitters
.size(); ++i
)
140 CParticleEmitter
* emitter
= emitters
[i
];
142 emitter
->Bind(shader
->GetShader());
143 emitter
->RenderArray(shader
->GetShader());
146 CVertexBuffer::Unbind();
148 pglBlendEquationEXT(GL_FUNC_ADD
);
149 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
157 void ParticleRenderer::RenderBounds(int cullGroup
, CShaderProgramPtr
& shader
)
159 std::vector
<CParticleEmitter
*>& emitters
= m
->emitters
[cullGroup
];
161 for (size_t i
= 0; i
< emitters
.size(); ++i
)
163 CParticleEmitter
* emitter
= emitters
[i
];
165 CBoundingBoxAligned bounds
= emitter
->m_Type
->CalculateBounds(emitter
->GetPosition(), emitter
->GetParticleBounds());
166 bounds
.Render(shader
);