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
#include <new>
#include <string>
#include <plib/sg.h>
@ -60,7 +59,6 @@ public:
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 ); }
private:

View File

@ -60,16 +60,16 @@ private:
public:
bool init( size_t number_preallocated_objects );
void destroy();
// this is false, if a reallocation failed
bool isHealthy() const { return m_healthy; }
// returns a new *free* object, allocated memory if necessary
T* getNewObject();
// returns a new *free* array of objects
//T* getNewObjects( size_t number_objects );
// 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* getObjectAt( size_t index );
size_t getNumberObjectsUsed() const { return m_number_objects_used; }
size_t getNumberBlocks() const { return m_number_blocks; }
private:
// 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* 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 getNumberBlocks() const { return m_Buffer.getNumberBlocks(); }
bool isHealthy() const { return m_Buffer.isHealthy(); }
private:

View File

@ -57,24 +57,18 @@ ReplayBuffers::getNewFrame()
return frame;
}
ReplayFrame const*
ReplayBuffers::getFrame( size_t frame_index ) const
{
return m_BufferFrame.getObjectAt( frame_index );
}
bool ReplayBuffers::saveReplayHumanReadable( FILE *fd ) const
{
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;
ReplayFrame const *frame;
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;
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;
ReplayFrame *frame;
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 );
if( fscanf( fd, "frame %u time %f\n", &tmp, &frame->time ) != 2 ) return false;
frame->p_kart_states = m_BufferKartState.getArrayAt( frame_idx );
assert( frame->p_kart_states );
if( fscanf( fd, "frame %u time %f\n", &tmp, &frame->time ) != 2 ) return false;
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;
}

View File

@ -24,7 +24,6 @@
#include <cstdio>
#include <new>
#include "replay_buffer_tpl.hpp"
@ -51,14 +50,16 @@ public:
// returs frame at given position from replay data
// 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 loadReplayHumanReadable( FILE *fd, size_t number_cars );
private:
bool isHealthy() const { return m_BufferFrame.isHealthy() && m_BufferKartState.isHealthy(); }
size_t getNumberFramesUsed() const { return m_BufferFrame.getNumberObjectsUsed(); }
bool isHealthy() const { return m_BufferFrame.isHealthy() && m_BufferKartState.isHealthy(); }
private:
typedef Buffer<ReplayFrame> BufferFrame;

View File

@ -1,11 +1,137 @@
#ifdef HAVE_GHOST_REPLAY
#include "kart_properties_manager.hpp"
#include "kart_properties.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

View File

@ -22,23 +22,67 @@
#ifdef HAVE_GHOST_REPLAY
#include <vector>
#include <plib/ssg.h>
#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:
// - the loading of replay-file
// - the rendering of the replay (interpolation if needed)
class ReplayPlayer : public ReplayBase
{
public:
ReplayPlayer() : ReplayBase() {}
virtual ~ReplayPlayer() { destroy(); }
ReplayPlayer();
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 // HEADER_REPLAYPLAYER_H

View File

@ -52,7 +52,6 @@
#include "highscore_manager.hpp"
#include "scene.hpp"
#include "camera.hpp"
#include "robots/default_robot.hpp"
#ifdef HAVE_GHOST_REPLAY
# include "replay_player.hpp"
@ -207,12 +206,14 @@ World::World(const RaceSetup& raceSetup_) : m_race_setup(raceSetup_)
#ifdef HAVE_GHOST_REPLAY
m_replay_recorder.initRecorder( m_race_setup.getNumKarts() );
m_p_replay_player = new ReplayPlayer;
if( !loadReplayHumanReadable( "test1" ) )
{
delete m_p_replay_player;
m_p_replay_player = NULL;
}
if( m_p_replay_player ) m_p_replay_player->showReplayAt( m_clock );
#endif
}
@ -292,9 +293,13 @@ void World::draw()
void World::update(float delta)
{
if(user_config->m_replay_history) delta=history->GetNextDelta();
m_clock += delta;
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.
// 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
pushReplayFrameData();
// 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();
if( m_p_replay_player ) m_p_replay_player->showReplayAt( m_clock );
}
#endif
}
@ -408,6 +419,9 @@ void World::update(float delta)
//-----------------------------------------------------------------------------
void World::pushReplayFrameData()
{
// we dpnt record the startphase ..
assert( m_phase != START_PHASE );
ReplayFrame *pFrame = m_replay_recorder.getNewFrame();
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, "difficulty: %d\n", m_race_setup.m_difficulty);
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 ) )
{
@ -501,26 +517,8 @@ bool World::loadReplayHumanReadable( std::string const &filename )
return false;
}
bool blnRet = false;
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;
}
bool blnRet = m_p_replay_player->loadReplayHumanReadable( fd );
if( !m_p_replay_player->loadReplayHumanReadable( fd, number_karts ) ) goto close;
blnRet = true;
close:
fclose( fd ); fd = NULL;
return blnRet;
@ -542,6 +540,10 @@ void World::checkRaceStatus()
m_phase = RACE_PHASE;
m_clock = 0.0f;
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)
{

View File

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