From 0b72d0f86ca4e66bbd2a218842155de35799961f Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Tue, 13 Sep 2005 03:57:34 +0000 Subject: [PATCH] Atlas: More portable GL context usage. Smoother and more responsive terrain editing. This was SVN commit r2705. --- source/tools/atlas/AtlasUI/AtlasUI.vcproj | 10 +++ .../HighResTimer/HighResTimer.cpp | 31 +++++++++ .../HighResTimer/HighResTimer.h | 9 +++ .../tools/atlas/AtlasUI/Misc/DLLInterface.h | 5 ++ .../AtlasUI/ScenarioEditor/ScenarioEditor.cpp | 68 ++++++++++++++---- .../AtlasUI/ScenarioEditor/ScenarioEditor.h | 1 + .../ScenarioEditor/Tools/AlterElevation.cpp | 23 +++++-- .../ScenarioEditor/Tools/Common/Tools.h | 2 +- source/tools/atlas/GameInterface/GameLoop.cpp | 69 ++++++++++++------- source/tools/atlas/GameInterface/GameLoop.h | 4 +- .../GameInterface/Handlers/Elevation.cpp | 29 ++++++-- .../GameInterface/Handlers/GraphicsSetup.cpp | 6 +- .../GameInterface/Handlers/MessageHandler.h | 1 + .../atlas/GameInterface/InputProcessor.cpp | 9 --- .../tools/atlas/GameInterface/MessagePasser.h | 12 ---- .../atlas/GameInterface/MessagePasserImpl.cpp | 10 +-- .../atlas/GameInterface/MessagePasserImpl.h | 4 +- source/tools/atlas/GameInterface/Messages.h | 5 +- 18 files changed, 208 insertions(+), 90 deletions(-) create mode 100644 source/tools/atlas/AtlasUI/CustomControls/HighResTimer/HighResTimer.cpp create mode 100644 source/tools/atlas/AtlasUI/CustomControls/HighResTimer/HighResTimer.h diff --git a/source/tools/atlas/AtlasUI/AtlasUI.vcproj b/source/tools/atlas/AtlasUI/AtlasUI.vcproj index 2e052c6290..28bef702f2 100644 --- a/source/tools/atlas/AtlasUI/AtlasUI.vcproj +++ b/source/tools/atlas/AtlasUI/AtlasUI.vcproj @@ -281,6 +281,16 @@ RelativePath=".\CustomControls\FileHistory\FileHistory.h"> + + + + + + SetCurrent(); +} + +ATLASDLLIMPEXP void Atlas_GLSwapBuffers(void* context) +{ + ((wxGLContext*)context)->SwapBuffers(); +} + + ////////////////////////////////////////////////////////////////////////// + +volatile bool g_FrameHasEnded; +// Called from game thread +ATLASDLLIMPEXP void Atlas_NotifyEndOfFrame() +{ + g_FrameHasEnded = true; +} + enum { ID_Quit = 1, @@ -133,6 +159,8 @@ BEGIN_EVENT_TABLE(ScenarioEditor, wxFrame) EVT_MENU(wxID_REDO, ScenarioEditor::OnRedo) EVT_MENU(ID_Wireframe, ScenarioEditor::OnWireframe) + + EVT_IDLE(ScenarioEditor::OnIdle) END_EVENT_TABLE() @@ -213,7 +241,7 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent) // Send setup messages to game engine: #ifndef UI_ONLY - ADD_COMMAND(SetContext(canvas->GetHDC(), canvas->GetContext()->GetGLRC())); + ADD_COMMAND(SetContext(canvas->GetContext())); ADD_COMMAND(CommandString("init")); @@ -225,8 +253,9 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent) // XXX USE_TOOL(AlterElevation); + // Set up a timer to make sure tool-updates happen even when there's no idle time m_Timer.SetOwner(this); - m_Timer.Start(50); + m_Timer.Start(20); } @@ -244,13 +273,26 @@ void ScenarioEditor::OnClose(wxCloseEvent&) } +static void UpdateTool() +{ + // Don't keep posting events if the game can't keep up + if (g_FrameHasEnded) + { + g_FrameHasEnded = false; // (threadiness doesn't matter here) + // TODO: Smoother timing stuff? + static double last = g_Timer.GetTime(); + double time = g_Timer.GetTime(); + g_CurrentTool->OnTick(time-last); + last = time; + } +} void ScenarioEditor::OnTimer(wxTimerEvent&) { - // TODO: Improve timer stuff - smoother, etc - static wxLongLong last = wxGetLocalTimeMillis(); - wxLongLong time = wxGetLocalTimeMillis(); - g_CurrentTool->OnTick((time-last).ToLong()); - last = time; + UpdateTool(); +} +void ScenarioEditor::OnIdle(wxIdleEvent&) +{ + UpdateTool(); } void ScenarioEditor::OnQuit(wxCommandEvent&) diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h index 6f9ef0a527..57436c9cad 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h @@ -9,6 +9,7 @@ public: ScenarioEditor(wxWindow* parent); void OnClose(wxCloseEvent& event); void OnTimer(wxTimerEvent& event); + void OnIdle(wxIdleEvent& event); void OnQuit(wxCommandEvent& event); void OnUndo(wxCommandEvent& event); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/AlterElevation.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/AlterElevation.cpp index bf3d3f9f0a..c79704f727 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/AlterElevation.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/AlterElevation.cpp @@ -10,7 +10,7 @@ class AlterElevation : public ITool { public: AlterElevation() - : m_IsActive(false) + : m_Direction(0) { } @@ -19,13 +19,19 @@ public: if (evt.LeftDown()) { ScenarioEditor::GetCommandProc().FinaliseLastCommand(); - m_IsActive = true; + m_Direction = +1; m_Pos = Position(evt.GetPosition()); } - else if (evt.LeftUp()) + else if (evt.RightDown()) { ScenarioEditor::GetCommandProc().FinaliseLastCommand(); - m_IsActive = false; + m_Direction = -1; + m_Pos = Position(evt.GetPosition()); + } + else if (evt.LeftUp() || evt.RightUp()) + { + ScenarioEditor::GetCommandProc().FinaliseLastCommand(); + m_Direction = 0; } else if (evt.Dragging()) { @@ -44,15 +50,18 @@ public: void OnTick(float dt) { - if (m_IsActive) + if (m_Direction) { - ADD_WORLDCOMMAND(AlterElevation, (m_Pos, dt*4.096f)); + // TODO: If the mouse hasn't been moved in this stroke, use the + // same tile position as last time (else it's annoying when digging + // deep holes or building tall hills.) + ADD_WORLDCOMMAND(AlterElevation, (m_Pos, dt*4096.f*m_Direction)); } } private: - bool m_IsActive; + int m_Direction; // +1 = raise, -1 = lower, 0 = inactive Position m_Pos; }; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.h index 440ebb1f5c..dbe5c2d67a 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.h @@ -14,7 +14,7 @@ public: virtual void OnMouse(wxMouseEvent& evt) = 0; virtual void OnKey(wxKeyEvent& evt, int dir) = 0; - virtual void OnTick(float dt) = 0; + virtual void OnTick(float dt) = 0; // dt in seconds virtual ~ITool() {}; }; diff --git a/source/tools/atlas/GameInterface/GameLoop.cpp b/source/tools/atlas/GameInterface/GameLoop.cpp index 7db7b67a0f..9b5837c0b7 100644 --- a/source/tools/atlas/GameInterface/GameLoop.cpp +++ b/source/tools/atlas/GameInterface/GameLoop.cpp @@ -13,23 +13,17 @@ #include "lib/timer.h" #include "ps/CLogger.h" -#include - using namespace AtlasMessage; extern void Render_(); -#define __declspec(spec_) -#define __stdcall - -extern "C" { __declspec(dllimport) int __stdcall SwapBuffers(void*); } - // HACK (and not exactly portable) - // - // (Er, actually that's what most of this file is. Oh well.) - // Loaded from DLL: void (*Atlas_StartWindow)(wchar_t* type); void (*Atlas_SetMessagePasser)(MessagePasser*, MessagePasser*); +void (*Atlas_GLSetCurrent)(void* context); +void (*Atlas_GLSwapBuffers)(void* context); +void (*Atlas_NotifyEndOfFrame)(); + static MessagePasserImpl msgPasser_Command; static MessagePasserImpl msgPasser_Input; @@ -49,11 +43,14 @@ static void* LaunchWindow(void*) bool BeginAtlas(int argc, char* argv[], void* dll) { - *(void**)&Atlas_StartWindow = dlsym(dll, "Atlas_StartWindow"); - *(void**)&Atlas_SetMessagePasser = dlsym(dll, "Atlas_SetMessagePasser"); - - if (!Atlas_StartWindow || !Atlas_SetMessagePasser) - return false; + // Load required symbols from the DLL +#define GET(x) *(void**)&x = dlsym(dll, #x); if (! x) return false; + GET(Atlas_StartWindow); + GET(Atlas_SetMessagePasser); + GET(Atlas_GLSetCurrent); + GET(Atlas_GLSwapBuffers); + GET(Atlas_NotifyEndOfFrame); +#undef GET // Pass our message handler to Atlas Atlas_SetMessagePasser(&msgPasser_Command, &msgPasser_Input); @@ -66,7 +63,9 @@ bool BeginAtlas(int argc, char* argv[], void* dll) state.argv = argv; state.running = true; state.rendering = false; - state.currentDC = NULL; + state.glContext = NULL; + + double last_activity = get_time(); while (state.running) { @@ -82,18 +81,18 @@ bool BeginAtlas(int argc, char* argv[], void* dll) static double last_time = time; const float length = (float)(time-last_time); last_time = time; - assert(length >= 0.0f); + debug_assert(length >= 0.0f); // TODO: filter out big jumps, e.g. when having done a lot of slow // processing in the last frame state.frameLength = length; } + // Process the input that was received in the past if (g_Input.ProcessInput(&state)) recent_activity = true; ////////////////////////////////////////////////////////////////////////// - // if (!(in interactive-tool mode)) { mCommand* msg; while ((msg = msgPasser_Command.Retrieve()) != NULL) @@ -164,18 +163,36 @@ bool BeginAtlas(int argc, char* argv[], void* dll) { Render_(); glFinish(); -#if OS_WIN - SwapBuffers((void*)state.currentDC); -#endif + Atlas_GLSwapBuffers((void*)state.glContext); } - // Be nice to the processor if we're not doing anything useful, but - // nice to the user if we are - if (! recent_activity) - SDL_Delay(100); + Atlas_NotifyEndOfFrame(); + + double time = get_time(); + if (recent_activity) + last_activity = time; + + // Be nice to the processor (by sleeping) if we're not doing anything + // useful, but nice to the user (by just yielding to other threads) if we are + + if (time - last_activity > 0.5) // if there was no recent activity... + { + double sleepUntil = time + 0.5; // only redraw at 2fps + while (time < sleepUntil) + { + // To minimise latency when the user starts doing stuff, only + // sleep for a short while, then check if anything's happened, + // then go back to sleep + SDL_Delay(50); + if (!msgPasser_Input.IsEmpty() || !msgPasser_Command.IsEmpty()) + break; + time = get_time(); + } + } else + { SDL_Delay(0); - // Probable TODO: allow interruption of sleep by incoming messages + } } // TODO: delete all remaining messages, to avoid memory leak warnings diff --git a/source/tools/atlas/GameInterface/GameLoop.h b/source/tools/atlas/GameInterface/GameLoop.h index 43c34bbf17..9e681ea0e7 100644 --- a/source/tools/atlas/GameInterface/GameLoop.h +++ b/source/tools/atlas/GameInterface/GameLoop.h @@ -1,13 +1,15 @@ #ifndef GAMELOOP_H__ #define GAMELOOP_H__ +extern void (*Atlas_GLSetCurrent)(void* context); + struct GameLoopState { int argc; char** argv; bool running; bool rendering; - const void* currentDC; + const void* glContext; float frameLength; // smoothed to avoid large jumps struct diff --git a/source/tools/atlas/GameInterface/Handlers/Elevation.cpp b/source/tools/atlas/GameInterface/Handlers/Elevation.cpp index d9d05f3a39..2572af4da7 100644 --- a/source/tools/atlas/GameInterface/Handlers/Elevation.cpp +++ b/source/tools/atlas/GameInterface/Handlers/Elevation.cpp @@ -9,6 +9,7 @@ namespace AtlasMessage { + BEGIN_COMMAND(AlterElevation) // TODO: much more efficient version of this, and without the memory leaks @@ -25,7 +26,8 @@ BEGIN_COMMAND(AlterElevation) delete NewTerrain; } - void Do() { + void Do() + { CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); @@ -33,16 +35,29 @@ BEGIN_COMMAND(AlterElevation) OldTerrain = new u16[verts]; memcpy(OldTerrain, terrain->GetHeightMap(), verts*sizeof(u16)); + int amount = (int)d->amount; +// debug_printf("%d\n", amount); + + // If the thing's being updated very fast, 'amount' is often very + // small (even zero) so the integer truncation is significant + static float roundingError = 0.0; + roundingError += d->amount - (float)amount; + if (roundingError >= 1.f) + { + amount += (int)roundingError; + roundingError -= (float)(int)roundingError; + } + CVector3D vec; d->pos.GetWorldSpace(vec); uint32_t x, z; terrain->CalcFromPosition(vec, x, z); - terrain->RaiseVertex(x, z, (int)d->amount); + terrain->RaiseVertex(x, z, amount); terrain->MakeDirty(x, z, x, z); - } - void Undo() { + void Undo() + { CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); if (! NewTerrain) { @@ -53,12 +68,14 @@ BEGIN_COMMAND(AlterElevation) terrain->SetHeightMap(OldTerrain); // CTerrain duplicates the data } - void Redo() { + void Redo() + { CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); terrain->SetHeightMap(NewTerrain); // CTerrain duplicates the data } - void MergeWithSelf(cAlterElevation* prev) { + void MergeWithSelf(cAlterElevation* prev) + { std::swap(prev->NewTerrain, NewTerrain); } diff --git a/source/tools/atlas/GameInterface/Handlers/GraphicsSetup.cpp b/source/tools/atlas/GameInterface/Handlers/GraphicsSetup.cpp index 835970aec7..8025b4a11e 100644 --- a/source/tools/atlas/GameInterface/Handlers/GraphicsSetup.cpp +++ b/source/tools/atlas/GameInterface/Handlers/GraphicsSetup.cpp @@ -82,10 +82,8 @@ REGISTER(CommandString_render_disable); void fSetContext(IMessage* msg) { mSetContext* cmd = static_cast(msg); -#if OS_WIN - wglMakeCurrent((HDC)cmd->hdc, (HGLRC)cmd->hglrc); - g_GameLoop->currentDC = cmd->hdc; -#endif + g_GameLoop->glContext = cmd->context; + Atlas_GLSetCurrent((void*)g_GameLoop->glContext); } REGISTER(SetContext); diff --git a/source/tools/atlas/GameInterface/Handlers/MessageHandler.h b/source/tools/atlas/GameInterface/Handlers/MessageHandler.h index 95a9626bb4..e3fcaddcb8 100644 --- a/source/tools/atlas/GameInterface/Handlers/MessageHandler.h +++ b/source/tools/atlas/GameInterface/Handlers/MessageHandler.h @@ -15,6 +15,7 @@ extern msgHandlers& GetMsgHandlers(); #define CAT1(a,b) a##b #define CAT2(a,b) CAT1(a,b) +// TODO quite urgently: Fix this, because it's broken and not very helpful anyway #define REGISTER(t) namespace CAT2(hndlr_, __LINE__) { struct init { init() { \ bool notAlreadyRegisted = GetMsgHandlers().insert(std::pair(#t, &f##t)).second; \ assert(notAlreadyRegisted); \ diff --git a/source/tools/atlas/GameInterface/InputProcessor.cpp b/source/tools/atlas/GameInterface/InputProcessor.cpp index ad57533242..8e0d1732d4 100644 --- a/source/tools/atlas/GameInterface/InputProcessor.cpp +++ b/source/tools/atlas/GameInterface/InputProcessor.cpp @@ -5,8 +5,6 @@ #include "ps/Game.h" #include "graphics/Camera.h" -#include - bool InputProcessor::ProcessInput(GameLoopState* state) { if (! g_Game) @@ -24,13 +22,6 @@ bool InputProcessor::ProcessInput(GameLoopState* state) else forwards.Normalize(); - float l; - l = forwards.GetLength(); - assert(fabsf(l - 1.f) < 0.0001f); - l = leftwards.GetLength(); - assert(fabsf(l - 1.f) < 0.0001f); - - bool moved = false; if (state->input.scrollSpeed[0] != 0.0f) diff --git a/source/tools/atlas/GameInterface/MessagePasser.h b/source/tools/atlas/GameInterface/MessagePasser.h index 0b3e436fc3..71dfc7324a 100644 --- a/source/tools/atlas/GameInterface/MessagePasser.h +++ b/source/tools/atlas/GameInterface/MessagePasser.h @@ -9,9 +9,6 @@ template class MessagePasser public: virtual void Add(T*)=0; virtual T* Retrieve()=0; - - virtual void Query(T&)=0; - virtual void QueryDone()=0; }; struct mCommand; @@ -24,13 +21,4 @@ extern MessagePasser* g_MessagePasser_Input; } - -/* - -atlas->game command ("initialise now", "render now") -atlas->game->atlas query ("what is at position (x,y)?") -game->atlas notification ("game ended") ?? - -*/ - #endif // MESSAGEPASSER_H__ diff --git a/source/tools/atlas/GameInterface/MessagePasserImpl.cpp b/source/tools/atlas/GameInterface/MessagePasserImpl.cpp index e9e9453c6f..0f51327857 100644 --- a/source/tools/atlas/GameInterface/MessagePasserImpl.cpp +++ b/source/tools/atlas/GameInterface/MessagePasserImpl.cpp @@ -49,12 +49,12 @@ template T* MessagePasserImpl::Retrieve() return msg; } -template void MessagePasserImpl::Query(T&) -{ -} - -template void MessagePasserImpl::QueryDone() +template bool MessagePasserImpl::IsEmpty() { + m_Mutex.Lock(); + bool empty = m_Queue.empty(); + m_Mutex.Unlock(); + return empty; } MessagePasser* g_MessagePasser_Command = NULL; diff --git a/source/tools/atlas/GameInterface/MessagePasserImpl.h b/source/tools/atlas/GameInterface/MessagePasserImpl.h index 407ac7f8ec..70496838a2 100644 --- a/source/tools/atlas/GameInterface/MessagePasserImpl.h +++ b/source/tools/atlas/GameInterface/MessagePasserImpl.h @@ -8,9 +8,7 @@ template class MessagePasserImpl : public AtlasMessage::MessagePass public: virtual void Add(T* msg); virtual T* Retrieve(); - - virtual void Query(T&); - virtual void QueryDone(); + virtual bool IsEmpty(); private: CMutex m_Mutex; diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h index 2fd4ef2d33..4dd55c9dad 100644 --- a/source/tools/atlas/GameInterface/Messages.h +++ b/source/tools/atlas/GameInterface/Messages.h @@ -90,9 +90,8 @@ COMMAND(CommandString) ////////////////////////////////////////////////////////////////////////// COMMAND(SetContext) - mSetContext(void* /* HDC */ hdc_, void* /* HGLRC */ hglrc_) : hdc(hdc_), hglrc(hglrc_) {}; - const void* hdc; - const void* hglrc; + mSetContext(void* /* wxGLContext */ context_) : context(context_) {}; + const void* context; }; COMMAND(ResizeScreen)