diff --git a/source/graphics/Terrain.cpp b/source/graphics/Terrain.cpp index e30433016e..c82ecfc35b 100755 --- a/source/graphics/Terrain.cpp +++ b/source/graphics/Terrain.cpp @@ -364,3 +364,29 @@ float CTerrain::FlattenArea(float x0,float x1,float z0,float z1) return y*HEIGHT_SCALE; } +/////////////////////////////////////////////////////////////////////////////// + +void CTerrain::RaiseVertex(int x, int z, int amount) +{ + // Ignore out-of-bounds vertices + if (x < 0 || z < 0 || x >= (int)m_MapSize || z >= (int)m_MapSize) + return; + + m_Heightmap[x + z*m_MapSize] = clamp(m_Heightmap[x + z*m_MapSize] + amount, 0, 65535); +} + +void CTerrain::MakeDirty(int x0, int z0, int x1, int z1) +{ + // flag vertex data as dirty for affected patches, and rebuild bounds of these patches + int px0 = clamp((x0/PATCH_SIZE)-1, 0, (int)m_MapSizePatches); + int px1 = clamp((x1/PATCH_SIZE)+1, 0, (int)m_MapSizePatches); + int pz0 = clamp((z0/PATCH_SIZE)-1, 0, (int)m_MapSizePatches); + int pz1 = clamp((z1/PATCH_SIZE)+1, 0, (int)m_MapSizePatches); + for (int j = pz0; j < pz1; j++) { + for (int i = px0; i < px1; i++) { + CPatch* patch = GetPatch(i,j); + patch->CalcBounds(); + patch->SetDirty(RENDERDATA_UPDATE_VERTICES); + } + } +} diff --git a/source/graphics/Terrain.h b/source/graphics/Terrain.h index 61b761a574..e4f751a7d0 100755 --- a/source/graphics/Terrain.h +++ b/source/graphics/Terrain.h @@ -65,6 +65,12 @@ public: // the average height of the flattened area float FlattenArea(float x0,float x1,float z0,float z1); + // raise a given vertex, clamped to min/max height; ignored if out of bounds + void RaiseVertex(int x, int y, int amount); + + // mark a specific square of tiles as dirty - use this after modifying the heightmap + void MakeDirty(int x0, int z0, int x1, int z1); + private: // delete any data allocated by this terrain void ReleaseData(); diff --git a/source/main.cpp b/source/main.cpp index 68dd0a79f3..248f99733d 100755 --- a/source/main.cpp +++ b/source/main.cpp @@ -85,7 +85,6 @@ #define LOG_CATEGORY "main" -CConsole* g_Console = 0; extern int conInputHandler(const SDL_Event* ev); // Globals @@ -883,8 +882,7 @@ static void InitPs() { TIMER(ps_console); - float ConsoleHeight = g_yres * 0.6f; - g_Console->SetSize(0, g_yres-ConsoleHeight, (float)g_xres, ConsoleHeight); + g_Console->UpdateScreenSize(g_xres, g_yres); // Calculate and store the line spacing CFont font("console"); diff --git a/source/maths/MathUtil.h b/source/maths/MathUtil.h index fe6dac1cf8..8d8f52d831 100755 --- a/source/maths/MathUtil.h +++ b/source/maths/MathUtil.h @@ -18,11 +18,11 @@ T Interpolate( T& a, T& b, float l ) } template -T clamp(T value,T min,T max) +inline T clamp(T value, T min, T max) { - if (valuemax) return max; - else return value; + if (value<=min) return min; + else if (value>=max) return max; + else return value; } #if 0 diff --git a/source/ps/CConsole.cpp b/source/ps/CConsole.cpp index a880e85700..b47bbcfe7a 100755 --- a/source/ps/CConsole.cpp +++ b/source/ps/CConsole.cpp @@ -19,6 +19,8 @@ extern bool keys[SDLK_LAST]; +CConsole* g_Console = 0; + CConsole::CConsole() { @@ -54,6 +56,12 @@ void CConsole::SetSize(float X, float Y, float W, float H) m_fHeight = H; } +void CConsole::UpdateScreenSize(int w, int h) +{ + float height = h * 0.6f; + SetSize(0, h-height, (float)w, height); +} + void CConsole::ToggleVisible() { diff --git a/source/ps/CConsole.h b/source/ps/CConsole.h index 486a30be34..5a1ae2ee2b 100755 --- a/source/ps/CConsole.h +++ b/source/ps/CConsole.h @@ -84,6 +84,7 @@ public: ~CConsole(); void SetSize(float X = 300, float Y = 0, float W = 800, float H = 600); + void UpdateScreenSize(int w, int h); void ToggleVisible(); void SetVisible( bool visible ); @@ -114,5 +115,6 @@ public: int m_iFontOffset; // distance to move up before drawing }; -#endif +extern CConsole* g_Console; +#endif diff --git a/source/ps/Game.cpp b/source/ps/Game.cpp index dc8fca6129..34f20d45c4 100755 --- a/source/ps/Game.cpp +++ b/source/ps/Game.cpp @@ -1,6 +1,7 @@ #include "precompiled.h" #include "Game.h" +#include "GameAttributes.h" #include "CLogger.h" #ifndef NO_GUI #include "gui/CGUI.h" diff --git a/source/ps/Game.h b/source/ps/Game.h index e1c1fc6ca7..d8f09b4a01 100755 --- a/source/ps/Game.h +++ b/source/ps/Game.h @@ -8,12 +8,13 @@ ERROR_GROUP(Game); #include "World.h" #include "Simulation.h" -#include "Player.h" #include "GameView.h" -#include "GameAttributes.h" #include +class CPlayer; +class CGameAttributes; + // Default player limit (not counting the Gaia player) // This may be overriden by system.cfg ("max_players") #define PS_MAX_PLAYERS 6 diff --git a/source/ps/Network/Client.cpp b/source/ps/Network/Client.cpp index f173a82d7c..390278b77b 100755 --- a/source/ps/Network/Client.cpp +++ b/source/ps/Network/Client.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #define LOG_CAT_NET "net" diff --git a/source/ps/Network/Server.h b/source/ps/Network/Server.h index 7bfe41fa9f..64fce00202 100755 --- a/source/ps/Network/Server.h +++ b/source/ps/Network/Server.h @@ -3,6 +3,7 @@ #include "Network/Session.h" #include "Game.h" +#include "GameAttributes.h" #include "TurnManager.h" #include "scripting/JSMap.h" diff --git a/source/ps/World.cpp b/source/ps/World.cpp index 98d9645eae..865cde8707 100755 --- a/source/ps/World.cpp +++ b/source/ps/World.cpp @@ -7,6 +7,7 @@ #include "World.h" #include "MapReader.h" #include "Game.h" +#include "GameAttributes.h" #include "Terrain.h" #include "LightEnv.h" #include "BaseEntityCollection.h" diff --git a/source/tools/atlas/AtlasUI/AtlasUI.vcproj b/source/tools/atlas/AtlasUI/AtlasUI.vcproj index 1c8b2809e1..997c82d832 100644 --- a/source/tools/atlas/AtlasUI/AtlasUI.vcproj +++ b/source/tools/atlas/AtlasUI/AtlasUI.vcproj @@ -386,6 +386,23 @@ + + + + + + + + + + GetParent(); } - wxASSERT_MSG(0, _T("Couldn't find command processor")); + wxFAIL_MSG(_T("Couldn't find command processor")); return NULL; } diff --git a/source/tools/atlas/AtlasUI/Misc/DLLInterface.cpp b/source/tools/atlas/AtlasUI/Misc/DLLInterface.cpp index 2ab2ec399d..f76194db11 100644 --- a/source/tools/atlas/AtlasUI/Misc/DLLInterface.cpp +++ b/source/tools/atlas/AtlasUI/Misc/DLLInterface.cpp @@ -37,11 +37,15 @@ BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID WXUNUSED(lpRese return TRUE; } -AtlasMessage::MessagePasser* AtlasMessage::g_MessagePasser = NULL; +using namespace AtlasMessage; -ATLASDLLIMPEXP void Atlas_SetMessagePasser(AtlasMessage::MessagePasser* handler) +MessagePasser* AtlasMessage::g_MessagePasser_Command = NULL; +MessagePasser* AtlasMessage::g_MessagePasser_Input = NULL; + +ATLASDLLIMPEXP void Atlas_SetMessagePasser(MessagePasser* handler_cmd, MessagePasser* handler_in) { - AtlasMessage::g_MessagePasser = handler; + g_MessagePasser_Command = handler_cmd; + g_MessagePasser_Input = handler_in; } ATLASDLLIMPEXP void Atlas_StartWindow(wchar_t* type) diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp index 4ab19d45a1..d273a8a2c1 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp @@ -10,15 +10,18 @@ #include "Sections/Map/Map.h" +#include "tools/Common/Tools.h" + //#define UI_ONLY ////////////////////////////////////////////////////////////////////////// +// TODO: move into another file class Canvas : public wxGLCanvas { public: Canvas(wxWindow* parent, int* attribList) - : wxGLCanvas(parent, -1, wxDefaultPosition, wxDefaultSize, 0, _T("GLCanvas"), attribList), + : wxGLCanvas(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS, _T("GLCanvas"), attribList), m_SuppressResize(true) { } @@ -28,7 +31,8 @@ public: // Be careful not to send 'resize' messages to the game before we've // told it that this canvas exists if (! m_SuppressResize) - AtlasMessage::g_MessagePasser->Add(new AtlasMessage::mResizeScreen(GetSize().GetWidth(), GetSize().GetHeight())); + ADD_COMMAND(ResizeScreen(GetClientSize().GetWidth(), GetClientSize().GetHeight())); + // TODO: fix flashing } void InitSize() @@ -37,18 +41,79 @@ public: SetSize(320, 240); } + bool KeyScroll(wxKeyEvent& evt, bool enable) + { + int dir; + switch (evt.GetKeyCode()) + { + case WXK_LEFT: dir = AtlasMessage::mScrollConstant::LEFT; break; + case WXK_RIGHT: dir = AtlasMessage::mScrollConstant::RIGHT; break; + case WXK_UP: dir = AtlasMessage::mScrollConstant::FORWARDS; break; + case WXK_DOWN: dir = AtlasMessage::mScrollConstant::BACKWARDS; break; + case WXK_SHIFT: dir = -1; break; + default: return false; + } + + float speed = wxGetKeyState(WXK_SHIFT) ? 240.0f : 120.0f; + + if (dir == -1) // changed modifier keys - update all currently-scrolling directions + { + if (wxGetKeyState(WXK_LEFT)) ADD_INPUT(ScrollConstant(AtlasMessage::mScrollConstant::LEFT, speed)); + if (wxGetKeyState(WXK_RIGHT)) ADD_INPUT(ScrollConstant(AtlasMessage::mScrollConstant::RIGHT, speed)); + if (wxGetKeyState(WXK_UP)) ADD_INPUT(ScrollConstant(AtlasMessage::mScrollConstant::FORWARDS, speed)); + if (wxGetKeyState(WXK_DOWN)) ADD_INPUT(ScrollConstant(AtlasMessage::mScrollConstant::BACKWARDS, speed)); + } + else + { + ADD_INPUT(ScrollConstant(dir, enable ? speed : 0.0f)); + } + return true; + } + + void OnKeyDown(wxKeyEvent& evt) + { + if (KeyScroll(evt, true)) + return; + + g_CurrentTool->OnKey(evt, ITool::KEY_DOWN); + + evt.Skip(); + } + + void OnKeyUp(wxKeyEvent& evt) + { + if (KeyScroll(evt, false)) + return; + + g_CurrentTool->OnKey(evt, ITool::KEY_UP); + + evt.Skip(); + } + + void OnMouse(wxMouseEvent& evt) + { + g_CurrentTool->OnMouse(evt); + } + private: bool m_SuppressResize; DECLARE_EVENT_TABLE(); }; BEGIN_EVENT_TABLE(Canvas, wxGLCanvas) 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) END_EVENT_TABLE() ////////////////////////////////////////////////////////////////////////// BEGIN_EVENT_TABLE(ScenarioEditor, wxFrame) EVT_CLOSE(ScenarioEditor::OnClose) + EVT_TIMER(wxID_ANY, ScenarioEditor::OnTimer) END_EVENT_TABLE() @@ -82,23 +147,52 @@ ScenarioEditor::ScenarioEditor() // Send setup messages to game engine: #ifndef UI_ONLY - ADD_MESSAGE(SetContext(canvas->GetHDC(), canvas->GetContext()->GetGLRC())); + ADD_COMMAND(SetContext(canvas->GetHDC(), canvas->GetContext()->GetGLRC())); - ADD_MESSAGE(CommandString("init")); + ADD_COMMAND(CommandString("init")); canvas->InitSize(); - ADD_MESSAGE(CommandString("render_enable")); + ADD_COMMAND(CommandString("render_enable")); #endif + + // XXX + USE_TOOL(AlterElevation); + + m_Timer.SetOwner(this); + m_Timer.Start(50); } + void ScenarioEditor::OnClose(wxCloseEvent&) { #ifndef UI_ONLY - ADD_MESSAGE(CommandString("shutdown")); + ADD_COMMAND(CommandString("shutdown")); #endif - ADD_MESSAGE(CommandString("exit")); - + ADD_COMMAND(CommandString("exit")); + + SetCurrentTool(NULL); + // TODO: What if it's still rendering while we're destroying the canvas? Destroy(); } + + +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; +} + +////////////////////////////////////////////////////////////////////////// + +AtlasMessage::Position::Position(const wxPoint& pt) +{ + // TODO: convert to world space (via the engine) + x = pt.x/32.f; + y = 0.f; + z = pt.y/32.f; +} diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h index b9976d335a..9b7224e245 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.h @@ -3,6 +3,10 @@ class ScenarioEditor : public wxFrame public: ScenarioEditor(); void OnClose(wxCloseEvent& evt); + void OnTimer(wxTimerEvent& evt); + +private: + wxTimer m_Timer; DECLARE_EVENT_TABLE(); }; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.cpp new file mode 100644 index 0000000000..79a361fd81 --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.cpp @@ -0,0 +1,10 @@ +#include "stdafx.h" + +#include "Sidebar.h" + +Sidebar::Sidebar(wxWindow* parent) + : wxPanel(parent) +{ + m_MainSizer = new wxBoxSizer(wxVERTICAL); + SetSizer(m_MainSizer); +} diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.h new file mode 100644 index 0000000000..47dc9abaa4 --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Common/Sidebar.h @@ -0,0 +1,8 @@ +class Sidebar : public wxPanel +{ +public: + Sidebar(wxWindow* parent); + +protected: + wxSizer* m_MainSizer; // vertical box sizer +}; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp new file mode 100644 index 0000000000..7f1205c38c --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" + +#include "Map.h" + +#include "ActionButton.h" + +#include "GameInterface/Messages.h" + +static void GenerateMap() +{ + ADD_COMMAND(GenerateMap(9)); +} + +MapSidebar::MapSidebar(wxWindow* parent) + : Sidebar(parent) +{ + m_MainSizer->Add(new ActionButton(this, _T("Generate Map"), &GenerateMap)); +} diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.h new file mode 100644 index 0000000000..643f46124f --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.h @@ -0,0 +1,7 @@ +#include "../Common/Sidebar.h" + +class MapSidebar : public Sidebar +{ +public: + MapSidebar(wxWindow* parent); +}; \ No newline at end of file diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/AlterElevation.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/AlterElevation.cpp new file mode 100644 index 0000000000..8e40bb77f9 --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/AlterElevation.cpp @@ -0,0 +1,57 @@ +#include "stdafx.h" + +#include "Common/Tools.h" + +#include "GameInterface/Messages.h" + +using AtlasMessage::Position; + +class AlterElevation : public ITool +{ +public: + AlterElevation() + : m_IsActive(false) + { + } + + void OnMouse(wxMouseEvent& evt) + { + if (evt.LeftDown()) + { + m_IsActive = true; + m_Pos = Position(evt.GetPosition()); + } + else if (evt.LeftUp()) + { + m_IsActive = false; + } + else if (evt.Dragging()) + { + m_Pos = Position(evt.GetPosition()); + } + else + { + evt.Skip(); + } + } + + void OnKey(wxKeyEvent& evt, int dir) + { + evt.Skip(); + } + + void OnTick(float dt) + { + if (m_IsActive) + { + ADD_COMMAND(AlterElevation(m_Pos, dt*4.096f)); + } + } + +private: + + bool m_IsActive; + Position m_Pos; +}; + +DECLARE_TOOL(AlterElevation); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp new file mode 100644 index 0000000000..4bfa817275 --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" + +#include "Tools.h" + +class DummyTool : public ITool +{ + void OnMouse(wxMouseEvent& evt) { evt.Skip(); } + void OnKey(wxKeyEvent& evt, int) { evt.Skip(); } + void OnTick(float) {} +} dummy; + +ITool* g_CurrentTool = &dummy; + +void SetCurrentTool(ITool* tool) +{ + if (g_CurrentTool != &dummy) + delete g_CurrentTool; + + if (tool == NULL) + g_CurrentTool = &dummy; + else + g_CurrentTool = tool; +} diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.h new file mode 100644 index 0000000000..05059d4270 --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.h @@ -0,0 +1,25 @@ +#ifndef TOOLS_H__ +#define TOOLS_H__ + +class wxMouseEvent; +class wxKeyEvent; + +class ITool +{ +public: + enum { KEY_DOWN, KEY_UP, KEY_CHAR }; + + virtual void OnMouse(wxMouseEvent& evt) = 0; + virtual void OnKey(wxKeyEvent& evt, int dir) = 0; + virtual void OnTick(float dt) = 0; + + virtual ~ITool() {}; +}; + +#define DECLARE_TOOL(name) ITool* CreateTool_##name() { return new name(); } +#define USE_TOOL(name) { extern ITool* CreateTool_##name(); SetCurrentTool(CreateTool_##name()); } + +extern ITool* g_CurrentTool; +extern void SetCurrentTool(ITool*); // for internal use only + +#endif // TOOLS_H__ diff --git a/source/tools/atlas/GameInterface/GameLoop.cpp b/source/tools/atlas/GameInterface/GameLoop.cpp index 3876287fd8..df7185ffd9 100644 --- a/source/tools/atlas/GameInterface/GameLoop.cpp +++ b/source/tools/atlas/GameInterface/GameLoop.cpp @@ -6,13 +6,12 @@ #include "Messages.h" #include "handlers/MessageHandler.h" +#include "InputProcessor.h" + #include "lib/sdl.h" #include "lib/ogl.h" +#include "lib/timer.h" #include "ps/CLogger.h" -//#include "gui/GUI.h" -//#include "renderer/Renderer.h" -//#include "ps/Game.h" -//#include "ps/Loader.h" #ifdef NDEBUG #pragma comment(lib, "AtlasUI") @@ -23,9 +22,25 @@ using namespace AtlasMessage; extern __declspec(dllimport) void Atlas_StartWindow(wchar_t* type); -extern __declspec(dllimport) void Atlas_SetMessagePasser(MessagePasser*); +extern __declspec(dllimport) void Atlas_SetMessagePasser(MessagePasser*, MessagePasser*); + +extern void Render_(); + +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.) + + + +static MessagePasserImpl msgPasser_Command; +static MessagePasserImpl msgPasser_Input; + +static InputProcessor g_Input; + +static GameLoopState state; +GameLoopState* g_GameLoop = &state; -static MessagePasserImpl msgPasser; static void* LaunchWindow(void*) { @@ -33,23 +48,11 @@ static void* LaunchWindow(void*) return NULL; } -extern void Render_(); - -//extern int g_xres, g_yres; - -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.) - -static GameLoopState state; -GameLoopState* g_GameLoop = &state; - void BeginAtlas(int argc, char** argv) { // Pass our message handler to Atlas - Atlas_SetMessagePasser(&msgPasser); + Atlas_SetMessagePasser(&msgPasser_Command, &msgPasser_Input); // Create a new thread, and launch the Atlas window inside that thread pthread_t gameThread; @@ -63,49 +66,113 @@ void BeginAtlas(int argc, char** argv) while (state.running) { - IMessage* msg; - while (msg = msgPasser.Retrieve()) + bool recent_activity = false; + + ////////////////////////////////////////////////////////////////////////// + // (TODO: Work out why these things have to be in this order (to avoid + // jumps when starting to move, etc) + + // Calculate frame length { - std::string name (msg->GetType()); - - if (name == "CommandString") - { - // Allow some laziness: For commands that don't need any data other - // than their name, we just use CommandString (and then need to - // construct a reference to the appropriate handler for the - // given string) - name += "_"; - name += static_cast(msg)->name; - // use 'static_cast' when casting messages, to make it clear - // that it's slightly dangerous - we have to just assume that - // GetType is correct, since we can't use proper RTTI - } - handlers::const_iterator it = GetHandlers().find(name); - if (it != GetHandlers().end()) - { - it->second(msg); - } - else - { - debug_warn("Unrecognised message"); - // TODO: CLogger might not be initialised - LOG(ERROR, "atlas", "Unrecognised message (%s)", name.c_str()); - } - - delete msg; + const double time = get_time(); + static double last_time = time; + const float length = (float)(time-last_time); + last_time = time; + 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; } + if (g_Input.ProcessInput(&state)) + recent_activity = true; + + ////////////////////////////////////////////////////////////////////////// + + // if (!(in interactive-tool mode)) + { + mCommand* msg; + while (msg = msgPasser_Command.Retrieve()) + { + recent_activity = true; + + std::string name (msg->GetType()); + + if (name == "CommandString") + { + // Allow some laziness: For commands that don't need any data other + // than their name, we just use CommandString (and then need to + // construct a reference to the appropriate handler for the + // given string) + name += "_"; + name += static_cast(msg)->name; + // use 'static_cast' when casting messages, to make it clear + // that it's slightly dangerous - we have to just assume that + // GetType is correct, since we can't use proper RTTI + } + handlers::const_iterator it = GetHandlers().find(name); + if (it != GetHandlers().end()) + { + it->second(msg); + } + else + { + debug_warn("Unrecognised message"); + // TODO: CLogger might not be initialised + LOG(ERROR, "atlas", "Unrecognised message (%s)", name.c_str()); + } + + delete msg; + } + } + + // Exit, if desired if (! state.running) break; + // Now do the same (roughly), for input events: + { + mInput* msg; + while (msg = msgPasser_Input.Retrieve()) + { + recent_activity = true; + + std::string name (msg->GetType()); + handlers::const_iterator it = GetHandlers().find(name); + if (it != GetHandlers().end()) + { + it->second(msg); + } + else + { + debug_warn("Unrecognised message"); + // TODO: CLogger might not be initialised + LOG(ERROR, "atlas", "Unrecognised message (%s)", name.c_str()); + } + + delete msg; + } + } + + ////////////////////////////////////////////////////////////////////////// + if (state.rendering) { Render_(); - SwapBuffers(state.currentDC); + glFinish(); + SwapBuffers((void*)state.currentDC); } - SDL_Delay(50); + // 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); + else + SDL_Delay(0); + // Probable TODO: allow interruption of sleep by incoming messages } + // TODO: delete all remaining messages, to avoid memory leak warnings + pthread_join(gameThread, NULL); } diff --git a/source/tools/atlas/GameInterface/GameLoop.h b/source/tools/atlas/GameInterface/GameLoop.h index 9251b3b19a..43c34bbf17 100644 --- a/source/tools/atlas/GameInterface/GameLoop.h +++ b/source/tools/atlas/GameInterface/GameLoop.h @@ -1,10 +1,21 @@ +#ifndef GAMELOOP_H__ +#define GAMELOOP_H__ + struct GameLoopState { int argc; char** argv; bool running; bool rendering; - void* currentDC; + const void* currentDC; + float frameLength; // smoothed to avoid large jumps + + struct + { + float scrollSpeed[4]; // [fwd, bwd, left, right]. 0.0f for disabled. + } input; }; -extern GameLoopState* g_GameLoop; \ No newline at end of file +extern GameLoopState* g_GameLoop; + +#endif // GAMELOOP_H__ diff --git a/source/tools/atlas/GameInterface/Handlers/CameraCtrl.cpp b/source/tools/atlas/GameInterface/Handlers/CameraCtrl.cpp new file mode 100644 index 0000000000..30261505ee --- /dev/null +++ b/source/tools/atlas/GameInterface/Handlers/CameraCtrl.cpp @@ -0,0 +1,24 @@ +#include "precompiled.h" + +#include "MessageHandler.h" +#include "../GameLoop.h" + +namespace AtlasMessage { + + +void fScrollConstant(IMessage* msg) +{ + mScrollConstant* cmd = static_cast(msg); + + if (cmd->dir < 0 || cmd->dir > 3) + { + debug_warn("ScrollConstant: invalid direction"); + } + else + { + g_GameLoop->input.scrollSpeed[cmd->dir] = cmd->speed; + } +} +REGISTER(ScrollConstant); + +} \ No newline at end of file diff --git a/source/tools/atlas/GameInterface/Handlers/Elevation.cpp b/source/tools/atlas/GameInterface/Handlers/Elevation.cpp new file mode 100644 index 0000000000..44d3d6b585 --- /dev/null +++ b/source/tools/atlas/GameInterface/Handlers/Elevation.cpp @@ -0,0 +1,35 @@ +#include "precompiled.h" + +#include "MessageHandler.h" + +#include "graphics/Terrain.h" +#include "ps/Game.h" + +namespace AtlasMessage { + + +void fAlterElevation(IMessage* msg) +{ + mAlterElevation* cmd = static_cast(msg); + + // TODO: + // Create something like the AtlasCommandProcessor, so that undo is + // possible: Push an AlterElevationCommand onto the command stack, + // which has Do and Undo methods, and also has Merge (so multiple + // consecutive elevation-altering moments can be combined into a single + // brush stroke, to conserve memory and also so 'undo' undoes the entire + // stroke at once). + + CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); + + u16* heightmap = terrain->GetHeightMap(); + int size = terrain->GetVerticesPerSide(); + + int x = (int)cmd->pos.x; + int z = (int)cmd->pos.z; + terrain->RaiseVertex(x, z, (int)cmd->amount); + terrain->MakeDirty(x, z, x, z); +} +REGISTER(AlterElevation); + +} diff --git a/source/tools/atlas/GameInterface/Handlers/GraphicsSetup.cpp b/source/tools/atlas/GameInterface/Handlers/GraphicsSetup.cpp index 35d9668a0d..92577d855b 100644 --- a/source/tools/atlas/GameInterface/Handlers/GraphicsSetup.cpp +++ b/source/tools/atlas/GameInterface/Handlers/GraphicsSetup.cpp @@ -6,7 +6,9 @@ #include "renderer/Renderer.h" #include "gui/GUI.h" #include "ps/Game.h" +#include "ps/GameAttributes.h" #include "ps/Loader.h" +#include "ps/CConsole.h" extern int g_xres, g_yres; @@ -22,11 +24,19 @@ void fCommandString_init(IMessage*) oglInit(); Init_(g_GameLoop->argc, g_GameLoop->argv, false); + // HACK (to stop things looking very ugly when scrolling) - should + // use proper config system. + if(oglHaveExtension("WGL_EXT_swap_control")) + wglSwapIntervalEXT(1); + // Set attributes for the game: - g_GameAttributes.m_MapFile = L""; // start without a map + // Start without a map + g_GameAttributes.m_MapFile = L""; + // Make all players locally controlled for (int i=1; i<8; ++i) g_GameAttributes.GetSlot(i)->AssignLocal(); + // Start the game: g_Game = new CGame(); PSRETURN ret = g_Game->StartGame(&g_GameAttributes); assert(ret == PSRETURN_OK); @@ -70,7 +80,7 @@ void fSetContext(IMessage* msg) { mSetContext* cmd = static_cast(msg); // TODO: portability - wglMakeCurrent(cmd->hdc, cmd->hglrc); + wglMakeCurrent((HDC)cmd->hdc, (HGLRC)cmd->hglrc); g_GameLoop->currentDC = cmd->hdc; } REGISTER(SetContext); @@ -81,14 +91,16 @@ void fResizeScreen(IMessage* msg) mResizeScreen* cmd = static_cast(msg); g_xres = cmd->width; g_yres = cmd->height; - if (g_xres == 0) g_xres = 1; // avoid GL errors caused by invalid sizes - if (g_yres == 0) g_yres = 1; - SViewPort vp; - vp.m_X = vp.m_Y = 0; - vp.m_Width = g_xres; - vp.m_Height = g_yres; - g_Renderer.SetViewport(vp); + if (g_xres <= 2) g_xres = 2; // avoid GL errors caused by invalid sizes + if (g_yres <= 2) g_yres = 2; +// SViewPort vp; +// vp.m_X = vp.m_Y = 0; +// vp.m_Width = g_xres; +// vp.m_Height = g_yres; +// g_Renderer.SetViewport(vp); // TODO: what does this do? + g_Renderer.Resize(g_xres, g_yres); g_GUI.UpdateResolution(); + g_Console->UpdateScreenSize(g_xres, g_yres); } REGISTER(ResizeScreen); diff --git a/source/tools/atlas/GameInterface/Handlers/Map.cpp b/source/tools/atlas/GameInterface/Handlers/Map.cpp index 9308221e39..46b87562c3 100644 --- a/source/tools/atlas/GameInterface/Handlers/Map.cpp +++ b/source/tools/atlas/GameInterface/Handlers/Map.cpp @@ -3,6 +3,8 @@ #include "MessageHandler.h" #include "graphics/Patch.h" +#include "graphics/TextureManager.h" +#include "graphics/TextureEntry.h" #include "ps/Game.h" namespace AtlasMessage { @@ -12,17 +14,38 @@ void fGenerateMap(IMessage* msg) { mGenerateMap* cmd = static_cast(msg); - int tiles = cmd->size * PATCH_SIZE + 1; + // Convert size in patches to number of vertices + int vertices = cmd->size * PATCH_SIZE + 1; - u16* heightmap = new u16[tiles*tiles]; - for (int y = 0; y < tiles; ++y) - for (int x = 0; x < tiles; ++x) - heightmap[x + y*tiles] = 32768; + // Generate flat heightmap + u16* heightmap = new u16[vertices*vertices]; + for (int z = 0; z < vertices; ++z) + for (int x = 0; x < vertices; ++x) + heightmap[x + z*vertices] = 32768 +(int)(2048.f*(rand()/(float)RAND_MAX-0.5f)); - g_Game->GetWorld()->GetTerrain()->Resize(cmd->size); - g_Game->GetWorld()->GetTerrain()->SetHeightMap(heightmap); + // Initialise terrain using the heightmap + CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); + terrain->Initialize(cmd->size, heightmap); delete[] heightmap; + + // Cover terrain with default texture + // TODO: split into fCoverWithTexture + CTextureEntry* texentry = g_TexMan.FindTexture("grass1_spring.dds"); // TODO: make default customisable + Handle tex = texentry ? texentry->GetHandle() : 0; + + int patches = terrain->GetPatchesPerSide(); + for (int pz = 0; pz < patches; ++pz) { + for (int px = 0; px < patches; ++px) { + + CPatch* patch = terrain->GetPatch(px, pz); + + for (int z = 0; z < PATCH_SIZE; ++z) + for (int x = 0; x < PATCH_SIZE; ++x) + patch->m_MiniPatches[z][x].Tex1 = tex; + } + } + } REGISTER(GenerateMap); diff --git a/source/tools/atlas/GameInterface/Handlers/MessageHandler.cpp b/source/tools/atlas/GameInterface/Handlers/MessageHandler.cpp index bae2f32ee6..ddbc9ffdd3 100644 --- a/source/tools/atlas/GameInterface/Handlers/MessageHandler.cpp +++ b/source/tools/atlas/GameInterface/Handlers/MessageHandler.cpp @@ -8,9 +8,11 @@ namespace AtlasMessage handlers& GetHandlers() { // Make sure this is initialised when it's first required, rather than - // hoping to be lucky with static initialisation order + // hoping to be lucky with static initialisation order. + // (TODO: But is it safe to be sticking things into STL containers during + // static initialisation?) static handlers h; return h; } -} \ No newline at end of file +} diff --git a/source/tools/atlas/GameInterface/Handlers/MessageHandler.h b/source/tools/atlas/GameInterface/Handlers/MessageHandler.h index 22aa9fbbf0..8486cde56e 100644 --- a/source/tools/atlas/GameInterface/Handlers/MessageHandler.h +++ b/source/tools/atlas/GameInterface/Handlers/MessageHandler.h @@ -3,6 +3,9 @@ namespace AtlasMessage { +// (Random note: Be careful not to give handler .cpp files the same name +// as any other file in the project, because it makes everything very confused) + typedef void (*handler)(IMessage*); typedef std::map handlers; extern handlers& GetHandlers(); @@ -15,4 +18,4 @@ extern handlers& GetHandlers(); assert(notAlreadyRegisted); \ } } init; }; -} \ No newline at end of file +} diff --git a/source/tools/atlas/GameInterface/InputProcessor.cpp b/source/tools/atlas/GameInterface/InputProcessor.cpp new file mode 100644 index 0000000000..1d6f883d73 --- /dev/null +++ b/source/tools/atlas/GameInterface/InputProcessor.cpp @@ -0,0 +1,59 @@ +#include "precompiled.h" + +#include "InputProcessor.h" + +#include "ps/Game.h" +#include "graphics/Camera.h" + +bool InputProcessor::ProcessInput(GameLoopState* state) +{ + if (! g_Game) + return false; + + CCamera* camera = g_Game->GetView()->GetCamera(); + + CVector3D leftwards = camera->m_Orientation.GetLeft(); + + // Calculate a vector pointing forwards, parallel to the ground + CVector3D forwards = camera->m_Orientation.GetIn(); + forwards.Y = 0.0f; + if (forwards.GetLength() < 0.001f) // be careful if the camera is looking straight down + forwards = CVector3D(1.f, 0.f, 0.f); + else + forwards.Normalize(); + + float l; + l = forwards.GetLength(); + assert(abs(l - 1.f) < 0.0001f); + l = leftwards.GetLength(); + assert(abs(l - 1.f) < 0.0001f); + + + bool moved = false; + + if (state->input.scrollSpeed[0] != 0.0f) + { + camera->m_Orientation.Translate(forwards * (state->input.scrollSpeed[0] * state->frameLength)); + moved = true; + } + + if (state->input.scrollSpeed[1] != 0.0f) + { + camera->m_Orientation.Translate(forwards * (-state->input.scrollSpeed[1] * state->frameLength)); + moved = true; + } + + if (state->input.scrollSpeed[2] != 0.0f) + { + camera->m_Orientation.Translate(leftwards * (state->input.scrollSpeed[2] * state->frameLength)); + moved = true; + } + + if (state->input.scrollSpeed[3] != 0.0f) + { + camera->m_Orientation.Translate(leftwards * (-state->input.scrollSpeed[3] * state->frameLength)); + moved = true; + } + + return moved; +} diff --git a/source/tools/atlas/GameInterface/InputProcessor.h b/source/tools/atlas/GameInterface/InputProcessor.h new file mode 100644 index 0000000000..c97a3d5f39 --- /dev/null +++ b/source/tools/atlas/GameInterface/InputProcessor.h @@ -0,0 +1,8 @@ +#include "GameLoop.h" + +class InputProcessor +{ +public: + // Returns true if the camera has moved + bool ProcessInput(GameLoopState* state); +}; diff --git a/source/tools/atlas/GameInterface/MessagePasser.h b/source/tools/atlas/GameInterface/MessagePasser.h index 6e24501f65..0b3e436fc3 100644 --- a/source/tools/atlas/GameInterface/MessagePasser.h +++ b/source/tools/atlas/GameInterface/MessagePasser.h @@ -3,21 +3,24 @@ namespace AtlasMessage { -struct IMessage; -class MessagePasser +template class MessagePasser { public: - virtual void Add(IMessage*)=0; - virtual IMessage* Retrieve()=0; + virtual void Add(T*)=0; + virtual T* Retrieve()=0; - virtual void Query(IMessage&)=0; + virtual void Query(T&)=0; virtual void QueryDone()=0; }; -extern MessagePasser* g_MessagePasser; +struct mCommand; +struct mInput; +extern MessagePasser* g_MessagePasser_Command; +extern MessagePasser* g_MessagePasser_Input; -#define ADD_MESSAGE(type) AtlasMessage::g_MessagePasser->Add(new AtlasMessage::m##type) +#define ADD_COMMAND(type) AtlasMessage::g_MessagePasser_Command->Add(new AtlasMessage::m##type) +#define ADD_INPUT(type) AtlasMessage::g_MessagePasser_Input -> Add(new AtlasMessage::m##type) } diff --git a/source/tools/atlas/GameInterface/MessagePasserImpl.cpp b/source/tools/atlas/GameInterface/MessagePasserImpl.cpp index c8e78ca636..6d23dd8337 100644 --- a/source/tools/atlas/GameInterface/MessagePasserImpl.cpp +++ b/source/tools/atlas/GameInterface/MessagePasserImpl.cpp @@ -4,7 +4,7 @@ using namespace AtlasMessage; -void MessagePasserImpl::Add(IMessage* msg) +template void MessagePasserImpl::Add(T* msg) { m_Mutex.Lock(); @@ -13,11 +13,15 @@ void MessagePasserImpl::Add(IMessage* msg) m_Mutex.Unlock(); } -IMessage* MessagePasserImpl::Retrieve() +template T* MessagePasserImpl::Retrieve() { + // (It should be fairly easy to use a more efficient thread-safe queue, + // since there's only one thread adding items and one thread consuming; + // but it's not worthwhile yet.) + m_Mutex.Lock(); - IMessage* msg = NULL; + T* msg = NULL; if (! m_Queue.empty()) { msg = m_Queue.front(); @@ -29,12 +33,17 @@ IMessage* MessagePasserImpl::Retrieve() return msg; } -void MessagePasserImpl::Query(IMessage&) +template void MessagePasserImpl::Query(T&) { } -void MessagePasserImpl::QueryDone() +template void MessagePasserImpl::QueryDone() { } -MessagePasser* g_MessagePasser = NULL; +MessagePasser* g_MessagePasser_Command = NULL; +MessagePasser* g_MessagePasser_Input = NULL; + +// Explicit instantiation: +template MessagePasserImpl; +template MessagePasserImpl; \ No newline at end of file diff --git a/source/tools/atlas/GameInterface/MessagePasserImpl.h b/source/tools/atlas/GameInterface/MessagePasserImpl.h index 8eaa09a8ff..407ac7f8ec 100644 --- a/source/tools/atlas/GameInterface/MessagePasserImpl.h +++ b/source/tools/atlas/GameInterface/MessagePasserImpl.h @@ -3,17 +3,16 @@ #include "ps/ThreadUtil.h" #include -class MessagePasserImpl : public AtlasMessage::MessagePasser +template class MessagePasserImpl : public AtlasMessage::MessagePasser { public: - virtual void Add(AtlasMessage::IMessage* msg); - virtual AtlasMessage::IMessage* Retrieve(); + virtual void Add(T* msg); + virtual T* Retrieve(); - virtual void Query(AtlasMessage::IMessage&); + virtual void Query(T&); virtual void QueryDone(); private: CMutex m_Mutex; - std::queue m_Queue; + std::queue m_Queue; }; - \ No newline at end of file diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h index 8d71db1411..800ee89723 100644 --- a/source/tools/atlas/GameInterface/Messages.h +++ b/source/tools/atlas/GameInterface/Messages.h @@ -3,48 +3,99 @@ #include "MessagePasser.h" +// Structures in this file are passed over the DLL boundary, so some +// carefulness and/or luck is required... + +class wxPoint; + namespace AtlasMessage { +struct Position +{ + Position() : x(0.f), y(0.f), z(0.f) {} + Position(float x_, float y_, float z_) : x(x_), y(y_), z(z_) {} + Position(const wxPoint& pt); // converts screen-space to world-space coords + float x, y, z; // world coordinates +}; + + struct IMessage { virtual const char* GetType() const = 0; virtual ~IMessage() {} }; -#define DEFINE(t) struct m##t : public IMessage { const char* GetType() const { return #t; } +// High-level message types, as a limited form of type-safety to prevent e.g. +// adding input message into the command queue +struct mCommand : public IMessage {}; +struct mInput : public IMessage {}; + +#define COMMAND(t) struct m##t : public mCommand { const char* GetType() const { return #t; } +#define INPUT(t) struct m##t : public mInput { const char* GetType() const { return #t; } ////////////////////////////////////////////////////////////////////////// -DEFINE(CommandString) +COMMAND(CommandString) mCommandString(const std::string& name_) : name(name_) {} const std::string name; }; ////////////////////////////////////////////////////////////////////////// -DEFINE(SetContext) +COMMAND(SetContext) mSetContext(void* /* HDC */ hdc_, void* /* HGLRC */ hglrc_) : hdc(hdc_), hglrc(hglrc_) {}; -void* hdc; -void* hglrc; +const void* hdc; +const void* hglrc; }; -DEFINE(ResizeScreen) +COMMAND(ResizeScreen) mResizeScreen(int width_, int height_) : width(width_), height(height_) {} -int width, height; +const int width, height; }; ////////////////////////////////////////////////////////////////////////// -DEFINE(GenerateMap) +COMMAND(GenerateMap) mGenerateMap(int size_) : size(size_) {} -int size; // size in number of patches +const int size; // size in number of patches }; ////////////////////////////////////////////////////////////////////////// -#undef DEFINE +INPUT(ScrollConstant) +mScrollConstant(int dir_, float speed_) : dir(dir_), speed(speed_) {} +const int dir; // as in enum below +const float speed; // set speed 0.0f to stop scrolling +enum { FORWARDS, BACKWARDS, LEFT, RIGHT }; +}; + +////////////////////////////////////////////////////////////////////////// + +/* +// TODO: Proper tool system +COMMAND(ToolBegin) +mToolBegin(std::string name_) : name(name_) {} +const std::string name; +}; + +COMMAND(ToolEnd) +mToolEnd() {} +}; +*/ + +COMMAND(AlterElevation) +mAlterElevation(Position pos_, float amount_) : pos(pos_), amount(amount_) {} +const Position pos; +const float amount; +}; + +////////////////////////////////////////////////////////////////////////// + + +#undef COMMAND +#undef INPUT } -#endif // MESSAGES_H__ \ No newline at end of file +#endif // MESSAGES_H__