1 //  SuperTuxKart - a fun racing game with go-kart
2 //  Copyright (C) 2015 SuperTuxKart-Team
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 3
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17 
18 #ifndef SERVER_ONLY
19 #include "graphics/draw_calls.hpp"
20 
21 #include "config/stk_config.hpp"
22 #include "config/user_config.hpp"
23 #include "graphics/cpu_particle_manager.hpp"
24 #include "graphics/irr_driver.hpp"
25 #include "graphics/lod_node.hpp"
26 #include "graphics/shaders.hpp"
27 #include "graphics/stk_particle.hpp"
28 #include "graphics/stk_text_billboard.hpp"
29 #include "graphics/text_billboard_drawer.hpp"
30 #include "graphics/sp/sp_base.hpp"
31 #include "graphics/sp/sp_mesh_node.hpp"
32 #include "tracks/track.hpp"
33 #include "utils/profiler.hpp"
34 
35 #include <numeric>
36 
37 // ----------------------------------------------------------------------------
isCulledPrecise(const scene::ICameraSceneNode * cam,const scene::ISceneNode * node,bool visualization)38 bool DrawCalls::isCulledPrecise(const scene::ICameraSceneNode *cam,
39                                 const scene::ISceneNode* node,
40                                 bool visualization)
41 {
42     if (!node->getAutomaticCulling() && !visualization)
43         return false;
44 
45     const core::matrix4 &trans = node->getAbsoluteTransformation();
46     core::vector3df edges[8];
47     node->getBoundingBox().getEdges(edges);
48     for (unsigned i = 0; i < 8; i++)
49         trans.transformVect(edges[i]);
50 
51     /* From irrlicht
52        /3--------/7
53       / |       / |
54      /  |      /  |
55     1---------5   |
56     |  /2- - -|- -6
57     | /       |  /
58     |/        | /
59     0---------4/
60     */
61 
62     if (visualization)
63     {
64         addEdgeForViz(edges[0], edges[1]);
65         addEdgeForViz(edges[1], edges[5]);
66         addEdgeForViz(edges[5], edges[4]);
67         addEdgeForViz(edges[4], edges[0]);
68         addEdgeForViz(edges[2], edges[3]);
69         addEdgeForViz(edges[3], edges[7]);
70         addEdgeForViz(edges[7], edges[6]);
71         addEdgeForViz(edges[6], edges[2]);
72         addEdgeForViz(edges[0], edges[2]);
73         addEdgeForViz(edges[1], edges[3]);
74         addEdgeForViz(edges[5], edges[7]);
75         addEdgeForViz(edges[4], edges[6]);
76         if (!node->getAutomaticCulling())
77         {
78             return false;
79         }
80     }
81 
82     const scene::SViewFrustum &frust = *cam->getViewFrustum();
83     for (s32 i = 0; i < scene::SViewFrustum::VF_PLANE_COUNT; i++)
84     {
85         if (isBoxInFrontOfPlane(frust.planes[i], edges))
86         {
87             return true;
88         }
89     }
90     return false;
91 
92 }   // isCulledPrecise
93 
94 // ----------------------------------------------------------------------------
isBoxInFrontOfPlane(const core::plane3df & plane,const core::vector3df * edges)95 bool DrawCalls::isBoxInFrontOfPlane(const core::plane3df &plane,
96                                     const core::vector3df* edges)
97 {
98     for (u32 i = 0; i < 8; i++)
99     {
100         if (plane.classifyPointRelation(edges[i]) != core::ISREL3D_FRONT)
101             return false;
102     }
103     return true;
104 }   // isBoxInFrontOfPlane
105 
106 // ----------------------------------------------------------------------------
addEdgeForViz(const core::vector3df & p0,const core::vector3df & p1)107 void DrawCalls::addEdgeForViz(const core::vector3df &p0,
108                               const core::vector3df &p1)
109 {
110     m_bounding_boxes.push_back(p0.X);
111     m_bounding_boxes.push_back(p0.Y);
112     m_bounding_boxes.push_back(p0.Z);
113     m_bounding_boxes.push_back(p1.X);
114     m_bounding_boxes.push_back(p1.Y);
115     m_bounding_boxes.push_back(p1.Z);
116 }   // addEdgeForViz
117 
118 // ----------------------------------------------------------------------------
renderBoundingBoxes()119 void DrawCalls::renderBoundingBoxes()
120 {
121     Shaders::ColoredLine *line = Shaders::ColoredLine::getInstance();
122     line->use();
123     line->bindVertexArray();
124     line->bindBuffer();
125     line->setUniforms(irr::video::SColor(255, 255, 0, 0));
126     const float *tmp = m_bounding_boxes.data();
127     for (unsigned int i = 0; i < m_bounding_boxes.size(); i += 1024 * 6)
128     {
129         unsigned count = std::min((unsigned)m_bounding_boxes.size() - i,
130             (unsigned)1024 * 6);
131         glBufferSubData(GL_ARRAY_BUFFER, 0, count * sizeof(float), &tmp[i]);
132 
133         glDrawArrays(GL_LINES, 0, count / 3);
134     }
135     m_bounding_boxes.clear();
136 }   // renderBoundingBoxes
137 
138 // ----------------------------------------------------------------------------
parseSceneManager(core::list<scene::ISceneNode * > & List,const scene::ICameraSceneNode * cam)139 void DrawCalls::parseSceneManager(core::list<scene::ISceneNode*> &List,
140                                   const scene::ICameraSceneNode *cam)
141 {
142     core::list<scene::ISceneNode*>::Iterator I = List.begin(), E = List.end();
143     for (; I != E; ++I)
144     {
145         if (LODNode *node = dynamic_cast<LODNode *>(*I))
146         {
147             node->updateVisibility();
148         }
149         (*I)->updateAbsolutePosition();
150         if (!(*I)->isVisible())
151             continue;
152 
153         if (STKParticle *node = dynamic_cast<STKParticle*>(*I))
154         {
155             if (!isCulledPrecise(cam, *I, irr_driver->getBoundingBoxesViz()))
156                 CPUParticleManager::getInstance()->addParticleNode(node);
157             continue;
158         }
159 
160         if (scene::IBillboardSceneNode *node =
161             dynamic_cast<scene::IBillboardSceneNode*>(*I))
162         {
163             if (!isCulledPrecise(cam, *I))
164                 CPUParticleManager::getInstance()->addBillboardNode(node);
165             continue;
166         }
167 
168         if (STKTextBillboard *tb =
169             dynamic_cast<STKTextBillboard*>(*I))
170         {
171             if (!isCulledPrecise(cam, *I, irr_driver->getBoundingBoxesViz()))
172                 TextBillboardDrawer::addTextBillboard(tb);
173             continue;
174         }
175 
176         SP::SPMeshNode* node = dynamic_cast<SP::SPMeshNode*>(*I);
177         if (node)
178         {
179             SP::addObject(node);
180         }
181         parseSceneManager((*I)->getChildren(), cam);
182     }
183 }
184 
185 // ----------------------------------------------------------------------------
DrawCalls()186 DrawCalls::DrawCalls()
187 {
188     m_sync = 0;
189 } //DrawCalls
190 
191 // ----------------------------------------------------------------------------
~DrawCalls()192 DrawCalls::~DrawCalls()
193 {
194     CPUParticleManager::kill();
195     STKParticle::destroyFlipsBuffer();
196 } //~DrawCalls
197 
198 // ----------------------------------------------------------------------------
199  /** Prepare draw calls before scene rendering
200  */
prepareDrawCalls(scene::ICameraSceneNode * camnode)201 void DrawCalls::prepareDrawCalls(scene::ICameraSceneNode *camnode)
202 {
203     CPUParticleManager::getInstance()->reset();
204     TextBillboardDrawer::reset();
205     PROFILER_PUSH_CPU_MARKER("- culling", 0xFF, 0xFF, 0x0);
206     SP::prepareDrawCalls();
207     parseSceneManager(
208         irr_driver->getSceneManager()->getRootSceneNode()->getChildren(),
209         camnode);
210     SP::handleDynamicDrawCall();
211     SP::updateModelMatrix();
212     PROFILER_POP_CPU_MARKER();
213 
214     PROFILER_PUSH_CPU_MARKER("- cpu particle generation", 0x2F, 0x1F, 0x11);
215     CPUParticleManager::getInstance()->generateAll();
216     PROFILER_POP_CPU_MARKER();
217 
218     // Add a 1 s timeout
219     if (m_sync != 0)
220     {
221         PROFILER_PUSH_CPU_MARKER("- Sync Stall", 0xFF, 0x0, 0x0);
222         GLenum reason = glClientWaitSync(m_sync, GL_SYNC_FLUSH_COMMANDS_BIT, 0);
223         if (reason != GL_ALREADY_SIGNALED)
224         {
225             do
226             {
227                 reason = glClientWaitSync(m_sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000000);
228             }
229             while (reason == GL_TIMEOUT_EXPIRED);
230         }
231         glDeleteSync(m_sync);
232         m_sync = 0;
233         PROFILER_POP_CPU_MARKER();
234     }
235 
236     PROFILER_PUSH_CPU_MARKER("- particle and text billboard upload", 0x3F,
237         0x03, 0x61);
238     CPUParticleManager::getInstance()->uploadAll();
239     TextBillboardDrawer::updateAll();
240     PROFILER_POP_CPU_MARKER();
241 
242     PROFILER_PUSH_CPU_MARKER("- SP::upload instance and skinning matrices",
243         0xFF, 0x0, 0xFF);
244     SP::uploadAll();
245     PROFILER_POP_CPU_MARKER();
246 }
247 
248 #endif
249