Replaced all sg* data structures in the camera with portable (and bullet

based) data structures (except for the final call to set the ssg
camera).


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/trunk/supertuxkart@2127 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk
2008-06-10 02:35:10 +00:00
parent a9c3c61cc0
commit bbbf1a0479
12 changed files with 265 additions and 185 deletions

View File

@@ -27,6 +27,7 @@ AM_CPPFLAGS=-DSUPERTUXKART_DATADIR="\"$(datadir)/games/@PACKAGE@/\""
supertuxkart_SOURCES = main.cpp \
vec3.cpp vec3.hpp \
coord.hpp \
actionmap.cpp actionmap.hpp \
material.cpp material.hpp \
material_manager.cpp material_manager.hpp \

View File

@@ -19,6 +19,7 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <plib/ssg.h>
#include "coord.hpp"
#include "world.hpp"
#include "player_kart.hpp"
#include "track_manager.hpp"
@@ -29,15 +30,12 @@
Camera::Camera(int camera_index, const Kart* kart)
{
m_mode = CM_NORMAL;
m_context = new ssgContext ;
m_distance = kart->getKartProperties()->getCameraDistance();
m_kart = kart;
btVector3 start_pos = m_kart->getPos();
sgSetVec3(m_current_pos.xyz, start_pos.getX(), start_pos.getY(), start_pos.getZ());
sgSetVec3(m_current_pos.xyz, 0, 0, 0);
sgSetVec3(m_current_pos.hpr, 0, 0, 0);
m_mode = CM_NORMAL;
m_context = new ssgContext ;
m_distance = kart->getKartProperties()->getCameraDistance();
m_kart = kart;
m_xyz = kart->getPos();
m_hpr = Vec3(0,0,0);
// FIXME: clipping should be configurable for slower machines
const Track* track = world->getTrack();
@@ -102,14 +100,10 @@ void Camera::setMode(Mode mode)
const Track* track=world->getTrack();
// If the track doesn't have a final position, ignore this mode
if(!track->hasFinalCamera()) return;
btVector3 coord(m_current_pos.xyz[0],m_current_pos.xyz[1],
m_current_pos.xyz[2]);
m_velocity = (track->getCameraPosition()-coord)/1.0f;
btVector3 rotation(m_current_pos.hpr[0],m_current_pos.hpr[1],
m_current_pos.hpr[2]);
// Rotate faster
m_angular_velocity = (track->getCameraHPR()-rotation)/1.0f;
m_final_time=0.0f;
const float duration = 1.0f;
m_velocity = (track->getCameraPosition()-m_xyz)/duration;
m_angular_velocity = (track->getCameraHPR()-m_hpr)/duration;
m_final_time = 0.0f;
}
m_mode = mode;
m_last_pitch = 0.0f;
@@ -125,78 +119,73 @@ void Camera::setMode(Mode mode)
*/
void Camera::reset()
{
setMode(CM_NORMAL);
m_last_pitch = 0.0f;
m_xyz = m_kart->getPos();
m_hpr = Vec3(0,0,0);
} // reset
//-----------------------------------------------------------------------------
void Camera::update (float dt)
{
sgCoord kartcoord;
const Kart *kart;
if(m_mode==CM_FINAL) return finalCamera(dt);
Vec3 kart_xyz, kart_hpr;
const Kart *kart;
// First define the position of the kart
if(m_mode==CM_LEADER_MODE)
{
kart=world->getKart(0);
sgCopyCoord(&kartcoord, kart->getCoord());
kart = world->getKart(0);
kart_hpr = kart->getRotation();
}
else
{
kart = m_kart;
sgCopyCoord(&kartcoord, kart->getCoord());
kart = m_kart;
kart_hpr = kart->getRotation();
// Use the terrain pitch to avoid the camera following a wheelie the kart is doing
kartcoord.hpr[1]=RAD_TO_DEGREE(m_kart->getTerrainPitch(DEGREE_TO_RAD(kartcoord.hpr[0])) );
kartcoord.hpr[2] = 0;
// Only adjust the pitch if it's not the first frame (which is indicated by having
// dt=0). Otherwise the camera will change pitch during ready-set-go.
if(dt>0)
kart_hpr.setPitch( m_kart->getTerrainPitch(kart_hpr.getHeading()) );
kart_hpr.setRoll(0.0f);
// Only adjust the pitch if it's not the race start, otherwise
// the camera will change pitch during ready-set-go.
if(world->isRacePhase())
{
// If the terrain pitch is 'significantly' different from the camera angle,
// start adjusting the camera. This helps with steep declines, where
// otherwise the track is not visible anymore.
if(fabsf(kartcoord.hpr[1]-m_last_pitch)>1.0f) {
kartcoord.hpr[1] = m_last_pitch + (kartcoord.hpr[1]-m_last_pitch)*2.0f*dt;
}
else
{
kartcoord.hpr[1]=m_last_pitch;
if(fabsf(kart_hpr.getPitch()-m_last_pitch)>M_PI/180.0f) {
m_last_pitch = m_last_pitch + (kart_hpr.getPitch()-m_last_pitch)*2.0f*dt;
}
kart_hpr.setPitch(m_last_pitch);
} // dt>0.0
m_last_pitch = kartcoord.hpr[1];
} // m_mode!=CM_LEADER_MODE
if(m_mode==CM_SIMPLE_REPLAY) kartcoord.hpr[0] = 0;
kart_xyz = kart->getPos();
if(m_mode==CM_SIMPLE_REPLAY) kart_hpr.setHeading(0.0f);
// Set the camera position relative to the kart
// --------------------------------------------
sgMat4 cam_pos;
// The reverse mode and the cam used in follow the leader mode (when a
// kart has been eliminated) are facing backwards:
bool reverse= m_mode==CM_REVERSE || m_mode==CM_LEADER_MODE;
sgMakeTransMat4(cam_pos, 0.f, -m_distance, reverse ? 0.75f : 1.5f);
bool reverse = m_mode==CM_REVERSE || m_mode==CM_LEADER_MODE;
Vec3 cam_rel_pos(0.f, -m_distance, reverse ? 0.75f : 1.5f) ;
// Set the camera rotation
// -----------------------
sgMat4 cam_rot;
sgMakeRotMat4(cam_rot, reverse ? 180.0f : 0.0f,
m_mode==CM_CLOSEUP ? -15.0f : -5.0f,
0);
btQuaternion cam_rot(0.0f,
m_mode==CM_CLOSEUP ? -0.2618f : -0.0873f, // -15 or -5 degrees
reverse ? M_PI : 0.0f);
// Camera position relative to the kart
btTransform relative_to_kart(cam_rot, cam_rel_pos);
btMatrix3x3 rotation;
rotation.setEulerZYX(kart_hpr.getPitch(), kart_hpr.getRoll(), kart_hpr.getHeading());
btTransform result = btTransform(rotation, kart_xyz) * relative_to_kart;
// Matrix that transforms stuff to kart-space
sgMat4 tokart;
sgMakeCoordMat4 (tokart, &kartcoord);
sgMat4 relative;
sgMultMat4(relative, cam_pos, cam_rot);
sgMat4 result;
sgMultMat4(result, tokart, relative);
sgSetCoord(&m_current_pos, result);
m_context -> setCamera (&m_current_pos) ;
// Convert transform to coordinate and pass on to plib
Coord c(result);
m_xyz = c.getXYZ();
m_hpr = c.getHPR();
m_context -> setCamera(&c.toSgCoord());
} // update
//-----------------------------------------------------------------------------
@@ -206,18 +195,10 @@ void Camera::finalCamera(float dt)
m_final_time += dt;
if( m_final_time<1.0f )
{
btVector3 coord(m_current_pos.xyz[0],m_current_pos.xyz[1],m_current_pos.xyz[2]);
coord += m_velocity*dt;
printf("y %f vely %f dt %f\n",coord.getY(), m_velocity.getY(), dt);
m_current_pos.xyz[0]=coord.getX();
m_current_pos.xyz[1]=coord.getY();
m_current_pos.xyz[2]=coord.getZ();
btVector3 rotation(m_current_pos.hpr[0],m_current_pos.hpr[1],m_current_pos.hpr[2]);
rotation += m_angular_velocity*dt;
m_current_pos.hpr[0]=rotation.getX();
m_current_pos.hpr[1]=rotation.getY();
m_current_pos.hpr[2]=rotation.getZ();
m_context->setCamera(&m_current_pos);
m_xyz += m_velocity*dt;
m_hpr += m_angular_velocity*dt;
Coord coord(m_xyz, m_hpr);
m_context->setCamera(&coord.toSgCoord());
}
} // finalCamera

