Atlas: simple camera control and terrain editing.

Terrain: added terrain-editing code to CTerrain, for better
encapsulation or something.
Console: simplified screen resizing.
Game/etc: removed some unnecessary header file inclusions.

This was SVN commit r2459.
This commit is contained in:
Ykkrosh 2005-07-03 16:25:48 +00:00
parent e84b8bd017
commit 549150fe38
37 changed files with 741 additions and 123 deletions

View File

@ -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);
}
}
}

View File

@ -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();

View File

@ -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");

View File

@ -18,11 +18,11 @@ T Interpolate( T& a, T& b, float l )
}
template <typename T>
T clamp(T value,T min,T max)
inline T clamp(T value, T min, T max)
{
if (value<min) return min;
else if (value>max) return max;
else return value;
if (value<=min) return min;
else if (value>=max) return max;
else return value;
}
#if 0

View File

@ -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()
{

View File

@ -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

View File

@ -1,6 +1,7 @@
#include "precompiled.h"
#include "Game.h"
#include "GameAttributes.h"
#include "CLogger.h"
#ifndef NO_GUI
#include "gui/CGUI.h"

View File

@ -8,12 +8,13 @@ ERROR_GROUP(Game);
#include "World.h"
#include "Simulation.h"
#include "Player.h"
#include "GameView.h"
#include "GameAttributes.h"
#include <vector>
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

View File

@ -11,6 +11,7 @@
#include <CLogger.h>
#include <CConsole.h>
#include <Game.h>
#include <GameAttributes.h>
#define LOG_CAT_NET "net"

View File

@ -3,6 +3,7 @@
#include "Network/Session.h"
#include "Game.h"
#include "GameAttributes.h"
#include "TurnManager.h"
#include "scripting/JSMap.h"

View File

@ -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"

View File

@ -386,6 +386,23 @@
</File>
</Filter>
</Filter>
<Filter
Name="Tools"
Filter="">
<File
RelativePath=".\ScenarioEditor\Tools\AlterElevation.cpp">
</File>
<Filter
Name="Common"
Filter="">
<File
RelativePath=".\ScenarioEditor\Tools\Common\Tools.cpp">
</File>
<File
RelativePath=".\ScenarioEditor\Tools\Common\Tools.h">
</File>
</Filter>
</Filter>
</Filter>
</Filter>
<File

View File

@ -24,7 +24,7 @@ AtlasWindowCommandProc* AtlasWindowCommandProc::GetFromParentFrame(wxWindow* obj
win = win->GetParent();
}
wxASSERT_MSG(0, _T("Couldn't find command processor"));
wxFAIL_MSG(_T("Couldn't find command processor"));
return NULL;
}

View File

@ -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<mCommand>* AtlasMessage::g_MessagePasser_Command = NULL;
MessagePasser<mInput>* AtlasMessage::g_MessagePasser_Input = NULL;
ATLASDLLIMPEXP void Atlas_SetMessagePasser(MessagePasser<mCommand>* handler_cmd, MessagePasser<mInput>* handler_in)
{
AtlasMessage::g_MessagePasser = handler;
g_MessagePasser_Command = handler_cmd;
g_MessagePasser_Input = handler_in;
}
ATLASDLLIMPEXP void Atlas_StartWindow(wchar_t* type)

View File

@ -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;
}

View File

@ -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();
};

View File

@ -0,0 +1,10 @@
#include "stdafx.h"
#include "Sidebar.h"
Sidebar::Sidebar(wxWindow* parent)
: wxPanel(parent)
{
m_MainSizer = new wxBoxSizer(wxVERTICAL);
SetSizer(m_MainSizer);
}

View File

@ -0,0 +1,8 @@
class Sidebar : public wxPanel
{
public:
Sidebar(wxWindow* parent);
protected:
wxSizer* m_MainSizer; // vertical box sizer
};

View File

@ -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));
}

View File

@ -0,0 +1,7 @@
#include "../Common/Sidebar.h"
class MapSidebar : public Sidebar
{
public:
MapSidebar(wxWindow* parent);
};

View File

@ -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);

View File

@ -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;
}

View File

@ -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__

View File

@ -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<mCommand>*, MessagePasser<mInput>*);
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<mCommand> msgPasser_Command;
static MessagePasserImpl<mInput> 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<mCommandString*>(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<mCommandString*>(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);
}

View File

@ -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;
extern GameLoopState* g_GameLoop;
#endif // GAMELOOP_H__

View File

@ -0,0 +1,24 @@
#include "precompiled.h"
#include "MessageHandler.h"
#include "../GameLoop.h"
namespace AtlasMessage {
void fScrollConstant(IMessage* msg)
{
mScrollConstant* cmd = static_cast<mScrollConstant*>(msg);
if (cmd->dir < 0 || cmd->dir > 3)
{
debug_warn("ScrollConstant: invalid direction");
}
else
{
g_GameLoop->input.scrollSpeed[cmd->dir] = cmd->speed;
}
}
REGISTER(ScrollConstant);
}

View File

@ -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<mAlterElevation*>(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);
}

View File

@ -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<mSetContext*>(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<mResizeScreen*>(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);

View File

@ -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<mGenerateMap*>(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);

View File

@ -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;
}
}
}

View File

@ -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<std::string, handler> handlers;
extern handlers& GetHandlers();
@ -15,4 +18,4 @@ extern handlers& GetHandlers();
assert(notAlreadyRegisted); \
} } init; };
}
}

View File

@ -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;
}

View File

@ -0,0 +1,8 @@
#include "GameLoop.h"
class InputProcessor
{
public:
// Returns true if the camera has moved
bool ProcessInput(GameLoopState* state);
};

View File

@ -3,21 +3,24 @@
namespace AtlasMessage
{
struct IMessage;
class MessagePasser
template <typename T> 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<mCommand>* g_MessagePasser_Command;
extern MessagePasser<mInput>* 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)
}

View File

@ -4,7 +4,7 @@
using namespace AtlasMessage;
void MessagePasserImpl::Add(IMessage* msg)
template<typename T> void MessagePasserImpl<T>::Add(T* msg)
{
m_Mutex.Lock();
@ -13,11 +13,15 @@ void MessagePasserImpl::Add(IMessage* msg)
m_Mutex.Unlock();
}
IMessage* MessagePasserImpl::Retrieve()
template <typename T> T* MessagePasserImpl<T>::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 <typename T> void MessagePasserImpl<T>::Query(T&)
{
}
void MessagePasserImpl::QueryDone()
template <typename T> void MessagePasserImpl<T>::QueryDone()
{
}
MessagePasser* g_MessagePasser = NULL;
MessagePasser<mCommand>* g_MessagePasser_Command = NULL;
MessagePasser<mInput>* g_MessagePasser_Input = NULL;
// Explicit instantiation:
template MessagePasserImpl<mCommand>;
template MessagePasserImpl<mInput>;

View File

@ -3,17 +3,16 @@
#include "ps/ThreadUtil.h"
#include <queue>
class MessagePasserImpl : public AtlasMessage::MessagePasser
template <typename T> class MessagePasserImpl : public AtlasMessage::MessagePasser<T>
{
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<AtlasMessage::IMessage*> m_Queue;
std::queue<T*> m_Queue;
};

View File

@ -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__
#endif // MESSAGES_H__