From 0a44bf57d544914c2e22fc1ff5649b11385c3967 Mon Sep 17 00:00:00 2001 From: ikework Date: Fri, 21 Sep 2007 18:02:53 +0000 Subject: [PATCH] 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 --- src/replay_base.hpp | 2 - src/replay_buffer_tpl.hpp | 7 +- src/replay_buffers.cpp | 29 ++++---- src/replay_buffers.hpp | 9 +-- src/replay_player.cpp | 148 +++++++++++++++++++++++++++++++++++--- src/replay_player.hpp | 52 ++++++++++++-- src/world.cpp | 50 ++++++------- src/world.hpp | 8 ++- 8 files changed, 240 insertions(+), 65 deletions(-) diff --git a/src/replay_base.hpp b/src/replay_base.hpp index 94dc8d376..f946d49f3 100644 --- a/src/replay_base.hpp +++ b/src/replay_base.hpp @@ -23,7 +23,6 @@ #ifdef HAVE_GHOST_REPLAY -#include #include #include @@ -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: diff --git a/src/replay_buffer_tpl.hpp b/src/replay_buffer_tpl.hpp index b167d5b40..d8be375f2 100644 --- a/src/replay_buffer_tpl.hpp +++ b/src/replay_buffer_tpl.hpp @@ -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: diff --git a/src/replay_buffers.cpp b/src/replay_buffers.cpp index e71550911..f714123c0 100644 --- a/src/replay_buffers.cpp +++ b/src/replay_buffers.cpp @@ -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; } diff --git a/src/replay_buffers.hpp b/src/replay_buffers.hpp index c1a36224e..ab63f9199 100644 --- a/src/replay_buffers.hpp +++ b/src/replay_buffers.hpp @@ -24,7 +24,6 @@ #include -#include #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 BufferFrame; diff --git a/src/replay_player.cpp b/src/replay_player.cpp index 06045bb6f..1b94bee5a 100644 --- a/src/replay_player.cpp +++ b/src/replay_player.cpp @@ -1,11 +1,137 @@ -#ifdef HAVE_GHOST_REPLAY - -#include "replay_player.hpp" - - - - - - - -#endif // HAVE_GHOST_REPLAY +#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 diff --git a/src/replay_player.hpp b/src/replay_player.hpp index 88b54ae4b..e753e04eb 100644 --- a/src/replay_player.hpp +++ b/src/replay_player.hpp @@ -22,23 +22,67 @@ #ifdef HAVE_GHOST_REPLAY +#include +#include + #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 ReplayKarts; + + int m_current_frame_index; + ReplayKarts m_Karts; }; - #endif // HAVE_GHOST_REPLAY #endif // HEADER_REPLAYPLAYER_H diff --git a/src/world.cpp b/src/world.cpp index 47664f529..8afc449e0 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -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) { diff --git a/src/world.hpp b/src/world.hpp index 8c8285097..1d64bae16 100644 --- a/src/world.hpp +++ b/src/world.hpp @@ -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;