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:
parent
e5fd4c885c
commit
0a44bf57d5
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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(); }
|
||||
|
||||
private:
|
||||
typedef Buffer<ReplayFrame> BufferFrame;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
// 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)
|
||||
{
|
||||
|
@ -130,6 +130,7 @@ private:
|
||||
sgCoord init_pos);
|
||||
|
||||
#ifdef HAVE_GHOST_REPLAY
|
||||
private:
|
||||
void pushReplayFrameData();
|
||||
bool saveReplayHumanReadable( std::string const &filename ) const;
|
||||
bool loadReplayHumanReadable( std::string const &filename );
|
||||
@ -137,6 +138,7 @@ private:
|
||||
ReplayRecorder m_replay_recorder;
|
||||
ReplayPlayer *m_p_replay_player;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
extern World* world;
|
||||
|
Loading…
Reference in New Issue
Block a user