From c470008104f9237cd0ba4be81d558db3f03ace3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Thu, 11 Jan 2018 03:09:49 -0300 Subject: [PATCH] Better views and viewport in ratio instead of pixels. --HG-- branch : dev-2.1 --- include/eepp/math/transform.hpp | 64 +++++++ include/eepp/window/input.hpp | 2 +- include/eepp/window/view.hpp | 67 +++---- include/eepp/window/window.hpp | 16 +- src/eepp/graphics/framebuffer.cpp | 4 +- src/eepp/math/transform.cpp | 190 ++++++++++++++++++++ src/eepp/window/backend/SDL2/windowsdl2.cpp | 10 +- src/eepp/window/backend/SFML/windowsfml.cpp | 6 +- src/eepp/window/input.cpp | 6 +- src/eepp/window/view.cpp | 181 ++++++++++++------- src/eepp/window/window.cpp | 72 ++++++-- src/test/eetest.cpp | 21 ++- 12 files changed, 497 insertions(+), 142 deletions(-) create mode 100644 include/eepp/math/transform.hpp create mode 100644 src/eepp/math/transform.cpp diff --git a/include/eepp/math/transform.hpp b/include/eepp/math/transform.hpp new file mode 100644 index 000000000..80c9790ee --- /dev/null +++ b/include/eepp/math/transform.hpp @@ -0,0 +1,64 @@ +#ifndef SFML_TRANSFORM_HPP +#define SFML_TRANSFORM_HPP + +#include +#include +#include + +namespace EE { namespace Math { + +class EE_API Transform { + public: + Transform(); + + Transform(float a00, float a01, float a02, float a10, float a11, float a12, float a20, float a21, float a22 ); + + const float* getMatrix() const; + + Transform getInverse() const; + + Vector2f transformPoint(float x, float y) const; + + Vector2f transformPoint(const Vector2f& point) const; + + Rectf transformRect(const Rectf& rectangle) const; + + Transform& combine(const Transform& transform); + + Transform& translate(float x, float y); + + Transform& translate(const Vector2f& offset); + + Transform& rotate(float angle); + + Transform& rotate(float angle, float centerX, float centerY); + + Transform& rotate(float angle, const Vector2f& center); + + Transform& scale(float scaleX, float scaleY); + + Transform& scale(float scaleX, float scaleY, float centerX, float centerY); + + Transform& scale(const Vector2f& factors); + + Transform& scale(const Vector2f& factors, const Vector2f& center); + + static const Transform Identity; + private: + + float mMatrix[16]; +}; + +EE_API Transform operator *(const Transform& left, const Transform& right); + +EE_API Transform& operator *=(Transform& left, const Transform& right); + +EE_API Vector2f operator *(const Transform& left, const Vector2f& right); + +EE_API bool operator ==(const Transform& left, const Transform& right); + +EE_API bool operator !=(const Transform& left, const Transform& right); + +}} + +#endif diff --git a/include/eepp/window/input.hpp b/include/eepp/window/input.hpp index c5b5e55e7..2bd1b7ab2 100644 --- a/include/eepp/window/input.hpp +++ b/include/eepp/window/input.hpp @@ -116,7 +116,7 @@ class EE_API Input { void setMousePos( const Vector2i& Pos ); /** @return The mouse position over the current view */ - Vector2i getMousePosFromView( const View& View ); + Vector2f getMousePosFromView( const View& View ); /** Set the mouse speed ( only affects grabed windows ) */ void setMouseSpeed( const Float& Speed ); diff --git a/include/eepp/window/view.hpp b/include/eepp/window/view.hpp index e2ba49fc7..2f86f69e3 100755 --- a/include/eepp/window/view.hpp +++ b/include/eepp/window/view.hpp @@ -1,7 +1,9 @@ #ifndef EE_WINDOWCVIEW_H #define EE_WINDOWCVIEW_H -#include +#include +#include +using namespace EE::Math; namespace EE { namespace Window { @@ -10,51 +12,54 @@ class EE_API View { public: View(); - View( const int& X, const int& Y, const int& Width, const int& Height ); + explicit View(const Rectf& rectangle); - View( const Rect& View ); + View(const Vector2f& center, const Vector2f& size); - ~View(); + void setCenter(float x, float y); - /** Offset the position */ - void move( const int& OffsetX, const int& OffsetY ); + void setCenter(const Vector2f& center); - /** Offset the position */ - void move( const Vector2i& Offset ); + void setSize(float width, float height); - /** Scale the current view (from center) */ - void scale( const Float& Factor ); + void setSize(const Sizef& size); - /** Scale the current view (from center) */ - void scale( const Vector2f& Factor ); + void setRotation(float angle); - /** @return The center position of the view */ - Vector2i getCenter() const; + void setViewport(const Rectf& viewport); - /** Set the center position of the view ( will move it if is needed ) */ - void setCenter( const Vector2i& center ); + void reset(const Rectf& rectangle); - /** @return The current view ( Left = X, Right = Width, Top = Y, Bottom = Height ) */ - Rect getView() const { return mView; } + const Vector2f& getCenter() const; - /** Set a new position to the view */ - void setPosition( const int& X, const int& Y ); + const Sizef & getSize() const; - /** Set a new size to the view */ - void setSize( const int& Width, const int& Height ); + float getRotation() const; - /** Creates a new view */ - void setView( const int& X, const int& Y, const int& Width, const int& Height ); + const Rectf& getViewport() const; + + void move(float offsetX, float offsetY); + + void move(const Vector2f& offset); + + void rotate(float angle); + + void zoom(float factor); + + const Transform& getTransform() const; + + const Transform& getInverseTransform() const; private: friend class Window; - bool mNeedUpdate; - Rect mView; - Vector2f mCenter; - - void calcCenter(); - - bool needUpdate() const; + Vector2f mCenter; ///< Center of the view, in scene coordinates + Sizef mSize; ///< Size of the view, in scene coordinates + float mRotation; ///< Angle of rotation of the view rectangle, in degrees + Rectf mViewport; ///< Viewport rectangle, expressed as a factor of the render-target's size + mutable Transform mTransform; ///< Precomputed projection transform corresponding to the view + mutable Transform mInverseTransform; ///< Precomputed inverse projection transform corresponding to the view + mutable bool mTransformUpdated; ///< Internal state telling if the transform needs to be updated + mutable bool mInvTransformUpdated; ///< Internal state telling if the inverse transform needs to be updated }; }} diff --git a/include/eepp/window/window.hpp b/include/eepp/window/window.hpp index 59671e51b..fa2325c66 100644 --- a/include/eepp/window/window.hpp +++ b/include/eepp/window/window.hpp @@ -295,9 +295,15 @@ class EE_API Window { /** Set a new 2D projection matrix */ void set2DProjection( const Uint32& Width, const Uint32& Height ); + /** Set a new projection matrix */ + void setProjection( const Transform& transform ); + /** Set the current Viewport ( and creates a new ortho proyection if needed ) */ void setViewport( const Int32& x, const Int32& y, const Uint32& Width, const Uint32& Height ); + /** @return The viewport in pixels of the view */ + Rect getViewport(const View& view); + /** Set the window background color */ void setClearColor( const RGB& Color ); @@ -407,6 +413,14 @@ class EE_API Window { ** @param func The main loop function ** @param fps The desired FPS ( 0 = infinite ) */ void runMainLoop( void (*func)(), int fps = 0 ); + + Vector2f mapPixelToCoords(const Vector2i & point); + + Vector2f mapPixelToCoords(const Vector2i & point, const View & view); + + Vector2i mapCoordsToPixel(const Vector2f & point); + + Vector2i mapCoordsToPixel(const Vector2f & point, const View & view); protected: friend class Engine; friend class Input; @@ -474,8 +488,6 @@ class EE_API Window { void getElapsedTime(); - void viewCheckUpdate(); - void logSuccessfulInit( const std::string& BackendName ); void logFailureInit( const std::string& ClassName, const std::string& BackendName ); diff --git a/src/eepp/graphics/framebuffer.cpp b/src/eepp/graphics/framebuffer.cpp index 7e352ebd8..242cb1380 100644 --- a/src/eepp/graphics/framebuffer.cpp +++ b/src/eepp/graphics/framebuffer.cpp @@ -65,7 +65,7 @@ void FrameBuffer::setBufferView() { GLi->getCurrentMatrix( GL_PROJECTION_MATRIX, mProjMat ); GLi->getCurrentMatrix( GL_MODELVIEW_MATRIX, mModelViewMat ); - mView.setSize( mSize.getWidth(), mSize.getHeight() ); + mView.reset( Rectf( 0, 0, mSize.getWidth(), mSize.getHeight() ) ); sFBOActiveViews.push_back(&mView); GLi->viewport( 0, 0, mSize.getWidth(), mSize.getHeight() ); @@ -85,7 +85,7 @@ void FrameBuffer::recoverView() { mWindow->setView( mWindow->getView() ); } else { const View* view = sFBOActiveViews.back(); - GLi->viewport( 0, 0, view->getView().getWidth(), view->getView().getHeight() ); + GLi->viewport( 0, 0, view->getSize().getWidth(), view->getSize().getHeight() ); } // Recover the user projection and modelview matrix diff --git a/src/eepp/math/transform.cpp b/src/eepp/math/transform.cpp new file mode 100644 index 000000000..b931854b0 --- /dev/null +++ b/src/eepp/math/transform.cpp @@ -0,0 +1,190 @@ +#include +#include + +namespace EE { namespace Math { + +const Transform Transform::Identity; + +Transform::Transform() { + // Identity matrix + mMatrix[0] = 1.f; mMatrix[4] = 0.f; mMatrix[8] = 0.f; mMatrix[12] = 0.f; + mMatrix[1] = 0.f; mMatrix[5] = 1.f; mMatrix[9] = 0.f; mMatrix[13] = 0.f; + mMatrix[2] = 0.f; mMatrix[6] = 0.f; mMatrix[10] = 1.f; mMatrix[14] = 0.f; + mMatrix[3] = 0.f; mMatrix[7] = 0.f; mMatrix[11] = 0.f; mMatrix[15] = 1.f; +} + +Transform::Transform( float a00, float a01, float a02, float a10, float a11, float a12, float a20, float a21, float a22 ) { + mMatrix[0] = a00; mMatrix[4] = a01; mMatrix[8] = 0.f; mMatrix[12] = a02; + mMatrix[1] = a10; mMatrix[5] = a11; mMatrix[9] = 0.f; mMatrix[13] = a12; + mMatrix[2] = 0.f; mMatrix[6] = 0.f; mMatrix[10] = 1.f; mMatrix[14] = 0.f; + mMatrix[3] = a20; mMatrix[7] = a21; mMatrix[11] = 0.f; mMatrix[15] = a22; +} + +const float* Transform::getMatrix() const { + return mMatrix; +} + +Transform Transform::getInverse() const { + // Compute the determinant + float det = mMatrix[0] * (mMatrix[15] * mMatrix[5] - mMatrix[7] * mMatrix[13]) - + mMatrix[1] * (mMatrix[15] * mMatrix[4] - mMatrix[7] * mMatrix[12]) + + mMatrix[3] * (mMatrix[13] * mMatrix[4] - mMatrix[5] * mMatrix[12]); + + // Compute the inverse if the determinant is not zero + // (don't use an epsilon because the determinant may *really* be tiny) + if (det != 0.f) { + return Transform( (mMatrix[15] * mMatrix[5] - mMatrix[7] * mMatrix[13]) / det, + -(mMatrix[15] * mMatrix[4] - mMatrix[7] * mMatrix[12]) / det, + (mMatrix[13] * mMatrix[4] - mMatrix[5] * mMatrix[12]) / det, + -(mMatrix[15] * mMatrix[1] - mMatrix[3] * mMatrix[13]) / det, + (mMatrix[15] * mMatrix[0] - mMatrix[3] * mMatrix[12]) / det, + -(mMatrix[13] * mMatrix[0] - mMatrix[1] * mMatrix[12]) / det, + (mMatrix[7] * mMatrix[1] - mMatrix[3] * mMatrix[5]) / det, + -(mMatrix[7] * mMatrix[0] - mMatrix[3] * mMatrix[4]) / det, + (mMatrix[5] * mMatrix[0] - mMatrix[1] * mMatrix[4]) / det); + } else { + return Identity; + } +} + +Vector2f Transform::transformPoint(float x, float y) const { + return Vector2f(mMatrix[0] * x + mMatrix[4] * y + mMatrix[12], + mMatrix[1] * x + mMatrix[5] * y + mMatrix[13]); +} + +Vector2f Transform::transformPoint(const Vector2f& point) const { + return transformPoint(point.x, point.y); +} + +Rectf Transform::transformRect(const Rectf& rectangle) const { + // Transform the 4 corners of the rectangle + const Vector2f points[] = + { + transformPoint(rectangle.Left, rectangle.Top), + transformPoint(rectangle.Left, rectangle.Top + rectangle.Bottom), + transformPoint(rectangle.Left + rectangle.Right, rectangle.Top), + transformPoint(rectangle.Left + rectangle.Right, rectangle.Top + rectangle.Bottom) + }; + + // Compute the bounding rectangle of the transformed points + float left = points[0].x; + float top = points[0].y; + float right = points[0].x; + float bottom = points[0].y; + for (int i = 1; i < 4; ++i) { + if (points[i].x < left) left = points[i].x; + else if (points[i].x > right) right = points[i].x; + if (points[i].y < top) top = points[i].y; + else if (points[i].y > bottom) bottom = points[i].y; + } + + return Rectf(left, top, right - left, bottom - top); +} + +Transform& Transform::combine(const Transform& transform) { + const float* a = mMatrix; + const float* b = transform.mMatrix; + + *this = Transform(a[0] * b[0] + a[4] * b[1] + a[12] * b[3], + a[0] * b[4] + a[4] * b[5] + a[12] * b[7], + a[0] * b[12] + a[4] * b[13] + a[12] * b[15], + a[1] * b[0] + a[5] * b[1] + a[13] * b[3], + a[1] * b[4] + a[5] * b[5] + a[13] * b[7], + a[1] * b[12] + a[5] * b[13] + a[13] * b[15], + a[3] * b[0] + a[7] * b[1] + a[15] * b[3], + a[3] * b[4] + a[7] * b[5] + a[15] * b[7], + a[3] * b[12] + a[7] * b[13] + a[15] * b[15]); + + return *this; +} + +Transform& Transform::translate(float x, float y) { + Transform translation(1, 0, x, + 0, 1, y, + 0, 0, 1); + + return combine(translation); +} + +Transform& Transform::translate(const Vector2f& offset) { + return translate(offset.x, offset.y); +} + +Transform& Transform::rotate(float angle) { + float rad = angle * 3.141592654f / 180.f; + float cos = std::cos(rad); + float sin = std::sin(rad); + + Transform rotation(cos, -sin, 0, + sin, cos, 0, + 0, 0, 1); + + return combine(rotation); +} + +Transform& Transform::rotate(float angle, float centerX, float centerY) { + float rad = angle * 3.141592654f / 180.f; + float cos = std::cos(rad); + float sin = std::sin(rad); + + Transform rotation(cos, -sin, centerX * (1 - cos) + centerY * sin, + sin, cos, centerY * (1 - cos) - centerX * sin, + 0, 0, 1); + + return combine(rotation); +} + +Transform& Transform::rotate(float angle, const Vector2f& center) { + return rotate(angle, center.x, center.y); +} + +Transform& Transform::scale(float scaleX, float scaleY) { + Transform scaling(scaleX, 0, 0, + 0, scaleY, 0, + 0, 0, 1); + + return combine(scaling); +} + +Transform& Transform::scale(float scaleX, float scaleY, float centerX, float centerY) { + Transform scaling(scaleX, 0, centerX * (1 - scaleX), + 0, scaleY, centerY * (1 - scaleY), + 0, 0, 1); + + return combine(scaling); +} + +Transform& Transform::scale(const Vector2f& factors) { + return scale(factors.x, factors.y); +} + +Transform& Transform::scale(const Vector2f& factors, const Vector2f& center) { + return scale(factors.x, factors.y, center.x, center.y); +} + +Transform operator *(const Transform& left, const Transform& right) { + return Transform(left).combine(right); +} + +Transform& operator *=(Transform& left, const Transform& right) { + return left.combine(right); +} + +Vector2f operator *(const Transform& left, const Vector2f& right) { + return left.transformPoint(right); +} + +bool operator ==(const Transform& left, const Transform& right) { + const float* a = left.getMatrix(); + const float* b = right.getMatrix(); + + return ((a[0] == b[0]) && (a[1] == b[1]) && (a[3] == b[3]) && + (a[4] == b[4]) && (a[5] == b[5]) && (a[7] == b[7]) && + (a[12] == b[12]) && (a[13] == b[13]) && (a[15] == b[15])); +} + +bool operator !=(const Transform& left, const Transform& right) { + return !(left == right); +} + +}} diff --git a/src/eepp/window/backend/SDL2/windowsdl2.cpp b/src/eepp/window/backend/SDL2/windowsdl2.cpp index a322ee1e8..0d635be21 100644 --- a/src/eepp/window/backend/SDL2/windowsdl2.cpp +++ b/src/eepp/window/backend/SDL2/windowsdl2.cpp @@ -214,7 +214,7 @@ bool WindowSDL::create( WindowSettings Settings, ContextSettings Context ) { createView(); - setup2D(); + setup2D( false ); mWindow.Created = true; @@ -349,9 +349,9 @@ void WindowSDL::onWindowResize( Uint32 Width, Uint32 Height ) { mWindow.WindowConfig.Height = Height; mWindow.WindowSize = Sizei( Width, Height ); - mDefaultView.setView( 0, 0, Width, Height ); + mDefaultView.reset( Rectf( 0, 0, Width, Height ) ); - setup2D(); + setup2D( false ); SDL_PumpEvents(); @@ -407,9 +407,7 @@ void WindowSDL::setSize( Uint32 Width, Uint32 Height, bool Windowed ) { BitOp::setBitFlagValue( &mWindow.WindowConfig.Style, WindowStyle::Fullscreen, !Windowed ); - mDefaultView.setView( 0, 0, Width, Height ); - - setup2D(); + setup2D( false ); SDL_PumpEvents(); diff --git a/src/eepp/window/backend/SFML/windowsfml.cpp b/src/eepp/window/backend/SFML/windowsfml.cpp index 29cd70f0c..096c07e0c 100644 --- a/src/eepp/window/backend/SFML/windowsfml.cpp +++ b/src/eepp/window/backend/SFML/windowsfml.cpp @@ -79,7 +79,7 @@ bool WindowSFML::create( WindowSettings Settings, ContextSettings Context ) { createView(); - setup2D(); + setup2D( false ); mWindow.Created = true; mVisible = true; @@ -183,9 +183,9 @@ void WindowSFML::videoResize( Uint32 Width, Uint32 Height ) { mWindow.WindowConfig.Width = Width; mWindow.WindowConfig.Height = Height; - mDefaultView.setView( 0, 0, Width, Height ); + mDefaultView.reset( Rectf( 0, 0, Width, Height ) ); - setup2D(); + setup2D( false ); mCursorManager->reload(); diff --git a/src/eepp/window/input.cpp b/src/eepp/window/input.cpp index 16f178b5c..029647bbb 100644 --- a/src/eepp/window/input.cpp +++ b/src/eepp/window/input.cpp @@ -286,10 +286,8 @@ Vector2f Input::getMousePosf() { return Vector2f( (Float)mMousePos.x, (Float)mMousePos.y ); } -Vector2i Input::getMousePosFromView( const View& View ) { - Vector2i RealMousePos = getMousePos(); - Rect RView = View.getView(); - return Vector2i( RealMousePos.x - RView.Left, RealMousePos.y - RView.Top ); +Vector2f Input::getMousePosFromView( const View& View ) { + return mWindow->mapPixelToCoords( getMousePos(), View ); } Uint32 Input::pushCallback( const InputCallback& cb ) { diff --git a/src/eepp/window/view.cpp b/src/eepp/window/view.cpp index ed0379470..ca29151e9 100755 --- a/src/eepp/window/view.cpp +++ b/src/eepp/window/view.cpp @@ -2,106 +2,151 @@ namespace EE { namespace Window { -View::View() : mNeedUpdate(true) { - mView.Left = 0; - mView.Right = 0; - mView.Top = 0; - mView.Bottom = 0; - - calcCenter(); +View::View() : + mCenter (), + mSize (), + mRotation (0), + mViewport (0, 0, 1, 1), + mTransformUpdated (false), + mInvTransformUpdated(false) +{ + reset(Rectf(0, 0, 1000, 1000)); } -View::View( const int& X, const int& Y, const int& Width, const int& Height ) : mNeedUpdate(true) { - setView( X, Y, Width, Height ); +View::View(const Rectf& rectangle) : + mCenter (), + mSize (), + mRotation (0), + mViewport (0, 0, 1, 1), + mTransformUpdated (false), + mInvTransformUpdated(false) +{ + reset(rectangle); } -View::View( const Rect& View ) : mNeedUpdate(true) { - mView = View; +View::View(const Vector2f& center, const Vector2f& size) : + mCenter (center), + mSize (size), + mRotation (0), + mViewport (0, 0, 1, 1), + mTransformUpdated (false), + mInvTransformUpdated(false) +{} - calcCenter(); +void View::setCenter(float x, float y) { + mCenter.x = x; + mCenter.y = y; + + mTransformUpdated = false; + mInvTransformUpdated = false; } -View::~View() { +void View::setCenter(const Vector2f& center) { + setCenter(center.x, center.y); } -void View::setView( const int& X, const int& Y, const int& Width, const int& Height ) { - mView.Left = X; - mView.Top = Y; - mView.Right = Width; - mView.Bottom = Height; - calcCenter(); - mNeedUpdate = true; +void View::setSize(float width, float height) { + mSize.x = width; + mSize.y = height; + + mTransformUpdated = false; + mInvTransformUpdated = false; } -void View::calcCenter() { - mCenter.x = ( ( mView.Left + mView.Right ) - mView.Left ) * 0.5f; - mCenter.y = ( ( mView.Top + mView.Bottom ) - mView.Top ) * 0.5f; +void View::setSize(const Sizef & size) { + setSize(size.x, size.y); } -Vector2i View::getCenter() const { - return Vector2i( (int)mCenter.x, (Int32)mCenter.y ); +void View::setRotation(float angle) { + mRotation = static_cast(fmod(angle, 360)); + if (mRotation < 0) + mRotation += 360.f; + + mTransformUpdated = false; + mInvTransformUpdated = false; } -void View::setCenter( const Vector2i& Center ) { - mCenter.x = (Float)Center.x; - mCenter.y = (Float)Center.y; - mView.Left = static_cast ( mCenter.x - (Float)mView.Right * 0.5f ); - mView.Top = static_cast ( mCenter.y - (Float)mView.Bottom * 0.5f ); - - mNeedUpdate = true; +void View::setViewport(const Rectf& viewport) { + mViewport = viewport; } -void View::move( const int& OffsetX, const int& OffsetY ) { - mView.Left += OffsetX; - mView.Top += OffsetY; +void View::reset(const Rectf& rectangle) { + mCenter.x = rectangle.Left + rectangle.Right / 2.f; + mCenter.y = rectangle.Top + rectangle.Bottom / 2.f; + mSize.x = rectangle.Right; + mSize.y = rectangle.Bottom; + mRotation = 0; - calcCenter(); - mNeedUpdate = true; + mTransformUpdated = false; + mInvTransformUpdated = false; } -void View::move( const Vector2i& Offset ) { - move( Offset.x, Offset.y ); +const Vector2f& View::getCenter() const { + return mCenter; } -void View::scale( const Vector2f& Factor ) { - Vector2f v( mView.Right * 0.5f, mView.Bottom * 0.5f ); - - mView.Left = mView.Left + static_cast ( v.x - v.x * Factor.x ); - mView.Top = mView.Top + static_cast ( v.y - v.y * Factor.y ); - mView.Right = static_cast( (Float)mView.Right * Factor.x ); - mView.Bottom = static_cast( (Float)mView.Bottom * Factor.y ); - - calcCenter(); - mNeedUpdate = true; +const Sizef& View::getSize() const { + return mSize; } -void View::scale( const Float& Factor ) { - scale( Vector2f( Factor, Factor ) ); +float View::getRotation() const { + return mRotation; } -void View::setPosition( const int& X, const int& Y ) { - mView.Left = X; - mView.Top = Y; - - calcCenter(); - mNeedUpdate = true; +const Rectf& View::getViewport() const { + return mViewport; } -void View::setSize( const int& Width, const int& Height ) { - mView.Right = Width; - mView.Bottom = Height; - - calcCenter(); - mNeedUpdate = true; +void View::move(float offsetX, float offsetY) { + setCenter(mCenter.x + offsetX, mCenter.y + offsetY); } -bool View::needUpdate() const { - bool Need = mNeedUpdate; +void View::move(const Vector2f& offset) { + setCenter(mCenter + offset); +} - if ( Need ) - const_cast(this)->mNeedUpdate = false; +void View::rotate(float angle) { + setRotation(mRotation + angle); +} - return Need; +void View::zoom(float factor) { + setSize(mSize.x * factor, mSize.y * factor); +} + +const Transform& View::getTransform() const { + // Recompute the matrix if needed + if (!mTransformUpdated) { + // Rotation components + float angle = mRotation * 3.141592654f / 180.f; + float cosine = static_cast(std::cos(angle)); + float sine = static_cast(std::sin(angle)); + float tx = -mCenter.x * cosine - mCenter.y * sine + mCenter.x; + float ty = mCenter.x * sine - mCenter.y * cosine + mCenter.y; + + // Projection components + float a = 2.f / mSize.x; + float b = -2.f / mSize.y; + float c = -a * mCenter.x; + float d = -b * mCenter.y; + + // Rebuild the projection matrix + mTransform = Transform( a * cosine, a * sine, a * tx + c, + -b * sine, b * cosine, b * ty + d, + 0.f, 0.f, 1.f); + mTransformUpdated = true; + } + + return mTransform; +} + +const Transform& View::getInverseTransform() const { + // Recompute the matrix if needed + if (!mInvTransformUpdated) { + mInverseTransform = getTransform().getInverse(); + mInvTransformUpdated = true; + } + + return mInverseTransform; } }} diff --git a/src/eepp/window/window.cpp b/src/eepp/window/window.cpp index f3da23da5..d37577d81 100644 --- a/src/eepp/window/window.cpp +++ b/src/eepp/window/window.cpp @@ -104,18 +104,69 @@ void Window::set2DProjection( const Uint32& Width, const Uint32& Height ) { GLi->loadIdentity(); } -void Window::setViewport(const Int32& x, const Int32& y, const Uint32& Width, const Uint32& Height ) { +void Window::setProjection(const Transform & transform) { + GLi->matrixMode( GL_PROJECTION ); + GLi->loadMatrixf( transform.getMatrix() ); + + GLi->matrixMode( GL_MODELVIEW ); + GLi->loadIdentity(); +} + +Vector2f Window::mapPixelToCoords(const Vector2i& point) { + return mapPixelToCoords(point, getView()); +} + +Vector2f Window::mapPixelToCoords(const Vector2i& point, const View& view) { + // First, convert from viewport coordinates to homogeneous coordinates + Vector2f normalized; + Rect viewport = getViewport(view); + normalized.x = -1.f + 2.f * (point.x - viewport.Left) / viewport.Right; + normalized.y = 1.f - 2.f * (point.y - viewport.Top) / viewport.Bottom; + + // Then transform by the inverse of the view matrix + return view.getInverseTransform().transformPoint(normalized); +} + +Vector2i Window::mapCoordsToPixel(const Vector2f& point) { + return mapCoordsToPixel(point, getView()); +} + +Vector2i Window::mapCoordsToPixel(const Vector2f& point, const View& view) { + // First, transform the point by the view matrix + Vector2f normalized = view.getTransform().transformPoint(point); + + // Then convert to viewport coordinates + Vector2i pixel; + Rect viewport = getViewport(view); + pixel.x = static_cast(( normalized.x + 1.f) / 2.f * viewport.Right + viewport.Left); + pixel.y = static_cast((-normalized.y + 1.f) / 2.f * viewport.Bottom + viewport.Top); + + return pixel; +} + +void Window::setViewport( const Int32& x, const Int32& y, const Uint32& Width, const Uint32& Height ) { GLi->viewport( x, getHeight() - ( y + Height ), Width, Height ); } +Rect Window::getViewport( const View& view ) { + float width = static_cast(getSize().getWidth()); + float height = static_cast(getSize().getHeight()); + const Rectf& viewport = view.getViewport(); + + return Rect( static_cast(0.5f + width * viewport.Left), + static_cast(0.5f + height * viewport.Top), + static_cast(0.5f + width * viewport.Right), + static_cast(0.5f + height * viewport.Bottom)); +} + void Window::setView( const View& View ) { mCurrentView = &View; - Rect RView = mCurrentView->getView(); + Rect viewport = getViewport( *mCurrentView ); - setViewport( RView.Left, RView.Top, RView.Right, RView.Bottom ); + setViewport( viewport.Left, viewport.Top, viewport.Right, viewport.Bottom ); - set2DProjection( RView.Right, RView.Bottom ); + setProjection( View.getTransform() ); } const View& Window::getDefaultView() const { @@ -127,7 +178,7 @@ const View& Window::getView() const { } void Window::createView() { - mDefaultView.setView( 0, 0, mWindow.WindowConfig.Width, mWindow.WindowConfig.Height ); + mDefaultView.reset( Rectf( 0, 0, mWindow.WindowConfig.Width, mWindow.WindowConfig.Height ) ); mCurrentView = &mDefaultView; } @@ -148,8 +199,6 @@ void Window::setup2D( const bool& KeepView ) { if ( !KeepView ) { setView( mDefaultView ); - - mCurrentView->needUpdate(); } BlendMode::setMode( BlendAlpha, true ); @@ -298,12 +347,6 @@ void Window::limitFps() { } } -void Window::viewCheckUpdate() { - if ( mCurrentView->needUpdate() ) { - setView( *mCurrentView ); - } -} - void Window::clear() { GLi->clear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); } @@ -311,9 +354,6 @@ void Window::clear() { void Window::display( bool clear ) { GlobalBatchRenderer::instance()->draw(); - if ( mCurrentView->needUpdate() ) - setView( *mCurrentView ); - swapBuffers(); #if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN diff --git a/src/test/eetest.cpp b/src/test/eetest.cpp index 57267629b..eb4829d0a 100644 --- a/src/test/eetest.cpp +++ b/src/test/eetest.cpp @@ -1642,19 +1642,22 @@ void EETest::render() { if ( !MultiViewportMode ) { Scenes[ Screen ](); } else { - Views[0].setView( 0, 0, mWindow->getWidth(), static_cast( HHeight ) ); - Views[1].setView( 0, static_cast ( HHeight ), mWindow->getWidth(), static_cast( HHeight ) ); - - mWindow->setView( Views[1] ); - Mouse = KM->getMousePosFromView( Views[1] ); - Mousef = Vector2f( (Float)Mouse.x, (Float)Mouse.y ); - screen2(); + Views[0].reset( Rectf( 0, 0, mWindow->getWidth(), HHeight ) ); + Views[0].setViewport( Rectf( 0, 0, 1, 0.5f ) ); mWindow->setView( Views[0] ); - Mouse = KM->getMousePosFromView( Views[0] ); - Mousef = Vector2f( (Float)Mouse.x, (Float)Mouse.y ); + Mousef = KM->getMousePosFromView( Views[0] ); + Mouse = Vector2i( Mousef.x, Mousef.y ); screen1(); + Views[1].reset( Rectf( 0, 0, mWindow->getWidth(), HHeight ) ); + Views[1].setViewport( Rectf( 0, 0.5f, 1, 0.5f ) ); + + mWindow->setView( Views[1] ); + Mousef = KM->getMousePosFromView( Views[1] ); + Mouse = Vector2i( Mousef.x, Mousef.y ); + screen2(); + mWindow->setView( mWindow->getDefaultView() ); GLi->getClippingMask()->clipEnable( (Int32)HWidth - 320, (Int32)HHeight - 240, 640, 480 ); screen3();