1 // This file belongs to the "MiniCore" game engine.
2 // Copyright (C) 2015 Jussi Lind <jussi.lind@iki.fi>
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
8 //
9 // This program 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 this program; if not, write to the Free Software
16 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 // MA 02110-1301, USA.
18 //
19
20 #include "mcworldrenderer.hh"
21
22 #include "mccamera.hh"
23 #include "mclogger.hh"
24 #include "mcobject.hh"
25 #include "mcparticle.hh"
26 #include "mcshape.hh"
27 #include "mcshapeview.hh"
28 #include "mcsurfaceparticle.hh"
29 #include "mcsurfaceparticlerenderer.hh"
30 #include "mcsurfaceparticlerendererlegacy.hh"
31 #include "mcsurfaceview.hh"
32
33 #include <algorithm>
34
35 #include <MCGLEW>
36
MCWorldRenderer()37 MCWorldRenderer::MCWorldRenderer()
38 : m_surfaceParticleRenderer(nullptr)
39 {
40 }
41
glScene()42 MCGLScene & MCWorldRenderer::glScene()
43 {
44 return m_glScene;
45 }
46
buildObjectBatches(MCCamera * camera)47 void MCWorldRenderer::buildObjectBatches(MCCamera * camera)
48 {
49 m_defaultLayer.objectBatches()[camera].clear();
50 auto & batchVector = m_defaultLayer.objectBatches()[camera];
51 static std::vector<MCObject *> childStack;
52 childStack.clear();
53 for (auto && object : MCWorld::instance().objectGrid().getObjectsWithinBBox(camera->bbox()))
54 {
55 childStack.push_back(object);
56 while (childStack.size())
57 {
58 auto parent = childStack.back();
59 childStack.pop_back();
60
61 if (parent->isRenderable() && parent->shape() && parent->shape()->view())
62 {
63 const int objectViewId = object->typeId() * 1024 + parent->shape()->view()->viewId();
64 auto batchIter = std::find_if(batchVector.begin(), batchVector.end(), [&](const MCRenderLayer::ObjectBatch & batch) {
65 return batch.objectViewId == objectViewId;
66 });
67 if (batchIter != batchVector.end())
68 {
69 auto & batch = (*batchIter);
70 batch.objects.push_back(parent);
71 batch.priority = std::max(parent->location().k(), batch.priority);
72 }
73 else
74 {
75 MCRenderLayer::ObjectBatch batch;
76 batch.objectViewId = objectViewId;
77 batch.objects.push_back(parent);
78 batch.priority = parent->location().k();
79 batchVector.push_back(batch);
80 }
81 }
82
83 for (auto child : parent->children())
84 {
85 childStack.push_back(child.get());
86 }
87 }
88 }
89
90 std::stable_sort(batchVector.begin(), batchVector.end(), [](const MCRenderLayer::ObjectBatch & l, const MCRenderLayer::ObjectBatch & r) {
91 return l.priority < r.priority;
92 });
93 }
94
buildParticleBatches(MCCamera * camera)95 void MCWorldRenderer::buildParticleBatches(MCCamera * camera)
96 {
97 m_defaultLayer.particleBatches()[camera].clear();
98 auto & batchVector = m_defaultLayer.particleBatches()[camera];
99 for (auto && particleIter : m_particleSet)
100 {
101 MCParticle & particle = *particleIter;
102 const MCBBoxF bbox(
103 particle.location().i() - particle.radius(),
104 particle.location().j() - particle.radius(),
105 particle.location().i() + particle.radius(),
106 particle.location().j() + particle.radius());
107
108 if (camera->isVisible(bbox))
109 {
110 auto batchIter = std::find_if(batchVector.begin(), batchVector.end(), [&](const MCRenderLayer::ObjectBatch & batch) {
111 return batch.objectViewId == static_cast<int>(particle.typeId());
112 });
113 if (batchIter != batchVector.end())
114 {
115 auto & batch = (*batchIter);
116 batch.objects.push_back(&particle);
117 batch.priority = std::max(particle.location().k(), batch.priority);
118 }
119 else
120 {
121 MCRenderLayer::ObjectBatch batch;
122 batch.objectViewId = particle.typeId();
123 batch.objects.push_back(&particle);
124 batch.priority = particle.location().k();
125 batchVector.push_back(batch);
126 }
127 }
128 else
129 {
130 // Optimization that kills non-visible particles.
131 if (particle.dieWhenOffScreen())
132 {
133 bool isVisibleInAnyCamera = false;
134 for (MCCamera * visibilityCamera : m_visibilityCameras)
135 {
136 if (visibilityCamera != camera && visibilityCamera->isVisible(bbox))
137 {
138 isVisibleInAnyCamera = true;
139 break;
140 }
141 }
142
143 if (!isVisibleInAnyCamera)
144 {
145 particle.die();
146 }
147 }
148 }
149 }
150
151 std::stable_sort(batchVector.begin(), batchVector.end(), [](const MCRenderLayer::ObjectBatch & l, const MCRenderLayer::ObjectBatch & r) {
152 return l.priority < r.priority;
153 });
154 }
155
buildBatches(MCCamera * camera)156 void MCWorldRenderer::buildBatches(MCCamera * camera)
157 {
158 // This code tests the visibility and sorts the objects with respect
159 // to their view id's into "batches". MCWorld::render()
160 // (and MCWorld::renderShadows()) then goes through these batches
161 // and perform the actual rendering.
162
163 // Grouping the objects like this reduces texture switches etc and increases
164 // overall performance.
165
166 if (!camera)
167 {
168 return;
169 }
170
171 if (!m_surfaceParticleRenderer)
172 {
173 createSurfaceParticleRenderer();
174 }
175
176 buildObjectBatches(camera);
177
178 buildParticleBatches(camera);
179 }
180
render(MCCamera * camera,MCRenderGroup renderGroup)181 void MCWorldRenderer::render(MCCamera * camera, MCRenderGroup renderGroup)
182 {
183 switch (renderGroup)
184 {
185 case MCRenderGroup::Objects:
186 renderObjects(camera);
187 break;
188 case MCRenderGroup::ObjectShadows:
189 renderObjectShadows(camera);
190 break;
191 case MCRenderGroup::Particles:
192 renderParticles(camera);
193 break;
194 case MCRenderGroup::ParticleShadows:
195 renderParticleShadows(camera);
196 break;
197 default:
198 break;
199 }
200 }
201
renderObjects(MCCamera * camera)202 void MCWorldRenderer::renderObjects(MCCamera * camera)
203 {
204 if (m_defaultLayer.depthTestEnabled())
205 {
206 glEnable(GL_DEPTH_TEST);
207 }
208 else
209 {
210 glDisable(GL_DEPTH_TEST);
211 }
212
213 glDepthMask(m_defaultLayer.depthMaskEnabled());
214
215 renderObjectBatches(camera, m_defaultLayer);
216
217 glDepthMask(GL_TRUE);
218 }
219
renderParticles(MCCamera * camera)220 void MCWorldRenderer::renderParticles(MCCamera * camera)
221 {
222 if (m_defaultLayer.depthTestEnabled())
223 {
224 glEnable(GL_DEPTH_TEST);
225 }
226 else
227 {
228 glDisable(GL_DEPTH_TEST);
229 }
230
231 glDepthMask(m_defaultLayer.depthMaskEnabled());
232
233 renderParticleBatches(camera, m_defaultLayer);
234
235 glDepthMask(GL_TRUE);
236 }
237
renderObjectBatches(MCCamera * camera,MCRenderLayer & layer)238 void MCWorldRenderer::renderObjectBatches(MCCamera * camera, MCRenderLayer & layer)
239 {
240 for (auto && batch : layer.objectBatches()[camera])
241 {
242 const size_t itemCountInBatch = batch.objects.size();
243 if (itemCountInBatch > 0)
244 {
245 MCObject * object = batch.objects[0];
246 std::shared_ptr<MCShapeView> view = object->shape()->view();
247
248 view->bind();
249 object->render(camera);
250
251 for (size_t i = 1; i < itemCountInBatch - 1; i++)
252 {
253 batch.objects[i]->render(camera);
254 }
255
256 object = batch.objects[itemCountInBatch - 1];
257 object->render(camera);
258 view->release();
259 }
260 }
261 }
262
createSurfaceParticleRenderer()263 void MCWorldRenderer::createSurfaceParticleRenderer()
264 {
265 #ifdef __MC_GLES__
266 MCLogger().info() << "Particle renderer using vertex arrays.";
267 m_surfaceParticleRenderer = new MCSurfaceParticleRendererLegacy;
268 #else
269 MCLogger().info() << "Particle renderer using VAO.";
270 m_surfaceParticleRenderer = new MCSurfaceParticleRenderer;
271 #endif
272 }
273
renderParticleBatches(MCCamera * camera,MCRenderLayer & layer)274 void MCWorldRenderer::renderParticleBatches(MCCamera * camera, MCRenderLayer & layer)
275 {
276 for (auto && batch : layer.particleBatches()[camera])
277 {
278 if (batch.objects.size())
279 {
280 if (dynamic_cast<MCSurfaceParticle *>(batch.objects[0]))
281 {
282 m_surfaceParticleRenderer->setBatch(batch, camera);
283 m_surfaceParticleRenderer->render();
284 }
285 }
286 }
287 }
288
renderObjectShadows(MCCamera * camera)289 void MCWorldRenderer::renderObjectShadows(MCCamera * camera)
290 {
291 glEnable(GL_DEPTH_TEST);
292 glEnable(GL_BLEND);
293 glBlendFunc(GL_SRC_ALPHA, GL_DST_COLOR);
294
295 renderObjectShadowBatches(camera, m_defaultLayer);
296
297 glDisable(GL_BLEND);
298 glDisable(GL_DEPTH_TEST);
299 }
300
renderParticleShadows(MCCamera * camera)301 void MCWorldRenderer::renderParticleShadows(MCCamera * camera)
302 {
303 glEnable(GL_DEPTH_TEST);
304 glEnable(GL_BLEND);
305 glBlendFunc(GL_SRC_ALPHA, GL_DST_COLOR);
306
307 renderParticleShadowBatches(camera, m_defaultLayer);
308
309 glDisable(GL_BLEND);
310 glDisable(GL_DEPTH_TEST);
311 }
312
renderObjectShadowBatches(MCCamera * camera,MCRenderLayer & layer)313 void MCWorldRenderer::renderObjectShadowBatches(MCCamera * camera, MCRenderLayer & layer)
314 {
315 // Render batches
316 for (auto && batch : layer.objectBatches()[camera])
317 {
318 const size_t itemCountInBatch = batch.objects.size();
319 if (itemCountInBatch > 0)
320 {
321 MCObject * object = batch.objects[0];
322 std::shared_ptr<MCShapeView> view = object->shape()->view();
323 if (view && view->hasShadow())
324 {
325 view->bindShadow();
326 object->renderShadow(camera);
327
328 for (size_t i = 1; i < itemCountInBatch - 1; i++)
329 {
330 batch.objects[i]->renderShadow(camera);
331 }
332
333 object = batch.objects[itemCountInBatch - 1];
334 object->renderShadow(camera);
335 view->releaseShadow();
336 }
337 }
338 }
339 }
340
renderParticleShadowBatches(MCCamera * camera,MCRenderLayer & layer)341 void MCWorldRenderer::renderParticleShadowBatches(MCCamera * camera, MCRenderLayer & layer)
342 {
343 for (auto && batch : layer.particleBatches()[camera])
344 {
345 if (batch.objects.size())
346 {
347 // Currently support shadows only for surface particles.
348 if (MCSurfaceParticle * particle = dynamic_cast<MCSurfaceParticle *>(batch.objects[0]))
349 {
350 if (particle->hasShadow())
351 {
352 m_surfaceParticleRenderer->setBatch(batch, camera, true);
353 m_surfaceParticleRenderer->renderShadows();
354 }
355 }
356 }
357 }
358 }
359
enableDepthTest(bool enable)360 void MCWorldRenderer::enableDepthTest(bool enable)
361 {
362 m_defaultLayer.setDepthTestEnabled(enable);
363 }
364
enableDepthMask(bool enable)365 void MCWorldRenderer::enableDepthMask(bool enable)
366 {
367 m_defaultLayer.setDepthMaskEnabled(enable);
368 }
369
addObject(MCObject & object)370 void MCWorldRenderer::addObject(MCObject & object)
371 {
372 if (object.isParticle())
373 {
374 MCParticle * particle = static_cast<MCParticle *>(&object);
375 if (particle->m_indexInRenderArray == -1)
376 {
377 particle->m_indexInRenderArray = m_particleSet.size();
378 m_particleSet.push_back(static_cast<MCParticle *>(&object));
379 }
380 }
381 }
382
removeObject(MCObject & object)383 void MCWorldRenderer::removeObject(MCObject & object)
384 {
385 if (object.isParticle())
386 {
387 MCParticle * particle = static_cast<MCParticle *>(&object);
388 if (particle->m_indexInRenderArray >= 0 && m_particleSet.size())
389 {
390 m_particleSet.back()->m_indexInRenderArray = particle->m_indexInRenderArray;
391 m_particleSet[particle->m_indexInRenderArray] = m_particleSet.back();
392 m_particleSet.pop_back();
393 particle->m_indexInRenderArray = -1;
394 }
395 }
396 }
397
addParticleVisibilityCamera(MCCamera & camera)398 void MCWorldRenderer::addParticleVisibilityCamera(MCCamera & camera)
399 {
400 m_visibilityCameras.push_back(&camera);
401 }
402
removeParticleVisibilityCameras()403 void MCWorldRenderer::removeParticleVisibilityCameras()
404 {
405 m_visibilityCameras.clear();
406 }
407
clear()408 void MCWorldRenderer::clear()
409 {
410 m_defaultLayer.clear();
411
412 for (auto particle : m_particleSet)
413 {
414 particle->m_indexInRenderArray = -1;
415 }
416 m_particleSet.clear();
417 }
418
~MCWorldRenderer()419 MCWorldRenderer::~MCWorldRenderer()
420 {
421 delete m_surfaceParticleRenderer;
422 }
423