mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-09-21 20:06:40 +00:00
Atlas: More portable GL context usage. Smoother and more responsive terrain editing.
This was SVN commit r2705.
This commit is contained in:
@@ -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() {};
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user