View File

@@ -22,7 +22,7 @@
#ifndef HEADER_CAMERA_H
#define HEADER_CAMERA_H
#include "LinearMath/btVector3.h"
#include "vec3.hpp"
class ssgContext;
class Kart;
@@ -41,15 +41,17 @@ public:
};
protected:
ssgContext *m_context ;
sgCoord m_current_pos;
// sgCoord m_current_pos;
Vec3 m_xyz; // current position of camera
Vec3 m_hpr; // heading, pitch, roll of camera
const Kart *m_kart; // the kart the camera is attached to
Mode m_mode; // CM_ value, see above
float m_x, m_y, m_w, m_h; // window to us
float m_current_speed; // current speed of camera
float m_last_pitch; // for tiling the camera when going downhill
float m_distance; // distance between camera and kart
btVector3 m_velocity; // camera velocity for final mode
btVector3 m_angular_velocity; // camera angular velocity for final mode
Vec3 m_velocity; // camera velocity for final mode
Vec3 m_angular_velocity; // camera angular velocity for final mode
float m_final_time; // time when final camera mode started
private:

66
src/coord.hpp Executable file
View File

@@ -0,0 +1,66 @@
// $Id: vec3.hpp 1954 2008-05-20 10:01:26Z scifly $
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2008 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 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.
#ifndef HEADER_COORD_H
#define HEADER_COORD_H
#include <plib/sg.h>
#include "vec3.hpp"
#include "constants.hpp"
#include "LinearMath/btTransform.h"
class Coord
{
private:
Vec3 m_xyz;
Vec3 m_hpr;
sgCoord m_coord;
void setSgCoord()
{
sgSetCoord(&m_coord, m_xyz.toFloat(), m_hpr.toFloat());
// Convert hpr in radians to degrees, which sg needs.
for(int i=0; i<3; i++)
{
m_coord.hpr[i] = RAD_TO_DEGREE(m_coord.hpr[i]);
}
}
public:
Coord(const Vec3& xyz, const Vec3& hpr)
{
m_xyz = xyz;
m_hpr = hpr;
setSgCoord();
} // Coord
// ------------------------------------------------------------------------
Coord(const btTransform& t)
{
m_xyz = t.getOrigin();
m_hpr.setHPR(t.getBasis());
setSgCoord();
} // Coord
// ------------------------------------------------------------------------
const sgCoord& toSgCoord() const { return m_coord; }
const Vec3& getXYZ() const { return m_xyz; }
const Vec3& getHPR() const { return m_hpr; }
}; // Coord
#endif

