replay ghost works very basic.

* no interpolation of ghosts position between sampled frames
* no transparency or something else to distinguish

to see it:
* run a game, rename file replay/test.rph to replay/test1.rph - this file is shown in replay

git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/trunk/supertuxkart@1253 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
ikework 2007-09-21 18:02:53 +00:00
parent e5fd4c885c
commit 0a44bf57d5
8 changed files with 240 additions and 65 deletions

View File

@ -23,7 +23,6 @@
#ifdef HAVE_GHOST_REPLAY #ifdef HAVE_GHOST_REPLAY
#include <new>
#include <string> #include <string>
#include <plib/sg.h> #include <plib/sg.h>
@ -60,7 +59,6 @@ public:
virtual void destroy(); virtual void destroy();
bool loadReplayHumanReadable( FILE *fd, size_t number_cars ) { return m_ReplayBuffers.loadReplayHumanReadable( fd, number_cars ); }
bool saveReplayHumanReadable( FILE *fd ) const { return m_ReplayBuffers.saveReplayHumanReadable( fd ); } bool saveReplayHumanReadable( FILE *fd ) const { return m_ReplayBuffers.saveReplayHumanReadable( fd ); }
private: private:

View File

@ -60,16 +60,16 @@ private:
public: public:
bool init( size_t number_preallocated_objects ); bool init( size_t number_preallocated_objects );
void destroy(); void destroy();
// this is false, if a reallocation failed
bool isHealthy() const { return m_healthy; } bool isHealthy() const { return m_healthy; }
// returns a new *free* object, allocated memory if necessary // returns a new *free* object, allocated memory if necessary
T* getNewObject(); T* getNewObject();
// returns a new *free* array of objects
//T* getNewObjects( size_t number_objects );
// returs object at given position, like usual array access, // returs object at given position, like usual array access,
// does not allocate memory // does not allocate memory, index must be < getNumberObjectsUsed()
T const* getObjectAt( size_t index ) const; T const* getObjectAt( size_t index ) const;
T* getObjectAt( size_t index ); T* getObjectAt( size_t index );
size_t getNumberObjectsUsed() const { return m_number_objects_used; } size_t getNumberObjectsUsed() const { return m_number_objects_used; }
size_t getNumberBlocks() const { return m_number_blocks; }
private: private:
// adds a new block of objects to m_pp_blocks with a size of m_block_size // adds a new block of objects to m_pp_blocks with a size of m_block_size
@ -121,6 +121,7 @@ public:
T const* getArrayAt( size_t index ) const { assert( m_array_size ); return m_Buffer.getObjectAt( m_array_size * index ); } T const* getArrayAt( size_t index ) const { assert( m_array_size ); return m_Buffer.getObjectAt( m_array_size * index ); }
T* getArrayAt( size_t index ) { assert( m_array_size ); return m_Buffer.getObjectAt( m_array_size * index ); } T* getArrayAt( size_t index ) { assert( m_array_size ); return m_Buffer.getObjectAt( m_array_size * index ); }
size_t getNumberArraysUsed() const { return m_Buffer.getNumberObjectsUsed() / m_array_size; } size_t getNumberArraysUsed() const { return m_Buffer.getNumberObjectsUsed() / m_array_size; }
size_t getNumberBlocks() const { return m_Buffer.getNumberBlocks(); }
bool isHealthy() const { return m_Buffer.isHealthy(); } bool isHealthy() const { return m_Buffer.isHealthy(); }
private: private:

View File

