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