View File

@@ -106,7 +106,7 @@ void GameManager::run()
if (race_manager->raceIsActive())
{
music_on = false;
if(user_config->m_profile) dt=1.0f/60.0f;
if(user_config->m_profile) dt=1.0f/60.0f;
// In the first call dt might be large (includes loading time),
// which can cause the camera to significantly tilt
scene->draw(world->getPhase()==World::SETUP_PHASE ? 0.0f : dt);
@@ -126,10 +126,10 @@ void GameManager::run()
(float)m_frame_count/(SDL_GetTicks() * 0.001));
if(!user_config->m_replay_history) history->Save();
std::exit(-2);
}
} // if profile finished
} // if m_profile
}
}
} // phase != limbo phase
} // if race is active
else
{
glMatrixMode ( GL_PROJECTION ) ;

View File

@@ -1176,6 +1176,10 @@
RelativePath="../../../src\constants.hpp"
>
</File>
<File
RelativePath="..\..\coord.hpp"
>
</File>
<File
RelativePath="../../../src\cup_data.hpp"
>

View File

@@ -23,6 +23,7 @@
#include "bullet/Demos/OpenGL/GL_ShapeDrawer.h"
#include "loader.hpp"
#include "coord.hpp"
#include "herring_manager.hpp"
#include "sound_manager.hpp"
#include "file_manager.hpp"
@@ -535,8 +536,8 @@ void Kart::update(float dt)
//m_wheel_rotation gives the rotation around the X-axis, and since velocity's
//timeframe is the delta time, we don't have to multiply it with dt.
m_wheel_rotation += m_speed*dt / m_kart_properties->getWheelRadius();
m_wheel_rotation=fmodf(m_wheel_rotation, 2*M_PI);
m_wheel_rotation += m_speed*dt / m_kart_properties->getWheelRadius();
m_wheel_rotation=fmodf(m_wheel_rotation, 2*M_PI);
if ( m_rescue )
{
@@ -552,7 +553,7 @@ void Kart::update(float dt)
world->getPhysics()->removeKart(this);
}
m_curr_pos.xyz[2] += rescue_height*dt/rescue_time;
m_transform.setOrigin(btVector3(m_curr_pos.xyz[0],m_curr_pos.xyz[1],
m_curr_pos.xyz[2]));
btQuaternion q_roll (btVector3(0.f, 1.f, 0.f),
@@ -560,9 +561,7 @@ void Kart::update(float dt)
btQuaternion q_pitch(btVector3(1.f, 0.f, 0.f),
-m_rescue_pitch*dt/rescue_time*M_PI/180.0f);
m_transform.setRotation(m_transform.getRotation()*q_roll*q_pitch);
m_body->setCenterOfMassTransform(m_transform);
//printf("Set %f %f %f\n",pos.getOrigin().x(),pos.getOrigin().y(),pos.getOrigin().z());
m_motion_state->setWorldTransform(m_transform);
} // if m_rescue
m_attachment.update(dt);
@@ -685,9 +684,9 @@ void Kart::handleZipper()
float current_speed = v.length();
float speed = std::min(current_speed+stk_config->m_zipper_speed_gain,
getMaxSpeed());
// Avoid NAN problems, which can happen if e.g. a kart is rescued on
// top of zipper, and then dropped.
if(current_speed>0.00001) m_body->setLinearVelocity(v*(speed/current_speed));
// Avoid NAN problems, which can happen if e.g. a kart is rescued on
// top of zipper, and then dropped.
if(current_speed>0.00001) m_body->setLinearVelocity(v*(speed/current_speed));
} // handleZipper
//-----------------------------------------------------------------------------
#define sgn(x) ((x<0)?-1.0f:((x>0)?1.0f:0.0f))
@@ -1061,12 +1060,12 @@ void Kart::placeModel ()
// replayed.
if(!user_config->m_replay_history)
{
float m[4][4];
getTrans().getOpenGLMatrix((float*)&m);
//printf(" is %f %f %f\n",t.getOrigin().x(),t.getOrigin().y(),t.getOrigin().z());
//float m[4][4];
//getTrans().getOpenGLMatrix((float*)&m);
//sgSetCoord(&m_curr_pos, m);
Coord coord(getTrans());
// Transfer the new position and hpr to m_curr_pos
sgSetCoord(&m_curr_pos, m);
sgCopyCoord(&m_curr_pos, &(coord.toSgCoord()));
}
sgCoord c ;
sgCopyCoord ( &c, &m_curr_pos );

