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