1 //  SuperTuxKart - a fun racing game with go-kart
2 //  Copyright (C) 2009-2015 Joerg Henrichs
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 #include "graphics/irr_driver.hpp"
19 
20 #include "challenges/story_mode_timer.hpp"
21 #include "config/player_manager.hpp"
22 #include "config/user_config.hpp"
23 #include "font/bold_face.hpp"
24 #include "font/digit_face.hpp"
25 #include "font/font_manager.hpp"
26 #include "font/regular_face.hpp"
27 #include "graphics/2dutils.hpp"
28 #include "graphics/b3d_mesh_loader.hpp"
29 #include "graphics/camera.hpp"
30 #include "graphics/central_settings.hpp"
31 #include "graphics/fixed_pipeline_renderer.hpp"
32 #include "graphics/glwrap.hpp"
33 #include "graphics/graphics_restrictions.hpp"
34 #include "graphics/light.hpp"
35 #include "graphics/material.hpp"
36 #include "graphics/material_manager.hpp"
37 #include "graphics/particle_kind_manager.hpp"
38 #include "graphics/per_camera_node.hpp"
39 #include "graphics/referee.hpp"
40 #include "graphics/render_target.hpp"
41 #include "graphics/rtts.hpp"
42 #include "graphics/shader.hpp"
43 #include "graphics/shader_based_renderer.hpp"
44 #include "graphics/shader_files_manager.hpp"
45 #include "graphics/shared_gpu_objects.hpp"
46 #include "graphics/sp_mesh_loader.hpp"
47 #include "graphics/sp/sp_base.hpp"
48 #include "graphics/sp/sp_dynamic_draw_call.hpp"
49 #include "graphics/sp/sp_mesh.hpp"
50 #include "graphics/sp/sp_mesh_node.hpp"
51 #include "graphics/sp/sp_shader_manager.hpp"
52 #include "graphics/sp/sp_texture_manager.hpp"
53 #include "graphics/stk_text_billboard.hpp"
54 #include "graphics/stk_tex_manager.hpp"
55 #include "graphics/stk_texture.hpp"
56 #include "graphics/sun.hpp"
57 #include "guiengine/engine.hpp"
58 #include "guiengine/message_queue.hpp"
59 #include "guiengine/modaldialog.hpp"
60 #include "guiengine/scalable_font.hpp"
61 #include "guiengine/screen.hpp"
62 #include "guiengine/screen_keyboard.hpp"
63 #include "guiengine/skin.hpp"
64 #include "io/file_manager.hpp"
65 #include "items/item_manager.hpp"
66 #include "items/powerup_manager.hpp"
67 #include "items/attachment_manager.hpp"
68 #include "items/projectile_manager.hpp"
69 #include "karts/abstract_kart.hpp"
70 #include "karts/kart_properties_manager.hpp"
71 #include "main_loop.hpp"
72 #include "modes/world.hpp"
73 #include "network/network_config.hpp"
74 #include "network/stk_host.hpp"
75 #include "network/stk_peer.hpp"
76 #include "physics/physics.hpp"
77 #include "scriptengine/property_animator.hpp"
78 #include "states_screens/dialogs/confirm_resolution_dialog.hpp"
79 #include "states_screens/state_manager.hpp"
80 #include "tracks/track_manager.hpp"
81 #include "tracks/track.hpp"
82 #include "utils/constants.hpp"
83 #include "utils/log.hpp"
84 #include "utils/profiler.hpp"
85 #include "utils/string_utils.hpp"
86 #include "utils/translation.hpp"
87 #include "utils/vs.hpp"
88 
89 #include <irrlicht.h>
90 
91 #ifdef ENABLE_RECORDER
92 #include <chrono>
93 #include <openglrecorder.h>
94 #endif
95 
96 /* Build-time check that the Irrlicht we're building against works for us.
97  * Should help prevent distros building against an incompatible library.
98  */
99 
100 #if (  IRRLICHT_VERSION_MAJOR < 1                   || \
101        IRRLICHT_VERSION_MINOR < 7                   || \
102       _IRR_MATERIAL_MAX_TEXTURES_ < 8               || \
103       ( !defined(_IRR_COMPILE_WITH_OPENGL_) &&         \
104         !defined(SERVER_ONLY)               &&         \
105         !defined(_IRR_COMPILE_WITH_OGLES2_)       ) || \
106       !defined(_IRR_COMPILE_WITH_B3D_LOADER_)             )
107 #error "Building against an incompatible Irrlicht. Distros, \
108 please use the included version."
109 #endif
110 
111 using namespace irr;
112 
113 /** singleton */
114 IrrDriver *irr_driver = NULL;
115 
116 #ifndef SERVER_ONLY
117 GPUTimer* m_perf_query[Q_LAST];
118 static const char* m_perf_query_phase[Q_LAST] =
119 {
120     "Shadows Cascade 0",
121     "Shadows Cascade 1",
122     "Shadows Cascade 2",
123     "Shadows Cascade 3",
124     "Solid Pass",
125     "Env Map",
126     "SunLight",
127     "PointLights",
128     "SSAO",
129     "Light Scatter",
130     "Glow",
131     "Combine Diffuse Color",
132     "Skybox",
133     "Transparent",
134     "Particles",
135     "Depth of Field",
136     "Godrays",
137     "Bloom",
138     "Tonemap",
139     "Motion Blur",
140     "Lightning",
141     "MLAA",
142     "GUI",
143 };
144 #endif
145 
146 const int MIN_SUPPORTED_HEIGHT = 768;
147 const int MIN_SUPPORTED_WIDTH  = 1024;
148 const bool ALLOW_1280_X_720    = true;
149 
150 // ----------------------------------------------------------------------------
151 /** The constructor creates the irrlicht device. It first creates a NULL
152  *  device. This is necessary to handle the Chicken/egg problem with irrlicht:
153  *  access to the file system is given from the device, but we can't create the
154  *  device before reading the user_config file (for resolution, fullscreen).
155  *  So we create a dummy device here to begin with, which is then later (once
156  *  the real device exists) changed in initDevice().
157  */
IrrDriver()158 IrrDriver::IrrDriver()
159 {
160     m_render_nw_debug = false;
161     m_resolution_changing = RES_CHANGE_NONE;
162 
163     struct irr::SIrrlichtCreationParameters p;
164     p.DriverType    = video::EDT_NULL;
165     p.WindowSize    = core::dimension2d<u32>(640,480);
166     p.Bits          = 16U;
167     p.Fullscreen    = false;
168     p.SwapInterval  = 0;
169     p.EventReceiver = NULL;
170     p.FileSystem    = file_manager->getFileSystem();
171     p.PrivateData   = NULL;
172 
173     m_device = createDeviceEx(p);
174 
175     m_request_screenshot = false;
176     m_renderer            = NULL;
177     m_wind                = new Wind();
178     m_ssaoviz = false;
179     m_shadowviz = false;
180     m_boundingboxesviz = false;
181     m_last_light_bucket_distance = 0;
182     m_clear_color                = video::SColor(255, 100, 101, 140);
183     m_skinning_joint             = 0;
184     m_recording = false;
185     m_sun_interposer = NULL;
186     m_scene_complexity           = 0;
187 
188 #ifndef SERVER_ONLY
189     for (unsigned i = 0; i < Q_LAST; i++)
190     {
191         m_perf_query[i] = new GPUTimer(m_perf_query_phase[i]);
192     }
193 #endif
194 }   // IrrDriver
195 
196 // ----------------------------------------------------------------------------
197 /** Destructor - removes the irrlicht device.
198  */
~IrrDriver()199 IrrDriver::~IrrDriver()
200 {
201 #ifdef ENABLE_RECORDER
202     ogrDestroy();
203 #endif
204     STKTexManager::getInstance()->kill();
205     delete m_wind;
206     delete m_renderer;
207 #ifndef SERVER_ONLY
208     for (unsigned i = 0; i < Q_LAST; i++)
209     {
210         delete m_perf_query[i];
211     }
212 #endif
213     assert(m_device != NULL);
214     m_device->drop();
215     m_device = NULL;
216     m_modes.clear();
217 }   // ~IrrDriver
218 
219 // ----------------------------------------------------------------------------
getGPUQueryPhaseName(unsigned q)220 const char* IrrDriver::getGPUQueryPhaseName(unsigned q)
221 {
222 #ifndef SERVER_ONLY
223     assert(q < Q_LAST);
224     return m_perf_query_phase[q];
225 #else
226     return "";
227 #endif
228 }   // getGPUQueryPhaseName
229 
230 // ----------------------------------------------------------------------------
231 /** Called before a race is started, after all cameras are set up.
232  */
reset()233 void IrrDriver::reset()
234 {
235 #ifndef SERVER_ONLY
236     m_renderer->resetPostProcessing();
237 #endif
238 }   // reset
239 
240 // ----------------------------------------------------------------------------
getMainSetup()241 core::array<video::IRenderTarget> &IrrDriver::getMainSetup()
242 {
243   return m_mrt;
244 }   // getMainSetup
245 
246 // ----------------------------------------------------------------------------
247 
248 #ifndef SERVER_ONLY
249 
getGPUTimer(unsigned i)250 GPUTimer &IrrDriver::getGPUTimer(unsigned i)
251 {
252     return *m_perf_query[i];
253 }   // getGPUTimer
254 #endif
255 // ----------------------------------------------------------------------------
256 
257 #ifndef SERVER_ONLY
createRenderTarget(const irr::core::dimension2du & dimension,const std::string & name)258 std::unique_ptr<RenderTarget> IrrDriver::createRenderTarget(const irr::core::dimension2du &dimension,
259                                                             const std::string &name)
260 {
261     return m_renderer->createRenderTarget(dimension, name);
262 }   // createRenderTarget
263 #endif   // ~SERVER_ONLY
264 
265 // ----------------------------------------------------------------------------
266 /** If the position of the window should be remembered, store it in the config
267  *  file.
268  *  \post The user config file must still be saved!
269  */
updateConfigIfRelevant()270 void IrrDriver::updateConfigIfRelevant()
271 {
272 #ifndef SERVER_ONLY
273     if (!UserConfigParams::m_fullscreen &&
274          UserConfigParams::m_remember_window_location)
275     {
276         int x = 0;
277         int y = 0;
278 
279         bool success = m_device->getWindowPosition(&x, &y);
280 
281         if (!success)
282         {
283             Log::warn("irr_driver", "Could not retrieve window location");
284             return;
285         }
286 
287         Log::verbose("irr_driver", "Retrieved window location for config: "
288                                    "%i %i", x, y);
289 
290         // If the windows position is saved, it must be a non-negative
291         // number. So if the window is partly off screen, move it to the
292         // corresponding edge.
293         UserConfigParams::m_window_x = std::max(0, x);
294         UserConfigParams::m_window_y = std::max(0, y);
295     }
296 #endif   // !SERVER_ONLY
297 }   // updateConfigIfRelevant
getSplitscreenWindow(int WindowNum)298 core::recti IrrDriver::getSplitscreenWindow(int WindowNum)
299 {
300     const int playernum = RaceManager::get()->getNumLocalPlayers();
301     const float playernum_sqrt = sqrtf((float)playernum);
302 
303     int rows = int(  UserConfigParams::split_screen_horizontally
304                    ? ceil(playernum_sqrt)
305                    : round(playernum_sqrt)                       );
306     int cols = int(  UserConfigParams::split_screen_horizontally
307                    ? round(playernum_sqrt)
308                    : ceil(playernum_sqrt)                        );
309 
310     if (rows == 0){rows = 1;}
311     if (cols == 0) {cols = 1;}
312     //This could add a bit of overhang
313     const int width_of_space =
314         int(ceil(   (float)irr_driver->getActualScreenSize().Width
315                   / (float)cols)                                  );
316     const int height_of_space =
317         int (ceil(  (float)irr_driver->getActualScreenSize().Height
318                   / (float)rows)                                   );
319 
320     const int x_grid_Position = WindowNum % cols;
321     const int y_grid_Position = int(floor((WindowNum) / cols));
322 
323 //To prevent the viewport going over the right side, we use std::min to ensure the right corners are never larger than the total width
324     return core::recti(
325         x_grid_Position * width_of_space,
326         y_grid_Position * height_of_space,
327         (x_grid_Position * width_of_space) + width_of_space,
328         (y_grid_Position * height_of_space) + height_of_space);
329 }
330 // ----------------------------------------------------------------------------
331 /** Gets a list of supported video modes from the irrlicht device. This data
332  *  is stored in m_modes.
333  */
createListOfVideoModes()334 void IrrDriver::createListOfVideoModes()
335 {
336     // Note that this is actually reported by valgrind as a leak, but it is
337     // a leak in irrlicht: this list is dynamically created the first time
338     // it is used, but then not cleaned on exit.
339     video::IVideoModeList* modes = m_device->getVideoModeList();
340     const int count = modes->getVideoModeCount();
341 
342     for(int i=0; i<count; i++)
343     {
344         // only consider 32-bit resolutions for now
345         if (modes->getVideoModeDepth(i) >= 24)
346         {
347             const int w = modes->getVideoModeResolution(i).Width;
348             const int h = modes->getVideoModeResolution(i).Height;
349 #ifndef MOBILE_STK
350             // Mobile STK reports only 1 desktop (phone) resolution at native scale
351             if ((h < MIN_SUPPORTED_HEIGHT || w < MIN_SUPPORTED_WIDTH) &&
352                 (!(h==600 && w==800 && UserConfigParams::m_artist_debug_mode) &&
353                 (!(h==720 && w==1280 && ALLOW_1280_X_720 == true))))
354                 continue;
355 #endif
356             VideoMode mode(w, h);
357             m_modes.push_back( mode );
358         }   // if depth >=24
359     }   // for i < video modes count
360 }   // createListOfVideoModes
361 
362 // ----------------------------------------------------------------------------
363 /** This creates the actualy OpenGL device. This is called
364  */
initDevice()365 void IrrDriver::initDevice()
366 {
367     SIrrlichtCreationParameters params;
368 
369     // If --no-graphics option was used, the null device can still be used.
370     if (!GUIEngine::isNoGraphics())
371     {
372         // This code is only executed once. No need to reload the video
373         // modes every time the resolution changes.
374         if(m_modes.size()==0)
375         {
376             createListOfVideoModes();
377             // The debug name is only set if irrlicht is compiled in debug
378             // mode. So we use this to print a warning to the user.
379             if(m_device->getDebugName())
380             {
381                 Log::warn("irr_driver",
382                           "!!!!! Performance warning: Irrlicht compiled with "
383                           "debug mode.!!!!!\n");
384                 Log::warn("irr_driver",
385                           "!!!!! This can have a significant performance "
386                           "impact         !!!!!\n");
387             }
388 
389         } // end if firstTime
390 
391         video::IVideoModeList* modes = m_device->getVideoModeList();
392         const core::dimension2d<u32> ssize = modes->getDesktopResolution();
393 
394         if (ssize.Width < 1 || ssize.Height < 1)
395         {
396             Log::warn("irr_driver", "Unknown desktop resolution.");
397         }
398         else if (UserConfigParams::m_width > (int)ssize.Width ||
399                  UserConfigParams::m_height > (int)ssize.Height)
400         {
401             Log::warn("irr_driver", "The window size specified in "
402                       "user config is larger than your screen!");
403             UserConfigParams::m_width = (int)ssize.Width;
404             UserConfigParams::m_height = (int)ssize.Height;
405         }
406 
407         if (UserConfigParams::m_fullscreen)
408         {
409             if (modes->getVideoModeCount() > 0)
410             {
411                 core::dimension2d<u32> res = core::dimension2du(
412                                                     UserConfigParams::m_width,
413                                                     UserConfigParams::m_height);
414                 res = modes->getVideoModeResolution(res, res);
415 
416                 UserConfigParams::m_width = res.Width;
417                 UserConfigParams::m_height = res.Height;
418             }
419             else
420             {
421                 Log::warn("irr_driver", "Cannot get information about "
422                           "resolutions. Disable fullscreen.");
423                 UserConfigParams::m_fullscreen = false;
424             }
425         }
426 
427         if (UserConfigParams::m_width < 1 || UserConfigParams::m_height < 1)
428         {
429             Log::warn("irr_driver", "Invalid window size. "
430                          "Try to use the default one.");
431             UserConfigParams::m_width = MIN_SUPPORTED_WIDTH;
432             UserConfigParams::m_height = MIN_SUPPORTED_HEIGHT;
433         }
434 
435         m_device->closeDevice();
436         m_video_driver  = NULL;
437         m_gui_env       = NULL;
438         m_scene_manager = NULL;
439         // In some circumstances it would happen that a WM_QUIT message
440         // (apparently sent for this NULL device) is later received by
441         // the actual window, causing it to immediately quit.
442         // Following advise on the irrlicht forums I added the following
443         // two calles - the first one didn't make a difference (but
444         // certainly can't hurt), but the second one apparenlty solved
445         // the problem for now.
446         m_device->clearSystemMessages();
447         m_device->run();
448         m_device->drop();
449         m_device  = NULL;
450 
451         params.ForceLegacyDevice = (UserConfigParams::m_force_legacy_device ||
452             UserConfigParams::m_gamepad_visualisation);
453 
454         // Try 32 and, upon failure, 24 then 16 bit per pixels
455         for (int bits=32; bits>15; bits -=8)
456         {
457             if(UserConfigParams::logMisc())
458                 Log::verbose("irr_driver", "Trying to create device with "
459                              "%i bits\n", bits);
460 
461 #if defined(USE_GLES2)
462             params.DriverType    = video::EDT_OGLES2;
463 #else
464             params.DriverType    = video::EDT_OPENGL;
465 #endif
466             params.PrivateData   = NULL;
467             params.Stencilbuffer = false;
468             params.Bits          = bits;
469             params.EventReceiver = this;
470             params.Fullscreen    = UserConfigParams::m_fullscreen;
471             params.SwapInterval  = UserConfigParams::m_swap_interval;
472             params.FileSystem    = file_manager->getFileSystem();
473             params.WindowSize    =
474                 core::dimension2du(UserConfigParams::m_width,
475                                    UserConfigParams::m_height);
476             params.HandleSRGB    = false;
477             params.ShadersPath   = (file_manager->getShadersDir() +
478                                                            "irrlicht/").c_str();
479             // Set window to remembered position
480             if (  !UserConfigParams::m_fullscreen
481                 && UserConfigParams::m_remember_window_location
482                 && UserConfigParams::m_window_x >= 0
483                 && UserConfigParams::m_window_y >= 0            )
484             {
485                 params.WindowPosition.X = UserConfigParams::m_window_x;
486                 params.WindowPosition.Y = UserConfigParams::m_window_y;
487             }
488 
489             /*
490             switch ((int)UserConfigParams::m_antialiasing)
491             {
492             case 0:
493                 break;
494             case 1:
495                 params.AntiAlias = 2;
496                 break;
497             case 2:
498                 params.AntiAlias = 4;
499                 break;
500             case 3:
501                 params.AntiAlias = 8;
502                 break;
503             default:
504                 Log::error("irr_driver",
505                            "[IrrDriver] WARNING: Invalid value for "
506                            "anti-alias setting : %i\n",
507                            (int)UserConfigParams::m_antialiasing);
508             }
509             */
510             m_device = createDeviceEx(params);
511 
512             if(m_device)
513                 break;
514         }   // for bits=32, 24, 16
515 
516 
517         // if still no device, try with a default screen size, maybe
518         // size is the problem
519         if(!m_device)
520         {
521             UserConfigParams::m_width  = MIN_SUPPORTED_WIDTH;
522             UserConfigParams::m_height = MIN_SUPPORTED_HEIGHT;
523 #if defined(USE_GLES2)
524             m_device = createDevice(video::EDT_OGLES2,
525 #else
526             m_device = createDevice(video::EDT_OPENGL,
527 #endif
528                         core::dimension2du(UserConfigParams::m_width,
529                                            UserConfigParams::m_height ),
530                                     32, //bits per pixel
531                                     UserConfigParams::m_fullscreen,
532                                     false,  // stencil buffers
533                                     0,  // vsync
534                                     this,   // event receiver
535                                     file_manager->getFileSystem()
536                                     );
537             if (m_device)
538             {
539                 Log::verbose("irr_driver", "An invalid resolution was set in "
540                              "the config file, reverting to saner values\n");
541             }
542         }
543     }
544 
545     if(!m_device)
546     {
547         Log::fatal("irr_driver", "Couldn't initialise irrlicht device. Quitting.\n");
548     }
549 #ifndef SERVER_ONLY
550 
551     // Assume sp is supported
552     CentralVideoSettings::m_supports_sp = true;
553     CVS->init();
554 
555     bool recreate_device = false;
556 
557     // Some drivers are able to create OpenGL 3.1 context, but shader-based
558     // pipeline doesn't work for them. For example some radeon drivers
559     // support only GLSL 1.3 and it causes STK to crash. We should force to use
560     // fixed pipeline in this case.
561     if (!GUIEngine::isNoGraphics() &&
562         (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_FORCE_LEGACY_DEVICE) ||
563         (CVS->isGLSL() && !CentralVideoSettings::m_supports_sp)))
564     {
565         Log::warn("irr_driver", "Driver doesn't support shader-based pipeline. "
566                                 "Re-creating device to workaround the issue.");
567 
568         params.ForceLegacyDevice = true;
569         recreate_device = true;
570     }
571 #endif
572 
573 #ifndef SERVER_ONLY
574     if (!GUIEngine::isNoGraphics() && recreate_device)
575     {
576         m_device->closeDevice();
577         m_device->clearSystemMessages();
578         m_device->run();
579         m_device->drop();
580 
581         m_device = createDeviceEx(params);
582 
583         if(!m_device)
584         {
585             Log::fatal("irr_driver", "Couldn't initialise irrlicht device. Quitting.\n");
586         }
587 
588         CVS->init();
589     }
590 #endif
591 
592     m_scene_manager = m_device->getSceneManager();
593     m_gui_env       = m_device->getGUIEnvironment();
594     m_video_driver  = m_device->getVideoDriver();
595 
596     B3DMeshLoader* b3dl = new B3DMeshLoader(m_scene_manager);
597     m_scene_manager->addExternalMeshLoader(b3dl);
598     b3dl->drop();
599 
600     SPMeshLoader* spml = new SPMeshLoader(m_scene_manager);
601     m_scene_manager->addExternalMeshLoader(spml);
602     spml->drop();
603 
604     m_actual_screen_size = m_video_driver->getCurrentRenderTargetSize();
605 
606 #ifdef ENABLE_RECORDER
607     ogrRegGeneralCallback(OGR_CBT_START_RECORDING,
608         [] (void* user_data)
609         {
610             // Make sure reset progress bar each time
611             MessageQueue::showProgressBar(-1, L"");
612             MessageQueue::add
613             (MessageQueue::MT_GENERIC, _("Video recording started."));
614         }, NULL);
615     ogrRegStringCallback(OGR_CBT_ERROR_RECORDING,
616         [](const char* s, void* user_data)
617         { Log::error("openglrecorder", "%s", s); }, NULL);
618     ogrRegStringCallback(OGR_CBT_SAVED_RECORDING,
619         [] (const char* s, void* user_data) { MessageQueue::add
620         (MessageQueue::MT_GENERIC, _("Video saved in \"%s\".", s));
621         }, NULL);
622     ogrRegIntCallback(OGR_CBT_PROGRESS_RECORDING,
623         [] (const int i, void* user_data)
624         { MessageQueue::showProgressBar(i, _("Encoding progress:")); }, NULL);
625 
626     RecorderConfig cfg;
627     cfg.m_triple_buffering = 1;
628     cfg.m_record_audio = 1;
629     cfg.m_width = m_actual_screen_size.Width;
630     cfg.m_height = m_actual_screen_size.Height;
631     int vf = UserConfigParams::m_video_format;
632     cfg.m_video_format = (VideoFormat)vf;
633     cfg.m_audio_format = OGR_AF_VORBIS;
634     cfg.m_audio_bitrate = UserConfigParams::m_audio_bitrate;
635     cfg.m_video_bitrate = UserConfigParams::m_video_bitrate;
636     cfg.m_record_fps = UserConfigParams::m_record_fps;
637     cfg.m_record_jpg_quality = UserConfigParams::m_recorder_jpg_quality;
638     if (ogrInitConfig(&cfg) == 0)
639     {
640         Log::error("irr_driver",
641             "RecorderConfig is invalid, use the default one.");
642     }
643 
644     ogrRegReadPixelsFunction([]
645         (int x, int y, int w, int h, unsigned int f, unsigned int t, void* d)
646         { glReadPixels(x, y, w, h, f, t, d); });
647 
648 #ifndef USE_GLES2
649     ogrRegPBOFunctions([](int n, unsigned int* b) { glGenBuffers(n, b); },
650         [](unsigned int t, unsigned int b) { glBindBuffer(t, b); },
651         [](unsigned int t, ptrdiff_t s, const void* d, unsigned int u)
652         { glBufferData(t, s, d, u); },
653         [](int n, const unsigned int* b) { glDeleteBuffers(n, b); },
654         [](unsigned int t, unsigned int a) { return glMapBuffer(t, a); },
655         [](unsigned int t) { return glUnmapBuffer(t); });
656 #else
657     ogrRegPBOFunctionsRange([](int n, unsigned int* b) { glGenBuffers(n, b); },
658         [](unsigned int t, unsigned int b) { glBindBuffer(t, b); },
659         [](unsigned int t, ptrdiff_t s, const void* d, unsigned int u)
660         { glBufferData(t, s, d, u); },
661         [](int n, const unsigned int* b) { glDeleteBuffers(n, b); },
662         [](unsigned int t, ptrdiff_t o, ptrdiff_t l, unsigned int a)
663         { return glMapBufferRange(t, o, l, a); },
664         [](unsigned int t) { return glUnmapBuffer(t); });
665 #endif
666 
667 #endif
668 
669 #ifndef SERVER_ONLY
670     if (CVS->isGLSL())
671     {
672         m_renderer = new ShaderBasedRenderer();
673         preloadShaders();
674     }
675     else
676         m_renderer = new FixedPipelineRenderer();
677 #endif
678 
679     if (UserConfigParams::m_shadows_resolution != 0 &&
680         (UserConfigParams::m_shadows_resolution < 512 ||
681          UserConfigParams::m_shadows_resolution > 2048))
682     {
683         Log::warn("irr_driver",
684                "Invalid value for UserConfigParams::m_shadows_resolution : %i",
685             (int)UserConfigParams::m_shadows_resolution);
686         UserConfigParams::m_shadows_resolution = 0;
687     }
688 
689     // This remaps the window, so it has to be done before the clear to avoid flicker
690     m_device->setResizable(false);
691 
692     // Immediate clear to black for a nicer user loading experience
693     m_video_driver->beginScene(/*backBuffer clear*/true, /* Z */ false);
694     m_video_driver->endScene();
695 
696 #ifndef SERVER_ONLY
697     if (CVS->isGLSL())
698     {
699         Log::info("irr_driver", "GLSL supported.");
700     }
701 
702     // m_glsl might be reset in rtt if an error occurs.
703     if (CVS->isGLSL())
704     {
705         m_mrt.clear();
706         m_mrt.reallocate(2);
707     }
708     else
709     {
710         Log::warn("irr_driver", "Using the fixed pipeline (old GPU, or "
711                                 "shaders disabled in options)");
712     }
713 #endif
714 
715     // Only change video driver settings if we are showing graphics
716     if (!GUIEngine::isNoGraphics())
717     {
718         m_device->setWindowClass("SuperTuxKart");
719         m_device->setWindowCaption(L"SuperTuxKart");
720         m_device->getVideoDriver()
721             ->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
722         m_device->getVideoDriver()
723             ->setTextureCreationFlag(video::ETCF_OPTIMIZED_FOR_QUALITY, true);
724 
725         // Force creation of mipmaps even if the mipmaps flag in a b3d file
726         // does not set the 'enable mipmap' flag.
727         m_scene_manager->getParameters()
728             ->setAttribute(scene::B3D_LOADER_IGNORE_MIPMAP_FLAG, true);
729     } // If showing graphics
730 
731     // Initialize material2D
732     video::SMaterial& material2D = m_video_driver->getMaterial2D();
733     material2D.setFlag(video::EMF_ANTI_ALIASING, true);
734     for (unsigned int n=0; n<video::MATERIAL_MAX_TEXTURES; n++)
735     {
736         material2D.TextureLayer[n].BilinearFilter = false;
737         material2D.TextureLayer[n].TrilinearFilter = true;
738         material2D.TextureLayer[n].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
739         material2D.TextureLayer[n].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
740 
741         //material2D.TextureLayer[n].LODBias = 16;
742         material2D.UseMipMaps = true;
743     }
744     material2D.AntiAliasing=video::EAAM_FULL_BASIC;
745     //m_video_driver->enableMaterial2D();
746 
747 #ifndef SERVER_ONLY
748     // set cursor visible by default (what's the default is not too clearly documented,
749     // so let's decide ourselves...)
750     if (!GUIEngine::isNoGraphics())
751         m_device->getCursorControl()->setVisible(true);
752 #endif
753     m_pointer_shown = true;
754 
755     if (GUIEngine::isNoGraphics())
756         return;
757 }   // initDevice
758 
759 // ----------------------------------------------------------------------------
setMaxTextureSize()760 void IrrDriver::setMaxTextureSize()
761 {
762     const unsigned max =
763         (UserConfigParams::m_high_definition_textures & 0x01) == 0 ?
764         UserConfigParams::m_max_texture_size : 2048;
765     io::IAttributes &att = m_video_driver->getNonConstDriverAttributes();
766     att.setAttribute("MAX_TEXTURE_SIZE", core::dimension2du(max, max));
767 }   // setMaxTextureSize
768 
769 // ----------------------------------------------------------------------------
unsetMaxTextureSize()770 void IrrDriver::unsetMaxTextureSize()
771 {
772     io::IAttributes &att = m_video_driver->getNonConstDriverAttributes();
773     att.setAttribute("MAX_TEXTURE_SIZE", core::dimension2du(2048, 2048));
774 }   // unsetMaxTextureSize
775 
776 // ----------------------------------------------------------------------------
cleanSunInterposer()777 void IrrDriver::cleanSunInterposer()
778 {
779     delete m_sun_interposer;
780     m_sun_interposer = NULL;
781 }   // cleanSunInterposer
782 
783 // ----------------------------------------------------------------------------
createSunInterposer()784 void IrrDriver::createSunInterposer()
785 {
786 #ifndef SERVER_ONLY
787     scene::IMesh * sphere = m_scene_manager->getGeometryCreator()
788         ->createSphereMesh(1, 16, 16);
789     Material* material = material_manager->getDefaultSPMaterial("solid");
790     m_sun_interposer = new SP::SPDynamicDrawCall
791         (scene::EPT_TRIANGLES, NULL/*shader*/, material);
792     for (unsigned i = 0; i < sphere->getMeshBufferCount(); i++)
793     {
794         scene::IMeshBuffer* mb = sphere->getMeshBuffer(i);
795         if (!mb)
796         {
797             continue;
798         }
799         assert(mb->getVertexType() == video::EVT_STANDARD);
800         video::S3DVertex* v_ptr = (video::S3DVertex*)mb->getVertices();
801         uint16_t* idx_ptr = mb->getIndices();
802         for (unsigned j = 0; j < mb->getIndexCount(); j++)
803         {
804             // For sun interposer we only need position for glow shader
805             video::S3DVertexSkinnedMesh sp;
806             const unsigned v_idx = idx_ptr[j];
807             sp.m_position = v_ptr[v_idx].Pos;
808             m_sun_interposer->addSPMVertex(sp);
809         }
810     }
811     m_sun_interposer->recalculateBoundingBox();
812     m_sun_interposer->setPosition(Track::getCurrentTrack()
813         ->getGodRaysPosition());
814     m_sun_interposer->setScale(core::vector3df(20));
815     sphere->drop();
816 #endif
817 }
818 
819 //-----------------------------------------------------------------------------
getOpenGLData(std::string * vendor,std::string * renderer,std::string * version)820 void IrrDriver::getOpenGLData(std::string *vendor, std::string *renderer,
821                               std::string *version)
822 {
823 #ifndef SERVER_ONLY
824     if (GUIEngine::isNoGraphics())
825         return;
826 
827     *vendor   = (char*)glGetString(GL_VENDOR  );
828     *renderer = (char*)glGetString(GL_RENDERER);
829     *version  = (char*)glGetString(GL_VERSION );
830 #endif
831 }   // getOpenGLData
832 
833 //-----------------------------------------------------------------------------
showPointer()834 void IrrDriver::showPointer()
835 {
836 #ifndef SERVER_ONLY
837     if (GUIEngine::isNoGraphics())
838         return;
839 
840     if (!m_pointer_shown)
841     {
842         m_pointer_shown = true;
843         this->getDevice()->getCursorControl()->setVisible(true);
844     }
845 #endif
846 }   // showPointer
847 
848 //-----------------------------------------------------------------------------
hidePointer()849 void IrrDriver::hidePointer()
850 {
851 #ifndef SERVER_ONLY
852     if (GUIEngine::isNoGraphics())
853         return;
854 
855     // always visible in artist debug mode, to be able to use the context menu
856     if (UserConfigParams::m_artist_debug_mode)
857     {
858         this->getDevice()->getCursorControl()->setVisible(true);
859         return;
860     }
861 
862     if (m_pointer_shown)
863     {
864         m_pointer_shown = false;
865         this->getDevice()->getCursorControl()->setVisible(false);
866     }
867 #endif
868 }   // hidePointer
869 
870 //-----------------------------------------------------------------------------
871 
getMouseLocation()872 core::position2di IrrDriver::getMouseLocation()
873 {
874     return this->getDevice()->getCursorControl()->getPosition();
875 }
876 
877 //-----------------------------------------------------------------------------
878 /** Moves the STK main window to coordinates (x,y)
879  *  \return true on success, false on failure
880  *          (always true on Linux at the moment)
881  */
moveWindow(int x,int y)882 bool IrrDriver::moveWindow(int x, int y)
883 {
884 #ifndef SERVER_ONLY
885     bool success = m_device->moveWindow(x, y);
886 
887     if (!success)
888     {
889         Log::warn("irr_driver", "Could not set window location\n");
890         return false;
891     }
892 #endif
893     return true;
894 }
895 //-----------------------------------------------------------------------------
896 
changeResolution(const int w,const int h,const bool fullscreen)897 void IrrDriver::changeResolution(const int w, const int h,
898                                  const bool fullscreen)
899 {
900     // update user config values
901     UserConfigParams::m_prev_width = UserConfigParams::m_width;
902     UserConfigParams::m_prev_height = UserConfigParams::m_height;
903     UserConfigParams::m_prev_fullscreen = UserConfigParams::m_fullscreen;
904 
905     UserConfigParams::m_width = w;
906     UserConfigParams::m_height = h;
907     UserConfigParams::m_fullscreen = fullscreen;
908 
909     // Setting this flag will trigger a call to applyResolutionSetting()
910     // in the next update call. This avoids the problem that changeResolution
911     // is actually called from the gui, i.e. the event loop, i.e. while the
912     // old device is active - so we can't delete this device (which we must
913     // do in applyResolutionSettings
914     if (w < 1024 || h < 720)
915         m_resolution_changing = RES_CHANGE_YES_WARN;
916     else
917         m_resolution_changing = RES_CHANGE_YES;
918 }   // changeResolution
919 
920 //-----------------------------------------------------------------------------
921 
applyResolutionSettings(bool recreate_device)922 void IrrDriver::applyResolutionSettings(bool recreate_device)
923 {
924 #ifndef SERVER_ONLY
925     // show black before resolution switch so we don't see OpenGL's buffer
926     // garbage during switch
927     if (recreate_device)
928     {
929         m_video_driver->beginScene(true, true, video::SColor(255,100,101,140));
930         GL32_draw2DRectangle( video::SColor(255, 0, 0, 0),
931                                 core::rect<s32>(0, 0,
932                                                 UserConfigParams::m_prev_width,
933                                                 UserConfigParams::m_prev_height) );
934         m_video_driver->endScene();
935     }
936     track_manager->removeAllCachedData();
937     delete attachment_manager;
938     attachment_manager = NULL;
939     ProjectileManager::get()->removeTextures();
940     ItemManager::removeTextures();
941     kart_properties_manager->unloadAllKarts();
942     delete powerup_manager;
943     powerup_manager = NULL;
944     Referee::cleanup();
945     ParticleKindManager::get()->cleanup();
946     delete input_manager;
947     input_manager = NULL;
948     delete font_manager;
949     font_manager = NULL;
950     GUIEngine::clear();
951     GUIEngine::cleanUp();
952 
953     if (recreate_device)
954     {
955         m_device->closeDevice();
956         m_device->clearSystemMessages();
957         m_device->run();
958     }
959     delete material_manager;
960     material_manager = NULL;
961 
962     // ---- Reinit
963     // FIXME: this load sequence is (mostly) duplicated from main.cpp!!
964     // That's just error prone
965     // (we're sure to update main.cpp at some point and forget this one...)
966     STKTexManager::getInstance()->kill();
967 #ifdef ENABLE_RECORDER
968     if (recreate_device)
969     {
970         ogrDestroy();
971         m_recording = false;
972     }
973 #endif
974     // initDevice will drop the current device.
975     if (recreate_device)
976     {
977         delete m_renderer;
978         m_renderer = NULL;
979         SharedGPUObjects::reset();
980 
981         SP::setMaxTextureSize();
982         initDevice();
983     }
984 #ifndef SERVER_ONLY
985     for (unsigned i = 0; i < Q_LAST; i++)
986     {
987         m_perf_query[i]->reset();
988     }
989     if (!recreate_device)
990     {
991         SP::SPTextureManager::get()->stopThreads();
992         SP::SPShaderManager::destroy();
993         SP::SPTextureManager::destroy();
994         ShaderBase::killShaders();
995         ShaderFilesManager::getInstance()->removeAllShaderFiles();
996         unsetMaxTextureSize();
997         SP::setMaxTextureSize();
998         m_renderer->createPostProcessing();
999     }
1000     if (CVS->isGLSL())
1001         SP::loadShaders();
1002 #endif
1003 
1004     font_manager = new FontManager();
1005     font_manager->loadFonts();
1006 
1007     input_manager = new InputManager();
1008     input_manager->setMode(InputManager::MENU);
1009     // Input manager set first so it recieves SDL joystick event
1010     // Re-init GUI engine
1011     GUIEngine::init(m_device, m_video_driver, StateManager::get());
1012     // If not recreate device we need to add the previous joystick manually
1013     if (!recreate_device)
1014         input_manager->addJoystick();
1015 
1016     setMaxTextureSize();
1017     //material_manager->reInit();
1018     material_manager = new MaterialManager();
1019     material_manager->loadMaterial();
1020     powerup_manager = new PowerupManager();
1021     attachment_manager = new AttachmentManager();
1022 
1023     GUIEngine::addLoadingIcon(
1024         irr_driver->getTexture(file_manager
1025                                ->getAsset(FileManager::GUI_ICON,"options_video.png"))
1026                              );
1027 
1028     file_manager->pushTextureSearchPath(file_manager->getAsset(FileManager::MODEL,""), "models");
1029     const std::string materials_file =
1030         file_manager->getAssetChecked(FileManager::MODEL, "materials.xml");
1031     if (materials_file != "")
1032     {
1033         material_manager->addSharedMaterial(materials_file);
1034     }
1035 
1036     powerup_manager->loadPowerupsModels();
1037     ItemManager::loadDefaultItemMeshes();
1038     ProjectileManager::get()->loadData();
1039     Referee::init();
1040     GUIEngine::addLoadingIcon(
1041         irr_driver->getTexture(file_manager->getAsset(FileManager::GUI_ICON,"gift.png")) );
1042 
1043 
1044     kart_properties_manager->loadAllKarts();
1045 
1046     attachment_manager->loadModels();
1047     file_manager->popTextureSearchPath();
1048     std::string banana = file_manager->getAsset(FileManager::GUI_ICON, "banana.png");
1049     GUIEngine::addLoadingIcon(irr_driver->getTexture(banana) );
1050     // No need to reload cached track data (track_manager->cleanAllCachedData
1051     // above) - this happens dynamically when the tracks are loaded.
1052     GUIEngine::reshowCurrentScreen();
1053     MessageQueue::updatePosition();
1054     // Preload the explosion effects (explode.png)
1055     ParticleKindManager::get()->getParticles("explosion.xml");
1056 #endif   // !SERVER_ONLY
1057 }   // applyResolutionSettings
1058 
1059 // ----------------------------------------------------------------------------
1060 
cancelResChange()1061 void IrrDriver::cancelResChange()
1062 {
1063     UserConfigParams::m_width = UserConfigParams::m_prev_width;
1064     UserConfigParams::m_height = UserConfigParams::m_prev_height;
1065     UserConfigParams::m_fullscreen = UserConfigParams::m_prev_fullscreen;
1066 
1067     // This will trigger calling applyResolutionSettings in update(). This is
1068     // necessary to avoid that the old screen is deleted, while it is
1069     // still active (i.e. sending out events which triggered the change
1070     // of resolution
1071     // Setting this flag will trigger a call to applyResolutionSetting()
1072     // in the next update call. This avoids the problem that changeResolution
1073     // is actually called from the gui, i.e. the event loop, i.e. while the
1074     // old device is active - so we can't delete this device (which we must
1075     // do in applyResolutionSettings)
1076     m_resolution_changing=RES_CHANGE_SAME;
1077 
1078 }   // cancelResChange
1079 
1080 // ----------------------------------------------------------------------------
1081 /** Prints statistics about rendering, e.g. number of drawn and culled
1082  *  triangles etc. Note that printing this information will also slow
1083  *  down STK.
1084  */
printRenderStats()1085 void IrrDriver::printRenderStats()
1086 {
1087     io::IAttributes * attr = m_scene_manager->getParameters();
1088     Log::verbose("irr_driver",
1089            "[%ls], FPS:%3d Tri:%.03fm Cull %d/%d nodes (%d,%d,%d)\n",
1090            m_video_driver->getName(),
1091            m_video_driver->getFPS (),
1092            (f32) m_video_driver->getPrimitiveCountDrawn( 0 ) * ( 1.f / 1000000.f ),
1093            attr->getAttributeAsInt ( "culled" ),
1094            attr->getAttributeAsInt ( "calls" ),
1095            attr->getAttributeAsInt ( "drawn_solid" ),
1096            attr->getAttributeAsInt ( "drawn_transparent" ),
1097            attr->getAttributeAsInt ( "drawn_transparent_effect" )
1098            );
1099 
1100 }   // printRenderStats
1101 
1102 // ----------------------------------------------------------------------------
1103 /** Loads an animated mesh and returns a pointer to it.
1104  *  \param filename File to load.
1105  */
getAnimatedMesh(const std::string & filename)1106 scene::IAnimatedMesh *IrrDriver::getAnimatedMesh(const std::string &filename)
1107 {
1108     scene::IAnimatedMesh *m  = NULL;
1109 
1110     if (StringUtils::getExtension(filename) == "b3dz")
1111     {
1112         // compressed file
1113         io::IFileSystem* file_system = getDevice()->getFileSystem();
1114         if (!file_system->addFileArchive(filename.c_str(),
1115                                          /*ignoreCase*/false,
1116                                          /*ignorePath*/true, io::EFAT_ZIP))
1117         {
1118             Log::error("irr_driver",
1119                        "getMesh: Failed to open zip file <%s>\n",
1120                        filename.c_str());
1121             return NULL;
1122         }
1123 
1124         // Get the recently added archive
1125         io::IFileArchive* zip_archive =
1126         file_system->getFileArchive(file_system->getFileArchiveCount()-1);
1127         io::IReadFile* content = zip_archive->createAndOpenFile(0);
1128         m = m_scene_manager->getMesh(content);
1129         content->drop();
1130 
1131         file_system->removeFileArchive(file_system->getFileArchiveCount()-1);
1132     }
1133     else
1134     {
1135         m = m_scene_manager->getMesh(filename.c_str());
1136     }
1137 
1138     if(!m) return NULL;
1139 
1140     setAllMaterialFlags(m);
1141 
1142     return m;
1143 }   // getAnimatedMesh
1144 
1145 // ----------------------------------------------------------------------------
1146 
1147 /** Loads a non-animated mesh and returns a pointer to it.
1148  *  \param filename  File to load.
1149  */
getMesh(const std::string & filename)1150 scene::IMesh *IrrDriver::getMesh(const std::string &filename)
1151 {
1152     scene::IAnimatedMesh* am = getAnimatedMesh(filename);
1153     if (am == NULL)
1154     {
1155         Log::error("irr_driver", "Cannot load mesh <%s>\n",
1156                    filename.c_str());
1157         return NULL;
1158     }
1159     return am->getMesh(0);
1160 }   // getMesh
1161 
1162 // ----------------------------------------------------------------------------
1163 /** Sets the material flags in this mesh depending on the settings in
1164  *  material_manager.
1165  *  \param mesh  The mesh to change the settings in.
1166  */
setAllMaterialFlags(scene::IMesh * mesh) const1167 void IrrDriver::setAllMaterialFlags(scene::IMesh *mesh) const
1168 {
1169 #ifndef SERVER_ONLY
1170     if (CVS->isGLSL())
1171     {
1172         return;
1173     }
1174 #endif
1175     unsigned int n=mesh->getMeshBufferCount();
1176     for(unsigned int i=0; i<n; i++)
1177     {
1178         scene::IMeshBuffer *mb = mesh->getMeshBuffer(i);
1179         video::SMaterial &irr_material = mb->getMaterial();
1180         video::ITexture* t = irr_material.getTexture(0);
1181         if (t)
1182         {
1183             material_manager->setAllMaterialFlags(t, mb);
1184         }
1185         else
1186         {
1187             material_manager->setAllUntexturedMaterialFlags(mb);
1188         }
1189     }  // for i<getMeshBufferCount()
1190 }   // setAllMaterialFlags
1191 
1192 // ----------------------------------------------------------------------------
1193 /** Converts the mesh into a water scene node.
1194  *  \param mesh The mesh which is converted into a water scene node.
1195  *  \param wave_height Height of the water waves.
1196  *  \param wave_speed Speed of the water waves.
1197  *  \param wave_length Lenght of a water wave.
1198  */
addWaterNode(scene::IMesh * mesh,scene::IMesh ** welded,float wave_height,float wave_speed,float wave_length)1199 scene::ISceneNode* IrrDriver::addWaterNode(scene::IMesh *mesh,
1200                                            scene::IMesh **welded,
1201                                            float wave_height,
1202                                            float wave_speed,
1203                                            float wave_length)
1204 {
1205     mesh->setMaterialFlag(video::EMF_GOURAUD_SHADING, true);
1206     scene::IMesh* welded_mesh = m_scene_manager->getMeshManipulator()
1207                                                ->createMeshWelded(mesh);
1208     scene::ISceneNode* out = NULL;
1209 
1210     // TODO: using cand's new WaterNode would be better, but it does not
1211     // support our material flags (like transparency, etc.)
1212     //if (!m_glsl)
1213     //{
1214         out = m_scene_manager->addWaterSurfaceSceneNode(welded_mesh,
1215                                                      wave_height, wave_speed,
1216                                                      wave_length);
1217     //} else
1218     //{
1219     //    out = new WaterNode(m_scene_manager, welded_mesh, wave_height, wave_speed,
1220     //                        wave_length);
1221     //}
1222 
1223     out->getMaterial(0).setFlag(video::EMF_GOURAUD_SHADING, true);
1224     welded_mesh->drop();  // The scene node keeps a reference
1225 
1226     *welded = welded_mesh;
1227 
1228     return out;
1229 }   // addWaterNode
1230 
1231 // ----------------------------------------------------------------------------
1232 /** Adds a mesh that will be optimised using an oct tree.
1233  *  \param mesh Mesh to add.
1234  */
addOctTree(scene::IMesh * mesh)1235 scene::IMeshSceneNode *IrrDriver::addOctTree(scene::IMesh *mesh)
1236 {
1237     return m_scene_manager->addOctreeSceneNode(mesh);
1238 }   // addOctTree
1239 
1240 // ----------------------------------------------------------------------------
1241 /** Adds a sphere with a given radius and color.
1242  *  \param radius The radius of the sphere.
1243  *  \param color The color to use (default (0,0,0,0)
1244  */
addSphere(float radius,const video::SColor & color)1245 scene::ISceneNode *IrrDriver::addSphere(float radius,
1246                                         const video::SColor &color)
1247 {
1248     scene::IMesh *mesh = m_scene_manager->getGeometryCreator()
1249                        ->createSphereMesh(radius);
1250 
1251     mesh->setMaterialFlag(video::EMF_COLOR_MATERIAL, true);
1252     video::SMaterial &m = mesh->getMeshBuffer(0)->getMaterial();
1253     m.AmbientColor    = color;
1254     m.DiffuseColor    = color;
1255     m.EmissiveColor   = color;
1256     m.BackfaceCulling = false;
1257     m.MaterialType    = video::EMT_SOLID;
1258 #ifndef SERVER_ONLY
1259     if (CVS->isGLSL())
1260     {
1261         SP::SPMesh* spm = SP::convertEVTStandard(mesh, &color);
1262         SP::SPMeshNode* spmn = new SP::SPMeshNode(spm,
1263             m_scene_manager->getRootSceneNode(), m_scene_manager, -1,
1264             "sphere");
1265         spmn->setMesh(spm);
1266         spm->drop();
1267         spmn->drop();
1268         return spmn;
1269     }
1270 #endif
1271 
1272     scene::IMeshSceneNode *node = m_scene_manager->addMeshSceneNode(mesh);
1273     mesh->drop();
1274     return node;
1275 }   // addSphere
1276 
1277 // ----------------------------------------------------------------------------
1278 /** Adds a particle scene node.
1279  */
addParticleNode(bool default_emitter)1280 scene::IParticleSystemSceneNode *IrrDriver::addParticleNode(bool default_emitter)
1281 {
1282     return m_scene_manager->addParticleSystemSceneNode(default_emitter);
1283 }   // addParticleNode
1284 
1285 // ----------------------------------------------------------------------------
1286 /** Adds a static mesh to scene. This should be used for smaller objects,
1287  *  since the node is not optimised.
1288  *  \param mesh The mesh to add.
1289  */
addMesh(scene::IMesh * mesh,const std::string & debug_name,scene::ISceneNode * parent,std::shared_ptr<RenderInfo> render_info)1290 scene::ISceneNode *IrrDriver::addMesh(scene::IMesh *mesh,
1291                                       const std::string& debug_name,
1292                                       scene::ISceneNode *parent,
1293                                       std::shared_ptr<RenderInfo> render_info)
1294 {
1295 #ifdef SERVER_ONLY
1296     return m_scene_manager->addMeshSceneNode(mesh, parent);
1297 #else
1298     if (!CVS->isGLSL())
1299         return m_scene_manager->addMeshSceneNode(mesh, parent);
1300 
1301     if (!parent)
1302       parent = m_scene_manager->getRootSceneNode();
1303 
1304     scene::ISceneNode* node = NULL;
1305     SP::SPMesh* spm = dynamic_cast<SP::SPMesh*>(mesh);
1306     if (spm || mesh == NULL)
1307     {
1308         SP::SPMeshNode* spmn = new SP::SPMeshNode(spm, parent, m_scene_manager,
1309             -1, debug_name, core::vector3df(0, 0, 0), core::vector3df(0, 0, 0),
1310             core::vector3df(1.0f, 1.0f, 1.0f), render_info);
1311         spmn->setMesh(spm);
1312         spmn->setAnimationState(false);
1313         node = spmn;
1314     }
1315     else
1316     {
1317         Log::warn("IrrDriver", "Use only spm in glsl");
1318         return NULL;
1319     }
1320     node->drop();
1321 
1322     return node;
1323 #endif
1324 }   // addMesh
1325 
1326 // ----------------------------------------------------------------------------
1327 
addPerCameraNode(scene::ISceneNode * node,scene::ICameraSceneNode * camera,scene::ISceneNode * parent)1328 PerCameraNode *IrrDriver::addPerCameraNode(scene::ISceneNode* node,
1329                                            scene::ICameraSceneNode* camera,
1330                                            scene::ISceneNode *parent)
1331 {
1332     return new PerCameraNode((parent ? parent
1333                                      : m_scene_manager->getRootSceneNode()),
1334                              m_scene_manager, -1, camera, node);
1335 }   // addNode
1336 
1337 // ----------------------------------------------------------------------------
1338 /** Adds a billboard node to scene.
1339  */
addBillboard(const core::dimension2d<f32> size,const std::string & tex_name,scene::ISceneNode * parent)1340 scene::ISceneNode *IrrDriver::addBillboard(const core::dimension2d< f32 > size,
1341                                            const std::string& tex_name,
1342                                            scene::ISceneNode* parent)
1343 {
1344     scene::IBillboardSceneNode* node;
1345     node = m_scene_manager->addBillboardSceneNode(parent, size);
1346 
1347     const bool full_path = tex_name.find('/') != std::string::npos;
1348 
1349     Material* m = material_manager->getMaterial(tex_name, full_path,
1350         /*make_permanent*/false, /*complain_if_not_found*/true,
1351         /*strip_path*/full_path, /*install*/false);
1352 
1353     video::ITexture* tex = m->getTexture(true/*srgb*/,
1354         m->getShaderName() == "additive" ||
1355         m->getShaderName() == "alphablend" ?
1356         true : false/*premul_alpha*/);
1357 
1358     assert(node->getMaterialCount() > 0);
1359     node->setMaterialTexture(0, tex);
1360     if (!(m->getShaderName() == "additive" ||
1361         m->getShaderName() == "alphablend"))
1362     {
1363         // Alpha test for billboard otherwise
1364         m->setShaderName("alphatest");
1365     }
1366     m->setMaterialProperties(&(node->getMaterial(0)), NULL);
1367     return node;
1368 }   // addBillboard
1369 
1370 // ----------------------------------------------------------------------------
1371 /** Creates a quad mesh with a given material.
1372  *  \param material The material to use (NULL if no material).
1373  *  \param create_one_quad If true creates one quad in the mesh.
1374  */
createQuadMesh(const video::SMaterial * material,bool create_one_quad)1375 scene::IMesh *IrrDriver::createQuadMesh(const video::SMaterial *material,
1376                                         bool create_one_quad)
1377 {
1378     scene::SMeshBuffer *buffer = new scene::SMeshBuffer();
1379     if(create_one_quad)
1380     {
1381         video::S3DVertex v;
1382         v.Pos    = core::vector3df(0,0,0);
1383         v.Normal = core::vector3df(1/sqrt(2.0f), 1/sqrt(2.0f), 0);
1384 
1385         // Add the vertices
1386         // ----------------
1387         buffer->Vertices.push_back(v);
1388         buffer->Vertices.push_back(v);
1389         buffer->Vertices.push_back(v);
1390         buffer->Vertices.push_back(v);
1391 
1392         // Define the indices for the triangles
1393         // ------------------------------------
1394         buffer->Indices.push_back(0);
1395         buffer->Indices.push_back(1);
1396         buffer->Indices.push_back(2);
1397 
1398         buffer->Indices.push_back(0);
1399         buffer->Indices.push_back(2);
1400         buffer->Indices.push_back(3);
1401     }
1402     if(material)
1403         buffer->Material = *material;
1404     scene::SMesh *mesh   = new scene::SMesh();
1405     mesh->addMeshBuffer(buffer);
1406     mesh->recalculateBoundingBox();
1407     buffer->drop();
1408     return mesh;
1409 }   // createQuadMesh
1410 
1411 // ----------------------------------------------------------------------------
1412 /** Creates a quad mesh buffer with a given width and height (z coordinate is
1413  *  0).
1414  *  \param material The material to use for this quad.
1415  *  \param w Width of the quad.
1416  *  \param h Height of the quad.
1417  */
createTexturedQuadMesh(const video::SMaterial * material,const double w,const double h)1418 scene::IMesh *IrrDriver::createTexturedQuadMesh(const video::SMaterial *material,
1419                                                 const double w, const double h)
1420 {
1421     scene::SMeshBuffer *buffer = new scene::SMeshBuffer();
1422 
1423     const float w_2 = (float)w/2.0f;
1424     const float h_2 = (float)h/2.0f;
1425 
1426     video::S3DVertex v1;
1427     v1.Pos    = core::vector3df(-w_2,-h_2,0);
1428     v1.Normal = core::vector3df(0, 0, 1);
1429     v1.TCoords = core::vector2d<f32>(1,1);
1430     v1.Color = video::SColor(255, 255, 255, 255);
1431 
1432     video::S3DVertex v2;
1433     v2.Pos    = core::vector3df(w_2,-h_2,0);
1434     v2.Normal = core::vector3df(0, 0, 1);
1435     v2.TCoords = core::vector2d<f32>(0,1);
1436     v2.Color = video::SColor(255, 255, 255, 255);
1437 
1438     video::S3DVertex v3;
1439     v3.Pos    = core::vector3df(w_2,h_2,0);
1440     v3.Normal = core::vector3df(0, 0, 1);
1441     v3.TCoords = core::vector2d<f32>(0,0);
1442     v3.Color = video::SColor(255, 255, 255, 255);
1443 
1444     video::S3DVertex v4;
1445     v4.Pos    = core::vector3df(-w_2,h_2,0);
1446     v4.Normal = core::vector3df(0, 0, 1);
1447     v4.TCoords = core::vector2d<f32>(1,0);
1448     v4.Color = video::SColor(255, 255, 255, 255);
1449 
1450     // Add the vertices
1451     // ----------------
1452     buffer->Vertices.push_back(v1);
1453     buffer->Vertices.push_back(v2);
1454     buffer->Vertices.push_back(v3);
1455     buffer->Vertices.push_back(v4);
1456 
1457     // Define the indices for the triangles
1458     // ------------------------------------
1459     buffer->Indices.push_back(0);
1460     buffer->Indices.push_back(1);
1461     buffer->Indices.push_back(2);
1462 
1463     buffer->Indices.push_back(0);
1464     buffer->Indices.push_back(2);
1465     buffer->Indices.push_back(3);
1466 
1467     if (material) buffer->Material = *material;
1468     scene::SMesh *mesh = new scene::SMesh();
1469     mesh->addMeshBuffer(buffer);
1470     mesh->recalculateBoundingBox();
1471     buffer->drop();
1472     return mesh;
1473 }   // createQuadMesh
1474 
1475 // ----------------------------------------------------------------------------
1476 /** Removes a scene node from the scene.
1477  *  \param node The scene node to remove.
1478  */
removeNode(scene::ISceneNode * node)1479 void IrrDriver::removeNode(scene::ISceneNode *node)
1480 {
1481     node->remove();
1482 }   // removeNode
1483 
1484 // ----------------------------------------------------------------------------
1485 /** Removes a mesh from the mesh cache, freeing the memory.
1486  *  \param mesh The mesh to remove.
1487  */
removeMeshFromCache(scene::IMesh * mesh)1488 void IrrDriver::removeMeshFromCache(scene::IMesh *mesh)
1489 {
1490     m_scene_manager->getMeshCache()->removeMesh(mesh);
1491 }   // removeMeshFromCache
1492 
1493 // ----------------------------------------------------------------------------
1494 /** Removes a texture from irrlicht's texture cache.
1495  *  \param t The texture to remove.
1496  */
removeTexture(video::ITexture * t)1497 void IrrDriver::removeTexture(video::ITexture *t)
1498 {
1499     STKTexture* stkt = dynamic_cast<STKTexture*>(t);
1500     if (stkt)
1501     {
1502         STKTexManager::getInstance()->removeTexture(stkt);
1503         return;
1504     }
1505     m_video_driver->removeTexture(t);
1506 }   // removeTexture
1507 
1508 // ----------------------------------------------------------------------------
1509 /** Adds an animated mesh to the scene.
1510  *  \param mesh The animated mesh to add.
1511  */
addAnimatedMesh(scene::IAnimatedMesh * mesh,const std::string & debug_name,scene::ISceneNode * parent,std::shared_ptr<RenderInfo> render_info)1512 scene::IAnimatedMeshSceneNode *IrrDriver::addAnimatedMesh(scene::IAnimatedMesh *mesh,
1513     const std::string& debug_name, scene::ISceneNode* parent,
1514     std::shared_ptr<RenderInfo> render_info)
1515 {
1516     scene::IAnimatedMeshSceneNode* node;
1517 #ifndef SERVER_ONLY
1518     SP::SPMesh* spm = dynamic_cast<SP::SPMesh*>(mesh);
1519     if (CVS->isGLSL() && (spm || mesh == NULL))
1520     {
1521         if (!parent)
1522         {
1523             parent = m_scene_manager->getRootSceneNode();
1524         }
1525         SP::SPMeshNode* spmn = new SP::SPMeshNode(spm, parent, m_scene_manager,
1526             -1, debug_name, core::vector3df(0, 0, 0), core::vector3df(0, 0, 0),
1527             core::vector3df(1.0f, 1.0f, 1.0f), render_info);
1528         spmn->drop();
1529         spmn->setMesh(mesh);
1530         node = spmn;
1531     }
1532     else
1533 #endif
1534     {
1535         node = m_scene_manager->addAnimatedMeshSceneNode(mesh, parent, -1,
1536             core::vector3df(0, 0, 0),
1537             core::vector3df(0, 0, 0),
1538             core::vector3df(1, 1, 1),
1539             /*addIfMeshIsZero*/true);
1540     }
1541     return node;
1542 
1543 }   // addAnimatedMesh
1544 
1545 // ----------------------------------------------------------------------------
1546 /** Adds a sky dome. Documentation from irrlicht:
1547  *  A skydome is a large (half-) sphere with a panoramic texture on the inside
1548  *  and is drawn around the camera position.
1549  *  \param texture: Texture for the dome.
1550  *  \param horiRes: Number of vertices of a horizontal layer of the sphere.
1551  *  \param vertRes: Number of vertices of a vertical layer of the sphere.
1552  *  \param texturePercentage: How much of the height of the texture is used.
1553  *         Should be between 0 and 1.
1554  *  \param spherePercentage: How much of the sphere is drawn. Value should be
1555  *          between 0 and 2, where 1 is an exact half-sphere and 2 is a full
1556  *          sphere.
1557  */
addSkyDome(video::ITexture * texture,int hori_res,int vert_res,float texture_percent,float sphere_percent)1558 scene::ISceneNode *IrrDriver::addSkyDome(video::ITexture *texture,
1559                                          int hori_res, int vert_res,
1560                                          float texture_percent,
1561                                          float sphere_percent)
1562 {
1563     Log::error("skybox", "Using deprecated SkyDome");
1564     return m_scene_manager->addSkyDomeSceneNode(texture, hori_res, vert_res,
1565                                                 texture_percent,
1566                                                 sphere_percent);
1567 }   // addSkyDome
1568 
1569 // ----------------------------------------------------------------------------
1570 /** Adds a skybox using. Irrlicht documentation:
1571  *  A skybox is a big cube with 6 textures on it and is drawn around the camera
1572  *  position.
1573  *  \param top: Texture for the top plane of the box.
1574  *  \param bottom: Texture for the bottom plane of the box.
1575  *  \param left: Texture for the left plane of the box.
1576  *  \param right: Texture for the right plane of the box.
1577  *  \param front: Texture for the front plane of the box.
1578  *  \param back: Texture for the back plane of the box.
1579  */
addSkyBox(const std::vector<video::ITexture * > & texture,const std::vector<video::ITexture * > & spherical_harmonics_textures)1580 scene::ISceneNode *IrrDriver::addSkyBox(const std::vector<video::ITexture*> &texture,
1581     const std::vector<video::ITexture*> &spherical_harmonics_textures)
1582 {
1583 #ifndef SERVER_ONLY
1584     assert(texture.size() == 6);
1585 
1586     m_renderer->addSkyBox(texture, spherical_harmonics_textures);
1587 
1588 #endif
1589     return m_scene_manager->addSkyBoxSceneNode(texture[0], texture[1],
1590                                                texture[2], texture[3],
1591                                                texture[4], texture[5]);
1592 }   // addSkyBox
1593 
1594 // ----------------------------------------------------------------------------
suppressSkyBox()1595 void IrrDriver::suppressSkyBox()
1596 {
1597 #ifndef SERVER_ONLY
1598     m_renderer->removeSkyBox();;
1599 #endif
1600 }   // suppressSkyBox
1601 
1602 // ----------------------------------------------------------------------------
1603 /** Adds a camera to the scene.
1604  */
addCameraSceneNode()1605 scene::ICameraSceneNode *IrrDriver::addCameraSceneNode()
1606  {
1607      return m_scene_manager->addCameraSceneNode();
1608  }   // addCameraSceneNode
1609 
1610 // ----------------------------------------------------------------------------
1611 /** Removes a camera. This can't be done with removeNode() since the camera
1612  *  can be marked as active, meaning a drop will not delete it. While this
1613  *  doesn't really cause a memory leak (the camera is removed the next time
1614  *  a camera is added), it's a bit cleaner and easier to check for memory
1615  *  leaks, since the scene root should now always be empty.
1616  */
removeCameraSceneNode(scene::ICameraSceneNode * camera)1617 void IrrDriver::removeCameraSceneNode(scene::ICameraSceneNode *camera)
1618 {
1619     if(camera==m_scene_manager->getActiveCamera())
1620         m_scene_manager->setActiveCamera(NULL);    // basically causes a drop
1621     camera->remove();
1622 }   // removeCameraSceneNode
1623 
1624 // ----------------------------------------------------------------------------
1625 /** Loads a texture from a file and returns the texture object. This is just
1626  *  a convenient wrapper which loads the texture from a STK asset directory.
1627  *  It calls the file manager to get the full path, then calls the normal
1628  *  getTexture() function.s
1629  *  \param type The FileManager::AssetType of the texture.
1630  *  \param filename File name of the texture to load.
1631  *  \param is_premul If the alpha values needd to be multiplied for
1632  *         all pixels.
1633  *  \param is_prediv If the alpha value needs to be divided into
1634  *         each pixel.
1635  */
getTexture(FileManager::AssetType type,const std::string & filename,bool is_premul,bool is_prediv,bool complain_if_not_found)1636 video::ITexture *IrrDriver::getTexture(FileManager::AssetType type,
1637                                        const std::string &filename,
1638                                        bool is_premul,
1639                                        bool is_prediv,
1640                                        bool complain_if_not_found)
1641 {
1642     const std::string path = file_manager->getAsset(type, filename);
1643     return getTexture(path, is_premul, is_prediv, complain_if_not_found);
1644 }   // getTexture
1645 
1646 // ----------------------------------------------------------------------------
1647 /** Loads a texture from a file and returns the texture object.
1648  *  \param filename File name of the texture to load.
1649  *  \param is_premul If the alpha values needd to be multiplied for
1650  *         all pixels.
1651  *  \param is_prediv If the alpha value needs to be divided into
1652  *         each pixel.
1653  */
getTexture(const std::string & filename,bool is_premul,bool is_prediv,bool complain_if_not_found)1654 video::ITexture *IrrDriver::getTexture(const std::string &filename,
1655                                        bool is_premul,
1656                                        bool is_prediv,
1657                                        bool complain_if_not_found)
1658 {
1659     return STKTexManager::getInstance()->getTexture(filename);
1660 }   // getTexture
1661 
1662 // ----------------------------------------------------------------------------
1663 /** Appends a pointer to each texture used in this mesh to the vector.
1664  *  \param mesh The mesh from which the textures are being determined.
1665  *  \param texture_list The list to which to attach the pointer to.
1666  */
grabAllTextures(const scene::IMesh * mesh)1667 void IrrDriver::grabAllTextures(const scene::IMesh *mesh)
1668 {
1669 #ifndef SERVER_ONLY
1670     if (CVS->isGLSL())
1671     {
1672         // SPM files has shared_ptr auto-delete texture
1673         return;
1674     }
1675 #endif
1676     const unsigned int n = mesh->getMeshBufferCount();
1677 
1678     for(unsigned int i=0; i<n; i++)
1679     {
1680         scene::IMeshBuffer *b = mesh->getMeshBuffer(i);
1681         video::SMaterial   &m = b->getMaterial();
1682         for(unsigned int j=0; j<video::MATERIAL_MAX_TEXTURES; j++)
1683         {
1684             video::ITexture *t = m.getTexture(j);
1685             if(t)
1686                 t->grab();
1687         }   // for j < MATERIAL_MAX_TEXTURE
1688     }   // for i <getMeshBufferCount
1689 }   // grabAllTextures
1690 
1691 // ----------------------------------------------------------------------------
1692 /** Appends a pointer to each texture used in this mesh to the vector.
1693  *  \param mesh The mesh from which the textures are being determined.
1694  *  \param texture_list The list to which to attach the pointer to.
1695  */
dropAllTextures(const scene::IMesh * mesh)1696 void IrrDriver::dropAllTextures(const scene::IMesh *mesh)
1697 {
1698 #ifndef SERVER_ONLY
1699     if (CVS->isGLSL())
1700     {
1701         // SPM files has shared_ptr auto-delete texture
1702         return;
1703     }
1704 #endif
1705     const unsigned int n = mesh->getMeshBufferCount();
1706 
1707     for(unsigned int i=0; i<n; i++)
1708     {
1709         scene::IMeshBuffer *b = mesh->getMeshBuffer(i);
1710         video::SMaterial   &m = b->getMaterial();
1711         for(unsigned int j=0; j<video::MATERIAL_MAX_TEXTURES; j++)
1712         {
1713             video::ITexture *t = m.getTexture(j);
1714             if(t)
1715             {
1716                 t->drop();
1717                 if(t->getReferenceCount()==1)
1718                     removeTexture(t);
1719             }   // if t
1720         }   // for j < MATERIAL_MAX_TEXTURE
1721     }   // for i <getMeshBufferCount
1722 }   // dropAllTextures
1723 
1724 // ----------------------------------------------------------------------------
onLoadWorld()1725 void IrrDriver::onLoadWorld()
1726 {
1727 #ifndef SERVER_ONLY
1728     m_renderer->onLoadWorld();
1729 #endif
1730 }   // onLoadWorld
1731 
1732     // ----------------------------------------------------------------------------
onUnloadWorld()1733 void IrrDriver::onUnloadWorld()
1734 {
1735 #ifndef SERVER_ONLY
1736     m_renderer->onUnloadWorld();
1737 #endif
1738 }   // onUnloadWorld
1739 
1740 // ----------------------------------------------------------------------------
1741 /** Sets the ambient light.
1742  *  \param light The colour of the light to set.
1743  *  \param force_SH_computation If false, do not recompute spherical harmonics
1744  *  coefficient when spherical harmonics textures have been defined
1745  */
setAmbientLight(const video::SColorf & light,bool force_SH_computation)1746 void IrrDriver::setAmbientLight(const video::SColorf &light, bool force_SH_computation)
1747 {
1748 #ifndef SERVER_ONLY
1749     video::SColorf color = light;
1750     color.r = powf(color.r, 1.0f / 2.2f);
1751     color.g = powf(color.g, 1.0f / 2.2f);
1752     color.b = powf(color.b, 1.0f / 2.2f);
1753 
1754     m_scene_manager->setAmbientLight(color);
1755     m_renderer->setAmbientLight(light, force_SH_computation);
1756 #endif
1757 }   // setAmbientLight
1758 
1759 // ----------------------------------------------------------------------------
getAmbientLight() const1760 video::SColorf IrrDriver::getAmbientLight() const
1761 {
1762     return m_scene_manager->getAmbientLight();
1763 }
1764 
1765 // ----------------------------------------------------------------------------
1766 /** Displays the FPS on the screen.
1767  */
displayFPS()1768 void IrrDriver::displayFPS()
1769 {
1770 #ifndef SERVER_ONLY
1771     gui::IGUIFont* font = GUIEngine::getSmallFont();
1772     core::rect<s32> position;
1773 
1774     const int fheight = font->getHeightPerLine();
1775     if (UserConfigParams::m_artist_debug_mode)
1776         position = core::rect<s32>(51, 0, 30*fheight+51, 2*fheight + fheight / 3);
1777     else
1778         position = core::rect<s32>(75, 0, 18*fheight+75 , fheight + fheight / 5);
1779     GL32_draw2DRectangle(video::SColor(150, 96, 74, 196), position, NULL);
1780     // We will let pass some time to let things settle before trusting FPS counter
1781     // even if we also ignore fps = 1, which tends to happen in first checks
1782     const int NO_TRUST_COUNT = 200;
1783     static int no_trust = NO_TRUST_COUNT;
1784 
1785     // Min and max info tracking, per mode, so user can check game vs menus
1786     bool current_state     = StateManager::get()->getGameState()
1787                                == GUIEngine::GAME;
1788     static bool prev_state = false;
1789     static int min         = 999; // Absurd values for start will print first time
1790     static int max         = 0;   // but no big issue, maybe even "invisible"
1791     static float low       = 1000000.0f; // These two are for polycount stats
1792     static float high      = 0.0f;       // just like FPS, but in KTris
1793 
1794     // Reset limits if state changes
1795     if (prev_state != current_state)
1796     {
1797         min = 999;
1798         max = 0;
1799         low = 1000000.0f;
1800         high = 0.0f;
1801         no_trust = NO_TRUST_COUNT;
1802         prev_state = current_state;
1803     }
1804 
1805     uint32_t ping = 0;
1806     if (STKHost::existHost())
1807         ping = STKHost::get()->getClientPingToServer();
1808     if (no_trust)
1809     {
1810         no_trust--;
1811 
1812         static video::SColor fpsColor = video::SColor(255, 0, 0, 0);
1813         font->drawQuick(StringUtils::insertValues (L"FPS: ... Ping: %dms", ping),
1814             core::rect< s32 >(100,0,400,50), fpsColor, false);
1815         return;
1816     }
1817 
1818     // Ask for current frames per second and last number of triangles
1819     // processed (trimed to thousands)
1820     const int fps         = m_video_driver->getFPS();
1821     const float kilotris  = m_video_driver->getPrimitiveCountDrawn(0)
1822                                 * (1.f / 1000.f);
1823 
1824     if (min > fps && fps > 1) min = fps; // Start moments sometimes give useless 1
1825     if (max < fps) max = fps;
1826     if (low > kilotris) low = kilotris;
1827     if (high < kilotris) high = kilotris;
1828 
1829     core::stringw fps_string;
1830 
1831     if ((UserConfigParams::m_artist_debug_mode)&&(CVS->isGLSL()))
1832     {
1833         fps_string = StringUtils::insertValues
1834                     (L"FPS: %d/%d/%d  - PolyCount: %d Solid, "
1835                       "%d Shadows - LightDist : %d\nComplexity %d, Total skinning joints: %d, "
1836                       "Ping: %dms",
1837                     min, fps, max, SP::sp_solid_poly_count,
1838                     SP::sp_shadow_poly_count, m_last_light_bucket_distance, irr_driver->getSceneComplexity(),
1839                     m_skinning_joint, ping);
1840     }
1841     else
1842     {
1843         if (CVS->isGLSL())
1844         {
1845             fps_string = _("FPS: %d/%d/%d - %d KTris, Ping: %dms", min, fps,
1846                 max, SP::sp_solid_poly_count / 1000, ping);
1847         }
1848         else
1849         {
1850             fps_string = _("FPS: %d/%d/%d - %d KTris, Ping: %dms", min, fps,
1851                 max, (int)roundf(kilotris), ping);
1852         }
1853     }
1854 
1855     static video::SColor fpsColor = video::SColor(255, 0, 0, 0);
1856 
1857     font->drawQuick( fps_string.c_str(), position, fpsColor, false );
1858 #endif
1859 }   // updateFPS
1860 
1861 // ----------------------------------------------------------------------------
1862 /** Displays the timer for Story Mode on the screen.
1863  *  This can't be done in race or overworld GUIs as
1864  *  the speedrun timer has to be displayed on all screens.
1865  */
displayStoryModeTimer()1866 void IrrDriver::displayStoryModeTimer()
1867 {
1868 #ifndef SERVER_ONLY
1869     if (story_mode_timer->getStoryModeTime() < 0)
1870         return;
1871 
1872     gui::ScalableFont* font = GUIEngine::getHighresDigitFont();
1873 
1874     core::stringw timer_string;
1875     timer_string = story_mode_timer->getTimerString().c_str();
1876 
1877     //The normal timer size ; to not write over it
1878     core::dimension2du area = font->getDimension(L"99:99.99");
1879     int regular_timer_width = area.Width;
1880     if (UserConfigParams::m_speedrun_mode)
1881         area = font->getDimension(L"99:99:99.999");
1882     else
1883         area = font->getDimension(L"99:99:99");
1884 
1885     int screen_width = irr_driver->getActualScreenSize().Width;
1886     int screen_height = irr_driver->getActualScreenSize().Height;
1887     int speedrun_string_width = area.Width;
1888     int dist_from_right = speedrun_string_width + regular_timer_width + screen_width*4/100;
1889 
1890     core::rect<s32> position(screen_width - dist_from_right, screen_height*2/100,
1891                              screen_width                  , screen_height*6/100);
1892 
1893     font->setColoredBorder(irr::video::SColor(255, 0, 32, 80));
1894 
1895     if ( (UserConfigParams::m_speedrun_mode && story_mode_timer->speedrunIsFinished()) ||
1896          (!UserConfigParams::m_speedrun_mode && PlayerManager::getCurrentPlayer()->isFinished()) )
1897         font->draw(timer_string.c_str(), position, video::SColor(255, 0, 255, 0), false, false, NULL, true);
1898     else
1899         font->draw(timer_string.c_str(), position, video::SColor(255, 220, 255, 0), false, false, NULL, true);
1900 
1901     font->disableColoredBorder();
1902 #endif
1903 } // displayStoryModeTimer
1904 
1905 // ----------------------------------------------------------------------------
1906 /** Requess a screenshot from irrlicht, and save it in a file.
1907  */
doScreenShot()1908 void IrrDriver::doScreenShot()
1909 {
1910     m_request_screenshot = false;
1911 
1912     video::IImage* image = m_video_driver->createScreenShot();
1913     if(!image)
1914     {
1915         Log::error("irr_driver", "Could not create screen shot.");
1916         return;
1917     }
1918 
1919     // Screenshot was successful.
1920     time_t rawtime;
1921     time ( &rawtime );
1922     tm* timeInfo = localtime( &rawtime );
1923     char time_buffer[256];
1924     sprintf(time_buffer, "%i.%02i.%02i_%02i.%02i.%02i",
1925             timeInfo->tm_year + 1900, timeInfo->tm_mon+1,
1926             timeInfo->tm_mday, timeInfo->tm_hour,
1927             timeInfo->tm_min, timeInfo->tm_sec);
1928 
1929     std::string track_name = RaceManager::get()->getTrackName();
1930     if (World::getWorld() == NULL) track_name = "menu";
1931     std::string path = file_manager->getScreenshotDir()+track_name+"-"
1932                      + time_buffer+".png";
1933 
1934     if (irr_driver->getVideoDriver()->writeImageToFile(image, path.c_str(), 0))
1935     {
1936         RaceGUIBase* base = World::getWorld()
1937                           ? World::getWorld()->getRaceGUI()
1938                           : NULL;
1939         if (base)
1940         {
1941             base->addMessage(
1942                       core::stringw(("Screenshot saved to\n" + path).c_str()),
1943                       NULL, 2.0f, video::SColor(255,255,255,255), true, false);
1944         }   // if base
1945     }
1946     else
1947     {
1948         RaceGUIBase* base = World::getWorld()->getRaceGUI();
1949         if (base)
1950         {
1951             base->addMessage(
1952                 core::stringw(("FAILED saving screenshot to\n" + path +
1953                               "\n:(").c_str()),
1954                 NULL, 2.0f, video::SColor(255,255,255,255),
1955                 true, false);
1956         }   // if base
1957     }   // if failed writing screenshot file
1958     image->drop();
1959 }   // doScreenShot
1960 
1961 // ----------------------------------------------------------------------------
handleWindowResize()1962 void IrrDriver::handleWindowResize()
1963 {
1964     bool dialog_exists = GUIEngine::ModalDialog::isADialogActive() ||
1965             GUIEngine::ScreenKeyboard::isActive();
1966 
1967     // This will allow main menu auto resize if missed a resize event
1968     core::dimension2du current_screen_size =
1969         m_video_driver->getCurrentRenderTargetSize();
1970     GUIEngine::Screen* screen = GUIEngine::getCurrentScreen();
1971     if (screen && screen->isResizable())
1972     {
1973         current_screen_size.Width = screen->getWidth();
1974         current_screen_size.Height = screen->getHeight();
1975     }
1976 
1977     if (m_actual_screen_size != m_video_driver->getCurrentRenderTargetSize() ||
1978         current_screen_size != m_video_driver->getCurrentRenderTargetSize())
1979     {
1980         // Don't update when dialog is opened
1981         if (dialog_exists)
1982             return;
1983 
1984         m_actual_screen_size = m_video_driver->getCurrentRenderTargetSize();
1985         UserConfigParams::m_width = m_actual_screen_size.Width;
1986         UserConfigParams::m_height = m_actual_screen_size.Height;
1987         resizeWindow();
1988     }
1989     // In case reset by opening options in game
1990     if (!dialog_exists &&
1991         StateManager::get()->getGameState() == GUIEngine::GAME &&
1992         !m_device->isResizable())
1993         m_device->setResizable(true);
1994 }   // handleWindowResize
1995 
1996 // ----------------------------------------------------------------------------
1997 /** Update, called once per frame.
1998  *  \param dt Time since last update
1999  *  \param is_loading True if the rendering is called during loading of world,
2000  *         in which case world, physics etc must not be accessed/
2001  */
update(float dt,bool is_loading)2002 void IrrDriver::update(float dt, bool is_loading)
2003 {
2004     bool show_dialog_yes = m_resolution_changing == RES_CHANGE_YES;
2005     bool show_dialog_warn = m_resolution_changing == RES_CHANGE_YES_WARN;
2006     // If the resolution should be switched, do it now. This will delete the
2007     // old device and create a new one.
2008     if (m_resolution_changing!=RES_CHANGE_NONE)
2009     {
2010         applyResolutionSettings(m_resolution_changing != RES_CHANGE_SAME);
2011         m_resolution_changing = RES_CHANGE_NONE;
2012     }
2013 
2014     handleWindowResize();
2015 
2016     if (show_dialog_yes)
2017         new ConfirmResolutionDialog(false);
2018     else if (show_dialog_warn)
2019         new ConfirmResolutionDialog(true);
2020 
2021     m_wind->update();
2022 
2023     PropertyAnimator::get()->update(dt);
2024 #ifndef SERVER_ONLY
2025     if (CVS->isGLSL())
2026     {
2027         SP::SPTextureManager::get()->checkForGLCommand();
2028     }
2029 #endif
2030     World *world = World::getWorld();
2031 
2032     if (world)
2033     {
2034 #ifndef SERVER_ONLY
2035         m_renderer->render(dt, is_loading);
2036 
2037         GUIEngine::Screen* current_screen = GUIEngine::getCurrentScreen();
2038         if (current_screen != NULL && current_screen->needs3D())
2039         {
2040             GUIEngine::render(dt, is_loading);
2041         }
2042 
2043         if (!is_loading && Physics::get())
2044         {
2045             IrrDebugDrawer* debug_drawer = Physics::get()->getDebugDrawer();
2046             if (debug_drawer != NULL && debug_drawer->debugEnabled())
2047             {
2048                 debug_drawer->beginNextFrame();
2049             }
2050         }
2051 #endif
2052     }
2053     else
2054     {
2055 #ifndef SERVER_ONLY
2056         m_video_driver->beginScene(/*backBuffer clear*/ true, /*zBuffer*/ true,
2057                                    video::SColor(255,100,101,140));
2058 
2059         GUIEngine::render(dt, is_loading);
2060         if (m_render_nw_debug && !is_loading)
2061         {
2062             renderNetworkDebug();
2063         }
2064         m_video_driver->endScene();
2065 #endif
2066     }
2067 
2068     if (m_request_screenshot) doScreenShot();
2069 
2070     // Enable this next print statement to get render information printed
2071     // E.g. number of triangles rendered, culled etc. The stats is only
2072     // printed while the race is running and not while the in-game menu
2073     // is shown. This way the output can be studied by just opening the
2074     // menu.
2075     //if(World::getWorld() && World::getWorld()->isRacePhase())
2076     //    printRenderStats();
2077 #ifdef ENABLE_RECORDER
2078     if (m_recording)
2079     {
2080         PROFILER_PUSH_CPU_MARKER("- Recording", 0x0, 0x50, 0x40);
2081         ogrCapture();
2082         PROFILER_POP_CPU_MARKER();
2083     }
2084 #endif
2085 }   // update
2086 
2087 // ----------------------------------------------------------------------------
renderNetworkDebug()2088 void IrrDriver::renderNetworkDebug()
2089 {
2090 #ifndef SERVER_ONLY
2091     if (!NetworkConfig::get()->isNetworking() ||
2092         NetworkConfig::get()->isServer() || !STKHost::existHost())
2093         return;
2094 
2095     auto peer = STKHost::get()->getServerPeerForClient();
2096     if (!peer)
2097         return;
2098 
2099     const core::dimension2d<u32>& screen_size =
2100         m_video_driver->getScreenSize();
2101     core::rect<s32>background_rect(
2102         (int)(0.02f * screen_size.Width),
2103         (int)(0.3f * screen_size.Height),
2104         (int)(0.98f * screen_size.Width),
2105         (int)(0.6f * screen_size.Height));
2106     video::SColor color(0x80, 0xFF, 0xFF, 0xFF);
2107     GL32_draw2DRectangle(color, background_rect);
2108     uint64_t r, d, h, m, s, f;
2109     r = STKHost::get()->getNetworkTimer();
2110     d = r / 86400000;
2111     r = r % 86400000;
2112     h = r / 3600000;
2113     r = r % 3600000;
2114     m = r / 60000;
2115     r = r % 60000;
2116     s = r / 1000;
2117     f = r % 1000;
2118     char str[128];
2119     sprintf(str, "%d day(s), %02d:%02d:%02d.%03d",
2120         (int)d, (int)h, (int)m, (int)s, (int)f);
2121 
2122     gui::IGUIFont* font = GUIEngine::getFont();
2123     unsigned height = font->getHeightPerLine() + 2;
2124     background_rect.UpperLeftCorner.X += 5;
2125     static video::SColor black = video::SColor(255, 0, 0, 0);
2126     font->drawQuick(StringUtils::insertValues(
2127         L"Server time: %s      Server state frequency: %d",
2128         str, NetworkConfig::get()->getStateFrequency()),
2129         background_rect, black, false);
2130 
2131     background_rect.UpperLeftCorner.Y += height;
2132     font->drawQuick(StringUtils::insertValues(
2133         L"Upload speed (KBps): %f      Download speed (KBps): %f",
2134         (float)STKHost::get()->getUploadSpeed() / 1024.0f,
2135         (float)STKHost::get()->getDownloadSpeed() / 1024.0f,
2136         NetworkConfig::get()->getStateFrequency()), background_rect, black,
2137         false);
2138 
2139     background_rect.UpperLeftCorner.Y += height;
2140     font->drawQuick(StringUtils::insertValues(
2141         L"Packet loss: %d      Packet loss variance: %d",
2142         peer->getENetPeer()->packetLoss,
2143         peer->getENetPeer()->packetLossVariance,
2144         NetworkConfig::get()->getStateFrequency()), background_rect, black,
2145         false);
2146 #endif
2147 }   // renderNetworkDebug
2148 
2149 // ----------------------------------------------------------------------------
setRecording(bool val)2150 void IrrDriver::setRecording(bool val)
2151 {
2152 #ifdef ENABLE_RECORDER
2153     if (!CVS->isARBPixelBufferObjectUsable())
2154     {
2155         Log::warn("irr_driver", "PBO extension missing, can't record video.");
2156         return;
2157     }
2158     if (val == (ogrCapturing() == 1))
2159         return;
2160     m_recording = val;
2161     if (val == true)
2162     {
2163         std::string track_name = World::getWorld() != NULL ?
2164             RaceManager::get()->getTrackName() : "menu";
2165         time_t rawtime;
2166         time(&rawtime);
2167         tm* timeInfo = localtime(&rawtime);
2168         char time_buffer[256];
2169         sprintf(time_buffer, "%i.%02i.%02i_%02i.%02i.%02i",
2170             timeInfo->tm_year + 1900, timeInfo->tm_mon + 1,
2171             timeInfo->tm_mday, timeInfo->tm_hour,
2172             timeInfo->tm_min, timeInfo->tm_sec);
2173         ogrSetSavedName((file_manager->getScreenshotDir() +
2174             track_name + "_" + time_buffer).c_str());
2175         ogrPrepareCapture();
2176     }
2177     else
2178     {
2179         ogrStopCapture();
2180     }
2181 #else
2182     Log::error("Recorder", "Recording unavailable, STK was compiled without "
2183                "recording support.  Please re-compile STK with libopenglrecorder "
2184                "to enable recording.  If you got SuperTuxKart from your distribution's "
2185                "repositories, please use the official binaries, or contact your "
2186                "distributions's package mantainers.");
2187 #endif
2188 }   // setRecording
2189 
2190 // ----------------------------------------------------------------------------
2191 
requestScreenshot()2192 void IrrDriver::requestScreenshot()
2193 {
2194     RaceGUIBase* base = World::getWorld()
2195                          ? World::getWorld()->getRaceGUI()
2196                          : NULL;
2197     if (base)
2198     {
2199         base->clearAllMessages();
2200     }
2201 
2202     m_request_screenshot = true;
2203 }
2204 
2205 // ----------------------------------------------------------------------------
2206 /** This is not really used to process events, it's only used to shut down
2207  *  irrLicht's chatty logging until the event handler is ready to take
2208  *  the task.
2209  */
OnEvent(const irr::SEvent & event)2210 bool IrrDriver::OnEvent(const irr::SEvent &event)
2211 {
2212     //TODO: ideally we wouldn't use this object to STFU irrlicht's chatty
2213     //      debugging, we'd just create the EventHandler earlier so it
2214     //      can act upon it
2215     switch (event.EventType)
2216     {
2217     case irr::EET_LOG_TEXT_EVENT:
2218     {
2219         // Ignore 'normal' messages
2220         if (event.LogEvent.Level > 1)
2221         {
2222             Log::warn("[IrrDriver Temp Logger]", "Level %d: %s\n",
2223                    event.LogEvent.Level,event.LogEvent.Text);
2224         }
2225         return true;
2226     }
2227     default:
2228         return false;
2229     }   // switch
2230 
2231     return false;
2232 }   // OnEvent
2233 
2234 // ----------------------------------------------------------------------------
addLight(const core::vector3df & pos,float energy,float radius,float r,float g,float b,bool sun,scene::ISceneNode * parent)2235 scene::ISceneNode *IrrDriver::addLight(const core::vector3df &pos,
2236                                        float energy, float radius,
2237                                        float r, float g, float b,
2238                                        bool sun, scene::ISceneNode* parent)
2239 {
2240 #ifndef SERVER_ONLY
2241     if (CVS->isGLSL())
2242     {
2243         if (parent == NULL) parent = m_scene_manager->getRootSceneNode();
2244         LightNode *light = NULL;
2245 
2246         if (!sun)
2247             light = new LightNode(m_scene_manager, parent, energy, radius,
2248                                   r, g, b);
2249         else
2250             light = new SunNode(m_scene_manager, parent, r, g, b);
2251 
2252         light->setPosition(pos);
2253         light->updateAbsolutePosition();
2254 
2255         m_lights.push_back(light);
2256 
2257         if (sun)
2258         {
2259             m_renderer->addSunLight(pos);
2260         }
2261         return light;
2262     }
2263     else
2264     {
2265         scene::ILightSceneNode* light = m_scene_manager
2266                ->addLightSceneNode(m_scene_manager->getRootSceneNode(),
2267                                    pos, video::SColorf(r, g, b, 1.0f));
2268         light->setRadius(radius);
2269         return light;
2270     }
2271 #else
2272     return NULL;
2273 #endif
2274 }   // addLight
2275 
2276 // ----------------------------------------------------------------------------
2277 
clearLights()2278 void IrrDriver::clearLights()
2279 {
2280     for (unsigned int i = 0; i < m_lights.size(); i++)
2281     {
2282         m_lights[i]->drop();
2283     }
2284 
2285     m_lights.clear();
2286 }   // clearLights
2287 
2288 // ----------------------------------------------------------------------------
getRenderTargetTexture(TypeRTT which)2289 GLuint IrrDriver::getRenderTargetTexture(TypeRTT which)
2290 {
2291     return m_renderer->getRenderTargetTexture(which);
2292 }   // getRenderTargetTexture
2293 
2294 // ----------------------------------------------------------------------------
getDepthStencilTexture()2295 GLuint IrrDriver::getDepthStencilTexture()
2296 {
2297     return m_renderer->getDepthStencilTexture();
2298 }   // getDepthStencilTexture
2299 
2300 // ----------------------------------------------------------------------------
resetDebugModes()2301 void IrrDriver::resetDebugModes()
2302 {
2303     m_ssaoviz = false;
2304     m_shadowviz = false;
2305     m_lightviz = false;
2306     m_boundingboxesviz = false;
2307 #ifndef SERVER_ONLY
2308     SP::sp_debug_view = false;
2309 #endif
2310 }
2311 
2312 // ----------------------------------------------------------------------------
resizeWindow()2313 void IrrDriver::resizeWindow()
2314 {
2315 #ifndef SERVER_ONLY
2316     // Reload fonts
2317     font_manager->getFont<BoldFace>()->init();
2318     font_manager->getFont<DigitFace>()->init();
2319     font_manager->getFont<RegularFace>()->init();
2320     // Reload GUIEngine
2321     GUIEngine::reloadForNewSize();
2322     if (World::getWorld())
2323     {
2324         for(unsigned int i=0; i<Camera::getNumCameras(); i++)
2325             Camera::getCamera(i)->setupCamera();
2326         ShaderBasedRenderer* sbr = dynamic_cast<ShaderBasedRenderer*>(m_renderer);
2327         if (sbr)
2328         {
2329             delete sbr->getRTTs();
2330             // This will recreate the RTTs
2331             sbr->onLoadWorld();
2332         }
2333         STKTextBillboard::updateAllTextBillboards();
2334         World::getWorld()->getRaceGUI()->recreateGUI();
2335     }
2336 
2337 #ifdef ENABLE_RECORDER
2338     ogrDestroy();
2339     RecorderConfig cfg;
2340     cfg.m_triple_buffering = 1;
2341     cfg.m_record_audio = 1;
2342     cfg.m_width = m_actual_screen_size.Width;
2343     cfg.m_height = m_actual_screen_size.Height;
2344     int vf = UserConfigParams::m_video_format;
2345     cfg.m_video_format = (VideoFormat)vf;
2346     cfg.m_audio_format = OGR_AF_VORBIS;
2347     cfg.m_audio_bitrate = UserConfigParams::m_audio_bitrate;
2348     cfg.m_video_bitrate = UserConfigParams::m_video_bitrate;
2349     cfg.m_record_fps = UserConfigParams::m_record_fps;
2350     cfg.m_record_jpg_quality = UserConfigParams::m_recorder_jpg_quality;
2351     if (ogrInitConfig(&cfg) == 0)
2352     {
2353         Log::error("irr_driver",
2354             "RecorderConfig is invalid, use the default one.");
2355     }
2356 #endif
2357 #endif
2358 }
2359