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