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