1
0
forked from 0ad/0ad

# Fixes for simulation speed in scenario edtior. Various code cleanups.

Game, Simulation, etc: Separated 'update' and 'interpolate', and made
'update' return whether it's going fast enough (so callers can decide to
do more updates per render). Changed some time variables to 'double' so
they have enough precision in long games.
Atlas: Added "Fast" playback button. Made simulation sometimes go at
real-time speed, if it's just slightly too slow at rendering.
VertexBuffer: Removed some non-useful glGetError calls.
Entity: Commented out redundant Tick code. Fixed syntax error in
disabled code that confused the IDE.
Aura: Changed string code again, to simply use ASCII instead of UTF-16.
(SpiderMonkey seems to handle it just as efficiently, for small
strings.)
Misc: Some more minor header-file cleanup.
SVN log: Added feed link.

This was SVN commit r4807.
This commit is contained in:
Ykkrosh 2007-01-24 20:17:28 +00:00
parent 5f45e08799
commit ee3243ff92
28 changed files with 251 additions and 168 deletions

View File

@ -9,6 +9,7 @@
#include "MapWriter.h" #include "MapWriter.h"
#include "Model.h" #include "Model.h"
#include "ObjectBase.h" #include "ObjectBase.h"
#include "ObjectEntry.h"
#include "ObjectManager.h" #include "ObjectManager.h"
#include "Patch.h" #include "Patch.h"
#include "Terrain.h" #include "Terrain.h"
@ -571,5 +572,3 @@ void CMapWriter::RewriteAllMaps(CTerrain* pTerrain, CUnitManager* pUnitMan,
writer.SaveMap(n, pTerrain, pUnitMan, pWaterMan, pSkyMan, pLightEnv, pCamera, pCinema); writer.SaveMap(n, pTerrain, pUnitMan, pWaterMan, pSkyMan, pLightEnv, pCamera, pCinema);
} }
} }

View File

@ -14,7 +14,6 @@
#include "renderer/Renderer.h" #include "renderer/Renderer.h"
#include "renderer/WaterManager.h" #include "renderer/WaterManager.h"
#include "simulation/EntityManager.h"
#include "simulation/Entity.h" #include "simulation/Entity.h"
#include "TerrainProperties.h" #include "TerrainProperties.h"

View File

@ -9,12 +9,12 @@
#include "lib/res/graphics/unifont.h" #include "lib/res/graphics/unifont.h"
#include "lib/sysdep/sysdep.h" #include "lib/sysdep/sysdep.h"
#include "maths/MathUtil.h" #include "maths/MathUtil.h"
#include "network/Client.h"
#include "network/Server.h"
#include "ps/CLogger.h" #include "ps/CLogger.h"
#include "ps/Globals.h" #include "ps/Globals.h"
#include "ps/Hotkey.h" #include "ps/Hotkey.h"
#include "ps/Interact.h" #include "ps/Interact.h"
#include "network/Client.h"
#include "network/Server.h"
#include "ps/Pyrogenesis.h" #include "ps/Pyrogenesis.h"
#include "scripting/ScriptingHost.h" #include "scripting/ScriptingHost.h"
#include "simulation/Entity.h" #include "simulation/Entity.h"

View File

