stk-code_catmod/src/moveable.cpp

394 lines
12 KiB
C++
Raw Normal View History

// $Id$
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004-2005 Steve Baker <sjbaker1@airmail.net>
// Copyright (C) 2006 Joerg Henrichs, Steve Baker
//
// 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 2
// 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 "world.hpp"
#include "player_kart.hpp"
#include "material_manager.hpp"
#include "material.hpp"
#include "user_config.hpp"
#include "history.hpp"
Moveable::Moveable (bool bHasHistory)
{
#ifdef BULLET
m_body = 0;
m_motion_state = 0;
#endif
m_shadow = 0;
m_first_time = true ;
m_model = new ssgTransform();
m_model->ref();
sgZeroVec3 ( m_reset_pos.xyz ) ; sgZeroVec3 ( m_reset_pos.hpr ) ;
reset ();
if(bHasHistory)
{
m_history_velocity = new sgCoord[history->GetSize()];
m_history_position = new sgCoord[history->GetSize()];
}
else
{
m_history_velocity = NULL;
m_history_position = NULL;
}
} // Moveable
//-----------------------------------------------------------------------------
Moveable::~Moveable()
{
#ifdef BULLET
// The body is being removed from the world in kart/projectile
if(m_body) delete m_body;
if(m_motion_state) delete m_motion_state;
#endif
if(m_history_velocity)
{
delete [] m_history_velocity;
delete [] m_history_position;
}
// FIXME what about model?
} // ~Moveable
//-----------------------------------------------------------------------------
void Moveable::reset ()
{
#ifndef BULLET
m_on_ground = true;
#endif
m_collided = false;
m_crashed = false;
m_material_hot = NULL;
m_normal_hot = NULL;
sgZeroVec3 ( m_velocity.xyz ) ;
sgZeroVec3 ( m_velocity.hpr ) ;
sgCopyCoord ( &m_curr_pos, &m_reset_pos ) ;
sgZeroVec3 ( m_abs_velocity ) ;
} // reset
//-----------------------------------------------------------------------------
#ifdef BULLET
void Moveable::createBody(float mass, btTransform& trans,
btCollisionShape *shape, MoveableType m) {
btVector3 inertia;
shape->calculateLocalInertia(mass, inertia);
m_motion_state = new btDefaultMotionState(trans);
// Then create a rigid body
// ------------------------
m_body = new btRigidBody(mass, m_motion_state,
shape, inertia);
m_body->setUserPointer(this);
setMoveableType(m);
} // createBody
#endif
//-----------------------------------------------------------------------------
void Moveable::update (float dt)
{
if(m_history_velocity)
{
if(user_config->m_replay_history)
{
sgCoord tmp;
sgCopyCoord(&tmp, &(m_history_velocity[history->GetCurrentIndex()]));
//printf("m_velocity=%f,%f,%f,%f,%f,%f\n",
// m_velocity.xyz[0],m_velocity.xyz[1],m_velocity.xyz[2],
// m_velocity.hpr[0],m_velocity.hpr[1],m_velocity.hpr[2]);
//printf("tmp =%f,%f,%f,%f,%f,%f\n",
// tmp.xyz[0],tmp.xyz[1],tmp.xyz[2],
// tmp.hpr[0],tmp.hpr[1],tmp.hpr[2]);
#undef IGNORE_Z_IN_HISTORY
#ifdef IGNORE_Z_IN_HISTORY
const float DUMMY=m_velocity.xyz[2];
sgCopyCoord(&m_velocity, &tmp);
m_velocity.xyz[2]=DUMMY;
#else
sgCopyCoord(&m_velocity, &tmp);
#endif
}
else
{
sgCopyCoord(&(m_history_velocity[history->GetCurrentIndex()]), &m_velocity);
}
} // if m_history_velocity
sgMat4 result;
updatePosition(dt,result);
sgVec3 start ; sgCopyVec3 (start, m_curr_pos.xyz );
sgVec3 end ; sgCopyVec3 (end , result[3] );
const float HOT = collectIsectData(start, end );
sgCopyVec3 (result[3], end) ;
sgVec3 prev_pos;
sgCopyVec3(prev_pos, m_curr_pos.xyz);
sgSetCoord (&m_curr_pos, result);
sgSubVec3 (m_abs_velocity, m_curr_pos.xyz, prev_pos);
if(m_history_position)
{
if(user_config->m_replay_history)
{
sgCoord tmp;
sgCopyCoord(&tmp, &(m_history_position[history->GetCurrentIndex()]));
//printf("m_curr_pos=%f,%f,%f,%f,%f,%f\n",
// m_curr_pos.xyz[0],m_curr_pos.xyz[1],m_curr_pos.xyz[2],
// m_curr_pos.hpr[0],m_curr_pos.hpr[1],m_curr_pos.hpr[2]);
//printf("tmp =%f,%f,%f,%f,%f,%f --> %d\n",
// tmp.xyz[0],tmp.xyz[1],tmp.xyz[2],
// tmp.hpr[0],tmp.hpr[1],tmp.hpr[2],
// history->GetCurrentIndex());
#ifdef IGNORE_Z_IN_HISTORY
const float DUMMY=m_curr_pos.xyz[2];
sgCopyCoord(&m_curr_pos, &tmp);
m_curr_pos.xyz[2]=DUMMY;
#else
sgCopyCoord(&m_curr_pos, &tmp);
#endif
}
else
{
sgCopyCoord(&(m_history_position[history->GetCurrentIndex()]), &m_curr_pos);
}
} // if m_history_position
const float HAT = m_curr_pos.xyz[2]-HOT;
#ifndef BULLET
m_on_ground = ( HAT <= 0.01 );
doCollisionAnalysis(dt, HOT);
#endif
placeModel () ;
m_first_time = false ;
} // update
//-----------------------------------------------------------------------------
/**
* Computes the new position and hpr of the kart after a single time step.
*/
void Moveable::updatePosition(float dt, sgMat4 result)
{
sgCoord scaled_velocity ;
sgMat4 delta, mat;
/* Scale velocities to current time step. */
sgScaleVec3 (scaled_velocity.xyz, m_velocity.xyz, dt);
sgScaleVec3 (scaled_velocity.hpr, m_velocity.hpr, dt);
sgMakeCoordMat4(delta, & scaled_velocity );
sgMakeCoordMat4(mat , & m_curr_pos );
sgMultMat4 (result, mat, delta );
} // updatePosition
//-----------------------------------------------------------------------------
void Moveable::WriteHistory(char* s, int kartNumber, int indx)
{
sprintf(s, "Kart %d: v=%f,%f,%f,%f,%f,%f, p=%f,%f,%f,%f,%f,%f", kartNumber,
m_history_velocity[indx].xyz[0],
m_history_velocity[indx].xyz[1],
m_history_velocity[indx].xyz[2],
m_history_velocity[indx].hpr[0],
m_history_velocity[indx].hpr[1],
m_history_velocity[indx].hpr[2],
m_history_position[indx].xyz[0],
m_history_position[indx].xyz[1],
m_history_position[indx].xyz[2],
m_history_position[indx].hpr[0],
m_history_position[indx].hpr[1],
m_history_position[indx].hpr[2]);
} // WriteHistory
//-----------------------------------------------------------------------------
void Moveable::ReadHistory(char* s, int kartNumber, int indx)
{
int k;
sscanf(s, "Kart %d: v=%f,%f,%f,%f,%f,%f, p=%f,%f,%f,%f,%f,%f", &k,
m_history_velocity[indx].xyz+0,
m_history_velocity[indx].xyz+1,
m_history_velocity[indx].xyz+2,
m_history_velocity[indx].hpr+0,
m_history_velocity[indx].hpr+1,
m_history_velocity[indx].hpr+2,
m_history_position[indx].xyz+0,
m_history_position[indx].xyz+1,
m_history_position[indx].xyz+2,
m_history_position[indx].hpr+0,
m_history_position[indx].hpr+1,
m_history_position[indx].hpr+2);
if(k!=kartNumber)
{
fprintf(stderr,"WARNING: tried reading data for kart %d, found:\n",
kartNumber);
fprintf(stderr,"%s\n",s);
exit(-2);
}
} // ReadHistory
//-----------------------------------------------------------------------------
#ifndef BULLET
void Moveable::doCollisionAnalysis ( float,float ) { /* Empty by Default. */ }
#endif
#define ISECT_STEP_SIZE 0.4f
#define COLLISION_SPHERE_RADIUS 0.6f
#define max(m,n) ((m)>(n) ? (m) : (n)) /* return highest number */
//-----------------------------------------------------------------------------
float Moveable::collectIsectData ( sgVec3 start, sgVec3 end )
{
sgVec3 vel ;
m_collided = m_crashed = false ; /* Initial assumption */
sgSubVec3 ( vel, end, start ) ;
const float SPEED = sgLengthVec3 ( vel ) ;
/*
At higher speeds, we must test frequently so we can't
pass through something thin by mistake.
At very high speeds, this is getting costly...so beware!
*/
int nsteps = (int) ceil ( SPEED / ISECT_STEP_SIZE ) ;
if ( nsteps == 0 ) nsteps = 1 ;
if ( nsteps > 100 )
{
fprintf(stderr, "WARNING: Speed too high for collision detection!\n"
"WARNING: Nsteps=%d, Speed=%f!\n"
"moveable %p, vel=%f,%f,%f\n",
nsteps, SPEED, this, vel[0], vel[1], vel[2]);
nsteps = 100 ;
}
sgScaleVec3 ( vel, vel, 1.0f / (float) nsteps ) ;
sgVec3 pos1, pos2 ;
sgCopyVec3 ( pos1, start ) ;
float hot = -9999.0 ;
for ( int i = 0 ; i < nsteps ; i++ )
{
sgAddVec3 ( pos2, pos1, vel ) ;
float hot1 = getIsectData ( pos1, pos2 ) ;
hot = max(hot, hot1);
sgCopyVec3 ( pos1, pos2 ) ;
if(m_collided) break;
}
sgCopyVec3 ( end, pos2 ) ;
return hot ;
} // collectIsectData
//-----------------------------------------------------------------------------
float Moveable::getIsectData ( sgVec3 start, sgVec3 end )
{
int num_hits;
sgSphere sphere;
/*
It's necessary to lift the center of the bounding sphere
somewhat so that Player can stand on a slope.
*/
sphere.setRadius ( COLLISION_SPHERE_RADIUS ) ;
sphere.setCenter ( 0.0f, 0.0f, COLLISION_SPHERE_RADIUS + 0.3f) ;
/* Do a bounding-sphere test for Player. */
sgSetVec3 ( m_surface_avoidance_vector, 0.0f, 0.0f, 0.0f );
// new collision algorithm
AllHits a;
sphere.setCenter ( end[0],end[1],end[2]+ COLLISION_SPHERE_RADIUS + 0.3f) ;
num_hits = world->Collision(&sphere, &a);
for(AllHits::iterator i=a.begin(); i!=a.end(); i++)
{
if ( (*i)->m_plane[2]>0.4 ) continue;
const float DIST = sphere.getRadius()-(*i)->m_dist;
sgVec3 nrm ;
sgCopyVec3 ( nrm, (*i)->m_plane ) ;
sgScaleVec3 ( nrm, nrm, DIST ) ;
sgAddVec3 ( m_surface_avoidance_vector, nrm ) ;
sgVec3 tmp ;
sgCopyVec3 ( tmp, sphere.getCenter() ) ;
sgAddVec3 ( tmp, nrm ) ;
sphere.setCenter ( tmp ) ;
m_collided = true ;
Material* m = material_manager->getMaterial( (*i)->m_leaf);
if (m->isZipper () ) m_collided = false ;
if (m->isCrashable() ) m_crashed = true ;
if (m->isReset () ) OutsideTrack(1);
} // for i in a
sgAddVec3(end, m_surface_avoidance_vector);
// H.O.T == Height Of Terrain
// ==========================
const float TOP = COLLISION_SPHERE_RADIUS + max(start[2],end[2]);
sgVec3 dstart; sgCopyVec3(dstart, end);
sgVec3 dummy; sgCopyVec3(dummy, end);
dummy[2]=TOP;
ssgLeaf* m_leaf;
const float HOT = world->GetHOT(dummy, dummy, &m_leaf, &m_normal_hot);
if(m_leaf)
{
m_material_hot = material_manager->getMaterial(m_leaf);
// Only rescue the kart if it (nearly) touches the reset-material,
// not only when it is above it. The condition for touching
// a material is coarser then for the m_on_ground condition
// (which tests for <0.01) - since the kart might have been falling
// for quite some time, it might be really fast, so I guess a somewhat
// coarser test is better for that case.
if(m_material_hot->isReset() &&
fabs(TOP-COLLISION_SPHERE_RADIUS - HOT)<0.2) OutsideTrack(1);
if(m_material_hot->isZipper()) handleZipper();
}
else
{
OutsideTrack(0);
}
if (end[2] < HOT )
{
end[2] = HOT ;
} // end[2]<HOT
return HOT ;
} // getIsectData