1 // SuperTuxKart - a fun racing game with go-kart
2 //
3 // Copyright (C) 2013-2015 Joerg Henrichs, Marianne Gagnon
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 3
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 #include "tracks/track_object_presentation.hpp"
20
21 #include "audio/sfx_base.hpp"
22 #include "audio/sfx_buffer.hpp"
23 #include "challenges/unlock_manager.hpp"
24 #include "config/user_config.hpp"
25 #include "graphics/camera.hpp"
26 #include "graphics/central_settings.hpp"
27 #include "graphics/irr_driver.hpp"
28 #include "graphics/light.hpp"
29 #include "graphics/material_manager.hpp"
30 #include "graphics/particle_emitter.hpp"
31 #include "graphics/particle_kind_manager.hpp"
32 #include "graphics/stk_particle.hpp"
33 #include "graphics/sp/sp_shader_manager.hpp"
34 #include "io/file_manager.hpp"
35 #include "io/xml_node.hpp"
36 #include "input/device_manager.hpp"
37 #include "input/input_device.hpp"
38 #include "input/input_manager.hpp"
39 #include "items/item_manager.hpp"
40 #include "karts/abstract_kart.hpp"
41 #include "modes/world.hpp"
42 #include "scriptengine/script_engine.hpp"
43 #include "states_screens/dialogs/tutorial_message_dialog.hpp"
44 #include "tracks/check_cylinder.hpp"
45 #include "tracks/check_manager.hpp"
46 #include "tracks/check_trigger.hpp"
47 #include "tracks/model_definition_loader.hpp"
48 #include "tracks/track.hpp"
49 #include "tracks/track_manager.hpp"
50 #include "tracks/track_object_manager.hpp"
51 #include "utils/stk_process.hpp"
52 #include "utils/string_utils.hpp"
53
54 #include <IBillboardSceneNode.h>
55 #include <ICameraSceneNode.h>
56 #include <ILightSceneNode.h>
57 #include <IMeshManipulator.h>
58 #include <IMeshSceneNode.h>
59 #include <IParticleSystemSceneNode.h>
60 #include <ISceneManager.h>
61
62 // ----------------------------------------------------------------------------
TrackObjectPresentation(const XMLNode & xml_node)63 TrackObjectPresentation::TrackObjectPresentation(const XMLNode& xml_node)
64 {
65 m_init_xyz = core::vector3df(0,0,0);
66 m_init_hpr = core::vector3df(0,0,0);
67 m_init_scale = core::vector3df(1,1,1);
68
69 if (!xml_node.get("xyz", &m_init_xyz ))
70 {
71 // support for old deprecated syntax
72 xml_node.getXYZ(&m_init_xyz);
73 }
74
75 xml_node.get("hpr", &m_init_hpr );
76 xml_node.get("scale", &m_init_scale);
77 } // TrackObjectPresentation
78
79 // ----------------------------------------------------------------------------
getPosition() const80 const core::vector3df& TrackObjectPresentationSceneNode::getPosition() const
81 {
82 if (m_node == NULL) return m_init_xyz;
83 return m_node->getPosition();
84 } // getPosition
85
86 // ----------------------------------------------------------------------------
getAbsolutePosition() const87 const core::vector3df TrackObjectPresentationSceneNode::getAbsolutePosition() const
88 {
89 if (m_node == NULL) return m_init_xyz;
90 m_node->updateAbsolutePosition();
91 return m_node->getAbsolutePosition();
92 } // getAbsolutePosition
93
94 // ----------------------------------------------------------------------------
95
getAbsoluteCenterPosition() const96 const core::vector3df TrackObjectPresentationSceneNode::getAbsoluteCenterPosition() const
97 {
98 if (m_node == NULL) return m_init_xyz;
99 m_node->updateAbsolutePosition();
100 core::aabbox3d<f32> bounds = m_node->getTransformedBoundingBox();
101 return bounds.getCenter();
102 }
103
104 // ----------------------------------------------------------------------------
getRotation() const105 const core::vector3df& TrackObjectPresentationSceneNode::getRotation() const
106 {
107 if (m_node == NULL) return m_init_hpr;
108 return m_node->getRotation();
109 } // getRotation
110
111 // ----------------------------------------------------------------------------
getScale() const112 const core::vector3df& TrackObjectPresentationSceneNode::getScale() const
113 {
114 if (m_node == NULL) return m_init_scale;
115 return m_node->getScale();
116 } // getScale
117
118 // ----------------------------------------------------------------------------
move(const core::vector3df & xyz,const core::vector3df & hpr,const core::vector3df & scale,bool isAbsoluteCoord)119 void TrackObjectPresentationSceneNode::move(const core::vector3df& xyz,
120 const core::vector3df& hpr,
121 const core::vector3df& scale,
122 bool isAbsoluteCoord)
123 {
124 if (m_node == NULL) return;
125
126 if (m_node->getParent() != NULL && isAbsoluteCoord)
127 {
128 scene::ISceneNode* parent = m_node->getParent();
129 m_node->setPosition((xyz - parent->getAbsolutePosition())
130 / parent->getScale());
131 }
132 else
133 {
134 m_node->setPosition(xyz);
135 }
136 m_node->setRotation(hpr);
137 m_node->setScale(scale);
138 m_node->updateAbsolutePosition();
139 } // move
140
141 // ----------------------------------------------------------------------------
setEnable(bool enabled)142 void TrackObjectPresentationSceneNode::setEnable(bool enabled)
143 {
144 if (m_node != NULL && (!enabled || !m_force_always_hidden))
145 m_node->setVisible(enabled);
146 } // setEnable
147
148 // ----------------------------------------------------------------------------
reset()149 void TrackObjectPresentationSceneNode::reset()
150 {
151 if (m_node == NULL) return;
152
153 m_node->setPosition(m_init_xyz);
154 m_node->setRotation(m_init_hpr);
155 m_node->setScale(m_init_scale);
156 } // reset
157
158 // ----------------------------------------------------------------------------
TrackObjectPresentationEmpty(const XMLNode & xml_node)159 TrackObjectPresentationEmpty::TrackObjectPresentationEmpty(const XMLNode& xml_node)
160 : TrackObjectPresentationSceneNode(xml_node)
161 {
162 m_node = irr_driver->getSceneManager()->addEmptySceneNode();
163 m_node->setPosition(m_init_xyz);
164 m_node->setRotation(m_init_hpr);
165 m_node->setScale(m_init_scale);
166 } // TrackObjectPresentationEmpty
167
168 // ----------------------------------------------------------------------------
~TrackObjectPresentationEmpty()169 TrackObjectPresentationEmpty::~TrackObjectPresentationEmpty()
170 {
171 irr_driver->removeNode(m_node);
172 } // ~TrackObjectPresentationEmpty
173
174 // ----------------------------------------------------------------------------
TrackObjectPresentationLibraryNode(TrackObject * parent,const XMLNode & xml_node,ModelDefinitionLoader & model_def_loader)175 TrackObjectPresentationLibraryNode::TrackObjectPresentationLibraryNode(
176 TrackObject* parent,
177 const XMLNode& xml_node,
178 ModelDefinitionLoader& model_def_loader)
179 : TrackObjectPresentationSceneNode(xml_node)
180 {
181 m_parent = NULL;
182 m_start_executed = false;
183 m_reset_executed = false;
184
185 std::string name;
186 xml_node.get("name", &name);
187 m_name = name;
188
189 m_node = irr_driver->getSceneManager()->addEmptySceneNode();
190 #ifdef DEBUG
191 m_node->setName(("libnode_" + name).c_str());
192 #endif
193
194 XMLNode* libroot;
195 std::string lib_path =
196 file_manager->getAsset(FileManager::LIBRARY, name) + "/";
197
198 bool create_lod_definitions = true;
199
200 if (!model_def_loader.containsLibraryNode(name))
201 {
202 Track* track = Track::getCurrentTrack();
203 std::string local_lib_node_path;
204 std::string local_script_file_path;
205 if (track != NULL)
206 {
207 local_lib_node_path = track->getTrackFile("library/" + name + "/node.xml");
208 local_script_file_path = track->getTrackFile("library/" + name + "/scripting.as");
209 }
210 std::string lib_node_path = lib_path + "node.xml";
211 std::string lib_script_file_path = lib_path + "scripting.as";
212
213 if (local_lib_node_path.size() > 0 && file_manager->fileExists(local_lib_node_path))
214 {
215 lib_path = track->getTrackFile("library/" + name);
216 libroot = file_manager->createXMLTree(local_lib_node_path);
217 if (track != NULL)
218 {
219 Scripting::ScriptEngine::getInstance()->loadScript(local_script_file_path, false);
220 }
221 }
222 else if (file_manager->fileExists(lib_node_path))
223 {
224 libroot = file_manager->createXMLTree(lib_node_path);
225 if (track != NULL)
226 {
227 Scripting::ScriptEngine::getInstance()->loadScript(lib_script_file_path, false);
228 }
229 }
230 else
231 {
232 Log::error("TrackObjectPresentationLibraryNode",
233 "Cannot find library '%s'", lib_node_path.c_str());
234 return;
235 }
236
237 if (libroot == NULL)
238 {
239 Log::error("TrackObjectPresentationLibraryNode",
240 "Cannot find library '%s'", lib_node_path.c_str());
241 return;
242 }
243
244 std::string unique_id = StringUtils::insertValues("library/%s", name.c_str());
245 file_manager->pushTextureSearchPath(lib_path + "/", unique_id);
246 file_manager->pushModelSearchPath(lib_path);
247 material_manager->pushTempMaterial(lib_path + "/materials.xml");
248 #ifndef SERVER_ONLY
249 if (CVS->isGLSL())
250 {
251 SP::SPShaderManager::get()->loadSPShaders(lib_path);
252 }
253 #endif
254 model_def_loader.addToLibrary(name, libroot);
255
256 // Load LOD groups
257 const XMLNode *lod_xml_node = libroot->getNode("lod");
258 if (lod_xml_node != NULL)
259 {
260 for (unsigned int i = 0; i < lod_xml_node->getNumNodes(); i++)
261 {
262 const XMLNode* lod_group_xml = lod_xml_node->getNode(i);
263 for (unsigned int j = 0; j < lod_group_xml->getNumNodes(); j++)
264 {
265 model_def_loader.addModelDefinition(lod_group_xml->getNode(j));
266 }
267 }
268 }
269 }
270 else
271 {
272 libroot = model_def_loader.getLibraryNodes()[name];
273 assert(libroot != NULL);
274 // LOD definitions are already created, don't create them again
275 create_lod_definitions = false;
276 }
277
278 m_node->setPosition(m_init_xyz);
279 m_node->setRotation(m_init_hpr);
280 m_node->setScale(m_init_scale);
281 m_node->updateAbsolutePosition();
282
283 assert(libroot != NULL);
284 Track::getCurrentTrack()->loadObjects(libroot, lib_path, model_def_loader,
285 create_lod_definitions, m_node,
286 parent);
287 m_parent = parent;
288 } // TrackObjectPresentationLibraryNode
289
290 // ----------------------------------------------------------------------------
291
update(float dt)292 void TrackObjectPresentationLibraryNode::update(float dt)
293 {
294 // Child process currently has no scripting engine
295 if (STKProcess::getType() == PT_CHILD)
296 return;
297
298 if (!m_start_executed)
299 {
300 m_start_executed = true;
301 std::string fn_name = StringUtils::insertValues("void %s::onStart(const string)", m_name.c_str());
302
303 if (m_parent != NULL)
304 {
305 std::string lib_id = m_parent->getID();
306 std::string* lib_id_ptr = &lib_id;
307
308 Scripting::ScriptEngine::getInstance()->runFunction(false, fn_name,
309 [&](asIScriptContext* ctx) {
310 ctx->SetArgObject(0, lib_id_ptr);
311 });
312 }
313 }
314 if (!m_reset_executed)
315 {
316 m_reset_executed = true;
317 std::string fn_name = StringUtils::insertValues("void %s::onReset(const string)", m_name.c_str());
318
319 if (m_parent != NULL)
320 {
321 std::string lib_id = m_parent->getID();
322 std::string* lib_id_ptr = &lib_id;
323
324 Scripting::ScriptEngine::getInstance()->runFunction(false, fn_name,
325 [&](asIScriptContext* ctx) {
326 ctx->SetArgObject(0, lib_id_ptr);
327 });
328 }
329 }
330 }
331
332 // ----------------------------------------------------------------------------
~TrackObjectPresentationLibraryNode()333 TrackObjectPresentationLibraryNode::~TrackObjectPresentationLibraryNode()
334 {
335 irr_driver->removeNode(m_node);
336 } // TrackObjectPresentationLibraryNode
337 // ----------------------------------------------------------------------------
move(const core::vector3df & xyz,const core::vector3df & hpr,const core::vector3df & scale,bool isAbsoluteCoord)338 void TrackObjectPresentationLibraryNode::move(const core::vector3df& xyz, const core::vector3df& hpr,
339 const core::vector3df& scale, bool isAbsoluteCoord)
340 {
341 TrackObjectPresentationSceneNode::move(xyz, hpr, scale, isAbsoluteCoord);
342
343 for (TrackObject* obj : m_parent->getChildren())
344 {
345 if (obj->getPhysicalObject() != NULL)
346 {
347 obj->movePhysicalBodyToGraphicalNode(obj->getAbsolutePosition(), obj->getRotation());
348 }
349 }
350 }
351 // ----------------------------------------------------------------------------
TrackObjectPresentationLOD(const XMLNode & xml_node,scene::ISceneNode * parent,ModelDefinitionLoader & model_def_loader,std::shared_ptr<RenderInfo> ri)352 TrackObjectPresentationLOD::TrackObjectPresentationLOD(const XMLNode& xml_node,
353 scene::ISceneNode* parent,
354 ModelDefinitionLoader& model_def_loader,
355 std::shared_ptr<RenderInfo> ri)
356 : TrackObjectPresentationSceneNode(xml_node)
357 {
358 m_node = model_def_loader.instanciateAsLOD(&xml_node, parent, ri);
359 if (m_node == NULL) throw std::runtime_error("Cannot load LOD node");
360 m_node->setPosition(m_init_xyz);
361 m_node->setRotation(m_init_hpr);
362 m_node->setScale(m_init_scale);
363 } // TrackObjectPresentationLOD
364
365 // ----------------------------------------------------------------------------
~TrackObjectPresentationLOD()366 TrackObjectPresentationLOD::~TrackObjectPresentationLOD()
367 {
368 if (m_node)
369 irr_driver->removeNode(m_node);
370 } // TrackObjectPresentationLOD
371
372 // ----------------------------------------------------------------------------
reset()373 void TrackObjectPresentationLOD::reset()
374 {
375 LODNode* ln = dynamic_cast<LODNode*>(m_node);
376 if (ln)
377 {
378 for (scene::ISceneNode* node : ln->getAllNodes())
379 {
380 scene::IAnimatedMeshSceneNode* a_node =
381 dynamic_cast<scene::IAnimatedMeshSceneNode*>(node);
382 if (a_node)
383 {
384 a_node->setLoopMode(true);
385 a_node->setAnimationEndCallback(NULL);
386 RandomGenerator rg;
387 int animation_set = 0;
388 if (a_node->getAnimationSetNum() > 0)
389 animation_set = rg.get(a_node->getAnimationSetNum());
390 a_node->useAnimationSet(animation_set);
391 }
392 }
393 }
394 } // reset
395
396 // ----------------------------------------------------------------------------
TrackObjectPresentationMesh(const XMLNode & xml_node,bool enabled,scene::ISceneNode * parent,std::shared_ptr<RenderInfo> render_info)397 TrackObjectPresentationMesh::TrackObjectPresentationMesh(
398 const XMLNode& xml_node,
399 bool enabled,
400 scene::ISceneNode* parent,
401 std::shared_ptr<RenderInfo> render_info)
402 : TrackObjectPresentationSceneNode(xml_node)
403 {
404 m_is_looped = false;
405 m_mesh = NULL;
406 m_node = NULL;
407
408 xml_node.get("looped", &m_is_looped );
409 std::string model_name;
410 xml_node.get("model", &model_name );
411
412 m_render_info = render_info;
413 m_model_file = model_name;
414 m_is_in_skybox = false;
415 std::string render_pass;
416 xml_node.get("renderpass", &render_pass);
417
418 // for backwards compatibility, if unspecified assume there is
419 bool skeletal_animation = true;
420 xml_node.get("skeletal-animation", &skeletal_animation);
421
422 if (render_pass == "skybox")
423 {
424 m_is_in_skybox = true;
425 }
426
427 bool animated = skeletal_animation && (UserConfigParams::m_animated_characters ||
428 World::getWorld()->getIdent() == IDENT_CUTSCENE);
429 bool displacing = false;
430 xml_node.get("displacing", &displacing);
431 animated &= !displacing;
432
433 if (animated)
434 m_mesh = irr_driver->getAnimatedMesh(model_name);
435 else
436 m_mesh = irr_driver->getMesh(model_name);
437
438 if (!m_mesh)
439 {
440 throw std::runtime_error("Model '" + model_name + "' cannot be found");
441 }
442
443 init(&xml_node, parent, enabled);
444 } // TrackObjectPresentationMesh
445
446 // ----------------------------------------------------------------------------
TrackObjectPresentationMesh(scene::IAnimatedMesh * model,const core::vector3df & xyz,const core::vector3df & hpr,const core::vector3df & scale)447 TrackObjectPresentationMesh::TrackObjectPresentationMesh(
448 scene::IAnimatedMesh* model,
449 const core::vector3df& xyz,
450 const core::vector3df& hpr,
451 const core::vector3df& scale)
452 : TrackObjectPresentationSceneNode(xyz, hpr, scale)
453 {
454 m_is_looped = false;
455 m_is_in_skybox = false;
456 m_mesh = NULL;
457 m_node = NULL;
458 m_mesh = model;
459 m_render_info = NULL;
460 init(NULL, NULL, true);
461 } // TrackObjectPresentationMesh
462
463 // ----------------------------------------------------------------------------
TrackObjectPresentationMesh(const std::string & model_file,const core::vector3df & xyz,const core::vector3df & hpr,const core::vector3df & scale)464 TrackObjectPresentationMesh::TrackObjectPresentationMesh(
465 const std::string& model_file,
466 const core::vector3df& xyz,
467 const core::vector3df& hpr,
468 const core::vector3df& scale)
469 : TrackObjectPresentationSceneNode(xyz, hpr, scale)
470 {
471 m_is_looped = false;
472 m_mesh = NULL;
473 m_node = NULL;
474 m_is_in_skybox = false;
475 m_render_info = NULL;
476 bool animated = (UserConfigParams::m_particles_effects > 1 ||
477 World::getWorld()->getIdent() == IDENT_CUTSCENE);
478
479 m_model_file = model_file;
480 file_manager->pushTextureSearchPath(StringUtils::getPath(model_file), "");
481 #ifndef SERVER_ONLY
482 if (file_manager->fileExists(model_file))
483 {
484 if (animated)
485 {
486 m_mesh = irr_driver->getAnimatedMesh(model_file);
487 }
488 else
489 {
490 m_mesh = irr_driver->getMesh(model_file);
491 }
492 }
493 #endif
494
495 if (!m_mesh)
496 {
497 throw std::runtime_error("Model '" + model_file + "' cannot be found");
498 }
499
500 file_manager->popTextureSearchPath();
501 init(NULL, NULL, true);
502 } // TrackObjectPresentationMesh
503
504 // ----------------------------------------------------------------------------
init(const XMLNode * xml_node,scene::ISceneNode * parent,bool enabled)505 void TrackObjectPresentationMesh::init(const XMLNode* xml_node,
506 scene::ISceneNode* parent, bool enabled)
507 {
508 // for backwards compatibility, if unspecified assume there is
509 bool skeletal_animation = true;
510 if(xml_node)
511 xml_node->get("skeletal-animation", &skeletal_animation);
512
513 bool animated = skeletal_animation && (UserConfigParams::m_particles_effects > 1 ||
514 World::getWorld()->getIdent() == IDENT_CUTSCENE);
515 bool displacing = false;
516 std::string interaction;
517 if (xml_node)
518 {
519 xml_node->get("displacing", &displacing);
520 xml_node->get("interaction", &interaction);
521 }
522 animated &= !displacing;
523
524 m_mesh->grab();
525 irr_driver->grabAllTextures(m_mesh);
526
527 if (interaction == "physicsonly")
528 {
529 std::string type;
530 xml_node->get("type", &type);
531 if (type == "animation" || xml_node->hasChildNamed("curve"))
532 {
533 // Animated
534 //m_node = irr_driver->getSceneManager()->addEmptySceneNode();
535 m_node = irr_driver->addMesh(m_mesh, m_model_file, parent, m_render_info);
536 enabled = false;
537 m_force_always_hidden = true;
538 }
539 else
540 {
541 // Static
542 m_node = irr_driver->addMesh(m_mesh, m_model_file, parent, m_render_info);
543 enabled = false;
544 m_force_always_hidden = true;
545 Track *track = Track::getCurrentTrack();
546 if (track && track && xml_node)
547 track->addPhysicsOnlyNode(m_node);
548 }
549 }
550 else if (m_is_in_skybox)
551 {
552 // Tell the driver that this mesh is a part of the background
553 scene::IMeshSceneNode * const node =
554 irr_driver->getSceneManager()->addMeshSceneNode(m_mesh);
555 node->grab();
556 node->setParent(NULL);
557
558 irr_driver->addBackgroundNode(node);
559
560 m_node = node;
561 }
562 else if (animated)
563 {
564 scene::IAnimatedMeshSceneNode *node =
565 irr_driver->addAnimatedMesh((scene::IAnimatedMesh*)m_mesh,
566 m_model_file, parent, m_render_info);
567 m_node = node;
568
569 std::vector<int> frames_start;
570 if (xml_node)
571 xml_node->get("frame-start", &frames_start);
572
573 std::vector<int> frames_end;
574 if (xml_node)
575 xml_node->get("frame-end", &frames_end);
576
577 if (frames_start.empty() && frames_end.empty())
578 {
579 frames_start.push_back(node->getStartFrame());
580 frames_end.push_back(node->getEndFrame());
581 }
582 assert(frames_start.size() == frames_end.size());
583 for (unsigned int i = 0 ; i < frames_start.size() ; i++)
584 node->addAnimationSet(frames_start[i], frames_end[i]);
585 node->useAnimationSet(0);
586
587 Track *track = Track::getCurrentTrack();
588 if (track && track && xml_node)
589 track->handleAnimatedTextures(m_node, *xml_node);
590 Track::uploadNodeVertexBuffer(node);
591 }
592 else
593 {
594 m_node = irr_driver->addMesh(m_mesh, m_model_file, parent, m_render_info);
595 Track *track = Track::getCurrentTrack();
596 if (track && xml_node)
597 track->handleAnimatedTextures(m_node, *xml_node);
598 Track::uploadNodeVertexBuffer(m_node);
599 }
600
601 if(!enabled)
602 m_node->setVisible(false);
603
604 m_node->setPosition(m_init_xyz);
605 m_node->setRotation(m_init_hpr);
606 m_node->setScale(m_init_scale);
607 } // init
608
609 // ----------------------------------------------------------------------------
~TrackObjectPresentationMesh()610 TrackObjectPresentationMesh::~TrackObjectPresentationMesh()
611 {
612 if (m_node)
613 irr_driver->removeNode(m_node);
614
615 if(m_mesh)
616 {
617 irr_driver->dropAllTextures(m_mesh);
618 m_mesh->drop();
619 if(m_mesh->getReferenceCount()==1)
620 irr_driver->removeMeshFromCache(m_mesh);
621 }
622 } // ~TrackObjectPresentationMesh
623
624 // ----------------------------------------------------------------------------
reset()625 void TrackObjectPresentationMesh::reset()
626 {
627 if (m_node->getType()==scene::ESNT_ANIMATED_MESH)
628 {
629 scene::IAnimatedMeshSceneNode *a_node =
630 (scene::IAnimatedMeshSceneNode*)m_node;
631
632 a_node->setPosition(m_init_xyz);
633 a_node->setRotation(m_init_hpr);
634 a_node->setScale(m_init_scale);
635 a_node->setLoopMode(m_is_looped);
636 a_node->setAnimationEndCallback(NULL);
637 a_node->setCurrentFrame((float)(a_node->getStartFrame()));
638
639 // trick to reset the animation AND also the timer inside it
640 a_node->OnAnimate(0);
641 a_node->OnAnimate(0);
642
643 // irrlicht's "setFrameLoop" is a misnomer, it just sets the first and
644 // last frame, even if looping is disabled
645 RandomGenerator rg;
646 int animation_set = 0;
647 if (a_node->getAnimationSetNum() > 0)
648 animation_set = rg.get(a_node->getAnimationSetNum());
649 a_node->useAnimationSet(animation_set);
650 }
651 } // reset
652
653 // ----------------------------------------------------------------------------
TrackObjectPresentationSound(const XMLNode & xml_node,scene::ISceneNode * parent,bool disable_for_multiplayer)654 TrackObjectPresentationSound::TrackObjectPresentationSound(
655 const XMLNode& xml_node,
656 scene::ISceneNode* parent,
657 bool disable_for_multiplayer)
658 : TrackObjectPresentation(xml_node)
659 {
660 // TODO: respect 'parent' if any
661
662 m_enabled = true;
663 m_sound = NULL;
664 m_xyz = m_init_xyz;
665
666 std::string sound;
667 xml_node.get("sound", &sound);
668
669 float rolloff = 0.5;
670 xml_node.get("rolloff", &rolloff );
671 float volume = 1.0;
672 xml_node.get("volume", &volume );
673
674 bool trigger_when_near = false;
675 xml_node.get("play-when-near", &trigger_when_near);
676
677 float trigger_distance = 1.0f;
678 xml_node.get("distance", &trigger_distance);
679
680 xml_node.get("conditions", &m_trigger_condition);
681
682 float max_dist = 390.0f;
683 xml_node.get("max_dist", &max_dist );
684
685 if (trigger_when_near)
686 {
687 Track::getCurrentTrack()->getCheckManager()->add(
688 new CheckTrigger(m_init_xyz, trigger_distance, std::bind(
689 &TrackObjectPresentationSound::onTriggerItemApproached,
690 this, std::placeholders::_1)));
691 }
692
693 if (disable_for_multiplayer)
694 return;
695 // first try track dir, then global dir
696 std::string soundfile = Track::getCurrentTrack()->getTrackFile(sound);
697 //std::string soundfile = file_manager->getAsset(FileManager::MODEL,sound);
698 if (!file_manager->fileExists(soundfile))
699 {
700 soundfile = file_manager->getAsset(FileManager::SFX, sound);
701 }
702
703 SFXBuffer* buffer = new SFXBuffer(soundfile,
704 true /* positional */,
705 rolloff,
706 max_dist,
707 volume);
708 buffer->load();
709
710 m_sound = SFXManager::get()->createSoundSource(buffer, true, true);
711 if (m_sound != NULL)
712 {
713 m_sound->setPosition(m_init_xyz);
714 if (!trigger_when_near && m_trigger_condition.empty())
715 {
716 m_sound->setLoop(true);
717 m_sound->play();
718 }
719 }
720 else
721 Log::error("TrackObject", "Sound emitter object could not be created.");
722
723 } // TrackObjectPresentationSound
724
725 // ----------------------------------------------------------------------------
updateGraphics(float dt)726 void TrackObjectPresentationSound::updateGraphics(float dt)
727 {
728 if (m_sound != NULL && m_enabled)
729 {
730 // muting when too far is implemented manually since not supported by
731 // OpenAL so need to call this every frame to update the muting state
732 // if listener moved
733 m_sound->setPosition(m_xyz);
734 }
735 } // update
736
737 // ----------------------------------------------------------------------------
onTriggerItemApproached(int kart_id)738 void TrackObjectPresentationSound::onTriggerItemApproached(int kart_id)
739 {
740 if (m_sound != NULL && m_sound->getStatus() != SFXBase::SFX_PLAYING && m_enabled)
741 {
742 m_sound->play();
743 }
744 } // onTriggerItemApproached
745
746 // ----------------------------------------------------------------------------
triggerSound(bool loop)747 void TrackObjectPresentationSound::triggerSound(bool loop)
748 {
749 if (m_sound != NULL && m_enabled)
750 {
751 m_sound->setLoop(loop);
752 m_sound->play();
753 }
754 } // triggerSound
755
756 // ----------------------------------------------------------------------------
stopSound()757 void TrackObjectPresentationSound::stopSound()
758 {
759 if (m_sound != NULL)
760 m_sound->stop();
761 } // stopSound
762
763 // ----------------------------------------------------------------------------
~TrackObjectPresentationSound()764 TrackObjectPresentationSound::~TrackObjectPresentationSound()
765 {
766 if (m_sound)
767 {
768 m_sound->deleteSFX();
769 }
770 } // ~TrackObjectPresentationSound
771
772 // ----------------------------------------------------------------------------
move(const core::vector3df & xyz,const core::vector3df & hpr,const core::vector3df & scale,bool isAbsoluteCoord)773 void TrackObjectPresentationSound::move(const core::vector3df& xyz,
774 const core::vector3df& hpr,
775 const core::vector3df& scale,
776 bool isAbsoluteCoord)
777 {
778 m_xyz = xyz;
779 if (m_sound != NULL && m_enabled)
780 m_sound->setPosition(xyz);
781 } // move
782
783 // ----------------------------------------------------------------------------
784
setEnable(bool enabled)785 void TrackObjectPresentationSound::setEnable(bool enabled)
786 {
787 if (enabled != m_enabled)
788 {
789 m_enabled = enabled;
790 if (enabled)
791 triggerSound(true);
792 else
793 stopSound();
794 }
795 }
796
797 // ----------------------------------------------------------------------------
TrackObjectPresentationBillboard(const XMLNode & xml_node,scene::ISceneNode * parent)798 TrackObjectPresentationBillboard::TrackObjectPresentationBillboard(
799 const XMLNode& xml_node,
800 scene::ISceneNode* parent)
801 : TrackObjectPresentationSceneNode(xml_node)
802 {
803 std::string texture_name;
804 float width, height;
805
806 m_fade_out_start = 50.0f;
807 m_fade_out_end = 150.0f;
808
809 xml_node.get("texture", &texture_name);
810 xml_node.get("width", &width );
811 xml_node.get("height", &height );
812
813 m_fade_out_when_close = false;
814 xml_node.get("fadeout", &m_fade_out_when_close);
815
816 if (m_fade_out_when_close)
817 {
818 xml_node.get("start", &m_fade_out_start);
819 xml_node.get("end", &m_fade_out_end );
820 }
821 m_node = irr_driver->addBillboard(core::dimension2df(width, height),
822 texture_name, parent);
823 m_node->setPosition(m_init_xyz);
824 } // TrackObjectPresentationBillboard
825
826 // ----------------------------------------------------------------------------
updateGraphics(float dt)827 void TrackObjectPresentationBillboard::updateGraphics(float dt)
828 {
829 if (GUIEngine::isNoGraphics()) return;
830 #ifndef SERVER_ONLY
831 if (m_fade_out_when_close)
832 {
833 scene::ICameraSceneNode* curr_cam = irr_driver->getSceneManager()
834 ->getActiveCamera();
835 const float dist = m_node->getAbsolutePosition()
836 .getDistanceFrom( curr_cam->getPosition() );
837
838 scene::IBillboardSceneNode* node = (scene::IBillboardSceneNode*)m_node;
839
840 if (dist < m_fade_out_start)
841 {
842 node->setColor(video::SColor(0, 255, 255, 255));
843 }
844 else if (dist > m_fade_out_end)
845 {
846 node->setColor(video::SColor(255, 255, 255, 255));
847 }
848 else
849 {
850 int a = (int)(255*(dist - m_fade_out_start)
851 / (m_fade_out_end - m_fade_out_start));
852 node->setColor(video::SColor(a, 255, 255, 255));
853 }
854 } // m_fade_out_when_close
855 #endif
856 } // update
857
858 // ----------------------------------------------------------------------------
~TrackObjectPresentationBillboard()859 TrackObjectPresentationBillboard::~TrackObjectPresentationBillboard()
860 {
861 if (m_node)
862 irr_driver->removeNode(m_node);
863 } // ~TrackObjectPresentationBillboard
864
865 // ----------------------------------------------------------------------------
TrackObjectPresentationParticles(const XMLNode & xml_node,scene::ISceneNode * parent)866 TrackObjectPresentationParticles::TrackObjectPresentationParticles(
867 const XMLNode& xml_node,
868 scene::ISceneNode* parent)
869 : TrackObjectPresentationSceneNode(xml_node)
870 {
871 m_emitter = NULL;
872 m_lod_emitter_node = NULL;
873 std::string path;
874 xml_node.get("kind", &path);
875
876 int clip_distance = -1;
877 xml_node.get("clip_distance", &clip_distance);
878 xml_node.get("conditions", &m_trigger_condition);
879
880 bool auto_emit = true;
881 xml_node.get("auto_emit", &auto_emit);
882
883 m_delayed_stop = false;
884 m_delayed_stop_time = 0.0;
885
886 #ifndef SERVER_ONLY
887 try
888 {
889 ParticleKind* kind = ParticleKindManager::get()->getParticles(path);
890 if (kind == NULL)
891 {
892 throw std::runtime_error(path + " could not be loaded");
893 }
894 ParticleEmitter* emitter = new ParticleEmitter(kind, m_init_xyz, parent);
895
896
897 if (clip_distance > 0)
898 {
899 scene::ISceneManager* sm = irr_driver->getSceneManager();
900 scene::ISceneNode* sroot = sm->getRootSceneNode();
901 LODNode* lod = new LODNode("particles", !parent ? sroot : parent, sm);
902 lod->add(clip_distance, (scene::ISceneNode*)emitter->getNode(), true);
903 m_node = lod;
904 m_lod_emitter_node = lod;
905 m_emitter = emitter;
906 }
907 else
908 {
909 m_node = emitter->getNode();
910 m_emitter = emitter;
911 }
912
913 if (m_trigger_condition.size() > 0 || !auto_emit)
914 {
915 m_emitter->setCreationRateAbsolute(0.0f);
916 }
917 }
918 catch (std::runtime_error& e)
919 {
920 Log::warn ("Track", "Could not load particles '%s'; cause :\n %s",
921 path.c_str(), e.what());
922 }
923 #endif
924 } // TrackObjectPresentationParticles
925
926 // ----------------------------------------------------------------------------
~TrackObjectPresentationParticles()927 TrackObjectPresentationParticles::~TrackObjectPresentationParticles()
928 {
929 if (m_emitter)
930 {
931 if (m_lod_emitter_node != NULL)
932 {
933 irr_driver->removeNode(m_lod_emitter_node);
934 m_emitter->unsetNode();
935 }
936 delete m_emitter; // this will also delete m_node
937 }
938 } // ~TrackObjectPresentationParticles
939
940 // ----------------------------------------------------------------------------
updateGraphics(float dt)941 void TrackObjectPresentationParticles::updateGraphics(float dt)
942 {
943 if (m_emitter != NULL)
944 {
945 m_emitter->update(dt);
946 }
947
948 if (m_delayed_stop)
949 {
950 if (m_delayed_stop_time < 0.0f)
951 {
952 m_delayed_stop = false;
953 stop();
954 }
955 m_delayed_stop_time -= dt;
956 }
957 } // update
958
959 // ----------------------------------------------------------------------------
triggerParticles()960 void TrackObjectPresentationParticles::triggerParticles()
961 {
962 #ifndef SERVER_ONLY
963 if (m_emitter != NULL)
964 {
965 m_emitter->setCreationRateAbsolute(1.0f);
966 m_emitter->setParticleType(m_emitter->getParticlesInfo());
967 }
968 #endif
969 } // triggerParticles
970 // ----------------------------------------------------------------------------
stop()971 void TrackObjectPresentationParticles::stop()
972 {
973 #ifndef SERVER_ONLY
974 if (m_emitter != NULL)
975 {
976 m_emitter->setCreationRateAbsolute(0.0f);
977 }
978 #endif
979 }
980 // ----------------------------------------------------------------------------
stopIn(double delay)981 void TrackObjectPresentationParticles::stopIn(double delay)
982 {
983 m_delayed_stop = true;
984 m_delayed_stop_time = delay;
985 }
986 // ----------------------------------------------------------------------------
setRate(float rate)987 void TrackObjectPresentationParticles::setRate(float rate)
988 {
989 #ifndef SERVER_ONLY
990 if (m_emitter != NULL)
991 {
992 m_emitter->setCreationRateAbsolute(rate);
993 m_emitter->setParticleType(m_emitter->getParticlesInfo());
994 }
995 #endif
996 } // setRate
997
998 // ----------------------------------------------------------------------------
TrackObjectPresentationLight(const XMLNode & xml_node,scene::ISceneNode * parent)999 TrackObjectPresentationLight::TrackObjectPresentationLight(
1000 const XMLNode& xml_node,
1001 scene::ISceneNode* parent)
1002 : TrackObjectPresentationSceneNode(xml_node)
1003 {
1004 m_color.set(0);
1005 xml_node.get("color", &m_color);
1006 const video::SColorf colorf(m_color);
1007
1008 m_energy = 1.0f;
1009 xml_node.get("energy", &m_energy);
1010
1011 m_distance = 20.f * m_energy;
1012 xml_node.get("distance", &m_distance);
1013 #ifndef SERVER_ONLY
1014 if (CVS->isGLSL())
1015 {
1016 m_node = irr_driver->addLight(m_init_xyz, m_energy, m_distance,
1017 colorf.r, colorf.g, colorf.b, false,
1018 parent);
1019 }
1020 else
1021 #endif
1022 {
1023 m_node = NULL; // lights require shaders to work
1024 }
1025 } // TrackObjectPresentationLight
1026
1027 // ----------------------------------------------------------------------------
~TrackObjectPresentationLight()1028 TrackObjectPresentationLight::~TrackObjectPresentationLight()
1029 {
1030 } // ~TrackObjectPresentationLight
1031 // ----------------------------------------------------------------------------
setEnergy(float energy)1032 void TrackObjectPresentationLight::setEnergy(float energy)
1033 {
1034 m_energy = energy;
1035 LightNode* lnode = dynamic_cast<LightNode*>(m_node);
1036 if (lnode != NULL)
1037 {
1038 lnode->setEnergy(energy);
1039 }
1040 }
1041 // ----------------------------------------------------------------------------
setEnable(bool enabled)1042 void TrackObjectPresentationLight::setEnable(bool enabled)
1043 {
1044 if (m_node != NULL)
1045 m_node->setVisible(enabled);
1046 } // setEnable
1047
1048 // ----------------------------------------------------------------------------
TrackObjectPresentationActionTrigger(const XMLNode & xml_node,TrackObject * parent)1049 TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger(
1050 const XMLNode& xml_node,
1051 TrackObject* parent)
1052 : TrackObjectPresentation(xml_node)
1053 {
1054 float trigger_distance = 1.0f;
1055 xml_node.get("distance", &trigger_distance);
1056 xml_node.get("action", &m_action );
1057
1058 std::string trigger_type;
1059 xml_node.get("trigger-type", &trigger_type);
1060 if (trigger_type == "point" || trigger_type.empty())
1061 {
1062 m_type = TRIGGER_TYPE_POINT;
1063 }
1064 else if (trigger_type == "cylinder")
1065 {
1066 m_type = TRIGGER_TYPE_CYLINDER;
1067 }
1068 else
1069 {
1070 assert(false);
1071 }
1072 m_xml_reenable_timeout = 999999.9f;
1073 xml_node.get("reenable-timeout", &m_xml_reenable_timeout);
1074 setReenableTimeout(0.0f);
1075
1076 if (m_action.empty())
1077 {
1078 Log::warn("TrackObject", "Action-trigger has no action defined.");
1079 return;
1080 }
1081
1082 if (parent != NULL)
1083 {
1084 core::vector3df parent_xyz = parent->getInitXYZ();
1085 core::vector3df parent_rot = parent->getInitRotation();
1086 core::vector3df parent_scale = parent->getInitScale();
1087 core::matrix4 lm, sm, rm;
1088 lm.setTranslation(parent_xyz);
1089 sm.setScale(parent_scale);
1090 rm.setRotationDegrees(parent_rot);
1091 core::matrix4 abs_trans = lm * rm * sm;
1092
1093 m_library_id = parent->getID();
1094 m_library_name = parent->getName();
1095 xml_node.get("triggered-object", &m_triggered_object);
1096 if (!m_library_id.empty() && !m_triggered_object.empty() &&
1097 !m_library_name.empty())
1098 {
1099 abs_trans.transformVect(m_init_xyz);
1100 }
1101 }
1102
1103 if (m_type == TRIGGER_TYPE_POINT)
1104 {
1105 Track::getCurrentTrack()->getCheckManager()->add(
1106 new CheckTrigger(m_init_xyz, trigger_distance, std::bind(
1107 &TrackObjectPresentationActionTrigger::onTriggerItemApproached,
1108 this, std::placeholders::_1)));
1109 }
1110 else if (m_type == TRIGGER_TYPE_CYLINDER)
1111 {
1112 Track::getCurrentTrack()->getCheckManager()->add(new CheckCylinder(xml_node, std::bind(
1113 &TrackObjectPresentationActionTrigger::onTriggerItemApproached,
1114 this, std::placeholders::_1)));
1115 }
1116 else
1117 {
1118 assert(false);
1119 }
1120 } // TrackObjectPresentationActionTrigger
1121
1122 // ----------------------------------------------------------------------------
TrackObjectPresentationActionTrigger(const core::vector3df & xyz,const std::string & script_name,float distance)1123 TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger(
1124 const core::vector3df& xyz,
1125 const std::string& script_name,
1126 float distance)
1127 : TrackObjectPresentation(xyz)
1128 {
1129 m_init_xyz = xyz;
1130 m_init_hpr = core::vector3df(0, 0, 0);
1131 m_init_scale = core::vector3df(1, 1, 1);
1132 float trigger_distance = distance;
1133 m_action = script_name;
1134 m_xml_reenable_timeout = 999999.9f;
1135 setReenableTimeout(0.0f);
1136 m_type = TRIGGER_TYPE_POINT;
1137 Track::getCurrentTrack()->getCheckManager()->add(
1138 new CheckTrigger(m_init_xyz, trigger_distance, std::bind(
1139 &TrackObjectPresentationActionTrigger::onTriggerItemApproached,
1140 this, std::placeholders::_1)));
1141 } // TrackObjectPresentationActionTrigger
1142
1143 // ----------------------------------------------------------------------------
onTriggerItemApproached(int kart_id)1144 void TrackObjectPresentationActionTrigger::onTriggerItemApproached(int kart_id)
1145 {
1146 if (m_reenable_timeout > StkTime::getMonoTimeMs() ||
1147 STKProcess::getType() == PT_CHILD)
1148 {
1149 return;
1150 }
1151 setReenableTimeout(m_xml_reenable_timeout);
1152
1153 if (!m_library_id.empty() && !m_triggered_object.empty() &&
1154 !m_library_name.empty())
1155 {
1156 Scripting::ScriptEngine::getInstance()->runFunction(true, "void "
1157 + m_library_name + "::" + m_action +
1158 "(int, const string, const string)", [=](asIScriptContext* ctx)
1159 {
1160 ctx->SetArgDWord(0, kart_id);
1161 ctx->SetArgObject(1, &m_library_id);
1162 ctx->SetArgObject(2, &m_triggered_object);
1163 });
1164 }
1165 else
1166 {
1167 Scripting::ScriptEngine::getInstance()->runFunction(true,
1168 "void " + m_action + "(int)", [=](asIScriptContext* ctx)
1169 {
1170 ctx->SetArgDWord(0, kart_id);
1171 });
1172 }
1173 } // onTriggerItemApproached
1174