View File

@@ -72,8 +72,9 @@ void Moveable::reset ()
m_material_hot = NULL;
m_normal_hot = NULL;
if(m_body) m_body->setLinearVelocity(btVector3(0.0, 0.0, 0.0));
sgCopyCoord ( &m_curr_pos, &m_reset_pos ) ;
sgCopyCoord( &m_curr_pos, &m_reset_pos );
m_hpr = Vec3(m_curr_pos.hpr);
m_hpr.degreeToRad();
} // reset
//-----------------------------------------------------------------------------
@@ -95,6 +96,8 @@ void Moveable::createBody(float mass, btTransform& trans,
// functions are not called correctly. So only init the pointer to zero.
m_user_pointer.zero();
m_body->setUserPointer(&m_user_pointer);
const btMatrix3x3& basis=m_body->getWorldTransform().getBasis();
m_hpr.setHPR(basis);
} // createBody
//-----------------------------------------------------------------------------
@@ -160,8 +163,8 @@ void Moveable::update (float dt)
} // if m_history_position
m_velocityLC = getVelocity()*getTrans().getBasis();
const btMatrix3x3& basis=m_body->getWorldTransform().getBasis();
m_hpr.setHPR(basis);
m_motion_state->getWorldTransform(m_transform);
m_hpr.setHPR(m_transform.getBasis());
placeModel();
m_first_time = false ;
@@ -170,7 +173,6 @@ void Moveable::update (float dt)
//-----------------------------------------------------------------------------
void Moveable::placeModel()
{
m_motion_state->getWorldTransform(m_transform);
m_model_transform->setTransform(&m_curr_pos);
} // placeModel

