Back to index

supertuxkart  0.5+dfsg1
moving_physics.cpp
Go to the documentation of this file.
00001 //  $Id: moving_physics.cpp 839 2006-10-24 00:01:56Z hiker $
00002 //
00003 //  SuperTuxKart - a fun racing game with go-kart
00004 //  Copyright (C) 2006 Joerg Henrichs
00005 //
00006 //  This program is free software; you can redistribute it and/or
00007 //  modify it under the terms of the GNU General Public License
00008 //  as published by the Free Software Foundation; either version 2
00009 //  of the License, or (at your option) any later version.
00010 //
00011 //  This program is distributed in the hope that it will be useful,
00012 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 //  GNU General Public License for more details.
00015 //
00016 //  You should have received a copy of the GNU General Public License
00017 //  along with this program; if not, write to the Free Software
00018 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00019 
00020 #include <string>
00021 #include <vector>
00022 #include <plib/sg.h>
00023 #include "moving_physics.hpp"
00024 #include "string_utils.hpp"
00025 #include "world.hpp"
00026 #include "ssg_help.hpp"
00027 #include "scene.hpp"
00028 
00029 // -----------------------------------------------------------------------------
00030 MovingPhysics::MovingPhysics(const std::string data)
00031              : ssgTransform(), Callback()
00032 {
00033     m_shape        = NULL;
00034     m_body         = NULL;
00035     m_motion_state = NULL;
00036     m_mass         = 1;
00037     setUserData(new ssgBase());   // prevent tree optimisations to remove this node
00038 
00039     std::vector<std::string> parameters = StringUtils::split(data, ' ');
00040     if(parameters.size()<2)
00041     {
00042         fprintf(stderr, "Invalid physics specification: '%s'\n",data.c_str());
00043     }
00044     parameters.erase(parameters.begin());
00045     std::string &shape=parameters[0];
00046     m_body_type = MP_NONE;
00047     if(shape=="cone"   ) m_body_type = MP_CONE;
00048     else if(shape=="box"    ) m_body_type = MP_BOX;
00049     else if(shape=="sphere" ) m_body_type = MP_SPHERE;
00050     parameters.erase(parameters.begin());
00051 
00052     // Scan for additional parameters, which are in the form of keyword=value
00053     // (without any spaces). Currently only mass=... is supported.
00054     while(parameters.size()>0)
00055     {
00056         // Split the parameter string by '=' to get the keyword and value
00057         std::vector<std::string> p=StringUtils::split(parameters[0],'=');
00058         if(p.size()!=2) 
00059         {
00060             fprintf(stderr, "Invalid physics parameter string: '%s'\n",data.c_str());
00061             break;
00062         } 
00063         if(p[0]=="mass") 
00064         {
00065             StringUtils::from_string<float>(p[1], m_mass);
00066         }
00067         else
00068         {
00069             fprintf(stderr, "Invalid physics parameter string: '%s'\n",
00070                     data.c_str());
00071             break;
00072         }
00073 
00074         parameters.erase(parameters.begin());
00075     }
00076 }   // MovingPhysics
00077 
00078 // -----------------------------------------------------------------------------
00079 MovingPhysics::~MovingPhysics()
00080 {
00081     world->getPhysics()->removeBody(m_body);
00082     delete m_body;
00083     delete m_motion_state;
00084     delete m_shape;
00085     scene->remove(this);
00086 }  // ~MovingPhysics
00087 
00088 // -----------------------------------------------------------------------------
00091 /* Main problem is that the loader (see world::loadTrack) adds a 
00092    ssgTransform->ssgRangeSelector->MovingPhysics, and that this ssgTransform M
00093    contains the actual position. So, if the physic position P would be set
00094    immediately, the transform M causes the position the object is drawn to be
00095    wrong (M*P is drawn instead of P). So, to correct this, the body has to be
00096    attached to the root of the scene graph. The body is therefore removed from
00097    its old place, and appended to the root. The original position has to be
00098    recomputed by going up in the scene graph and multiplying the transforms.  */
00099 void MovingPhysics::init()
00100 {
00101 
00102     // 1. Remove the object from the graph and attach it to the root
00103     // -------------------------------------------------------------
00104     if(getNumParents()>1) 
00105     {
00106         fprintf(stderr, "WARNING: physical object with more than one parent!!\n");
00107         return;
00108     }
00109     ssgBranch *parent = getParent(0);
00110 
00111     scene->add(this);
00112     parent->removeKid(this);
00113 
00114     // 2. Determine the original position of the object
00115     // ------------------------------------------------
00116 
00117     ssgEntity *p=parent;
00118     sgMat4    pos;
00119     sgMakeIdentMat4(pos);
00120     while(p->getNumParents())
00121     {
00122         if(p->getNumParents()!=1)
00123         {
00124             // FIXME: Not sure if this is needed: if one object has more than
00125             //        one parent (--> this object appears more than once in the
00126             //        scene), we have to follow all possible ways up to the
00127             //        root, and for each way add one instance to the root.
00128             //        For now this is unsupported, and we abort here.
00129             fprintf(stderr, "MovingPhysics: init: %d parents found, ignored.\n",
00130                     p->getNumParents());
00131             return;
00132         }   // if numparents!=1
00133         if(p->isAKindOf(ssgTypeTransform()))
00134         {
00135             ssgBaseTransform *trans=(ssgBaseTransform*)p;
00136             sgMat4 change_position;
00137             trans->getTransform(change_position);
00138             sgPostMultMat4(pos, change_position);
00139         }
00140         if(p->getNumKids()==0)
00141         {
00142             ssgBranch *new_parent=p->getParent(0);
00143             new_parent->removeKid(p);
00144             p = new_parent;
00145         }
00146         else
00147         {
00148             p=p->getParent(0);
00149         }
00150     }   // while
00151 
00152     // 3. Determine size of the object
00153     // -------------------------------
00154     float x_min, x_max, y_min, y_max, z_min, z_max, radius;
00155     MinMax(this, &x_min, &x_max, &y_min, &y_max, &z_min, &z_max);
00156     m_half_height = 0.5f*(z_max-z_min);
00157     switch (m_body_type)
00158     {
00159     case MP_CONE:   radius = 0.5f*std::max(x_max-x_min, y_max-y_min);
00160                     m_shape = new btConeShapeZ(radius, z_max-z_min);
00161                     setName("cone");
00162                     break;
00163     case MP_BOX:    m_shape = new btBoxShape(btVector3(0.5f*(x_max-x_min),
00164                                                        0.5f*(y_max-y_min),
00165                                                        0.5f*(z_max-z_min) ) );
00166                     setName("box");
00167                     break;
00168     case MP_SPHERE: radius = std::max(x_max-x_min, y_max-y_min);
00169                     radius = 0.5f*std::max(radius, z_max-z_min);
00170                     m_shape = new btSphereShape(radius);
00171                     setName("sphere");
00172                     break;
00173     case MP_NONE:   fprintf(stderr, "WARNING: Uninitialised moving shape\n");
00174         break;
00175     }
00176 
00177     // 4. Create the rigid object
00178     // --------------------------
00179     
00180     m_init_pos.setIdentity();
00181     m_init_pos.setOrigin(btVector3(pos[3][0],pos[3][1],pos[3][2]+m_half_height));
00182     m_motion_state = new btDefaultMotionState(m_init_pos);
00183     btVector3 inertia;
00184     m_shape->calculateLocalInertia(m_mass, inertia);
00185     btRigidBody::btRigidBodyConstructionInfo info(m_mass, m_motion_state, m_shape, inertia);
00186     
00187     // Make sure that the cones stop rolling by defining angular friction != 0.
00188     info.m_angularDamping = 0.5f;
00189     m_body = new btRigidBody(info);
00190     m_user_pointer.set(this);
00191     m_body->setUserPointer(&m_user_pointer);
00192     world->getPhysics()->addBody(m_body);
00193 }   // init
00194 
00195 // -----------------------------------------------------------------------------
00196 void MovingPhysics::update(float dt)
00197 {
00198     btTransform t;
00199     m_motion_state->getWorldTransform(t);
00200     float m[4][4];
00201     t.getOpenGLMatrix((float*)&m);
00202     
00203     // Transfer the new position and hpr to curr_pos
00204     sgCoord curr_pos;
00205     sgSetCoord(&curr_pos, m);
00206     if(curr_pos.xyz[2]<-100)
00207     {
00208         m_body->setCenterOfMassTransform(m_init_pos);
00209         curr_pos.xyz[0]=m_init_pos.getOrigin().getX();
00210         curr_pos.xyz[1]=m_init_pos.getOrigin().getY();
00211         curr_pos.xyz[2]=m_init_pos.getOrigin().getZ();
00212     }
00213     setTransform(&curr_pos);
00214 
00215 }   // update
00216 // -----------------------------------------------------------------------------
00217 void MovingPhysics::reset()
00218 {
00219     m_body->setCenterOfMassTransform(m_init_pos);
00220     m_body->setAngularVelocity(btVector3(0,0,0));
00221     m_body->setLinearVelocity(btVector3(0,0,0));
00222 }   // reset 
00223 
00224 // -----------------------------------------------------------------------------
00225 void MovingPhysics::handleExplosion(const btVector3& pos, bool direct_hit) {
00226     if(direct_hit) {
00227         btVector3 impulse(0.0f, 0.0f, stk_config->m_explosion_impulse_objects);
00228         m_body->applyCentralImpulse(impulse);
00229     }
00230     else  // only affected by a distant explosion
00231     {
00232         btTransform t;
00233         m_motion_state->getWorldTransform(t);
00234         btVector3 diff=t.getOrigin()-pos;
00235 
00236         float len2=diff.length2();
00237 
00238         // The correct formhale would be to first normalise diff,
00239         // then apply the impulse (which decreases 1/r^2 depending
00240         // on the distance r), so:
00241         // diff/len(diff) * impulseSize/len(diff)^2
00242         // = diff*impulseSize/len(diff)^3
00243         // We use diff*impulseSize/len(diff)^2 here, this makes the impulse
00244         // somewhat larger, which is actually more fun :)
00245         btVector3 impulse=diff*stk_config->m_explosion_impulse_objects/len2;
00246         m_body->applyCentralImpulse(impulse);
00247     }
00248     m_body->activate();
00249 
00250 }   // handleExplosion
00251 
00252 // -----------------------------------------------------------------------------
00253 /* EOF */
00254