diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 1460dee7b..f8f4c64ab 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -115,7 +115,7 @@ add_component_dir (loadinglistener ) add_component_dir (myguiplatform - myguirendermanager myguidatamanager myguiplatform myguitexture myguiloglistener additivelayer + myguirendermanager myguidatamanager myguiplatform myguitexture myguiloglistener additivelayer scalinglayer ) add_component_dir (widgets diff --git a/components/myguiplatform/scalinglayer.cpp b/components/myguiplatform/scalinglayer.cpp new file mode 100644 index 000000000..7f7cac69b --- /dev/null +++ b/components/myguiplatform/scalinglayer.cpp @@ -0,0 +1,138 @@ +#include "scalinglayer.hpp" + +#include + +namespace osgMyGUI +{ + + /// @brief the ProxyRenderTarget allows to adjust the pixel scale and offset for a "source" render target. + class ProxyRenderTarget : public MyGUI::IRenderTarget + { + public: + /// @param target The target to render to. + /// @param viewSize The size of the underlying layer node to render. + /// @param hoffset The horizontal rendering offset, specified as an offset from the left screen edge in range 0-1. + /// @param voffset The vertical rendering offset, specified as an offset from the top screen edge in range 0-1. + ProxyRenderTarget(MyGUI::IRenderTarget* target, MyGUI::IntSize viewSize, float hoffset, float voffset) + : mTarget(target) + , mViewSize(viewSize) + , mHOffset(hoffset) + , mVOffset(voffset) + { + } + + virtual void begin() + { + mTarget->begin(); + } + + virtual void end() + { + mTarget->end(); + } + + virtual void doRender(MyGUI::IVertexBuffer* _buffer, MyGUI::ITexture* _texture, size_t _count) + { + mTarget->doRender(_buffer, _texture, _count); + } + + virtual const MyGUI::RenderTargetInfo& getInfo() + { + mInfo = mTarget->getInfo(); + mInfo.hOffset = mHOffset; + mInfo.vOffset = mVOffset; + mInfo.pixScaleX = 1.f / mViewSize.width; + mInfo.pixScaleY = 1.f / mViewSize.height; + return mInfo; + } + + private: + MyGUI::IRenderTarget* mTarget; + MyGUI::IntSize mViewSize; + float mHOffset, mVOffset; + MyGUI::RenderTargetInfo mInfo; + }; + + MyGUI::ILayerItem *ScalingLayer::getLayerItemByPoint(int _left, int _top) const + { + screenToLayerCoords(_left, _top); + + return OverlappedLayer::getLayerItemByPoint(_left, _top); + } + + void ScalingLayer::screenToLayerCoords(int& _left, int& _top) const + { + float scale = getScaleFactor(); + if (scale <= 0.f) + return; + + MyGUI::IntSize globalViewSize = MyGUI::RenderManager::getInstance().getViewSize(); + + _left -= globalViewSize.width/2; + _top -= globalViewSize.height/2; + + _left /= scale; + _top /= scale; + + _left += mViewSize.width/2; + _top += mViewSize.height/2; + } + + float ScalingLayer::getScaleFactor() const + { + MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); + float w = viewSize.width; + float h = viewSize.height; + + float heightScale = (h / mViewSize.height); + float widthScale = (w / mViewSize.width); + return std::min(widthScale, heightScale); + } + + MyGUI::IntPoint ScalingLayer::getPosition(int _left, int _top) const + { + screenToLayerCoords(_left, _top); + return MyGUI::IntPoint(_left, _top); + } + + void ScalingLayer::renderToTarget(MyGUI::IRenderTarget *_target, bool _update) + { + MyGUI::IntSize globalViewSize = MyGUI::RenderManager::getInstance().getViewSize(); + MyGUI::IntSize viewSize = globalViewSize; + viewSize.width /= getScaleFactor(); + viewSize.height /= getScaleFactor(); + + float hoffset = (globalViewSize.width - mViewSize.width*getScaleFactor())/2.f / static_cast(globalViewSize.width); + float voffset = (globalViewSize.height - mViewSize.height*getScaleFactor())/2.f / static_cast(globalViewSize.height); + + ProxyRenderTarget proxy(_target, viewSize, hoffset, voffset); + + MyGUI::OverlappedLayer::renderToTarget(&proxy, _update); + } + + void ScalingLayer::resizeView(const MyGUI::IntSize &_viewSize) + { + // do nothing + } + + void ScalingLayer::deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) + { + MyGUI::OverlappedLayer::deserialization(_node, _version); + + MyGUI::xml::ElementEnumerator info = _node->getElementEnumerator(); + while (info.next()) + { + if (info->getName() == "Property") + { + const std::string& key = info->findAttribute("key"); + const std::string& value = info->findAttribute("value"); + + if (key == "Size") + { + mViewSize = MyGUI::IntSize::parse(value); + } + } + } + } + +} diff --git a/components/myguiplatform/scalinglayer.hpp b/components/myguiplatform/scalinglayer.hpp new file mode 100644 index 000000000..3ee1489f1 --- /dev/null +++ b/components/myguiplatform/scalinglayer.hpp @@ -0,0 +1,31 @@ +#ifndef OPENMW_COMPONENTS_MYGUIPLATFORM_SCALINGLAYER +#define OPENMW_COMPONENTS_MYGUIPLATFORM_SCALINGLAYER + +#include + +namespace osgMyGUI +{ + + ///@brief A Layer that lays out and renders widgets in screen-relative coordinates. The "Size" property determines the size of the virtual screen, + /// which is then upscaled to the real screen size during rendering. The aspect ratio is kept intact, adding blanks to the sides when necessary. + class ScalingLayer : public MyGUI::OverlappedLayer + { + public: + MYGUI_RTTI_DERIVED(ScalingLayer) + + virtual void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version); + + virtual MyGUI::ILayerItem* getLayerItemByPoint(int _left, int _top) const; + virtual MyGUI::IntPoint getPosition(int _left, int _top) const; + virtual void renderToTarget(MyGUI::IRenderTarget* _target, bool _update); + + virtual void resizeView(const MyGUI::IntSize& _viewSize); + + private: + void screenToLayerCoords(int& _left, int& _top) const; + float getScaleFactor() const; + }; + +} + +#endif