1 //
2 //  SuperTuxKart - a fun racing game with go-kart
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.hpp"
20 
21 #include "animations/three_d_animation.hpp"
22 #include "graphics/central_settings.hpp"
23 #include "graphics/irr_driver.hpp"
24 #include "graphics/lod_node.hpp"
25 #include "graphics/material.hpp"
26 #include "graphics/material_manager.hpp"
27 #include "graphics/render_info.hpp"
28 #include "graphics/sp/sp_mesh_buffer.hpp"
29 #include "graphics/sp/sp_mesh_node.hpp"
30 #include "io/file_manager.hpp"
31 #include "io/xml_node.hpp"
32 #include "input/device_manager.hpp"
33 #include "items/item_manager.hpp"
34 #include "network/network_config.hpp"
35 #include "physics/physical_object.hpp"
36 #include "race/race_manager.hpp"
37 #include "scriptengine/script_engine.hpp"
38 #include "tracks/track.hpp"
39 #include "tracks/model_definition_loader.hpp"
40 #include "utils/string_utils.hpp"
41 
42 #include <IAnimatedMeshSceneNode.h>
43 #include <ISceneManager.h>
44 
45 /** A track object: any additional object on the track. This object implements
46  *  a graphics-only representation, i.e. there is no physical representation.
47  *  Derived classes can implement a physical representation (see
48  *  physics/physical_object) or animations.
49  * \param xml_node The xml node from which the initial data is taken. This is
50  *                 for now: initial position, initial rotation, name of the
51  *                 model, enable/disable status, timer information.
52  * \param lod_node Lod node (defaults to NULL).
53  */
TrackObject(const XMLNode & xml_node,scene::ISceneNode * parent,ModelDefinitionLoader & model_def_loader,TrackObject * parent_library)54 TrackObject::TrackObject(const XMLNode &xml_node, scene::ISceneNode* parent,
55                          ModelDefinitionLoader& model_def_loader,
56                          TrackObject* parent_library)
57 {
58     init(xml_node, parent, model_def_loader, parent_library);
59 }   // TrackObject
60 
61 // ----------------------------------------------------------------------------
62 /**
63  * \param is_dynamic Only if interaction == 'movable', i.e. the object is
64  *        affected by physics
65  * \param physics_settings If interaction != 'ghost'
66  */
TrackObject(const core::vector3df & xyz,const core::vector3df & hpr,const core::vector3df & scale,const char * interaction,TrackObjectPresentation * presentation,bool is_dynamic,const PhysicalObject::Settings * physics_settings)67 TrackObject::TrackObject(const core::vector3df& xyz, const core::vector3df& hpr,
68                          const core::vector3df& scale, const char* interaction,
69                          TrackObjectPresentation* presentation,
70                          bool is_dynamic,
71                          const PhysicalObject::Settings* physics_settings)
72 {
73     m_init_xyz        = xyz;
74     m_init_hpr        = hpr;
75     m_init_scale      = scale;
76     m_enabled         = true;
77     m_presentation    = NULL;
78     m_animator        = NULL;
79     m_parent_library  = NULL;
80     m_interaction     = interaction;
81     m_presentation    = presentation;
82     m_is_driveable    = false;
83     m_soccer_ball     = false;
84     m_initially_visible = false;
85     m_type            = "";
86 
87     if (m_interaction != "ghost" && m_interaction != "none" &&
88         physics_settings )
89     {
90         m_physical_object = std::make_shared<PhysicalObject>
91             (is_dynamic, *physics_settings, this);
92     }
93 
94     reset();
95 }   // TrackObject
96 
97 // ----------------------------------------------------------------------------
98 /** Initialises the track object based on the specified XML data.
99  *  \param xml_node The XML data.
100  *  \param parent The parent scene node.
101  *  \param model_def_loader Used to load level-of-detail nodes.
102  */
init(const XMLNode & xml_node,scene::ISceneNode * parent,ModelDefinitionLoader & model_def_loader,TrackObject * parent_library)103 void TrackObject::init(const XMLNode &xml_node, scene::ISceneNode* parent,
104                        ModelDefinitionLoader& model_def_loader,
105                        TrackObject* parent_library)
106 {
107     m_init_xyz   = core::vector3df(0,0,0);
108     m_init_hpr   = core::vector3df(0,0,0);
109     m_init_scale = core::vector3df(1,1,1);
110     m_enabled    = true;
111     m_initially_visible = false;
112     m_presentation = NULL;
113     m_animator = NULL;
114     m_parent_library = parent_library;
115 
116     xml_node.get("id",      &m_id        );
117     xml_node.get("model",   &m_name      );
118     xml_node.get("xyz",     &m_init_xyz  );
119     xml_node.get("hpr",     &m_init_hpr  );
120     xml_node.get("scale",   &m_init_scale);
121     xml_node.get("enabled", &m_enabled   );
122 
123     m_interaction = "static";
124     xml_node.get("interaction", &m_interaction);
125     xml_node.get("lod_group", &m_lod_group);
126 
127     m_is_driveable = false;
128     xml_node.get("driveable", &m_is_driveable);
129 
130     bool lod_instance = false;
131     xml_node.get("lod_instance", &lod_instance);
132 
133     m_soccer_ball = false;
134     xml_node.get("soccer_ball", &m_soccer_ball);
135 
136     std::string type;
137     xml_node.get("type",    &type );
138 
139     m_type = type;
140 
141     m_initially_visible = true;
142     xml_node.get("if", &m_visibility_condition);
143     if (m_visibility_condition == "false")
144     {
145         m_initially_visible = false;
146     }
147     if (!m_initially_visible)
148         setEnabled(false);
149 
150     if (xml_node.getName() == "particle-emitter")
151     {
152         m_type = "particle-emitter";
153         m_presentation = new TrackObjectPresentationParticles(xml_node, parent);
154     }
155     else if (xml_node.getName() == "light")
156     {
157         m_type = "light";
158         m_presentation = new TrackObjectPresentationLight(xml_node, parent);
159     }
160     else if (xml_node.getName() == "library")
161     {
162         xml_node.get("name", &m_name);
163         m_presentation = new TrackObjectPresentationLibraryNode(this, xml_node, model_def_loader);
164         if (parent_library != NULL)
165         {
166             Track::getCurrentTrack()->addMetaLibrary(parent_library, this);
167         }
168     }
169     else if (type == "sfx-emitter")
170     {
171         // FIXME: at this time sound emitters are just disabled in multiplayer
172         //        otherwise the sounds would be constantly heard, for networking
173         //        the index of item needs to be same so we create and disable it
174         //        in TrackObjectPresentationSound constructor
175         m_presentation = new TrackObjectPresentationSound(xml_node, parent,
176             RaceManager::get()->getNumLocalPlayers() > 1);
177     }
178     else if (type == "action-trigger")
179     {
180         std::string action;
181         xml_node.get("action", &action);
182         m_name = action; //adds action as name so that it can be found by using getName()
183         m_presentation = new TrackObjectPresentationActionTrigger(xml_node, parent_library);
184     }
185     else if (type == "billboard")
186     {
187         m_presentation = new TrackObjectPresentationBillboard(xml_node, parent);
188     }
189     else if (type=="cutscene_camera")
190     {
191         m_presentation = new TrackObjectPresentationEmpty(xml_node);
192     }
193     else
194     {
195         // Colorization settings
196         std::string model_name;
197         xml_node.get("model", &model_name);
198 #ifndef SERVER_ONLY
199         if (CVS->isGLSL())
200         {
201             scene::IMesh* mesh = NULL;
202             // Use the first material in mesh to determine hue
203             Material* colorized = NULL;
204             if (model_name.size() > 0)
205             {
206                 mesh = irr_driver->getMesh(model_name);
207                 if (mesh != NULL)
208                 {
209                     for (u32 j = 0; j < mesh->getMeshBufferCount(); j++)
210                     {
211                         SP::SPMeshBuffer* mb = static_cast<SP::SPMeshBuffer*>
212                             (mesh->getMeshBuffer(j));
213                         std::vector<Material*> mbs = mb->getAllSTKMaterials();
214                         for (Material* m : mbs)
215                         {
216                             if (m->isColorizable() && m->hasRandomHue())
217                             {
218                                 colorized = m;
219                                 break;
220                             }
221                         }
222                         if (colorized != NULL)
223                         {
224                             break;
225                         }
226                     }
227                 }
228             }
229             else
230             {
231                 std::string group_name = "";
232                 xml_node.get("lod_group", &group_name);
233                 // Try to get the first mesh from lod groups
234                 mesh = model_def_loader.getFirstMeshFor(group_name);
235                 if (mesh != NULL)
236                 {
237                     for (u32 j = 0; j < mesh->getMeshBufferCount(); j++)
238                     {
239                         SP::SPMeshBuffer* mb = static_cast<SP::SPMeshBuffer*>
240                             (mesh->getMeshBuffer(j));
241                         std::vector<Material*> mbs = mb->getAllSTKMaterials();
242                         for (Material* m : mbs)
243                         {
244                             if (m->isColorizable() && m->hasRandomHue())
245                             {
246                                 colorized = m;
247                                 break;
248                             }
249                         }
250                         if (colorized != NULL)
251                         {
252                             break;
253                         }
254                     }
255                 }
256             }
257 
258             // If at least one material is colorizable, add RenderInfo for it
259             if (colorized != NULL)
260             {
261                 const float hue = colorized->getRandomHue();
262                 if (hue > 0.0f)
263                 {
264                     m_render_info = std::make_shared<RenderInfo>(hue);
265                 }
266             }
267         }
268 #endif
269         scene::ISceneNode *glownode = NULL;
270         bool is_movable = false;
271         if (lod_instance)
272         {
273             m_type = "lod";
274             TrackObjectPresentationLOD* lod_node =
275                 new TrackObjectPresentationLOD(xml_node, parent, model_def_loader, m_render_info);
276             m_presentation = lod_node;
277 
278             LODNode* node = (LODNode*)lod_node->getNode();
279             if (type == "movable" && parent != NULL)
280             {
281                 // HACK: unparent movables from their parent library object if any,
282                 // because bullet provides absolute transforms, not transforms relative
283                 // to the parent object
284                 node->updateAbsolutePosition();
285                 core::matrix4 absTransform = node->getAbsoluteTransformation();
286                 node->setParent(irr_driver->getSceneManager()->getRootSceneNode());
287                 node->setPosition(absTransform.getTranslation());
288                 node->setRotation(absTransform.getRotationDegrees());
289                 node->setScale(absTransform.getScale());
290             }
291 
292             glownode = node->getAllNodes()[0];
293         }
294         else
295         {
296             m_type = "mesh";
297             m_presentation = new TrackObjectPresentationMesh(xml_node,
298                                                              m_enabled,
299                                                              parent,
300                                                              m_render_info);
301             scene::ISceneNode* node = ((TrackObjectPresentationMesh *)m_presentation)->getNode();
302             if (type == "movable" && parent != NULL)
303             {
304                 // HACK: unparent movables from their parent library object if any,
305                 // because bullet provides absolute transforms, not transforms relative
306                 // to the parent object
307                 node->updateAbsolutePosition();
308                 core::matrix4 absTransform = node->getAbsoluteTransformation();
309                 node->setParent(irr_driver->getSceneManager()->getRootSceneNode());
310                 node->setPosition(absTransform.getTranslation());
311                 // Doesn't seem necessary to set rotation here, TODO: not sure why
312                 //node->setRotation(absTransform.getRotationDegrees());
313                 node->setScale(absTransform.getScale());
314                 is_movable = true;
315             }
316 
317             glownode = node;
318         }
319 
320         std::string render_pass;
321         xml_node.get("renderpass", &render_pass);
322 
323         if (m_interaction != "ghost" && m_interaction != "none" &&
324             render_pass != "skybox"                                     )
325         {
326             m_physical_object = PhysicalObject::fromXML(type == "movable",
327                                                    xml_node,
328                                                    this);
329         }
330 
331         if (parent_library != NULL)
332         {
333             if (is_movable)
334                 parent_library->addMovableChild(this);
335             else
336                 parent_library->addChild(this);
337         }
338 
339         video::SColor glow;
340         if (xml_node.get("glow", &glow) && glownode)
341         {
342             float r, g, b;
343             r = glow.getRed() / 255.0f;
344             g = glow.getGreen() / 255.0f;
345             b = glow.getBlue() / 255.0f;
346             SP::SPMeshNode* spmn = dynamic_cast<SP::SPMeshNode*>(glownode);
347             if (spmn)
348             {
349                 spmn->setGlowColor(video::SColorf(r, g, b));
350             }
351         }
352 
353         bool is_in_shadowpass = true;
354         if (xml_node.get("shadow-pass", &is_in_shadowpass) && glownode)
355         {
356             SP::SPMeshNode* spmn = dynamic_cast<SP::SPMeshNode*>(glownode);
357             if (spmn)
358             {
359                 spmn->setInShadowPass(is_in_shadowpass);
360             }
361         }
362 
363         bool forcedbloom = false;
364         if (xml_node.get("forcedbloom", &forcedbloom) && forcedbloom && glownode)
365         {
366             float power = 1;
367             xml_node.get("bloompower", &power);
368             btClamp(power, 0.5f, 10.0f);
369             irr_driver->addForcedBloomNode(glownode, power);
370         }
371     }
372 
373 
374     if (type == "animation" || xml_node.hasChildNamed("curve"))
375     {
376         try
377         {
378             m_animator = new ThreeDAnimation(xml_node, this);
379         }
380         catch (std::exception& e)
381         {
382 #ifndef SERVER_ONLY
383             Log::debug("TrackObject", e.what());
384 #endif
385         }
386     }
387 
388     reset();
389 
390     if (!m_initially_visible)
391         setEnabled(false);
392     if (parent_library != NULL && !parent_library->isEnabled())
393         setEnabled(false);
394 }   // TrackObject
395 
396 // ----------------------------------------------------------------------------
397 
onWorldReady()398 void TrackObject::onWorldReady()
399 {
400     if (m_visibility_condition == "false")
401     {
402         m_initially_visible = false;
403     }
404     else if (m_visibility_condition.size() > 0)
405     {
406         unsigned char result = -1;
407         Scripting::ScriptEngine* script_engine =
408                                         Scripting::ScriptEngine::getInstance();
409 
410         std::ostringstream fn_signature;
411         std::vector<std::string> arguments;
412         if (m_visibility_condition.find("(") != std::string::npos &&
413             m_visibility_condition.find(")") != std::string::npos)
414         {
415             // There are arguments to pass to the function
416             // TODO: For the moment we only support string arguments
417             // TODO: this parsing could be improved
418             unsigned first = (unsigned)m_visibility_condition.find("(");
419             unsigned last = (unsigned)m_visibility_condition.find_last_of(")");
420             std::string fn_name = m_visibility_condition.substr(0, first);
421             std::string str_arguments = m_visibility_condition.substr(first + 1, last - first - 1);
422             arguments = StringUtils::split(str_arguments, ',');
423 
424             fn_signature << "bool " << fn_name << "(";
425 
426             for (unsigned int i = 0; i < arguments.size(); i++)
427             {
428                 if (i > 0)
429                     fn_signature << ",";
430                 fn_signature << "string";
431             }
432 
433             fn_signature << ",Track::TrackObject@)";
434         }
435         else
436         {
437             fn_signature << "bool " << m_visibility_condition << "(Track::TrackObject@)";
438         }
439 
440         TrackObject* self = this;
441         script_engine->runFunction(true, fn_signature.str(),
442             [&](asIScriptContext* ctx)
443             {
444                 for (unsigned int i = 0; i < arguments.size(); i++)
445                 {
446                     ctx->SetArgObject(i, &arguments[i]);
447                 }
448                 ctx->SetArgObject((int)arguments.size(), self);
449             },
450             [&](asIScriptContext* ctx) { result = ctx->GetReturnByte(); });
451 
452         if (result == 0)
453             m_initially_visible = false;
454     }
455     if (!m_initially_visible)
456         setEnabled(false);
457 }
458 
459 // ----------------------------------------------------------------------------
460 
461 /** Destructor. Removes the node from the scene graph, and also
462  *  drops the textures of the mesh. Sound buffers are also freed.
463  */
~TrackObject()464 TrackObject::~TrackObject()
465 {
466     delete m_presentation;
467     delete m_animator;
468 }   // ~TrackObject
469 
470 // ----------------------------------------------------------------------------
471 /** Initialises an object before a race starts.
472  */
reset()473 void TrackObject::reset()
474 {
475     if (m_presentation   ) m_presentation->reset();
476     if (m_animator       ) m_animator->reset();
477     if (m_physical_object) m_physical_object->reset();
478 }   // reset
479 
480 // ----------------------------------------------------------------------------
481 /** Enables or disables this object. This affects the visibility, i.e.
482  *  disabled objects will not be displayed anymore.
483  *  \param mode Enable (true) or disable (false) this object.
484  */
setEnabled(bool enabled)485 void TrackObject::setEnabled(bool enabled)
486 {
487     m_enabled = enabled;
488 
489     if (m_presentation != NULL)
490         m_presentation->setEnable(m_enabled);
491 
492     if (getType() == "mesh")
493     {
494         if (m_physical_object)
495         {
496             if (enabled)
497                 m_physical_object->addBody();
498             else
499                 m_physical_object->removeBody();
500         }
501     }
502 
503     for (unsigned int i = 0; i < m_movable_children.size(); i++)
504     {
505         m_movable_children[i]->setEnabled(enabled);
506     }
507 }   // setEnable
508 
509 // ----------------------------------------------------------------------------
510 
resetEnabled()511 void TrackObject::resetEnabled()
512 {
513     m_enabled = m_initially_visible;
514 
515     if (m_presentation != NULL)
516         m_presentation->setEnable(m_initially_visible);
517 
518     if (getType() == "mesh")
519     {
520         if (m_physical_object)
521         {
522             if (m_initially_visible)
523                 m_physical_object->addBody();
524             else
525                 m_physical_object->removeBody();
526         }
527     }
528 
529     for (unsigned int i = 0; i < m_movable_children.size(); i++)
530     {
531         m_movable_children[i]->resetEnabled();
532     }
533 }   // resetEnabled
534 
535 // ----------------------------------------------------------------------------
536 /** This updates all only graphical elements. It is only called once per
537  *  rendered frame, not once per time step.
538  *  float dt Time since last rame.
539  */
updateGraphics(float dt)540 void TrackObject::updateGraphics(float dt)
541 {
542     if (m_presentation) m_presentation->updateGraphics(dt);
543     if (m_physical_object) m_physical_object->updateGraphics(dt);
544     if (m_animator) m_animator->updateWithWorldTicks(false/*has_physics*/);
545 }   // update
546 
547 // ----------------------------------------------------------------------------
548 /** This updates once per physics time step.
549  *  float dt Time since last rame.
550  */
update(float dt)551 void TrackObject::update(float dt)
552 {
553     if (m_presentation) m_presentation->update(dt);
554     if (m_physical_object) m_physical_object->update(dt);
555     if (m_animator) m_animator->updateWithWorldTicks(true/*has_physics*/);
556 }   // update
557 
558 
559 // ----------------------------------------------------------------------------
560 /** This reset all physical object moved by 3d animation back to current ticks
561  */
resetAfterRewind()562 void TrackObject::resetAfterRewind()
563 {
564     if (!m_animator || !m_physical_object)
565         return;
566     m_animator->updateWithWorldTicks(true/*has_physics*/);
567     btTransform new_trans;
568     m_physical_object->getMotionState()->getWorldTransform(new_trans);
569     m_physical_object->getBody()->setCenterOfMassTransform(new_trans);
570     m_physical_object->getBody()->saveKinematicState(stk_config->ticks2Time(1));
571 }   // resetAfterRewind
572 
573 // ----------------------------------------------------------------------------
574 /** Does a raycast against the track object. The object must have a physical
575  *  object.
576  *  \param from/to The from and to position for the raycast.
577  *  \param xyz The position in world where the ray hit.
578  *  \param material The material of the mesh that was hit.
579  *  \param normal The intrapolated normal at that position.
580  *  \param interpolate_normal If true, the returned normal is the interpolated
581  *         based on the three normals of the triangle and the location of the
582  *         hit point (which is more compute intensive, but results in much
583  *         smoother results).
584  *  \return True if a triangle was hit, false otherwise (and no output
585  *          variable will be set.
586  */
castRay(const btVector3 & from,const btVector3 & to,btVector3 * hit_point,const Material ** material,btVector3 * normal,bool interpolate_normal) const587 bool TrackObject::castRay(const btVector3 &from,
588                           const btVector3 &to, btVector3 *hit_point,
589                           const Material **material, btVector3 *normal,
590                           bool interpolate_normal) const
591 {
592     if(!m_physical_object)
593     {
594         Log::warn("TrackObject", "Can't raycast on non-physical object.");
595         return false;
596     }
597     return m_physical_object->castRay(from, to, hit_point, material, normal,
598                                       interpolate_normal);
599 }   // castRay
600 
601 // ----------------------------------------------------------------------------
602 
move(const core::vector3df & xyz,const core::vector3df & hpr,const core::vector3df & scale,bool update_rigid_body,bool isAbsoluteCoord)603 void TrackObject::move(const core::vector3df& xyz, const core::vector3df& hpr,
604                        const core::vector3df& scale, bool update_rigid_body,
605                        bool isAbsoluteCoord)
606 {
607     if (m_presentation != NULL)
608         m_presentation->move(xyz, hpr, scale, isAbsoluteCoord);
609 
610     if (update_rigid_body && m_physical_object)
611     {
612         movePhysicalBodyToGraphicalNode(xyz, hpr);
613     }
614 }   // move
615 
616 // ----------------------------------------------------------------------------
617 
movePhysicalBodyToGraphicalNode(const core::vector3df & xyz,const core::vector3df & hpr)618 void TrackObject::movePhysicalBodyToGraphicalNode(const core::vector3df& xyz,
619                                                   const core::vector3df& hpr)
620 {
621     // If we set a bullet position from an irrlicht position, we need to
622     // get the absolute transform from the presentation object (as set in
623     // the line before), since xyz etc here are only relative to a
624     // potential parent scene node.
625     TrackObjectPresentationSceneNode *tops =
626         dynamic_cast<TrackObjectPresentationSceneNode*>(m_presentation);
627     if (tops)
628     {
629         const core::matrix4 &m = tops->getNode()
630             ->getAbsoluteTransformation();
631         m_physical_object->move(m.getTranslation(), m.getRotationDegrees());
632     }
633     else
634     {
635         m_physical_object->move(xyz, hpr);
636     }
637 }   // movePhysicalBodyToGraphicalNode
638 
639 // ----------------------------------------------------------------------------
getPosition() const640 const core::vector3df& TrackObject::getPosition() const
641 {
642     if (m_presentation != NULL)
643         return m_presentation->getPosition();
644     else
645         return m_init_xyz;
646 }   // getPosition
647 
648 // ----------------------------------------------------------------------------
649 
getAbsoluteCenterPosition() const650 const core::vector3df TrackObject::getAbsoluteCenterPosition() const
651 {
652     if (m_presentation != NULL)
653         return m_presentation->getAbsoluteCenterPosition();
654     else
655         return m_init_xyz;
656 }   // getAbsolutePosition
657 
658 // ----------------------------------------------------------------------------
659 
getAbsolutePosition() const660 const core::vector3df TrackObject::getAbsolutePosition() const
661 {
662     if (m_presentation != NULL)
663         return m_presentation->getAbsolutePosition();
664     else
665         return m_init_xyz;
666 }   // getAbsolutePosition
667 
668 // ----------------------------------------------------------------------------
669 
getRotation() const670 const core::vector3df& TrackObject::getRotation() const
671 {
672     if (m_presentation != NULL)
673         return m_presentation->getRotation();
674     else
675         return m_init_xyz;
676 }  // getRotation
677 
678 // ----------------------------------------------------------------------------
679 
getScale() const680 const core::vector3df& TrackObject::getScale() const
681 {
682     if (m_presentation != NULL)
683         return m_presentation->getScale();
684     else
685         return m_init_scale;
686 }   // getScale
687 
688 // ----------------------------------------------------------------------------
689 
addMovableChild(TrackObject * child)690 void TrackObject::addMovableChild(TrackObject* child)
691 {
692     if (!m_enabled)
693         child->setEnabled(false);
694     m_movable_children.push_back(child);
695 }
696 
697 // ----------------------------------------------------------------------------
698 
addChild(TrackObject * child)699 void TrackObject::addChild(TrackObject* child)
700 {
701     if (!m_enabled)
702         child->setEnabled(false);
703     m_children.push_back(child);
704 }
705 
706 // ----------------------------------------------------------------------------
707 
708 // scripting function
moveTo(const Scripting::SimpleVec3 * pos,bool isAbsoluteCoord)709 void TrackObject::moveTo(const Scripting::SimpleVec3* pos, bool isAbsoluteCoord)
710 {
711     move(core::vector3df(pos->getX(), pos->getY(), pos->getZ()),
712         core::vector3df(0.0f, 0.0f, 0.0f), // TODO: preserve rotation
713         core::vector3df(1.0f, 1.0f, 1.0f), // TODO: preserve scale
714         true, // updateRigidBody
715         isAbsoluteCoord);
716 }
717 
718 // ----------------------------------------------------------------------------
getMesh()719 scene::IAnimatedMeshSceneNode* TrackObject::getMesh()
720 {
721     if (getPresentation<TrackObjectPresentationLOD>())
722     {
723         LODNode* ln = dynamic_cast<LODNode*>
724             (getPresentation<TrackObjectPresentationLOD>()->getNode());
725         if (ln && !ln->getAllNodes().empty())
726         {
727             scene::IAnimatedMeshSceneNode* an =
728                 dynamic_cast<scene::IAnimatedMeshSceneNode*>
729                 (ln->getFirstNode());
730             if (an)
731             {
732                 return an;
733             }
734         }
735     }
736     else if (getPresentation<TrackObjectPresentationMesh>())
737     {
738         scene::IAnimatedMeshSceneNode* an =
739             dynamic_cast<scene::IAnimatedMeshSceneNode*>
740             (getPresentation<TrackObjectPresentationMesh>()->getNode());
741         if (an)
742         {
743             return an;
744         }
745     }
746     Log::debug("TrackObject", "No animated mesh");
747     return NULL;
748 }   // getMesh
749 
750 // ----------------------------------------------------------------------------
751 /* This function will join this (if true) static track object to main track
752  * model, the geometry creator in irrlicht will draw its physical shape to
753  * triangle mesh, so it can be combined to main track mesh.
754  * \return True if this track object is joinable and can be removed if needeed
755  */
joinToMainTrack()756 bool TrackObject::joinToMainTrack()
757 {
758     // If no physical object or there is animator, skip it
759     // Also no joining if will affect kart (like moveable, flatten...)
760     if (!isEnabled() || !m_physical_object || hasAnimatorRecursively() ||
761         m_physical_object->isDynamic() || m_physical_object->isCrashReset() ||
762         m_physical_object->isExplodeKartObject() ||
763         m_physical_object->isFlattenKartObject())
764         return false;
765 
766     // Scripting exploding barrel is assumed to be joinable in networking
767     // as it doesn't support it
768     if (!NetworkConfig::get()->isNetworking() &&
769         (!m_physical_object->getOnKartCollisionFunction().empty() ||
770         !m_physical_object->getOnItemCollisionFunction().empty()))
771         return false;
772 
773     // Skip driveable non-exact shape object
774     // Notice driveable object should always has exact shape specified in
775     // blender
776     if (m_is_driveable && !m_physical_object->hasTriangleMesh())
777         return false;
778 
779     m_physical_object->joinToMainTrack();
780     // This will remove the separated body
781     m_physical_object.reset();
782     return true;
783 }   // joinToMainTrack
784 
785 // ----------------------------------------------------------------------------
cloneToChild()786 TrackObject* TrackObject::cloneToChild()
787 {
788     // Only clone object that is enabled and has a physical object
789     // Soccer ball is made disabled by soccer world to hide initially
790     if ((isEnabled() || m_soccer_ball) && m_physical_object)
791     {
792         TrackObject* to_clone = new TrackObject(*this);
793         // We handle visibility condition in main process already
794         to_clone->m_visibility_condition.clear();
795         to_clone->m_presentation = NULL;
796         to_clone->m_render_info.reset();
797         if (m_animator)
798             to_clone->m_animator = m_animator->clone(to_clone);
799         to_clone->m_parent_library = NULL;
800         to_clone->m_movable_children.clear();
801         to_clone->m_children.clear();
802         to_clone->m_physical_object = m_physical_object->clone(to_clone);
803         // All track objects need to be initially enabled in init
804         to_clone->m_enabled = true;
805         return to_clone;
806     }
807     return NULL;
808 }   // joinToMainTrack
809