View File

@@ -66,7 +66,7 @@ public:
virtual void setVelocity(const btVector3& v) {m_body->setLinearVelocity(v); }
sgCoord* getCoord () {return &m_curr_pos; }
const Vec3& getPos () const {return (Vec3&)m_transform.getOrigin();}
const Vec3& getRotation() const {return m_hpr; }
const Vec3& getRotation() const {return m_hpr; }
const sgCoord* getCoord () const {return &m_curr_pos; }
const sgVec4* getNormalHOT () const {return m_normal_hot; }
const sgCoord* getResetPos () const {return &m_reset_pos; }

View File

@@ -56,8 +56,8 @@ void PlayerKart::reset()
m_controls.jump = false;
m_penalty_time = 0;
m_time_last_crash_sound = -10.0f;
m_camera->setMode(Camera::CM_NORMAL); // can be changed if camera was eliminated
Kart::reset();
m_camera->reset();
} // reset
// ----------------------------------------------------------------------------
@@ -161,7 +161,15 @@ void PlayerKart::update(float dt)
m_penalty_time=1.0;
// A warning gets displayed in RaceGUI
}
placeModel();
else
{
// The call to update is necessary here (even though the kart
// shouldn't actually change) to update m_transform. Otherwise
// the camera gets the wrong position.
// FIXME: that might not necessary anymore once m_curr_pos is removed!
Kart::update(dt);
}
return;
}
if(m_penalty_time>0.0)

View File