@ -57,24 +57,18 @@ ReplayBuffers::getNewFrame()
return frame; return frame;
} }
ReplayFrame const*
ReplayBuffers::getFrame( size_t frame_index ) const
{
return m_BufferFrame.getObjectAt( frame_index );
}
bool ReplayBuffers::saveReplayHumanReadable( FILE *fd ) const bool ReplayBuffers::saveReplayHumanReadable( FILE *fd ) const
{ {
if( !isHealthy() ) return false; if( !isHealthy() ) return false;
if( fprintf( fd, "frames: %u\n", getNumberFramesUsed() ) < 1 ) return false; if( fprintf( fd, "frames: %u\n", getNumberFrames() ) < 1 ) return false;
unsigned int frame_idx, kart_idx; unsigned int frame_idx, kart_idx;
ReplayFrame const *frame; ReplayFrame const *frame;
ReplayKartState const *kart; ReplayKartState const *kart;
for( frame_idx = 0; frame_idx < getNumberFramesUsed(); ++frame_idx ) for( frame_idx = 0; frame_idx < getNumberFrames(); ++frame_idx )
{ {
frame = getFrame( frame_idx ); frame = getFrameAt( frame_idx );
if( fprintf( fd, "frame %u time %f\n", frame_idx, frame->time ) < 1 ) return false; if( fprintf( fd, "frame %u time %f\n", frame_idx, frame->time ) < 1 ) return false;
for( kart_idx = 0; kart_idx < m_number_cars; ++kart_idx ) for( kart_idx = 0; kart_idx < m_number_cars; ++kart_idx )
@ -100,14 +94,13 @@ bool ReplayBuffers::loadReplayHumanReadable( FILE *fd, size_t number_cars )
unsigned int frame_idx, kart_idx, tmp; unsigned int frame_idx, kart_idx, tmp;
ReplayFrame *frame; ReplayFrame *frame;
ReplayKartState *kart; ReplayKartState *kart;
for( frame_idx = 0; frame_idx < getNumberFramesUsed(); ++frame_idx ) for( frame_idx = 0; frame_idx < frames; ++frame_idx )
{ {
frame = m_BufferFrame.getObjectAt( frame_idx ); // if we are here, it cant fail, since enough objects have to be allocated above
frame = getNewFrame();
assert( frame ); assert( frame );
if( fscanf( fd, "frame %u time %f\n", &tmp, &frame->time ) != 2 ) return false;
frame->p_kart_states = m_BufferKartState.getArrayAt( frame_idx ); if( fscanf( fd, "frame %u time %f\n", &tmp, &frame->time ) != 2 ) return false;
assert( frame->p_kart_states );
for( kart_idx = 0; kart_idx < number_cars; ++kart_idx ) for( kart_idx = 0; kart_idx < number_cars; ++kart_idx )
{ {
@ -119,6 +112,14 @@ bool ReplayBuffers::loadReplayHumanReadable( FILE *fd, size_t number_cars )
} }
} }
assert( frames == getNumberFrames() );
assert( m_BufferFrame.getNumberObjectsUsed() == getNumberFrames() );
assert( m_BufferKartState.getNumberArraysUsed() == getNumberFrames() );
// there should be no reallocation ..
assert( m_BufferFrame.getNumberBlocks() == 1 );
assert( m_BufferKartState.getNumberBlocks() == 1 );
return true; return true;
} }

View File

@ -24,7 +24,6 @@
#include <cstdio> #include <cstdio>
#include <new>
#include "replay_buffer_tpl.hpp" #include "replay_buffer_tpl.hpp"
@ -51,14 +50,16 @@ public:
// returs frame at given position from replay data // returs frame at given position from replay data
// used to *show* the replay // used to *show* the replay
ReplayFrame const* getFrame( size_t frame_index ) const; // if frame_index >= num_frames -> returns NULL
ReplayFrame const* getFrameAt( size_t frame_index ) const { return m_BufferFrame.getObjectAt( frame_index ); }
size_t getNumberFrames() const { return m_BufferFrame.getNumberObjectsUsed(); }
bool saveReplayHumanReadable( FILE *fd ) const; bool saveReplayHumanReadable( FILE *fd ) const;
bool loadReplayHumanReadable( FILE *fd, size_t number_cars ); bool loadReplayHumanReadable( FILE *fd, size_t number_cars );
private: private:
bool isHealthy() const { return m_BufferFrame.isHealthy() && m_BufferKartState.isHealthy(); } bool isHealthy() const { return m_BufferFrame.isHealthy() && m_BufferKartState.isHealthy(); }
size_t getNumberFramesUsed() const { return m_BufferFrame.getNumberObjectsUsed(); }
private: private:
typedef Buffer<ReplayFrame> BufferFrame; typedef Buffer<ReplayFrame> BufferFrame;

View File

@ -1,11 +1,137 @@
#ifdef HAVE_GHOST_REPLAY #ifdef HAVE_GHOST_REPLAY
#include "kart_properties_manager.hpp"
#include "kart_properties.hpp"
#include "replay_player.hpp" #include "replay_player.hpp"
ReplayKart::ReplayKart()
: m_kart_properties(NULL), m_model(NULL)
{
}
ReplayKart::~ReplayKart()
{
destroy();
}
void ReplayKart::destroy()
{
m_kart_properties = NULL;
}
bool ReplayKart::init( const std::string &strKartIdent )
{
assert( !m_kart_properties );
m_model = new ssgTransform();
m_model->ref();
m_kart_properties = kart_properties_manager->getKart( strKartIdent );
if( NULL == m_kart_properties ) return false;
ssgEntity *obj = m_kart_properties->getModel();
assert( obj );
// Optimize the model, this can't be done while loading the model
// because it seems that it removes the name of the wheels or something
// else needed to load the wheels as a separate object.
ssgFlatten(obj);
ssgRangeSelector *lod = new ssgRangeSelector;
float r [ 2 ] = { -10.0f, 100.0f } ;
lod -> addKid ( obj ) ;
lod -> setRanges ( r, 2 ) ;
m_model -> addKid ( lod ) ;
return true;
}
#include "scene.hpp"
ReplayPlayer::ReplayPlayer()
: ReplayBase(), m_current_frame_index(-1)
{
}
ReplayPlayer::~ReplayPlayer()
{
destroy();
}
void ReplayPlayer::destroy()
{
m_current_frame_index = -1;
m_Karts.clear();
ReplayBase::destroy();
}
bool ReplayPlayer::loadReplayHumanReadable( FILE *fd )
{
destroy();
bool blnRet = false;
int intTemp;
char buff[1000];
size_t number_karts;
if( fscanf( fd, "Version: %s\n", buff ) != 1 ) return false;
if( fscanf( fd, "numkarts: %u\n", &number_karts ) != 1 ) return false;
if( fscanf( fd, "numplayers: %s\n", buff ) != 1 ) return false;
if( fscanf( fd, "difficulty: %s\n", buff ) != 1 ) return false;
if( fscanf( fd, "track: %s\n", buff ) != 1 ) return false;
for( size_t k = 0; k < number_karts; ++k )
{
if( fscanf( fd, "model %d: %s\n", &intTemp, buff ) != 2 ) return false;
m_Karts.resize( m_Karts.size() + 1 );
ReplayKart &kart = m_Karts[ m_Karts.size() - 1 ];
if( !kart.init( buff ) ) return false;
scene->add ( kart.getModel() );
}
if( !m_ReplayBuffers.loadReplayHumanReadable( fd, number_karts ) ) return false;
m_current_frame_index = 0;
updateObjects();
return true;
}
void ReplayPlayer::showReplayAt( float abs_time )
{
assert( m_current_frame_index > -1 );
assert( (size_t)m_current_frame_index < m_ReplayBuffers.getNumberFrames() );
ReplayFrame const* frame;
// find the current frame, we only scroll forward ..
while(1)
{
// end reached?
if( (m_current_frame_index + 1) == m_ReplayBuffers.getNumberFrames() ) break;
// check time of next frame
frame = m_ReplayBuffers.getFrameAt( m_current_frame_index+1 );
if( frame->time > abs_time ) break;
++m_current_frame_index;
}
updateObjects();
}
void ReplayPlayer::updateObjects()
{
ReplayFrame const* frame = m_ReplayBuffers.getFrameAt( m_current_frame_index );
for( size_t k = 0; k < m_Karts.size(); ++k )
{
m_Karts[ k ].setPosition( frame->p_kart_states[ k ].position );
}
}
#endif // HAVE_GHOST_REPLAY #endif // HAVE_GHOST_REPLAY

View File

@ -22,23 +22,67 @@
#ifdef HAVE_GHOST_REPLAY #ifdef HAVE_GHOST_REPLAY
#include <vector>
#include <plib/ssg.h>
#include "replay_base.hpp" #include "replay_base.hpp"
class KartProperties;
class ReplayKart
{
public:
ReplayKart();
~ReplayKart();
ReplayKart( ReplayKart const &kart ) { *this = kart; }
ReplayKart& operator=( ReplayKart const &kart )
{
assert( this != &kart );
m_kart_properties = kart.m_kart_properties;
m_model = kart.m_model;
sgCopyCoord ( &m_position, &kart.m_position );
return *this;
}
bool init( const std::string &strKartIdent );
void destroy();
ssgTransform* getModel() { return m_model; }
void setPosition( const sgCoord &pos ) { sgCopyCoord ( &m_position, &pos ); m_model->setTransform(&m_position); }
private:
const KartProperties *m_kart_properties;
sgCoord m_position;
ssgTransform *m_model;
};
// class managing: // class managing:
// - the loading of replay-file // - the loading of replay-file
// - the rendering of the replay (interpolation if needed) // - the rendering of the replay (interpolation if needed)
class ReplayPlayer : public ReplayBase class ReplayPlayer : public ReplayBase
{ {
public: public:
ReplayPlayer() : ReplayBase() {} ReplayPlayer();
virtual ~ReplayPlayer() { destroy(); } virtual ~ReplayPlayer();
void destroy() { ReplayBase::destroy(); } void destroy();
bool loadReplayHumanReadable( FILE *fd );
void showReplayAt( float abs_time );
private:
void updateObjects();
private:
typedef std::vector<ReplayKart> ReplayKarts;
int m_current_frame_index;
ReplayKarts m_Karts;
}; };
#endif // HAVE_GHOST_REPLAY #endif // HAVE_GHOST_REPLAY
#endif // HEADER_REPLAYPLAYER_H #endif // HEADER_REPLAYPLAYER_H

View File

@ -52,7 +52,6 @@
#include "highscore_manager.hpp" #include "highscore_manager.hpp"
#include "scene.hpp" #include "scene.hpp"
#include "camera.hpp" #include "camera.hpp"
#include "robots/default_robot.hpp" #include "robots/default_robot.hpp"
#ifdef HAVE_GHOST_REPLAY #ifdef HAVE_GHOST_REPLAY
# include "replay_player.hpp" # include "replay_player.hpp"
@ -207,12 +206,14 @@ World::World(const RaceSetup& raceSetup_) : m_race_setup(raceSetup_)
#ifdef HAVE_GHOST_REPLAY #ifdef HAVE_GHOST_REPLAY
m_replay_recorder.initRecorder( m_race_setup.getNumKarts() ); m_replay_recorder.initRecorder( m_race_setup.getNumKarts() );
m_p_replay_player = new ReplayPlayer; m_p_replay_player = new ReplayPlayer;
if( !loadReplayHumanReadable( "test1" ) ) if( !loadReplayHumanReadable( "test1" ) )
{ {
delete m_p_replay_player; delete m_p_replay_player;
m_p_replay_player = NULL; m_p_replay_player = NULL;
} }
if( m_p_replay_player ) m_p_replay_player->showReplayAt( m_clock );
#endif #endif
} }
@ -292,9 +293,13 @@ void World::draw()
void World::update(float delta) void World::update(float delta)
{ {
if(user_config->m_replay_history) delta=history->GetNextDelta(); if(user_config->m_replay_history) delta=history->GetNextDelta();
m_clock += delta;
checkRaceStatus(); checkRaceStatus();
// this line was before checkRaceStatus. but m_clock is set to 0.0 in
// checkRaceStatus on start, so m_clock would not be synchron and the
// first delta would not be added .. that would cause a gap in
// replay-recording
m_clock += delta;
// Count the number of collision in the next 'FRAMES_FOR_TRAFFIC_JAM' frames. // Count the number of collision in the next 'FRAMES_FOR_TRAFFIC_JAM' frames.
// If a kart has more than one hit, play 'traffic jam' noise. // If a kart has more than one hit, play 'traffic jam' noise.
@ -400,7 +405,13 @@ void World::update(float delta)
} }
#ifdef HAVE_GHOST_REPLAY #ifdef HAVE_GHOST_REPLAY
// we start recording after START_PHASE, since during start-phase m_clock is incremented
// normally, but after switching to RACE_PHASE m_clock is set back to 0.0
if( m_phase != START_PHASE )
{
pushReplayFrameData(); pushReplayFrameData();
if( m_p_replay_player ) m_p_replay_player->showReplayAt( m_clock );
}
#endif #endif
} }
@ -408,6 +419,9 @@ void World::update(float delta)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void World::pushReplayFrameData() void World::pushReplayFrameData()
{ {
// we dpnt record the startphase ..
assert( m_phase != START_PHASE );
ReplayFrame *pFrame = m_replay_recorder.getNewFrame(); ReplayFrame *pFrame = m_replay_recorder.getNewFrame();
if( !pFrame ) return; if( !pFrame ) return;
@ -457,9 +471,11 @@ bool World::saveReplayHumanReadable( std::string const &filename ) const
fprintf(fd, "numplayers: %d\n", m_race_setup.getNumPlayers()); fprintf(fd, "numplayers: %d\n", m_race_setup.getNumPlayers());
fprintf(fd, "difficulty: %d\n", m_race_setup.m_difficulty); fprintf(fd, "difficulty: %d\n", m_race_setup.m_difficulty);
fprintf(fd, "track: %s\n", m_track->getIdent()); fprintf(fd, "track: %s\n", m_track->getIdent());
for( size_t k = 0; k < m_kart.size(); ++k )
for (RaceSetup::Karts::const_iterator i = m_race_setup.m_karts.begin() ;
i != m_race_setup.m_karts.end() ; ++i )
{ {
fprintf(fd, "model %d: %s\n",k, m_kart[k]->getName().c_str()); fprintf(fd, "model %d: %s\n", i-m_race_setup.m_karts.begin(), (*i).c_str());
} }
if( !m_replay_recorder.saveReplayHumanReadable( fd ) ) if( !m_replay_recorder.saveReplayHumanReadable( fd ) )
{ {
@ -501,26 +517,8 @@ bool World::loadReplayHumanReadable( std::string const &filename )
return false; return false;
} }
bool blnRet = false; bool blnRet = m_p_replay_player->loadReplayHumanReadable( fd );
int intTemp;
char buff[1000];
size_t number_karts;
size_t frames;
if( fscanf( fd, "Version: %s\n", buff ) != 1 ) goto close;
if( fscanf( fd, "numkarts: %u\n", &number_karts ) != 1 ) goto close;
if( fscanf( fd, "numplayers: %s\n", buff ) != 1 ) goto close;
if( fscanf( fd, "difficulty: %s\n", buff ) != 1 ) goto close;
if( fscanf( fd, "track: %s\n", buff ) != 1 ) goto close;
for( size_t k = 0; k < m_kart.size(); ++k )
{
if( fscanf( fd, "model %d: %s\n", &intTemp, buff ) != 2 ) goto close;
}
if( !m_p_replay_player->loadReplayHumanReadable( fd, number_karts ) ) goto close;
blnRet = true;
close:
fclose( fd ); fd = NULL; fclose( fd ); fd = NULL;
return blnRet; return blnRet;
@ -542,6 +540,10 @@ void World::checkRaceStatus()
m_phase = RACE_PHASE; m_phase = RACE_PHASE;
m_clock = 0.0f; m_clock = 0.0f;
sound_manager->playSfx(SOUND_START); sound_manager->playSfx(SOUND_START);
#ifdef HAVE_GHOST_REPLAY
// push positions at time 0.0 to replay-data
pushReplayFrameData();
#endif
} }
else if (m_clock > 1.0 && m_ready_set_go == 2) else if (m_clock > 1.0 && m_ready_set_go == 2)
{ {

View File

@ -130,6 +130,7 @@ private:
sgCoord init_pos); sgCoord init_pos);
#ifdef HAVE_GHOST_REPLAY #ifdef HAVE_GHOST_REPLAY
private:
void pushReplayFrameData(); void pushReplayFrameData();
bool saveReplayHumanReadable( std::string const &filename ) const; bool saveReplayHumanReadable( std::string const &filename ) const;
bool loadReplayHumanReadable( std::string const &filename ); bool loadReplayHumanReadable( std::string const &filename );
@ -137,6 +138,7 @@ private:
ReplayRecorder m_replay_recorder; ReplayRecorder m_replay_recorder;
ReplayPlayer *m_p_replay_player; ReplayPlayer *m_p_replay_player;
#endif #endif
}; };
extern World* world; extern World* world;