etqw-sdk/source/game/demos/DemoManager.cpp
2008-05-29 00:00:00 +00:00

735 lines
20 KiB
C++

// Copyright (C) 2007 Id Software, Inc.
//
#include "../precompiled.h"
#pragma hdrstop
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#include "DemoManager.h"
#include "DemoScript.h"
#include "../Player.h"
#include "../guis/UserInterfaceLocal.h"
//===============================================================
//
// sdDemoProperties
//
//===============================================================
/*
================
sdDemoProperties::GetProperty
================
*/
sdProperties::sdProperty* sdDemoProperties::GetProperty( const char* name ) {
return properties.GetProperty( name, sdProperties::PT_INVALID, false );
}
/*
============
sdDemoProperties::GetProperty
============
*/
sdProperties::sdProperty* sdDemoProperties::GetProperty( const char* name, sdProperties::ePropertyType type ) {
sdProperties::sdProperty* prop = properties.GetProperty( name, sdProperties::PT_INVALID, false );
if ( prop && prop->GetValueType() != type ) {
gameLocal.Error( "sdDemoProperties::GetProperty: type mismatch for property '%s'", name );
}
return prop;
}
/*
================
sdDemoProperties::Init
================
*/
void sdDemoProperties::Init() {
properties.RegisterProperty( "state", state );
properties.RegisterProperty( "time", time );
properties.RegisterProperty( "size", size );
properties.RegisterProperty( "position", position );
properties.RegisterProperty( "frame", frame );
properties.RegisterProperty( "cutIsSet", cutIsSet );
properties.RegisterProperty( "cutStartMarker", cutStartMarker );
properties.RegisterProperty( "cutEndMarker", cutEndMarker );
properties.RegisterProperty( "demoName", demoName );
properties.RegisterProperty( "writingMDF", writingMDF );
properties.RegisterProperty( "mdfName", mdfName );
properties.RegisterProperty( "viewOrigin", viewOrigin );
properties.RegisterProperty( "viewAngles", viewAngles );
}
/*
================
sdDemoProperties::Shutdown
================
*/
void sdDemoProperties::Shutdown() {
properties.Clear();
}
/*
================
sdDemoProperties::UpdateProperties
================
*/
void sdDemoProperties::UpdateProperties() {
state = sdDemoManager::GetInstance().GetState();
time = MS2SEC( sdDemoManager::GetInstance().GetTime() );
size = sdDemoManager::GetInstance().GetSize();
position = sdDemoManager::GetInstance().GetPosition();
frame = sdDemoManager::GetInstance().GetFrame();
cutIsSet = sdDemoManager::GetInstance().CutIsSet() ? 1.0f : 0.0f;
cutStartMarker = sdDemoManager::GetInstance().GetCutStartMarker();
cutEndMarker = sdDemoManager::GetInstance().GetCutEndMarker();
demoName = networkSystem->GetDemoName();
writingMDF = sdDemoManager::GetInstance().WritingMDF() ? 1.0f : 0.0f;
mdfName = sdDemoManager::GetInstance().GetMDFFileName();
const renderView_t& view = sdDemoManager::GetInstance().GetRenderedView();
viewOrigin = view.vieworg;
idAngles angles = view.viewaxis.ToAngles();
viewAngles = idVec3( angles.pitch, angles.yaw, angles.roll );
}
//===============================================================
//
// sdDemoManagerLocal
//
//===============================================================
idCVar sdDemoManagerLocal::g_demoOutputMDF( "g_demoOutputMDF", "0", CVAR_GAME | CVAR_INTEGER, "output entity keyframe data from demo", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
idCVar sdDemoManagerLocal::g_showDemoHud( "g_showDemoHud", "0", CVAR_GAME | CVAR_BOOL, "draw the demo hud gui" );
idCVar sdDemoManagerLocal::g_showDemoView( "g_showDemoView", "0", CVAR_GAME | CVAR_BOOL, "show player's calculated view when paused instead of free-fly cam" );
idCVar sdDemoManagerLocal::g_demoAnalyze( "g_demoAnalyze", "0", CVAR_GAME | CVAR_BOOL, "analyze demo during playback" );
idCVar sdDemoManagerLocal::demo_noclip( "demo_noclip", "0", CVAR_SYSTEM | CVAR_BOOL, "noclip through a demo" );
/*
================
sdDemoManagerLocal::sdDemoManagerLocal
================
*/
sdDemoManagerLocal::~sdDemoManagerLocal() {
}
/*
================
sdDemoManagerLocal::Init
================
*/
void sdDemoManagerLocal::Init() {
sdDemoScript::InitClass();
cameraFactory.RegisterType( sdDemoCamera_Fixed::GetType(), sdCameraFactory::Allocator< sdDemoCamera_Fixed > );
cameraFactory.RegisterType( sdDemoCamera_Anim::GetType(), sdCameraFactory::Allocator< sdDemoCamera_Anim > );
state = DS_NONE;
script = NULL;
activeCamera = NULL;
memset( &renderedView, 0, sizeof( renderView_t ) );
demoGameFrames = 0;
pausedTime = previousPausedTime = 0;
melFrames = 0;
viewAngles.Zero();
deltaViewAngles.Zero();
viewOrigin.Zero();
currentVelocity.Zero();
melDataFile = NULL;
localDemoProperties.Init();
}
/*
================
sdDemoManagerLocal::InitGUIs
================
*/
void sdDemoManagerLocal::InitGUIs() {
hud = gameLocal.LoadUserInterface( "guis/demos/hud", false, true );
}
/*
============
sdDemoManagerLocal::GetPosition
============
*/
float sdDemoManagerLocal::GetPosition() const {
if( endPosition == startPosition ) {
return 0;
}
return ( position - startPosition ) / static_cast< float >( endPosition - startPosition );
}
/*
============
sdDemoManagerLocal::GetCutStartMarker
============
*/
float sdDemoManagerLocal::GetCutStartMarker() const {
if( endPosition == startPosition ) {
return 0;
}
return ( cutStartMarker - startPosition ) / static_cast< float >( endPosition - startPosition );
}
/*
============
sdDemoManagerLocal::GetCutEndMarker
============
*/
float sdDemoManagerLocal::GetCutEndMarker() const {
if( endPosition == startPosition ) {
return 0;
}
return ( cutEndMarker - startPosition ) / static_cast< float >( endPosition - startPosition );
}
/*
================
sdDemoManagerLocal::StartDemo
================
*/
void sdDemoManagerLocal::StartDemo() {
state = static_cast< demoState_t >( networkSystem->GetDemoState( time, position, length, startPosition, endPosition, cutStartMarker, cutEndMarker ) );
if ( state != DS_PLAYING ) {
return;
}
viewOrigin.Zero();
viewAngles.Zero();
currentVelocity.Zero();
deltaViewAngles.Zero();
renderedView.vieworg.Zero();
renderedView.viewaxis.Identity();
script = new sdDemoScript;
if ( !script->Parse( networkSystem->GetDemoName() ) ) {
delete script;
script = NULL;
}
activeCamera = NULL;
demoGameFrames = 0;
if ( g_demoOutputMDF.GetInteger() > 0 ) {
melDataFileName = "demos/";
melDataFileName += networkSystem->GetDemoName();
if ( g_demoOutputMDF.GetInteger() == 2 ) {
sysTime_t time;
sys->RealTime( &time );
melDataFileName += va( "_%d%02d%02d_%02d%02d%02d", 1900 + time.tm_year, 1 + time.tm_mon, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec );
}
melDataFileName.SetFileExtension( "mdf" );
melDataFile = fileSystem->OpenFileWrite( melDataFileName, "fs_devpath" );
melFrames = 0;
if ( melDataFile ) {
melDataFile->WriteInt( 0 ); // dummy for numFrames
melDataFile->WriteInt( ENTITYNUM_MAX_NORMAL );
// melDataFile->WriteInt( g_demoOutputMDF.GetInteger() == 2 ? 1 : 0 ); // timestamps in demo?
melDataFile->WriteInt( 1 ); // timestamps in demo?
melPrimitives.AssureSize( ENTITYNUM_MAX_NORMAL, NULL );
}
}
if ( g_demoAnalyze.GetBool() ) {
demoAnalyzer.Start();
}
sdUserInterfaceLocal* ui = gameLocal.GetUserInterface( hud );
if ( ui && !ui->IsActive() ) {
ui->Activate();
}
localDemoProperties.UpdateProperties();
}
/*
================
sdDemoManagerLocal::EndDemo
================
*/
void sdDemoManagerLocal::EndDemo() {
state = DS_NONE;
delete script;
script = NULL;
melDataFileName.Clear();
if ( melDataFile ) {
melDataFile->Seek( 0, FS_SEEK_SET );
melDataFile->WriteInt( melFrames );
fileSystem->CloseFile( melDataFile );
melDataFile = NULL;
melPrimitives.DeleteContents( true );
}
if ( demoAnalyzer.IsActive() ) {
demoAnalyzer.Stop();
}
sdUserInterfaceLocal* ui = gameLocal.GetUserInterface( hud );
if ( ui && ui->IsActive() ) {
ui->Deactivate();
}
}
/*
================
sdDemoManagerLocal::RunDemoFrame
================
*/
void sdDemoManagerLocal::RunDemoFrame( const usercmd_t* demoCmd ) {
if ( demoCmd ) {
this->demoCmd = *demoCmd;
} else {
memset( &( this->demoCmd ), 0, sizeof( usercmd_t ) );
if ( gameLocal.localClientNum >= 0 && gameLocal.localClientNum < MAX_CLIENTS ) {
for( int i = 0; i < 3; i++ ) {
this->demoCmd.angles[ i ] = gameLocal.usercmds[ gameLocal.localClientNum ].angles[ i ];
}
}
}
// update demo state
demoState_t oldState = state;
previousTime = time;
state = static_cast< demoState_t >( networkSystem->GetDemoState( time, position, length, startPosition, endPosition, cutStartMarker, cutEndMarker ) );
switch( state ) {
case DS_PAUSED:
if ( oldState != DS_PAUSED ) {
if ( !demo_noclip.GetBool() ) {
idPlayer* player = gameLocal.GetLocalViewPlayer();
if ( player != NULL ) {
viewOrigin = player->firstPersonViewOrigin;
viewAngles = player->viewAngles;
}
currentVelocity.Zero();
}
for( int i = 0; i < 3; i++ ) {
deltaViewAngles[ i ] = viewAngles[ i ] - SHORT2ANGLE( this->demoCmd.angles[ i ] );
}
pausedTime = sys->Milliseconds();
}
previousPausedTime = pausedTime;
pausedTime = sys->Milliseconds();
break;
case DS_PLAYING:
if ( script ) {
script->RunFrame();
}
if ( activeCamera ) {
activeCamera->RunFrame();
}
if ( demoAnalyzer.IsActive() ) {
demoAnalyzer.RunFrame();
}
break;
}
localDemoProperties.UpdateProperties();
}
/*
================
sdDemoManagerLocal::EndDemoFrame
================
*/
void sdDemoManagerLocal::EndDemoFrame() {
if ( state == DS_PLAYING && melDataFile && gameLocal.isNewFrame ) {
const sdDeclTargetInfo* targetInfoDecl = gameLocal.GetMDFExportTargets();
idList< idEntity* > filteredEntities;
idList< idEntity* > entityList;
idList< int > entityNumList;
// if ( g_demoOutputMDF.GetInteger() == 2 ) {
// write timestamp, assumes demo is played back in normal speed (33 msec frames)
melDataFile->WriteInt( ( gameLocal.time - gameLocal.startTime ) / USERCMD_MSEC );
// }
//
// write camera data
//
idVec3 cameraOrigin;
idMat3 cameraAxis;
idAngles cameraAngles;
cameraOrigin = renderedView.vieworg;
cameraAxis[ 0 ] = -renderedView.viewaxis[ 1 ];
cameraAxis[ 1 ] = renderedView.viewaxis[ 2 ];
cameraAxis[ 2 ] = -renderedView.viewaxis[ 0 ];
cameraOrigin.ToMayaSelf();
cameraAxis.ToMayaSelf();
cameraAngles = cameraAxis.ToAnglesMaya();
// origin
melDataFile->WriteDouble( cameraOrigin.x );
melDataFile->WriteDouble( cameraOrigin.y );
melDataFile->WriteDouble( cameraOrigin.z );
// angles
melDataFile->WriteDouble( cameraAngles.pitch );
melDataFile->WriteDouble( cameraAngles.yaw );
melDataFile->WriteDouble( cameraAngles.roll );
//
// filter entities and make a list of entities to hide
//
for ( int i = 0; i < ENTITYNUM_MAX_NORMAL; i++ ) {
idEntity* ent = gameLocal.entities[ i ];
if ( !ent ) {
if ( melPrimitives[ i ] && melPrimitives[ i ]->visible ) {
entityNumList.Append( i );
}
continue;
}
if ( targetInfoDecl ) {
if ( !targetInfoDecl->FilterEntity( ent ) ) {
if ( melPrimitives[ ent->entityNumber ] && melPrimitives[ ent->entityNumber ]->visible ) {
entityNumList.Append( ent->entityNumber );
}
continue;
}
}
filteredEntities.Append( ent );
}
// write list
melDataFile->WriteInt( entityNumList.Num() );
for ( int i = 0; i < entityNumList.Num(); i++ ) {
melDataFile->WriteInt( entityNumList[ i ] );
}
//
// build list of entities to create
//
entityList.Clear();
for ( int i = 0; i < filteredEntities.Num(); i++ ) {
idEntity* ent = filteredEntities[ i ];
if ( !melPrimitives[ ent->entityNumber ] ) {
melPrimitives[ ent->entityNumber ] = new melPrimitive_t;
melPrimitives[ ent->entityNumber ]->visible = false;
melPrimitives[ ent->entityNumber ]->bounds.Zero();
entityList.Append( ent );
}
}
// write list
melDataFile->WriteInt( entityList.Num() );
for ( int i = 0; i < entityList.Num(); i++ ) {
melDataFile->WriteInt( entityList[ i ]->entityNumber );
}
//
// build list of entities to make visible
//
entityList.Clear();
for ( int i = 0; i < filteredEntities.Num(); i++ ) {
idEntity* ent = filteredEntities[ i ];
if ( !melPrimitives[ ent->entityNumber ]->visible ) {
melPrimitives[ ent->entityNumber ]->visible = true;
entityList.Append( ent );
}
}
// write list
melDataFile->WriteInt( entityList.Num() );
for ( int i = 0; i < entityList.Num(); i++ ) {
melDataFile->WriteInt( entityList[ i ]->entityNumber );
}
//
// build list of entity bounding box updates
//
entityList.Clear();
for ( int i = 0; i < filteredEntities.Num(); i++ ) {
idEntity* ent = filteredEntities[ i ];
if ( !melPrimitives[ ent->entityNumber ]->bounds.Compare( ent->GetPhysics()->GetBounds() ) && !ent->GetPhysics()->GetBounds().IsCleared() ) {
melPrimitives[ ent->entityNumber ]->bounds = ent->GetPhysics()->GetBounds();
//if ( !melPrimitives[ ent->entityNumber ]->bounds.Compare( ent->GetRenderEntity()->bounds ) ) {
// melPrimitives[ ent->entityNumber ]->bounds = ent->GetRenderEntity()->bounds;
entityList.Append( ent );
}
}
// write list
melDataFile->WriteInt( entityList.Num() );
for ( int i = 0; i < entityList.Num(); i++ ) {
idEntity* ent = entityList[ i ];
melDataFile->WriteInt( ent->entityNumber );
// size information
melDataFile->WriteDouble( melPrimitives[ ent->entityNumber ]->bounds.GetMaxs().x - melPrimitives[ ent->entityNumber ]->bounds.GetMins().x );
melDataFile->WriteDouble( melPrimitives[ ent->entityNumber ]->bounds.GetMaxs().y - melPrimitives[ ent->entityNumber ]->bounds.GetMins().y );
melDataFile->WriteDouble( melPrimitives[ ent->entityNumber ]->bounds.GetMaxs().z - melPrimitives[ ent->entityNumber ]->bounds.GetMins().z );
}
//
// write list of entity origin and angles
//
melDataFile->WriteInt( filteredEntities.Num() );
for ( int i = 0; i < filteredEntities.Num(); i++ ) {
idEntity* ent = filteredEntities[ i ];
melDataFile->WriteInt( ent->entityNumber );
// origin
idVec3 originMaya = (ent->GetPhysics()->GetOrigin()).ToMaya();
//idVec3 originMaya = (ent->GetRenderEntity()->origin).ToMaya();
melDataFile->WriteDouble( originMaya.x );
melDataFile->WriteDouble( originMaya.y );
melDataFile->WriteDouble( originMaya.z );
// angles
if ( ent->IsType( idPlayer::Type ) ) {
melDataFile->WriteDouble( ent->GetPhysics()->GetAxis().ToAngles().roll );
melDataFile->WriteDouble( static_cast<const idPlayer *>(ent)->viewAngles.yaw - 90.f );
melDataFile->WriteDouble( -ent->GetPhysics()->GetAxis().ToAngles().pitch );
} else {
idMat3 mayaAxis;
idAngles anglesMaya;
mayaAxis[ 0 ] = -ent->GetPhysics()->GetAxis()[ 1 ];
mayaAxis[ 1 ] = ent->GetPhysics()->GetAxis()[ 2 ];
mayaAxis[ 2 ] = -ent->GetPhysics()->GetAxis()[ 0 ];
mayaAxis.ToMayaSelf();
anglesMaya = mayaAxis.ToAnglesMaya();
melDataFile->WriteDouble( anglesMaya.pitch );
melDataFile->WriteDouble( anglesMaya.yaw );
melDataFile->WriteDouble( anglesMaya.roll );
}
}
melFrames++;
}
if ( gameLocal.isNewFrame && state != DS_PAUSED ) {
demoGameFrames++;
}
}
/*
================
sdDemoManagerLocal::SetActiveCamera
================
*/
void sdDemoManagerLocal::SetActiveCamera( sdDemoCamera* camera ) {
activeCamera = camera;
activeCamera->Start();
// jrad - ensure that this is running updates
sdPostProcess* postProcess = gameLocal.localPlayerProperties.GetPostProcess();
if( postProcess != NULL && !postProcess->Enabled() ) {
postProcess->Enable( true );
}
}
/*
================
sdDemoManagerLocal::CalculateRenderView
================
*/
bool sdDemoManagerLocal::CalculateRenderView( renderView_t* renderView ) {
if ( sdDemoManager::GetInstance().NoClip() ) {
// jrad - ensure that this is running updates
sdPostProcess* postProcess = gameLocal.localPlayerProperties.GetPostProcess();
if( postProcess != NULL && !postProcess->Enabled() ) {
postProcess->Enable( true );
}
// update camera origin
for( int i = 0; i < 3; i++ ) {
viewAngles[ i ] = idMath::AngleNormalize180( SHORT2ANGLE( demoCmd.angles[ i ] ) + deltaViewAngles[ i ] );
}
viewAngles.pitch = idMath::ClampFloat( -89.f, 89.f, viewAngles.pitch );
for( int i = 0; i < 3; i++ ) {
deltaViewAngles[ i ] = viewAngles[ i ] - SHORT2ANGLE( demoCmd.angles[ i ] );
}
// calculate vectors
idVec3 gravityNormal = gameLocal.GetGravity();
idVec3 viewForward, viewRight;
gravityNormal.Normalize();
viewForward = viewAngles.ToForward();
viewForward.Normalize();
viewRight = gravityNormal.Cross( viewForward );
viewRight.Normalize();
// scale user command
int max;
float scale;
int forwardmove;
int rightmove;
int upmove;
forwardmove = demoCmd.forwardmove;
rightmove = demoCmd.rightmove;
upmove = demoCmd.upmove;
max = abs( forwardmove );
if ( abs( rightmove ) > max ) {
max = abs( rightmove );
}
if ( abs( upmove ) > max ) {
max = abs( upmove );
}
if ( !max ) {
scale = 0.0f;
} else {
float total = idMath::Sqrt( (float) forwardmove * forwardmove + rightmove * rightmove + upmove * upmove );
scale = pm_democamspeed.GetFloat() * max / ( 127.0f * total );
}
// move
float frametime;
if ( state == DS_PAUSED ) {
frametime = MS2SEC( pausedTime - previousPausedTime );
} else {
frametime = MS2SEC( gameLocal.msec );
}
float speed, drop, friction, newspeed, stopspeed;
float wishspeed;
idVec3 wishdir;
// friction
speed = currentVelocity.Length();
if ( speed < 20.f ) {
currentVelocity.Zero();
}
else {
stopspeed = pm_democamspeed.GetFloat() * 0.3f;
if ( speed < stopspeed ) {
speed = stopspeed;
}
friction = 12.0f /*PM_NOCLIPFRICTION*/;
drop = speed * friction * frametime;
// scale the velocity
newspeed = speed - drop;
if (newspeed < 0) {
newspeed = 0;
}
currentVelocity *= newspeed / speed;
}
// accelerate
wishdir = scale * (viewForward * demoCmd.forwardmove + viewRight * demoCmd.rightmove);
wishdir -= scale * gravityNormal * demoCmd.upmove;
wishspeed = wishdir.Normalize();
wishspeed *= scale;
// q2 style accelerate
float addspeed, accelspeed, currentspeed;
currentspeed = currentVelocity * wishdir;
addspeed = wishspeed - currentspeed;
if ( addspeed > 0 ) {
accelspeed = 10.0f /*PM_ACCELERATE*/ * frametime * wishspeed;
if ( accelspeed > addspeed ) {
accelspeed = addspeed;
}
currentVelocity += accelspeed * wishdir;
}
// move
viewOrigin += frametime * currentVelocity;
renderView->viewID = 0;
renderView->vieworg = viewOrigin;
renderView->viewaxis = viewAngles.ToMat3();
renderView->fov_x = g_fov.GetFloat();
renderView->time = gameLocal.time;
renderView->x = 0;
renderView->y = 0;
renderView->width = SCREEN_WIDTH;
renderView->height = SCREEN_HEIGHT;
return true;
} else {
currentVelocity.Zero();
}
if ( state == DS_PLAYING ) {
if ( activeCamera ) {
renderView->viewID = 0;
renderView->vieworg = activeCamera->GetOrigin();
renderView->viewaxis = activeCamera->GetAxis();
renderView->fov_x = activeCamera->GetFov();
renderView->time = gameLocal.time;
renderView->x = 0;
renderView->y = 0;
renderView->width = SCREEN_WIDTH;
renderView->height = SCREEN_HEIGHT;
return true;
}
}
return false;
}
/*
================
sdDemoManagerLocal::Shutdown
================
*/
void sdDemoManagerLocal::Shutdown() {
localDemoProperties.Shutdown();
cameraFactory.Shutdown();
delete script;
sdDemoScript::Shutdown();
}