Atlas: More portable GL context usage. Smoother and more responsive terrain editing.

This was SVN commit r2705.
This commit is contained in:
Ykkrosh
2005-09-13 03:57:34 +00:00
parent 35591f4d24
commit 0b72d0f86c
18 changed files with 208 additions and 90 deletions
+10
View File
@@ -281,6 +281,16 @@
RelativePath=".\CustomControls\FileHistory\FileHistory.h">
</File>
</Filter>
<Filter
Name="HighResTimer"
Filter="">
<File
RelativePath=".\CustomControls\HighResTimer\HighResTimer.cpp">
</File>
<File
RelativePath=".\CustomControls\HighResTimer\HighResTimer.h">
</File>
</Filter>
</Filter>
<Filter
Name="Misc"
@@ -0,0 +1,31 @@
#include "stdafx.h"
#include "HighResTimer.h"
// TODO: Portability and general betterness. (But it's good enough for now.)
HighResTimer::HighResTimer()
{
LARGE_INTEGER freq;
BOOL ok = QueryPerformanceFrequency(&freq);
if (! ok)
{
wxLogError(_("QPF failed!"));
}
else
{
m_TickLength = freq.QuadPart;
}
}
double HighResTimer::GetTime()
{
LARGE_INTEGER count;
BOOL ok = QueryPerformanceCounter(&count);
if (! ok)
{
wxLogError(_("QPC failed!"));
return 0.0;
}
return (double)count.QuadPart / (double)m_TickLength.GetValue();
}
@@ -0,0 +1,9 @@
class HighResTimer
{
public:
HighResTimer();
double GetTime(); // in seconds, relative to some arbitrary time
private:
wxLongLong m_TickLength;
};
@@ -4,3 +4,8 @@ namespace AtlasMessage { class MessageHandler; }
ATLASDLLIMPEXP void Atlas_SetMessageHandler(AtlasMessage::MessageHandler*);
ATLASDLLIMPEXP void Atlas_StartWindow(wchar_t* type);
ATLASDLLIMPEXP void Atlas_GLSetCurrent(void* context);
ATLASDLLIMPEXP void Atlas_GLSwapBuffers(void* context);
ATLASDLLIMPEXP void Atlas_NotifyEndOfFrame();
@@ -4,6 +4,7 @@
#include "wx/glcanvas.h"
#include "CustomControls/SnapSplitterWindow/SnapSplitterWindow.h"
#include "CustomControls/HighResTimer/HighResTimer.h"
#include "GameInterface/MessagePasser.h"
#include "GameInterface/Messages.h"
@@ -14,6 +15,8 @@
//#define UI_ONLY
static HighResTimer g_Timer;
//////////////////////////////////////////////////////////////////////////
// TODO: move into another file
@@ -100,17 +103,40 @@ private:
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(Canvas, wxGLCanvas)
EVT_SIZE(Canvas::OnResize)
EVT_KEY_DOWN(Canvas::OnKeyDown)
EVT_KEY_UP(Canvas::OnKeyUp)
EVT_SIZE (Canvas::OnResize)
EVT_KEY_DOWN (Canvas::OnKeyDown)
EVT_KEY_UP (Canvas::OnKeyUp)
EVT_LEFT_DOWN(Canvas::OnMouse)
EVT_LEFT_UP (Canvas::OnMouse)
EVT_MOTION (Canvas::OnMouse)
EVT_LEFT_DOWN (Canvas::OnMouse)
EVT_LEFT_UP (Canvas::OnMouse)
EVT_RIGHT_DOWN(Canvas::OnMouse)
EVT_RIGHT_UP (Canvas::OnMouse)
EVT_MOTION (Canvas::OnMouse)
END_EVENT_TABLE()
// GL functions exported from DLL, and called by game (in a separate
// thread to the standard wx one)
ATLASDLLIMPEXP void Atlas_GLSetCurrent(void* context)
{
((wxGLContext*)context)->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&)
@@ -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);
@@ -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;
};
@@ -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() {};
};
+43 -26
View File
@@ -13,23 +13,17 @@
#include "lib/timer.h"
#include "ps/CLogger.h"
#include <assert.h>
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<mCommand>*, MessagePasser<mInput>*);
void (*Atlas_GLSetCurrent)(void* context);
void (*Atlas_GLSwapBuffers)(void* context);
void (*Atlas_NotifyEndOfFrame)();
static MessagePasserImpl<mCommand> msgPasser_Command;
static MessagePasserImpl<mInput> 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
+3 -1
View File
@@ -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
@@ -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);
}
@@ -82,10 +82,8 @@ REGISTER(CommandString_render_disable);
void fSetContext(IMessage* msg)
{
mSetContext* cmd = static_cast<mSetContext*>(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);
@@ -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<std::string, msgHandler>(#t, &f##t)).second; \
assert(notAlreadyRegisted); \
@@ -5,8 +5,6 @@
#include "ps/Game.h"
#include "graphics/Camera.h"
#include <assert.h>
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)
@@ -9,9 +9,6 @@ template <typename T> 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<mInput>* 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__
@@ -49,12 +49,12 @@ template <typename T> T* MessagePasserImpl<T>::Retrieve()
return msg;
}
template <typename T> void MessagePasserImpl<T>::Query(T&)
{
}
template <typename T> void MessagePasserImpl<T>::QueryDone()
template <typename T> bool MessagePasserImpl<T>::IsEmpty()
{
m_Mutex.Lock();
bool empty = m_Queue.empty();
m_Mutex.Unlock();
return empty;
}
MessagePasser<mCommand>* g_MessagePasser_Command = NULL;
@@ -8,9 +8,7 @@ template <typename T> 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;
+2 -3
View File
@@ -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)