Atlas: [very] primitive brushes

This was SVN commit r2843.
This commit is contained in:
Ykkrosh 2005-10-04 02:50:59 +00:00
parent 7abf24b6c6
commit 721f6e4a1e
11 changed files with 287 additions and 78 deletions

View File

@ -100,9 +100,23 @@ public:
evt.Skip();
}
void OnMouseCapture(wxMouseCaptureChangedEvent& WXUNUSED(evt))
{
if (m_MouseCaptured)
{
// unexpected loss of capture (i.e. not through ReleaseMouse)
m_MouseCaptured = false;
// (Note that this can be made to happen easily, by alt-tabbing away,
// and mouse events will be missed; so it is never guaranteed that e.g.
// two LeftDown events will be separated by a LeftUp.)
}
}
void OnMouse(wxMouseEvent& evt)
{
// Capture on button-down
// Capture on button-down, so we can respond even when the mouse
// moves off the window
if (!m_MouseCaptured && evt.ButtonDown())
{
m_MouseCaptured = true;
@ -117,10 +131,14 @@ public:
ReleaseMouse();
}
if (evt.ButtonDown())
SetFocus();
g_CurrentTool->OnMouse(evt);
// TODO: what should happen if the tool and camera both respond to the
// same buttons? (e.g. left+right for rotating)
// TODO: if the tool responded to the mouse action, should we avoid moving
// the camera too? (This is mostly avoided by not sharing buttons between
// camera and tools, so maybe it's not a problem.)
if (evt.GetWheelRotation())
{
@ -193,6 +211,7 @@ BEGIN_EVENT_TABLE(Canvas, wxGLCanvas)
EVT_MIDDLE_UP (Canvas::OnMouse)
EVT_MOUSEWHEEL (Canvas::OnMouse)
EVT_MOTION (Canvas::OnMouse)
EVT_MOUSE_CAPTURE_CHANGED(Canvas::OnMouseCapture)
END_EVENT_TABLE()
// GL functions exported from DLL, and called by game (in a separate

View File

@ -8,22 +8,35 @@ using AtlasMessage::Position;
class AlterElevation : public StateDrivenTool<AlterElevation>
{
private:
int m_Direction; // +1 = raise, -1 = lower
Position m_Pos;
public:
AlterElevation()
{
SetState(&Waiting);
}
private:
int m_Direction; // +1 = raise, -1 = lower
Position m_Pos;
protected:
struct Waiting : public State
void OnEnable(AlterElevation*)
{
bool mouse(AlterElevation* obj, wxMouseEvent& evt)
int w = 2, h = 3;
float* data = new float[w*h];
for (int i = 0; i < w*h; ++i) data[i] = 1.f;
POST_COMMAND(SetBrush(w, h, data));
}
void OnDisable(AlterElevation*)
{
POST_COMMAND(BrushPreview(false, Position()));
}
struct sWaiting : public State
{
bool OnMouse(AlterElevation* obj, wxMouseEvent& evt)
{
if (evt.LeftDown())
{
@ -37,25 +50,26 @@ protected:
SET_STATE(Lowering);
return true;
}
else
else if (evt.Moving())
{
return false;
POST_COMMAND(BrushPreview(true, Position(evt.GetPosition())));
}
return false;
}
}
Waiting;
struct Altering_common : public State
struct sAltering_common : public State
{
void leave(AlterElevation*)
void OnLeave(AlterElevation*)
{
ScenarioEditor::GetCommandProc().FinaliseLastCommand();
}
bool mouse(AlterElevation* obj, wxMouseEvent& evt)
bool OnMouse(AlterElevation* obj, wxMouseEvent& evt)
{
if (isMouseUp(evt))
if (IsMouseUp(evt))
{
SET_STATE(Waiting);
return true;
@ -63,6 +77,7 @@ protected:
else if (evt.Dragging())
{
obj->m_Pos = Position(evt.GetPosition());
POST_COMMAND(BrushPreview(true, obj->m_Pos));
return true;
}
else
@ -71,26 +86,26 @@ protected:
}
}
void tick(AlterElevation* obj, float dt)
void OnTick(AlterElevation* obj, float dt)
{
ADD_WORLDCOMMAND(AlterElevation, (obj->m_Pos, dt*4096.f*getDirection()));
ADD_WORLDCOMMAND(AlterElevation, (obj->m_Pos, dt*4096.f*GetDirection()));
}
virtual bool isMouseUp(wxMouseEvent& evt) = 0;
virtual int getDirection() = 0;
virtual bool IsMouseUp(wxMouseEvent& evt) = 0;
virtual int GetDirection() = 0;
};
struct Raising : public Altering_common
struct sRaising : public sAltering_common
{
bool isMouseUp(wxMouseEvent& evt) { return evt.LeftUp(); }
int getDirection() { return +1; }
bool IsMouseUp(wxMouseEvent& evt) { return evt.LeftUp(); }
int GetDirection() { return +1; }
}
Raising;
struct Lowering : public Altering_common
struct sLowering : public sAltering_common
{
bool isMouseUp(wxMouseEvent& evt) { return evt.RightUp(); }
int getDirection() { return -1; }
bool IsMouseUp(wxMouseEvent& evt) { return evt.RightUp(); }
int GetDirection() { return -1; }
}
Lowering;
};

View File

@ -56,38 +56,7 @@ private:
template <typename T>
class StateDrivenTool : public ITool
{
protected:
struct State
{
virtual void enter(T* WXUNUSED(obj)) {}
virtual void leave(T* WXUNUSED(obj)) {}
virtual void tick (T* WXUNUSED(obj), float WXUNUSED(dt)) {}
virtual bool mouse(T* WXUNUSED(obj), wxMouseEvent& WXUNUSED(evt))
{
return false;
}
virtual bool key(T* WXUNUSED(obj), wxKeyEvent& WXUNUSED(evt), KeyEventType WXUNUSED(type))
{
return false;
}
};
struct Disabled : public State
{
}
Disabled;
State* m_CurrentState;
void SetState(State* state)
{
m_CurrentState->leave(static_cast<T*>(this));
// this cast is safe as long as the class is used as in
// "class Something : public StateDrivenTool<Something> { ... }"
m_CurrentState = state;
m_CurrentState->enter(static_cast<T*>(this));
}
public:
StateDrivenTool()
: m_CurrentState(&Disabled)
{
@ -98,20 +67,56 @@ protected:
SetState(&Disabled);
}
protected:
// Called when the tool is enabled/disabled; always called in zero or
// more enable-->disable pairs per object instance.
virtual void OnEnable(T* WXUNUSED(obj)) {}
virtual void OnDisable(T* WXUNUSED(obj)) {}
struct State
{
virtual void OnEnter(T* WXUNUSED(obj)) {}
virtual void OnLeave(T* WXUNUSED(obj)) {}
virtual void OnTick (T* WXUNUSED(obj), float WXUNUSED(dt)) {}
// Should return true if the event has been handled (else the event will
// be passed to a lower-priority level)
virtual bool OnMouse(T* WXUNUSED(obj), wxMouseEvent& WXUNUSED(evt)) { return false; }
virtual bool OnKey(T* WXUNUSED(obj), wxKeyEvent& WXUNUSED(evt), KeyEventType WXUNUSED(type)) { return false; }
};
struct sDisabled : public State
{
void OnEnter(T* obj) { obj->OnDisable(obj); }
void OnLeave(T* obj) { obj->OnEnable(obj); }
}
Disabled;
void SetState(State* state)
{
m_CurrentState->OnLeave(static_cast<T*>(this));
// this cast is safe as long as the class is used as in
// "class Something : public StateDrivenTool<Something> { ... }"
m_CurrentState = state;
m_CurrentState->OnEnter(static_cast<T*>(this));
}
private:
State* m_CurrentState;
virtual void OnMouse(wxMouseEvent& evt)
{
m_CurrentState->mouse(static_cast<T*>(this), evt);
m_CurrentState->OnMouse(static_cast<T*>(this), evt);
}
virtual void OnKey(wxKeyEvent& evt, KeyEventType dir)
{
m_CurrentState->key(static_cast<T*>(this), evt, dir);
m_CurrentState->OnKey(static_cast<T*>(this), evt, dir);
}
virtual void OnTick(float dt)
{
m_CurrentState->tick(static_cast<T*>(this), dt);
m_CurrentState->OnTick(static_cast<T*>(this), dt);
}
};

View File

@ -0,0 +1,77 @@
#include "precompiled.h"
#include "Brushes.h"
#include "ps/Game.h"
#include "graphics/Terrain.h"
#include "lib/ogl.h"
using namespace AtlasMessage;
Brush::Brush()
: m_W(0), m_H(0), m_Data(NULL), m_Enabled(false)
{
}
Brush::~Brush()
{
delete[] m_Data;
}
void Brush::SetData(int w, int h, const float* data)
{
m_W = w;
m_H = h;
m_Data = data;
}
void Brush::GetBottomRight(int& x, int& y) const
{
CVector3D c = m_Centre;
if (m_W % 2) c.X += CELL_SIZE/2.f;
if (m_H % 2) c.Z += CELL_SIZE/2.f;
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
u32 cx, cy;
terrain->CalcFromPosition(c, cx, cy);
x = cx - (m_W-1)/2;
y = cy - (m_H-1)/2;
}
void Brush::SetRenderEnabled(bool enabled)
{
m_Enabled = enabled;
}
void Brush::Render()
{
if (! m_Enabled)
return;
glPointSize(4.f); // TODO: does this clobber state that other people expect to stay unchanged?
glDisable(GL_DEPTH_TEST);
glBegin(GL_POINTS);
glColor3f(0.f, 1.f, 0.f);
int x0, y0;
GetBottomRight(x0, y0);
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
for (int dy = 0; dy < m_H; ++dy)
{
for (int dx = 0; dx < m_W; ++dx)
{
CVector3D pos;
terrain->CalcPosition(x0+dx, y0+dy, pos);
glVertex3f(pos.X, pos.Y, pos.Z);
}
}
glEnd();
glEnable(GL_DEPTH_TEST);
}
Brush AtlasMessage::g_CurrentBrush;

View File

@ -0,0 +1,32 @@
#include "maths/Vector3D.h"
namespace AtlasMessage {
struct Brush
{
Brush();
~Brush();
void SetData(int w, int h, const float* data);
void SetRenderEnabled(bool enabled); // initial state is disabled
void Render(); // only does anything if enabled
void GetBottomRight(int& x, int& y) const;
float Get(int x, int y) const
{
debug_assert(x >= 0 && x < m_W && y >= 0 && y < m_H);
return m_Data[x + y*m_W];
}
int m_W, m_H;
CVector3D m_Centre;
private:
const float* m_Data;
bool m_Enabled;
};
extern Brush g_CurrentBrush;
}

View File

@ -4,6 +4,7 @@
#include "MessagePasserImpl.h"
#include "Messages.h"
#include "Brushes.h"
#include "Handlers/MessageHandler.h"
#include "InputProcessor.h"
@ -16,7 +17,14 @@
using namespace AtlasMessage;
extern void Render_();
void AtlasRender()
{
Render_();
g_CurrentBrush.Render();
}
// Loaded from DLL:
void (*Atlas_StartWindow)(wchar_t* type);
@ -46,7 +54,7 @@ static void* LaunchWindow(void*)
bool BeginAtlas(int argc, char* argv[], void* dll)
{
// Load required symbols from the DLL
#define GET(x) *(void**)&x = dlsym(dll, #x); if (! x) return false;
#define GET(x) *(void**)&x = dlsym(dll, #x); debug_assert(x); if (! x) return false;
GET(Atlas_StartWindow);
GET(Atlas_SetMessagePasser);
GET(Atlas_GLSetCurrent);
@ -165,8 +173,7 @@ bool BeginAtlas(int argc, char* argv[], void* dll)
if (state.rendering)
{
Render_();
glFinish();
AtlasRender();
Atlas_GLSwapBuffers((void*)state.glContext);
}
@ -174,8 +181,8 @@ bool BeginAtlas(int argc, char* argv[], void* dll)
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
// Be nice to the processor (by sleeping lots) if we're not doing anything
// useful, and 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...
{
@ -185,6 +192,7 @@ bool BeginAtlas(int argc, char* argv[], void* dll)
// 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
// (TODO: This should probably be done with something like semaphores)
Atlas_NotifyEndOfFrame(); // (TODO: rename to NotifyEndOfQuiteShortProcessingPeriodSoPleaseSendMeNewMessages or something)
SDL_Delay(50);
if (!msgPasser_Input.IsEmpty() || !msgPasser_Command.IsEmpty())

View File

@ -0,0 +1,20 @@
#include "precompiled.h"
#include "MessageHandler.h"
#include "../Brushes.h"
namespace AtlasMessage {
MESSAGEHANDLER(SetBrush)
{
g_CurrentBrush.SetData(msg->width, msg->height, msg->data);
}
MESSAGEHANDLER(BrushPreview)
{
g_CurrentBrush.SetRenderEnabled(msg->enable);
msg->pos.GetWorldSpace(g_CurrentBrush.m_Centre);
}
}

View File

@ -35,10 +35,21 @@ MESSAGEHANDLER(Scroll)
static CVector3D lastCameraPos = camera.GetTranslation();
// Ensure roughly correct motion when dragging is combined with other
// movements
// movements.
if (lastCameraPos != camera.GetTranslation())
targetPos += camera.GetTranslation() - lastCameraPos;
// General operation:
//
// When selecting a target point to drag, remember targetPos (a world-space
// point on the terrain, underneath the mouse) and targetDistance (from the
// camera to the target point).
//
// When dragging to a different position, the target point should remain
// under the moved mouse; so calculate the ray through the camera and mouse,
// multiply by targetDistance and add to targetPos, resulting in the required
// camera position.
if (msg->type == eScrollType::FROM)
{
msg->pos.GetWorldSpace(targetPos);
@ -74,14 +85,15 @@ MESSAGEHANDLER(RotateAround)
if (msg->type == eRotateAroundType::FROM)
{
msg->pos.GetScreenSpace(lastX, lastY);
msg->pos.GetWorldSpace(focusPos);
msg->pos.GetScreenSpace(lastX, lastY); // get mouse position
msg->pos.GetWorldSpace(focusPos); // get point on terrain under mouse
}
else if (msg->type == eRotateAroundType::TO)
{
float x, y;
msg->pos.GetScreenSpace(x, y);
msg->pos.GetScreenSpace(x, y); // get mouse position
// Rotate around X and Y axes by amounts depending on the mouse delta
float rotX = 6.f * (y-lastY) / g_Renderer.GetHeight();
float rotY = 6.f * (x-lastX) / g_Renderer.GetWidth();
@ -100,7 +112,7 @@ MESSAGEHANDLER(RotateAround)
// Make sure up is still pointing up, regardless of any rounding errors.
// (Maybe this distorts the camera in other ways, but at least the errors
// are far less noticeable to me.)
camera._21 = 0.f; // (_21 = Y component returned by GetUp())
camera._21 = 0.f; // (_21 = Y component returned by GetLeft())
camera.Translate(focusPos + offset);

View File

@ -7,6 +7,8 @@
#include "graphics/Terrain.h"
#include "ps/Game.h"
#include "../Brushes.h"
namespace AtlasMessage {
@ -47,12 +49,15 @@ BEGIN_COMMAND(AlterElevation)
roundingError -= (float)(int)roundingError;
}
CVector3D vec;
d->pos.GetWorldSpace(vec);
uint32_t x, z;
terrain->CalcFromPosition(vec, x, z);
terrain->RaiseVertex(x, z, amount);
terrain->MakeDirty(x, z, x, z);
int x0, y0;
d->pos.GetWorldSpace(g_CurrentBrush.m_Centre);
g_CurrentBrush.GetBottomRight(x0, y0);
for (int dy = 0; dy < g_CurrentBrush.m_H; ++dy)
for (int dx = 0; dx < g_CurrentBrush.m_W; ++dx)
if (g_CurrentBrush.Get(dx, dy)) // TODO: variable raise amount?
terrain->RaiseVertex(x0+dx, y0+dy, amount);
terrain->MakeDirty(x0, y0, x0+g_CurrentBrush.m_W-1, y0+g_CurrentBrush.m_H-1);
}
void Undo()

View File

@ -36,6 +36,19 @@ COMMAND(RenderStyle,
((bool, wireframe))
);
//////////////////////////////////////////////////////////////////////////
COMMAND(SetBrush,
((int, width)) // number of vertices
((int, height))
((float*, data)) // width*height array, allocated with new[]
);
COMMAND(BrushPreview,
((bool, enable))
((Position, pos)) // only used if enable==true
);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

View File

@ -14,6 +14,8 @@ class CVector3D;
namespace AtlasMessage
{
//////////////////////////////////////////////////////////////////////////
struct Position
{
Position() : type(0) { type0.x = type0.y = type0.z = 0.f; }
@ -31,6 +33,7 @@ struct Position
void GetScreenSpace(float& x, float& y) const; // (implementation in Misc.cpp)
};
//////////////////////////////////////////////////////////////////////////
struct IMessage
{