@@ -16,64 +16,70 @@
// 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 "vec3.hpp"
void Vec3::setHPR(const btMatrix3x3& m)
{
float f[4][4];
m.getOpenGLSubMatrix((float*)f);
// sgSetCoord(m_curr_pos, f);
//void sgSetCoord ( sgCoord *dst, const sgMat4 src )
float s = m.getColumn(0).length();
if ( s <= 0.00001 )
{
fprintf(stderr,"setHPR: bad matrix\n");
setValue(0,0,0);
return ;
}
s=1/s;
#define CLAMPTO1(x) x<-1 ? -1 : (x>1 ? 1 : x)
setY(asin(CLAMPTO1(m.getRow(2).getY())));
SGfloat cp = cos(getY());
/* If pointing nearly vertically up - then heading is ill-defined */
if ( cp > -0.00001 && cp < 0.00001 )
{
float cr = CLAMPTO1( m.getRow(1).getX()*s);
float sr = CLAMPTO1(-m.getRow(1).getZ()*s);
setX(0.0f);
setZ(atan2(sr, cr ));
}
else
{
cp = s / cp ; // includes the scaling factor
float sr = CLAMPTO1( -m.getRow(2).getX() * cp );
float cr = CLAMPTO1( m.getRow(2).getZ() * cp );
float sh = CLAMPTO1( -m.getRow(0).getY() * cp );
float ch = CLAMPTO1( m.getRow(1).getY() * cp );
if ( (sh == 0.0f && ch == 0.0f) || (sr == 0.0f && cr == 0.0f) )
{
cr = CLAMPTO1( m.getRow(1).getX()*s);
sr = CLAMPTO1(-m.getRow(1).getZ()*s) ;
setX(0.0f);
}
else
setX(atan2(sh, ch ));
setZ(atan2(sr, cr ));
}
} // setHPR
// ----------------------------------------------------------------------------
#include "vec3.hpp"
#include "constants.hpp"
void Vec3::setHPR(const btMatrix3x3& m)
{
float f[4][4];
m.getOpenGLSubMatrix((float*)f);
float s = m.getColumn(0).length();
if ( s <= 0.00001 )
{
fprintf(stderr,"setHPR: bad matrix\n");
setValue(0,0,0);
return ;
}
s=1/s;
#define CLAMPTO1(x) x<-1 ? -1 : (x>1 ? 1 : x)
setY(asin(CLAMPTO1(m.getRow(2).getY())));
SGfloat cp = cos(getY());
/* If pointing nearly vertically up - then heading is ill-defined */
if ( cp > -0.00001 && cp < 0.00001 )
{
float cr = CLAMPTO1( m.getRow(1).getX()*s);
float sr = CLAMPTO1(-m.getRow(1).getZ()*s);
setX(0.0f);
setZ(atan2(sr, cr ));
}
else
{
cp = s / cp ; // includes the scaling factor
float sr = CLAMPTO1( -m.getRow(2).getX() * cp );
float cr = CLAMPTO1( m.getRow(2).getZ() * cp );
float sh = CLAMPTO1( -m.getRow(0).getY() * cp );
float ch = CLAMPTO1( m.getRow(1).getY() * cp );
if ( (sh == 0.0f && ch == 0.0f) || (sr == 0.0f && cr == 0.0f) )
{
cr = CLAMPTO1( m.getRow(1).getX()*s);
sr = CLAMPTO1(-m.getRow(1).getZ()*s) ;
setX(0.0f);
}
else
setX(atan2(sh, ch ));
setZ(atan2(sr, cr ));
}
} // setHPR
// ----------------------------------------------------------------------------
void Vec3::degreeToRad()
{
m_x=DEGREE_TO_RAD(m_x);
m_y=DEGREE_TO_RAD(m_y);
m_z=DEGREE_TO_RAD(m_z);
} // degreeToRad
// ----------------------------------------------------------------------------

View File

@@ -16,27 +16,38 @@
// 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_VEC3_H
#define HEADER_VEC3_H
#include <plib/sg.h>
#ifndef HEADER_VEC3_H
#define HEADER_VEC3_H
#include <plib/sg.h>
#include "LinearMath/btVector3.h"
#include "LinearMath/btMatrix3x3.h"
class Vec3 : public btVector3
{
private:
inline float clampToUnity(float f) {return f<-1?f:(f>1?1:f);}
public:
inline Vec3(sgVec3 a) : btVector3(a[0], a[1], a[2]) {}
inline Vec3(const btVector3& a) : btVector3(a) {}
inline Vec3() : btVector3() {}
void setHPR(const btMatrix3x3& m);
inline const float operator[](int n) const {return *(&m_x+n); }
float* toFloat() const {return (float*)this; }
Vec3& operator=(const btVector3& a) {*(btVector3*)this=a; return *this;}
Vec3& operator=(const btMatrix3x3& m) {setHPR(m); return *this;}
}; // Vec3
#endif
#include "LinearMath/btMatrix3x3.h"
class Vec3 : public btVector3
{
private:
inline float clampToUnity(float f) {return f<-1?f:(f>1?1:f);}
public:
inline Vec3(sgVec3 a) : btVector3(a[0], a[1], a[2]) {}
inline Vec3(const btVector3& a) : btVector3(a) {}
inline Vec3() : btVector3() {}
inline Vec3(float x, float y, float z)
: btVector3(x,y,z) {}
void setHPR(const btMatrix3x3& m);
inline const float operator[](int n) const {return *(&m_x+n); }
inline const float getHeading() const {return m_x; }
inline const float getPitch() const {return m_y; }
inline const float getRoll() const {return m_z; }
inline const void setHeading(float f) {m_x = f;}
inline const void setPitch(float f) {m_y = f;}
inline const void setRoll(float f) {m_z = f;}
float* toFloat() const {return (float*)this; }
void degreeToRad();
Vec3& operator=(const btVector3& a) {*(btVector3*)this=a; return *this;}
Vec3& operator=(const btMatrix3x3& m) {setHPR(m); return *this;}
}; // Vec3
#endif