1 // SuperTuxKart - a fun racing game with go-kart
2 //
3 // Copyright (C) 2004-2015 Steve Baker <sjbaker1@airmail.net>
4 // Copyright (C) 2009-2015 Joerg Henrichs, Steve Baker
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 3
9 // of the License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 #include "tracks/track.hpp"
21
22 #include "addons/addon.hpp"
23 #include "audio/music_manager.hpp"
24 #include "challenges/challenge_status.hpp"
25 #include "challenges/unlock_manager.hpp"
26 #include "config/player_manager.hpp"
27 #include "config/stk_config.hpp"
28 #include "config/user_config.hpp"
29 #include "graphics/camera_end.hpp"
30 #include "graphics/CBatchingMesh.hpp"
31 #include "graphics/central_settings.hpp"
32 #include "graphics/cpu_particle_manager.hpp"
33 #include "graphics/irr_driver.hpp"
34 #include "graphics/lod_node.hpp"
35 #include "graphics/material.hpp"
36 #include "graphics/material_manager.hpp"
37 #include "graphics/mesh_tools.hpp"
38 #include "graphics/moving_texture.hpp"
39 #include "graphics/particle_emitter.hpp"
40 #include "graphics/particle_kind.hpp"
41 #include "graphics/particle_kind_manager.hpp"
42 #include "graphics/render_target.hpp"
43 #include "graphics/shader_files_manager.hpp"
44 #include "graphics/stk_tex_manager.hpp"
45 #include "graphics/sp/sp_base.hpp"
46 #include "graphics/sp/sp_mesh.hpp"
47 #include "graphics/sp/sp_mesh_buffer.hpp"
48 #include "graphics/sp/sp_mesh_node.hpp"
49 #include "graphics/sp/sp_shader_manager.hpp"
50 #include "graphics/sp/sp_texture_manager.hpp"
51 #include "io/file_manager.hpp"
52 #include "io/xml_node.hpp"
53 #include "items/item.hpp"
54 #include "items/item_manager.hpp"
55 #include "items/network_item_manager.hpp"
56 #include "items/powerup_manager.hpp"
57 #include "karts/abstract_kart.hpp"
58 #include "karts/kart_properties.hpp"
59 #include "main_loop.hpp"
60 #include "modes/linear_world.hpp"
61 #include "modes/easter_egg_hunt.hpp"
62 #include "network/network_config.hpp"
63 #include "network/protocols/game_protocol.hpp"
64 #include "network/protocols/server_lobby.hpp"
65 #include "physics/physical_object.hpp"
66 #include "physics/physics.hpp"
67 #include "physics/triangle_mesh.hpp"
68 #include "race/race_manager.hpp"
69 #include "scriptengine/script_engine.hpp"
70 #include "tracks/arena_graph.hpp"
71 #include "tracks/bezier_curve.hpp"
72 #include "tracks/check_manager.hpp"
73 #include "tracks/check_structure.hpp"
74 #include "tracks/drive_graph.hpp"
75 #include "tracks/drive_node.hpp"
76 #include "tracks/model_definition_loader.hpp"
77 #include "tracks/track_manager.hpp"
78 #include "tracks/track_object_manager.hpp"
79 #include "utils/constants.hpp"
80 #include "utils/log.hpp"
81 #include "utils/mini_glm.hpp"
82 #include "utils/string_utils.hpp"
83 #include "utils/translation.hpp"
84
85 #include <IBillboardTextSceneNode.h>
86 #include <ILightSceneNode.h>
87 #include <IMeshCache.h>
88 #include <IMeshManipulator.h>
89 #include <IMeshSceneNode.h>
90 #include <ISceneManager.h>
91 #include <SMeshBuffer.h>
92
93 #include <iostream>
94 #include <stdexcept>
95 #include <sstream>
96 #include <wchar.h>
97
98 using namespace irr;
99
100
101 const float Track::NOHIT = -99999.9f;
102 bool Track::m_dont_load_navmesh = false;
103 std::atomic<Track*> Track::m_current_track[PT_COUNT];
104
105 // ----------------------------------------------------------------------------
Track(const std::string & filename)106 Track::Track(const std::string &filename)
107 {
108 #ifdef DEBUG
109 m_magic_number = 0x17AC3802;
110 #endif
111
112 m_minimap_invert_x_z = false;
113 m_materials_loaded = false;
114 m_filename = filename;
115 m_root =
116 StringUtils::getPath(StringUtils::removeExtension(m_filename));
117 m_ident = StringUtils::getBasename(m_root);
118 // If this is an addon track, add "addon_" to the identifier - just in
119 // case that an addon track has the same directory name (and therefore
120 // identifier) as an included track.
121 if(Addon::isAddon(filename))
122 {
123 m_ident = Addon::createAddonId(m_ident);
124 m_is_addon = true;
125 }
126 else
127 m_is_addon = false;
128
129 // The directory should always have a '/' at the end, but getBasename
130 // above returns "" if a "/" is at the end, so we add the "/" here.
131 m_root += "/";
132 m_designer = "";
133 m_screenshot = "";
134 m_version = 0;
135 m_track_mesh = NULL;
136 m_gfx_effect_mesh = NULL;
137 m_internal = false;
138 m_enable_auto_rescue = true; // Below set to false in arenas
139 m_enable_push_back = true;
140 m_reverse_available = false;
141 m_is_arena = false;
142 m_is_ctf = false;
143 m_max_arena_players = 0;
144 m_has_easter_eggs = false;
145 m_has_navmesh = false;
146 m_is_soccer = false;
147 m_is_cutscene = false;
148 m_camera_far = 1000.0f;
149 m_bloom = true;
150 m_is_day = true;
151 m_bloom_threshold = 0.75f;
152 m_color_inlevel = core::vector3df(0.0,1.0, 255.0);
153 m_color_outlevel = core::vector2df(0.0, 255.0);
154 m_clouds = false;
155 m_godrays = false;
156 m_displacement_speed = 1.0f;
157 m_physical_object_uid = 0;
158 m_shadows = true;
159 m_sky_particles = NULL;
160 m_sky_dx = 0.05f;
161 m_sky_dy = 0.0f;
162 m_godrays_opacity = 1.0f;
163 m_godrays_color = video::SColor(255, 255, 255, 255);
164 m_weather_lightning = false;
165 m_weather_sound = "";
166 m_cache_track = UserConfigParams::m_cache_overworld &&
167 m_ident=="overworld";
168 m_render_target = NULL;
169 m_check_manager = NULL;
170 m_minimap_x_scale = 1.0f;
171 m_minimap_y_scale = 1.0f;
172 m_force_disable_fog = false;
173 m_startup_run = false;
174 m_red_flag = m_blue_flag =
175 btTransform(btQuaternion(0.0f, 0.0f, 0.0f, 1.0f));
176 m_default_number_of_laps = 3;
177 m_all_nodes.clear();
178 m_static_physics_only_nodes.clear();
179 m_all_cached_meshes.clear();
180 loadTrackInfo();
181 } // Track
182
183 //-----------------------------------------------------------------------------
184 /** Destructor, removes quad data structures etc. */
~Track()185 Track::~Track()
186 {
187 // Note that the music information in m_music is globally managed
188 // by the music_manager, and is freed there. So no need to free it
189 // here (esp. since various track might share the same music).
190 #ifdef DEBUG
191 assert(m_magic_number == 0x17AC3802);
192 m_magic_number = 0xDEADBEEF;
193 #endif
194 } // ~Track
195
196 //-----------------------------------------------------------------------------
197 /** A < comparison of tracks. This is used to sort the tracks when displaying
198 * them in the gui.
199 */
operator <(const Track & other) const200 bool Track::operator<(const Track &other) const
201 {
202 PlayerProfile *p = PlayerManager::getCurrentPlayer();
203 bool this_is_locked = p->isLocked(getIdent());
204 bool other_is_locked = p->isLocked(other.getIdent());
205 if(this_is_locked == other_is_locked)
206 {
207 return getSortName() < other.getSortName();
208 }
209 else
210 return other_is_locked;
211 } // operator<
212
213 //-----------------------------------------------------------------------------
214 /** Returns the name of the track, which is e.g. displayed on the screen. */
getName() const215 core::stringw Track::getName() const
216 {
217 core::stringw translated = _(m_name.c_str());
218 int index = translated.find("|");
219 if(index>-1)
220 {
221 translated = translated.subString(0, index);
222 }
223 return translated;
224 } // getName
225
226 //-----------------------------------------------------------------------------
227 /** Returns the name of the track used to sort the tracks alphabetically.
228 * This can be used to e.g. sort 'The Island' as 'Island,The'; or
229 * to replace certain language-specific characters (e.g. German 'ae' with 'a')
230 * The sort name can be specified by setting the name of a track to:
231 * "normal name|sort name"
232 */
getSortName() const233 core::stringw Track::getSortName() const
234 {
235 core::stringw translated = translations->w_gettext(m_name.c_str());
236 translated.make_lower();
237 int index = translated.find("|");
238 if(index>-1)
239 {
240 translated = translated.subString(index+1, translated.size());
241 }
242 return translated;
243 } // getSortName
244
245 //-----------------------------------------------------------------------------
246 /** Returns true if this track belongs to the specified track group.
247 * \param group_name Group name to test for.
248 */
isInGroup(const std::string & group_name)249 bool Track::isInGroup(const std::string &group_name)
250 {
251 return std::find(m_groups.begin(), m_groups.end(), group_name)
252 != m_groups.end();
253 } // isInGroup
254
255 //-----------------------------------------------------------------------------
256 /** Returns number of completed challenges */
getNumOfCompletedChallenges()257 unsigned int Track::getNumOfCompletedChallenges()
258 {
259 unsigned int unlocked_challenges = 0;
260 PlayerProfile *player = PlayerManager::getCurrentPlayer();
261 for (unsigned int i=0; i<m_challenges.size(); i++)
262 {
263 if (m_challenges[i].m_challenge_id == "tutorial")
264 {
265 unlocked_challenges++;
266 continue;
267 }
268 if (player->getChallengeStatus(m_challenges[i].m_challenge_id)
269 ->isSolvedAtAnyDifficulty())
270 {
271 unlocked_challenges++;
272 }
273 }
274
275 return unlocked_challenges;
276 } // getNumOfCompletedChallenges
277
278 //-----------------------------------------------------------------------------
279 /** Removes all cached data structures. This is called before the resolution
280 * is changed.
281 */
removeCachedData()282 void Track::removeCachedData()
283 {
284 m_materials_loaded = false;
285 } // cleanCachedData
286
287 //-----------------------------------------------------------------------------
288 /** Prepates the track for a new race. This function must be called after all
289 * karts are created, since the check objects allocate data structures
290 * depending on the number of karts.
291 */
reset()292 void Track::reset()
293 {
294 m_ambient_color = m_default_ambient_color;
295 m_check_manager->reset(*this);
296 m_item_manager->reset();
297 m_track_object_manager->reset();
298 m_startup_run = false;
299 } // reset
300
301 //-----------------------------------------------------------------------------
302 /** Removes the physical body from the world.
303 * Called at the end of a race.
304 */
cleanup()305 void Track::cleanup()
306 {
307 irr_driver->resetSceneComplexity();
308 m_physical_object_uid = 0;
309 #ifdef USE_RESIZE_CACHE
310 if (!UserConfigParams::m_high_definition_textures)
311 {
312 file_manager->popTextureSearchPath();
313 }
314 #endif
315 file_manager->popTextureSearchPath();
316 file_manager->popModelSearchPath();
317
318 Graph::destroy();
319 m_item_manager = nullptr;
320 #ifndef SERVER_ONLY
321 if (CVS->isGLSL())
322 {
323 if (!GUIEngine::isNoGraphics())
324 {
325 CPUParticleManager::getInstance()->cleanMaterialMap();
326 }
327
328 SP::resetEmptyFogColor();
329 }
330 ParticleKindManager::get()->cleanUpTrackSpecificGfx();
331 #endif
332
333 for (unsigned int i = 0; i < m_animated_textures.size(); i++)
334 {
335 delete m_animated_textures[i];
336 }
337 m_animated_textures.clear();
338
339 for (unsigned int i = 0; i < m_all_nodes.size(); i++)
340 {
341 irr_driver->removeNode(m_all_nodes[i]);
342 }
343 m_all_nodes.clear();
344
345 for (unsigned int i = 0; i < m_static_physics_only_nodes.size(); i++)
346 {
347 m_static_physics_only_nodes[i]->remove();
348 }
349 m_static_physics_only_nodes.clear();
350
351 delete m_check_manager;
352 m_check_manager = NULL;
353
354 delete m_track_object_manager;
355 m_track_object_manager = NULL;
356
357 for (unsigned int i = 0; i < m_object_physics_only_nodes.size(); i++)
358 {
359 m_object_physics_only_nodes[i]->drop();
360 }
361 m_object_physics_only_nodes.clear();
362
363 #ifndef SERVER_ONLY
364 irr_driver->removeNode(m_sun);
365 if (CVS->isGLSL())
366 m_sun->drop();
367 #endif
368 delete m_track_mesh;
369 m_track_mesh = NULL;
370
371 delete m_gfx_effect_mesh;
372 m_gfx_effect_mesh = NULL;
373
374 #ifndef SERVER_ONLY
375 if (CVS->isGLSL())
376 irr_driver->cleanSunInterposer();
377 #endif
378
379
380 // The m_all_cached_mesh contains each mesh loaded from a file, which
381 // means that the mesh is stored in irrlichts mesh cache. To clean
382 // everything loaded by this track, we drop the ref count for each mesh
383 // here, till the ref count is 1, which means the mesh is only contained
384 // in the mesh cache, and can therefore be removed. Meshes load more
385 // than once are in m_all_cached_mesh more than once (which is easier
386 // than storing the mesh only once, but then having to test for each
387 // mesh if it is already contained in the list or not).
388 for (unsigned int i = 0; i < m_all_cached_meshes.size(); i++)
389 {
390 irr_driver->dropAllTextures(m_all_cached_meshes[i]);
391 // If a mesh is not in Irrlicht's texture cache, its refcount is
392 // 1 (since its scene node was removed, so the only other reference
393 // is in m_all_cached_meshes). In this case we only drop it once
394 // and don't try to remove it from the cache.
395 if (m_all_cached_meshes[i]->getReferenceCount() == 1)
396 {
397 m_all_cached_meshes[i]->drop();
398 continue;
399 }
400 m_all_cached_meshes[i]->drop();
401 if (m_all_cached_meshes[i]->getReferenceCount() == 1)
402 irr_driver->removeMeshFromCache(m_all_cached_meshes[i]);
403 }
404 m_all_cached_meshes.clear();
405
406 // Now free meshes that are not associated to any scene node.
407 for (unsigned int i = 0; i < m_detached_cached_meshes.size(); i++)
408 {
409 irr_driver->dropAllTextures(m_detached_cached_meshes[i]);
410 irr_driver->removeMeshFromCache(m_detached_cached_meshes[i]);
411 }
412 m_detached_cached_meshes.clear();
413
414 for(unsigned int i=0; i<m_sky_textures.size(); i++)
415 {
416 m_sky_textures[i]->drop();
417 if(m_sky_textures[i]->getReferenceCount()==1)
418 irr_driver->removeTexture(m_sky_textures[i]);
419 }
420 m_sky_textures.clear();
421
422 for (unsigned int i = 0; i<m_spherical_harmonics_textures.size(); i++)
423 {
424 m_spherical_harmonics_textures[i]->drop();
425 if (m_spherical_harmonics_textures[i]->getReferenceCount() == 1)
426 irr_driver->removeTexture(m_spherical_harmonics_textures[i]);
427 }
428 m_spherical_harmonics_textures.clear();
429
430 if(m_cache_track)
431 material_manager->makeMaterialsPermanent();
432 else
433 {
434 // remove temporary materials loaded by the material manager
435 material_manager->popTempMaterial();
436 }
437
438 #ifndef SERVER_ONLY
439 irr_driver->clearGlowingNodes();
440 irr_driver->clearLights();
441 irr_driver->clearForcedBloom();
442 irr_driver->clearBackgroundNodes();
443
444 if (CVS->isGLSL())
445 {
446 SP::SPShaderManager::get()->removeUnusedShaders();
447 ShaderFilesManager::getInstance()->removeUnusedShaderFiles();
448 SP::SPTextureManager::get()->removeUnusedTextures();
449 }
450 #endif
451 if(UserConfigParams::logMemory())
452 {
453 Log::debug("track",
454 "[memory] After cleaning '%s': mesh cache %d texture cache %d\n",
455 getIdent().c_str(),
456 irr_driver->getSceneManager()->getMeshCache()->getMeshCount(),
457 irr_driver->getVideoDriver()->getTextureCount());
458 #ifdef DEBUG
459 scene::IMeshCache *cache = irr_driver->getSceneManager()->getMeshCache();
460 for(unsigned int i=0; i<cache->getMeshCount(); i++)
461 {
462 const io::SNamedPath &name = cache->getMeshName(i);
463 std::vector<std::string>::iterator p;
464 p = std::find(m_old_mesh_buffers.begin(), m_old_mesh_buffers.end(),
465 name.getInternalName().c_str());
466 if(p!=m_old_mesh_buffers.end())
467 m_old_mesh_buffers.erase(p);
468 else
469 {
470 Log::debug("track", "[memory] Leaked mesh buffer '%s'.\n",
471 name.getInternalName().c_str());
472 } // if name not found
473 } // for i < cache size
474
475 video::IVideoDriver *vd = irr_driver->getVideoDriver();
476 for(unsigned int i=0; i<vd->getTextureCount(); i++)
477 {
478 video::ITexture *t = vd->getTextureByIndex(i);
479 std::vector<video::ITexture*>::iterator p;
480 p = std::find(m_old_textures.begin(), m_old_textures.end(),
481 t);
482 if(p!=m_old_textures.end())
483 {
484 m_old_textures.erase(p);
485 }
486 else
487 {
488 Log::debug("track", "[memory] Leaked texture '%s'.\n",
489 t->getName().getInternalName().c_str());
490 }
491 }
492 #endif
493 } // if verbose
494
495 #ifdef __DEBUG_DUMP_MESH_CACHE_AFTER_CLEANUP__
496 scene::IMeshCache* meshCache = irr_driver->getSceneManager()->getMeshCache();
497 int count = meshCache->getMeshCount();
498 for (int i = 0; i < count; i++)
499 {
500 scene::IAnimatedMesh* mesh = meshCache->getMeshByIndex(i);
501 io::SNamedPath path = meshCache->getMeshName(mesh);
502 Log::info("CACHE", "[%i] %s", i, path.getPath().c_str());
503 }
504 #endif
505
506 m_meta_library.clear();
507 Scripting::ScriptEngine::getInstance()->cleanupCache();
508
509 m_current_track[PT_MAIN] = NULL;
510 } // cleanup
511
512 //-----------------------------------------------------------------------------
loadTrackInfo()513 void Track::loadTrackInfo()
514 {
515 // Default values
516 m_use_fog = false;
517 m_fog_max = 1.0f;
518 m_fog_start = 0.0f;
519 m_fog_end = 1000.0f;
520 m_fog_height_start = 0.0f;
521 m_fog_height_end = 100.0f;
522 m_gravity = 9.80665f;
523 m_friction = stk_config->m_default_track_friction;
524 m_smooth_normals = false;
525 m_godrays = false;
526 m_godrays_opacity = 1.0f;
527 m_godrays_color = video::SColor(255, 255, 255, 255);
528 /* ARGB */
529 m_fog_color = video::SColor(255, 77, 179, 230);
530 m_default_ambient_color = video::SColor(255, 120, 120, 120);
531 m_sun_specular_color = video::SColor(255, 255, 255, 255);
532 m_sun_diffuse_color = video::SColor(255, 255, 255, 255);
533 m_sun_position = core::vector3df(0, 10, 10);
534 irr_driver->setSSAORadius(1.);
535 irr_driver->setSSAOK(1.5);
536 irr_driver->setSSAOSigma(1.);
537 XMLNode *root = file_manager->createXMLTree(m_filename);
538
539 if(!root || root->getName()!="track")
540 {
541 delete root;
542 std::ostringstream o;
543 o<<"Can't load track '"<<m_filename<<"', no track element.";
544 throw std::runtime_error(o.str());
545 }
546 root->get("name", &m_name);
547
548 std::string designer;
549 root->get("designer", &designer);
550 m_designer = StringUtils::xmlDecode(designer);
551
552 root->get("version", &m_version);
553 std::vector<std::string> filenames;
554 root->get("music", &filenames);
555 root->get("screenshot", &m_screenshot);
556 root->get("gravity", &m_gravity);
557 root->get("friction", &m_friction);
558 root->get("soccer", &m_is_soccer);
559 root->get("arena", &m_is_arena);
560 root->get("ctf", &m_is_ctf);
561 root->get("max-arena-players", &m_max_arena_players);
562 root->get("cutscene", &m_is_cutscene);
563 root->get("groups", &m_groups);
564 root->get("internal", &m_internal);
565 root->get("reverse", &m_reverse_available);
566 root->get("default-number-of-laps",&m_default_number_of_laps);
567 root->get("push-back", &m_enable_push_back);
568 root->get("clouds", &m_clouds);
569 root->get("bloom", &m_bloom);
570 root->get("bloom-threshold", &m_bloom_threshold);
571 root->get("shadows", &m_shadows);
572 root->get("is-during-day", &m_is_day);
573 root->get("displacement-speed", &m_displacement_speed);
574 root->get("color-level-in", &m_color_inlevel);
575 root->get("color-level-out", &m_color_outlevel);
576
577 getMusicInformation(filenames, m_music);
578 if (m_default_number_of_laps <= 0)
579 m_default_number_of_laps = 3;
580 m_actual_number_of_laps = m_default_number_of_laps;
581
582 // Make the default for auto-rescue in battle mode and soccer mode to be false
583 if(m_is_arena || m_is_soccer)
584 m_enable_auto_rescue = false;
585 root->get("auto-rescue", &m_enable_auto_rescue);
586 root->get("smooth-normals", &m_smooth_normals);
587 // Reverse is meaningless in arena
588 if(m_is_arena || m_is_soccer)
589 m_reverse_available = false;
590
591
592 for(unsigned int i=0; i<root->getNumNodes(); i++)
593 {
594 const XMLNode *mode=root->getNode(i);
595 if(mode->getName()!="mode") continue;
596 TrackMode tm;
597 mode->get("name", &tm.m_name );
598 mode->get("quads", &tm.m_quad_name );
599 mode->get("graph", &tm.m_graph_name);
600 mode->get("scene", &tm.m_scene );
601 m_all_modes.push_back(tm);
602 }
603 // If no mode is specified, add a default mode.
604 if(m_all_modes.size()==0)
605 {
606 TrackMode tm;
607 m_all_modes.push_back(tm);
608 }
609
610 if(m_groups.size()==0) m_groups.push_back(DEFAULT_GROUP_NAME);
611 const XMLNode *xml_node = root->getNode("curves");
612
613 if(xml_node) loadCurves(*xml_node);
614
615 // Set the correct paths
616 if (m_screenshot.length() > 0)
617 {
618 m_screenshot = m_root+m_screenshot;
619 }
620 delete root;
621
622 std::string dir = StringUtils::getPath(m_filename);
623 std::string easter_name = dir + "/easter_eggs.xml";
624
625 XMLNode *easter = file_manager->createXMLTree(easter_name);
626
627 if(easter)
628 {
629 for(unsigned int i=0; i<easter->getNumNodes(); i++)
630 {
631 const XMLNode *eggs = easter->getNode(i);
632 if(eggs->getNumNodes() > 0)
633 {
634 m_has_easter_eggs = true;
635 break;
636 }
637 }
638 delete easter;
639 }
640
641 if(file_manager->fileExists(m_root+"navmesh.xml") && !m_dont_load_navmesh)
642 m_has_navmesh = true;
643 else if ( (m_is_arena || m_is_soccer) && !m_dont_load_navmesh)
644 {
645 Log::warn("Track", "NavMesh is not found for arena %s, "
646 "disable AI for it.\n", m_name.c_str());
647 }
648 if (m_is_soccer)
649 {
650 // Currently only max eight players in soccer mode
651 m_max_arena_players = 8;
652 }
653 // Max 10 players supported in arena
654 if (m_max_arena_players > 10)
655 m_max_arena_players = 10;
656
657 } // loadTrackInfo
658
659 //-----------------------------------------------------------------------------
660 /** Loads all curves from the XML node.
661 */
loadCurves(const XMLNode & node)662 void Track::loadCurves(const XMLNode &node)
663 {
664 for(unsigned int i=0; i<node.getNumNodes(); i++)
665 {
666 const XMLNode *curve = node.getNode(i);
667 m_all_curves.push_back(new BezierCurve(*curve));
668 } // for i<node.getNumNodes
669 } // loadCurves
670
671 //-----------------------------------------------------------------------------
672 /** Loads all music information for the specified files (which is taken from
673 * the track.xml file).
674 * \param filenames List of filenames to load.
675 * \param music On return contains the music information object for the
676 * specified files.
677 */
getMusicInformation(std::vector<std::string> & filenames,std::vector<MusicInformation * > & music)678 void Track::getMusicInformation(std::vector<std::string>& filenames,
679 std::vector<MusicInformation*>& music )
680 {
681 for(int i=0; i<(int)filenames.size(); i++)
682 {
683 std::string full_path = m_root+filenames[i];
684 MusicInformation* mi = music_manager->getMusicInformation(full_path);
685 if(!mi)
686 {
687 try
688 {
689 std::string shared_name = file_manager->searchMusic(filenames[i]);
690 if(shared_name!="")
691 mi = music_manager->getMusicInformation(shared_name);
692 }
693 catch (...)
694 {
695 mi = NULL;
696 }
697 }
698 if(mi)
699 m_music.push_back(mi);
700 else
701 Log::warn("track",
702 "Music information file '%s' not found for track '%s' - ignored.\n",
703 filenames[i].c_str(), m_name.c_str());
704
705 } // for i in filenames
706
707 if (m_music.empty() && !isInternal() && !m_is_cutscene)
708 {
709 m_music.push_back(stk_config->m_default_music);
710
711 Log::warn("track",
712 "Music information for track '%s' replaced by default music.\n",
713 m_name.c_str());
714 }
715
716 } // getMusicInformation
717
718 //-----------------------------------------------------------------------------
719 /** Select and set the music for this track (doesn't actually start it yet).
720 */
startMusic() const721 void Track::startMusic() const
722 {
723 // In case that the music wasn't found (a warning was already printed)
724 if(m_music.size()>0)
725 music_manager->startMusic(m_music[rand()% m_music.size()], false);
726 else
727 music_manager->clearCurrentMusic();
728 } // startMusic
729
730 //-----------------------------------------------------------------------------
731 /** Loads the quad graph for arena, i.e. the definition of all quads, and the
732 * way they are connected to each other. Input file name is hardcoded for now
733 */
loadArenaGraph(const XMLNode & node)734 void Track::loadArenaGraph(const XMLNode &node)
735 {
736 // Determine if rotate minimap is needed for soccer mode (for blue team)
737 // Only need to test local player
738 if (RaceManager::get()->isSoccerMode())
739 {
740 const unsigned pk = RaceManager::get()->getNumPlayers();
741 for (unsigned i = 0; i < pk; i++)
742 {
743 if (!RaceManager::get()->getKartInfo(i).isNetworkPlayer() &&
744 RaceManager::get()->getKartInfo(i).getKartTeam() ==
745 KART_TEAM_BLUE)
746 {
747 m_minimap_invert_x_z = true;
748 break;
749 }
750 }
751 }
752
753 ArenaGraph* graph = new ArenaGraph(m_root+"navmesh.xml", &node);
754 Graph::setGraph(graph);
755
756 if(Graph::get()->getNumNodes()==0)
757 {
758 Log::warn("track", "No graph nodes defined for track '%s'\n",
759 m_filename.c_str());
760 }
761 else
762 {
763 loadMinimap();
764 }
765 } // loadArenaGraph
766
767 //-----------------------------------------------------------------------------
getArenaStartRotation(const Vec3 & xyz,float heading)768 btQuaternion Track::getArenaStartRotation(const Vec3& xyz, float heading)
769 {
770 btQuaternion def_pos(Vec3(0, 1, 0), heading * DEGREE_TO_RAD);
771 if (!ArenaGraph::get())
772 return def_pos;
773
774 // Set the correct axis based on normal of the starting position
775 int node = Graph::UNKNOWN_SECTOR;
776 Graph::get()->findRoadSector(xyz, &node);
777 if (node == Graph::UNKNOWN_SECTOR)
778 {
779 Log::warn("track", "Starting position is not on ArenaGraph");
780 return def_pos;
781 }
782
783 const Vec3& normal = Graph::get()->getQuad(node)->getNormal();
784 btQuaternion q = shortestArcQuat(Vec3(0, 1, 0), normal);
785 btMatrix3x3 m;
786 m.setRotation(q);
787 return btQuaternion(m.getColumn(1), heading * DEGREE_TO_RAD) * q;
788
789 } // getArenaStartRotation
790
791 //-----------------------------------------------------------------------------
792 /** Loads the drive graph, i.e. the definition of all quads, and the way
793 * they are connected to each other.
794 */
loadDriveGraph(unsigned int mode_id,const bool reverse)795 void Track::loadDriveGraph(unsigned int mode_id, const bool reverse)
796 {
797 new DriveGraph(m_root+m_all_modes[mode_id].m_quad_name,
798 m_root+m_all_modes[mode_id].m_graph_name, reverse);
799
800 // setGraph is done in DriveGraph constructor
801 assert(DriveGraph::get());
802 DriveGraph::get()->setupPaths();
803 #ifdef DEBUG
804 for(unsigned int i=0; i<DriveGraph::get()->getNumNodes(); i++)
805 {
806 assert(DriveGraph::get()->getNode(i)->getPredecessor(0)!=-1);
807 }
808 #endif
809
810 if(DriveGraph::get()->getNumNodes()==0)
811 {
812 Log::warn("track", "No graph nodes defined for track '%s'\n",
813 m_filename.c_str());
814 if (RaceManager::get()->getNumberOfKarts() > 1)
815 {
816 Log::fatal("track", "I can handle the lack of driveline in single"
817 "kart mode, but not with AIs\n");
818 }
819 }
820 else
821 {
822 loadMinimap();
823 }
824 } // loadDriveGraph
825
826 // -----------------------------------------------------------------------------
827
mapPoint2MiniMap(const Vec3 & xyz,Vec3 * draw_at) const828 void Track::mapPoint2MiniMap(const Vec3 &xyz, Vec3 *draw_at) const
829 {
830 if (m_minimap_invert_x_z)
831 {
832 Vec3 invert = xyz;
833 invert.setX(-xyz.x());
834 invert.setZ(-xyz.z());
835 Graph::get()->mapPoint2MiniMap(invert, draw_at);
836 }
837 else
838 Graph::get()->mapPoint2MiniMap(xyz, draw_at);
839 draw_at->setX(draw_at->getX() * m_minimap_x_scale);
840 draw_at->setY(draw_at->getY() * m_minimap_y_scale);
841 }
842 // -----------------------------------------------------------------------------
843 /** Convert the track tree into its physics equivalents.
844 * \param main_track_count The number of meshes that are already converted
845 * when the main track was converted. Only the additional meshes
846 * added later still need to be converted.
847 */
createPhysicsModel(unsigned int main_track_count)848 void Track::createPhysicsModel(unsigned int main_track_count)
849 {
850 // Remove the temporary track rigid body, and then convert all objects
851 // (i.e. the track and all additional objects) into a new rigid body
852 // and convert this again. So this way we have an optimised track
853 // rigid body which includes all track objects.
854 // Note that removing the rigid body does not remove the already collected
855 // triangle information, so there is no need to convert the actual track
856 // (first element in m_track_mesh) again!
857
858 if (m_track_mesh == NULL)
859 {
860 Log::error("track",
861 "m_track_mesh == NULL, cannot createPhysicsModel\n");
862 return;
863 }
864
865
866 // Now convert all objects that are only used for the physics
867 // (like invisible walls).
868 for (unsigned int i = 0; i<m_static_physics_only_nodes.size(); i++)
869 {
870 main_loop->renderGUI(5550, i, m_static_physics_only_nodes.size());
871
872 convertTrackToBullet(m_static_physics_only_nodes[i]);
873 if (UserConfigParams::m_physics_debug &&
874 m_static_physics_only_nodes[i]->getType() == scene::ESNT_MESH)
875 {
876 const video::SColor color(255, 255, 105, 180);
877
878 scene::IMesh *mesh = ((scene::IMeshSceneNode*)m_static_physics_only_nodes[i])->getMesh();
879 scene::IMeshBuffer *mb = mesh->getMeshBuffer(0);
880 mb->getMaterial().BackfaceCulling = false;
881 video::S3DVertex * const verts = (video::S3DVertex *) mb->getVertices();
882 const u32 max = mb->getVertexCount();
883 for (i = 0; i < max; i++)
884 {
885 verts[i].Color = color;
886 }
887 }
888 else
889 irr_driver->removeNode(m_static_physics_only_nodes[i]);
890 }
891 main_loop->renderGUI(5560);
892 if (!UserConfigParams::m_physics_debug)
893 m_static_physics_only_nodes.clear();
894
895 for (unsigned int i = 0; i<m_object_physics_only_nodes.size(); i++)
896 {
897 main_loop->renderGUI(5565, i, m_static_physics_only_nodes.size());
898 convertTrackToBullet(m_object_physics_only_nodes[i]);
899 m_object_physics_only_nodes[i]->setVisible(false);
900 m_object_physics_only_nodes[i]->grab();
901 irr_driver->removeNode(m_object_physics_only_nodes[i]);
902 }
903
904 m_track_mesh->removeAll();
905 m_gfx_effect_mesh->removeAll();
906 for(unsigned int i=main_track_count; i<m_all_nodes.size(); i++)
907 {
908 main_loop->renderGUI(5570, i, m_all_nodes.size());
909 convertTrackToBullet(m_all_nodes[i]);
910 uploadNodeVertexBuffer(m_all_nodes[i]);
911 }
912 main_loop->renderGUI(5580);
913 m_track_mesh->createPhysicalBody(m_friction);
914 main_loop->renderGUI(5585);
915 m_gfx_effect_mesh->createCollisionShape();
916 main_loop->renderGUI(5590);
917
918 } // createPhysicsModel
919
920 // -----------------------------------------------------------------------------
921
922
923 /** Convert the graohics track into its physics equivalents.
924 * \param mesh The mesh to convert.
925 * \param node The scene node.
926 */
convertTrackToBullet(scene::ISceneNode * node)927 void Track::convertTrackToBullet(scene::ISceneNode *node)
928 {
929 if (node->getType() == scene::ESNT_TEXT)
930 return;
931
932 if (node->getType() == scene::ESNT_LOD_NODE)
933 {
934 node = ((LODNode*)node)->getFirstNode();
935 if (node == NULL)
936 {
937 Log::warn("track",
938 "This track contains an empty LOD group.");
939 return;
940 }
941 }
942 node->updateAbsolutePosition();
943
944 std::vector<core::matrix4> matrices;
945 matrices.push_back(node->getAbsoluteTransformation());
946
947 scene::IMesh *mesh;
948 switch(node->getType())
949 {
950 case scene::ESNT_MESH :
951 case scene::ESNT_WATER_SURFACE :
952 case scene::ESNT_OCTREE :
953 mesh = ((scene::IMeshSceneNode*)node)->getMesh();
954 break;
955 case scene::ESNT_ANIMATED_MESH :
956 mesh = ((scene::IAnimatedMeshSceneNode*)node)->getMesh();
957 break;
958 case scene::ESNT_SKY_BOX :
959 case scene::ESNT_SKY_DOME:
960 case scene::ESNT_PARTICLE_SYSTEM :
961 case scene::ESNT_TEXT:
962 // These are non-physical
963 return;
964 break;
965 default:
966 int type_as_int = node->getType();
967 char* type = (char*)&type_as_int;
968 Log::debug("track",
969 "[convertTrackToBullet] Unknown scene node type : %c%c%c%c.\n",
970 type[0], type[1], type[2], type[3]);
971 return;
972 } // switch node->getType()
973
974 //core::matrix4 mat;
975 //mat.setRotationDegrees(hpr);
976 //mat.setTranslation(pos);
977 //core::matrix4 mat_scale;
978 //// Note that we can't simply call mat.setScale, since this would
979 //// overwrite the elements on the diagonal, making any rotation incorrect.
980 //mat_scale.setScale(scale);
981 //mat *= mat_scale;
982
983 for(unsigned int i=0; i<mesh->getMeshBufferCount(); i++)
984 {
985 scene::IMeshBuffer *mb = mesh->getMeshBuffer(i);
986 // FIXME: take translation/rotation into account
987 if (mb->getVertexType() != video::EVT_STANDARD &&
988 mb->getVertexType() != video::EVT_2TCOORDS &&
989 mb->getVertexType() != video::EVT_TANGENTS &&
990 mb->getVertexType() != video::EVT_SKINNED_MESH)
991 {
992 Log::warn("track", "convertTrackToBullet: Ignoring type '%d'!\n",
993 mb->getVertexType());
994 continue;
995 }
996 u16 *mbIndices = mb->getIndices();
997 Vec3 vertices[3];
998 Vec3 normals[3];
999
1000 #ifndef SERVER_ONLY
1001 SP::SPMeshBuffer* spmb = dynamic_cast<SP::SPMeshBuffer*>(mb);
1002 if (spmb)
1003 {
1004 video::S3DVertexSkinnedMesh* mbVertices = (video::S3DVertexSkinnedMesh*)mb->getVertices();
1005 for (unsigned int matrix_index = 0; matrix_index < matrices.size(); matrix_index++)
1006 {
1007 for (unsigned int j = 0; j < mb->getIndexCount(); j += 3)
1008 {
1009 TriangleMesh* tmesh = m_track_mesh;
1010 Material* material = spmb->getSTKMaterial(j);
1011 if (material->isSurface())
1012 {
1013 tmesh = m_gfx_effect_mesh;
1014 }
1015 else if (material->isIgnore())
1016 {
1017 continue;
1018 }
1019 for (unsigned int k = 0; k < 3; k++)
1020 {
1021 int indx = mbIndices[j + k];
1022 core::vector3df v = mbVertices[indx].m_position;
1023 matrices[matrix_index].transformVect(v);
1024 vertices[k] = v;
1025 normals[k] = MiniGLM::decompressVector3(mbVertices[indx].m_normal);
1026 } // for k
1027 if (tmesh)
1028 {
1029 tmesh->addTriangle(vertices[0], vertices[1],
1030 vertices[2], normals[0],
1031 normals[1], normals[2],
1032 material);
1033 }
1034 } // for j
1035 } // for matrix_index
1036 }
1037 else
1038 #endif
1039 {
1040 const video::SMaterial& irrMaterial = mb->getMaterial();
1041 std::string t1_full_path, t2_full_path;
1042 video::ITexture* t1 = irrMaterial.getTexture(0);
1043 if (t1)
1044 {
1045 t1_full_path = t1->getName().getPtr();
1046 t1_full_path = file_manager->getFileSystem()->getAbsolutePath(
1047 t1_full_path.c_str()).c_str();
1048 }
1049 video::ITexture* t2 = irrMaterial.getTexture(1);
1050 if (t2)
1051 {
1052 t2_full_path = t2->getName().getPtr();
1053 t2_full_path = file_manager->getFileSystem()->getAbsolutePath(
1054 t2_full_path.c_str()).c_str();
1055 }
1056 const Material* material = material_manager->getMaterialSPM(
1057 t1_full_path, t2_full_path);
1058 TriangleMesh *tmesh = m_track_mesh;
1059 // Special gfx meshes will not be stored as a normal physics body,
1060 // but converted to a collision body only, so that ray tests
1061 // against them can be done.
1062 if (material->isSurface())
1063 tmesh = m_gfx_effect_mesh;
1064 // A material which is a surface must be converted,
1065 // even if it's marked as ignore. So only ignore
1066 // non-surface materials.
1067 else if(material->isIgnore())
1068 continue;
1069
1070 if (mb->getVertexType() == video::EVT_STANDARD)
1071 {
1072 irr::video::S3DVertex* mbVertices=(video::S3DVertex*)mb->getVertices();
1073 for (unsigned int matrix_index = 0; matrix_index < matrices.size(); matrix_index++)
1074 {
1075 for (unsigned int j = 0; j < mb->getIndexCount(); j += 3)
1076 {
1077 for (unsigned int k = 0; k < 3; k++)
1078 {
1079 int indx = mbIndices[j + k];
1080 core::vector3df v = mbVertices[indx].Pos;
1081 matrices[matrix_index].transformVect(v);
1082 vertices[k] = v;
1083 normals[k] = mbVertices[indx].Normal;
1084 } // for k
1085
1086 if (tmesh)
1087 {
1088 tmesh->addTriangle(vertices[0], vertices[1],
1089 vertices[2], normals[0],
1090 normals[1], normals[2],
1091 material);
1092 }
1093 } // for j
1094 } // for matrix_index
1095 }
1096 else if (mb->getVertexType() == video::EVT_2TCOORDS)
1097 {
1098 irr::video::S3DVertex2TCoords* mbVertices = (video::S3DVertex2TCoords*)mb->getVertices();
1099 for (unsigned int matrix_index = 0; matrix_index < matrices.size(); matrix_index++)
1100 {
1101 for (unsigned int j = 0; j < mb->getIndexCount(); j += 3)
1102 {
1103 for (unsigned int k = 0; k < 3; k++)
1104 {
1105 int indx = mbIndices[j + k];
1106 core::vector3df v = mbVertices[indx].Pos;
1107 matrices[matrix_index].transformVect(v);
1108 vertices[k] = v;
1109 normals[k] = mbVertices[indx].Normal;
1110 } // for k
1111
1112 if (tmesh)
1113 {
1114 tmesh->addTriangle(vertices[0], vertices[1],
1115 vertices[2], normals[0],
1116 normals[1], normals[2],
1117 material);
1118 }
1119 } // for j
1120 } // for matrix_index
1121 }
1122 else if (mb->getVertexType() == video::EVT_TANGENTS)
1123 {
1124 irr::video::S3DVertexTangents* mbVertices = (video::S3DVertexTangents*)mb->getVertices();
1125 for (unsigned int matrix_index = 0; matrix_index < matrices.size(); matrix_index++)
1126 {
1127 for (unsigned int j = 0; j < mb->getIndexCount(); j += 3)
1128 {
1129 for (unsigned int k = 0; k < 3; k++)
1130 {
1131 int indx = mbIndices[j + k];
1132 core::vector3df v = mbVertices[indx].Pos;
1133 matrices[matrix_index].transformVect(v);
1134 vertices[k] = v;
1135 normals[k] = mbVertices[indx].Normal;
1136 } // for k
1137
1138 if (tmesh)
1139 {
1140 tmesh->addTriangle(vertices[0], vertices[1],
1141 vertices[2], normals[0],
1142 normals[1], normals[2],
1143 material);
1144 }
1145 } // for j
1146 } // for matrix_index
1147 }
1148 }
1149 } // for i<getMeshBufferCount
1150
1151 } // convertTrackToBullet
1152
1153 // ----------------------------------------------------------------------------
1154
loadMinimap()1155 void Track::loadMinimap()
1156 {
1157 #ifndef SERVER_ONLY
1158 if (GUIEngine::isNoGraphics())
1159 return;
1160
1161 //Create the minimap resizing it as necessary.
1162 core::dimension2du mini_map_size = World::getWorld()->getRaceGUI()->getMiniMapSize();
1163
1164 //Use twice the size of the rendered minimap to reduce significantly aliasing
1165 m_render_target = Graph::get()->makeMiniMap(mini_map_size * 2,
1166 "minimap::" + m_ident, video::SColor(127, 255, 255, 255),
1167 m_minimap_invert_x_z);
1168
1169 updateMiniMapScale();
1170 #endif
1171 } // loadMinimap
1172
1173 // ----------------------------------------------------------------------------
updateMiniMapScale()1174 void Track::updateMiniMapScale()
1175 {
1176 if (!m_render_target)
1177 return;
1178
1179 core::dimension2du mini_map_size = World::getWorld()->getRaceGUI()->getMiniMapSize();
1180 // Happens in race result gui
1181 if (mini_map_size.Width == 0 || mini_map_size.Height == 0)
1182 return;
1183 core::dimension2du mini_map_texture_size = m_render_target->getTextureSize();
1184
1185 if(mini_map_texture_size.Width)
1186 m_minimap_x_scale = float(mini_map_size.Width) / float(mini_map_texture_size.Width);
1187 else
1188 m_minimap_x_scale = 0;
1189
1190 if(mini_map_texture_size.Height)
1191 m_minimap_y_scale = float(mini_map_size.Height) / float(mini_map_texture_size.Height);
1192 else
1193 m_minimap_y_scale = 0;
1194 }
1195
1196 // ----------------------------------------------------------------------------
1197 /** Loads the main track model (i.e. all other objects contained in the
1198 * scene might use raycast on this track model to determine the actual
1199 * height of the terrain.
1200 */
loadMainTrack(const XMLNode & root)1201 bool Track::loadMainTrack(const XMLNode &root)
1202 {
1203 assert(m_track_mesh==NULL);
1204 assert(m_gfx_effect_mesh==NULL);
1205
1206 m_challenges.clear();
1207
1208 m_track_mesh = new TriangleMesh(/*can_be_transformed*/false);
1209 m_gfx_effect_mesh = new TriangleMesh(/*can_be_transformed*/false);
1210
1211 const XMLNode *track_node = root.getNode("track");
1212 std::string model_name;
1213 track_node->get("model", &model_name);
1214 std::string full_path = m_root+model_name;
1215 scene::IMesh *mesh = irr_driver->getMesh(full_path);
1216
1217 if(!mesh)
1218 {
1219 Log::fatal("track",
1220 "Main track model '%s' in '%s' not found, aborting.\n",
1221 track_node->getName().c_str(), model_name.c_str());
1222 }
1223
1224 scene::ISceneNode* scene_node = NULL;
1225 scene::IMesh* tangent_mesh = NULL;
1226 #ifdef SERVER_ONLY
1227 if (false)
1228 #else
1229 if (m_version < 7 && !CVS->isGLSL() && !GUIEngine::isNoGraphics())
1230 #endif
1231 {
1232 // The mesh as returned does not have all mesh buffers with the same
1233 // texture combined. This can result in a _HUGE_ overhead. E.g. instead
1234 // of 46 different mesh buffers over 500 (for some tracks even >1000)
1235 // were created. This means less effect from hardware support, less
1236 // vertices per opengl operation, more overhead on CPU, ...
1237 // So till we have a better b3d exporter which can combine the different
1238 // meshes which use the same texture when exporting, the meshes are
1239 // combined using CBatchingMesh.
1240 scene::CBatchingMesh *merged_mesh = new scene::CBatchingMesh();
1241 merged_mesh->addMesh(mesh);
1242 merged_mesh->finalize();
1243 tangent_mesh = merged_mesh;
1244 // The reference count of the mesh is 1, since it is in irrlicht's
1245 // cache. So we only have to remove it from the cache.
1246 irr_driver->removeMeshFromCache(mesh);
1247 }
1248 else
1249 {
1250 // SPM does the combine for you
1251 tangent_mesh = mesh;
1252 tangent_mesh->grab();
1253 }
1254 // The merged mesh is grabbed by the octtree, so we don't need
1255 // to keep a reference to it.
1256 scene_node = irr_driver->addMesh(tangent_mesh, "track_main");
1257 // We should drop the merged mesh (since it's now referred to in the
1258 // scene node), but then we need to grab it since it's in the
1259 // m_all_cached_meshes.
1260 m_all_cached_meshes.push_back(tangent_mesh);
1261 irr_driver->grabAllTextures(tangent_mesh);
1262 main_loop->renderGUI(4000);
1263
1264 #ifdef DEBUG
1265 std::string debug_name=model_name+" (main track, octtree)";
1266 scene_node->setName(debug_name.c_str());
1267 #endif
1268 //merged_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1269
1270 core::vector3df xyz(0,0,0);
1271 track_node->getXYZ(&xyz);
1272 core::vector3df hpr(0,0,0);
1273 track_node->getHPR(&hpr);
1274 scene_node->setPosition(xyz);
1275 scene_node->setRotation(hpr);
1276 handleAnimatedTextures(scene_node, *track_node);
1277 m_all_nodes.push_back(scene_node);
1278
1279 MeshTools::minMax3D(tangent_mesh, &m_aabb_min, &m_aabb_max);
1280 // Increase the maximum height of the track: since items that fly
1281 // too high explode, e.g. cakes can not be show when being at the
1282 // top of the track (since they will explode when leaving the AABB
1283 // of the track). While the test for this in Flyable::updateAndDelete
1284 // could be relaxed to fix this, it is not certain how the physics
1285 // will handle items that are out of the AABB
1286 m_aabb_max.setY(m_aabb_max.getY()+30.0f);
1287 Physics::get()->init(m_aabb_min, m_aabb_max);
1288
1289 ModelDefinitionLoader lodLoader(this);
1290
1291 // Load LOD groups
1292 const XMLNode *lod_xml_node = root.getNode("lod");
1293 if (lod_xml_node != NULL)
1294 {
1295 for (unsigned int i = 0; i < lod_xml_node->getNumNodes(); i++)
1296 {
1297 main_loop->renderGUI(4100, i, lod_xml_node->getNumNodes());
1298
1299 const XMLNode* lod_group_xml = lod_xml_node->getNode(i);
1300 for (unsigned int j = 0; j < lod_group_xml->getNumNodes(); j++)
1301 {
1302 lodLoader.addModelDefinition(lod_group_xml->getNode(j));
1303 }
1304 }
1305 }
1306 main_loop->renderGUI(4200);
1307
1308 for (unsigned int i=0; i<track_node->getNumNodes(); i++)
1309 {
1310 main_loop->renderGUI(4300, i, track_node->getNumNodes());
1311
1312 const XMLNode *n=track_node->getNode(i);
1313 // Animated textures have already been handled
1314 if(n->getName()=="animated-texture") continue;
1315 // Only "object" entries are allowed now inside of the model tag
1316 if(n->getName()!="static-object")
1317 {
1318 Log::error("track",
1319 "Incorrect tag '%s' inside <model> of scene file - ignored\n",
1320 n->getName().c_str());
1321 continue;
1322 }
1323
1324 core::vector3df xyz(0,0,0);
1325 n->get("xyz", &xyz);
1326 core::vector3df hpr(0,0,0);
1327 n->get("hpr", &hpr);
1328 core::vector3df scale(1.0f, 1.0f, 1.0f);
1329 n->get("scale", &scale);
1330
1331 bool tangent = false;
1332 n->get("tangents", &tangent);
1333
1334 scene::ISceneNode* scene_node;
1335 model_name="";
1336 n->get("model", &model_name);
1337 full_path = m_root+model_name;
1338 std::string interaction;
1339 n->get("interaction", &interaction);
1340
1341 // a special challenge orb object for overworld
1342 std::string challenge;
1343 n->get("challenge", &challenge);
1344
1345 bool lod_instance = false;
1346 n->get("lod_instance", &lod_instance);
1347
1348 if (lod_instance)
1349 {
1350 LODNode* node = lodLoader.instanciateAsLOD(n, NULL, NULL);
1351 if (node != NULL)
1352 {
1353 node->setPosition(xyz);
1354 node->setRotation(hpr);
1355 node->setScale(scale);
1356 node->updateAbsolutePosition();
1357
1358 m_all_nodes.push_back( node );
1359 }
1360 }
1361 else
1362 {
1363 // TODO: check if mesh is animated or not
1364 scene::IMesh *a_mesh = irr_driver->getMesh(full_path);
1365 if(!a_mesh)
1366 {
1367 Log::error("track", "Object model '%s' not found, ignored.\n",
1368 full_path.c_str());
1369 continue;
1370 }
1371
1372 // The meshes loaded here are in irrlicht's mesh cache. So we
1373 // have to keep track of them in order to properly remove them
1374 // from memory. We could add each track only once in a list, but
1375 // it's actually faster to add meshes multipl times (if they are
1376 // used more than once), and increase the ref count each time.
1377 // When removing the meshes, we drop them till the ref count is
1378 // 1 - which means that the only reference is now in the cache,
1379 // and can therefore be removed.
1380 m_all_cached_meshes.push_back(a_mesh);
1381 irr_driver->grabAllTextures(a_mesh);
1382 a_mesh->grab();
1383 scene_node = irr_driver->addMesh(a_mesh, model_name);
1384 scene_node->setPosition(xyz);
1385 scene_node->setRotation(hpr);
1386 scene_node->setScale(scale);
1387 #ifdef DEBUG
1388 std::string debug_name = model_name+" (static track-object)";
1389 scene_node->setName(debug_name.c_str());
1390 #endif
1391
1392 handleAnimatedTextures(scene_node, *n);
1393
1394 // for challenge orbs, a bit more work to do
1395 // TODO: this is hardcoded for the overworld, convert to scripting
1396 if (challenge.size() > 0)
1397 {
1398 const ChallengeData* c = NULL;
1399
1400 if (challenge != "tutorial")
1401 {
1402 c = unlock_manager->getChallengeData(challenge);
1403 if (c == NULL)
1404 {
1405 Log::error("track", "Cannot find challenge named <%s>\n",
1406 challenge.c_str());
1407 scene_node->remove();
1408 continue;
1409 }
1410 }
1411
1412 m_challenges.push_back( OverworldChallenge(xyz, challenge) );
1413
1414 if (c && c->isGrandPrix())
1415 {
1416
1417 }
1418 else
1419 {
1420 if (challenge != "tutorial")
1421 {
1422 Track* t = track_manager->getTrack(c->getTrackId());
1423 if (t == NULL)
1424 {
1425 Log::error("track", "Cannot find track named <%s>\n",
1426 c->getTrackId().c_str());
1427 continue;
1428 }
1429
1430 std::string sshot = t->getScreenshotFile();
1431 video::ITexture* screenshot = irr_driver->getTexture(sshot);
1432
1433 if (screenshot == NULL)
1434 {
1435 Log::error("track",
1436 "Cannot find track screenshot <%s>",
1437 sshot.c_str());
1438 continue;
1439 }
1440 scene_node->getMaterial(0).setTexture(0, screenshot);
1441 }
1442 }
1443
1444 // make transparent
1445 for (unsigned int m=0; m<a_mesh->getMeshBufferCount(); m++)
1446 {
1447 scene::IMeshBuffer* mb = a_mesh->getMeshBuffer(m);
1448 if (mb->getVertexType() == video::EVT_STANDARD)
1449 {
1450 video::S3DVertex* v = (video::S3DVertex*)mb->getVertices();
1451 for (unsigned int n=0; n<mb->getVertexCount(); n++)
1452 {
1453 v[n].Color.setAlpha(125);
1454 }
1455 }
1456 }
1457
1458
1459 LODNode* lod_node = new LODNode("challenge_orb",
1460 irr_driver->getSceneManager()->getRootSceneNode(),
1461 irr_driver->getSceneManager());
1462 lod_node->setPosition(xyz);
1463 lod_node->add(50, scene_node, true /* reparent */);
1464
1465 m_all_nodes.push_back( lod_node );
1466 }
1467 else
1468 {
1469 if(interaction=="physics-only")
1470 m_static_physics_only_nodes.push_back(scene_node);
1471 else
1472 m_all_nodes.push_back( scene_node );
1473 }
1474 }
1475
1476 } // for i
1477
1478 // This will (at this stage) only convert the main track model.
1479 for(unsigned int i=0; i<m_all_nodes.size(); i++)
1480 {
1481 main_loop->renderGUI(4350, i, m_all_nodes.size());
1482 convertTrackToBullet(m_all_nodes[i]);
1483 main_loop->renderGUI(4360, i, m_all_nodes.size());
1484 uploadNodeVertexBuffer(m_all_nodes[i]);
1485 main_loop->renderGUI(4400, i, m_all_nodes.size());
1486 }
1487
1488 // Free the tangent (track mesh) after converting to physics
1489 if (GUIEngine::isNoGraphics())
1490 tangent_mesh->freeMeshVertexBuffer();
1491
1492 if (m_track_mesh == NULL)
1493 {
1494 Log::fatal("track", "m_track_mesh == NULL, cannot loadMainTrack\n");
1495 }
1496
1497 m_gfx_effect_mesh->createCollisionShape();
1498 scene_node->setMaterialFlag(video::EMF_LIGHTING, true);
1499 scene_node->setMaterialFlag(video::EMF_GOURAUD_SHADING, true);
1500 main_loop->renderGUI(4500);
1501
1502 return true;
1503 } // loadMainTrack
1504
1505 // ----------------------------------------------------------------------------
freeCachedMeshVertexBuffer()1506 void Track::freeCachedMeshVertexBuffer()
1507 {
1508 if (GUIEngine::isNoGraphics())
1509 {
1510 for (unsigned i = 0; i < m_all_cached_meshes.size(); i++)
1511 m_all_cached_meshes[i]->freeMeshVertexBuffer();
1512 }
1513 } // freeCachedMeshVertexBuffer
1514
1515 // ----------------------------------------------------------------------------
1516 /** Handles animated textures.
1517 * \param node The scene node for which animated textures are handled.
1518 * \param xml The node containing the data for the animated notion.
1519 */
handleAnimatedTextures(scene::ISceneNode * node,const XMLNode & xml)1520 void Track::handleAnimatedTextures(scene::ISceneNode *node, const XMLNode &xml)
1521 {
1522 for(unsigned int node_number = 0; node_number<xml.getNumNodes();
1523 node_number++)
1524 {
1525 const XMLNode *texture_node = xml.getNode(node_number);
1526 if(texture_node->getName()!="animated-texture") continue;
1527 std::string name;
1528 texture_node->get("name", &name);
1529 if(name=="")
1530 {
1531 Log::error("track",
1532 "Animated texture: no texture name specified for track '%s'\n",
1533 m_ident.c_str());
1534 continue;
1535 }
1536
1537 // to lower case, for case-insensitive comparison
1538 name = StringUtils::toLowerCase(name);
1539
1540 int moving_textures_found = 0;
1541 SP::SPMeshNode* spmn = dynamic_cast<SP::SPMeshNode*>(node);
1542 if (spmn)
1543 {
1544 for (unsigned i = 0; i < spmn->getSPM()->getMeshBufferCount(); i++)
1545 {
1546 SP::SPMeshBuffer* spmb = spmn->getSPM()->getSPMeshBuffer(i);
1547 const std::vector<Material*>& m = spmb->getAllSTKMaterials();
1548 bool found = false;
1549 for (unsigned j = 0; j < m.size(); j++)
1550 {
1551 Material* mat = m[j];
1552 std::string mat_name =
1553 StringUtils::getBasename(mat->getSamplerPath(0));
1554 mat_name = StringUtils::toLowerCase(mat_name);
1555 if (mat_name == name)
1556 {
1557 found = true;
1558 moving_textures_found++;
1559 spmb->enableTextureMatrix(j);
1560 MovingTexture* mt =
1561 new MovingTexture(NULL, *texture_node);
1562 mt->setSPTM(spmn->getTextureMatrix(i).data());
1563 m_animated_textures.push_back(mt);
1564 // For spm only 1 texture matrix per mesh buffer is
1565 // possible
1566 break;
1567 }
1568 }
1569 if (found)
1570 {
1571 break;
1572 }
1573 }
1574 }
1575 else
1576 {
1577 for(unsigned int i=0; i<node->getMaterialCount(); i++)
1578 {
1579 video::SMaterial &irrMaterial=node->getMaterial(i);
1580 for(unsigned int j=0; j<video::MATERIAL_MAX_TEXTURES; j++)
1581 {
1582 video::ITexture* t=irrMaterial.getTexture(j);
1583 if(!t) continue;
1584 std::string texture_name =
1585 StringUtils::getBasename(t->getName().getPtr());
1586
1587 // to lower case, for case-insensitive comparison
1588 texture_name = StringUtils::toLowerCase(texture_name);
1589
1590 if (texture_name != name) continue;
1591 core::matrix4 *m = &irrMaterial.getTextureMatrix(j);
1592 m_animated_textures.push_back(new MovingTexture(m, *texture_node));
1593 moving_textures_found++;
1594 } // for j<MATERIAL_MAX_TEXTURES
1595 } // for i<getMaterialCount
1596 }
1597 if (moving_textures_found == 0)
1598 Log::warn("AnimTexture", "Did not find animate texture '%s'", name.c_str());
1599 } // for node_number < xml->getNumNodes
1600 } // handleAnimatedTextures
1601
1602 // ----------------------------------------------------------------------------
1603 /** This updates all only graphical elements. It is only called once per
1604 * rendered frame, not once per time step.
1605 * float dt Time since last rame.
1606 */
updateGraphics(float dt)1607 void Track::updateGraphics(float dt)
1608 {
1609 m_track_object_manager->updateGraphics(dt);
1610
1611 for (unsigned int i = 0; i<m_animated_textures.size(); i++)
1612 {
1613 m_animated_textures[i]->update(dt);
1614 }
1615 m_item_manager->updateGraphics(dt);
1616
1617 } // updateGraphics
1618
1619 // ----------------------------------------------------------------------------
1620 /** Update, called once per physics time step.
1621 * \param dt Timestep.
1622 */
update(int ticks)1623 void Track::update(int ticks)
1624 {
1625 ProcessType type = STKProcess::getType();
1626 if (type == PT_MAIN && !m_startup_run) // first time running update = good point to run startup script
1627 {
1628 Scripting::ScriptEngine::getInstance()->runFunction(false, "void onStart()");
1629 m_startup_run = true;
1630 // After onStart all track objects will be hidden as needed
1631 // we only copy track objects with physical body which affects network
1632 if (LobbyProtocol::getByType<LobbyProtocol>(PT_CHILD))
1633 {
1634 Track* child_track = clone();
1635 m_current_track[PT_CHILD] = child_track;
1636 }
1637 }
1638 float dt = stk_config->ticks2Time(ticks);
1639 m_check_manager->update(dt);
1640 m_item_manager->update(ticks);
1641
1642 // TODO: enable onUpdate scripts if we ever find a compelling use for them
1643 //Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine();
1644 //script_engine->runScript("void onUpdate()");
1645 } // update
1646
1647 // ----------------------------------------------------------------------------
1648 /** Handles an explosion, i.e. it makes sure that all physical objects are
1649 * affected accordingly.
1650 * \param pos Position of the explosion.
1651 * \param obj If the hit was a physical object, this object will be affected
1652 * more. Otherwise this is NULL.
1653 * \param secondary_hits True if items that are not directly hit should
1654 * also be affected. */
handleExplosion(const Vec3 & pos,const PhysicalObject * obj,bool secondary_hits) const1655 void Track::handleExplosion(const Vec3 &pos, const PhysicalObject *obj,
1656 bool secondary_hits) const
1657 {
1658 m_track_object_manager->handleExplosion(pos, obj, secondary_hits);
1659 } // handleExplosion
1660
1661 // ----------------------------------------------------------------------------
1662 /** Creates a water node. OBSOLETE, kept for backwards compat only
1663 * \param node The XML node containing the specifications for the water node.
1664 */
createWater(const XMLNode & node)1665 void Track::createWater(const XMLNode &node)
1666 {
1667 std::string model_name;
1668 node.get("model", &model_name);
1669 std::string full_path = m_root+model_name;
1670
1671 scene::IMesh *mesh = irr_driver->getMesh(full_path);
1672 if (mesh == NULL)
1673 {
1674 Log::warn("Track", "Water not found : '%s'", full_path.c_str());
1675 return;
1676 }
1677
1678 /*
1679 float wave_height = 2.0f;
1680 float wave_speed = 300.0f;
1681 float wave_length = 10.0f;
1682 node.get("height", &wave_height);
1683 float time;
1684 if(node.get("time", &time))
1685 {
1686 wave_speed = time * 1000.0f/(2.0f*M_PI);
1687 }
1688 else
1689 node.get("speed", &wave_speed);
1690 if(wave_speed==0)
1691 {
1692 // A speed of 0 results in a division by zero, so avoid this.
1693 // The actual time for a wave from one maximum to the next is
1694 // given by 2*M_PI*speed/1000.
1695 Log::warn("Track",
1696 "Wave-speed or time is 0, resetting it to the default.");
1697 wave_speed =300.0f;
1698 }
1699 node.get("length", &wave_length);
1700 */
1701 scene::ISceneNode* scene_node = NULL;
1702 /*
1703 if (UserConfigParams::m_particles_effects > 1)
1704 {
1705 scene::IMesh *welded;
1706 scene_node = irr_driver->addWaterNode(mesh, &welded,
1707 wave_height,
1708 wave_speed,
1709 wave_length);
1710
1711 mesh->grab();
1712 irr_driver->grabAllTextures(mesh);
1713 m_all_cached_meshes.push_back(mesh);
1714
1715 mesh = welded;
1716 }
1717 else
1718 {*/
1719 scene_node = irr_driver->addMesh(mesh, "water");
1720 //}
1721
1722 if(!mesh || !scene_node)
1723 {
1724 Log::error("track", "Water model '%s' in '%s' not found, ignored.\n",
1725 node.getName().c_str(), model_name.c_str());
1726 return;
1727 }
1728
1729 #ifdef DEBUG
1730 std::string debug_name = model_name+"(water node)";
1731 scene_node->setName(debug_name.c_str());
1732 #endif
1733 mesh->grab();
1734 m_all_cached_meshes.push_back(mesh);
1735 irr_driver->grabAllTextures(mesh);
1736
1737 core::vector3df xyz(0,0,0);
1738 node.get("xyz", &xyz);
1739 core::vector3df hpr(0,0,0);
1740 node.get("hpr", &hpr);
1741 scene_node->setPosition(xyz);
1742 scene_node->setRotation(hpr);
1743 m_all_nodes.push_back(scene_node);
1744 handleAnimatedTextures(scene_node, node);
1745
1746 scene_node->getMaterial(0).setFlag(video::EMF_GOURAUD_SHADING, true);
1747 } // createWater
1748
1749 // ----------------------------------------------------------------------------
recursiveUpdatePosition(scene::ISceneNode * node)1750 static void recursiveUpdatePosition(scene::ISceneNode *node)
1751 {
1752 node->updateAbsolutePosition();
1753
1754 scene::ISceneNodeList::ConstIterator it = node->getChildren().begin();
1755 for (; it != node->getChildren().end(); ++it)
1756 {
1757 recursiveUpdatePosition(*it);
1758 }
1759 } // recursiveUpdatePosition
1760
1761 // ----------------------------------------------------------------------------
recursiveUpdatePhysics(std::vector<TrackObject * > & tos)1762 static void recursiveUpdatePhysics(std::vector<TrackObject*>& tos)
1763 {
1764 for (TrackObject* to : tos)
1765 {
1766 if (to->getPhysicalObject())
1767 {
1768 TrackObjectPresentationSceneNode* sn = to
1769 ->getPresentation<TrackObjectPresentationSceneNode>();
1770 if (sn)
1771 {
1772 to->getPhysicalObject()->move(
1773 sn->getNode()->getAbsoluteTransformation().getTranslation(),
1774 sn->getNode()->getAbsoluteTransformation()
1775 .getRotationDegrees());
1776 }
1777 }
1778 recursiveUpdatePhysics(to->getChildren());
1779 }
1780 } // recursiveUpdatePhysics
1781
1782 // ----------------------------------------------------------------------------
1783 /** This function load the actual scene, i.e. all parts of the track,
1784 * animations, items, ... It is called from world during initialisation.
1785 * Track is the first model to be loaded, so at this stage the root scene node
1786 * is empty.
1787 * \param parent The actual world.
1788 * \param reverse_track True if the track should be run in reverse.
1789 * \param mode_id Which of the modes of a track to use. This determines which
1790 * scene, quad, and graph file to load.
1791 */
loadTrackModel(bool reverse_track,unsigned int mode_id)1792 void Track::loadTrackModel(bool reverse_track, unsigned int mode_id)
1793 {
1794 assert(m_current_track[PT_MAIN].load() == NULL);
1795
1796 // Use m_filename to also get the path, not only the identifier
1797 STKTexManager::getInstance()
1798 ->setTextureErrorMessage("While loading track '%s'", m_filename);
1799 if(!m_reverse_available)
1800 {
1801 reverse_track = false;
1802 }
1803 main_loop->renderGUI(3000);
1804 m_check_manager = new CheckManager();
1805 assert(m_all_cached_meshes.size()==0);
1806 if(UserConfigParams::logMemory())
1807 {
1808 Log::debug("[memory] Before loading '%s': mesh cache %d "
1809 "texture cache %d\n",
1810 getIdent().c_str(),
1811 irr_driver->getSceneManager()->getMeshCache()->getMeshCount(),
1812 irr_driver->getVideoDriver()->getTextureCount());
1813 #ifdef DEBUG
1814 scene::IMeshCache *cache =
1815 irr_driver->getSceneManager()->getMeshCache();
1816 m_old_mesh_buffers.clear();
1817 for(unsigned int i=0; i<cache->getMeshCount(); i++)
1818 {
1819 const io::SNamedPath &name=cache->getMeshName(i);
1820 m_old_mesh_buffers.push_back(name.getInternalName().c_str());
1821 }
1822
1823 m_old_textures.clear();
1824 video::IVideoDriver *vd = irr_driver->getVideoDriver();
1825 for(unsigned int i=0; i<vd->getTextureCount(); i++)
1826 {
1827 video::ITexture *t=vd->getTextureByIndex(i);
1828 m_old_textures.push_back(t);
1829 }
1830 #endif
1831 }
1832
1833 CameraEnd::clearEndCameras();
1834 m_minimap_invert_x_z = false;
1835 m_sky_type = SKY_NONE;
1836 m_track_object_manager = new TrackObjectManager();
1837
1838 std::string unique_id = StringUtils::insertValues("tracks/%s", m_ident.c_str());
1839
1840 // Add the track directory to the texture search path
1841 file_manager->pushTextureSearchPath(m_root, unique_id);
1842 file_manager->pushModelSearchPath(m_root);
1843 main_loop->renderGUI(3100);
1844
1845 #ifndef SERVER_ONLY
1846 if (CVS->isGLSL())
1847 {
1848 SP::SPShaderManager::get()->loadSPShaders(m_root);
1849 }
1850 #endif
1851 main_loop->renderGUI(3200);
1852
1853 // First read the temporary materials.xml file if it exists
1854 try
1855 {
1856 std::string materials_file = m_root+"materials.xml";
1857 if(m_cache_track)
1858 {
1859 if(!m_materials_loaded)
1860 material_manager->addSharedMaterial(materials_file);
1861 m_materials_loaded = true;
1862 }
1863 else
1864 material_manager->pushTempMaterial(materials_file);
1865 }
1866 catch (std::exception& e)
1867 {
1868 // no temporary materials.xml file, ignore
1869 (void)e;
1870 }
1871 main_loop->renderGUI(3300);
1872
1873 // Start building the scene graph
1874 // Soccer field with navmesh requires it
1875 // for two goal line to be drawn them in minimap
1876 std::string path = m_root + m_all_modes[mode_id].m_scene;
1877 XMLNode *root = file_manager->createXMLTree(path);
1878
1879 // Make sure that we have a track (which is used for raycasts to
1880 // place other objects).
1881 if (!root || root->getName()!="scene")
1882 {
1883 std::ostringstream msg;
1884 msg<< "No track model defined in '"<<path
1885 <<"', aborting.";
1886 throw std::runtime_error(msg.str());
1887 }
1888
1889 m_current_track[PT_MAIN] = this;
1890 m_current_track[PT_CHILD] = NULL;
1891
1892 // Load the graph only now: this function is called from world, after
1893 // the race gui was created. The race gui is needed since it stores
1894 // the information about the size of the texture to render the mini
1895 // map to.
1896 // Load the un-raycasted flag position first (for minimap)
1897 if (m_is_ctf && RaceManager::get()->isCTFMode())
1898 {
1899 for (unsigned int i=0; i<root->getNumNodes(); i++)
1900 {
1901 const XMLNode *node = root->getNode(i);
1902 const std::string &name = node->getName();
1903 if (name == "red-flag")
1904 {
1905 m_red_flag.setOrigin(flagCommand(node));
1906 }
1907 else if (name == "blue-flag")
1908 {
1909 m_blue_flag.setOrigin(flagCommand(node));
1910 }
1911 } // for i<root->getNumNodes()
1912 }
1913 main_loop->renderGUI(3320);
1914
1915 if (!m_is_arena && !m_is_soccer && !m_is_cutscene)
1916 loadDriveGraph(mode_id, reverse_track);
1917 else if ((m_is_arena || m_is_soccer) && !m_is_cutscene && m_has_navmesh)
1918 loadArenaGraph(*root);
1919 main_loop->renderGUI(3340);
1920
1921 if (NetworkConfig::get()->isNetworking())
1922 {
1923 auto nim = std::make_shared<NetworkItemManager>();
1924 nim->rewinderAdd();
1925 m_item_manager = nim;
1926 }
1927 else
1928 {
1929 // Seed random engine locally
1930 uint32_t seed = (uint32_t)StkTime::getTimeSinceEpoch();
1931 ItemManager::updateRandomSeed(seed);
1932 m_item_manager = std::make_shared<ItemManager>();
1933 powerup_manager->setRandomSeed(seed);
1934 }
1935 main_loop->renderGUI(3360);
1936
1937 // Set the default start positions. Node that later the default
1938 // positions can still be overwritten.
1939 float forwards_distance = 1.5f;
1940 float sidewards_distance = 3.0f;
1941 float upwards_distance = 0.1f;
1942 int karts_per_row = 2;
1943
1944 const XMLNode *default_start = root->getNode("default-start");
1945 if (default_start)
1946 {
1947 default_start->get("forwards-distance", &forwards_distance );
1948 default_start->get("sidewards-distance", &sidewards_distance);
1949 default_start->get("upwards-distance", &upwards_distance );
1950 default_start->get("karts-per-row", &karts_per_row );
1951 }
1952
1953 if (!m_is_arena && !m_is_soccer && !m_is_cutscene)
1954 {
1955 if (RaceManager::get()->isFollowMode())
1956 {
1957 // In a FTL race the non-leader karts are placed at the end of the
1958 // field, so we need all start positions.
1959 m_start_transforms.resize(stk_config->m_max_karts);
1960 }
1961 else
1962 m_start_transforms.resize(RaceManager::get()->getNumberOfKarts());
1963 DriveGraph::get()->setDefaultStartPositions(&m_start_transforms,
1964 karts_per_row,
1965 forwards_distance,
1966 sidewards_distance,
1967 upwards_distance);
1968 }
1969 main_loop->renderGUI(3400);
1970
1971 // we need to check for fog before loading the main track model
1972 if (const XMLNode *node = root->getNode("sun"))
1973 {
1974 node->get("xyz", &m_sun_position );
1975 node->get("ambient", &m_default_ambient_color);
1976 node->get("sun-specular", &m_sun_specular_color);
1977 node->get("sun-diffuse", &m_sun_diffuse_color);
1978 node->get("fog", &m_use_fog);
1979 node->get("fog-color", &m_fog_color);
1980 node->get("fog-max", &m_fog_max);
1981 node->get("fog-start", &m_fog_start);
1982 node->get("fog-end", &m_fog_end);
1983 node->get("fog-start-height", &m_fog_height_start);
1984 node->get("fog-end-height", &m_fog_height_end);
1985 }
1986 main_loop->renderGUI(3500);
1987
1988 #ifndef SERVER_ONLY
1989 if (!GUIEngine::isNoGraphics() && CVS->isGLSL() && m_use_fog)
1990 {
1991 glBindBuffer(GL_UNIFORM_BUFFER, SP::sp_fog_ubo);
1992 glBufferSubData(GL_UNIFORM_BUFFER, 0, 4, &m_fog_start);
1993 glBufferSubData(GL_UNIFORM_BUFFER, 4, 4, &m_fog_end);
1994 glBufferSubData(GL_UNIFORM_BUFFER, 8, 4, &m_fog_max);
1995 // Fog density
1996 float val = -(1.0f / (40.0f * (m_fog_start + 0.001f)));
1997 glBufferSubData(GL_UNIFORM_BUFFER, 12, 4, &val);
1998 val = (float)m_fog_color.getRed() / 255.0f;
1999 glBufferSubData(GL_UNIFORM_BUFFER, 16, 4, &val);
2000 val = (float)m_fog_color.getGreen() / 255.0f;
2001 glBufferSubData(GL_UNIFORM_BUFFER, 20, 4, &val);
2002 val = (float)m_fog_color.getBlue() / 255.0f;
2003 glBufferSubData(GL_UNIFORM_BUFFER, 24, 4, &val);
2004 val = 0.0f;
2005 glBufferSubData(GL_UNIFORM_BUFFER, 28, 4, &val);
2006 glBindBuffer(GL_UNIFORM_BUFFER, 0);
2007 }
2008 else if (CVS->isGLSL())
2009 {
2010 SP::resetEmptyFogColor();
2011 }
2012 #endif
2013 main_loop->renderGUI(3600);
2014
2015 if (const XMLNode *node = root->getNode("lightshaft"))
2016 {
2017 m_godrays = true;
2018 node->get("opacity", &m_godrays_opacity);
2019 node->get("color", &m_godrays_color);
2020 node->get("xyz", &m_godrays_position);
2021 }
2022
2023 loadMainTrack(*root);
2024 main_loop->renderGUI(4700);
2025
2026 unsigned int main_track_count = (unsigned int)m_all_nodes.size();
2027
2028 ModelDefinitionLoader model_def_loader(this);
2029 main_loop->renderGUI(4800);
2030
2031 // Load LOD groups
2032 const XMLNode *lod_xml_node = root->getNode("lod");
2033 if (lod_xml_node != NULL)
2034 {
2035 for (unsigned int i = 0; i < lod_xml_node->getNumNodes(); i++)
2036 {
2037 main_loop->renderGUI(4900, i, lod_xml_node->getNumNodes());
2038
2039 const XMLNode* lod_group_xml = lod_xml_node->getNode(i);
2040 for (unsigned int j = 0; j < lod_group_xml->getNumNodes(); j++)
2041 {
2042 model_def_loader.addModelDefinition(lod_group_xml->getNode(j));
2043 }
2044 }
2045 }
2046
2047 loadObjects(root, path, model_def_loader, true, NULL, NULL);
2048 main_loop->renderGUI(5000);
2049
2050 Log::info("Track", "Overall scene complexity estimated at %d", irr_driver->getSceneComplexity());
2051 // Correct the parenting of meta library
2052 for (auto& p : m_meta_library)
2053 {
2054 auto* ln = p.first->getPresentation<TrackObjectPresentationLibraryNode>();
2055 assert(ln);
2056 TrackObjectPresentationLibraryNode* meta_ln = p.second
2057 ->getPresentation<TrackObjectPresentationLibraryNode>();
2058 assert(meta_ln);
2059 meta_ln->getNode()->setParent(ln->getNode());
2060 recursiveUpdatePosition(meta_ln->getNode());
2061 recursiveUpdatePhysics(p.second->getChildren());
2062 main_loop->renderGUI(5050);
2063
2064 }
2065
2066 model_def_loader.cleanLibraryNodesAfterLoad();
2067 main_loop->renderGUI(5100);
2068
2069 Scripting::ScriptEngine::getInstance()->compileLoadedScripts();
2070 main_loop->renderGUI(5200);
2071
2072 // Init all track objects
2073 m_track_object_manager->init();
2074 main_loop->renderGUI(5300);
2075
2076
2077 // ---- Fog
2078 // It's important to execute this BEFORE the code that creates the skycube,
2079 // otherwise the skycube node could be modified to have fog enabled, which
2080 // we don't want
2081 #ifndef SERVER_ONLY
2082 if (m_use_fog && Camera::getDefaultCameraType()!=Camera::CM_TYPE_DEBUG &&
2083 !CVS->isGLSL())
2084 {
2085 /* NOTE: if LINEAR type, density does not matter, if EXP or EXP2, start
2086 and end do not matter */
2087 irr_driver->getVideoDriver()->setFog(m_fog_color,
2088 video::EFT_FOG_LINEAR,
2089 m_fog_start, m_fog_end,
2090 1.0f);
2091 }
2092 #endif
2093
2094 // Sky dome and boxes support
2095 // --------------------------
2096 irr_driver->suppressSkyBox();
2097 #ifndef SERVER_ONLY
2098 if(!CVS->isGLSL() && m_sky_type==SKY_DOME && m_sky_textures.size() > 0)
2099 {
2100 scene::ISceneNode *node = irr_driver->addSkyDome(m_sky_textures[0],
2101 m_sky_hori_segments,
2102 m_sky_vert_segments,
2103 m_sky_texture_percent,
2104 m_sky_sphere_percent);
2105 for(unsigned int i=0; i<node->getMaterialCount(); i++)
2106 {
2107 main_loop->renderGUI(5350, i, node->getMaterialCount());
2108
2109 video::SMaterial &irrMaterial=node->getMaterial(i);
2110 for(unsigned int j=0; j<video::MATERIAL_MAX_TEXTURES; j++)
2111 {
2112 video::ITexture* t=irrMaterial.getTexture(j);
2113 if(!t) continue;
2114 core::matrix4 *m = &irrMaterial.getTextureMatrix(j);
2115 m_animated_textures.push_back(new MovingTexture(m, m_sky_dx, m_sky_dy));
2116 } // for j<MATERIAL_MAX_TEXTURES
2117 } // for i<getMaterialCount
2118
2119 m_all_nodes.push_back(node);
2120 }
2121 else if(m_sky_type==SKY_BOX && m_sky_textures.size() == 6)
2122 {
2123 //if (m_spherical_harmonics_textures.size() > 0)
2124 m_all_nodes.push_back(irr_driver->addSkyBox(m_sky_textures, m_spherical_harmonics_textures));
2125 //else
2126 // m_all_nodes.push_back(irr_driver->addSkyBox(m_sky_textures, m_sky_textures));
2127 }
2128 else if(m_sky_type==SKY_COLOR)
2129 {
2130 irr_driver->setClearbackBufferColor(m_sky_color);
2131 }
2132 #endif
2133 main_loop->renderGUI(5400);
2134
2135 // ---- Set ambient color
2136 m_ambient_color = m_default_ambient_color;
2137 irr_driver->setAmbientLight(m_ambient_color,
2138 m_spherical_harmonics_textures.size() != 6/*force_SH_computation*/);
2139
2140 // ---- Create sun (non-ambient directional light)
2141 if (m_sun_position.getLengthSQ() < 0.03f)
2142 {
2143 m_sun_position = core::vector3df(500, 250, 250);
2144 }
2145
2146 const video::SColorf tmpf(m_sun_diffuse_color);
2147 m_sun = irr_driver->addLight(m_sun_position, 0., 0., tmpf.r, tmpf.g, tmpf.b, true);
2148
2149 #ifndef SERVER_ONLY
2150 if (!CVS->isGLSL())
2151 {
2152 scene::ILightSceneNode *sun = (scene::ILightSceneNode *) m_sun;
2153
2154 sun->setLightType(video::ELT_DIRECTIONAL);
2155
2156 // The angle of the light is rather important - let the sun
2157 // point towards (0,0,0).
2158 if (m_sun_position.getLengthSQ() < 0.03f)
2159 // Backward compatibility: if no sun is specified, use the
2160 // old hardcoded default angle
2161 m_sun->setRotation(core::vector3df(180, 45, 45));
2162 else
2163 m_sun->setRotation((-m_sun_position).getHorizontalAngle());
2164
2165 sun->getLightData().SpecularColor = m_sun_specular_color;
2166 }
2167 else
2168 {
2169 irr_driver->createSunInterposer();
2170 m_sun->grab();
2171 }
2172 #endif
2173 main_loop->renderGUI(5500);
2174
2175 // Join all static physics only object to main track if possible
2176 // Take the visibility condition by scripting into account
2177 std::vector<TrackObject*> objs_removing;
2178 for (auto* to : m_track_object_manager->getObjects().m_contents_vector)
2179 {
2180 if (to->joinToMainTrack())
2181 {
2182 m_track_object_manager->removeDriveableObject(to);
2183 TrackObjectPresentationSceneNode* ts =
2184 to->getPresentation<TrackObjectPresentationSceneNode>();
2185 // physicial only node is always hidden, remove it from stk after
2186 // joining to track mesh
2187 if (ts && ts->isAlwaysHidden())
2188 objs_removing.push_back(to);
2189 }
2190 }
2191 for (auto* obj : objs_removing)
2192 m_track_object_manager->removeObject(obj);
2193
2194 createPhysicsModel(main_track_count);
2195 main_loop->renderGUI(5600);
2196
2197 freeCachedMeshVertexBuffer();
2198
2199 const bool arena_random_item_created =
2200 m_item_manager->randomItemsForArena(m_start_transforms);
2201
2202 if (!arena_random_item_created)
2203 {
2204 for (unsigned int i=0; i<root->getNumNodes(); i++)
2205 {
2206 const XMLNode *node = root->getNode(i);
2207 const std::string &name = node->getName();
2208 if (name=="banana" || name=="item" ||
2209 name=="small-nitro" || name=="big-nitro" ||
2210 name=="easter-egg" )
2211 {
2212 itemCommand(node);
2213 }
2214 } // for i<root->getNumNodes()
2215 }
2216 main_loop->renderGUI(5700);
2217
2218 if (m_is_ctf && RaceManager::get()->isCTFMode())
2219 {
2220 for (unsigned int i=0; i<root->getNumNodes(); i++)
2221 {
2222 const XMLNode *node = root->getNode(i);
2223 const std::string &name = node->getName();
2224 if (name == "red-flag" || name == "blue-flag")
2225 {
2226 flagCommand(node);
2227 }
2228 } // for i<root->getNumNodes()
2229 }
2230 delete root;
2231 main_loop->renderGUI(5800);
2232
2233 if (auto sl = LobbyProtocol::get<ServerLobby>())
2234 {
2235 sl->saveInitialItems(
2236 std::dynamic_pointer_cast<NetworkItemManager>(m_item_manager));
2237 }
2238
2239 main_loop->renderGUI(5900);
2240
2241 if (UserConfigParams::m_track_debug && Graph::get() && !m_is_cutscene)
2242 Graph::get()->createDebugMesh();
2243
2244 // Only print warning if not in battle mode, since battle tracks don't have
2245 // any quads or check lines.
2246 if (m_check_manager->getCheckStructureCount()==0 &&
2247 !RaceManager::get()->isBattleMode() && !m_is_cutscene)
2248 {
2249 Log::warn("track", "No check lines found in track '%s'.",
2250 m_ident.c_str());
2251 Log::warn("track", "Lap counting will not work, and start "
2252 "positions might be incorrect.");
2253 }
2254
2255 if (UserConfigParams::logMemory())
2256 {
2257 Log::debug("track", "[memory] After loading '%s': mesh cache %d "
2258 "texture cache %d\n", getIdent().c_str(),
2259 irr_driver->getSceneManager()->getMeshCache()->getMeshCount(),
2260 irr_driver->getVideoDriver()->getTextureCount());
2261 }
2262
2263 World *world = World::getWorld();
2264 if (world->useChecklineRequirements())
2265 {
2266 DriveGraph::get()->computeChecklineRequirements();
2267 }
2268 main_loop->renderGUI(6000);
2269
2270 EasterEggHunt *easter_world = dynamic_cast<EasterEggHunt*>(world);
2271 if(easter_world)
2272 {
2273 std::string dir = StringUtils::getPath(m_filename);
2274 easter_world->readData(dir+"/easter_eggs.xml");
2275 }
2276 main_loop->renderGUI(6100);
2277
2278 STKTexManager::getInstance()->unsetTextureErrorMessage();
2279 #ifndef SERVER_ONLY
2280 if (CVS->isGLSL())
2281 {
2282 for (video::ITexture* t : m_sky_textures)
2283 {
2284 t->drop();
2285 }
2286 m_sky_textures.clear();
2287 for (video::ITexture* t : m_spherical_harmonics_textures)
2288 {
2289 t->drop();
2290 }
2291 m_spherical_harmonics_textures.clear();
2292 }
2293 #endif // !SERVER_ONLY
2294 } // loadTrackModel
2295
2296 //-----------------------------------------------------------------------------
2297
loadObjects(const XMLNode * root,const std::string & path,ModelDefinitionLoader & model_def_loader,bool create_lod_definitions,scene::ISceneNode * parent,TrackObject * parent_library)2298 void Track::loadObjects(const XMLNode* root, const std::string& path,
2299 ModelDefinitionLoader& model_def_loader,
2300 bool create_lod_definitions, scene::ISceneNode* parent,
2301 TrackObject* parent_library)
2302 {
2303 unsigned int start_position_counter = 0;
2304
2305 unsigned int node_count = root->getNumNodes();
2306 const bool is_mode_ctf = m_is_ctf && RaceManager::get()->isCTFMode();
2307
2308 // We keep track of the complexity of the scene (amount of objects loaded, etc)
2309 irr_driver->addSceneComplexity(node_count);
2310 for (unsigned int i = 0; i < node_count; i++)
2311 {
2312 main_loop->renderGUI(4950, i, node_count);
2313 const XMLNode *node = root->getNode(i);
2314 const std::string name = node->getName();
2315 // The track object was already converted before the loop, and the
2316 // default start was already used, too - so ignore those.
2317 if (name == "track" || name == "default-start") continue;
2318 if (name == "object" || name == "library")
2319 {
2320 int geo_level = 0;
2321 node->get("geometry-level", &geo_level);
2322 if (UserConfigParams::m_geometry_level + geo_level - 2 > 0 &&
2323 !NetworkConfig::get()->isNetworking())
2324 continue;
2325 m_track_object_manager->add(*node, parent, model_def_loader, parent_library);
2326 }
2327 else if (name == "water")
2328 {
2329 createWater(*node);
2330 }
2331 else if (name == "banana" || name == "item" ||
2332 name == "small-nitro" || name == "big-nitro" ||
2333 name == "easter-egg" || name == "red-flag" ||
2334 name == "blue-flag")
2335 {
2336 // will be handled later
2337 }
2338 else if (name == "start" || name == "ctf-start")
2339 {
2340 if ((name == "start" && is_mode_ctf) ||
2341 (name == "ctf-start" && !is_mode_ctf))
2342 continue;
2343 unsigned int position = start_position_counter;
2344 start_position_counter++;
2345 node->get("position", &position);
2346 Vec3 xyz(0,0,0);
2347 node->getXYZ(&xyz);
2348 float h=0;
2349 node->get("h", &h);
2350
2351 if (position >= m_start_transforms.size())
2352 {
2353 m_start_transforms.resize(position + 1);
2354 }
2355
2356 m_start_transforms[position].setOrigin(xyz);
2357 m_start_transforms[position]
2358 .setRotation(getArenaStartRotation(xyz, h));
2359 }
2360 else if (name == "camera")
2361 {
2362 node->get("far", &m_camera_far);
2363 }
2364 else if (name == "checks")
2365 {
2366 m_check_manager->load(*node);
2367 }
2368 else if (name == "particle-emitter")
2369 {
2370 if (UserConfigParams::m_particles_effects > 1)
2371 {
2372 m_track_object_manager->add(*node, parent, model_def_loader, parent_library);
2373 }
2374 }
2375 else if (name == "sky-dome" || name == "sky-box" || name == "sky-color")
2376 {
2377 handleSky(*node, path);
2378 }
2379 else if (name == "end-cameras")
2380 {
2381 CameraEnd::readEndCamera(*node);
2382 }
2383 else if (name == "light")
2384 {
2385 m_track_object_manager->add(*node, parent, model_def_loader, parent_library);
2386 }
2387 else if (name == "weather")
2388 {
2389 std::string weather_particles;
2390
2391 node->get("particles", &weather_particles);
2392 node->get("lightning", &m_weather_lightning);
2393 node->get("sound", &m_weather_sound);
2394
2395 if (weather_particles.size() > 0)
2396 {
2397 m_sky_particles =
2398 ParticleKindManager::get()->getParticles(weather_particles);
2399 }
2400 }
2401 else if (name == "sun")
2402 {
2403 // handled above
2404 }
2405 else if (name == "lod")
2406 {
2407 // handled above
2408 }
2409 else if (name == "lightshaft")
2410 {
2411 // handled above
2412 }
2413 else if (name == "instancing")
2414 {
2415 // TODO: eventually remove, this is now automatic
2416 }
2417 else if (name == "subtitles")
2418 {
2419 std::vector<XMLNode*> subtitles;
2420 node->getNodes("subtitle", subtitles);
2421 for (unsigned int i = 0; i < subtitles.size(); i++)
2422 {
2423 int from = -1, to = -1;
2424 std::string subtitle_text;
2425 subtitles[i]->get("from", &from);
2426 subtitles[i]->get("to", &to);
2427 subtitles[i]->get("text", &subtitle_text);
2428 if (from != -1 && to != -1 && subtitle_text.size() > 0)
2429 {
2430 m_subtitles.push_back( Subtitle(from, to, _(subtitle_text.c_str())) );
2431 }
2432 }
2433 }
2434 else
2435 {
2436 Log::warn("track", "While loading track '%s', element '%s' was "
2437 "met but is unknown.",
2438 m_ident.c_str(), node->getName().c_str());
2439 }
2440
2441 } // for i<root->getNumNodes()
2442 }
2443
2444 //-----------------------------------------------------------------------------
2445 /** Handles a sky-dome or sky-box. It takes the xml node with the
2446 * corresponding data for the sky and stores the corresponding data in
2447 * the corresponding data structures.
2448 * \param xml_node XML node with the sky data.
2449 * \param filename Name of the file which is read, only used to print
2450 * meaningful error messages.
2451 */
handleSky(const XMLNode & xml_node,const std::string & filename)2452 void Track::handleSky(const XMLNode &xml_node, const std::string &filename)
2453 {
2454 if(xml_node.getName()=="sky-dome")
2455 {
2456 m_sky_type = SKY_DOME;
2457 m_sky_vert_segments = 16;
2458 m_sky_hori_segments = 16;
2459 m_sky_sphere_percent = 1.0f;
2460 m_sky_texture_percent = 1.0f;
2461 std::string s;
2462 xml_node.get("texture", &s );
2463 video::ITexture *t = irr_driver->getTexture(s);
2464 if (t != NULL)
2465 {
2466 t->grab();
2467 m_sky_textures.push_back(t);
2468 xml_node.get("vertical", &m_sky_vert_segments );
2469 xml_node.get("horizontal", &m_sky_hori_segments );
2470 xml_node.get("sphere-percent", &m_sky_sphere_percent );
2471 xml_node.get("texture-percent", &m_sky_texture_percent);
2472 xml_node.get("speed-x", &m_sky_dx );
2473 xml_node.get("speed-y", &m_sky_dy);
2474 }
2475 else
2476 {
2477 Log::error("track", "Sky-dome texture '%s' not found - ignored.",
2478 s.c_str());
2479 }
2480 } // if sky-dome
2481 else if(xml_node.getName()=="sky-box")
2482 {
2483 std::string s;
2484 xml_node.get("texture", &s);
2485 std::vector<std::string> v = StringUtils::split(s, ' ');
2486 for(unsigned int i=0; i<v.size(); i++)
2487 {
2488 video::ITexture* t = NULL;
2489 #ifndef SERVER_ONLY
2490 if (CVS->isGLSL())
2491 {
2492 t = STKTexManager::getInstance()->getTexture(v[i],
2493 (TexConfig*)NULL/*tex_config*/, true/*no_upload*/);
2494 }
2495 else
2496 #endif // !SERVER_ONLY
2497 {
2498 t = irr_driver->getTexture(v[i]);
2499 }
2500 if (t)
2501 {
2502 #ifndef SERVER_ONLY
2503 if (!CVS->isGLSL())
2504 #endif // !SERVER_ONLY
2505 t->grab();
2506 m_sky_textures.push_back(t);
2507 }
2508 else
2509 {
2510 Log::error("track","Sky-box texture '%s' not found - ignored.",
2511 v[i].c_str());
2512 }
2513 } // for i<v.size()
2514 if(m_sky_textures.size()!=6)
2515 {
2516 Log::error("track",
2517 "A skybox needs 6 textures, but %d are specified",
2518 (int)m_sky_textures.size());
2519 Log::error("track", "in '%s'.", filename.c_str());
2520
2521 }
2522 else
2523 {
2524 m_sky_type = SKY_BOX;
2525 }
2526
2527 std::string sh_textures;
2528 xml_node.get("sh-texture", &sh_textures);
2529 v = StringUtils::split(sh_textures, ' ');
2530 for (unsigned int i = 0; i<v.size(); i++)
2531 {
2532 video::ITexture* t = NULL;
2533 #ifndef SERVER_ONLY
2534 if (CVS->isGLSL())
2535 {
2536 t = STKTexManager::getInstance()->getTexture(v[i],
2537 (TexConfig*)NULL/*tex_config*/, true/*no_upload*/);
2538 }
2539 else
2540 #endif // !SERVER_ONLY
2541 {
2542 t = irr_driver->getTexture(v[i]);
2543 }
2544 if (t)
2545 {
2546 #ifndef SERVER_ONLY
2547 if (!CVS->isGLSL())
2548 #endif // !SERVER_ONLY
2549 t->grab();
2550 m_spherical_harmonics_textures.push_back(t);
2551 }
2552 else
2553 {
2554 Log::error("track", "Sky-box spherical harmonics texture '%s' not found - ignored.",
2555 v[i].c_str());
2556 }
2557 } // for i<v.size()
2558 }
2559 else if (xml_node.getName() == "sky-color")
2560 {
2561 m_sky_type = SKY_COLOR;
2562 xml_node.get("rgb", &m_sky_color);
2563 } // if sky-box
2564 } // handleSky
2565
2566 //-----------------------------------------------------------------------------
flagCommand(const XMLNode * node)2567 Vec3 Track::flagCommand(const XMLNode *node)
2568 {
2569 Vec3 xyz;
2570 // Set some kind of default in case Y is not defined in the file
2571 // (with the new track exporter it always is defined anyway).
2572 // Y is the height from which the item is dropped on the track.
2573 xyz.setY(1000);
2574 node->getXYZ(&xyz);
2575
2576 if (!m_track_mesh)
2577 return xyz;
2578
2579 Vec3 loc(xyz);
2580
2581 // Test if the item lies on a 3d node, if so adjust the normal
2582 // Also do a raycast if drop item is given
2583 Vec3 normal(0, 1, 0);
2584 Vec3 quad_normal = normal;
2585 Vec3 hit_point = loc;
2586 if (Graph::get())
2587 {
2588 int road_sector = Graph::UNKNOWN_SECTOR;
2589 Graph::get()->findRoadSector(xyz, &road_sector);
2590 // Only do custom direction of raycast if item is on quad graph
2591 if (road_sector != Graph::UNKNOWN_SECTOR)
2592 {
2593 quad_normal = Graph::get()->getQuad(road_sector)->getNormal();
2594 }
2595 }
2596
2597 const Material *m;
2598 // If raycast is used, increase the start position slightly
2599 // in case that the point is too close to the actual surface
2600 // (e.g. floating point errors can cause a problem here).
2601 loc += quad_normal * 0.1f;
2602
2603 #ifndef DEBUG
2604 m_track_mesh->castRay(loc, loc + (-10000 * quad_normal), &hit_point,
2605 &m, &normal);
2606 #else
2607 bool drop_success = m_track_mesh->castRay(loc, loc +
2608 (-10000 * quad_normal), &hit_point, &m, &normal);
2609 if (!drop_success)
2610 {
2611 Log::warn("track", "flag at position (%f,%f,%f) can not be dropped",
2612 loc.getX(), loc.getY(), loc.getZ());
2613 Log::warn("track", "onto terrain - position unchanged.");
2614 }
2615 #endif
2616
2617 m_track_object_manager->castRay
2618 (loc, loc + (-10000 * quad_normal), &hit_point, &m, &normal,
2619 /*interpolate*/false);
2620
2621 const std::string &name = node->getName();
2622 if (name == "red-flag")
2623 {
2624 m_red_flag = btTransform(shortestArcQuat(Vec3(0, 1, 0), normal),
2625 hit_point);
2626 }
2627 else
2628 {
2629 m_blue_flag = btTransform(shortestArcQuat(Vec3(0, 1, 0), normal),
2630 hit_point);
2631 }
2632 return hit_point;
2633 } // flagCommand
2634
2635 //-----------------------------------------------------------------------------
2636 /** Handle creation and placement of an item.
2637 * \param xyz The position of the item.
2638 * \param type The item type.
2639 * \param drop True if the item Z position should be determined based on
2640 * the track topology.
2641 */
itemCommand(const XMLNode * node)2642 void Track::itemCommand(const XMLNode *node)
2643 {
2644 const std::string &name = node->getName();
2645
2646 const bool is_mode_ctf = m_is_ctf && RaceManager::get()->isCTFMode();
2647 bool ctf = false;
2648 node->get("ctf", &ctf);
2649 if ((is_mode_ctf && !ctf) || (!is_mode_ctf && ctf))
2650 return;
2651
2652 Item::ItemType type;
2653 if (name=="banana" ) type = Item::ITEM_BANANA;
2654 else if(name=="item" ) type = Item::ITEM_BONUS_BOX;
2655 else if(name=="small-nitro") type = Item::ITEM_NITRO_SMALL;
2656 else if(name=="easter-egg" ) type = Item::ITEM_EASTER_EGG;
2657 else type = Item::ITEM_NITRO_BIG;
2658 Vec3 xyz;
2659 // Set some kind of default in case Y is not defined in the file
2660 // (with the new track exporter it always is defined anyway).
2661 // Y is the height from which the item is dropped on the track.
2662 xyz.setY(1000);
2663 node->getXYZ(&xyz);
2664 bool drop=true;
2665 node->get("drop", &drop);
2666
2667 // Some modes (e.g. time trial) don't have any bonus boxes
2668 if(type==Item::ITEM_BONUS_BOX &&
2669 !World::getWorld()->haveBonusBoxes())
2670 return;
2671
2672 // Only do easter eggs in easter egg mode.
2673 if(!(RaceManager::get()->isEggHuntMode()) && type==Item::ITEM_EASTER_EGG)
2674 {
2675 Log::warn("track",
2676 "Found easter egg in non-easter-egg mode - ignored.\n");
2677 return;
2678 }
2679
2680 Vec3 loc(xyz);
2681
2682 // Test if the item lies on a 3d node, if so adjust the normal
2683 // Also do a raycast if drop item is given
2684 Vec3 normal(0, 1, 0);
2685 Vec3 quad_normal = normal;
2686 Vec3 hit_point = loc;
2687 if (Graph::get())
2688 {
2689 int road_sector = Graph::UNKNOWN_SECTOR;
2690 Graph::get()->findRoadSector(xyz, &road_sector);
2691 // Only do custom direction of raycast if item is on quad graph
2692 if (road_sector != Graph::UNKNOWN_SECTOR)
2693 {
2694 quad_normal = Graph::get()->getQuad(road_sector)->getNormal();
2695 }
2696 }
2697
2698 if (drop)
2699 {
2700 const Material *m;
2701 // If raycast is used, increase the start position slightly
2702 // in case that the point is too close to the actual surface
2703 // (e.g. floating point errors can cause a problem here).
2704 loc += quad_normal * 0.1f;
2705
2706 #ifndef DEBUG
2707 m_track_mesh->castRay(loc, loc + (-10000 * quad_normal), &hit_point,
2708 &m, &normal);
2709 m_track_object_manager->castRay(loc,
2710 loc + (-10000 * quad_normal), &hit_point, &m, &normal,
2711 /*interpolate*/false);
2712 #else
2713 bool drop_success = m_track_mesh->castRay(loc, loc +
2714 (-10000 * quad_normal), &hit_point, &m, &normal);
2715 bool over_driveable = m_track_object_manager->castRay(loc,
2716 loc + (-10000 * quad_normal), &hit_point, &m, &normal,
2717 /*interpolate*/false);
2718 if (!drop_success && !over_driveable)
2719 {
2720 Log::warn("track",
2721 "Item at position (%f,%f,%f) can not be dropped",
2722 loc.getX(), loc.getY(), loc.getZ());
2723 Log::warn("track", "onto terrain - position unchanged.");
2724 }
2725 #endif
2726 }
2727
2728 m_item_manager->placeItem(type, drop ? hit_point : loc, normal);
2729 } // itemCommand
2730
2731 // ----------------------------------------------------------------------------
2732
buildHeightMap()2733 std::vector< std::vector<float> > Track::buildHeightMap()
2734 {
2735 std::vector< std::vector<float> > out(HEIGHT_MAP_RESOLUTION);
2736
2737 float x = m_aabb_min.getX();
2738 const float x_len = m_aabb_max.getX() - m_aabb_min.getX();
2739 const float z_len = m_aabb_max.getZ() - m_aabb_min.getZ();
2740
2741 const float x_step = x_len/HEIGHT_MAP_RESOLUTION;
2742 const float z_step = z_len/HEIGHT_MAP_RESOLUTION;
2743
2744 btVector3 hitpoint;
2745 const Material* material;
2746 btVector3 normal;
2747
2748 for (int i=0; i<HEIGHT_MAP_RESOLUTION; i++)
2749 {
2750 out[i].resize(HEIGHT_MAP_RESOLUTION);
2751 float z = m_aabb_min.getZ();
2752
2753 for (int j=0; j<HEIGHT_MAP_RESOLUTION; j++)
2754 {
2755 btVector3 pos(x, 100.0f, z);
2756 btVector3 to = pos;
2757 to.setY(-100000.f);
2758
2759 m_track_mesh->castRay(pos, to, &hitpoint, &material, &normal);
2760 z += z_step;
2761
2762 out[i][j] = hitpoint.getY();
2763 } // j<HEIGHT_MAP_RESOLUTION
2764 x += x_step;
2765 }
2766
2767 return out;
2768 } // buildHeightMap
2769
2770 // ----------------------------------------------------------------------------
drawMiniMap(const core::rect<s32> & dest_rect) const2771 void Track::drawMiniMap(const core::rect<s32>& dest_rect) const
2772 {
2773 if(m_render_target)
2774 m_render_target->draw2DImage(dest_rect, NULL,
2775 video::SColor(127, 255, 255, 255),
2776 true);
2777 }
2778
2779 // ----------------------------------------------------------------------------
2780 /** Returns the rotation of the sun. */
getSunRotation()2781 const core::vector3df& Track::getSunRotation()
2782 {
2783 return m_sun->getRotation();
2784 }
2785 //-----------------------------------------------------------------------------
2786 /** Determines if the kart is over ground.
2787 * Used in setting the starting positions of all the karts.
2788 * \param k The kart to project downward.
2789 * \return True of the kart is on terrain.
2790 */
2791
findGround(AbstractKart * kart)2792 bool Track::findGround(AbstractKart *kart)
2793 {
2794 const Vec3 &xyz = kart->getXYZ();
2795 Vec3 down = kart->getTrans().getBasis() * Vec3(0, -10000.0f, 0);
2796
2797 // Material and hit point are not needed;
2798 const Material *m;
2799 Vec3 hit_point, normal;
2800 bool over_ground = m_track_mesh->castRay(xyz, down, &hit_point,
2801 &m, &normal);
2802
2803 // Now also raycast against all track objects (that are driveable). If
2804 // there should be a closer result (than the one against the main track
2805 // mesh), its data will be returned.
2806 // From TerrainInfo::update
2807 bool over_driveable = m_track_object_manager->castRay(xyz, down,
2808 &hit_point, &m, &normal, /*interpolate*/false);
2809
2810 if (!over_ground && !over_driveable)
2811 {
2812 Log::warn("physics", "Kart at (%f %f %f) can not be dropped.",
2813 xyz.getX(),xyz.getY(),xyz.getZ());
2814 return false;
2815 }
2816
2817 // Check if the material the kart is about to be placed on would trigger
2818 // a reset. If so, this is not a valid position.
2819 if(m && m->isDriveReset())
2820 {
2821 Log::warn("physics","Kart at (%f %f %f) over reset terrain '%s'",
2822 xyz.getX(),xyz.getY(),xyz.getZ(),
2823 m->getTexFname().c_str());
2824 return false;
2825 }
2826
2827 // See if the kart is too high above the ground - it would drop
2828 // too long.
2829 if(xyz.getY() - hit_point.getY() > 5)
2830 {
2831 Log::warn("physics",
2832 "Kart at (%f %f %f) is too high above ground at (%f %f %f)",
2833 xyz.getX(),xyz.getY(),xyz.getZ(),
2834 hit_point.getX(),hit_point.getY(),hit_point.getZ());
2835 return false;
2836 }
2837
2838 btTransform t = kart->getBody()->getCenterOfMassTransform();
2839 // The computer offset is slightly too large, it should take
2840 // the default suspension rest instead of suspension rest (i.e. the
2841 // length of the suspension with the weight of the kart resting on
2842 // it). On the other hand this initial bouncing looks nice imho
2843 // - so I'll leave it in for now.
2844 float offset = kart->getKartProperties()->getSuspensionRest();
2845 t.setOrigin(hit_point + normal * offset);
2846 kart->getBody()->setCenterOfMassTransform(t);
2847 kart->setTrans(t);
2848
2849 return true;
2850 } // findGround
2851
2852 //-----------------------------------------------------------------------------
getTrackLength() const2853 float Track::getTrackLength() const
2854 {
2855 return DriveGraph::get()->getLapLength();
2856 } // getTrackLength
2857
2858 //-----------------------------------------------------------------------------
getAngle(int n) const2859 float Track::getAngle(int n) const
2860 {
2861 return DriveGraph::get()->getAngleToNext(n, 0);
2862 } // getAngle
2863
2864 //-----------------------------------------------------------------------------
uploadNodeVertexBuffer(scene::ISceneNode * node)2865 void Track::uploadNodeVertexBuffer(scene::ISceneNode *node)
2866 {
2867 #ifndef SERVER_ONLY
2868 if (!CVS->isGLSL())
2869 {
2870 return;
2871 }
2872 SP::SPMeshNode* spmn = dynamic_cast<SP::SPMeshNode*>(node);
2873 if (spmn)
2874 {
2875 SP::uploadSPM(spmn->getSPM());
2876 }
2877 #endif
2878 } // uploadNodeVertexBuffer
2879
2880 //-----------------------------------------------------------------------------
copyFromMainProcess()2881 void Track::copyFromMainProcess()
2882 {
2883 // Clear all unneeded objects copied in main process track
2884 m_physical_object_uid = 0;
2885 m_animated_textures.clear();
2886 m_animated_textures.shrink_to_fit();
2887 m_all_nodes.clear();
2888 m_all_nodes.shrink_to_fit();
2889 m_static_physics_only_nodes.clear();
2890 m_static_physics_only_nodes.shrink_to_fit();
2891 m_object_physics_only_nodes.clear();
2892 m_object_physics_only_nodes.shrink_to_fit();
2893 m_sun = NULL;
2894 m_all_cached_meshes.clear();
2895 m_all_cached_meshes.shrink_to_fit();
2896 m_detached_cached_meshes.clear();
2897 m_detached_cached_meshes.shrink_to_fit();
2898 m_sky_textures.clear();
2899 m_sky_textures.shrink_to_fit();
2900 m_spherical_harmonics_textures.clear();
2901 m_spherical_harmonics_textures.shrink_to_fit();
2902 m_meta_library.clear();
2903 m_meta_library.shrink_to_fit();
2904
2905 // Clone the needed object now in main process
2906 Track* main_track = m_current_track[PT_MAIN];
2907 CheckManager* main_cm = main_track->m_check_manager;
2908 m_check_manager = new CheckManager();
2909 for (unsigned i = 0; i < main_cm->getCheckStructureCount(); i++)
2910 {
2911 CheckStructure* cs = main_cm->getCheckStructure(i);
2912 m_check_manager->add(cs->clone());
2913 }
2914
2915 TrackObjectManager* main_tom = m_track_object_manager;
2916 m_track_object_manager = new TrackObjectManager();
2917 for (auto* to : main_tom->getObjects().m_contents_vector)
2918 {
2919 TrackObject* clone = to->cloneToChild();
2920 if (clone)
2921 m_track_object_manager->insertObject(clone);
2922 }
2923
2924 m_track_mesh = new TriangleMesh(/*can_be_transformed*/false);
2925 m_gfx_effect_mesh = new TriangleMesh(/*can_be_transformed*/false);
2926 m_track_mesh->copyFrom(*main_track->m_track_mesh);
2927 m_gfx_effect_mesh->copyFrom(*main_track->m_gfx_effect_mesh);
2928
2929 // At the moment we only use network for child track
2930 auto nim = std::make_shared<NetworkItemManager>();
2931 for (unsigned i = 0; i < m_item_manager->getNumberOfItems(); i++)
2932 {
2933 ItemState* it = m_item_manager->getItem(i);
2934 nim->insertItem(new Item(it->getType(), it->getXYZ(), it->getNormal(),
2935 NULL/*mesh*/, NULL/*lowres_mesh*/, NULL/*owner*/));
2936 }
2937 m_item_manager = nim;
2938 } // copyFromMainProcess
2939
2940 //-----------------------------------------------------------------------------
initChildTrack()2941 void Track::initChildTrack()
2942 {
2943 // This will be called in child process after main one copied to it
2944 assert(STKProcess::getType() == PT_CHILD);
2945 // Add in child process for rewind manager
2946 std::dynamic_pointer_cast<NetworkItemManager>
2947 (m_item_manager)->rewinderAdd();
2948 std::dynamic_pointer_cast<NetworkItemManager>
2949 (m_item_manager)->initServer();
2950
2951 // We call physics init in child process too
2952 Physics::get()->init(m_aabb_min, m_aabb_max);
2953 m_track_mesh->createPhysicalBody(m_friction);
2954 m_gfx_effect_mesh->createCollisionShape();
2955
2956 // All child track objects are only cloned if they have physical objects
2957 for (auto* to : m_track_object_manager->getObjects().m_contents_vector)
2958 to->getPhysicalObject()->addBody();
2959 m_track_object_manager->init();
2960
2961 if (auto sl = LobbyProtocol::get<ServerLobby>())
2962 {
2963 sl->saveInitialItems(
2964 std::dynamic_pointer_cast<NetworkItemManager>(m_item_manager));
2965 }
2966 } // initChildTrack
2967
2968 //-----------------------------------------------------------------------------
cleanChildTrack()2969 void Track::cleanChildTrack()
2970 {
2971 assert(STKProcess::getType() == PT_CHILD);
2972 Track* child_track = m_current_track[PT_CHILD];
2973 child_track->m_item_manager = nullptr;
2974 delete child_track->m_check_manager;
2975 delete child_track->m_track_object_manager;
2976 delete child_track->m_track_mesh;
2977 delete child_track->m_gfx_effect_mesh;
2978 delete child_track;
2979 m_current_track[PT_CHILD] = NULL;
2980 } // cleanChildTrack
2981