diff --git a/src/animations/animation_base.cpp b/src/animations/animation_base.cpp index 7638a19b5..a5b42da98 100644 --- a/src/animations/animation_base.cpp +++ b/src/animations/animation_base.cpp @@ -27,7 +27,6 @@ #include AnimationBase::AnimationBase(const XMLNode &node) - : TrackObject(node) { float fps=25; node.get("fps", &fps); @@ -43,12 +42,12 @@ AnimationBase::AnimationBase(const XMLNode &node) { m_playing = false; } - + reset(); } // AnimationBase // ---------------------------------------------------------------------------- /** Special constructor which takes one IPO (or curve). This is used by the */ -AnimationBase::AnimationBase(Ipo *ipo) : TrackObject() +AnimationBase::AnimationBase(Ipo *ipo) { m_anim_type = ATT_CYCLIC_ONCE; m_playing = true; @@ -83,8 +82,6 @@ void AnimationBase::reset() { curr->reset(); } - - TrackObject::reset(); } // reset // ---------------------------------------------------------------------------- @@ -95,12 +92,14 @@ void AnimationBase::reset() */ void AnimationBase::update(float dt, Vec3 *xyz, Vec3 *hpr, Vec3 *scale) { - TrackObject::update(dt); + assert(!isnan(m_current_time)); // Don't do anything if the animation is disabled if(!m_playing) return; m_current_time += dt; + assert(!isnan(m_current_time)); + Ipo* curr; for_in (curr, m_all_ipos) { diff --git a/src/animations/animation_base.hpp b/src/animations/animation_base.hpp index 7620840c9..ae20b0666 100644 --- a/src/animations/animation_base.hpp +++ b/src/animations/animation_base.hpp @@ -30,7 +30,6 @@ // Note that ipo.hpp is included here in order that PtrVector can call // the proper destructor! #include "animations/ipo.hpp" -#include "tracks/track_object.hpp" #include "utils/ptr_vector.hpp" #include @@ -41,7 +40,7 @@ class XMLNode; * \brief A base class for all animations. * \ingroup animations */ -class AnimationBase : public TrackObject +class AnimationBase { private: /** Two types of animations: cyclic ones that play all the time, and diff --git a/src/animations/billboard_animation.cpp b/src/animations/billboard_animation.cpp deleted file mode 100644 index 03d39475e..000000000 --- a/src/animations/billboard_animation.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "animations/billboard_animation.hpp" -#include "config/user_config.hpp" -#include "graphics/irr_driver.hpp" -#include "graphics/material.hpp" -#include "graphics/material_manager.hpp" -#include "io/file_manager.hpp" - -#include -#include -#include -#include - -class XMLNode; - -/** A 2d billboard animation. */ -BillboardAnimation::BillboardAnimation(const XMLNode &xml_node) - : AnimationBase(xml_node) -{ - std::string texture_name; - float width, height; - - xml_node.get("texture", &texture_name); - xml_node.get("width", &width ); - xml_node.get("height", &height ); - - m_fade_out_when_close = false; - xml_node.get("fadeout", &m_fade_out_when_close); - - if (m_fade_out_when_close) - { - xml_node.get("start", &m_fade_out_start); - xml_node.get("end", &m_fade_out_end ); - } - - video::ITexture *texture = - irr_driver->getTexture(file_manager->getTextureFile(texture_name)); - m_node = irr_driver->addBillboard(core::dimension2df(width, height), - texture); - Material *stk_material = material_manager->getMaterial(texture_name); - stk_material->setMaterialProperties(&(m_node->getMaterial(0)), NULL); - - m_node->setPosition(m_init_xyz); -} // BillboardAnimation - -// ---------------------------------------------------------------------------- -/** Update the animation, called one per time step. - * \param dt Time since last call. */ -void BillboardAnimation::update(float dt) -{ - //if ( UserConfigParams::m_graphical_effects ) - { - Vec3 xyz(m_node->getPosition()); - // Rotation doesn't make too much sense for a billboard, - // so just set it to 0 - Vec3 hpr(0, 0, 0); - Vec3 scale = m_node->getScale(); - AnimationBase::update(dt, &xyz, &hpr, &scale); - m_node->setPosition(xyz.toIrrVector()); - m_node->setScale(scale.toIrrVector()); - // Setting rotation doesn't make sense - } - - if (m_fade_out_when_close) - { - scene::ICameraSceneNode* curr_cam = irr_driver->getSceneManager()->getActiveCamera(); - const float dist = m_node->getPosition().getDistanceFrom( curr_cam->getPosition() ); - - scene::IBillboardSceneNode* node = (scene::IBillboardSceneNode*)m_node; - - if (dist < m_fade_out_start) - { - node->setColor(video::SColor(0, 255, 255, 255)); - } - else if (dist > m_fade_out_end) - { - node->setColor(video::SColor(255, 255, 255, 255)); - } - else - { - int a = (int)(255*(dist - m_fade_out_start) / (m_fade_out_end - m_fade_out_start)); - node->setColor(video::SColor(a, 255, 255, 255)); - } - } -} // update diff --git a/src/animations/billboard_animation.hpp b/src/animations/billboard_animation.hpp deleted file mode 100644 index a6a545c7b..000000000 --- a/src/animations/billboard_animation.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_BILLBOARD_ANIMATION_HPP -#define HEADER_BILLBOARD_ANIMATION_HPP - -#include - -#include "animations/animation_base.hpp" - -class XMLNode; - -/** - * \brief A 2d billboard animation. - * \ingroup animations - */ -class BillboardAnimation : public AnimationBase -{ - /** To create halo-like effects where the halo disapears when you get - close to it. Requested by samuncle */ - bool m_fade_out_when_close; - float m_fade_out_start; - float m_fade_out_end; - -public: - BillboardAnimation(const XMLNode &node); - virtual ~BillboardAnimation() {}; - virtual void update(float dt); - -}; // BillboardAnimation - -#endif - diff --git a/src/animations/ipo.cpp b/src/animations/ipo.cpp index 66973f587..95f5f9a0f 100644 --- a/src/animations/ipo.cpp +++ b/src/animations/ipo.cpp @@ -440,7 +440,8 @@ void Ipo::reset() * \param scale The scale that needs to be updated (can be NULL) */ void Ipo::update(float time, Vec3 *xyz, Vec3 *hpr,Vec3 *scale) -{ +{ + assert(!isnan(time)); switch(m_ipo_data->m_channel) { case Ipo::IPO_LOCX : if(xyz) xyz ->setX(get(time, 0)); break; @@ -474,6 +475,8 @@ void Ipo::update(float time, Vec3 *xyz, Vec3 *hpr,Vec3 *scale) */ float Ipo::get(float time, unsigned int index) const { + assert(!isnan(time)); + // Avoid crash in case that only one point is given for this IPO. if(m_next_n==0) return m_ipo_data->m_points[0][index]; @@ -489,5 +492,7 @@ float Ipo::get(float time, unsigned int index) const while(m_next_nm_points.size()-1 && time >=m_ipo_data->m_points[m_next_n].getW()) m_next_n++; - return m_ipo_data->get(time, index, m_next_n-1); + float rval = m_ipo_data->get(time, index, m_next_n-1); + assert(!isnan(rval)); + return rval; } // get diff --git a/src/animations/three_d_animation.cpp b/src/animations/three_d_animation.cpp index 8137d7bb9..a70407d73 100644 --- a/src/animations/three_d_animation.cpp +++ b/src/animations/three_d_animation.cpp @@ -29,15 +29,17 @@ #include "modes/world.hpp" #include "physics/kart_motion_state.hpp" #include "physics/physics.hpp" +#include "physics/physical_object.hpp" #include "physics/triangle_mesh.hpp" #include "tracks/bezier_curve.hpp" #include "utils/constants.hpp" #include #include -ThreeDAnimation::ThreeDAnimation(const XMLNode &node) - : AnimationBase(node) +ThreeDAnimation::ThreeDAnimation(const XMLNode &node, TrackObject* object) : AnimationBase(node) { + m_object = object; + m_crash_reset = false; m_explode_kart = false; node.get("reset", &m_crash_reset); @@ -45,259 +47,21 @@ ThreeDAnimation::ThreeDAnimation(const XMLNode &node) m_important_animation = (World::getWorld()->getIdent() == IDENT_CUSTSCENE); node.get("important", &m_important_animation); - - m_triangle_mesh = NULL; - - if (AnimationBase::m_node) - { - /** Save the initial position and rotation in the base animation object. */ - setInitialTransform(AnimationBase::m_node->getPosition(), - AnimationBase::m_node->getRotation() ); - m_hpr = AnimationBase::m_node->getRotation(); - } - else - m_hpr = m_init_hpr; - m_body = NULL; - m_motion_state = NULL; - m_collision_shape = NULL; - std::string shape; - node.get("shape", &shape); - if(shape!="") - { - createPhysicsBody(shape); - } - if (m_node == NULL) - { - m_node = irr_driver->getSceneManager()->addEmptySceneNode(); - } + /** Save the initial position and rotation in the base animation object. */ + setInitialTransform(object->getInitXYZ(), + object->getInitRotation() ); + m_hpr = object->getInitRotation(); + + assert(!isnan(m_hpr.getX())); + assert(!isnan(m_hpr.getY())); + assert(!isnan(m_hpr.getZ())); } // ThreeDAnimation -// ---------------------------------------------------------------------------- -/** Creates a bullet rigid body for this animated model. */ -void ThreeDAnimation::createPhysicsBody(const std::string &shape) -{ - if (m_interaction == "ghost" || m_node == NULL) return; - - // 1. Determine size of the object - // ------------------------------- - Vec3 extend = Vec3(1.0f, 1.0f, 1.0f); - - if (m_mesh != NULL) - { - Vec3 min, max; - MeshTools::minMax3D(m_mesh, &min, &max); - extend = max - min; - } - - if(shape=="box") - { - m_collision_shape = new btBoxShape(0.5*extend); - } - else if(shape=="coneX") - { - float radius = 0.5f*std::max(extend.getY(), extend.getZ()); - m_collision_shape = new btConeShapeX(radius, extend.getX()); - } - else if(shape=="coneY" || shape=="cone") - { - float radius = 0.5f*std::max(extend.getX(), extend.getZ()); - m_collision_shape = new btConeShape(radius, extend.getY()); - } - else if(shape=="coneZ") - { - // Note that the b3d model and therefore the extend has the - // irrlicht axis, i.e. Y and Z swapped. Also we need to - // convert - float radius = 0.5f*std::max(extend.getX(), extend.getY()); - m_collision_shape = new btConeShapeZ(radius, extend.getZ()); - } - else if(shape=="cylinderX") - { - m_collision_shape = new btCylinderShapeX(0.5f*extend); - } - else if(shape=="cylinderY") - { - m_collision_shape = new btCylinderShape(0.5f*extend); - } - else if(shape=="cylinderZ") - { - m_collision_shape = new btCylinderShapeZ(0.5f*extend); - } - else if(shape=="sphere") - { - float radius = std::max(extend.getX(), extend.getY()); - radius = 0.5f*std::max(radius, extend.getZ()); - m_collision_shape = new btSphereShape(radius); - } - else if(shape=="exact") - { - TriangleMesh* triangle_mesh = new TriangleMesh(); - - // In case of readonly materials we have to get the material from - // the mesh, otherwise from the node. This is esp. important for - // water nodes, which only have the material defined in the node, - // but not in the mesh at all! - bool is_readonly_material = false; - - scene::IMesh* mesh = NULL; - switch (m_node->getType()) - { - case scene::ESNT_MESH : - case scene::ESNT_WATER_SURFACE : - case scene::ESNT_OCTREE : - mesh = ((scene::IMeshSceneNode*)m_node)->getMesh(); - is_readonly_material = - ((scene::IMeshSceneNode*)m_node)->isReadOnlyMaterials(); - break; - case scene::ESNT_ANIMATED_MESH : - // for now just use frame 0 - mesh = ((scene::IAnimatedMeshSceneNode*)m_node)->getMesh()->getMesh(0); - is_readonly_material = - ((scene::IAnimatedMeshSceneNode*)m_node)->isReadOnlyMaterials(); - break; - default: - fprintf(stderr, "[3DAnimation] Unknown object type, cannot create exact collision body!\n"); - return; - } // switch node->getType() - - - //core::matrix4 mat; - //mat.setRotationDegrees(hpr); - //mat.setTranslation(pos); - //core::matrix4 mat_scale; - - // Note that we can't simply call mat.setScale, since this would - // overwrite the elements on the diagonal, making any rotation incorrect. - //mat_scale.setScale(scale); - //mat *= mat_scale; - - for(unsigned int i=0; igetMeshBufferCount(); i++) - { - scene::IMeshBuffer *mb = mesh->getMeshBuffer(i); - // FIXME: take translation/rotation into account - if (mb->getVertexType() != video::EVT_STANDARD && - mb->getVertexType() != video::EVT_2TCOORDS) - { - fprintf(stderr, "WARNING: ThreeDAnimation::createPhysicsBody: Ignoring type '%d'!\n", - mb->getVertexType()); - continue; - } - - // Handle readonly materials correctly: mb->getMaterial can return - // NULL if the node is not using readonly materials. E.g. in case - // of a water scene node, the mesh (which is the animated copy of - // the original mesh) does not contain any material information, - // the material is only available in the node. - const video::SMaterial &irrMaterial = - is_readonly_material ? mb->getMaterial() - : m_node->getMaterial(i); - video::ITexture* t=irrMaterial.getTexture(0); - - const Material* material=0; - TriangleMesh *tmesh = triangle_mesh; - if(t) - { - std::string image = std::string(core::stringc(t->getName()).c_str()); - material=material_manager->getMaterial(StringUtils::getBasename(image)); - if(material->isIgnore()) - continue; - } - - u16 *mbIndices = mb->getIndices(); - Vec3 vertices[3]; - Vec3 normals[3]; - - if (mb->getVertexType() == video::EVT_STANDARD) - { - irr::video::S3DVertex* mbVertices=(video::S3DVertex*)mb->getVertices(); - for(unsigned int j=0; jgetIndexCount(); j+=3) - { - for(unsigned int k=0; k<3; k++) - { - int indx=mbIndices[j+k]; - core::vector3df v = mbVertices[indx].Pos; - //mat.transformVect(v); - vertices[k]=v; - normals[k]=mbVertices[indx].Normal; - } // for k - if(tmesh) tmesh->addTriangle(vertices[0], vertices[1], - vertices[2], normals[0], - normals[1], normals[2], - material ); - } // for j - } - else - { - if (mb->getVertexType() == video::EVT_2TCOORDS) - { - irr::video::S3DVertex2TCoords* mbVertices = (video::S3DVertex2TCoords*)mb->getVertices(); - for(unsigned int j=0; jgetIndexCount(); j+=3) - { - for(unsigned int k=0; k<3; k++) - { - int indx=mbIndices[j+k]; - core::vector3df v = mbVertices[indx].Pos; - //mat.transformVect(v); - vertices[k]=v; - normals[k]=mbVertices[indx].Normal; - } // for k - if(tmesh) tmesh->addTriangle(vertices[0], vertices[1], - vertices[2], normals[0], - normals[1], normals[2], - material ); - } // for j - - } - } - } // for icreateCollisionShape(); - m_collision_shape = &triangle_mesh->getCollisionShape(); - m_triangle_mesh = triangle_mesh; - } - else - { - fprintf(stderr, "[3DAnimation] WARNING: Shape '%s' is not supported, ignored.\n", shape.c_str()); - return; - } - const core::vector3df &hpr = m_node->getRotation()*DEGREE_TO_RAD; - btQuaternion q(hpr.X, hpr.Y, hpr.Z); - const core::vector3df &xyz=m_node->getPosition(); - Vec3 p(xyz); - btTransform trans(q,p); - m_motion_state = new KartMotionState(trans); - btRigidBody::btRigidBodyConstructionInfo info(0, m_motion_state, - m_collision_shape); - - m_body = new btRigidBody(info); - m_user_pointer.set(this); - m_body->setUserPointer(&m_user_pointer); - World::getWorld()->getPhysics()->addBody(m_body); - m_body->setCollisionFlags( m_body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT); - m_body->setActivationState(DISABLE_DEACTIVATION); -} // createPhysicsBody - // ---------------------------------------------------------------------------- /** Destructor. */ ThreeDAnimation::~ThreeDAnimation() { - if(m_body) - { - World::getWorld()->getPhysics()->removeBody(m_body); - delete m_body; - delete m_motion_state; - // If an exact shape was used, the collision shape pointer - // here is a copy of the collision shape pointer in the - // triangle mesh. In order to avoid double-freeing this - // pointer, we don't free the pointer in this case. - if(!m_triangle_mesh) - delete m_collision_shape; - } - - if (m_triangle_mesh) - { - delete m_triangle_mesh; - } } // ~ThreeDAnimation // ---------------------------------------------------------------------------- @@ -308,12 +72,13 @@ void ThreeDAnimation::update(float dt) { //if ( UserConfigParams::m_graphical_effects || m_important_animation ) { - Vec3 xyz = m_node->getPosition(); - Vec3 scale = m_node->getScale(); + Vec3 xyz = m_object->getPosition(); + Vec3 scale = m_object->getScale(); AnimationBase::update(dt, &xyz, &m_hpr, &scale); //updates all IPOs - m_node->setPosition(xyz.toIrrVector()); - m_node->setScale(scale.toIrrVector()); + //m_node->setPosition(xyz.toIrrVector()); + //m_node->setScale(scale.toIrrVector()); + // Note that the rotation order of irrlicht is different from the one // in blender. So in order to reproduce the blender IPO rotations // correctly, we have to get the rotations around each axis and combine @@ -321,6 +86,9 @@ void ThreeDAnimation::update(float dt) core::matrix4 m; m.makeIdentity(); core::matrix4 mx; + assert(!isnan(m_hpr.getX())); + assert(!isnan(m_hpr.getY())); + assert(!isnan(m_hpr.getZ())); mx.setRotationDegrees(core::vector3df(m_hpr.getX(), 0, 0)); core::matrix4 my; my.setRotationDegrees(core::vector3df(0, m_hpr.getY(), 0)); @@ -328,30 +96,11 @@ void ThreeDAnimation::update(float dt) mz.setRotationDegrees(core::vector3df(0, 0, m_hpr.getZ())); m = my*mz*mx; core::vector3df hpr = m.getRotationDegrees(); - m_node->setRotation(hpr); + //m_node->setRotation(hpr); - // Now update the position of the bullet body if there is one: - if(m_body) + if (m_object) { - Vec3 hpr2(hpr); - hpr2.degreeToRad(); - btQuaternion q; - - core::matrix4 mat; - mat.setRotationDegrees(hpr); - - irr::core::quaternion tempQuat(mat); - q = btQuaternion(-tempQuat.X, -tempQuat.Y, -tempQuat.Z, tempQuat.W); - - - Vec3 p(xyz); - btTransform trans(q,p); - m_motion_state->setWorldTransform(trans); - } - - if (m_sound != NULL) - { - m_sound->position(xyz); + m_object->move(xyz.toIrrVector(), hpr, scale.toIrrVector()); } } } // update diff --git a/src/animations/three_d_animation.hpp b/src/animations/three_d_animation.hpp index d0436a860..ecf44d256 100644 --- a/src/animations/three_d_animation.hpp +++ b/src/animations/three_d_animation.hpp @@ -29,6 +29,12 @@ using namespace irr; #include "animations/animation_base.hpp" #include "physics/user_pointer.hpp" +namespace irr +{ + namespace scene { class IAnimatedMesh; class ISceneNode; class IMesh; } +} + +class TrackObject; class BezierCurve; class XMLNode; @@ -38,21 +44,8 @@ class XMLNode; class ThreeDAnimation : public AnimationBase { private: - /** The bullet collision shape for the physics. */ - btCollisionShape *m_collision_shape; - - /** The bullet rigid body. */ - btRigidBody *m_body; - - /** Motion state of the physical object. */ - btMotionState *m_motion_state; - - /** A user pointer to connect a bullet body with this object. */ - UserPointer m_user_pointer; - - /** Non-null only if the shape is exact */ - TriangleMesh *m_triangle_mesh; - + TrackObject *m_object; + /** True if a collision with this object should trigger * rescuing a kart. */ bool m_crash_reset; @@ -71,10 +64,10 @@ private: */ bool m_important_animation; - void createPhysicsBody(const std::string &shape); + //scene::ISceneNode* m_node; public: - ThreeDAnimation(const XMLNode &node); + ThreeDAnimation(const XMLNode &node, TrackObject* object); virtual ~ThreeDAnimation(); virtual void update(float dt); // ------------------------------------------------------------------------ diff --git a/src/io/xml_node.cpp b/src/io/xml_node.cpp index d18d70d2b..21dcaf85d 100644 --- a/src/io/xml_node.cpp +++ b/src/io/xml_node.cpp @@ -589,3 +589,12 @@ int XMLNode::getHPR(Vec3 *value) const } // getHPR Vec3 // ---------------------------------------------------------------------------- + +bool XMLNode::hasChildNamed(const char* name) const +{ + for (unsigned int i = 0; i < m_nodes.size(); i++) + { + if (m_nodes[i]->getName() == name) return true; + } + return false; +} \ No newline at end of file diff --git a/src/io/xml_node.hpp b/src/io/xml_node.hpp index e980268ef..5a2cd0eaf 100644 --- a/src/io/xml_node.hpp +++ b/src/io/xml_node.hpp @@ -100,6 +100,9 @@ public: int getXYZ(Vec3 *vaslue) const; int getHPR(core::vector3df *value) const; int getHPR(Vec3 *value) const; + + bool hasChildNamed(const char* name) const; + /** Handy functions to test the bit pattern returned by get(vector3df*).*/ static bool hasX(int b) { return (b&1)==1; } static bool hasY(int b) { return (b&2)==2; } diff --git a/src/modes/cutscene_world.cpp b/src/modes/cutscene_world.cpp index 5ded32085..d9be111f0 100644 --- a/src/modes/cutscene_world.cpp +++ b/src/modes/cutscene_world.cpp @@ -22,6 +22,7 @@ #include #include "animations/animation_base.hpp" +#include "animations/three_d_animation.hpp" #include "audio/music_manager.hpp" #include "challenges/game_slot.hpp" #include "challenges/unlock_manager.hpp" @@ -82,9 +83,10 @@ void CutsceneWorld::init() TrackObject* curr; for_in(curr, objects) { - if (curr->getType() == "particle-emitter" && !curr->getTriggerCondition().empty()) + if (curr->getType() == "particle-emitter" && + !curr->getPresentation()->getTriggerCondition().empty()) { - const std::string& condition = curr->getTriggerCondition(); + const std::string& condition = curr->getPresentation()->getTriggerCondition(); if (StringUtils::startsWith(condition, "frame ")) { @@ -102,9 +104,9 @@ void CutsceneWorld::init() m_particles_to_trigger[frame / FPS].push_back(curr); } } - else if (curr->getType() == "sfx-emitter" && !curr->getTriggerCondition().empty()) + else if (curr->getType() == "sfx-emitter" && !curr->getPresentation()->getTriggerCondition().empty()) { - const std::string& condition = curr->getTriggerCondition(); + const std::string& condition = curr->getPresentation()->getTriggerCondition(); if (StringUtils::startsWith(condition, "frame ")) { @@ -135,15 +137,14 @@ void CutsceneWorld::init() float FPS = 25.0f; // for now we assume the cutscene is saved at 25 FPS m_sounds_to_stop[frame / FPS].push_back(curr); - curr->triggerSound(true); + curr->getPresentation()->triggerSound(true); } } - if (dynamic_cast(curr) != NULL) + if (curr->getAnimator() != NULL) { m_duration = std::max(m_duration, - (double)dynamic_cast(curr) - ->getAnimationDuration()); + (double)curr->getAnimator()->getAnimationDuration()); } } @@ -273,10 +274,11 @@ void CutsceneWorld::update(float dt) { if (curr->getType() == "cutscene_camera") { - m_camera->setPosition(curr->getNode()->getPosition()); + scene::ISceneNode* anchorNode = curr->getPresentation()->getNode(); + m_camera->setPosition(anchorNode->getPosition()); m_camera->updateAbsolutePosition(); - core::vector3df rot = curr->getNode()->getRotation(); + core::vector3df rot = anchorNode->getRotation(); Vec3 rot2(rot); rot2.setPitch(rot2.getPitch() + 90.0f); m_camera->setRotation(rot2.toIrrVector()); @@ -299,7 +301,7 @@ void CutsceneWorld::update(float dt) std::vector objects = it->second; for (unsigned int i = 0; i < objects.size(); i++) { - objects[i]->triggerSound(); + objects[i]->getPresentation()->triggerSound(false); } m_sounds_to_trigger.erase(it++); } @@ -317,7 +319,7 @@ void CutsceneWorld::update(float dt) std::vector objects = it->second; for (unsigned int i = 0; i < objects.size(); i++) { - objects[i]->triggerParticles(); + objects[i]->getPresentation()->triggerParticles(); } m_particles_to_trigger.erase(it++); } @@ -334,7 +336,7 @@ void CutsceneWorld::update(float dt) std::vector objects = it->second; for (unsigned int i = 0; i < objects.size(); i++) { - objects[i]->stopSound(); + objects[i]->getPresentation()->stopSound(); } m_sounds_to_stop.erase(it++); } diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 9dfddfde5..e707087e3 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -123,7 +123,7 @@ void ThreeStrikesBattle::reset() evt.m_kart_info = m_kart_info; m_battle_events.push_back(evt); - PhysicalObject *obj; + TrackObject *obj; for_in(obj, m_tires) { m_track->getTrackObjectManager()->removeObject(obj); @@ -330,6 +330,8 @@ void ThreeStrikesBattle::update(float dt) tire = m_tire_dir+"/wheel-rear-right.b3d"; } +// TODO: add back tires +#if 0 TrackObjectManager* tom = m_track->getTrackObjectManager(); PhysicalObject* obj = tom->insertObject(tire, @@ -349,6 +351,7 @@ void ThreeStrikesBattle::update(float dt) m_insert_tire = 0; m_tires.push_back(obj); +#endif } } // update diff --git a/src/modes/three_strikes_battle.hpp b/src/modes/three_strikes_battle.hpp index 32280fdf0..267694333 100644 --- a/src/modes/three_strikes_battle.hpp +++ b/src/modes/three_strikes_battle.hpp @@ -19,7 +19,9 @@ #ifndef THREE_STRIKES_HPP #define THREE_STRIKES_HPP + #include "modes/world_with_rank.hpp" +#include "tracks/track_object.hpp" #include "states_screens/race_gui_base.hpp" #include @@ -68,7 +70,7 @@ private: /** A rotation to apply to the tires when inserting them. */ float m_tire_rotation; - PtrVector m_tires; + PtrVector m_tires; public: diff --git a/src/modes/world.cpp b/src/modes/world.cpp index b76421b87..5e5b8ee49 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -746,6 +746,14 @@ void World::updateWorld(float dt) #define MEASURE_FPS 0 +//----------------------------------------------------------------------------- + +void World::scheduleTutorial() +{ + m_schedule_exit_race = true; + m_schedule_tutorial = true; +} + //----------------------------------------------------------------------------- /** Updates the physics, all karts, the track, and projectile manager. * \param dt Time step size. diff --git a/src/modes/world.hpp b/src/modes/world.hpp index b2937336f..0de8d24c6 100644 --- a/src/modes/world.hpp +++ b/src/modes/world.hpp @@ -252,8 +252,7 @@ public: void schedulePause(Phase phase); void scheduleUnpause(); void scheduleExitRace() { m_schedule_exit_race = true; } - void scheduleTutorial() { m_schedule_exit_race = true; - m_schedule_tutorial = true; } + void scheduleTutorial(); void updateWorld(float dt); void handleExplosion(const Vec3 &xyz, AbstractKart *kart_hit, PhysicalObject *object); diff --git a/src/physics/physical_object.cpp b/src/physics/physical_object.cpp index 16b527179..8d7acafd2 100644 --- a/src/physics/physical_object.cpp +++ b/src/physics/physical_object.cpp @@ -24,11 +24,13 @@ using namespace irr; #include "graphics/irr_driver.hpp" +#include "graphics/material_manager.hpp" #include "graphics/mesh_tools.hpp" #include "io/file_manager.hpp" #include "io/xml_node.hpp" #include "modes/world.hpp" #include "physics/physics.hpp" +#include "physics/triangle_mesh.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" #include "utils/string_utils.hpp" @@ -37,8 +39,9 @@ using namespace irr; #include // ---------------------------------------------------------------------------- -PhysicalObject::PhysicalObject(const XMLNode &xml_node) - : TrackObject(xml_node) + +PhysicalObject::PhysicalObject(bool kinetic, const XMLNode &xml_node, + scene::ISceneNode* scenenode) { m_shape = NULL; m_body = NULL; @@ -50,6 +53,12 @@ PhysicalObject::PhysicalObject(const XMLNode &xml_node) m_crash_reset = false; m_explode_kart = false; + m_node = scenenode; + + m_init_xyz = scenenode->getPosition(); + m_init_hpr = scenenode->getRotation(); + m_init_scale = scenenode->getScale(); + std::string shape; xml_node.get("mass", &m_mass ); xml_node.get("radius", &m_radius ); @@ -71,36 +80,31 @@ PhysicalObject::PhysicalObject(const XMLNode &xml_node) else if(shape=="box" ) m_body_type = MP_BOX; else if(shape=="sphere" ) m_body_type = MP_SPHERE; + else if(shape=="exact") m_body_type = MP_EXACT; + else fprintf(stderr, "Unknown shape type : %s\n", shape.c_str()); - + m_init_pos.setIdentity(); - Vec3 hpr(m_init_hpr); - hpr.degreeToRad(); + Vec3 radHpr(m_init_hpr); + radHpr.degreeToRad(); btQuaternion q; - q.setEuler(hpr.getY(), hpr.getX(), hpr.getZ()); + q.setEuler(radHpr.getY(),radHpr.getX(), radHpr.getZ()); m_init_pos.setRotation(q); Vec3 init_xyz(m_init_xyz); m_init_pos.setOrigin(init_xyz); - - if (m_node == NULL) - { - std::string model_name; - xml_node.get("model", &model_name ); - fprintf(stderr, - "[PhysicalObject] WARNING, could not locate model '%s'\n", - model_name.c_str()); - } + m_kinetic = kinetic; + + init(); } // PhysicalObject // ---------------------------------------------------------------------------- - +/* PhysicalObject::PhysicalObject(const std::string& model, bodyTypes shape, float mass, float radius, const core::vector3df& hpr, const core::vector3df& pos, const core::vector3df& scale) - : TrackObject(pos, hpr, scale, model) { m_body_type = shape; m_mass = mass; @@ -114,16 +118,48 @@ PhysicalObject::PhysicalObject(const std::string& model, m_init_pos.setRotation(q); m_init_pos.setOrigin(btVector3(pos.X, pos.Y, pos.Z)); } - +*/ + // ---------------------------------------------------------------------------- PhysicalObject::~PhysicalObject() { World::getWorld()->getPhysics()->removeBody(m_body); delete m_body; delete m_motion_state; - delete m_shape; + + // If an exact shape was used, the collision shape pointer + // here is a copy of the collision shape pointer in the + // triangle mesh. In order to avoid double-freeing this + // pointer, we don't free the pointer in this case. + if (!m_triangle_mesh) + delete m_shape; + + if (m_triangle_mesh) + { + delete m_triangle_mesh; + } } // ~PhysicalObject +// ---------------------------------------------------------------------------- + +void PhysicalObject::move(const Vec3& xyz, const core::vector3df& hpr) +{ + Vec3 hpr2(hpr); + hpr2.degreeToRad(); + btQuaternion q; + + core::matrix4 mat; + mat.setRotationDegrees(hpr); + + irr::core::quaternion tempQuat(mat); + q = btQuaternion(-tempQuat.X, -tempQuat.Y, -tempQuat.Z, tempQuat.W); + + + Vec3 p(xyz); + btTransform trans(q,p); + m_motion_state->setWorldTransform(trans); +} + // ---------------------------------------------------------------------------- /** Additional initialisation after loading of the model is finished. */ @@ -187,57 +223,197 @@ void PhysicalObject::init() m_graphical_offset = -0.5f*(max+min); switch (m_body_type) { - case MP_CONE_Y: { - if(m_radius<0) m_radius = 0.5f*extend.length_2d(); - m_shape = new btConeShape(m_radius, extend.getY()); - break; - } - case MP_CONE_X: { - if(m_radius<0) - m_radius = 0.5f*sqrt(extend.getY()*extend.getY() + - extend.getZ()*extend.getZ()); - m_shape = new btConeShapeX(m_radius, extend.getY()); - break; - } - case MP_CONE_Z: { - if(m_radius<0) - m_radius = 0.5f*sqrt(extend.getX()*extend.getX() + - extend.getY()*extend.getY()); - m_shape = new btConeShapeZ(m_radius, extend.getY()); - break; - } - case MP_CYLINDER_Y: { - if(m_radius<0) m_radius = 0.5f*extend.length_2d(); - m_shape = new btCylinderShape(0.5f*extend); - break; - } - case MP_CYLINDER_X: { - if(m_radius<0) - m_radius = 0.5f*sqrt(extend.getY()*extend.getY() + - extend.getZ()*extend.getZ()); - m_shape = new btCylinderShapeX(0.5f*extend); - break; - } - case MP_CYLINDER_Z: { - if(m_radius<0) - m_radius = 0.5f*sqrt(extend.getX()*extend.getX() + - extend.getY()*extend.getY()); - m_shape = new btCylinderShapeZ(0.5f*extend); - break; - } - case MP_BOX: m_shape = new btBoxShape(0.5*extend); - break; - case MP_SPHERE: { - if(m_radius<0) + case MP_CONE_Y: + { + if (m_radius < 0) m_radius = 0.5f*extend.length_2d(); + m_shape = new btConeShape(m_radius, extend.getY()); + break; + } + case MP_CONE_X: + { + if (m_radius < 0) + m_radius = 0.5f*sqrt(extend.getY()*extend.getY() + + extend.getZ()*extend.getZ()); + m_shape = new btConeShapeX(m_radius, extend.getY()); + break; + } + case MP_CONE_Z: + { + if (m_radius < 0) + m_radius = 0.5f*sqrt(extend.getX()*extend.getX() + + extend.getY()*extend.getY()); + m_shape = new btConeShapeZ(m_radius, extend.getY()); + break; + } + case MP_CYLINDER_Y: + { + if (m_radius < 0) m_radius = 0.5f*extend.length_2d(); + m_shape = new btCylinderShape(0.5f*extend); + break; + } + case MP_CYLINDER_X: + { + if (m_radius < 0) + m_radius = 0.5f*sqrt(extend.getY()*extend.getY() + + extend.getZ()*extend.getZ()); + m_shape = new btCylinderShapeX(0.5f*extend); + break; + } + case MP_CYLINDER_Z: + { + if (m_radius < 0) + m_radius = 0.5f*sqrt(extend.getX()*extend.getX() + + extend.getY()*extend.getY()); + m_shape = new btCylinderShapeZ(0.5f*extend); + break; + } + case MP_SPHERE: + { + if(m_radius<0) + { + m_radius = std::max(extend.getX(), extend.getY()); + m_radius = 0.5f*std::max(m_radius, extend.getZ()); + } + m_shape = new btSphereShape(m_radius); + break; + } + case MP_EXACT: + { + TriangleMesh* triangle_mesh = new TriangleMesh(); + + // In case of readonly materials we have to get the material from + // the mesh, otherwise from the node. This is esp. important for + // water nodes, which only have the material defined in the node, + // but not in the mesh at all! + bool is_readonly_material = false; + + scene::IMesh* mesh = NULL; + switch (m_node->getType()) + { + case scene::ESNT_MESH : + case scene::ESNT_WATER_SURFACE : + case scene::ESNT_OCTREE : + mesh = ((scene::IMeshSceneNode*)m_node)->getMesh(); + is_readonly_material = + ((scene::IMeshSceneNode*)m_node)->isReadOnlyMaterials(); + break; + case scene::ESNT_ANIMATED_MESH : + // for now just use frame 0 + mesh = ((scene::IAnimatedMeshSceneNode*)m_node)->getMesh()->getMesh(0); + is_readonly_material = + ((scene::IAnimatedMeshSceneNode*)m_node)->isReadOnlyMaterials(); + break; + default: + fprintf(stderr, "[3DAnimation] Unknown object type, cannot create exact collision body!\n"); + return; + } // switch node->getType() + + + //core::matrix4 mat; + //mat.setRotationDegrees(hpr); + //mat.setTranslation(pos); + //core::matrix4 mat_scale; + + // Note that we can't simply call mat.setScale, since this would + // overwrite the elements on the diagonal, making any rotation incorrect. + //mat_scale.setScale(scale); + //mat *= mat_scale; + + for(unsigned int i=0; igetMeshBufferCount(); i++) + { + scene::IMeshBuffer *mb = mesh->getMeshBuffer(i); + // FIXME: take translation/rotation into account + if (mb->getVertexType() != video::EVT_STANDARD && + mb->getVertexType() != video::EVT_2TCOORDS) + { + fprintf(stderr, "WARNING: ThreeDAnimation::createPhysicsBody: Ignoring type '%d'!\n", + mb->getVertexType()); + continue; + } + + // Handle readonly materials correctly: mb->getMaterial can return + // NULL if the node is not using readonly materials. E.g. in case + // of a water scene node, the mesh (which is the animated copy of + // the original mesh) does not contain any material information, + // the material is only available in the node. + const video::SMaterial &irrMaterial = + is_readonly_material ? mb->getMaterial() + : m_node->getMaterial(i); + video::ITexture* t=irrMaterial.getTexture(0); + + const Material* material=0; + TriangleMesh *tmesh = triangle_mesh; + if(t) + { + std::string image = std::string(core::stringc(t->getName()).c_str()); + material=material_manager->getMaterial(StringUtils::getBasename(image)); + if(material->isIgnore()) + continue; + } + + u16 *mbIndices = mb->getIndices(); + Vec3 vertices[3]; + Vec3 normals[3]; + + if (mb->getVertexType() == video::EVT_STANDARD) + { + irr::video::S3DVertex* mbVertices=(video::S3DVertex*)mb->getVertices(); + for(unsigned int j=0; jgetIndexCount(); j+=3) + { + for(unsigned int k=0; k<3; k++) { - m_radius = std::max(extend.getX(), extend.getY()); - m_radius = 0.5f*std::max(m_radius, extend.getZ()); - } - m_shape = new btSphereShape(m_radius); - break; - } - case MP_NONE: fprintf(stderr, "WARNING: Uninitialised moving shape\n"); - break; + int indx=mbIndices[j+k]; + core::vector3df v = mbVertices[indx].Pos; + //mat.transformVect(v); + vertices[k]=v; + normals[k]=mbVertices[indx].Normal; + } // for k + if(tmesh) tmesh->addTriangle(vertices[0], vertices[1], + vertices[2], normals[0], + normals[1], normals[2], + material ); + } // for j + } + else + { + if (mb->getVertexType() == video::EVT_2TCOORDS) + { + irr::video::S3DVertex2TCoords* mbVertices = (video::S3DVertex2TCoords*)mb->getVertices(); + for(unsigned int j=0; jgetIndexCount(); j+=3) + { + for(unsigned int k=0; k<3; k++) + { + int indx=mbIndices[j+k]; + core::vector3df v = mbVertices[indx].Pos; + //mat.transformVect(v); + vertices[k]=v; + normals[k]=mbVertices[indx].Normal; + } // for k + if(tmesh) tmesh->addTriangle(vertices[0], vertices[1], + vertices[2], normals[0], + normals[1], normals[2], + material ); + } // for j + + } + } + + } // for icreateCollisionShape(); + m_shape = &triangle_mesh->getCollisionShape(); + m_triangle_mesh = triangle_mesh; + + break; + } + case MP_NONE: + default: + fprintf(stderr, "WARNING: Uninitialised moving shape\n"); + // intended fall-through + case MP_BOX: + { + m_shape = new btBoxShape(0.5*extend); + break; + } } // 2. Create the rigid object @@ -258,11 +434,19 @@ void PhysicalObject::init() m_body->setUserPointer(&m_user_pointer); World::getWorld()->getPhysics()->addBody(m_body); + + if (!m_kinetic) + { + m_body->setCollisionFlags( m_body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT); + m_body->setActivationState(DISABLE_DEACTIVATION); + } } // init // ---------------------------------------------------------------------------- void PhysicalObject::update(float dt) { + if (!m_kinetic) return; + btTransform t; m_motion_state->getWorldTransform(t); diff --git a/src/physics/physical_object.hpp b/src/physics/physical_object.hpp index dfcb7ec74..cfbade5b2 100644 --- a/src/physics/physical_object.hpp +++ b/src/physics/physical_object.hpp @@ -26,22 +26,35 @@ #include "physics/user_pointer.hpp" #include "tracks/track_object.hpp" #include "utils/vec3.hpp" +#include "utils/leak_check.hpp" class XMLNode; /** * \ingroup physics */ -class PhysicalObject : public TrackObject +class PhysicalObject { public: /** The supported collision shapes. */ enum bodyTypes {MP_NONE, MP_CONE_Y, MP_CONE_X, MP_CONE_Z, MP_CYLINDER_Y, MP_CYLINDER_X, MP_CYLINDER_Z, - MP_BOX, MP_SPHERE}; + MP_BOX, MP_SPHERE, MP_EXACT}; private: + + /** The initial XYZ position of the object. */ + core::vector3df m_init_xyz; + + /** The initial hpr of the object. */ + core::vector3df m_init_hpr; + + /** The initial scale of the object. */ + core::vector3df m_init_scale; + + scene::ISceneNode* m_node; + /** The shape of this object. */ bodyTypes m_body_type; @@ -90,20 +103,31 @@ private: /** If m_reset_when_too_low this object is set back to its start * position if its height is below this value. */ float m_reset_height; -public: - PhysicalObject (const XMLNode &node); + bool m_kinetic; + + /** Non-null only if the shape is exact */ + TriangleMesh *m_triangle_mesh; + +public: + PhysicalObject(bool kinetic, const XMLNode &node, + scene::ISceneNode* scenenode); + + /* PhysicalObject(const std::string& model, bodyTypes shape, float mass, float radius, const core::vector3df& hpr, const core::vector3df& pos, const core::vector3df& scale); + */ virtual ~PhysicalObject (); virtual void reset (); virtual void handleExplosion(const Vec3& pos, bool directHit); void update (float dt); void init (); + bool isKinetic () const { return m_kinetic; } + // ------------------------------------------------------------------------ /** Returns the rigid body of this physical object. */ btRigidBody *getBody () { return m_body; } @@ -112,6 +136,10 @@ public: * hits it. */ bool isCrashReset() const { return m_crash_reset; } bool isExplodeKartObject () const { return m_explode_kart; } + + void move(const Vec3& xyz, const core::vector3df& hpr); + + LEAK_CHECK() }; // PhysicalObject #endif diff --git a/src/tracks/check_goal.cpp b/src/tracks/check_goal.cpp index c36140fa1..0b96c6084 100644 --- a/src/tracks/check_goal.cpp +++ b/src/tracks/check_goal.cpp @@ -66,12 +66,12 @@ void CheckGoal::update(float dt) if(!obj->isSoccerBall()) continue; - const Vec3 &xyz = obj->getNode()->getPosition(); + const Vec3 &xyz = obj->getPresentation()->getNode()->getPosition(); if(isTriggered(m_previous_position[ball_index], xyz, ball_index)) { if(UserConfigParams::m_check_debug) printf("CHECK: Goal check structure %d triggered for object %s.\n", - m_index, obj->getDebugName()); + m_index, obj->getPresentation()->getNode()->getDebugName()); trigger(ball_index); } m_previous_position[ball_index] = xyz; @@ -122,7 +122,7 @@ void CheckGoal::reset(const Track &track) if(!obj->isSoccerBall()) continue; - const Vec3 &xyz = obj->getNode()->getPosition(); + const Vec3 &xyz = obj->getPresentation()->getNode()->getPosition(); m_previous_position.push_back(xyz); } diff --git a/src/tracks/track_object.cpp b/src/tracks/track_object.cpp index ed44d39d8..467482b21 100644 --- a/src/tracks/track_object.cpp +++ b/src/tracks/track_object.cpp @@ -18,11 +18,13 @@ #include "tracks/track_object.hpp" +#include "animations/three_d_animation.hpp" #include "audio/sfx_base.hpp" #include "audio/sfx_buffer.hpp" #include "audio/sfx_manager.hpp" #include "graphics/irr_driver.hpp" #include "graphics/lod_node.hpp" +#include "graphics/material_manager.hpp" #include "graphics/particle_emitter.hpp" #include "graphics/particle_kind_manager.hpp" #include "io/file_manager.hpp" @@ -31,6 +33,7 @@ #include "items/item_manager.hpp" #include "modes/overworld.hpp" #include "modes/world.hpp" +#include "physics/physical_object.hpp" #include "states_screens/dialogs/tutorial_message_dialog.hpp" #include "states_screens/dialogs/race_paused_dialog.hpp" #include "states_screens/main_menu_screen.hpp" @@ -39,346 +42,161 @@ #include #include #include +#include +#include -/** A track object: any additional object on the track. This object implements - * a graphics-only representation, i.e. there is no physical representation. - * Derived classes can implement a physical representation (see - * physics/physical_object) or animations. - * \param xml_node The xml node from which the initial data is taken. This is - * for now: initial position, initial rotation, name of the - * model, enable/disable status, timer information. - */ -TrackObject::TrackObject(const XMLNode &xml_node) +// ---------------------------------------------------------------------------- + +TrackObjectPresentation::TrackObjectPresentation(const XMLNode& xml_node) { m_init_xyz = core::vector3df(0,0,0); m_init_hpr = core::vector3df(0,0,0); m_init_scale = core::vector3df(1,1,1); - m_enabled = true; - m_is_looped = false; - m_sound = NULL; - m_mesh = NULL; - m_node = NULL; - m_emitter = NULL; - m_lod_emitter_node = NULL; - + xml_node.get("xyz", &m_init_xyz ); xml_node.get("hpr", &m_init_hpr ); xml_node.get("scale", &m_init_scale); - xml_node.get("enabled", &m_enabled ); +} + + +// ---------------------------------------------------------------------------- + + +const core::vector3df& TrackObjectPresentationSceneNode::getPosition() const +{ + return m_node->getPosition(); +} + +const core::vector3df& TrackObjectPresentationSceneNode::getRotation() const +{ + return m_node->getRotation(); +} + +const core::vector3df& TrackObjectPresentationSceneNode::getScale() const +{ + return m_node->getScale(); +} + + +void TrackObjectPresentationSceneNode::move(const core::vector3df& xyz, const core::vector3df& hpr, + const core::vector3df& scale) +{ + m_node->setPosition(xyz); + m_node->setRotation(hpr); + m_node->setScale(scale); + + //if (dynamic_cast(this) != NULL) +} + +void TrackObjectPresentationSceneNode::setEnable(bool enabled) +{ + m_node->setVisible(enabled); +} + +void TrackObjectPresentationSceneNode::reset() +{ + m_node->setPosition(m_init_xyz); + m_node->setRotation(m_init_hpr); + m_node->setScale(m_init_scale); +} + +// ---------------------------------------------------------------------------- + +TrackObjectPresentationEmpty::TrackObjectPresentationEmpty(const XMLNode& xml_node) : + TrackObjectPresentationSceneNode(xml_node) +{ + m_node = irr_driver->getSceneManager()->addEmptySceneNode(); + m_node->setPosition(m_init_xyz); + m_node->setRotation(m_init_hpr); + m_node->setScale(m_init_scale); +} + +TrackObjectPresentationEmpty::~TrackObjectPresentationEmpty() +{ + irr_driver->removeNode(m_node); +} + +// ---------------------------------------------------------------------------- + +TrackObjectPresentationMesh::TrackObjectPresentationMesh(const XMLNode& xml_node, bool enabled) : + TrackObjectPresentationSceneNode(xml_node) +{ + m_is_looped = false; + m_mesh = NULL; + m_node = NULL; + xml_node.get("looped", &m_is_looped ); std::string model_name; xml_node.get("model", &model_name ); - std::string sound; - xml_node.get("sound", &sound ); - - m_interaction = "static"; - xml_node.get("interaction", &m_interaction); - xml_node.get("lod_group", &m_lod_group); - m_soccer_ball = false; - xml_node.get("soccer_ball", &m_soccer_ball); - - std::string type; - xml_node.get("type", &type ); - - m_type = type; - - bool trigger_when_near = false; - float trigger_distance = 1.0f; - - if (xml_node.getName() == "particle-emitter") - { - m_type = "particle-emitter"; - std::string path; - irr::core::vector3df emitter_origin; - xml_node.get("kind", &path); - xml_node.getXYZ(&emitter_origin); - - int clip_distance = -1; - xml_node.get("clip_distance", &clip_distance); - - xml_node.get("conditions", &m_trigger_condition); - - try - { - ParticleKind* kind = ParticleKindManager::get()->getParticles( path.c_str() ); - if (kind == NULL) - { - throw std::runtime_error(path + " could not be loaded"); - } - ParticleEmitter* emitter = new ParticleEmitter( kind, emitter_origin ); - - if (clip_distance > 0) - { - scene::ISceneManager* sm = irr_driver->getSceneManager(); - scene::ISceneNode* sroot = sm->getRootSceneNode(); - LODNode* lod = new LODNode("particles", sroot, sm); - lod->add(clip_distance, (scene::ISceneNode*)emitter->getNode(), true); - //m_all_emitters.push_back(emitter); - m_node = lod; - m_lod_emitter_node = lod; - m_emitter = emitter; - } - else - { - m_node = emitter->getNode(); // FIXME: this leaks - m_emitter = emitter; - } - - if (m_trigger_condition.size() > 0) - { - m_emitter->setCreationRateAbsolute(0.0f); - } - } - catch (std::runtime_error& e) - { - fprintf(stderr, "[Track] WARNING: Could not load particles '%s'; cause :\n %s", path.c_str(), e.what()); - } - } - // FIXME: at this time sound emitters are just disabled in multiplayer - // otherwise the sounds would be constantly heard - else if (sound.size() > 0 && race_manager->getNumLocalPlayers() < 2) - { - float rolloff = 0.5; - xml_node.get("rolloff", &rolloff ); - float volume = 1.0; - xml_node.get("volume", &volume ); - - xml_node.get("play-when-near", &trigger_when_near); - - xml_node.get("distance", &trigger_distance); - - xml_node.get("conditions", &m_trigger_condition); - - float max_dist = 390.0f; - xml_node.get("max_dist", &max_dist ); - - // first try track dir, then global dir - std::string soundfile = file_manager->getModelFile(sound); - if (!file_manager->fileExists(soundfile)) - { - soundfile = file_manager->getSFXFile(sound); - } - - SFXBuffer* buffer = new SFXBuffer(soundfile, - true /* positional */, - rolloff, - max_dist, - volume); - buffer->load(); - - m_sound = sfx_manager->createSoundSource(buffer, true, true); - if (m_sound != NULL) - { - m_sound->position(m_init_xyz); - if (!trigger_when_near && m_trigger_condition.empty()) - { - m_sound->setLoop(true); - m_sound->play(); - } - } - else - { - fprintf(stderr, - "[TrackObject] Sound emitter object could not be created\n"); - } - } - else if (type == "action-trigger") - { - trigger_when_near = true; - - xml_node.get("distance", &trigger_distance); - - xml_node.get("action", &m_action); - - if (m_action.size() == 0) - { - fprintf(stderr, "[TrackObject] WARNING: action-trigger has no action defined\n"); - } - } - - // Some animated objects (billboards, sound emitters, action triggers) - // don't use this scene node - if (model_name == "") - { - m_mesh = NULL; - - if (trigger_when_near) - { - ItemManager::get()->newItem(m_init_xyz, trigger_distance, this); - } - } - else - { - std::string full_path = + std::string full_path = World::getWorld()->getTrack()->getTrackFile(model_name); - bool animated = (UserConfigParams::m_graphical_effects || - World::getWorld()->getIdent() == IDENT_CUSTSCENE); - - if (file_manager->fileExists(full_path)) - { - if (animated) - { - m_mesh = irr_driver->getAnimatedMesh(full_path); - } - else - { - m_mesh = irr_driver->getMesh(full_path); - } - } - - if(!m_mesh) - { - // If the model isn't found in the track directory, look - // in STK's model directory. - full_path = file_manager->getModelFile(model_name); - m_mesh = irr_driver->getAnimatedMesh(full_path); - - if(!m_mesh) - { - throw std::runtime_error("Model '" + model_name + "' cannot be found"); - } - } - - m_mesh->grab(); - irr_driver->grabAllTextures(m_mesh); - + bool animated = (UserConfigParams::m_graphical_effects || + World::getWorld()->getIdent() == IDENT_CUSTSCENE); + + if (file_manager->fileExists(full_path)) + { if (animated) { - scene::IAnimatedMeshSceneNode *node = - irr_driver->addAnimatedMesh((scene::IAnimatedMesh*)m_mesh); - m_node = node; - node->setName(model_name.c_str()); - - m_frame_start = node->getStartFrame(); - xml_node.get("frame-start", &m_frame_start); - - m_frame_end = node->getEndFrame(); - xml_node.get("frame-end", &m_frame_end); + m_mesh = irr_driver->getAnimatedMesh(full_path); } else { - m_node = irr_driver->addMesh(m_mesh); - m_frame_start = 0; - m_frame_end = 0; + m_mesh = irr_driver->getMesh(full_path); } -#ifdef DEBUG - std::string debug_name = model_name+" (track-object)"; - m_node->setName(debug_name.c_str()); -#endif - - if(!m_enabled) - m_node->setVisible(false); - - m_node->setPosition(m_init_xyz); - m_node->setRotation(m_init_hpr); - m_node->setScale(m_init_scale); } - reset(); -} // TrackObject - -// ---------------------------------------------------------------------------- - -TrackObject::TrackObject(const core::vector3df& pos, const core::vector3df& hpr, - const core::vector3df& scale, const std::string& model_name) -{ - m_init_xyz = pos; - m_init_hpr = hpr; - m_init_scale = scale; - m_enabled = true; - m_is_looped = false; - m_sound = NULL; - m_emitter = NULL; - // Some animated objects (billboards, sound emitters) don't use this scene node - if (model_name == "") + if(!m_mesh) { - m_node = NULL; - m_mesh = NULL; + // If the model isn't found in the track directory, look + // in STK's model directory. + full_path = file_manager->getModelFile(model_name); + m_mesh = irr_driver->getAnimatedMesh(full_path); + + if(!m_mesh) + { + throw std::runtime_error("Model '" + model_name + "' cannot be found"); + } + } + + m_mesh->grab(); + irr_driver->grabAllTextures(m_mesh); + + if (animated) + { + scene::IAnimatedMeshSceneNode *node = + irr_driver->addAnimatedMesh((scene::IAnimatedMesh*)m_mesh); + m_node = node; + + m_frame_start = node->getStartFrame(); + xml_node.get("frame-start", &m_frame_start); + + m_frame_end = node->getEndFrame(); + xml_node.get("frame-end", &m_frame_end); } else { - bool animated = (UserConfigParams::m_graphical_effects || - World::getWorld()->getIdent() == IDENT_CUSTSCENE); - - if (file_manager->fileExists(model_name)) - { - if (animated) - { - m_mesh = irr_driver->getAnimatedMesh(model_name); - } - else - { - m_mesh = irr_driver->getMesh(model_name); - } - } - - if(!m_mesh) - { - fprintf(stderr, "Warning: '%s' not found and is ignored.\n", - model_name.c_str()); - return; - } - - m_mesh->grab(); - irr_driver->grabAllTextures(m_mesh); - - if (animated) - { - scene::IAnimatedMeshSceneNode *node = irr_driver->addAnimatedMesh((scene::IAnimatedMesh*)m_mesh); - m_node = node; - - m_frame_start = node->getStartFrame(); - m_frame_end = node->getEndFrame(); - } - else - { - m_node = irr_driver->addMesh(m_mesh); - m_frame_start = 0; - m_frame_end = 0; - } - + m_node = irr_driver->addMesh(m_mesh); + m_frame_start = 0; + m_frame_end = 0; + } #ifdef DEBUG - std::string debug_name = model_name+" (track-object)"; - m_node->setName(debug_name.c_str()); + std::string debug_name = model_name+" (track-object)"; + m_node->setName(debug_name.c_str()); #endif - - if(!m_enabled) - m_node->setVisible(false); - - m_node->setPosition(m_init_xyz); - m_node->setRotation(m_init_hpr); - m_node->setScale(m_init_scale); - } - reset(); -} // TrackObject -// ---------------------------------------------------------------------------- -TrackObject::TrackObject() -{ - m_node = NULL; - m_mesh = NULL; - m_sound = NULL; - m_emitter = NULL; -} // TrackObject() + if(!enabled) + m_node->setVisible(false); -// ---------------------------------------------------------------------------- -/** Destructor. Removes the node from the scene graph, and also - * drops the textures of the mesh. Sound buffers are also freed. - */ -TrackObject::~TrackObject() + m_node->setPosition(m_init_xyz); + m_node->setRotation(m_init_hpr); + m_node->setScale(m_init_scale); +} + +TrackObjectPresentationMesh::~TrackObjectPresentationMesh() { - if (m_emitter) - { - if (m_lod_emitter_node != NULL) - { - irr_driver->removeNode(m_lod_emitter_node); - m_emitter->unsetNode(); - } - delete m_emitter; // this will also delete m_node - } - else if (m_node) + if (m_node) irr_driver->removeNode(m_node); if(m_mesh) @@ -388,21 +206,11 @@ TrackObject::~TrackObject() if(m_mesh->getReferenceCount()==1) irr_driver->removeMeshFromCache(m_mesh); } - - if (m_sound) - { - //delete m_sound->getBuffer(); - sfx_manager->deleteSFX(m_sound); - } -} // ~TrackObject +} -// ---------------------------------------------------------------------------- -/** Initialises an object before a race starts. - */ -void TrackObject::reset() +void TrackObjectPresentationMesh::reset() { - if(!m_node) return; - if(m_node->getType()==scene::ESNT_ANIMATED_MESH) + if (m_node->getType()==scene::ESNT_ANIMATED_MESH) { scene::IAnimatedMeshSceneNode *a_node = (scene::IAnimatedMeshSceneNode*)m_node; @@ -422,6 +230,528 @@ void TrackObject::reset() a_node->setFrameLoop(m_frame_start, m_frame_end); } } +} + + + +// ---------------------------------------------------------------------------- + + +TrackObjectPresentationSound::TrackObjectPresentationSound(const XMLNode& xml_node) : + TrackObjectPresentation(xml_node) +{ + m_sound = NULL; + m_xyz = m_init_xyz; + + std::string sound; + xml_node.get("sound", &sound); + + float rolloff = 0.5; + xml_node.get("rolloff", &rolloff ); + float volume = 1.0; + xml_node.get("volume", &volume ); + + bool trigger_when_near = false; + xml_node.get("play-when-near", &trigger_when_near); + + float trigger_distance = 1.0f; + xml_node.get("distance", &trigger_distance); + + xml_node.get("conditions", &m_trigger_condition); + + float max_dist = 390.0f; + xml_node.get("max_dist", &max_dist ); + + // first try track dir, then global dir + std::string soundfile = file_manager->getModelFile(sound); + if (!file_manager->fileExists(soundfile)) + { + soundfile = file_manager->getSFXFile(sound); + } + + SFXBuffer* buffer = new SFXBuffer(soundfile, + true /* positional */, + rolloff, + max_dist, + volume); + buffer->load(); + + m_sound = sfx_manager->createSoundSource(buffer, true, true); + if (m_sound != NULL) + { + m_sound->position(m_init_xyz); + if (!trigger_when_near && m_trigger_condition.empty()) + { + m_sound->setLoop(true); + m_sound->play(); + } + } + else + { + fprintf(stderr, + "[TrackObject] Sound emitter object could not be created\n"); + } + + if (trigger_when_near) + { + ItemManager::get()->newItem(m_init_xyz, trigger_distance, this); + } +} + +void TrackObjectPresentationSound::update(float dt) +{ + if (m_sound != NULL) + { + // muting when too far is implemented manually since not supported by OpenAL + // so need to call this every frame to update the muting state if listener + // moved + m_sound->position(m_xyz); + } +} + +void TrackObjectPresentationSound::onTriggerItemApproached(Item* who) +{ + if (m_sound != NULL && m_sound->getStatus() != SFXManager::SFX_PLAYING) + { + m_sound->play(); + } +} + +void TrackObjectPresentationSound::triggerSound(bool loop) +{ + if (m_sound != NULL) + { + m_sound->setLoop(loop); + m_sound->play(); + } +} + +void TrackObjectPresentationSound::stopSound() +{ + if (m_sound != NULL) m_sound->stop(); +} + +TrackObjectPresentationSound::~TrackObjectPresentationSound() +{ + if (m_sound) + { + //delete m_sound->getBuffer(); + sfx_manager->deleteSFX(m_sound); + } +} + +void TrackObjectPresentationSound::move(const core::vector3df& xyz, const core::vector3df& hpr, + const core::vector3df& scale) +{ + m_xyz = xyz; + if (m_sound != NULL) m_sound->position(xyz); +} + +// ---------------------------------------------------------------------------- + + +TrackObjectPresentationBillboard::TrackObjectPresentationBillboard(const XMLNode& xml_node) : + TrackObjectPresentationSceneNode(xml_node) +{ + std::string texture_name; + float width, height; + + m_fade_out_start = 50.0f; + m_fade_out_end = 150.0f; + + xml_node.get("texture", &texture_name); + xml_node.get("width", &width ); + xml_node.get("height", &height ); + + m_fade_out_when_close = false; + xml_node.get("fadeout", &m_fade_out_when_close); + + if (m_fade_out_when_close) + { + xml_node.get("start", &m_fade_out_start); + xml_node.get("end", &m_fade_out_end ); + } + + video::ITexture *texture = + irr_driver->getTexture(file_manager->getTextureFile(texture_name)); + m_node = irr_driver->addBillboard(core::dimension2df(width, height), + texture); + Material *stk_material = material_manager->getMaterial(texture_name); + stk_material->setMaterialProperties(&(m_node->getMaterial(0)), NULL); + + m_node->setPosition(m_init_xyz); +} + +void TrackObjectPresentationBillboard::update(float dt) +{ + if (m_fade_out_when_close) + { + scene::ICameraSceneNode* curr_cam = irr_driver->getSceneManager()->getActiveCamera(); + const float dist = m_node->getPosition().getDistanceFrom( curr_cam->getPosition() ); + + scene::IBillboardSceneNode* node = (scene::IBillboardSceneNode*)m_node; + + if (dist < m_fade_out_start) + { + node->setColor(video::SColor(0, 255, 255, 255)); + } + else if (dist > m_fade_out_end) + { + node->setColor(video::SColor(255, 255, 255, 255)); + } + else + { + int a = (int)(255*(dist - m_fade_out_start) / (m_fade_out_end - m_fade_out_start)); + node->setColor(video::SColor(a, 255, 255, 255)); + } + } +} + +TrackObjectPresentationBillboard::~TrackObjectPresentationBillboard() +{ + if (m_node) + irr_driver->removeNode(m_node); +} + +// ---------------------------------------------------------------------------- + + +TrackObjectPresentationParticles::TrackObjectPresentationParticles(const XMLNode& xml_node) : + TrackObjectPresentation(xml_node) +{ + m_emitter = NULL; + m_lod_emitter_node = NULL; + + std::string path; + irr::core::vector3df emitter_origin; + xml_node.get("kind", &path); + xml_node.getXYZ(&emitter_origin); + + int clip_distance = -1; + xml_node.get("clip_distance", &clip_distance); + + xml_node.get("conditions", &m_trigger_condition); + + try + { + ParticleKind* kind = ParticleKindManager::get()->getParticles( path.c_str() ); + if (kind == NULL) + { + throw std::runtime_error(path + " could not be loaded"); + } + ParticleEmitter* emitter = new ParticleEmitter( kind, emitter_origin ); + + /* + if (clip_distance > 0) + { + scene::ISceneManager* sm = irr_driver->getSceneManager(); + scene::ISceneNode* sroot = sm->getRootSceneNode(); + LODNode* lod = new LODNode("particles", sroot, sm); + lod->add(clip_distance, (scene::ISceneNode*)emitter->getNode(), true); + //m_all_emitters.push_back(emitter); + m_node = lod; + m_lod_emitter_node = lod; + m_emitter = emitter; + }*/ + //else + //{ + //m_node = emitter->getNode(); // FIXME: this leaks + m_emitter = emitter; + //} + + if (m_trigger_condition.size() > 0) + { + m_emitter->setCreationRateAbsolute(0.0f); + } + } + catch (std::runtime_error& e) + { + fprintf(stderr, "[Track] WARNING: Could not load particles '%s'; cause :\n %s", path.c_str(), e.what()); + } +} + +TrackObjectPresentationParticles::~TrackObjectPresentationParticles() +{ + if (m_emitter) + { + if (m_lod_emitter_node != NULL) + { + irr_driver->removeNode(m_lod_emitter_node); + m_emitter->unsetNode(); + } + delete m_emitter; // this will also delete m_node + } +} + +void TrackObjectPresentationParticles::update(float dt) +{ + if (m_emitter != NULL) + { + m_emitter->update(dt); + } +} + +void TrackObjectPresentationParticles::triggerParticles() +{ + if (m_emitter != NULL) + { + m_emitter->setCreationRateAbsolute(1.0f); + m_emitter->setParticleType(m_emitter->getParticlesInfo()); + } +} + +void TrackObjectPresentationParticles::move(const core::vector3df& xyz, const core::vector3df& hpr, + const core::vector3df& scale) +{ + if (m_emitter != NULL) m_emitter->setPosition(xyz); +} + +// ---------------------------------------------------------------------------- + + +TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger(const XMLNode& xml_node) : + TrackObjectPresentation(xml_node) +{ + float trigger_distance = 1.0f; + xml_node.get("distance", &trigger_distance); + + xml_node.get("action", &m_action); + + if (m_action.size() == 0) + { + fprintf(stderr, "[TrackObject] WARNING: action-trigger has no action defined\n"); + } + + ItemManager::get()->newItem(m_init_xyz, trigger_distance, this); +} + +void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) +{ + if (m_action == "garage") + { + new RacePausedDialog(0.8f, 0.6f); + //dynamic_cast(World::getWorld())->scheduleSelectKart(); + } + else if (m_action == "tutorial_drive") + { + //if (World::getWorld()->getPhase() == World::RACE_PHASE) + { + m_action = "__disabled__"; + //World::getWorld()->getRaceGUI()->clearAllMessages(); + + InputDevice* device = input_manager->getDeviceList()->getLatestUsedDevice(); + DeviceConfig* config = device->getConfiguration(); + irr::core::stringw accel = config->getBindingAsString(PA_ACCEL); + irr::core::stringw left = config->getBindingAsString(PA_STEER_LEFT); + irr::core::stringw right = config->getBindingAsString(PA_STEER_RIGHT); + + new TutorialMessageDialog(_("Accelerate with <%s> and steer with <%s> and <%s>", accel, left, right), + false); + } + } + else if (m_action == "tutorial_bananas") + { + m_action = "__disabled__"; + + new TutorialMessageDialog(_("Avoid bananas!"), true); + } + else if (m_action == "tutorial_giftboxes") + { + m_action = "__disabled__"; + InputDevice* device = input_manager->getDeviceList()->getLatestUsedDevice(); + DeviceConfig* config = device->getConfiguration(); + irr::core::stringw fire = config->getBindingAsString(PA_FIRE); + + new TutorialMessageDialog(_("Collect gift boxes, and fire the weapon with <%s> to blow away these boxes!", fire), + true); + } + else if (m_action == "tutorial_nitro_collect") + { + m_action = "__disabled__"; + + new TutorialMessageDialog(_("Collect nitro bottles (we will use them after the curve)"), + true); + } + else if (m_action == "tutorial_nitro_use") + { + m_action = "__disabled__"; + InputDevice* device = input_manager->getDeviceList()->getLatestUsedDevice(); + DeviceConfig* config = device->getConfiguration(); + irr::core::stringw nitro = config->getBindingAsString(PA_NITRO); + + new TutorialMessageDialog(_("Use the nitro you collected by pressing <%s>!", nitro), + true); + } + else if (m_action == "tutorial_rescue") + { + m_action = "__disabled__"; + InputDevice* device = input_manager->getDeviceList()->getLatestUsedDevice(); + DeviceConfig* config = device->getConfiguration(); + irr::core::stringw rescue = config->getBindingAsString(PA_RESCUE); + + new TutorialMessageDialog(_("Oops! When you're in trouble, press <%s> to be rescued", rescue), + false); + } + else if (m_action == "tutorial_skidding") + { + m_action = "__disabled__"; + //World::getWorld()->getRaceGUI()->clearAllMessages(); + + InputDevice* device = input_manager->getDeviceList()->getLatestUsedDevice(); + DeviceConfig* config = device->getConfiguration(); + irr::core::stringw skid = config->getBindingAsString(PA_DRIFT); + + + new TutorialMessageDialog(_("Accelerate and press the <%s> key while turning to skid. Skidding for a short while can help you turn faster to take sharp turns.", skid), + true); + } + else if (m_action == "tutorial_skidding2") + { + m_action = "__disabled__"; + World::getWorld()->getRaceGUI()->clearAllMessages(); + + new TutorialMessageDialog(_("Note that if you manage to skid for several seconds, you will receive a bonus speedup as a reward!"), + true); + } + else if (m_action == "tutorial_endmessage") + { + m_action = "__disabled__"; + World::getWorld()->getRaceGUI()->clearAllMessages(); + + new TutorialMessageDialog(_("You are now ready to race. Good luck!"), + true); + } + else if (m_action == "tutorial_exit") + { + World::getWorld()->scheduleExitRace(); + return; + } + else if (m_action == "__disabled__") + { + } + else + { + fprintf(stderr, "[TrackObject] WARNING: unknown action <%s>\n", + m_action.c_str()); + } +} + + +// ---------------------------------------------------------------------------- + + +/** A track object: any additional object on the track. This object implements + * a graphics-only representation, i.e. there is no physical representation. + * Derived classes can implement a physical representation (see + * physics/physical_object) or animations. + * \param xml_node The xml node from which the initial data is taken. This is + * for now: initial position, initial rotation, name of the + * model, enable/disable status, timer information. + */ +TrackObject::TrackObject(const XMLNode &xml_node) +{ + m_init_xyz = core::vector3df(0,0,0); + m_init_hpr = core::vector3df(0,0,0); + m_init_scale = core::vector3df(1,1,1); + m_enabled = true; + m_presentation = NULL; + m_animator = NULL; + + m_rigid_body = NULL; + + xml_node.get("xyz", &m_init_xyz ); + xml_node.get("hpr", &m_init_hpr ); + xml_node.get("scale", &m_init_scale); + xml_node.get("enabled", &m_enabled ); + + m_interaction = "static"; + xml_node.get("interaction", &m_interaction); + xml_node.get("lod_group", &m_lod_group); + + m_soccer_ball = false; + xml_node.get("soccer_ball", &m_soccer_ball); + + std::string type; + xml_node.get("type", &type ); + + m_type = type; + + + if (xml_node.getName() == "particle-emitter") + { + m_type = "particle-emitter"; + m_presentation = new TrackObjectPresentationParticles(xml_node); + } + else if (type == "sfx-emitter") + { + // FIXME: at this time sound emitters are just disabled in multiplayer + // otherwise the sounds would be constantly heard + if (race_manager->getNumLocalPlayers() < 2) + m_presentation = new TrackObjectPresentationSound(xml_node); + } + else if (type == "action-trigger") + { + m_presentation = new TrackObjectPresentationActionTrigger(xml_node); + } + else if (type == "billboard") + { + m_presentation = new TrackObjectPresentationBillboard(xml_node); + } + else if (type=="cutscene_camera") + { + m_presentation = new TrackObjectPresentationEmpty(xml_node); + } + else + { + TrackObjectPresentationMesh* mesh_presentation = + new TrackObjectPresentationMesh(xml_node, m_enabled); + + m_presentation = mesh_presentation; + + if (m_interaction != "ghost" && m_interaction != "none") + { + m_rigid_body = new PhysicalObject(type == "movable", + xml_node, + mesh_presentation->getNode()); + } + } + + + if (type == "animation" || xml_node.hasChildNamed("curve")) + { + m_animator = new ThreeDAnimation(xml_node, this); + } + + reset(); +} // TrackObject + +// ---------------------------------------------------------------------------- + +TrackObject::TrackObject() +{ + m_presentation = NULL; + m_animator = NULL; +} // TrackObject() + +// ---------------------------------------------------------------------------- + +/** Destructor. Removes the node from the scene graph, and also + * drops the textures of the mesh. Sound buffers are also freed. + */ +TrackObject::~TrackObject() +{ + delete m_presentation; + delete m_animator; +} // ~TrackObject + +// ---------------------------------------------------------------------------- +/** Initialises an object before a race starts. + */ +void TrackObject::reset() +{ + if (m_presentation != NULL) m_presentation->reset(); + + if (m_animator != NULL) m_animator->reset(); } // reset // ---------------------------------------------------------------------------- /** Enables or disables this object. This affects the visibility, i.e. @@ -431,183 +761,54 @@ void TrackObject::reset() void TrackObject::setEnable(bool mode) { m_enabled = mode; - if(m_node) - m_node->setVisible(m_enabled); + if (m_presentation != NULL) m_presentation->setEnable(m_enabled); } // setEnable -// ---------------------------------------------------------------------------- -/** This function is called from irrlicht when a (non-looped) animation ends. - */ -void TrackObject::OnAnimationEnd(scene::IAnimatedMeshSceneNode* node) -{ -} // OnAnimationEnd - // ---------------------------------------------------------------------------- void TrackObject::update(float dt) { - if (m_sound != NULL) - { - // muting when too far is implemented manually since not supported by OpenAL - // so need to call this every frame to update the muting state if listener - // moved - m_sound->position(m_init_xyz); - } + if (m_presentation != NULL) m_presentation->update(dt); - if (m_emitter != NULL) - { - m_emitter->update(dt); - } + if (m_rigid_body != NULL) m_rigid_body->update(dt); + + if (m_animator != NULL) m_animator->update(dt); } // update + + // ---------------------------------------------------------------------------- -/** Implement callback from TriggerItemListener. Not used by all track objects. */ -void TrackObject::onTriggerItemApproached(Item* who) +void TrackObject::move(const core::vector3df& xyz, const core::vector3df& hpr, + const core::vector3df& scale) { - if (m_sound != NULL && m_sound->getStatus() != SFXManager::SFX_PLAYING) - { - m_sound->play(); - } - else if (m_action.size() > 0) - { - if (m_action == "garage") - { - new RacePausedDialog(0.8f, 0.6f); - //dynamic_cast(World::getWorld())->scheduleSelectKart(); - } - else if (m_action == "tutorial_drive") - { - //if (World::getWorld()->getPhase() == World::RACE_PHASE) - { - m_action = "__disabled__"; - //World::getWorld()->getRaceGUI()->clearAllMessages(); - - InputDevice* device = input_manager->getDeviceList()->getLatestUsedDevice(); - DeviceConfig* config = device->getConfiguration(); - irr::core::stringw accel = config->getBindingAsString(PA_ACCEL); - irr::core::stringw left = config->getBindingAsString(PA_STEER_LEFT); - irr::core::stringw right = config->getBindingAsString(PA_STEER_RIGHT); - - new TutorialMessageDialog(_("Accelerate with <%s> and steer with <%s> and <%s>", accel, left, right), - false); - } - } - else if (m_action == "tutorial_bananas") - { - m_action = "__disabled__"; - - new TutorialMessageDialog(_("Avoid bananas!"), true); - } - else if (m_action == "tutorial_giftboxes") - { - m_action = "__disabled__"; - InputDevice* device = input_manager->getDeviceList()->getLatestUsedDevice(); - DeviceConfig* config = device->getConfiguration(); - irr::core::stringw fire = config->getBindingAsString(PA_FIRE); - - new TutorialMessageDialog(_("Collect gift boxes, and fire the weapon with <%s> to blow away these boxes!", fire), - true); - } - else if (m_action == "tutorial_nitro_collect") - { - m_action = "__disabled__"; - - new TutorialMessageDialog(_("Collect nitro bottles (we will use them after the curve)"), - true); - } - else if (m_action == "tutorial_nitro_use") - { - m_action = "__disabled__"; - InputDevice* device = input_manager->getDeviceList()->getLatestUsedDevice(); - DeviceConfig* config = device->getConfiguration(); - irr::core::stringw nitro = config->getBindingAsString(PA_NITRO); - - new TutorialMessageDialog(_("Use the nitro you collected by pressing <%s>!", nitro), - true); - } - else if (m_action == "tutorial_rescue") - { - m_action = "__disabled__"; - InputDevice* device = input_manager->getDeviceList()->getLatestUsedDevice(); - DeviceConfig* config = device->getConfiguration(); - irr::core::stringw rescue = config->getBindingAsString(PA_RESCUE); - - new TutorialMessageDialog(_("Oops! When you're in trouble, press <%s> to be rescued", rescue), - false); - } - else if (m_action == "tutorial_skidding") - { - m_action = "__disabled__"; - //World::getWorld()->getRaceGUI()->clearAllMessages(); - - InputDevice* device = input_manager->getDeviceList()->getLatestUsedDevice(); - DeviceConfig* config = device->getConfiguration(); - irr::core::stringw skid = config->getBindingAsString(PA_DRIFT); - - - new TutorialMessageDialog(_("Accelerate and press the <%s> key while turning to skid. Skidding for a short while can help you turn faster to take sharp turns.", skid), - true); - } - else if (m_action == "tutorial_skidding2") - { - m_action = "__disabled__"; - World::getWorld()->getRaceGUI()->clearAllMessages(); - - new TutorialMessageDialog(_("Note that if you manage to skid for several seconds, you will receive a bonus speedup as a reward!"), - true); - } - else if (m_action == "tutorial_endmessage") - { - m_action = "__disabled__"; - World::getWorld()->getRaceGUI()->clearAllMessages(); - - new TutorialMessageDialog(_("You are now ready to race. Good luck!"), - true); - } - else if (m_action == "tutorial_exit") - { - World::getWorld()->scheduleExitRace(); - return; - } - else if (m_action == "__disabled__") - { - } - else - { - fprintf(stderr, "[TrackObject] WARNING: unknown action <%s>\n", - m_action.c_str()); - } - } + if (m_presentation != NULL) m_presentation->move(xyz, hpr, scale); + if (m_rigid_body != NULL) m_rigid_body->move(xyz, hpr); } // ---------------------------------------------------------------------------- -/** if this is a sound object, play the object */ -void TrackObject::triggerSound(bool loop) +const core::vector3df& TrackObject::getPosition() const { - if (m_sound != NULL) - { - m_sound->setLoop(loop); - m_sound->play(); - } + if (m_presentation != NULL) + return m_presentation->getPosition(); + else + return m_init_xyz; } // ---------------------------------------------------------------------------- -/** if this is a sound object, stop the object */ -void TrackObject::stopSound() +const core::vector3df& TrackObject::getRotation() const { - if (m_sound != NULL) m_sound->stop(); + if (m_presentation != NULL) + return m_presentation->getRotation(); + else + return m_init_xyz; } // ---------------------------------------------------------------------------- -void TrackObject::triggerParticles() +const core::vector3df& TrackObject::getScale() const { - if (m_emitter != NULL) - { - m_emitter->setCreationRateAbsolute(1.0f); - m_emitter->setParticleType(m_emitter->getParticlesInfo()); - } + if (m_presentation != NULL) + return m_presentation->getScale(); + else + return m_init_xyz; } - -// ---------------------------------------------------------------------------- - diff --git a/src/tracks/track_object.hpp b/src/tracks/track_object.hpp index 8b672ed0c..1b1a443a5 100644 --- a/src/tracks/track_object.hpp +++ b/src/tracks/track_object.hpp @@ -28,6 +28,7 @@ namespace irr using namespace irr; #include "items/item.hpp" +#include "utils/cpp2011.h" #include "utils/no_copy.hpp" #include "utils/vec3.hpp" #include @@ -36,50 +37,12 @@ using namespace irr; class XMLNode; class SFXBase; class ParticleEmitter; +class PhysicalObject; +class ThreeDAnimation; -/** - * \ingroup tracks - * This is a base object for any separate object on the track, which - * might also have a skeletal animation. This is used by objects that - * have an IPO animation, as well as physical objects. - */ -class TrackObject : public scene::IAnimationEndCallBack, public NoCopy, - public TriggerItemListener +class TrackObjectPresentation { -//public: - // The different type of track objects: physical objects, graphical - // objects (without a physical representation) - the latter might be - // eye candy (to reduce work for physics), ... - //enum TrackObjectType {TO_PHYSICAL, TO_GRAPHICAL}; - -private: - /** True if the object is currently being displayed. */ - bool m_enabled; - - /** True if it is a looped animation. */ - bool m_is_looped; - - /** Start frame of the animation to be played. */ - unsigned int m_frame_start; - - /** End frame of the animation to be played. */ - unsigned int m_frame_end; - - /** Currently used for sound effects only, in cutscenes only atm */ - std::string m_trigger_condition; - - virtual void OnAnimationEnd(scene::IAnimatedMeshSceneNode* node); - - ParticleEmitter* m_emitter; - protected: - /** The irrlicht scene node this object is attached to. */ - scene::ISceneNode *m_node; - - /** The mesh used here. It needs to be stored so that it can be - * removed from irrlicht's mesh cache when it is deleted. */ - scene::IMesh *m_mesh; - /** The initial XYZ position of the object. */ core::vector3df m_init_xyz; @@ -88,42 +51,80 @@ protected: /** The initial scale of the object. */ core::vector3df m_init_scale; - - /** If a sound is attached to this object and/or this is a sound emitter object */ - SFXBase* m_sound; - /** LOD group this object is part of, if it is LOD */ - std::string m_lod_group; - - /** For action trigger objects */ - std::string m_action; - - std::string m_interaction; - - std::string m_type; - - LODNode* m_lod_emitter_node; - - bool m_soccer_ball; - + public: - TrackObject(const XMLNode &xml_node); - TrackObject(); - TrackObject(const core::vector3df& pos, const core::vector3df& hpr, - const core::vector3df& scale, const std::string& model); - ~TrackObject(); - virtual void update(float dt); - virtual void reset(); - /** To finish object constructions. Called after the track model - * is ready. */ - virtual void init() {}; - /** Called when an explosion happens. As a default does nothing, will - * e.g. be overwritten by physical objects etc. */ - virtual void handleExplosion(const Vec3& pos, bool directHit) {}; - void setEnable(bool mode); + + TrackObjectPresentation(const XMLNode& xml_node); + virtual ~TrackObjectPresentation() {} + + virtual void reset() {} + virtual void setEnable(bool enabled) {} + virtual void update(float dt) {} + virtual void move(const core::vector3df& xyz, const core::vector3df& hpr, + const core::vector3df& scale) {} + + virtual const core::vector3df& getPosition() const { return m_init_xyz; } + virtual const core::vector3df& getRotation() const { return m_init_hpr; } + virtual const core::vector3df& getScale() const { return m_init_scale; } + + LEAK_CHECK() +}; + +class TrackObjectPresentationSceneNode : public TrackObjectPresentation +{ +protected: + scene::ISceneNode* m_node; +public: + + TrackObjectPresentationSceneNode(const XMLNode& xml_node) : + TrackObjectPresentation(xml_node) + { + m_node = NULL; + } + + virtual const core::vector3df& getPosition() const OVERRIDE; + virtual const core::vector3df& getRotation() const OVERRIDE; + virtual const core::vector3df& getScale() const OVERRIDE; + virtual void move(const core::vector3df& xyz, const core::vector3df& hpr, + const core::vector3df& scale) OVERRIDE; + virtual void setEnable(bool enabled) OVERRIDE; + virtual void reset() OVERRIDE; scene::ISceneNode* getNode() { return m_node; } const scene::ISceneNode* getNode() const { return m_node; } +}; + +class TrackObjectPresentationEmpty : public TrackObjectPresentationSceneNode +{ +public: + + TrackObjectPresentationEmpty(const XMLNode& xml_node); + virtual ~TrackObjectPresentationEmpty(); +}; + +class TrackObjectPresentationMesh : public TrackObjectPresentationSceneNode +{ +private: + /** The mesh used here. It needs to be stored so that it can be + * removed from irrlicht's mesh cache when it is deleted. */ + scene::IMesh *m_mesh; + + /** True if it is a looped animation. */ + bool m_is_looped; + + /** Start frame of the animation to be played. */ + unsigned int m_frame_start; + + /** End frame of the animation to be played. */ + unsigned int m_frame_end; + +public: + TrackObjectPresentationMesh(const XMLNode& xml_node, bool enabled); + virtual ~TrackObjectPresentationMesh(); + + virtual void reset() OVERRIDE; + /** 2-step construction */ void setNode(scene::ISceneNode* node) @@ -149,22 +150,182 @@ public: m_node->setScale(m_init_scale); } } + +}; + +class TrackObjectPresentationSound : public TrackObjectPresentation, + public TriggerItemListener +{ +private: + + /** If a sound is attached to this object and/or this is a sound emitter object */ + SFXBase* m_sound; + + /** Currently used for sound effects only, in cutscenes only atm */ + std::string m_trigger_condition; + + core::vector3df m_xyz; + +public: + + TrackObjectPresentationSound(const XMLNode& xml_node); + virtual ~TrackObjectPresentationSound(); + virtual void onTriggerItemApproached(Item* who) OVERRIDE; + virtual void update(float dt) OVERRIDE; + void triggerSound(bool loop); + void stopSound(); + + /** Currently used for sound effects only, in cutscenes only atm */ + const std::string& getTriggerCondition() const { return m_trigger_condition; } + + virtual void move(const core::vector3df& xyz, const core::vector3df& hpr, + const core::vector3df& scale) OVERRIDE; +}; + +class TrackObjectPresentationBillboard : public TrackObjectPresentationSceneNode +{ + /** To make the billboard disappear when close to the camera. Useful for light halos : + * instead of "colliding" with the camera and suddenly disappearing when clipped by + * frustum culling, it will gently fade out. + */ + bool m_fade_out_when_close; + float m_fade_out_start; + float m_fade_out_end; +public: + TrackObjectPresentationBillboard(const XMLNode& xml_node); + virtual ~TrackObjectPresentationBillboard(); + virtual void update(float dt) OVERRIDE; +}; + + +class TrackObjectPresentationParticles : public TrackObjectPresentation +{ +private: + ParticleEmitter* m_emitter; + LODNode* m_lod_emitter_node; + std::string m_trigger_condition; + +public: + TrackObjectPresentationParticles(const XMLNode& xml_node); + virtual ~TrackObjectPresentationParticles(); + + virtual void update(float dt) OVERRIDE; + virtual void move(const core::vector3df& xyz, const core::vector3df& hpr, + const core::vector3df& scale) OVERRIDE; + std::string& getTriggerCondition() { return m_trigger_condition; } + + void triggerParticles(); +}; + +class TrackObjectPresentationActionTrigger : public TrackObjectPresentation, + public TriggerItemListener +{ +private: + + /** For action trigger objects */ + std::string m_action; + +public: + + + TrackObjectPresentationActionTrigger(const XMLNode& xml_node); + virtual ~TrackObjectPresentationActionTrigger() {} + + virtual void onTriggerItemApproached(Item* who) OVERRIDE; +}; + +/** + * \ingroup tracks + * This is a base object for any separate object on the track, which + * might also have a skeletal animation. This is used by objects that + * have an IPO animation, as well as physical objects. + */ +class TrackObject : public NoCopy +{ +//public: + // The different type of track objects: physical objects, graphical + // objects (without a physical representation) - the latter might be + // eye candy (to reduce work for physics), ... + //enum TrackObjectType {TO_PHYSICAL, TO_GRAPHICAL}; + +private: + /** True if the object is currently being displayed. */ + bool m_enabled; + + TrackObjectPresentation* m_presentation; + +protected: + + + /** The initial XYZ position of the object. */ + core::vector3df m_init_xyz; + + /** The initial hpr of the object. */ + core::vector3df m_init_hpr; + + /** The initial scale of the object. */ + core::vector3df m_init_scale; + + /** LOD group this object is part of, if it is LOD */ + std::string m_lod_group; + + std::string m_interaction; + + std::string m_type; + + bool m_soccer_ball; + + PhysicalObject* m_rigid_body; + + ThreeDAnimation* m_animator; + + +public: + TrackObject(const XMLNode &xml_node); + TrackObject(); + + /* + TrackObject(const core::vector3df& pos, const core::vector3df& hpr, + const core::vector3df& scale, const std::string& model); + */ + ~TrackObject(); + virtual void update(float dt); + virtual void reset(); + /** To finish object constructions. Called after the track model + * is ready. */ + virtual void init() {}; + /** Called when an explosion happens. As a default does nothing, will + * e.g. be overwritten by physical objects etc. */ + virtual void handleExplosion(const Vec3& pos, bool directHit) {}; + void setEnable(bool mode); const std::string& getLodGroup() const { return m_lod_group; } const std::string& getType() const { return m_type; } bool isSoccerBall() const { return m_soccer_ball; } + + const PhysicalObject* getPhysics() const { return m_rigid_body; } + PhysicalObject* getPhysics() { return m_rigid_body; } + + const core::vector3df getInitXYZ() const { return m_init_xyz; } + const core::vector3df getInitRotation() const { return m_init_hpr; } + const core::vector3df getInitScale() const { return m_init_scale; } - /** Currently used for sound effects only, in cutscenes only atm */ - const std::string& getTriggerCondition() const { return m_trigger_condition; } + void move(const core::vector3df& xyz, const core::vector3df& hpr, const core::vector3df& scale); + + template + T* getPresentation() { return dynamic_cast(m_presentation); } + + template + const T* getPresentation() const { return dynamic_cast(m_presentation); } - void triggerSound(bool loop=false); - void stopSound(); - void triggerParticles(); - - virtual void onTriggerItemApproached(Item* who); + ThreeDAnimation* getAnimator() { return m_animator; } + const ThreeDAnimation* getAnimator() const { return m_animator; } + const core::vector3df& getPosition() const; + const core::vector3df& getRotation() const; + const core::vector3df& getScale() const; }; // TrackObject #endif diff --git a/src/tracks/track_object_manager.cpp b/src/tracks/track_object_manager.cpp index f68804f53..5ac212762 100644 --- a/src/tracks/track_object_manager.cpp +++ b/src/tracks/track_object_manager.cpp @@ -20,7 +20,6 @@ #include "animations/ipo.hpp" #include "config/user_config.hpp" -#include "animations/billboard_animation.hpp" #include "animations/three_d_animation.hpp" #include "graphics/irr_driver.hpp" #include "graphics/lod_node.hpp" @@ -52,6 +51,8 @@ void TrackObjectManager::add(const XMLNode &xml_node) { try { + m_all_objects.push_back(new TrackObject(xml_node)); + /* std::string groupname; xml_node.get("lod_group", &groupname); bool is_lod = !groupname.empty(); @@ -67,11 +68,12 @@ void TrackObjectManager::add(const XMLNode &xml_node) { if (is_lod) { - m_lod_objects[groupname].push_back(new PhysicalObject(xml_node)); + assert(false); // TODO + //_lod_objects[groupname].push_back(new TrackObject(xml_node)); } else { - m_all_objects.push_back(new PhysicalObject(xml_node)); + m_all_objects.push_back(new TrackObject(xml_node)); } } else if(type=="animation") @@ -106,6 +108,7 @@ void TrackObjectManager::add(const XMLNode &xml_node) fprintf(stderr, "Unknown track object: '%s' - ignored.\n", type.c_str()); } + */ } catch (std::exception& e) { @@ -153,8 +156,8 @@ void TrackObjectManager::handleExplosion(const Vec3 &pos, const PhysicalObject * TrackObject* curr; for_in (curr, m_all_objects) { - if(secondary_hits || mp==curr) - curr->handleExplosion(pos, mp == curr); + if(secondary_hits || mp == curr->getPhysics()) + curr->handleExplosion(pos, mp == curr->getPhysics()); } } // handleExplosion @@ -228,27 +231,33 @@ void TrackObjectManager::enableFog(bool enable) TrackObject* curr; for_in (curr, m_all_objects) { - if (curr->getNode() != NULL) + TrackObjectPresentationMesh* meshPresentation = + curr->getPresentation(); + if (meshPresentation!= NULL) { - adjustForFog(curr->getNode(), enable); + adjustForFog(meshPresentation->getNode(), enable); } } } // enableFog // ---------------------------------------------------------------------------- -PhysicalObject* TrackObjectManager::insertObject(const std::string& model, +TrackObject* TrackObjectManager::insertObject(const std::string& model, PhysicalObject::bodyTypes shape, float mass, float radius, const core::vector3df& hpr, const core::vector3df& pos, const core::vector3df& scale) { + /* PhysicalObject* object = new PhysicalObject(model, shape, mass, radius, hpr, pos, scale); object->init(); m_all_objects.push_back(object); return object; + */ + assert(false); + // TODO } // ---------------------------------------------------------------------------- @@ -256,7 +265,7 @@ PhysicalObject* TrackObjectManager::insertObject(const std::string& model, * track objects, and then frees the object. * \param obj The physical object to remove. */ -void TrackObjectManager::removeObject(PhysicalObject* obj) +void TrackObjectManager::removeObject(TrackObject* obj) { m_all_objects.remove(obj); delete obj; @@ -277,7 +286,7 @@ void TrackObjectManager::assingLodNodes(const std::vector& lod_nodes) std::vector& queue = m_lod_objects[ lod_nodes[n]->getGroupName() ]; assert( queue.size() > 0 ); TrackObject* obj = queue[ queue.size() - 1 ]; - obj->setNode( lod_nodes[n] ); + obj->getPresentation()->setNode( lod_nodes[n] ); queue.erase( queue.end() - 1 ); m_all_objects.push_back(obj); diff --git a/src/tracks/track_object_manager.hpp b/src/tracks/track_object_manager.hpp index 45a9e744f..0374be362 100644 --- a/src/tracks/track_object_manager.hpp +++ b/src/tracks/track_object_manager.hpp @@ -64,14 +64,14 @@ public: /** Enable or disable fog on objects */ void enableFog(bool enable); - PhysicalObject* insertObject(const std::string& model, + TrackObject* insertObject(const std::string& model, PhysicalObject::bodyTypes shape, float mass, float radius, const core::vector3df& hpr, const core::vector3df& pos, const core::vector3df& scale); - void removeObject(PhysicalObject* who); + void removeObject(TrackObject* who); void assingLodNodes(const std::vector& lod);