Added elapsed real time (as opposed to elapsed simulation time) to MT_Interpolate messages. Fixes leftover TODO from #824. Refs #824.

This was SVN commit r11944.
This commit is contained in:
vts 2012-06-06 19:37:03 +00:00
parent f9fa347a7f
commit 6b50a660b9
32 changed files with 193 additions and 170 deletions

View File

@ -245,9 +245,9 @@ bool CCinemaPath::Validate()
return false;
}
bool CCinemaPath::Play(float DeltaTime)
bool CCinemaPath::Play(const float deltaRealTime)
{
m_TimeElapsed += m_Timescale*DeltaTime;
m_TimeElapsed += m_Timescale * deltaRealTime;
if (!Validate())
{
@ -337,9 +337,9 @@ void CCinemaManager::MoveToPointAt(float time)
m_CurrentPath->second.m_PreviousRotation );
}
bool CCinemaManager::Update(float DeltaTime)
bool CCinemaManager::Update(const float deltaRealTime)
{
if (!m_PathQueue.front().Play(DeltaTime))
if (!m_PathQueue.front().Play(deltaRealTime))
{
m_PathQueue.pop_front();
return false;

View File

@ -110,8 +110,12 @@ public:
public:
//Returns false if finished
bool Play(float DeltaTime);
/**
* Returns false if finished.
*
* @param deltaRealTime Elapsed real time since the last frame.
*/
bool Play(const float deltaRealTime);
bool Validate();
inline float GetTimescale() const { return m_Timescale; }
@ -129,7 +133,11 @@ public:
//Adds track to list of being played.
void QueuePath(const CStrW& name, bool queue);
void OverridePath(const CStrW& name); //clears track queue and replaces with 'name'
bool Update(float DeltaTime);
/**
* @param deltaRealTime Elapsed real time since the last frame.
*/
bool Update(const float deltaRealTime);
//These stop track play, and accept time, not ratio of time
void MoveToPointAt(float time);

View File

@ -665,7 +665,7 @@ CVector3D CGameView::GetSmoothPivot(CCamera& camera) const
return camera.m_Orientation.GetTranslation() + camera.m_Orientation.GetIn() * m->Zoom.GetSmoothedValue();
}
void CGameView::Update(float DeltaTime)
void CGameView::Update(const float deltaRealTime)
{
// If camera movement is being handled by the touch-input system,
// then we should stop to avoid conflicting with it
@ -678,11 +678,11 @@ void CGameView::Update(float DeltaTime)
// TODO: this is probably not an ideal place for this, it should probably go
// in a CCmpWaterManager or some such thing (once such a thing exists)
if (!m->Game->m_Paused)
g_Renderer.GetWaterManager()->m_WaterTexTimer += DeltaTime;
g_Renderer.GetWaterManager()->m_WaterTexTimer += deltaRealTime;
if (m->TrackManager.IsActive() && m->TrackManager.IsPlaying())
{
if (! m->TrackManager.Update(DeltaTime))
if (! m->TrackManager.Update(deltaRealTime))
{
// ResetCamera();
}
@ -698,13 +698,13 @@ void CGameView::Update(float DeltaTime)
mouse_last_y = g_mouse_y;
if (HotkeyIsPressed("camera.rotate.cw"))
m->RotateY.AddSmoothly(m->ViewRotateYSpeed * DeltaTime);
m->RotateY.AddSmoothly(m->ViewRotateYSpeed * deltaRealTime);
if (HotkeyIsPressed("camera.rotate.ccw"))
m->RotateY.AddSmoothly(-m->ViewRotateYSpeed * DeltaTime);
m->RotateY.AddSmoothly(-m->ViewRotateYSpeed * deltaRealTime);
if (HotkeyIsPressed("camera.rotate.up"))
m->RotateX.AddSmoothly(-m->ViewRotateXSpeed * DeltaTime);
m->RotateX.AddSmoothly(-m->ViewRotateXSpeed * deltaRealTime);
if (HotkeyIsPressed("camera.rotate.down"))
m->RotateX.AddSmoothly(m->ViewRotateXSpeed * DeltaTime);
m->RotateX.AddSmoothly(m->ViewRotateXSpeed * deltaRealTime);
float moveRightward = 0.f;
float moveForward = 0.f;
@ -718,39 +718,39 @@ void CGameView::Update(float DeltaTime)
if (g_mouse_active)
{
if (g_mouse_x >= g_xres - 2 && g_mouse_x < g_xres)
moveRightward += m->ViewScrollSpeed * DeltaTime;
moveRightward += m->ViewScrollSpeed * deltaRealTime;
else if (g_mouse_x <= 3 && g_mouse_x >= 0)
moveRightward -= m->ViewScrollSpeed * DeltaTime;
moveRightward -= m->ViewScrollSpeed * deltaRealTime;
if (g_mouse_y >= g_yres - 2 && g_mouse_y < g_yres)
moveForward -= m->ViewScrollSpeed * DeltaTime;
moveForward -= m->ViewScrollSpeed * deltaRealTime;
else if (g_mouse_y <= 3 && g_mouse_y >= 0)
moveForward += m->ViewScrollSpeed * DeltaTime;
moveForward += m->ViewScrollSpeed * deltaRealTime;
}
if (HotkeyIsPressed("camera.right"))
moveRightward += m->ViewScrollSpeed * DeltaTime;
moveRightward += m->ViewScrollSpeed * deltaRealTime;
if (HotkeyIsPressed("camera.left"))
moveRightward -= m->ViewScrollSpeed * DeltaTime;
moveRightward -= m->ViewScrollSpeed * deltaRealTime;
if (HotkeyIsPressed("camera.up"))
moveForward += m->ViewScrollSpeed * DeltaTime;
moveForward += m->ViewScrollSpeed * deltaRealTime;
if (HotkeyIsPressed("camera.down"))
moveForward -= m->ViewScrollSpeed * DeltaTime;
moveForward -= m->ViewScrollSpeed * deltaRealTime;
if (g_Joystick.IsEnabled())
{
// This could all be improved with extra speed and sensitivity settings
// (maybe use pow to allow finer control?), and inversion settings
moveRightward += g_Joystick.GetAxisValue(m->JoystickPanX) * m->ViewScrollSpeed * DeltaTime;
moveForward -= g_Joystick.GetAxisValue(m->JoystickPanY) * m->ViewScrollSpeed * DeltaTime;
moveRightward += g_Joystick.GetAxisValue(m->JoystickPanX) * m->ViewScrollSpeed * deltaRealTime;
moveForward -= g_Joystick.GetAxisValue(m->JoystickPanY) * m->ViewScrollSpeed * deltaRealTime;
m->RotateX.AddSmoothly(g_Joystick.GetAxisValue(m->JoystickRotateX) * m->ViewRotateXSpeed * DeltaTime);
m->RotateY.AddSmoothly(-g_Joystick.GetAxisValue(m->JoystickRotateY) * m->ViewRotateYSpeed * DeltaTime);
m->RotateX.AddSmoothly(g_Joystick.GetAxisValue(m->JoystickRotateX) * m->ViewRotateXSpeed * deltaRealTime);
m->RotateY.AddSmoothly(-g_Joystick.GetAxisValue(m->JoystickRotateY) * m->ViewRotateYSpeed * deltaRealTime);
// Use a +1 bias for zoom because I want this to work with trigger buttons that default to -1
m->Zoom.AddSmoothly((g_Joystick.GetAxisValue(m->JoystickZoomIn) + 1.0f) / 2.0f * m->ViewZoomSpeed * DeltaTime);
m->Zoom.AddSmoothly(-(g_Joystick.GetAxisValue(m->JoystickZoomOut) + 1.0f) / 2.0f * m->ViewZoomSpeed * DeltaTime);
m->Zoom.AddSmoothly((g_Joystick.GetAxisValue(m->JoystickZoomIn) + 1.0f) / 2.0f * m->ViewZoomSpeed * deltaRealTime);
m->Zoom.AddSmoothly(-(g_Joystick.GetAxisValue(m->JoystickZoomOut) + 1.0f) / 2.0f * m->ViewZoomSpeed * deltaRealTime);
}
if (moveRightward || moveForward)
@ -810,14 +810,14 @@ void CGameView::Update(float DeltaTime)
}
if (HotkeyIsPressed("camera.zoom.in"))
m->Zoom.AddSmoothly(-m->ViewZoomSpeed * DeltaTime);
m->Zoom.AddSmoothly(-m->ViewZoomSpeed * deltaRealTime);
if (HotkeyIsPressed("camera.zoom.out"))
m->Zoom.AddSmoothly(m->ViewZoomSpeed * DeltaTime);
m->Zoom.AddSmoothly(m->ViewZoomSpeed * deltaRealTime);
if (m->ConstrainCamera)
m->Zoom.ClampSmoothly(m->ViewZoomMin, m->ViewZoomMax);
float zoomDelta = -m->Zoom.Update(DeltaTime);
float zoomDelta = -m->Zoom.Update(deltaRealTime);
if (zoomDelta)
{
CVector3D forwards = m->ViewCamera.m_Orientation.GetIn();
@ -867,16 +867,16 @@ void CGameView::Update(float DeltaTime)
m->PosZ.SetValueSmoothly(desiredPivot.Z + delta.Z);
}
m->PosX.Update(DeltaTime);
m->PosY.Update(DeltaTime);
m->PosZ.Update(DeltaTime);
m->PosX.Update(deltaRealTime);
m->PosY.Update(deltaRealTime);
m->PosZ.Update(deltaRealTime);
// Handle rotation around the Y (vertical) axis
{
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixSmooth(m, &targetCam.m_Orientation);
float rotateYDelta = m->RotateY.Update(DeltaTime);
float rotateYDelta = m->RotateY.Update(deltaRealTime);
if (rotateYDelta)
{
// We've updated RotateY, and need to adjust Pos so that it's still
@ -903,7 +903,7 @@ void CGameView::Update(float DeltaTime)
CCamera targetCam = m->ViewCamera;
SetupCameraMatrixSmooth(m, &targetCam.m_Orientation);
float rotateXDelta = m->RotateX.Update(DeltaTime);
float rotateXDelta = m->RotateX.Update(deltaRealTime);
if (rotateXDelta)
{
CVector3D rightwards = targetCam.m_Orientation.GetLeft() * -1.0f;

View File

@ -69,10 +69,13 @@ public:
CObjectManager& GetObjectManager() const;
// Update: Update all the view information (i.e. rotate camera, scroll,
// whatever). This will *not* change any World information - only the
// *presentation*
void Update(float DeltaTime);
/**
* Updates all the view information (i.e. rotate camera, scroll, whatever). This will *not* change any
* World information - only the *presentation*.
*
* @param deltaRealTime Elapsed real time since the last frame.
*/
void Update(const float deltaRealTime);
void BeginFrame();
void Render();

View File

@ -60,9 +60,9 @@ void CParticleManager::ClearUnattachedEmitters()
m_UnattachedEmitters.clear();
}
void CParticleManager::Interpolate(float frameLength)
void CParticleManager::Interpolate(const float simFrameLength)
{
m_CurrentTime += frameLength;
m_CurrentTime += simFrameLength;
}
struct EmitterHasNoParticles

View File

@ -47,7 +47,7 @@ public:
void RenderSubmit(SceneCollector& collector, const CFrustum& frustum);
void Interpolate(float frameLength);
void Interpolate(const float simFrameLength);
float GetCurrentTime() const { return m_CurrentTime; }

View File

@ -309,9 +309,9 @@ static void Frame()
// .. new method - filtered and more smooth, but errors may accumulate
#else
const float TimeSinceLastFrame = 1.0 / g_frequencyFilter->SmoothedFrequency();
const float realTimeSinceLastFrame = 1.0 / g_frequencyFilter->SmoothedFrequency();
#endif
ENSURE(TimeSinceLastFrame > 0.0f);
ENSURE(realTimeSinceLastFrame > 0.0f);
// decide if update/render is necessary
bool need_render = !g_app_minimized;
@ -372,9 +372,9 @@ static void Frame()
if (g_Game && g_Game->IsGameStarted() && need_update)
{
g_Game->Update(TimeSinceLastFrame);
g_Game->Update(realTimeSinceLastFrame);
g_Game->GetView()->Update(float(TimeSinceLastFrame));
g_Game->GetView()->Update(float(realTimeSinceLastFrame));
CCamera* camera = g_Game->GetView()->GetCamera();
CMatrix3D& orientation = camera->m_Orientation;
@ -405,7 +405,7 @@ static void Frame()
g_UserReporter.Update();
g_Console->Update(TimeSinceLastFrame);
g_Console->Update(realTimeSinceLastFrame);
ogl_WarnIfError();
if(need_render)

View File

@ -58,7 +58,7 @@ static std::wstring Hexify(const std::string& s)
}
CNetTurnManager::CNetTurnManager(CSimulation2& simulation, u32 defaultTurnLength, int clientId, IReplayLogger& replay) :
m_Simulation2(simulation), m_CurrentTurn(0), m_ReadyTurn(1), m_TurnLength(defaultTurnLength), m_DeltaTime(0),
m_Simulation2(simulation), m_CurrentTurn(0), m_ReadyTurn(1), m_TurnLength(defaultTurnLength), m_DeltaSimTime(0),
m_PlayerId(-1), m_ClientId(clientId), m_HasSyncError(false), m_Replay(replay),
m_TimeWarpNumTurns(0)
{
@ -75,7 +75,7 @@ void CNetTurnManager::ResetState(u32 newCurrentTurn, u32 newReadyTurn)
{
m_CurrentTurn = newCurrentTurn;
m_ReadyTurn = newReadyTurn;
m_DeltaTime = 0;
m_DeltaSimTime = 0;
size_t queuedCommandsSize = m_QueuedCommands.size();
m_QueuedCommands.clear();
m_QueuedCommands.resize(queuedCommandsSize);
@ -86,11 +86,11 @@ void CNetTurnManager::SetPlayerID(int playerId)
m_PlayerId = playerId;
}
bool CNetTurnManager::WillUpdate(float frameLength)
bool CNetTurnManager::WillUpdate(float simFrameLength)
{
// Keep this in sync with the return value of Update()
if (m_DeltaTime + frameLength < 0)
if (m_DeltaSimTime + simFrameLength < 0)
return false;
if (m_ReadyTurn <= m_CurrentTurn)
@ -99,12 +99,12 @@ bool CNetTurnManager::WillUpdate(float frameLength)
return true;
}
bool CNetTurnManager::Update(float frameLength, size_t maxTurns)
bool CNetTurnManager::Update(float simFrameLength, size_t maxTurns)
{
m_DeltaTime += frameLength;
m_DeltaSimTime += simFrameLength;
// If we haven't reached the next turn yet, do nothing
if (m_DeltaTime < 0)
if (m_DeltaSimTime < 0)
return false;
NETTURN_LOG((L"Update current=%d ready=%d\n", m_CurrentTurn, m_ReadyTurn));
@ -120,7 +120,7 @@ bool CNetTurnManager::Update(float frameLength, size_t maxTurns)
// Reset the next-turn timer to 0 so we try again next update but
// so we don't rush to catch up in subsequent turns.
// TODO: we should do clever rate adjustment instead of just pausing like this.
m_DeltaTime = 0;
m_DeltaSimTime = 0;
return false;
}
@ -130,7 +130,7 @@ bool CNetTurnManager::Update(float frameLength, size_t maxTurns)
for (size_t i = 0; i < maxTurns; ++i)
{
// Check that we've reached the i'th next turn
if (m_DeltaTime < 0)
if (m_DeltaSimTime < 0)
break;
// Check that the i'th next turn is still ready
@ -173,7 +173,7 @@ bool CNetTurnManager::Update(float frameLength, size_t maxTurns)
NotifyFinishedUpdate(m_CurrentTurn);
// Set the time for the next turn update
m_DeltaTime -= m_TurnLength / 1000.f;
m_DeltaSimTime -= m_TurnLength / 1000.f;
}
return true;
@ -181,7 +181,7 @@ bool CNetTurnManager::Update(float frameLength, size_t maxTurns)
bool CNetTurnManager::UpdateFastForward()
{
m_DeltaTime = 0;
m_DeltaSimTime = 0;
NETTURN_LOG((L"UpdateFastForward current=%d ready=%d\n", m_CurrentTurn, m_ReadyTurn));
@ -247,13 +247,13 @@ void CNetTurnManager::OnSyncError(u32 turn, const std::string& expectedHash)
LOGERROR(L"%ls", msg.str().c_str());
}
void CNetTurnManager::Interpolate(float frameLength)
void CNetTurnManager::Interpolate(float simFrameLength, float realFrameLength)
{
// TODO: using m_TurnLength might be a bit dodgy when length changes - maybe
// we need to save the previous turn length?
float offset = clamp(m_DeltaTime / (m_TurnLength / 1000.f) + 1.0, 0.0, 1.0);
m_Simulation2.Interpolate(frameLength, offset);
float offset = clamp(m_DeltaSimTime / (m_TurnLength / 1000.f) + 1.0, 0.0, 1.0);
m_Simulation2.Interpolate(simFrameLength, offset, realFrameLength);
}
void CNetTurnManager::AddCommand(int client, int player, CScriptValRooted data, u32 turn)

View File

@ -71,10 +71,11 @@ public:
* Advance the simulation by a certain time. If this brings us past the current
* turn length, the next turns are processed and the function returns true.
* Otherwise, nothing happens and it returns false.
* @param frameLength length of the previous frame in seconds
* @param maxTurns maximum number of turns to simulate at once
*
* @param simFrameLength Length of the previous frame, in simulation seconds
* @param maxTurns Maximum number of turns to simulate at once
*/
bool Update(float frameLength, size_t maxTurns);
bool Update(float simFrameLength, size_t maxTurns);
/**
* Advance the simulation by as much as possible. Intended for catching up
@ -84,16 +85,17 @@ public:
bool UpdateFastForward();
/**
* Returns whether Update(frameLength, ...) will process at least one new turn.
* @param frameLength length of the previous frame in seconds
* Returns whether Update(simFrameLength, ...) will process at least one new turn.
* @param simFrameLength Length of the previous frame, in simulation seconds
*/
bool WillUpdate(float frameLength);
bool WillUpdate(float simFrameLength);
/**
* Advance the graphics by a certain time.
* @param frameLength length of the previous frame in seconds
* @param simFrameLength Length of the previous frame, in simulation seconds
* @param realFrameLength Length of the previous frame, in real time seconds
*/
void Interpolate(float frameLength);
void Interpolate(float simFrameLength, float realFrameLength);
/**
* Called by networking code when a simulation message is received.
@ -172,8 +174,9 @@ protected:
int m_PlayerId;
uint m_ClientId;
/// Time remaining until we ought to execute the next turn
float m_DeltaTime;
/// Simulation time remaining until we ought to execute the next turn (as a negative value to
/// add elapsed time increments to until we reach 0).
float m_DeltaSimTime;
bool m_HasSyncError;

View File

@ -144,12 +144,12 @@ void CConsole::Trim(wchar_t* szMessage, const wchar_t cChar, size_t iSize)
}
void CConsole::Update(const float DeltaTime)
void CConsole::Update(const float deltaRealTime)
{
if(m_bToggle)
{
const float AnimateTime = .30f;
const float Delta = DeltaTime / AnimateTime;
const float Delta = deltaRealTime / AnimateTime;
if(m_bVisible)
{
m_fVisibleFrac += Delta;

View File

@ -61,7 +61,10 @@ public:
void ToggleVisible();
void SetVisible(bool visible);
void Update(float DeltaTime);
/**
* @param deltaRealTime Elapsed real time since the last frame.
*/
void Update(const float deltaRealTime);
void Render();

View File

@ -214,7 +214,7 @@ PSRETURN CGame::ReallyStartGame()
// because Update might never interpolate (e.g. if the game starts paused)
// and we could end up rendering before having set up any models (so they'd
// all be invisible)
Interpolate(0);
Interpolate(0, 0);
debug_printf(L"GAME STARTED, ALL INIT COMPLETE\n");
m_GameStarted=true;
@ -252,16 +252,7 @@ void CGame::StartGame(const CScriptValRooted& attribs, const std::string& savedS
// 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.
/**
* Periodic heartbeat that controls the process.
* Simulation update is called and game status update is called.
*
* @param deltaTime Double. Elapsed time since last beat in seconds.
* @param doInterpolate Bool. Perform interpolation if true.
* @return bool false if it can't keep up with the desired simulation rate
* indicating that you might want to render less frequently.
**/
bool CGame::Update(double deltaTime, bool doInterpolate)
bool CGame::Update(const double deltaRealTime, bool doInterpolate)
{
if (m_Paused)
return true;
@ -269,14 +260,14 @@ bool CGame::Update(double deltaTime, bool doInterpolate)
if (!m_TurnManager)
return true;
deltaTime *= m_SimRate;
const double deltaSimTime = deltaRealTime * m_SimRate;
bool ok = true;
if (deltaTime)
if (deltaSimTime)
{
// To avoid confusing the profiler, we need to trigger the new turn
// while we're not nested inside any PROFILE blocks
if (m_TurnManager->WillUpdate(deltaTime))
if (m_TurnManager->WillUpdate(deltaSimTime))
g_Profiler.Turn();
// At the normal sim rate, we currently want to render at least one
@ -285,7 +276,7 @@ bool CGame::Update(double deltaTime, bool doInterpolate)
// so just use the sim rate itself as the number of turns per frame.
size_t maxTurns = (size_t)m_SimRate;
if (m_TurnManager->Update(deltaTime, maxTurns))
if (m_TurnManager->Update(deltaSimTime, maxTurns))
{
{
PROFILE3("gui sim update");
@ -298,26 +289,26 @@ bool CGame::Update(double deltaTime, bool doInterpolate)
if (doInterpolate)
{
m_TurnManager->Interpolate(deltaTime);
m_TurnManager->Interpolate(deltaSimTime, deltaRealTime);
}
// TODO: maybe we should add a CCmpParticleInterface that passes the interpolation commands
// etc to CParticleManager. But in the meantime just handle it explicitly here.
if (doInterpolate && CRenderer::IsInitialised())
g_Renderer.GetParticleManager().Interpolate(deltaTime);
g_Renderer.GetParticleManager().Interpolate(deltaSimTime);
return ok;
}
void CGame::Interpolate(float frameLength)
void CGame::Interpolate(float simFrameLength, float realFrameLength)
{
if (!m_TurnManager)
return;
m_TurnManager->Interpolate(frameLength);
m_TurnManager->Interpolate(simFrameLength, realFrameLength);
if (CRenderer::IsInitialised())
g_Renderer.GetParticleManager().Interpolate(frameLength);
g_Renderer.GetParticleManager().Interpolate(simFrameLength);
}

View File

@ -56,7 +56,7 @@ class CGame
**/
bool m_GameStarted;
/**
* scale multiplier for simulation rate.
* Timescale multiplier for simulation rate.
**/
float m_SimRate;
@ -76,12 +76,18 @@ public:
void StartGame(const CScriptValRooted& attribs, const std::string& savedState);
PSRETURN ReallyStartGame();
/*
Perform all per-frame updates
/**
* Periodic heartbeat that controls the process. performs all per-frame updates.
* Simulation update is called and game status update is called.
*
* @param deltaRealTime Elapsed real time since last beat/frame, in seconds.
* @param doInterpolate Perform graphics interpolation if true.
* @return bool false if it can't keep up with the desired simulation rate
* indicating that you might want to render less frequently.
*/
bool Update(double deltaTime, bool doInterpolate = true);
bool Update(const double deltaRealTime, bool doInterpolate = true);
void Interpolate(float frameLength);
void Interpolate(float simFrameLength, float realFrameLength);
int GetPlayerID();
void SetPlayerID(int playerID);

View File

@ -124,13 +124,18 @@ class CMessageInterpolate : public CMessage
public:
DEFAULT_MESSAGE_IMPL(Interpolate)
CMessageInterpolate(float frameTime, float offset) :
frameTime(frameTime), offset(offset)
CMessageInterpolate(float deltaSimTime, float offset, float deltaRealTime) :
deltaSimTime(deltaSimTime), offset(offset), deltaRealTime(deltaRealTime)
{
}
float frameTime; // time in seconds since previous interpolate
float offset; // range [0, 1] (inclusive); fractional time of current frame between previous/next simulation turns
/// Elapsed simulation time since previous interpolate, in seconds. This is similar to the elapsed real time, except
/// it is scaled by the current simulation rate (and might indeed be zero).
float deltaSimTime;
/// Range [0, 1] (inclusive); fractional time of current frame between previous/next simulation turns.
float offset;
/// Elapsed real time since previous interpolate, in seconds.
float deltaRealTime;
};
/**

View File

@ -143,7 +143,7 @@ public:
int ProgressiveLoad();
void Update(int turnLength, const std::vector<SimulationCommand>& commands);
static void UpdateComponents(CSimContext& simContext, fixed turnLengthFixed, const std::vector<SimulationCommand>& commands);
void Interpolate(float frameLength, float frameOffset);
void Interpolate(float simFrameLength, float frameOffset, float realFrameLength);
void DumpState();
@ -513,13 +513,13 @@ void CSimulation2Impl::UpdateComponents(CSimContext& simContext, fixed turnLengt
componentManager.FlushDestroyedComponents();
}
void CSimulation2Impl::Interpolate(float frameLength, float frameOffset)
void CSimulation2Impl::Interpolate(float simFrameLength, float frameOffset, float realFrameLength)
{
PROFILE3("sim interpolate");
m_LastFrameOffset = frameOffset;
CMessageInterpolate msg(frameLength, frameOffset);
CMessageInterpolate msg(simFrameLength, frameOffset, realFrameLength);
m_ComponentManager.BroadcastMessage(msg);
// Clean up any entities destroyed during interpolate (e.g. local corpses)
@ -653,9 +653,9 @@ void CSimulation2::Update(int turnLength, const std::vector<SimulationCommand>&
m->Update(turnLength, commands);
}
void CSimulation2::Interpolate(float frameLength, float frameOffset)
void CSimulation2::Interpolate(float simFrameLength, float frameOffset, float realFrameLength)
{
m->Interpolate(frameLength, frameOffset);
m->Interpolate(simFrameLength, frameOffset, realFrameLength);
}
void CSimulation2::RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling)

View File

@ -155,7 +155,7 @@ public:
void Update(int turnLength);
void Update(int turnLength, const std::vector<SimulationCommand>& commands);
void Interpolate(float frameLength, float frameOffset);
void Interpolate(float simFrameLength, float frameOffset, float realFrameLength);
void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling);
/**

View File

@ -157,7 +157,7 @@ public:
}
}
m_CurrentTime += msgData.frameTime;
m_CurrentTime += msgData.deltaSimTime;
if (m_CurrentTime > m_DelayTime)
{

View File

@ -80,7 +80,7 @@ public:
case MT_Interpolate:
{
const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
Interpolate(msgData.frameTime, msgData.offset);
Interpolate(msgData.deltaSimTime, msgData.offset);
break;
}
case MT_RenderSubmit:

View File

@ -438,7 +438,7 @@ public:
if (delta < 0) delta += 2*(float)M_PI; // range 0..2PI
delta -= (float)M_PI; // range -M_PI..M_PI
// Clamp to max rate
float deltaClamped = clamp(delta, -m_RotYSpeed*msgData.frameTime, +m_RotYSpeed*msgData.frameTime);
float deltaClamped = clamp(delta, -m_RotYSpeed*msgData.deltaSimTime, +m_RotYSpeed*msgData.deltaSimTime);
// Calculate new orientation, in a peculiar way in order to make sure the
// result gets close to m_orientation (rather than being n*2*M_PI out)
m_InterpolatedRotY = rotY + deltaClamped - delta;

View File

@ -89,7 +89,7 @@ public:
case MT_Interpolate:
{
const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
Interpolate(msgData.frameTime);
Interpolate(msgData.deltaSimTime);
break;
}
case MT_RenderSubmit:

View File

@ -17,7 +17,6 @@
#include "precompiled.h"
#include "simulation2/system/Component.h"
#include "ICmpSelectable.h"
#include "graphics/Overlay.h"
@ -40,6 +39,7 @@
#include "simulation2/components/ICmpPlayerManager.h"
#include "simulation2/components/ICmpWaterManager.h"
#include "simulation2/helpers/Render.h"
#include "simulation2/system/Component.h"
class CCmpSelectable : public ICmpSelectable
{
@ -62,7 +62,6 @@ public:
m_FadeBaselineAlpha(0.f), m_FadeDeltaAlpha(0.f), m_FadeProgress(0.f)
{
m_Color = CColor(0, 0, 0, m_FadeBaselineAlpha);
m_LastRealTime = 0;
}
~CCmpSelectable()
@ -189,6 +188,7 @@ private:
SOverlayLine* m_DebugBoundingBoxOverlay;
SOverlayLine* m_DebugSelectionBoxOverlay;
/// Is this entity selectable only in the editor?
bool m_EditorOnly;
/// Current selection overlay color. Alpha component is subject to fading.
@ -199,10 +199,10 @@ private:
float m_FadeDeltaAlpha;
/// Linear time progress of the fade, between 0 and m_FadeDuration.
float m_FadeProgress;
/// Total duration of a single fade, in seconds. Assumed constant for now; feel free to change this into
/// a member variable if you need to adjust it per component.
static const double FADE_DURATION;
double m_LastRealTime; // temporary member, only here to support the TODO case in HandleMessage.
};
const double CCmpSelectable::FADE_DURATION = 0.3;
@ -213,17 +213,11 @@ void CCmpSelectable::HandleMessage(const CMessage& msg, bool UNUSED(global))
{
case MT_Interpolate:
{
// TODO: temporary solution using real elapsed time instead of simulation time to prevent
// the overlay fades from not happening in atlas while the simulation is paused. As a cleaner
// solution, we should add a field to CMessageInterpolate that holds an elapsed real time delta.
//static double lastRealTime = 0;
const double currentRealTime = timer_Time();
float deltaRealTime = (float)(currentRealTime - m_LastRealTime);
m_LastRealTime = currentRealTime;
const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
if (m_FadeDeltaAlpha != 0.f)
{
m_FadeProgress += deltaRealTime;
m_FadeProgress += msgData.deltaRealTime;
if (m_FadeProgress >= FADE_DURATION)
{
const float targetAlpha = m_FadeBaselineAlpha + m_FadeDeltaAlpha;
@ -242,10 +236,7 @@ void CCmpSelectable::HandleMessage(const CMessage& msg, bool UNUSED(global))
// update dynamic overlay only when visible
if (m_Color.a > 0)
{
const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
UpdateDynamicOverlay(msgData.offset);
}
break;
}

View File

@ -190,7 +190,7 @@ public:
case MT_Interpolate:
{
const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
Interpolate(msgData.frameTime, msgData.offset);
Interpolate(msgData.deltaSimTime, msgData.offset);
break;
}
case MT_RenderSubmit:

View File

@ -269,7 +269,7 @@ public:
case MT_Interpolate:
{
const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
Interpolate(msgData.frameTime, msgData.offset);
Interpolate(msgData.deltaSimTime, msgData.offset);
break;
}
case MT_RenderSubmit:

View File

@ -95,17 +95,19 @@ MESSAGE_1(Update_Final, fixed, turnLength)
jsval CMessageInterpolate::ToJSVal(ScriptInterface& scriptInterface) const
{
TOJSVAL_SETUP();
SET_MSG_PROPERTY(frameTime);
SET_MSG_PROPERTY(deltaSimTime);
SET_MSG_PROPERTY(offset);
SET_MSG_PROPERTY(deltaRealTime);
return OBJECT_TO_JSVAL(obj);
}
CMessage* CMessageInterpolate::FromJSVal(ScriptInterface& scriptInterface, jsval val)
{
FROMJSVAL_SETUP();
GET_MSG_PROPERTY(float, frameTime);
GET_MSG_PROPERTY(float, deltaSimTime);
GET_MSG_PROPERTY(float, offset);
return new CMessageInterpolate(frameTime, offset);
GET_MSG_PROPERTY(float, deltaRealTime);
return new CMessageInterpolate(deltaSimTime, offset, deltaRealTime);
}
////////////////////////////////

View File

@ -158,7 +158,7 @@ public:
CMessageTurnStart msg1;
CMessageUpdate msg2(fixed::FromInt(100));
CMessageInterpolate msg3(0, 0);
CMessageInterpolate msg3(0, 0, 0);
TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent1, IID_Test1))->GetX(), 11000);
TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent2, IID_Test1))->GetX(), 12000);

View File

@ -509,20 +509,20 @@ void ActorViewer::Render()
ogl_WarnIfError();
}
void ActorViewer::Update(float dt)
void ActorViewer::Update(float simFrameLength, float realFrameLength)
{
m.Simulation2.Update((int)(dt*1000));
m.Simulation2.Interpolate(dt, 0);
g_Renderer.GetParticleManager().Interpolate(dt);
m.Simulation2.Update((int)(simFrameLength*1000));
m.Simulation2.Interpolate(simFrameLength, 0, realFrameLength);
g_Renderer.GetParticleManager().Interpolate(simFrameLength);
if (m.WalkEnabled && m.CurrentSpeed)
{
CmpPtr<ICmpPosition> cmpPosition(m.Simulation2, m.Entity);
if (cmpPosition)
{
// Move the model by speed*dt forwards
// Move the model by speed*simFrameLength forwards
float z = cmpPosition->GetPosition().Z.ToFloat();
z -= m.CurrentSpeed*dt;
z -= m.CurrentSpeed*simFrameLength;
// Wrap at the edges, so it doesn't run off into the horizon
ssize_t c = TERRAIN_TILE_SIZE * m.Terrain.GetPatchesPerSide()*PATCH_SIZE/2;
if (z < c - TERRAIN_TILE_SIZE*PATCH_SIZE * 0.1f)

View File

@ -46,7 +46,7 @@ public:
void SetAxesMarkerEnabled(bool enabled);
void SetPropPointsMode(int mode);
void Render();
void Update(float dt);
void Update(float simFrameLength, float realFrameLength);
private:
ActorViewerImpl& m;

View File

@ -144,12 +144,12 @@ static void* RunEngine(void* data)
{
double time = timer_Time();
static double last_time = time;
float length = (float)(time-last_time);
float realFrameLength = (float)(time-last_time);
last_time = time;
ENSURE(length >= 0.0f);
ENSURE(realFrameLength >= 0.0f);
// TODO: filter out big jumps, e.g. when having done a lot of slow
// processing in the last frame
state.frameLength = length;
state.realFrameLength = realFrameLength;
}
// Process the input that was received in the past
@ -222,7 +222,7 @@ static void* RunEngine(void* data)
if (g_GUI)
g_GUI->TickObjects();
state.view->Update(state.frameLength);
state.view->Update(state.realFrameLength);
state.view->Render();

View File

@ -34,7 +34,7 @@ struct GameLoopState
AtlasView* view; // current 'view' (controls updates, rendering, etc)
const void* glCanvas; // the wxGlCanvas to draw on
float frameLength; // smoothed to avoid large jumps
float realFrameLength; ///< Real-time duration of the last frame, in seconds. Smoothed to avoid large jumps (TODO).
struct Input
{

View File

@ -67,43 +67,43 @@ bool InputProcessor::ProcessInput(GameLoopState* state)
if (state->input.scrollSpeed[0] != 0.0f)
{
camera->m_Orientation.Translate(forwards * (input.scrollSpeed[0] * state->frameLength));
camera->m_Orientation.Translate(forwards * (input.scrollSpeed[0] * state->realFrameLength));
moved = true;
}
if (state->input.scrollSpeed[1] != 0.0f)
{
camera->m_Orientation.Translate(forwards * (-input.scrollSpeed[1] * state->frameLength));
camera->m_Orientation.Translate(forwards * (-input.scrollSpeed[1] * state->realFrameLength));
moved = true;
}
if (state->input.scrollSpeed[2] != 0.0f)
{
camera->m_Orientation.Translate(leftwards * (input.scrollSpeed[2] * state->frameLength));
camera->m_Orientation.Translate(leftwards * (input.scrollSpeed[2] * state->realFrameLength));
moved = true;
}
if (state->input.scrollSpeed[3] != 0.0f)
{
camera->m_Orientation.Translate(leftwards * (-input.scrollSpeed[3] * state->frameLength));
camera->m_Orientation.Translate(leftwards * (-input.scrollSpeed[3] * state->realFrameLength));
moved = true;
}
if (state->input.scrollSpeed[4] != 0.0f)
{
Rotate(*camera, input.scrollSpeed[4] * state->frameLength * g_ViewRotateScale);
Rotate(*camera, input.scrollSpeed[4] * state->realFrameLength * g_ViewRotateScale);
moved = true;
}
if (state->input.scrollSpeed[5] != 0.0f)
{
Rotate(*camera, -input.scrollSpeed[5] * state->frameLength * g_ViewRotateScale);
Rotate(*camera, -input.scrollSpeed[5] * state->realFrameLength * g_ViewRotateScale);
moved = true;
}
if (state->input.zoomDelta != 0.0f)
{
float zoom_proportion = powf(g_ViewZoomSmoothness, state->frameLength);
float zoom_proportion = powf(g_ViewZoomSmoothness, state->realFrameLength);
camera->m_Orientation.Translate(inwards * (input.zoomDelta * (1.0f - zoom_proportion)));
input.zoomDelta *= zoom_proportion;

View File

@ -74,9 +74,9 @@ AtlasViewActor::~AtlasViewActor()
delete m_ActorViewer;
}
void AtlasViewActor::Update(float frameLength)
void AtlasViewActor::Update(float realFrameLength)
{
m_ActorViewer->Update(frameLength * m_SpeedMultiplier);
m_ActorViewer->Update(realFrameLength * m_SpeedMultiplier, realFrameLength);
}
void AtlasViewActor::Render()
@ -114,9 +114,9 @@ bool AtlasViewActor::WantsHighFramerate()
return false;
}
void AtlasViewActor::SetSpeedMultiplier(float speed)
void AtlasViewActor::SetSpeedMultiplier(float speedMultiplier)
{
m_SpeedMultiplier = speed;
m_SpeedMultiplier = speedMultiplier;
}
ActorViewer& AtlasViewActor::GetActorViewer()
@ -181,9 +181,9 @@ CSimulation2* AtlasViewGame::GetSimulation2()
return g_Game->GetSimulation2();
}
void AtlasViewGame::Update(float frameLength)
void AtlasViewGame::Update(float realFrameLength)
{
float actualFrameLength = frameLength * m_SpeedMultiplier;
const float actualFrameLength = realFrameLength * m_SpeedMultiplier;
// Clean up any entities destroyed during UI message processing
g_Game->GetSimulation2()->FlushDestroyedEntities();
@ -191,14 +191,15 @@ void AtlasViewGame::Update(float frameLength)
if (m_SpeedMultiplier == 0.f)
{
// Update unit interpolation
g_Game->Interpolate(0.0);
g_Game->Interpolate(0.0, realFrameLength);
// Update particles even when the game is paused, so people can see
// what they look like. (TODO: maybe it'd be nice if this only applied in
// what they look like (i.e., use real time to simulate them).
// (TODO: maybe it'd be nice if this only applied in
// the not-testing-game editor state, not the testing-game-but-currently-paused
// state. Or maybe display a static snapshot of the particles (at some time
// later than 0 so they're actually visible) instead of animation, or something.)
g_Renderer.GetParticleManager().Interpolate(frameLength);
g_Renderer.GetParticleManager().Interpolate(realFrameLength);
}
else
{
@ -220,13 +221,13 @@ void AtlasViewGame::Update(float frameLength)
// Interpolate the graphics - we only want to do this once per visual frame,
// not in every call to g_Game->Update
g_Game->Interpolate(actualFrameLength);
g_Game->Interpolate(actualFrameLength, realFrameLength);
}
// Cinematic motion should be independent of simulation update, so we can
// preview the cinematics by themselves
if (g_Game->GetView()->GetCinema()->IsPlaying())
g_Game->GetView()->GetCinema()->Update(frameLength);
g_Game->GetView()->GetCinema()->Update(realFrameLength);
}
void AtlasViewGame::Render()

View File

@ -31,11 +31,14 @@ class CSimulation2;
class AtlasViewGame;
class AtlasViewActor;
/**
* Superclass for all Atlas game views.
*/
class AtlasView
{
public:
virtual ~AtlasView();
virtual void Update(float UNUSED(frameLength)) { };
virtual void Update(float UNUSED(realFrameLength)) { };
virtual void Render() { };
virtual void DrawOverlays() { };
virtual CCamera& GetCamera() = 0;
@ -72,12 +75,15 @@ private:
class SimState;
/**
* Main editor/game view of Atlas. Editing the world/scenario and simulation testing happens here.
*/
class AtlasViewGame : public AtlasView
{
public:
AtlasViewGame();
virtual ~AtlasViewGame();
virtual void Update(float frameLength);
virtual void Update(float realFrameLength);
virtual void Render();
virtual void DrawOverlays();
virtual CCamera& GetCamera();
@ -87,7 +93,7 @@ public:
virtual void SetParam(const std::wstring& name, bool value);
virtual void SetParam(const std::wstring& name, const std::wstring& value);
void SetSpeedMultiplier(float speed);
void SetSpeedMultiplier(float speedMultiplier);
void SaveState(const std::wstring& label);
void RestoreState(const std::wstring& label);
std::wstring DumpState(bool binary);
@ -112,13 +118,17 @@ private:
class ActorViewer;
/**
* Actor Viewer window in Atlas. Dedicated view for examining a single actor/entity and its variations,
* animations, etc. in more detail.
*/
class AtlasViewActor : public AtlasView
{
public:
AtlasViewActor();
~AtlasViewActor();
virtual void Update(float frameLength);
virtual void Update(float realFrameLength);
virtual void Render();
virtual CCamera& GetCamera();
virtual CSimulation2* GetSimulation2();
@ -129,7 +139,7 @@ public:
virtual void SetParam(const std::wstring& name, int value);
virtual void SetParam(const std::wstring& name, const AtlasMessage::Colour& value);
void SetSpeedMultiplier(float speed);
void SetSpeedMultiplier(float speedMultiplier);
ActorViewer& GetActorViewer();
private: