diff --git a/source/maths/Matrix3D.cpp b/source/maths/Matrix3D.cpp index ed7de827e3..351304ad74 100755 --- a/source/maths/Matrix3D.cpp +++ b/source/maths/Matrix3D.cpp @@ -473,6 +473,44 @@ void CMatrix3D::Rotate(const CQuaternion& quat) Concatenate(rotationMatrix); } +CQuaternion CMatrix3D::GetRotation() const +{ + float tr = _data2d[0][0] + _data2d[1][1] + _data2d[2][2]; + + int next[] = { 1, 2, 0 }; + + float quat[4]; + + if (tr > 0.f) + { + float s = sqrtf(tr + 1.f); + quat[3] = s * 0.5f; + s = 0.5f / s; + quat[0] = (_data2d[1][2] - _data2d[2][1]) * s; + quat[1] = (_data2d[2][0] - _data2d[0][2]) * s; + quat[2] = (_data2d[0][1] - _data2d[1][0]) * s; + } + else + { + int i = 0; + if (_data2d[1][1] > _data2d[0][0]) i = 1; + if (_data2d[2][2] > _data2d[i][i]) i = 2; + int j = next[i]; + int k = next[j]; + + float s = sqrtf((_data2d[i][i] - (_data2d[j][j] + _data2d[k][k])) + 1.f); + quat[i] = s * 0.5f; + + if (s != 0.f) s = 0.5f / s; + + quat[3] = (_data2d[j][k] - _data2d[k][j]) * s; + quat[j] = (_data2d[i][j] + _data2d[j][i]) * s; + quat[k] = (_data2d[i][k] + _data2d[k][i]) * s; + } + + return CQuaternion(quat[0], quat[1], quat[2], quat[3]); +} + void CMatrix3D::SetRotation(const CQuaternion& quat) { quat.ToMatrix(*this); @@ -506,10 +544,43 @@ static void test_inverse() } } +static void test_quats() +{ + srand(0); + for (int i = 0; i < 4; ++i) + { + CQuaternion q; + q.FromEulerAngles( + -6.28f + 12.56f*(rand()/(float)RAND_MAX), + -6.28f + 12.56f*(rand()/(float)RAND_MAX), + -6.28f + 12.56f*(rand()/(float)RAND_MAX) + ); + CMatrix3D m; + q.ToMatrix(m); + CQuaternion q2 = m.GetRotation(); + + float epsilon = 0.0001f; + // I hope there's a good reason why they're sometimes negated, and + // it's not just a bug... + bool ok_oneway = + fabs(q2.m_W - q.m_W) < epsilon && + fabs(q2.m_V.X - q.m_V.X) < epsilon && + fabs(q2.m_V.Y - q.m_V.Y) < epsilon && + fabs(q2.m_V.Z - q.m_V.Z) < epsilon; + bool ok_otherway = + fabs(q2.m_W + q.m_W) < epsilon && + fabs(q2.m_V.X + q.m_V.X) < epsilon && + fabs(q2.m_V.Y + q.m_V.Y) < epsilon && + fabs(q2.m_V.Z + q.m_V.Z) < epsilon; + TEST(ok_oneway ^ ok_otherway); + } +} + static void self_test() { test_inverse(); + test_quats(); } RUN_SELF_TEST; diff --git a/source/maths/Matrix3D.h b/source/maths/Matrix3D.h index 28734abde2..c250cee667 100755 --- a/source/maths/Matrix3D.h +++ b/source/maths/Matrix3D.h @@ -13,7 +13,7 @@ class CMatrix3D { public: // the matrix data itself - accessible as either longhand names - // or via a flat array + // or via a flat or 2d array union { struct { float _11, _21, _31, _41; @@ -22,6 +22,8 @@ public: float _14, _24, _34, _44; }; float _data[16]; + float _data2d[4][4]; + // (Be aware that _data2d[3][1] is _13, etc. This is to be considered a feature.) }; public: @@ -105,6 +107,8 @@ public: CVector3D GetUp() const; // return forward vector, derived from rotation CVector3D GetIn() const; + // return a quaternion representing the matrix's rotation + CQuaternion GetRotation() const; // transform a 3D vector by this matrix void Transform(const CVector3D &vector,CVector3D& result) const; diff --git a/source/maths/Quaternion.cpp b/source/maths/Quaternion.cpp index 049e2128ff..3b5bd1d1b3 100755 --- a/source/maths/Quaternion.cpp +++ b/source/maths/Quaternion.cpp @@ -19,8 +19,13 @@ CQuaternion::CQuaternion() m_W = 1; } +CQuaternion::CQuaternion(float x, float y, float z, float w) +: m_V(x, y, z), m_W(w) +{ +} + //quaternion addition -CQuaternion CQuaternion::operator + (CQuaternion &quat) +CQuaternion CQuaternion::operator + (const CQuaternion &quat) const { CQuaternion Temp; @@ -31,7 +36,7 @@ CQuaternion CQuaternion::operator + (CQuaternion &quat) } //quaternion addition/assignment -CQuaternion &CQuaternion::operator += (CQuaternion &quat) +CQuaternion &CQuaternion::operator += (const CQuaternion &quat) { m_W += quat.m_W; m_V += quat.m_V; @@ -40,7 +45,7 @@ CQuaternion &CQuaternion::operator += (CQuaternion &quat) } //quaternion multiplication -CQuaternion CQuaternion::operator * (CQuaternion &quat) +CQuaternion CQuaternion::operator * (const CQuaternion &quat) const { CQuaternion Temp; @@ -51,7 +56,7 @@ CQuaternion CQuaternion::operator * (CQuaternion &quat) } //quaternion multiplication/assignment -CQuaternion &CQuaternion::operator *= (CQuaternion &quat) +CQuaternion &CQuaternion::operator *= (const CQuaternion &quat) { (*this) = (*this) * quat; @@ -59,7 +64,7 @@ CQuaternion &CQuaternion::operator *= (CQuaternion &quat) } -void CQuaternion::FromEularAngles (float x, float y, float z) +void CQuaternion::FromEulerAngles (float x, float y, float z) { float cr, cp, cy; float sr, sp, sy; @@ -215,3 +220,19 @@ void CQuaternion::Normalize() m_W*=invlen; } } + +/////////////////////////////////////////////////////////////////////////////////////////////// + +CVector3D CQuaternion::Rotate(const CVector3D& vec) const +{ + // v' = q * v * q^-1 + // (where v is the quat. with w=0, xyz=vec) + + return (*this * CQuaternion(vec.X, vec.Y, vec.Z, 0.f) * GetInverse()).m_V; +} + +CQuaternion CQuaternion::GetInverse() const +{ + float lensqrd = SQR(m_V.X) + SQR(m_V.Y) + SQR(m_V.Z) + SQR(m_W); + return CQuaternion(-m_V.X/lensqrd, -m_V.Y/lensqrd, -m_V.Z/lensqrd, m_W/lensqrd); +} diff --git a/source/maths/Quaternion.h b/source/maths/Quaternion.h index 0ab0aa8a29..d37e9314ff 100755 --- a/source/maths/Quaternion.h +++ b/source/maths/Quaternion.h @@ -10,6 +10,7 @@ #define QUATERNION_H #include "Matrix3D.h" +#include "Vector3D.h" class CQuaternion { @@ -19,18 +20,19 @@ public: public: CQuaternion(); + CQuaternion(float x, float y, float z, float w); //quaternion addition - CQuaternion operator + (CQuaternion &quat); + CQuaternion operator + (const CQuaternion &quat) const; //quaternion addition/assignment - CQuaternion &operator += (CQuaternion &quat); + CQuaternion &operator += (const CQuaternion &quat); //quaternion multiplication - CQuaternion operator * (CQuaternion &quat); + CQuaternion operator * (const CQuaternion &quat) const; //quaternion multiplication/assignment - CQuaternion &operator *= (CQuaternion &quat); + CQuaternion &operator *= (const CQuaternion &quat); - void FromEularAngles (float x, float y, float z); + void FromEulerAngles (float x, float y, float z); //convert the quaternion to matrix CMatrix3D ToMatrix() const; @@ -44,6 +46,12 @@ public: // normalize this quaternion void Normalize(); + + // rotate a vector by this quaternion + CVector3D Rotate(const CVector3D& vec) const; + + // calculate q^-1 + CQuaternion GetInverse() const; }; #endif diff --git a/source/maths/Vector3D.cpp b/source/maths/Vector3D.cpp index 4142e7e84d..4d0e3c7b49 100755 --- a/source/maths/Vector3D.cpp +++ b/source/maths/Vector3D.cpp @@ -24,6 +24,11 @@ int CVector3D::operator ! () const return 1; } +bool CVector3D::operator== (const CVector3D &vector) const +{ + return (X == vector.X && Y == vector.Y && Z == vector.Z); +} + //vector addition CVector3D CVector3D::operator + (const CVector3D &vector) const { diff --git a/source/maths/Vector3D.h b/source/maths/Vector3D.h index 637710733d..bb5c07cc44 100755 --- a/source/maths/Vector3D.h +++ b/source/maths/Vector3D.h @@ -30,6 +30,10 @@ class CVector3D float& operator[](int index) { return *((&X)+index); } const float& operator[](int index) const { return *((&X)+index); } + //vector equality (testing float equality, so please be careful if necessary) + bool operator== (const CVector3D &vector) const; + bool operator!= (const CVector3D &vector) const { return !operator==(vector); } + //vector addition CVector3D operator + (const CVector3D &vector) const ; //vector addition/assignment diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp index 2634ef7c2f..1b50c0500b 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp @@ -25,7 +25,8 @@ class Canvas : public wxGLCanvas public: Canvas(wxWindow* parent, int* attribList) : wxGLCanvas(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS, _T("GLCanvas"), attribList), - m_SuppressResize(true) + m_SuppressResize(true), + m_MouseState(NONE), m_LastMouseState(NONE) { } @@ -34,7 +35,7 @@ public: // Be careful not to send 'resize' messages to the game before we've // told it that this canvas exists if (! m_SuppressResize) - ADD_COMMAND(ResizeScreen(GetClientSize().GetWidth(), GetClientSize().GetHeight())); + POST_COMMAND(ResizeScreen(GetClientSize().GetWidth(), GetClientSize().GetHeight())); // TODO: fix flashing } @@ -57,18 +58,22 @@ public: default: return false; } - float speed = wxGetKeyState(WXK_SHIFT) ? 240.0f : 120.0f; + float speed = 120.f; + if (wxGetKeyState(WXK_SHIFT)) + speed *= 4.f; + else if (wxGetKeyState(WXK_CONTROL)) + speed /= 4.f; 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)); + if (wxGetKeyState(WXK_LEFT)) POST_INPUT(ScrollConstant(AtlasMessage::mScrollConstant::LEFT, speed)); + if (wxGetKeyState(WXK_RIGHT)) POST_INPUT(ScrollConstant(AtlasMessage::mScrollConstant::RIGHT, speed)); + if (wxGetKeyState(WXK_UP)) POST_INPUT(ScrollConstant(AtlasMessage::mScrollConstant::FORWARDS, speed)); + if (wxGetKeyState(WXK_DOWN)) POST_INPUT(ScrollConstant(AtlasMessage::mScrollConstant::BACKWARDS, speed)); } else { - ADD_INPUT(ScrollConstant(dir, enable ? speed : 0.0f)); + POST_INPUT(ScrollConstant(dir, enable ? speed : 0.0f)); } return true; } @@ -96,10 +101,60 @@ public: void OnMouse(wxMouseEvent& evt) { 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) + + if (evt.GetWheelRotation()) + { + float speed = 16.f; + if (wxGetKeyState(WXK_SHIFT)) + speed *= 4.f; + else if (wxGetKeyState(WXK_CONTROL)) + speed /= 4.f; + + POST_INPUT(SmoothZoom(evt.GetWheelRotation() * speed / evt.GetWheelDelta())); + } + else + { + if (evt.LeftIsDown() && evt.RightIsDown()) + m_MouseState = ROTATEAROUND; + else if (evt.MiddleIsDown()) + m_MouseState = SCROLL; + else + m_MouseState = NONE; + + if (m_MouseState != m_LastMouseState) + { + switch (m_MouseState) + { + case NONE: break; + case SCROLL: POST_INPUT(Scroll(AtlasMessage::mScroll::FROM, evt.GetPosition())); break; + case ROTATEAROUND: POST_INPUT(RotateAround(AtlasMessage::mRotateAround::FROM, evt.GetPosition())); break; + default: wxFAIL; + } + m_LastMouseState = m_MouseState; + } + else if (evt.Dragging()) + { + switch (m_MouseState) + { + case NONE: break; + case SCROLL: POST_INPUT(Scroll(AtlasMessage::mScroll::TO, evt.GetPosition())); break; + case ROTATEAROUND: POST_INPUT(RotateAround(AtlasMessage::mRotateAround::TO, evt.GetPosition())); break; + default: wxFAIL; + } + } + } + } private: bool m_SuppressResize; + + enum { NONE, SCROLL, ROTATEAROUND }; + int m_MouseState, m_LastMouseState; + DECLARE_EVENT_TABLE(); }; BEGIN_EVENT_TABLE(Canvas, wxGLCanvas) @@ -107,11 +162,14 @@ BEGIN_EVENT_TABLE(Canvas, wxGLCanvas) EVT_KEY_DOWN (Canvas::OnKeyDown) EVT_KEY_UP (Canvas::OnKeyUp) - EVT_LEFT_DOWN (Canvas::OnMouse) - EVT_LEFT_UP (Canvas::OnMouse) - EVT_RIGHT_DOWN(Canvas::OnMouse) - EVT_RIGHT_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_MIDDLE_DOWN(Canvas::OnMouse) + EVT_MIDDLE_UP (Canvas::OnMouse) + EVT_MOUSEWHEEL (Canvas::OnMouse) + EVT_MOTION (Canvas::OnMouse) END_EVENT_TABLE() // GL functions exported from DLL, and called by game (in a separate @@ -221,7 +279,9 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent) int glAttribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, - WX_GL_DEPTH_SIZE, 24, // TODO: wx documentation doesn't say this is valid + WX_GL_DEPTH_SIZE, 24, // TODO: wx documentation doesn't say 24 is valid + WX_GL_BUFFER_SIZE, 24, // colour bits + WX_GL_MIN_ALPHA, 8, // alpha bits 0 }; Canvas* canvas = new Canvas(splitter, glAttribList); @@ -241,21 +301,25 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent) // Send setup messages to game engine: #ifndef UI_ONLY - ADD_COMMAND(SetContext(canvas->GetContext())); + POST_COMMAND(SetContext(canvas->GetContext())); - ADD_COMMAND(CommandString("init")); + POST_COMMAND(CommandString("init")); canvas->InitSize(); - ADD_COMMAND(GenerateMap(9)); + // Start with a blank map (so that the editor can assume there's always + // a valid map loaded) + POST_COMMAND(GenerateMap(9)); - ADD_COMMAND(CommandString("render_enable")); + POST_COMMAND(CommandString("render_enable")); #endif // XXX USE_TOOL(AlterElevation); - // Set up a timer to make sure tool-updates happen even when there's no idle time + // Set up a timer to make sure tool-updates happen frequently (in addition + // to the idle handler (which makes then happen more frequently if there's nothing + // else to do)) m_Timer.SetOwner(this); m_Timer.Start(20); } @@ -264,9 +328,9 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent) void ScenarioEditor::OnClose(wxCloseEvent&) { #ifndef UI_ONLY - ADD_COMMAND(CommandString("shutdown")); + POST_COMMAND(CommandString("shutdown")); #endif - ADD_COMMAND(CommandString("exit")); + POST_COMMAND(CommandString("exit")); SetCurrentTool(NULL); @@ -316,7 +380,7 @@ void ScenarioEditor::OnRedo(wxCommandEvent&) void ScenarioEditor::OnWireframe(wxCommandEvent& event) { - ADD_COMMAND(RenderStyle(event.IsChecked())); + POST_COMMAND(RenderStyle(event.IsChecked())); } ////////////////////////////////////////////////////////////////////////// diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp index 5d644621d9..8193d9af3d 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Map/Map.cpp @@ -14,7 +14,7 @@ static void LoadMap() if (dlg.ShowModal() == wxID_OK) { std::wstring map = dlg.GetFilename().c_str(); - ADD_COMMAND(LoadMap(map)); + POST_COMMAND(LoadMap(map)); } // TODO: Make this a non-undoable command @@ -22,12 +22,15 @@ static void LoadMap() static void GenerateMap() { - ADD_COMMAND(GenerateMap(9)); + POST_COMMAND(GenerateMap(9)); } MapSidebar::MapSidebar(wxWindow* parent) : Sidebar(parent) { + // TODO: Less ugliness + // TODO: Intercept arrow keys and send them to the GL window + m_MainSizer->Add(new ActionButton(this, _T("Load existing map"), &LoadMap)); m_MainSizer->Add(new ActionButton(this, _T("Generate empty map"), &GenerateMap)); } diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp index 38d64bce5f..200e31e1a6 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp @@ -40,10 +40,10 @@ WorldCommand::~WorldCommand() bool WorldCommand::Do() { if (m_AlreadyDone) - ADD_COMMAND(RedoCommand()); + POST_COMMAND(RedoCommand()); else { - ADD_COMMAND(DoCommand(m_Command)); + POST_COMMAND(DoCommand(m_Command)); m_AlreadyDone = true; } return true; @@ -51,7 +51,7 @@ bool WorldCommand::Do() bool WorldCommand::Undo() { - ADD_COMMAND(UndoCommand()); + POST_COMMAND(UndoCommand()); return true; } @@ -68,6 +68,6 @@ bool WorldCommand::Merge(AtlasWindowCommand* p) if (! m_Command->IsMergeable()) return false; - ADD_COMMAND(MergeCommand()); + POST_COMMAND(MergeCommand()); return true; } diff --git a/source/tools/atlas/GameInterface/GameLoop.h b/source/tools/atlas/GameInterface/GameLoop.h index 9e681ea0e7..0464bd9614 100644 --- a/source/tools/atlas/GameInterface/GameLoop.h +++ b/source/tools/atlas/GameInterface/GameLoop.h @@ -12,9 +12,10 @@ struct GameLoopState const void* glContext; float frameLength; // smoothed to avoid large jumps - struct + struct Input { float scrollSpeed[4]; // [fwd, bwd, left, right]. 0.0f for disabled. + float zoomDelta; } input; }; diff --git a/source/tools/atlas/GameInterface/Handlers/CameraCtrl.cpp b/source/tools/atlas/GameInterface/Handlers/CameraCtrl.cpp index 7321fab32b..7ea5bc0f5e 100644 --- a/source/tools/atlas/GameInterface/Handlers/CameraCtrl.cpp +++ b/source/tools/atlas/GameInterface/Handlers/CameraCtrl.cpp @@ -3,6 +3,11 @@ #include "MessageHandler.h" #include "../GameLoop.h" +#include "maths/Vector3D.h" +#include "maths/Quaternion.h" +#include "ps/Game.h" +#include "renderer/Renderer.h" + #include namespace AtlasMessage { @@ -20,4 +25,89 @@ MESSAGEHANDLER(ScrollConstant) } } +MESSAGEHANDLER(Scroll) +{ + static CVector3D targetPos; + static float targetDistance = 0.f; + + CMatrix3D& camera = g_Game->GetView()->GetCamera()->m_Orientation; + + static CVector3D lastCameraPos = camera.GetTranslation(); + + // Ensure roughly correct motion when dragging is combined with other + // movements + if (lastCameraPos != camera.GetTranslation()) + targetPos += camera.GetTranslation() - lastCameraPos; + + if (msg->type == mScroll::FROM) + { + msg->pos.GetWorldSpace(targetPos); + targetDistance = (targetPos - camera.GetTranslation()).GetLength(); + } + else if (msg->type == mScroll::TO) + { + CVector3D origin, dir; + float x, y; + msg->pos.GetScreenSpace(x, y); + g_Game->GetView()->GetCamera()->BuildCameraRay(x, y, origin, dir); + dir *= targetDistance; + camera.Translate(targetPos - dir - origin); + } + else + { + debug_warn("Scroll: Invalid type"); + } + lastCameraPos = camera.GetTranslation(); +} + +MESSAGEHANDLER(SmoothZoom) +{ + g_GameLoop->input.zoomDelta += msg->amount; +} + +MESSAGEHANDLER(RotateAround) +{ + static CVector3D focusPos; + static float lastX = 0.f, lastY = 0.f; + + CMatrix3D& camera = g_Game->GetView()->GetCamera()->m_Orientation; + + if (msg->type == mRotateAround::FROM) + { + msg->pos.GetScreenSpace(lastX, lastY); + msg->pos.GetWorldSpace(focusPos); + } + else if (msg->type == mRotateAround::TO) + { + float x, y; + msg->pos.GetScreenSpace(x, y); + + float rotX = 6.f * (y-lastY) / g_Renderer.GetHeight(); + float rotY = 6.f * (x-lastX) / g_Renderer.GetWidth(); + + // TODO: Stop the camera tilting + + CQuaternion q0, q1; + q0.FromAxisAngle(camera.GetLeft(), -rotX); + q1.FromAxisAngle(CVector3D(0.f, 1.f, 0.f), rotY); + CQuaternion q = q0*q1; + + CVector3D origin = camera.GetTranslation(); + CVector3D offset = q.Rotate(origin - focusPos); + + q *= camera.GetRotation(); + q.Normalize(); // to avoid things blowing up for some reason when turning upside-down + q.ToMatrix(camera); + camera.Translate(focusPos + offset); + + lastX = x; + lastY = y; + } + else + { + debug_warn("RotateAround: Invalid type"); + } +} + + } diff --git a/source/tools/atlas/GameInterface/InputProcessor.cpp b/source/tools/atlas/GameInterface/InputProcessor.cpp index 8e0d1732d4..998b50f451 100644 --- a/source/tools/atlas/GameInterface/InputProcessor.cpp +++ b/source/tools/atlas/GameInterface/InputProcessor.cpp @@ -5,6 +5,8 @@ #include "ps/Game.h" #include "graphics/Camera.h" +static float g_ViewZoomSmoothness = 0.02f; // TODO: configurable, like GameView + bool InputProcessor::ProcessInput(GameLoopState* state) { if (! g_Game) @@ -14,8 +16,10 @@ bool InputProcessor::ProcessInput(GameLoopState* state) CVector3D leftwards = camera->m_Orientation.GetLeft(); + CVector3D inwards = camera->m_Orientation.GetIn(); + // Calculate a vector pointing forwards, parallel to the ground - CVector3D forwards = camera->m_Orientation.GetIn(); + CVector3D forwards = inwards; 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); @@ -24,27 +28,41 @@ bool InputProcessor::ProcessInput(GameLoopState* state) bool moved = false; + GameLoopState::Input& input = state->input; + if (state->input.scrollSpeed[0] != 0.0f) { - camera->m_Orientation.Translate(forwards * (state->input.scrollSpeed[0] * state->frameLength)); + camera->m_Orientation.Translate(forwards * (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)); + camera->m_Orientation.Translate(forwards * (-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)); + camera->m_Orientation.Translate(leftwards * (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)); + camera->m_Orientation.Translate(leftwards * (-input.scrollSpeed[3] * state->frameLength)); + moved = true; + } + + if (state->input.zoomDelta != 0.0f) + { + float zoom_proportion = powf(g_ViewZoomSmoothness, state->frameLength); + camera->m_Orientation.Translate(inwards * (input.zoomDelta * (1.0f - zoom_proportion))); + input.zoomDelta *= zoom_proportion; + + if (fabsf(input.zoomDelta) < 0.1f) + input.zoomDelta = 0.0f; + moved = true; } diff --git a/source/tools/atlas/GameInterface/MessagePasser.h b/source/tools/atlas/GameInterface/MessagePasser.h index 71dfc7324a..5675fb7295 100644 --- a/source/tools/atlas/GameInterface/MessagePasser.h +++ b/source/tools/atlas/GameInterface/MessagePasser.h @@ -16,8 +16,8 @@ struct mInput; extern MessagePasser* g_MessagePasser_Command; extern MessagePasser* g_MessagePasser_Input; -#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) +#define POST_COMMAND(type) AtlasMessage::g_MessagePasser_Command->Add(new AtlasMessage::m##type) +#define POST_INPUT(type) AtlasMessage::g_MessagePasser_Input -> Add(new AtlasMessage::m##type) } diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h index 3083041c1f..17cbb12053 100644 --- a/source/tools/atlas/GameInterface/Messages.h +++ b/source/tools/atlas/GameInterface/Messages.h @@ -28,6 +28,7 @@ struct Position // Only for use in the game, not the UI. void GetWorldSpace(CVector3D& vec) const; // (implementation in Misc.cpp) + void GetScreenSpace(float& x, float& y) const; // (implementation in Misc.cpp) }; @@ -82,6 +83,10 @@ const bool NOMERGE = false; ////////////////////////////////////////////////////////////////////////// +// TODO: Make the command definitions less inelegant. + +////////////////////////////////////////////////////////////////////////// + COMMAND(CommandString) mCommandString(const std::string& name_) : name(name_) {} const std::string name; @@ -127,6 +132,25 @@ INPUT(ScrollConstant) enum { FORWARDS, BACKWARDS, LEFT, RIGHT }; }; +INPUT(Scroll) + mScroll(int type_, Position pos_) : type(type_), pos(pos_) {} + const int type; + const Position pos; + enum { FROM, TO }; +}; + +INPUT(SmoothZoom) + mSmoothZoom(float amount_) : amount(amount_) {} + const float amount; +}; + +INPUT(RotateAround) + mRotateAround(int type_, Position pos_) : type(type_), pos(pos_) {} + const int type; + const Position pos; + enum { FROM, TO }; +}; + ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// diff --git a/source/tools/atlas/GameInterface/Misc.cpp b/source/tools/atlas/GameInterface/Misc.cpp index 4c7f6c812f..992f202dc5 100644 --- a/source/tools/atlas/GameInterface/Misc.cpp +++ b/source/tools/atlas/GameInterface/Misc.cpp @@ -24,3 +24,22 @@ void AtlasMessage::Position::GetWorldSpace(CVector3D& vec) const vec.Set(0.f, 0.f, 0.f); } } + +void AtlasMessage::Position::GetScreenSpace(float& x, float& y) const +{ + switch (type) + { + case 0: + g_Game->GetView()->GetCamera()->GetScreenCoordinates(CVector3D(type0.x, type0.y, type0.x), x, y); + break; + + case 1: + x = type1.x; + y = type1.y; + break; + + default: + debug_warn("Invalid Position type"); + x = y = 0.f; + } +}