@ -157,27 +157,30 @@ PSRETURN CGame::StartGame(CGameAttributes *pAttribs)
return 0; return 0;
} }
void CGame::Update(double deltaTime) bool CGame::Update(double deltaTime, bool doInterpolate)
{ {
if( m_Paused ) if (m_Paused)
{ return true;
return;
}
deltaTime *= m_SimRate; deltaTime *= m_SimRate;
m_Time += deltaTime; m_Time += deltaTime;
m_Simulation->Update(deltaTime); bool ok = m_Simulation->Update(deltaTime);
if (doInterpolate)
m_Simulation->Interpolate(deltaTime);
// TODO Detect game over and bring up the summary screen or something // TODO Detect game over and bring up the summary screen or something
// ^ Quick game over hack is implemented, no summary screen however // ^ Quick game over hack is implemented, no summary screen however
if ( m_World->GetEntityManager()->GetDeath() ) if (m_World->GetEntityManager().GetDeath())
{ {
UpdateGameStatus(); UpdateGameStatus();
if (GameStatus != 0) if (GameStatus != 0)
EndGame(); EndGame();
} }
//reset death event flag //reset death event flag
m_World->GetEntityManager()->SetDeath(false); m_World->GetEntityManager().SetDeath(false);
return ok;
} }
void CGame::UpdateGameStatus() void CGame::UpdateGameStatus()
@ -188,7 +191,7 @@ void CGame::UpdateGameStatus()
for (int i=0; i<MAX_HANDLES; i++) for (int i=0; i<MAX_HANDLES; i++)
{ {
CHandle *handle = m_World->GetEntityManager()->getHandle(i); CHandle *handle = m_World->GetEntityManager().getHandle(i);
if ( !handle ) if ( !handle )
continue; continue;
CPlayer *tmpPlayer = handle->m_entity->GetPlayer(); CPlayer *tmpPlayer = handle->m_entity->GetPlayer();

View File

@ -2,7 +2,6 @@
#define _ps_Game_H #define _ps_Game_H
#include "ps/Errors.h" #include "ps/Errors.h"
#include "maths/MathUtil.h"
#include <vector> #include <vector>
class CWorld; class CWorld;
@ -28,7 +27,7 @@ class CGame
bool m_GameStarted; bool m_GameStarted;
float m_Time; double m_Time; // needs to be double to get enough precision
float m_SimRate; float m_SimRate;
enum EOG enum EOG
@ -58,7 +57,14 @@ public:
/* /*
Perform all per-frame updates Perform all per-frame updates
*/ */
void Update(double deltaTime);
// Returns false if it can't keep up with the desired simulation rate
// (indicating that you might want to render less frequently, or something).
// TODO: doInterpolate is optional because Atlas interpolates explicitly,
// so that it has more control over the update rate. The game might want to
// do the same, and then doInterpolate should be redundant and removed.
bool Update(double deltaTime, bool doInterpolate = true);
void UpdateGameStatus(); void UpdateGameStatus();
void EndGame(); void EndGame();
@ -90,11 +96,11 @@ public:
inline CSimulation *GetSimulation() inline CSimulation *GetSimulation()
{ return m_Simulation; } { return m_Simulation; }
inline float GetTime() inline double GetTime()
{ return m_Time; } { return m_Time; }
inline void SetSimRate(float simRate) inline void SetSimRate(float simRate)
{ m_SimRate=clamp(simRate, 0.0f, simRate); } { m_SimRate = std::max(simRate, 0.0f); }
inline float GetSimRate() inline float GetSimRate()
{ return m_SimRate; } { return m_SimRate; }

View File

@ -48,8 +48,8 @@ public:
{ return m_Terrain; } { return m_Terrain; }
inline CUnitManager &GetUnitManager() inline CUnitManager &GetUnitManager()
{ return *m_UnitManager; } { return *m_UnitManager; }
inline CEntityManager *GetEntityManager() inline CEntityManager &GetEntityManager()
{ return m_EntityManager; } { return *m_EntityManager; }
inline CProjectileManager &GetProjectileManager() inline CProjectileManager &GetProjectileManager()
{ return *m_ProjectileManager; } { return *m_ProjectileManager; }
inline CLOSManager *GetLOSManager() inline CLOSManager *GetLOSManager()

View File

@ -49,14 +49,19 @@ CVertexBuffer::CVertexBuffer(size_t vertexSize,bool dynamic)
// (and copy all old VBOs into there, because it needs to be // (and copy all old VBOs into there, because it needs to be
// consistent). // consistent).
glGetError(); // clear the error state // (PT: Disabled the VBOFailed test because it's not very useful at the
// moment (it'll just cause the program to terminate, and I don't think
// it has ever helped to discover any problems, and a later oglCheck()
// will tell us if there were any VBO issues anyway), and so it's a
// waste of time to call glGetError so frequently.)
// glGetError(); // clear the error state
pglGenBuffersARB(1,&m_Handle); pglGenBuffersARB(1,&m_Handle);
pglBindBufferARB(GL_ARRAY_BUFFER_ARB,m_Handle); pglBindBufferARB(GL_ARRAY_BUFFER_ARB,m_Handle);
if (glGetError() != GL_NO_ERROR) throw PSERROR_Renderer_VBOFailed(); // if (glGetError() != GL_NO_ERROR) throw PSERROR_Renderer_VBOFailed();
pglBufferDataARB(GL_ARRAY_BUFFER_ARB,size,0,m_Dynamic ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB); pglBufferDataARB(GL_ARRAY_BUFFER_ARB,size,0,m_Dynamic ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB);
if (glGetError() != GL_NO_ERROR) throw PSERROR_Renderer_VBOFailed(); // if (glGetError() != GL_NO_ERROR) throw PSERROR_Renderer_VBOFailed();
} else { } else {
m_SysMem=new u8[size]; m_SysMem=new u8[size];
@ -198,10 +203,10 @@ void CVertexBuffer::UpdateChunkVertices(VBChunk* chunk,void* data)
{ {
if (g_Renderer.m_Caps.m_VBO) { if (g_Renderer.m_Caps.m_VBO) {
debug_assert(m_Handle); debug_assert(m_Handle);
glGetError(); // clear the error state // glGetError(); // clear the error state
pglBindBufferARB(GL_ARRAY_BUFFER_ARB,m_Handle); pglBindBufferARB(GL_ARRAY_BUFFER_ARB,m_Handle);
pglBufferSubDataARB(GL_ARRAY_BUFFER_ARB,chunk->m_Index*m_VertexSize,chunk->m_Count*m_VertexSize,data); pglBufferSubDataARB(GL_ARRAY_BUFFER_ARB,chunk->m_Index*m_VertexSize,chunk->m_Count*m_VertexSize,data);
if (glGetError() != GL_NO_ERROR) throw PSERROR_Renderer_VBOFailed(); // if (glGetError() != GL_NO_ERROR) throw PSERROR_Renderer_VBOFailed();
} else { } else {
debug_assert(m_SysMem); debug_assert(m_SysMem);
memcpy2(m_SysMem+chunk->m_Index*m_VertexSize,data,chunk->m_Count*m_VertexSize); memcpy2(m_SysMem+chunk->m_Index*m_VertexSize,data,chunk->m_Count*m_VertexSize);

View File

@ -7,17 +7,6 @@
#include <algorithm> #include <algorithm>
namespace
{
// Avoid creating strings at runtime
#define ACTION(n) \
const char n##Name[] = "on" #n; \
utf16string n##Name16(n##Name, n##Name + ARRAY_SIZE(n##Name)-1)
ACTION(Enter);
ACTION(Exit);
ACTION(Tick);
}
CAura::CAura( JSContext* cx, CEntity* source, CStrW& name, float radius, size_t tickRate, const CVector4D& color, JSObject* handler ) CAura::CAura( JSContext* cx, CEntity* source, CStrW& name, float radius, size_t tickRate, const CVector4D& color, JSObject* handler )
: m_cx(cx), m_source(source), m_name(name), m_radius(radius), m_handler(handler), : m_cx(cx), m_source(source), m_name(name), m_radius(radius), m_handler(handler),
m_tickRate(tickRate), m_tickCyclePos(0), m_color(color) m_tickRate(tickRate), m_tickCyclePos(0), m_color(color)
@ -70,7 +59,7 @@ void CAura::Update( size_t timestep )
// Call onEnter on any new unit that has entered the aura // Call onEnter on any new unit that has entered the aura
jsval enterFunction; jsval enterFunction;
if( JS_GetUCProperty( m_cx, m_handler, EnterName16.c_str(), EnterName16.length(), &enterFunction ) if( JS_GetProperty( m_cx, m_handler, "onEnter", &enterFunction )
&& enterFunction != JSVAL_VOID) && enterFunction != JSVAL_VOID)
{ {
std::back_insert_iterator<std::vector<CEntity*> > ins( entered ); std::back_insert_iterator<std::vector<CEntity*> > ins( entered );
@ -87,7 +76,7 @@ void CAura::Update( size_t timestep )
// Call onExit on any unit that has exited the aura // Call onExit on any unit that has exited the aura
jsval exitFunction; jsval exitFunction;
if( JS_GetUCProperty( m_cx, m_handler, ExitName16.c_str(), ExitName16.length(), &exitFunction ) if( JS_GetProperty( m_cx, m_handler, "onExit", &exitFunction )
&& exitFunction != JSVAL_VOID ) && exitFunction != JSVAL_VOID )
{ {
std::back_insert_iterator<std::vector<CEntity*> > ins( exited ); std::back_insert_iterator<std::vector<CEntity*> > ins( exited );
@ -108,7 +97,7 @@ void CAura::Update( size_t timestep )
{ {
// It's time to tick; call OnTick on any unit that is in the aura // It's time to tick; call OnTick on any unit that is in the aura
jsval tickFunction; jsval tickFunction;
if( JS_GetUCProperty( m_cx, m_handler, TickName16.c_str(), TickName16.length(), &tickFunction ) if( JS_GetProperty( m_cx, m_handler, "onTick", &tickFunction )
&& tickFunction != JSVAL_VOID ) && tickFunction != JSVAL_VOID )
{ {
for( std::vector<CEntity*>::iterator it = curInfluenced.begin(); it != curInfluenced.end(); it++ ) for( std::vector<CEntity*>::iterator it = curInfluenced.begin(); it != curInfluenced.end(); it++ )
@ -128,7 +117,7 @@ void CAura::RemoveAll()
jsval rval; jsval rval;
jsval argv[1]; jsval argv[1];
jsval exitFunction; jsval exitFunction;
if( JS_GetUCProperty( m_cx, m_handler, ExitName16.c_str(), ExitName16.length(), &exitFunction ) if( JS_GetProperty( m_cx, m_handler, "onExit", &exitFunction )
&& exitFunction != JSVAL_VOID ) && exitFunction != JSVAL_VOID )
{ {
// Call the exit function on everything in our influence // Call the exit function on everything in our influence
@ -151,7 +140,7 @@ void CAura::Remove( CEntity* ent )
jsval rval; jsval rval;
jsval argv[1]; jsval argv[1];
jsval exitFunction; jsval exitFunction;
if( JS_GetUCProperty( m_cx, m_handler, ExitName16.c_str(), ExitName16.length(), &exitFunction ) if( JS_GetProperty( m_cx, m_handler, "onExit", &exitFunction )
&& exitFunction != JSVAL_VOID ) && exitFunction != JSVAL_VOID )
{ {
// Call the exit function on it // Call the exit function on it

View File

@ -328,7 +328,7 @@ void CEntity::update( size_t timestep )
m_position_previous = m_position; m_position_previous = m_position;
m_orientation_previous = m_orientation; m_orientation_previous = m_orientation;
CalculateRegen( timestep ); CalculateRegen( timestep );
if ( entf_get(ENTF_TRIGGER_RUN) ) if ( entf_get(ENTF_TRIGGER_RUN) )
@ -658,7 +658,7 @@ void UpdateAuras_Normal( SAura& aura, CEntity* e )
return( false ); return( false );
return( true ); return( true );
} }
}
#endif #endif
bool CEntity::Initialize() bool CEntity::Initialize()
@ -689,11 +689,13 @@ bool CEntity::Initialize()
return true; return true;
} }
/*
void CEntity::Tick() void CEntity::Tick()
{ {
CEventTick evt; CEventTick evt;
DispatchEvent( &evt ); DispatchEvent( &evt );
} }
*/
void CEntity::clearOrders() void CEntity::clearOrders()
{ {
@ -869,6 +871,8 @@ void CEntity::interpolate( float relativeoffset )
CVector3D old_graphics_position = m_graphics_position; CVector3D old_graphics_position = m_graphics_position;
CVector3D old_graphics_orientation = m_graphics_orientation; CVector3D old_graphics_orientation = m_graphics_orientation;
relativeoffset = clamp( relativeoffset, 0.f, 1.f );
m_graphics_position = Interpolate<CVector3D>( m_position_previous, m_position, relativeoffset ); m_graphics_position = Interpolate<CVector3D>( m_position_previous, m_position, relativeoffset );
// Avoid wraparound glitches for interpolating angles. // Avoid wraparound glitches for interpolating angles.

View File

@ -140,8 +140,8 @@ public:
int m_frameCheck; //counts the frame int m_frameCheck; //counts the frame
float m_lastCombatTime; double m_lastCombatTime;
float m_lastRunTime; double m_lastRunTime;
// Building to convert to if this is a foundation, or "" otherwise // Building to convert to if this is a foundation, or "" otherwise
CStrW m_building; CStrW m_building;
@ -256,7 +256,9 @@ public:
// Updates gameplay information for the specified timestep // Updates gameplay information for the specified timestep
void update( size_t timestep_millis ); void update( size_t timestep_millis );
// Updates graphical information for a point between the last and current simulation frame; 0 < relativeoffset < 1. // Updates graphical information for a point between the last and current
// simulation frame; should be 0 <= relativeoffset <= 1 (else it'll be
// clamped)
void interpolate( float relativeoffset ); void interpolate( float relativeoffset );
// Forces update of actor information during next call to 'interpolate'. // Forces update of actor information during next call to 'interpolate'.
@ -274,7 +276,7 @@ public:
void initAuraData(); void initAuraData();
// Process tick. // Process tick.
void Tick(); // void Tick(); // (see comment in CEntityManager::updateAll)
// Calculate distances along the terrain // Calculate distances along the terrain

View File

@ -26,6 +26,7 @@ CEntityFormation::CEntityFormation( CFormation*& base, size_t index )
m_index = (int)index; m_index = (int)index;
} }
CEntityFormation::~CEntityFormation() CEntityFormation::~CEntityFormation()
{ {
for ( int i=0; i<m_base->m_numSlots; ++i ) for ( int i=0; i<m_base->m_numSlots; ++i )
@ -38,6 +39,12 @@ CEntityFormation::~CEntityFormation()
} }
} }
} }
int CEntityFormation::GetSlotCount()
{
return m_base->m_numSlots;
}
void CEntityFormation::SwitchBase( CFormation*& base ) void CEntityFormation::SwitchBase( CFormation*& base )
{ {
std::vector<CEntity*> copy; std::vector<CEntity*> copy;

View File

@ -7,13 +7,13 @@
#ifndef ENTITYFORMATION_INCLUDED #ifndef ENTITYFORMATION_INCLUDED
#define ENTITYFORMATION_INCLUDED #define ENTITYFORMATION_INCLUDED
#include "Formation.h"
#include "ps/Vector2D.h" #include "ps/Vector2D.h"
class CVector2D; class CVector2D;
class CEntity; class CEntity;
struct CEntityList; struct CEntityList;
class CClassSet; class CClassSet;
class CFormation;
class CEntityFormation class CEntityFormation
{ {
@ -24,7 +24,7 @@ public:
int GetEntityCount() { return m_numEntities; } int GetEntityCount() { return m_numEntities; }
float GetSpeed() { return m_speed; } float GetSpeed() { return m_speed; }
int GetSlotCount() { return m_base->m_numSlots; } int GetSlotCount();
CEntityList GetEntityList(); CEntityList GetEntityList();
CVector2D GetSlotPosition( int order ); CVector2D GetSlotPosition( int order );
@ -38,7 +38,7 @@ public:
inline bool IsDuplication() { return m_duplication; } inline bool IsDuplication() { return m_duplication; }
inline void SetLock( bool lock ){ m_locked=lock; } inline void SetLock( bool lock ){ m_locked=lock; }
inline bool IsLocked() { return m_locked; } inline bool IsLocked() { return m_locked; }
inline bool IsValidOrder(int order) { return ( order >= 0 && order < m_base->m_numSlots ); } inline bool IsValidOrder(int order) { return ( order >= 0 && order < GetSlotCount() ); }
private: private:
int m_numEntities; int m_numEntities;

View File

@ -272,12 +272,14 @@ void CEntityManager::InitializeAll()
} }
} }
/*
void CEntityManager::TickAll() void CEntityManager::TickAll()
{ {
for( int i = 0; i < MAX_HANDLES; i++ ) for( int i = 0; i < MAX_HANDLES; i++ )
if( isEntityRefd(i) && m_entities[i].m_entity->m_extant ) if( isEntityRefd(i) && m_entities[i].m_entity->m_extant )
m_entities[i].m_entity->Tick(); m_entities[i].m_entity->Tick();
} }
*/
void CEntityManager::updateAll( size_t timestep ) void CEntityManager::updateAll( size_t timestep )
{ {

View File

@ -20,24 +20,21 @@
#include <set> #include <set>
#include "ps/Singleton.h"
#include "EntityHandles.h" #include "EntityHandles.h"
#include "EntityPredicate.h"
#include "EntityMessage.h"
#include "ps/Game.h" #include "ps/Game.h"
#include "ps/World.h" #include "ps/World.h"
#include "maths/Vector3D.h"
class CEntityTemplate; class CEntityTemplate;
class CPlayer; class CPlayer;
class CStrW; class CStrW;
class CVector3D;
#define MAX_HANDLES 4096 #define MAX_HANDLES 4096
// collision patch size, in graphics units, not tiles (1 tile = 4 units) // collision patch size, in graphics units, not tiles (1 tile = 4 units)
#define COLLISION_PATCH_SIZE 8 #define COLLISION_PATCH_SIZE 8
#define g_EntityManager (*(g_Game->GetWorld()->GetEntityManager())) #define g_EntityManager g_Game->GetWorld()->GetEntityManager()
class CEntityManager class CEntityManager
{ {
@ -92,7 +89,7 @@ public:
void updateAll( size_t timestep ); void updateAll( size_t timestep );
void interpolateAll( float relativeoffset ); void interpolateAll( float relativeoffset );
void InitializeAll(); void InitializeAll();
void TickAll(); // void TickAll();
void renderAll(); void renderAll();
void conformAll(); void conformAll();
void invalidateAll(); void invalidateAll();

View File

@ -5,10 +5,6 @@
#include "ps/CStr.h" #include "ps/CStr.h"
#include "EntityHandles.h"
class CEntityManager;
struct SEntityAction struct SEntityAction
{ {
int m_Id; int m_Id;
@ -33,11 +29,11 @@ class CClassSet
public: public:
CClassSet() : m_Parent(NULL) {} CClassSet() : m_Parent(NULL) {}
bool IsMember( const CStrW& Test ); bool IsMember(const CStrW& Test);
void Rebuild(); void Rebuild();
inline void SetParent( CClassSet* Parent ) inline void SetParent(CClassSet* Parent)
{ m_Parent = Parent; Rebuild(); } { m_Parent = Parent; Rebuild(); }
CStrW getMemberList(); CStrW getMemberList();

View File

@ -18,7 +18,6 @@
#define BASE_ENTITY_INCLUDED #define BASE_ENTITY_INCLUDED
#include "ps/CStr.h" #include "ps/CStr.h"
#include "graphics/ObjectEntry.h"
#include "scripting/ScriptableComplex.h" #include "scripting/ScriptableComplex.h"
#include "scripting/DOMEvent.h" #include "scripting/DOMEvent.h"

View File

@ -21,11 +21,13 @@ public:
CEventDeath() : CScriptEvent( L"death", EVENT_DEATH, false ) {} CEventDeath() : CScriptEvent( L"death", EVENT_DEATH, false ) {}
}; };
/*
class CEventTick : public CScriptEvent class CEventTick : public CScriptEvent
{ {
public: public:
CEventTick() : CScriptEvent( L"tick", EVENT_TICK, false ) {} CEventTick() : CScriptEvent( L"tick", EVENT_TICK, false ) {}
}; };
*/
class CEventGeneric : public CScriptEvent class CEventGeneric : public CScriptEvent
{ {

View File

@ -118,7 +118,7 @@ void CLOSManager::Update()
// Set visibility for each entity // Set visibility for each entity
std::vector<CEntity*> extant; std::vector<CEntity*> extant;
g_Game->GetWorld()->GetEntityManager()->GetExtant(extant); g_EntityManager.GetExtant(extant);
for(size_t i=0; i<extant.size(); i++) for(size_t i=0; i<extant.size(); i++)
{ {
CEntity* e = extant[i]; CEntity* e = extant[i];

View File

@ -2,33 +2,30 @@
#include <vector> #include <vector>
#include "EntityFormation.h"
#include "EntityManager.h"
#include "LOSManager.h"
#include "Projectile.h"
#include "Scheduler.h"
#include "Simulation.h"
#include "TurnManager.h"
#include "graphics/Model.h" #include "graphics/Model.h"
#include "graphics/Terrain.h" #include "graphics/Terrain.h"
#include "graphics/Unit.h" #include "graphics/Unit.h"
#include "graphics/UnitManager.h" #include "graphics/UnitManager.h"
#include "lib/timer.h" #include "maths/MathUtil.h"
#include "network/NetMessage.h"
#include "ps/CLogger.h" #include "ps/CLogger.h"
#include "ps/Game.h" #include "ps/Game.h"
#include "ps/GameAttributes.h" #include "ps/GameAttributes.h"
#include "ps/Loader.h" #include "ps/Loader.h"
#include "ps/LoaderThunks.h" #include "ps/LoaderThunks.h"
#include "network/NetMessage.h"
#include "ps/Profile.h" #include "ps/Profile.h"
#include "renderer/Renderer.h" #include "renderer/Renderer.h"
#include "renderer/WaterManager.h" #include "renderer/WaterManager.h"
#include "simulation/Entity.h" #include "simulation/Entity.h"
#include "simulation/LOSManager.h" #include "simulation/EntityFormation.h"
#include "simulation/TerritoryManager.h" #include "simulation/EntityManager.h"
#include "simulation/EntityTemplateCollection.h" #include "simulation/EntityTemplateCollection.h"
#include "simulation/LOSManager.h"
#include "gui/CGUI.h" #include "simulation/Projectile.h"
#include "simulation/Scheduler.h"
#include "simulation/Simulation.h"
#include "simulation/TerritoryManager.h"
#include "simulation/TurnManager.h"
CSimulation::CSimulation(CGame *pGame): CSimulation::CSimulation(CGame *pGame):
m_pGame(pGame), m_pGame(pGame),
@ -75,39 +72,59 @@ void CSimulation::RegisterInit(CGameAttributes *pAttribs)
void CSimulation::Update(double frameTime) bool CSimulation::Update(double frameTime)
{ {
bool ok = true;
m_DeltaTime += frameTime; m_DeltaTime += frameTime;
if( m_DeltaTime >= 0.0 && frameTime ) if (m_DeltaTime >= 0.0)
{ {
PROFILE( "simulation turn" );
// A new simulation frame is required. // A new simulation frame is required.
MICROLOG( L"calculate simulation" );
PROFILE( "simulation turn" );
Simulate(); Simulate();
m_DeltaTime -= (m_pTurnManager->GetTurnLength()/1000.0); double turnLength = m_pTurnManager->GetTurnLength() / 1000.0;
if( m_DeltaTime >= 0.0 ) m_DeltaTime -= turnLength;
if (m_DeltaTime >= 0.0)
{ {
// The desired sim frame rate can't be achieved. Settle for process & render // The desired sim frame rate can't be achieved - we're being called
// frames as fast as possible. // with average(frameTime) > turnLength.
frameTime -= m_DeltaTime; // so the animation stays in sync with the sim // Let the caller know we can't go fast enough - they should try
m_DeltaTime = 0.0; // cutting down on Interpolate and rendering, and call us a few times
// with frameTime == 0 to give us a chance to catch up.
ok = false;
} }
} }
PROFILE_START( "simulation interpolation" ); return ok;
Interpolate(frameTime, ((1000.0*m_DeltaTime) / (float)m_pTurnManager->GetTurnLength()) + 1.0); }
PROFILE_END( "simulation interpolation" );
void CSimulation::Interpolate(double frameTime)
{
double turnLength = m_pTurnManager->GetTurnLength()/1000.0;
// 'offset' should be how far we are between the previous and next
// simulation frames.
// m_DeltaTime/turnLength will usually be between -1 and 0, indicating
// the time until the next frame, so we can use that easily.
// If the simulation is going too slowly and hasn't been giving a chance
// to catch up before Interpolate is called, then m_DeltaTime > 0, and
// we'll just end up being clamped to offset=1 inside CEntity::interpolate,
// which is alright.
Interpolate(frameTime, m_DeltaTime / turnLength + 1.0);
} }
void CSimulation::Interpolate(double frameTime, double offset) void CSimulation::Interpolate(double frameTime, double offset)
{ {
const std::vector<CUnit*>& units=m_pWorld->GetUnitManager().GetUnits(); PROFILE( "simulation interpolation" );
for (uint i=0;i<units.size();++i)
const std::vector<CUnit*>& units = m_pWorld->GetUnitManager().GetUnits();
for (size_t i = 0; i < units.size(); ++i)
units[i]->GetModel()->Update((float)frameTime); units[i]->GetModel()->Update((float)frameTime);
g_EntityManager.interpolateAll( (float)offset ); g_EntityManager.interpolateAll(offset);
m_pWorld->GetProjectileManager().InterpolateAll( (float)offset ); m_pWorld->GetProjectileManager().InterpolateAll(frameTime);
g_Renderer.GetWaterManager()->m_WaterTexTimer += frameTime; g_Renderer.GetWaterManager()->m_WaterTexTimer += frameTime;
} }

View File

@ -39,10 +39,13 @@ public:
void RegisterInit(CGameAttributes *pGameAttributes); void RegisterInit(CGameAttributes *pGameAttributes);
int Initialize(CGameAttributes *pGameAttributes); int Initialize(CGameAttributes *pGameAttributes);
// Perform all CSimulation updates for the specified elapsed time. // Perform simulation updates for the specified elapsed time. If it is
// (If frameTime=0, no simulation updates are done, but the graphics // shorter than the time until the next simulation turn, nothing happens.
// are interpolated.) // Returns false if it can't keep up with the desired simulation rate.
void Update(double frameTime); bool Update(double frameTime);
// Update the graphical representations of the simulation by the given time.
void Interpolate(double frameTime);
// Calculate the message mask of a message to be queued // Calculate the message mask of a message to be queued
static uint GetMessageMask(CNetMessage *, uint oldMask, void *userdata); static uint GetMessageMask(CNetMessage *, uint oldMask, void *userdata);

View File

@ -1,7 +1,6 @@
#ifndef __STANCE_H__ #ifndef __STANCE_H__
#define __STANCE_H__ #define __STANCE_H__
#include "EntityHandles.h"
#include "ps/Vector2D.h" #include "ps/Vector2D.h"
class CEntity; class CEntity;

View File

@ -12,6 +12,7 @@
#include "lib/ogl.h" #include "lib/ogl.h"
#include "lib/timer.h" #include "lib/timer.h"
#include "maths/Bound.h" #include "maths/Bound.h"
#include "maths/MathUtil.h"
#include "ps/Game.h" #include "ps/Game.h"
#include "ps/Player.h" #include "ps/Player.h"
#include "ps/Profile.h" #include "ps/Profile.h"

View File

@ -14,6 +14,7 @@ enum
ID_GenerateMap, ID_GenerateMap,
ID_GenerateRMS, ID_GenerateRMS,
ID_SimPlay, ID_SimPlay,
ID_SimFast,
ID_SimPause, ID_SimPause,
ID_SimReset ID_SimReset
}; };
@ -22,6 +23,7 @@ enum
{ {
SimInactive, SimInactive,
SimPlaying, SimPlaying,
SimPlayingFast,
SimPaused SimPaused
}; };
@ -44,6 +46,7 @@ MapSidebar::MapSidebar(wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
{ {
wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Simulation test")); wxStaticBoxSizer* sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Simulation test"));
sizer->Add(new wxButton(this, ID_SimPlay, _("Play")), wxSizerFlags().Proportion(1)); sizer->Add(new wxButton(this, ID_SimPlay, _("Play")), wxSizerFlags().Proportion(1));
sizer->Add(new wxButton(this, ID_SimFast, _("Fast")), wxSizerFlags().Proportion(1));
sizer->Add(new wxButton(this, ID_SimPause, _("Pause")), wxSizerFlags().Proportion(1)); sizer->Add(new wxButton(this, ID_SimPause, _("Pause")), wxSizerFlags().Proportion(1));
sizer->Add(new wxButton(this, ID_SimReset, _("Reset")), wxSizerFlags().Proportion(1)); sizer->Add(new wxButton(this, ID_SimReset, _("Reset")), wxSizerFlags().Proportion(1));
UpdateSimButtons(); UpdateSimButtons();
@ -78,34 +81,46 @@ void MapSidebar::UpdateSimButtons()
wxCHECK(button, ); wxCHECK(button, );
button->Enable(m_SimState != SimPlaying); button->Enable(m_SimState != SimPlaying);
button = wxDynamicCast(FindWindow(ID_SimFast), wxButton);
wxCHECK(button, );
button->Enable(m_SimState != SimPlayingFast);
button = wxDynamicCast(FindWindow(ID_SimPause), wxButton); button = wxDynamicCast(FindWindow(ID_SimPause), wxButton);
wxCHECK(button, ); wxCHECK(button, );
button->Enable(m_SimState == SimPlaying); button->Enable(m_SimState == SimPlaying || m_SimState == SimPlayingFast);
button = wxDynamicCast(FindWindow(ID_SimReset), wxButton); button = wxDynamicCast(FindWindow(ID_SimReset), wxButton);
wxCHECK(button, ); wxCHECK(button, );
button->Enable(m_SimState != SimInactive); button->Enable(m_SimState != SimInactive);
} }
void MapSidebar::OnSimPlay(wxCommandEvent& WXUNUSED(event)) void MapSidebar::OnSimPlay(wxCommandEvent& event)
{ {
float speed = 1.f;
int newState = SimPlaying;
if (event.GetId() == ID_SimFast)
{
speed = 8.f;
newState = SimPlayingFast;
}
if (m_SimState == SimInactive) if (m_SimState == SimInactive)
{ {
POST_MESSAGE(SimStateSave, (L"default", true)); POST_MESSAGE(SimStateSave, (L"default", true));
POST_MESSAGE(SimPlay, (1.f)); POST_MESSAGE(SimPlay, (speed));
m_SimState = SimPlaying; m_SimState = newState;
} }
else if (m_SimState == SimPaused) else // paused or already playing at a different speed
{ {
POST_MESSAGE(SimPlay, (1.f)); POST_MESSAGE(SimPlay, (speed));
m_SimState = SimPlaying; m_SimState = newState;
} }
UpdateSimButtons(); UpdateSimButtons();
} }
void MapSidebar::OnSimPause(wxCommandEvent& WXUNUSED(event)) void MapSidebar::OnSimPause(wxCommandEvent& WXUNUSED(event))
{ {
if (m_SimState == SimPlaying) if (m_SimState == SimPlaying || m_SimState == SimPlayingFast)
{ {
POST_MESSAGE(SimPlay, (0.f)); POST_MESSAGE(SimPlay, (0.f));
m_SimState = SimPaused; m_SimState = SimPaused;
@ -115,7 +130,7 @@ void MapSidebar::OnSimPause(wxCommandEvent& WXUNUSED(event))
void MapSidebar::OnSimReset(wxCommandEvent& WXUNUSED(event)) void MapSidebar::OnSimReset(wxCommandEvent& WXUNUSED(event))
{ {
if (m_SimState == SimPlaying) if (m_SimState == SimPlaying || m_SimState == SimPlayingFast)
{ {
POST_MESSAGE(SimPlay, (0.f)); POST_MESSAGE(SimPlay, (0.f));
POST_MESSAGE(SimStateRestore, (L"default")); POST_MESSAGE(SimStateRestore, (L"default"));
@ -133,6 +148,7 @@ BEGIN_EVENT_TABLE(MapSidebar, Sidebar)
EVT_BUTTON(ID_GenerateMap, MapSidebar::GenerateMap) EVT_BUTTON(ID_GenerateMap, MapSidebar::GenerateMap)
EVT_BUTTON(ID_GenerateRMS, MapSidebar::GenerateRMS) EVT_BUTTON(ID_GenerateRMS, MapSidebar::GenerateRMS)
EVT_BUTTON(ID_SimPlay, MapSidebar::OnSimPlay) EVT_BUTTON(ID_SimPlay, MapSidebar::OnSimPlay)
EVT_BUTTON(ID_SimFast, MapSidebar::OnSimPlay)
EVT_BUTTON(ID_SimPause, MapSidebar::OnSimPause) EVT_BUTTON(ID_SimPause, MapSidebar::OnSimPause)
EVT_BUTTON(ID_SimReset, MapSidebar::OnSimReset) EVT_BUTTON(ID_SimReset, MapSidebar::OnSimReset)
END_EVENT_TABLE(); END_EVENT_TABLE();

View File

@ -9,6 +9,7 @@
#include "ps/CStr.h" #include "ps/CStr.h"
#include "ps/CLogger.h" #include "ps/CLogger.h"
#include "ps/VFSUtil.h" #include "ps/VFSUtil.h"
#include "maths/MathUtil.h"
#include "maths/Quaternion.h" #include "maths/Quaternion.h"
#include "lib/res/graphics/ogl_tex.h" #include "lib/res/graphics/ogl_tex.h"

View File

@ -17,43 +17,51 @@
#include "simulation/LOSManager.h" #include "simulation/LOSManager.h"
#include "simulation/Simulation.h" #include "simulation/Simulation.h"
namespace
{
void InitGame(const CStrW& map)
{
if (g_Game)
{
delete g_Game;
g_Game = NULL;
}
// Set attributes for the game:
g_GameAttributes.m_MapFile = map;
// Make all players locally controlled
for (int i = 1; i < 8; ++i)
g_GameAttributes.GetSlot(i)->AssignLocal();
// Make the whole world visible
g_GameAttributes.m_LOSSetting = 2;
g_GameAttributes.m_FogOfWar = false;
// Don't use screenshot mode, because we want working AI for the
// simulation-testing. Outside that simulation-testing, we avoid having
// the units move into attack mode by never calling CEntity::update.
g_GameAttributes.m_ScreenshotMode = false;
// Initialise the game:
g_Game = new CGame();
}
void StartGame()
{
PSRETURN ret = g_Game->StartGame(&g_GameAttributes);
debug_assert(ret == PSRETURN_OK);
LDR_NonprogressiveLoad();
ret = g_Game->ReallyStartGame();
debug_assert(ret == PSRETURN_OK);
// Make sure entities get rendered in the correct location
g_Game->GetSimulation()->Interpolate(0.0);
}
}
namespace AtlasMessage { namespace AtlasMessage {
static void InitGame(std::wstring map)
{
if (g_Game)
delete g_Game;
// Set attributes for the game:
g_GameAttributes.m_MapFile = map;
// Make all players locally controlled
for (int i=1; i<8; ++i)
g_GameAttributes.GetSlot(i)->AssignLocal();
// Make the whole world visible
g_GameAttributes.m_LOSSetting = 2;
g_GameAttributes.m_FogOfWar = false;
// Disable unit AI (and other things that may interfere with making things look nice)
g_GameAttributes.m_ScreenshotMode = false;
// Initialise the game:
g_Game = new CGame();
}
static void StartGame()
{
PSRETURN ret = g_Game->StartGame(&g_GameAttributes);
debug_assert(ret == PSRETURN_OK);
LDR_NonprogressiveLoad();
ret = g_Game->ReallyStartGame();
debug_assert(ret == PSRETURN_OK);
// Make sure entities get rendered in the correct location
g_Game->GetSimulation()->Update(0.0);
}
MESSAGEHANDLER(GenerateMap) MESSAGEHANDLER(GenerateMap)
{ {
InitGame(L""); InitGame(L"");
@ -84,17 +92,20 @@ MESSAGEHANDLER(GenerateMap)
Handle tex = texentry ? texentry->GetHandle() : 0; Handle tex = texentry ? texentry->GetHandle() : 0;
int patches = terrain->GetPatchesPerSide(); int patches = terrain->GetPatchesPerSide();
for (int pz = 0; pz < patches; ++pz) { for (int pz = 0; pz < patches; ++pz)
for (int px = 0; px < patches; ++px) { {
for (int px = 0; px < patches; ++px)
{
CPatch* patch = terrain->GetPatch(px, pz); CPatch* patch = terrain->GetPatch(px, pz);
for (int z = 0; z < PATCH_SIZE; ++z) for (int z = 0; z < PATCH_SIZE; ++z)
{
for (int x = 0; x < PATCH_SIZE; ++x) for (int x = 0; x < PATCH_SIZE; ++x)
{ {
patch->m_MiniPatches[z][x].Tex1 = tex; patch->m_MiniPatches[z][x].Tex1 = tex;
patch->m_MiniPatches[z][x].Tex1Priority = 0; patch->m_MiniPatches[z][x].Tex1Priority = 0;
} }
}
} }
} }

View File

@ -574,7 +574,11 @@ BEGIN_COMMAND(MoveObject)
if (unit->GetEntity()) if (unit->GetEntity())
{ {
// Set the current position, and also set the previous position so
// CEntity::interpolate puts the entity in the right place (without
// having to call CEntity::update before it'll look right)
unit->GetEntity()->m_position = pos; unit->GetEntity()->m_position = pos;
unit->GetEntity()->m_position_previous = pos;
if (unit->GetEntity()->m_base->m_isTerritoryCentre) if (unit->GetEntity()->m_base->m_isTerritoryCentre)
g_Game->GetWorld()->GetTerritoryManager()->DelayedRecalculate(); g_Game->GetWorld()->GetTerritoryManager()->DelayedRecalculate();

View File

@ -10,9 +10,12 @@
#include "graphics/GameView.h" #include "graphics/GameView.h"
#include "graphics/Model.h" #include "graphics/Model.h"
#include "graphics/ObjectBase.h" #include "graphics/ObjectBase.h"
#include "graphics/ObjectEntry.h"
#include "graphics/SColor.h" #include "graphics/SColor.h"
#include "graphics/Unit.h" #include "graphics/Unit.h"
#include "graphics/UnitManager.h" #include "graphics/UnitManager.h"
#include "lib/timer.h"
#include "maths/MathUtil.h"
#include "ps/Game.h" #include "ps/Game.h"
#include "ps/GameSetup/GameSetup.h" #include "ps/GameSetup/GameSetup.h"
#include "ps/Player.h" #include "ps/Player.h"
@ -169,20 +172,36 @@ ViewGame::~ViewGame()
void ViewGame::Update(float frameLength) void ViewGame::Update(float frameLength)
{ {
float actualFrameLength = frameLength * m_SpeedMultiplier;
if (m_SpeedMultiplier == 0.f) if (m_SpeedMultiplier == 0.f)
{ {
// TODO: I don't think this line is necessary, but I'm not positive...
// g_EntityManager.updateAll(0);
// Update unit interpolation // Update unit interpolation
g_Game->GetSimulation()->Update(0.0); g_Game->GetSimulation()->Interpolate(0.0);
} }
else else
{ {
// Update the whole world // Update the whole world
g_Game->Update(m_SpeedMultiplier * frameLength); // (Tell the game update not to interpolate graphics - we'll do that
// ourselves)
bool ok = g_Game->Update(actualFrameLength, false);
if (! ok)
{
// Whoops, we're trying to go faster than the simulation can manage.
// It's probably better to run at the right sim rate, at the expense
// of framerate, so let's try simulating a few more times.
double t = get_time();
while (!ok && get_time() < t + 0.1) // don't go much worse than 10fps
{
ok = g_Game->Update(0.0, false); // don't add on any extra sim time
}
}
// Interpolate the graphics - we only want to do it once per visual frame,
// not in every call to g_Game->Update
g_Game->GetSimulation()->Interpolate(actualFrameLength);
if (g_Game->GetView()->GetCinema()->IsPlaying()) if (g_Game->GetView()->GetCinema()->IsPlaying())
g_Game->GetView()->GetCinema()->Update(m_SpeedMultiplier * frameLength); g_Game->GetView()->GetCinema()->Update(actualFrameLength);
} }
} }
@ -316,23 +335,23 @@ void ViewGame::RestoreState(const std::wstring& label)
g_EntityManager.InitializeAll(); g_EntityManager.InitializeAll();
if (simState->onlyEntities) if (! simState->onlyEntities)
return;
for (size_t i = 0; i < simState->nonentities.size(); ++i)
{ {
SimState::Nonentity& n = simState->nonentities[i]; for (size_t i = 0; i < simState->nonentities.size(); ++i)
CUnit* unit = unitMan.CreateUnit(n.actorName, NULL, n.selections);
if (unit)
{ {
CMatrix3D m; SimState::Nonentity& n = simState->nonentities[i];
m.SetYRotation(n.angle + PI);
m.Translate(n.position);
unit->GetModel()->SetTransform(m);
unit->SetID(n.unitID); CUnit* unit = unitMan.CreateUnit(n.actorName, NULL, n.selections);
if (unit)
{
CMatrix3D m;
m.SetYRotation(n.angle + PI);
m.Translate(n.position);
unit->GetModel()->SetTransform(m);
unit->SetID(n.unitID);
}
} }
} }
} }

View File

@ -8,6 +8,8 @@ use File::Remote;
use XML::Atom::SimpleFeed; use XML::Atom::SimpleFeed;
use Data::UUID; use Data::UUID;
my $feed_url = 'http://www.wildfiregames.com/~philip/svnlog.xml';
sub doupdate : Local sub doupdate : Local
{ {
my ($self, $c) = @_; my ($self, $c) = @_;
@ -110,7 +112,7 @@ sub generate_text
{ {
my @logentries = SVNLog::Model::CDBI::Logentry->recent(7); my @logentries = SVNLog::Model::CDBI::Logentry->recent(7);
my $out = ''; my $out = qq{<a href="$feed_url"><img alt="Atom feed" title="Subscribe to feed of revision log (Atom 1.0 format)" src="/images/feed-icon-16x16.png" style="float: right"></a>};
for (@logentries) for (@logentries)
{ {
my ($revision, $author, $date, $msg) = ($_->revision, $_->author, $_->date, $_->public_msg); my ($revision, $author, $date, $msg) = ($_->revision, $_->author, $_->date, $_->public_msg);
@ -143,7 +145,7 @@ sub generate_feed
my $feed = new XML::Atom::SimpleFeed( my $feed = new XML::Atom::SimpleFeed(
title => "0 A.D. Revision Log", title => "0 A.D. Revision Log",
link => "http://www.wildfiregames.com/0ad/", link => "http://www.wildfiregames.com/0ad/",
link => { rel => 'self', href => 'http://www.wildfiregames.com/~philip/svnlog.xml' }, link => { rel => 'self', href => $feed_url },
id => "urn:uuid:" . $uid_gen->create_from_name_str('WFG SVN feed', 'feed'), id => "urn:uuid:" . $uid_gen->create_from_name_str('WFG SVN feed', 'feed'),
); );