From 61bfb22f27f5216f989c023a5e39fad7e356d2d6 Mon Sep 17 00:00:00 2001 From: Piotr Dziwinski Date: Fri, 3 Aug 2012 23:23:13 +0200 Subject: Basic font rendering - added basic font rendering - minor refactoring & fixes --- src/CMakeLists.txt | 1 + src/app/app.cpp | 54 ++--- src/app/app.h | 6 - src/graphics/core/device.h | 9 +- src/graphics/engine/engine.cpp | 148 +++++++----- src/graphics/engine/engine.h | 31 ++- src/graphics/engine/text.cpp | 477 ++++++++++++++++++++++++++++++++++++++- src/graphics/engine/text.h | 267 ++++++++++++++++++---- src/graphics/opengl/gldevice.cpp | 39 ++-- src/graphics/opengl/gldevice.h | 5 +- src/math/const.h | 11 +- src/math/func.h | 8 + src/math/geometry.h | 4 +- src/math/intsize.h | 15 +- src/math/size.h | 9 + 15 files changed, 895 insertions(+), 189 deletions(-) (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9bcd288..25a576e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -176,6 +176,7 @@ graphics/opengl/gldevice.cpp set(LIBS ${SDL_LIBRARY} ${SDLIMAGE_LIBRARY} +${SDLTTF_LIBRARY} ${OPENGL_LIBRARY} ${PNG_LIBRARIES} ${OPTIONAL_LIBS} diff --git a/src/app/app.cpp b/src/app/app.cpp index d20232d..3681172 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -122,6 +122,7 @@ bool CApplication::ParseArguments(int argc, char *argv[]) { waitDataDir = false; m_dataPath = arg; + continue; } if (arg == "-debug") @@ -153,10 +154,6 @@ bool CApplication::Create() // Temporarily -- only in windowed mode m_deviceConfig.fullScreen = false; - // Create the 3D engine - m_engine = new Gfx::CEngine(m_iMan, this); - - /* // Create the sound instance. m_sound = new CSound(m_iMan); @@ -224,20 +221,15 @@ bool CApplication::Create() return false; } + // Create the 3D engine + m_engine = new Gfx::CEngine(m_iMan, this); + m_engine->SetDevice(m_device); - if (! m_engine->Create() ) - { - SystemDialog( SDT_ERROR, "COLOBT - Fatal Error", - std::string("Error in CEngine::Create() :\n") + - std::string(m_engine->GetError()) ); - m_exitCode = 1; - return false; - } - if (! m_engine->AfterDeviceSetInit() ) + if (! m_engine->Create() ) { SystemDialog( SDT_ERROR, "COLOBT - Fatal Error", - std::string("Error in CEngine::AfterDeviceSetInit() :\n") + + std::string("Error in CEngine::Init() :\n") + std::string(m_engine->GetError()) ); m_exitCode = 1; return false; @@ -315,8 +307,7 @@ void CApplication::Destroy() if (m_engine != NULL) { - if (m_engine->GetWasInit()) - m_engine->Destroy(); + m_engine->Destroy(); delete m_engine; m_engine = NULL; @@ -324,8 +315,7 @@ void CApplication::Destroy() if (m_device != NULL) { - if (m_device->GetWasInit()) - m_device->Destroy(); + m_device->Destroy(); delete m_device; m_device = NULL; @@ -616,21 +606,6 @@ PressState TranslatePressState(unsigned char state) return STATE_RELEASED; } -/** Conversion of the position of the mouse from window coords to interface coords: - - x: 0=left, 1=right - - y: 0=down, 1=up */ -Math::Point CApplication::WindowToInterfaceCoords(Math::IntPoint pos) -{ - return Math::Point( static_cast(pos.x) / static_cast(m_deviceConfig.size.w), - 1.0f - static_cast(pos.y) / static_cast(m_deviceConfig.size.h) ); -} - -Math::IntPoint CApplication::InterfaceToWindowCoords(Math::Point pos) -{ - return Math::IntPoint(static_cast(pos.x * m_deviceConfig.size.w), - static_cast((1.0f - pos.y) * m_deviceConfig.size.h)); -} - /** The SDL event parsed is stored internally. If event is not available or is not understood, returned event is of type EVENT_NULL. */ Event CApplication::ParseEvent() @@ -666,14 +641,16 @@ Event CApplication::ParseEvent() event.mouseButton.button = m_private->currentEvent.button.button; event.mouseButton.state = TranslatePressState(m_private->currentEvent.button.state); - event.mouseButton.pos = WindowToInterfaceCoords(Math::IntPoint(m_private->currentEvent.button.x, m_private->currentEvent.button.y)); + event.mouseButton.pos = m_engine->WindowToInterfaceCoords( + Math::IntPoint(m_private->currentEvent.button.x, m_private->currentEvent.button.y)); } else if (m_private->currentEvent.type == SDL_MOUSEMOTION) { event.type = EVENT_MOUSE_MOVE; event.mouseMove.state = TranslatePressState(m_private->currentEvent.button.state); - event.mouseMove.pos = WindowToInterfaceCoords(Math::IntPoint(m_private->currentEvent.button.x, m_private->currentEvent.button.y)); + event.mouseMove.pos = m_engine->WindowToInterfaceCoords( + Math::IntPoint(m_private->currentEvent.button.x, m_private->currentEvent.button.y)); } else if (m_private->currentEvent.type == SDL_JOYAXISMOTION) { @@ -792,6 +769,11 @@ void CApplication::StepSimulation(float rTime) // TODO } +Gfx::GLDeviceConfig CApplication::GetVideoConfig() +{ + return m_deviceConfig; +} + VideoQueryResult CApplication::GetVideoResolutionList(std::vector &resolutions, bool fullScreen, bool resizeable) { @@ -891,7 +873,7 @@ bool CApplication::GetSystemMouseVisibile() void CApplication::SetSystemMousePos(Math::Point pos) { - Math::IntPoint windowPos = InterfaceToWindowCoords(pos); + Math::IntPoint windowPos = m_engine->InterfaceToWindowCoords(pos); SDL_WarpMouse(windowPos.x, windowPos.y); m_systemMousePos = pos; } diff --git a/src/app/app.h b/src/app/app.h index 483aa55..bba55b2 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -25,7 +25,6 @@ #include "graphics/core/device.h" #include "graphics/engine/engine.h" #include "graphics/opengl/gldevice.h" -#include "math/intsize.h" #include #include @@ -206,11 +205,6 @@ protected: //! Closes the joystick device void CloseJoystick(); - //! Converts window coords to interface coords - Math::Point WindowToInterfaceCoords(Math::IntPoint pos); - //! Converts the interface coords to window coords - Math::IntPoint InterfaceToWindowCoords(Math::Point pos); - protected: //! Instance manager CInstanceManager* m_iMan; diff --git a/src/graphics/core/device.h b/src/graphics/core/device.h index c10b853..3ab86dd 100644 --- a/src/graphics/core/device.h +++ b/src/graphics/core/device.h @@ -32,6 +32,7 @@ class CImage; +struct ImageData; namespace Gfx { @@ -279,8 +280,6 @@ public: //! Destroys the device, releasing every acquired resource virtual void Destroy() = 0; - //! Returns whether the device has been initialized - virtual bool GetWasInit() = 0; //! Returns the last encountered error virtual std::string GetError() = 0; @@ -317,6 +316,8 @@ public: //! Creates a texture from image; the image can be safely removed after that virtual Gfx::Texture CreateTexture(CImage *image, const Gfx::TextureCreateParams ¶ms) = 0; + //! Creates a texture from raw image data; image data can be freed after that + virtual Gfx::Texture CreateTexture(ImageData *data, const Gfx::TextureCreateParams ¶ms) = 0; //! Deletes a given texture, freeing it from video memory virtual void DestroyTexture(const Gfx::Texture &texture) = 0; //! Deletes all textures created so far @@ -324,8 +325,10 @@ public: //! Returns the maximum number of multitexture stages virtual int GetMaxTextureCount() = 0; - //! Sets the (multi)texture at given index + //! Sets the texture at given texture stage virtual void SetTexture(int index, const Gfx::Texture &texture) = 0; + //! Sets the texture image by ID at given texture stage + virtual void SetTexture(int index, unsigned int textureId) = 0; //! Returns the (multi)texture at given index virtual Gfx::Texture GetTexture(int index) = 0; //! Enables/disables the given texture stage diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index e544ee3..345a15c 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -25,6 +25,8 @@ #include "common/key.h" #include "common/logger.h" #include "graphics/core/device.h" +#include "graphics/engine/lightman.h" +#include "graphics/engine/text.h" #include "math/geometry.h" // Initial size of various vectors @@ -44,23 +46,21 @@ Gfx::CEngine::CEngine(CInstanceManager *iMan, CApplication *app) { m_iMan = iMan; m_app = app; - m_device = NULL; - - m_wasInit = false; + m_device = nullptr; m_iMan = iMan; m_iMan->AddInstance(CLASS_ENGINE, this); m_app = app; - m_lightMan = NULL; - m_text = NULL; - m_particle = NULL; - m_water = NULL; - m_cloud = NULL; - m_lightning = NULL; - m_planet = NULL; - m_sound = NULL; - m_terrain = NULL; + m_lightMan = nullptr; + m_text = nullptr; + m_particle = nullptr; + m_water = nullptr; + m_cloud = nullptr; + m_lightning = nullptr; + m_planet = nullptr; + m_sound = nullptr; + m_terrain = nullptr; m_focus = 0.75f; m_baseTime = 0; @@ -178,95 +178,93 @@ Gfx::CEngine::CEngine(CInstanceManager *iMan, CApplication *app) Gfx::CEngine::~CEngine() { - m_iMan = NULL; - m_app = NULL; - m_device = NULL; + m_iMan = nullptr; + m_app = nullptr; + m_device = nullptr; - m_sound = NULL; - m_terrain = NULL; + m_sound = nullptr; + m_terrain = nullptr; } -bool Gfx::CEngine::GetWasInit() +std::string Gfx::CEngine::GetError() { - return m_wasInit; + return m_error; } -std::string Gfx::CEngine::GetError() +void Gfx::CEngine::SetDevice(Gfx::CDevice *device) { - return m_error; + m_device = device; +} + +Gfx::CDevice* Gfx::CEngine::GetDevice() +{ + return m_device; } bool Gfx::CEngine::Create() { - m_wasInit = true; + m_size = m_lastSize = m_app->GetVideoConfig().size; - /*m_lightMan = new Gfx::CLight(m_iMan, this); + m_lightMan = new Gfx::CLightManager(m_iMan, this); m_text = new Gfx::CText(m_iMan, this); + /* TODO: m_particle = new Gfx::CParticle(m_iMan, this); m_water = new Gfx::CWater(m_iMan, this); m_cloud = new Gfx::CCloud(m_iMan, this); m_lightning = new Gfx::CLightning(m_iMan, this); m_planet = new Gfx::CPlanet(m_iMan, this);*/ + m_text->SetDevice(m_device); + if (! m_text->Create()) + { + m_error = std::string("Error creating CText: ") + m_text->GetError(); + return false; + } + + m_matWorldInterface.LoadIdentity(); m_matViewInterface.LoadIdentity(); Math::LoadOrthoProjectionMatrix(m_matProjInterface, 0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f); + m_device->SetClearColor(Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f)); + + m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_TEST, false); + + Gfx::TextureCreateParams params; + params.format = Gfx::TEX_IMG_RGB; + params.minFilter = Gfx::TEX_MIN_FILTER_NEAREST; + params.magFilter = Gfx::TEX_MAG_FILTER_NEAREST; + params.mipmap = false; + m_miceTexture = CreateTexture("mouse.png", params); + return true; } void Gfx::CEngine::Destroy() { - // TODO + m_text->Destroy(); - /*delete m_lightMan; - m_lightMan = NULL; + delete m_lightMan; + m_lightMan = nullptr; delete m_text; - m_text = NULL; + m_text = nullptr; + /* TODO: delete m_particle; - m_particle = NULL; + m_particle = nullptr; delete m_water; - m_water = NULL; + m_water = nullptr; delete m_cloud; - m_cloud = NULL; + m_cloud = nullptr; delete m_lightning; - m_lightning = NULL; + m_lightning = nullptr; delete m_planet; - m_planet = NULL;*/ - - m_wasInit = false; -} - -void Gfx::CEngine::SetDevice(Gfx::CDevice *device) -{ - m_device = device; -} - -Gfx::CDevice* Gfx::CEngine::GetDevice() -{ - return m_device; -} - -bool Gfx::CEngine::AfterDeviceSetInit() -{ - m_device->SetClearColor(Gfx::Color(0.0f, 0.0f, 0.0f, 0.0f)); - - m_device->SetRenderState(Gfx::RENDER_STATE_DEPTH_TEST, false); - - Gfx::TextureCreateParams params; - params.format = Gfx::TEX_IMG_RGB; - params.minFilter = Gfx::TEX_MIN_FILTER_NEAREST; - params.magFilter = Gfx::TEX_MAG_FILTER_NEAREST; - params.mipmap = false; - m_miceTexture = CreateTexture("mouse.png", params); - - return true; + m_planet = nullptr;*/ } void Gfx::CEngine::ResetAfterDeviceChanged() @@ -274,7 +272,6 @@ void Gfx::CEngine::ResetAfterDeviceChanged() // TODO } - Gfx::Texture Gfx::CEngine::CreateTexture(const std::string &texName, const Gfx::TextureCreateParams ¶ms) { CImage img; @@ -610,9 +607,38 @@ bool Gfx::CEngine::DrawInterface() DrawMouse(); + m_text->DrawString("abcdefghijklmnopqrstuvwxyz ąęśćółńż", Gfx::FONT_COLOBOT, 15.0f, Math::Point(0.25f, 0.2f), 1.0f, 0); + return true; } +/** Conversion of the position of the mouse from window coords to interface coords: + - x: 0=left, 1=right + - y: 0=down, 1=up */ +Math::Point Gfx::CEngine::WindowToInterfaceCoords(Math::IntPoint pos) +{ + return Math::Point( static_cast(pos.x) / static_cast(m_size.w), + 1.0f - static_cast(pos.y) / static_cast(m_size.h) ); +} + +Math::IntPoint Gfx::CEngine::InterfaceToWindowCoords(Math::Point pos) +{ + return Math::IntPoint(static_cast(pos.x * m_size.w), + static_cast((1.0f - pos.y) * m_size.h)); +} + +Math::Size Gfx::CEngine::WindowToInterfaceSize(Math::IntSize size) +{ + return Math::Size( static_cast(size.w) / static_cast(m_size.w), + static_cast(size.h) / static_cast(m_size.h) ); +} + +Math::IntSize Gfx::CEngine::InterfaceToWindowSize(Math::Size size) +{ + return Math::IntSize(static_cast(size.w * m_size.w), + static_cast(size.h * m_size.h)); +} + void Gfx::CEngine::DrawMouse() { if (! m_mouseVisible) diff --git a/src/graphics/engine/engine.h b/src/graphics/engine/engine.h index 25c5e5d..79844c6 100644 --- a/src/graphics/engine/engine.h +++ b/src/graphics/engine/engine.h @@ -29,6 +29,7 @@ #include "math/intsize.h" #include "math/matrix.h" #include "math/point.h" +#include "math/size.h" #include "math/vector.h" @@ -514,23 +515,18 @@ public: CEngine(CInstanceManager *iMan, CApplication *app); ~CEngine(); - //! Returns whether the device was initialized - bool GetWasInit(); //! Returns the last error encountered std::string GetError(); - //! Performs the first initialization, before a device was set - bool Create(); - //! Frees all resources before exit - void Destroy(); - //! Sets the device to be used void SetDevice(Gfx::CDevice *device); //! Returns the current device Gfx::CDevice* GetDevice(); - //! Performs initialization after a device was created and set - bool AfterDeviceSetInit(); + //! Performs the initialization; must be called after device was set + bool Create(); + //! Frees all resources before exit + void Destroy(); //! Resets some states and flushes textures after device was changed (e.g. resoulution changed) void ResetAfterDeviceChanged(); @@ -544,6 +540,17 @@ public: bool Render(); + //! Converts window coords to interface coords + Math::Point WindowToInterfaceCoords(Math::IntPoint pos); + //! Converts interface coords to window coords + Math::IntPoint InterfaceToWindowCoords(Math::Point pos); + + //! Converts window size to interface size + Math::Size WindowToInterfaceSize(Math::IntSize size); + //! Converts interface size to window size + Math::IntSize InterfaceToWindowSize(Math::Size size); + + bool WriteProfile(); void SetPause(bool pause); @@ -769,7 +776,8 @@ public: Math::Vector GetLookatPt(); float GetEyeDirH(); float GetEyeDirV(); - Math::Point GetDim(); + Math::IntPoint GetViewportSize(); + Math::IntPoint GetLastViewportSize(); void UpdateMatProj(); void ApplyChange(); @@ -903,8 +911,9 @@ protected: bool m_render; bool m_movieLock; - //! Current size of window + //! Current size of viewport Math::IntSize m_size; + //! Previous size of viewport Math::IntSize m_lastSize; std::vector m_objectTree; diff --git a/src/graphics/engine/text.cpp b/src/graphics/engine/text.cpp index 2a9543c..eea9fdb 100644 --- a/src/graphics/engine/text.cpp +++ b/src/graphics/engine/text.cpp @@ -19,5 +19,480 @@ #include "graphics/engine/text.h" +#include "app/app.h" +#include "common/image.h" +#include "common/iman.h" +#include "common/logger.h" +#include "common/stringutils.h" +#include "math/func.h" -// TODO implementation +#include +#include + + +namespace Gfx +{ + +/** + \struct CachedFont + \brief Base TTF font with UTF-8 char cache */ +struct CachedFont +{ + TTF_Font* font; + std::map cache; + + CachedFont() : font(nullptr) {} +}; + +}; + + + +Gfx::CText::CText(CInstanceManager *iMan, Gfx::CEngine* engine) +{ + m_iMan = iMan; + m_iMan->AddInstance(CLASS_TEXT, this); + + m_device = nullptr; + m_engine = engine; + + m_defaultSize = 12.0f; + m_fontPath = "fonts"; + + m_lastFontType = Gfx::FONT_COLOBOT; + m_lastFontSize = 0; + m_lastCachedFont = nullptr; +} + +Gfx::CText::~CText() +{ + m_iMan->DeleteInstance(CLASS_TEXT, this); + + m_iMan = nullptr; + m_device = nullptr; + m_engine = nullptr; +} + +bool Gfx::CText::Create() +{ + if (TTF_Init() != 0) + { + m_error = std::string("TTF_Init error: ") + std::string(TTF_GetError()); + return false; + } + + m_fonts[Gfx::FONT_COLOBOT] = new MultisizeFont("dvu_sans.ttf"); + m_fonts[Gfx::FONT_COLOBOT_BOLD] = new MultisizeFont("dvu_sans_bold.ttf"); + m_fonts[Gfx::FONT_COLOBOT_ITALIC] = new MultisizeFont("dvu_sans_italic.ttf"); + + m_fonts[Gfx::FONT_COURIER] = new MultisizeFont("dvu_sans_mono.ttf"); + m_fonts[Gfx::FONT_COURIER_BOLD] = new MultisizeFont("dvu_sans_mono_bold.ttf"); + + for (auto it = m_fonts.begin(); it != m_fonts.end(); ++it) + { + Gfx::FontType type = (*it).first; + CachedFont* cf = GetOrOpenFont(type, m_defaultSize); + if (cf == nullptr || cf->font == nullptr) + return false; + } + + return true; +} + +void Gfx::CText::Destroy() +{ + for (auto it = m_fonts.begin(); it != m_fonts.end(); ++it) + { + MultisizeFont* mf = (*it).second; + + for (auto jt = mf->fonts.begin(); jt != mf->fonts.end(); ++jt) + { + CachedFont* cf = (*jt).second; + + TTF_CloseFont(cf->font); + + cf->font = nullptr; + delete cf; + } + + mf->fonts.clear(); + delete mf; + } + + m_fonts.clear(); + + m_lastCachedFont = nullptr; + + TTF_Quit(); +} + +void Gfx::CText::SetDevice(Gfx::CDevice* device) +{ + m_device = device; +} + +std::string Gfx::CText::GetError() +{ + return m_error; +} + +void Gfx::CText::FlushCache() +{ + for (auto it = m_fonts.begin(); it != m_fonts.end(); ++it) + { + MultisizeFont *mf = (*it).second; + for (auto jt = mf->fonts.begin(); jt != mf->fonts.end(); ++jt) + { + CachedFont *f = (*jt).second; + f->cache.clear(); + } + } +} + +void Gfx::CText::DrawText(const std::string &text, const std::vector &format, + Math::Point pos, float width, Gfx::JustifyType justify, float size, + float stretch, int eol) +{ + // TODO +} + +void Gfx::CText::DrawText(const std::string &text, Gfx::FontType font, + Math::Point pos, float width, Gfx::JustifyType justify, float size, + float stretch, int eol) +{ + // TODO +} + +void Gfx::CText::SizeText(const std::string &text, const std::vector &format, + Math::Point pos, Gfx::JustifyType justify, float size, + Math::Point &start, Math::Point &end) +{ + // TODO +} + +void Gfx::CText::SizeText(const std::string &text, Gfx::FontType font, + Math::Point pos, Gfx::JustifyType justify, float size, + Math::Point &start, Math::Point &end) +{ + // TODO +} + +float Gfx::CText::GetAscent(Gfx::FontType font, float size) +{ + // TODO + return 0.0f; +} + +float Gfx::CText::GetDescent(Gfx::FontType font, float size) +{ + // TODO + return 0.0f; +} + +float Gfx::CText::GetHeight(Gfx::FontType font, float size) +{ + // TODO + return 0.0f; +} + + +float Gfx::CText::GetStringWidth(const std::string &text, + const std::vector &format, float size) +{ + // TODO + return 0.0f; +} + +float Gfx::CText::GetStringWidth(const std::string &text, Gfx::FontType font, float size) +{ + // TODO + return 0.0f; +} + +float Gfx::CText::GetCharWidth(int character, Gfx::FontType font, float size, float offset) +{ + // TODO + return 0.0f; +} + + +int Gfx::CText::Justify(const std::string &text, const std::vector &format, + float size, float width) +{ + // TODO + return 0; +} + +int Gfx::CText::Justify(const std::string &text, Gfx::FontType font, float size, float width) +{ + // TODO + return 0; +} + +int Gfx::CText::Detect(const std::string &text, const std::vector &format, + float size, float offset) +{ + // TODO + return 0; +} + +int Gfx::CText::Detect(const std::string &text, Gfx::FontType font, float size, float offset) +{ + // TODO + return 0; +} + +void Gfx::CText::DrawString(const std::string &text, const std::vector &format, + float size, Math::Point pos, float width, int eol) +{ + // TODO +} + +void Gfx::CText::DrawString(const std::string &text, Gfx::FontType font, + float size, Math::Point pos, float width, int eol) +{ + m_device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true); + m_device->SetTextureEnabled(0, true); + + m_device->SetRenderState(Gfx::RENDER_STATE_BLENDING, true); + m_device->SetBlendFunc(Gfx::BLEND_SRC_ALPHA, Gfx::BLEND_INV_SRC_ALPHA); + + unsigned int index = 0; + Math::Point screenPos = pos; + while (index < text.length()) + { + UTF8Char ch; + + int len = StrUtils::Utf8CharSizeAt(text, index); + if (len >= 1) + ch.c1 = text[index]; + if (len >= 2) + ch.c2 = text[index+1]; + if (len >= 3) + ch.c3 = text[index+2]; + + index += len; + + DrawChar(ch, font, size, screenPos); + } +} + +void Gfx::CText::DrawColor(int color, float size, Math::Point pos, float width) +{ + // TODO !!! + /* + float h, u1, u2, v1, v2, dp; + int icon; + + int icon = -1; + switch (color) + { + case Gfx::FONT_COLOR_LINK: + icon = 9; + break; + case Gfx::FONT_COLOR_TOKEN: + icon = 4; + break; + case Gfx::FONT_COLOR_TYPE: + icon = 5; + break; + } + icon = -1; + if ( color == COLOR_LINK ) icon = 9; // blue + if ( color == COLOR_TOKEN ) icon = 4; // orange + if ( color == COLOR_TYPE ) icon = 5; // green + if ( color == COLOR_CONST ) icon = 8; // red + if ( color == COLOR_REM ) icon = 6; // magenta + if ( color == COLOR_KEY ) icon = 10; // gray + + if ( icon == -1 ) return; + + if ( color == COLOR_LINK ) + { + m_engine->SetState(D3DSTATENORMAL); + } + + Math::IntSize vsize = m_engine->GetViewportSize(); + if (vsize.h <= 768.0f) // 1024x768 or less? + h = 1.01f / dim.y; // 1 pixel + else // more than 1024x768? + h = 2.0f / dim.y; // 2 pixels + + Math::Point p1, p2; + p1.x = pos.x; + p2.x = pos.x + width; + + if (color == Gfx::FONT_COLOR_LINK) + { + p1.y = pos.y; + p2.y = pos.y + h; // just emphasized + } + else + { + p1.y = pos.y; + p2.y = pos.y + (16.0f/256.0f)*(size/20.0f); + } + + u1 = (16.0f/256.0f)*(icon%16); + v1 = (240.0f/256.0f); + u2 = (16.0f/256.0f)+u1; + v2 = (16.0f/256.0f)+v1; + + dp = 0.5f/256.0f; + u1 += dp; + v1 += dp; + u2 -= dp; + v2 -= dp; + + Math::Vector n(0.0f, 0.0f, -1.0f); // normal + + Gfx::Vertex quad[] = + { + Gfx::Vertex(Math::Vector(p1.x, p1.y, 0.0f), n, Math::Point(u1, v2)), + Gfx::Vertex(Math::Vector(p1.x, p2.y, 0.0f), n, Math::Point(u1, v1)), + Gfx::Vertex(Math::Vector(p2.x, p1.y, 0.0f), n, Math::Point(u2, v2)), + Gfx::Vertex(Math::Vector(p2.x, p2.y, 0.0f), n, Math::Point(u2, v1)), + }; + + m_device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, quad, 4); + m_engine->AddStatisticTriangle(2); + + if (color == Gfx::FONT_COLOR_LINK) + m_engine->SetState(Gfx::ENG_RSTATE_TTEXTURE_WHITE);*/ +} + +void Gfx::CText::DrawChar(UTF8Char character, Gfx::FontType font, float size, Math::Point &pos) +{ + CachedFont* cf = GetOrOpenFont(font, size); + + if (cf == nullptr) + return; + + auto it = cf->cache.find(character); + CharTexture tex; + if (it != cf->cache.end()) + { + tex = (*it).second; + } + else + { + char str[] = { character.c1, character.c2, character.c3, '\0' }; + tex = CreateCharTexture(str, cf); + + if (tex.id == 0) // invalid + return; + + cf->cache[character] = tex; + } + + Math::Vector n(0.0f, 0.0f, -1.0f); // normal + + Gfx::Vertex quad[4] = + { + Gfx::Vertex(Math::Vector(pos.x, pos.y + tex.charSize.h, 0.0f), + n, Math::Point(0.0f, 0.0f)), + Gfx::Vertex(Math::Vector(pos.x, pos.y + tex.charSize.h - tex.texSize.h, 0.0f), + n, Math::Point(0.0f, 1.0f)), + Gfx::Vertex(Math::Vector(pos.x + tex.texSize.w, pos.y + tex.charSize.h, 0.0f), + n, Math::Point(1.0f, 0.0f)), + Gfx::Vertex(Math::Vector(pos.x + tex.texSize.w, pos.y + tex.charSize.h - tex.texSize.h, 0.0f), + n, Math::Point(1.0f, 1.0f)) + }; + + m_device->SetTexture(0, tex.id); + m_device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, quad, 4); + + pos.x += tex.charSize.w; +} + +Gfx::CachedFont* Gfx::CText::GetOrOpenFont(Gfx::FontType font, float size) +{ + // TODO: sizing + int pointSize = static_cast(size); + + if (m_lastCachedFont != nullptr) + { + if (m_lastFontType == font && m_lastFontSize == pointSize) + return m_lastCachedFont; + } + + auto it = m_fonts.find(font); + if (it == m_fonts.end()) + { + m_error = std::string("Invalid font type ") + StrUtils::ToString(static_cast(font)); + return nullptr; + } + + MultisizeFont* mf = (*it).second; + + auto jt = mf->fonts.find(pointSize); + if (jt != mf->fonts.end()) + { + m_lastCachedFont = (*jt).second; + m_lastFontType = font; + m_lastFontSize = pointSize; + return m_lastCachedFont; + } + + std::string path = CApplication::GetInstance().GetDataFilePath(m_fontPath, mf->fileName); + + m_lastCachedFont = new CachedFont(); + m_lastCachedFont->font = TTF_OpenFont(path.c_str(), pointSize); + if (m_lastCachedFont->font == nullptr) + m_error = std::string("TTF_OpenFont error ") + std::string(TTF_GetError()); + + mf->fonts[pointSize] = m_lastCachedFont; + + return m_lastCachedFont; +} + +Gfx::CharTexture Gfx::CText::CreateCharTexture(const char* str, Gfx::CachedFont* font) +{ + CharTexture texture; + + SDL_Surface* textSurface = nullptr; + SDL_Color white = {255, 255, 255, 0}; + textSurface = TTF_RenderUTF8_Blended(font->font, str, white); + + if (textSurface == nullptr) + { + m_error = "TTF_Render error"; + return texture; + } + + int w = Math::NextPowerOfTwo(textSurface->w); + int h = Math::NextPowerOfTwo(textSurface->h); + + textSurface->flags = textSurface->flags & (~SDL_SRCALPHA); + SDL_Surface* textureSurface = SDL_CreateRGBSurface(0, w, h, 32, 0x00ff0000, 0x0000ff00, + 0x000000ff, 0xff000000); + SDL_BlitSurface(textSurface, NULL, textureSurface, NULL); + + ImageData data; + data.surface = textureSurface; + + Gfx::TextureCreateParams createParams; + createParams.format = Gfx::TEX_IMG_RGBA; + createParams.minFilter = Gfx::TEX_MIN_FILTER_NEAREST; + createParams.magFilter = Gfx::TEX_MAG_FILTER_NEAREST; + createParams.mipmap = false; + + Gfx::Texture tex = m_device->CreateTexture(&data, createParams); + + data.surface = nullptr; + + SDL_FreeSurface(textSurface); + SDL_FreeSurface(textureSurface); + + if (! tex.valid) + { + m_error = "Texture create error"; + return texture; + } + + texture.id = tex.id; + texture.texSize = m_engine->WindowToInterfaceSize(Math::IntSize(textureSurface->w, textureSurface->h)); + texture.charSize = m_engine->WindowToInterfaceSize(Math::IntSize(textSurface->w, textSurface->h)); + + return texture; +} diff --git a/src/graphics/engine/text.h b/src/graphics/engine/text.h index c2de220..19d9882 100644 --- a/src/graphics/engine/text.h +++ b/src/graphics/engine/text.h @@ -19,95 +19,268 @@ #pragma once -#include "graphics/engine/engine.h" -#include "graphics/core/device.h" #include "math/point.h" +#include "math/size.h" +#include +#include class CInstanceManager; - namespace Gfx { -const float SMALLFONT = 10.0f; -const float BIGFONT = 15.0f; +class CEngine; +class CDevice; + +//! Standard small font size +const float FONT_SIZE_SMALL = 10.0f; +//! Standard big font size +const float FONT_SIZE_BIG = 15.0f; + +/** + \enum TextAlignType + \brief Type of text alignment */ +enum JustifyType +{ + TEXT_ALIGN_RIGHT, + TEXT_ALIGN_LEFT, + TEXT_ALIGN_CENTER +}; -const float NORMSTRETCH = 0.8f; +/* Font meta char constants */ +//! Type used for font character metainfo +typedef short FontMetaChar; +/** + \enum FontType + \brief Type of font + Bitmask in lower 4 bits (mask 0x00f) */ enum FontType { - FONT_COLOBOT = 0, - FONT_COURIER = 1, - FONT_BUTTON = 2, + //! Flag for bold font subtype + FONT_BOLD = 0x04, + //! Flag for italic font subtype + FONT_ITALIC = 0x08, + + //! Default colobot font used for interface + FONT_COLOBOT = 0x00, + //! Alias for bold colobot font + FONT_COLOBOT_BOLD = FONT_COLOBOT | FONT_BOLD, + //! Alias for italic colobot font + FONT_COLOBOT_ITALIC = FONT_COLOBOT | FONT_ITALIC, + + //! Courier (monospace) font used mainly in code editor (only regular & bold) + FONT_COURIER = 0x01, + //! Alias for bold courier font + FONT_COURIER_BOLD = FONT_COURIER | FONT_BOLD, + + // 0x02 left for possible another font + + //! Pseudo-font loaded from textures for buttons, icons, etc. + FONT_BUTTON = 0x03, }; +/** + \enum FontTitle + \brief Size of font title + + Bitmask in 2 bits left shifted 4 (mask 0x030) */ enum FontTitle { - TITLE_BIG = 0x04, - TITLE_NORM = 0x08, - TITLE_LITTLE = 0x0c, + FONT_TITLE_BIG = 0x01 << 4, + FONT_TITLE_NORM = 0x02 << 4, + FONT_TITLE_LITTLE = 0x03 << 4, }; +/** + \enum FontColor + \brief Font color type (?) + + Bitmask in 3 bits left shifted 6 (mask 0x1c0) */ enum FontColor { - COLOR_LINK = 0x10, - COLOR_TOKEN = 0x20, - COLOR_TYPE = 0x30, - COLOR_CONST = 0x40, - COLOR_REM = 0x50, - COLOR_KEY = 0x60, - COLOR_TABLE = 0x70, + FONT_COLOR_LINK = 0x01 << 6, + FONT_COLOR_TOKEN = 0x02 << 6, + FONT_COLOR_TYPE = 0x03 << 6, + FONT_COLOR_CONST = 0x04 << 6, + FONT_COLOR_REM = 0x05 << 6, + FONT_COLOR_KEY = 0x06 << 6, + FONT_COLOR_TABLE = 0x07 << 6, }; -const short FONT_MASK = 0x03; -const short TITLE_MASK = 0x0c; -const short COLOR_MASK = 0x70; -const short IMAGE_MASK = 0x80; +/** + \enum FontMask + \brief Masks in FontMetaChar for different attributes */ +enum FontMask +{ + //! Mask for FontType + FONT_MASK_FONT = 0x00f, + //! Mask for FontTitle + FONT_MASK_TITLE = 0x030, + //! Mask for FontColor + FONT_MASK_COLOR = 0x1c0, + //! Mask for image bit + FONT_MASK_IMAGE = 0x200 +}; + + +/** + \struct UTF8Char + \brief UTF-8 character in font cache + + Only 3-byte chars are supported */ +struct UTF8Char +{ + char c1, c2, c3; + + explicit UTF8Char(char ch1 = '\0', char ch2 = '\0', char ch3 = '\0') + : c1(ch1), c2(ch2), c3(ch3) {} + + inline bool operator<(const UTF8Char &other) const + { + if (c1 < other.c1) + return true; + else if (c1 > other.c1) + return false; + if (c2 < other.c2) + return true; + else if (c2 > other.c2) + return false; + return c3 < other.c3; + } + + inline bool operator==(const UTF8Char &other) const + { + return c1 == other.c1 && c2 == other.c2 && c3 == other.c3; + } +}; -class CText { +/** + \struct CharTexture + \brief Texture of font character */ +struct CharTexture +{ + unsigned int id; + Math::Size texSize; + Math::Size charSize; + + CharTexture() : id(0) {} +}; + +// Definition is private - in text.cpp +struct CachedFont; + +/** + \struct MultisizeFont + \brief Font with multiple possible sizes */ +struct MultisizeFont +{ + std::string fileName; + std::map fonts; + + MultisizeFont(const std::string &fn) + : fileName(fn) {} +}; + +/** + \class CText + \brief Text rendering engine + + ... */ +class CText +{ public: CText(CInstanceManager *iMan, Gfx::CEngine* engine); ~CText(); + //! Sets the device to be used void SetDevice(Gfx::CDevice *device); - void DrawText(char *string, char *format, int len, Math::Point pos, float width, int justif, float size, float stretch, int eol); - void DrawText(char *string, char *format, Math::Point pos, float width, int justif, float size, float stretch, int eol); - void DrawText(char *string, int len, Math::Point pos, float width, int justif, float size, float stretch, FontType font, int eol); - void DrawText(char *string, Math::Point pos, float width, int justif, float size, float stretch, FontType font, int eol); - void DimText(char *string, char *format, int len, Math::Point pos, int justif, float size, float stretch, Math::Point &start, Math::Point &end); - void DimText(char *string, char *format, Math::Point pos, int justif, float size, float stretch, Math::Point &start, Math::Point &end); - void DimText(char *string, int len, Math::Point pos, int justif, float size, float stretch, FontType font, Math::Point &start, Math::Point &end); - void DimText(char *string, Math::Point pos, int justif, float size, float stretch, FontType font, Math::Point &start, Math::Point &end); + //! Returns the last encountered error + std::string GetError(); - float RetAscent(float size, FontType font); - float RetDescent(float size, FontType font); - float RetHeight(float size, FontType font); + //! Initializes the font engine; must be called after SetDevice() + bool Create(); + //! Frees resources before exit + void Destroy(); - float RetStringWidth(char *string, char *format, int len, float size, float stretch); - float RetStringWidth(char *string, int len, float size, float stretch, FontType font); - float RetCharWidth(int character, float offset, float size, float stretch, FontType font); + //! Flushes cached textures + void FlushCache(); - int Justif(char *string, char *format, int len, float width, float size, float stretch); - int Justif(char *string, int len, float width, float size, float stretch, FontType font); - int Detect(char *string, char *format, int len, float offset, float size, float stretch); - int Detect(char *string, int len, float offset, float size, float stretch, FontType font); + //! Draws text (multi-format) + void DrawText(const std::string &text, const std::vector &format, + Math::Point pos, float width, Gfx::JustifyType justify, float size, + float stretch, int eol); + //! Draws text (one font) + void DrawText(const std::string &text, Gfx::FontType font, + Math::Point pos, float width, Gfx::JustifyType justify, float size, + float stretch, int eol); -protected: - void DrawString(char *string, char *format, int len, Math::Point pos, float width, float size, float stretch, int eol); - void DrawString(char *string, int len, Math::Point pos, float width, float size, float stretch, FontType font, int eol); - void DrawColor(Math::Point pos, float size, float width, int color); - void DrawChar(int character, Math::Point pos, float size, float stretch, FontType font); + //! Calculates dimensions for text (multi-format) + void SizeText(const std::string &text, const std::vector &format, + Math::Point pos, Gfx::JustifyType justify, float size, + Math::Point &start, Math::Point &end); + //! Calculates dimensions for text (one font) + void SizeText(const std::string &text, Gfx::FontType font, + Math::Point pos, Gfx::JustifyType justify, float size, + Math::Point &start, Math::Point &end); + + //! Returns the ascent font metric + float GetAscent(Gfx::FontType font, float size); + //! Returns the descent font metric + float GetDescent(Gfx::FontType font, float size); + //! Returns the height font metric + float GetHeight(Gfx::FontType font, float size); + + //! Returns width of string (multi-format) + float GetStringWidth(const std::string &text, + const std::vector &format, float size); + //! Returns width of string (single font) + float GetStringWidth(const std::string &text, Gfx::FontType font, float size); + //! Returns width of single character + float GetCharWidth(int character, Gfx::FontType font, float size, float offset); + + //! Justifies a line of text (multi-format) + int Justify(const std::string &text, const std::vector &format, + float size, float width); + //! Justifies a line of text (one font) + int Justify(const std::string &text, Gfx::FontType font, float size, float width); + + //! Returns the most suitable position to a given offset (multi-format) + int Detect(const std::string &text, const std::vector &format, + float size, float offset); + //! Returns the most suitable position to a given offset (one font) + int Detect(const std::string &text, Gfx::FontType font, float size, float offset); + +public: // for testing! + Gfx::CachedFont* GetOrOpenFont(Gfx::FontType type, float size); + Gfx::CharTexture CreateCharTexture(const char* utf8Char, Gfx::CachedFont* font); + + void DrawString(const std::string &text, const std::vector &format, + float size, Math::Point pos, float width, int eol); + void DrawString(const std::string &text, Gfx::FontType font, + float size, Math::Point pos, float width, int eol); + void DrawColor(int color, float size, Math::Point pos, float width); + void DrawChar(UTF8Char character, Gfx::FontType font, float size, Math::Point &pos); protected: CInstanceManager* m_iMan; Gfx::CEngine* m_engine; Gfx::CDevice* m_device; + std::string m_error; + float m_defaultSize; + std::string m_fontPath; + + std::map m_fonts; + + Gfx::FontType m_lastFontType; + int m_lastFontSize; + Gfx::CachedFont* m_lastCachedFont; }; }; // namespace Gfx diff --git a/src/graphics/opengl/gldevice.cpp b/src/graphics/opengl/gldevice.cpp index 3a255f4..cef372f 100644 --- a/src/graphics/opengl/gldevice.cpp +++ b/src/graphics/opengl/gldevice.cpp @@ -64,7 +64,6 @@ void Gfx::GLDeviceConfig::LoadDefault() Gfx::CGLDevice::CGLDevice(const Gfx::GLDeviceConfig &config) { m_config = config; - m_wasInit = false; m_lighting = false; m_texturing = false; } @@ -74,11 +73,6 @@ Gfx::CGLDevice::~CGLDevice() { } -bool Gfx::CGLDevice::GetWasInit() -{ - return m_wasInit; -} - std::string Gfx::CGLDevice::GetError() { return m_error; @@ -110,8 +104,6 @@ bool Gfx::CGLDevice::Create() /* NOTE: when not using GLEW, extension testing is not performed, as it is assumed that glext.h is up-to-date and the OpenGL shared library has the required functions present. */ - m_wasInit = true; - // This is mostly done in all modern hardware by default // DirectX doesn't even allow the option to turn off perspective correction anymore // So turn it on permanently @@ -158,8 +150,6 @@ void Gfx::CGLDevice::Destroy() m_currentTextures.clear(); m_texturesEnabled.clear(); m_textureStageParams.clear(); - - m_wasInit = false; } void Gfx::CGLDevice::ConfigChanged(const Gfx::GLDeviceConfig& newConfig) @@ -385,15 +375,20 @@ bool Gfx::CGLDevice::GetLightEnabled(int index) This struct must not be deleted in other way than through DeleteTexture() */ Gfx::Texture Gfx::CGLDevice::CreateTexture(CImage *image, const Gfx::TextureCreateParams ¶ms) { - Gfx::Texture result; - ImageData *data = image->GetData(); if (data == NULL) { m_error = "Invalid texture data"; - return result; // invalid texture + return Gfx::Texture(); // invalid texture } + return CreateTexture(data, params); +} + +Gfx::Texture Gfx::CGLDevice::CreateTexture(ImageData *data, const Gfx::TextureCreateParams ¶ms) +{ + Gfx::Texture result; + result.valid = true; result.size.w = data->surface->w; result.size.h = data->surface->h; @@ -531,6 +526,24 @@ void Gfx::CGLDevice::SetTexture(int index, const Gfx::Texture &texture) glDisable(GL_TEXTURE_2D); } +void Gfx::CGLDevice::SetTexture(int index, unsigned int textureId) +{ + assert(index >= 0); + assert(index < static_cast( m_currentTextures.size() )); + + // Enable the given texture stage + glActiveTexture(GL_TEXTURE0 + index); + glEnable(GL_TEXTURE_2D); + + m_currentTextures[index].id = textureId; + + glBindTexture(GL_TEXTURE_2D, textureId); + + // Disable the stage if it is set so + if ( (! m_texturing) || (! m_texturesEnabled[index]) ) + glDisable(GL_TEXTURE_2D); +} + /** Returns the previously assigned texture or invalid texture if the given stage is not enabled. */ Gfx::Texture Gfx::CGLDevice::GetTexture(int index) diff --git a/src/graphics/opengl/gldevice.h b/src/graphics/opengl/gldevice.h index 1864000..a41c41c 100644 --- a/src/graphics/opengl/gldevice.h +++ b/src/graphics/opengl/gldevice.h @@ -73,7 +73,6 @@ public: CGLDevice(const Gfx::GLDeviceConfig &config); virtual ~CGLDevice(); - virtual bool GetWasInit(); virtual std::string GetError(); virtual bool Create(); @@ -100,11 +99,13 @@ public: virtual bool GetLightEnabled(int index); virtual Gfx::Texture CreateTexture(CImage *image, const Gfx::TextureCreateParams ¶ms); + virtual Gfx::Texture CreateTexture(ImageData *data, const Gfx::TextureCreateParams ¶ms); virtual void DestroyTexture(const Gfx::Texture &texture); virtual void DestroyAllTextures(); virtual int GetMaxTextureCount(); virtual void SetTexture(int index, const Gfx::Texture &texture); + virtual void SetTexture(int index, unsigned int textureId); virtual Gfx::Texture GetTexture(int index); virtual void SetTextureEnabled(int index, bool enabled); virtual bool GetTextureEnabled(int index); @@ -163,8 +164,6 @@ private: private: //! Current config Gfx::GLDeviceConfig m_config; - //! Was initialized? - bool m_wasInit; //! Last encountered error std::string m_error; diff --git a/src/math/const.h b/src/math/const.h index dd7ab0f..b08a400 100644 --- a/src/math/const.h +++ b/src/math/const.h @@ -20,6 +20,8 @@ #pragma once +#include + // Math module namespace namespace Math @@ -30,12 +32,12 @@ namespace Math const float TOLERANCE = 1e-6f; //! Very small number (used in testing/returning some values) -const float VERY_SMALL = 1e-6f; +const float VERY_SMALL_NUM = 1e-6f; //! Very big number (used in testing/returning some values) -const float VERY_BIG = 1e6f; +const float VERY_BIG_NUM = 1e6f; //! Huge number -const float HUGE = 1.0e+38f; +const float HUGE_NUM = 1.0e+38f; //! PI const float PI = 3.14159265358979323846f; @@ -45,6 +47,9 @@ const float DEG_TO_RAD = 0.01745329251994329547f; //! Radians to degrees multiplier const float RAD_TO_DEG = 57.29577951308232286465f; +//! Natural logarithm of 2 +const float LOG_2 = log(2.0f); + /* @} */ // end of group }; // namespace Math diff --git a/src/math/func.h b/src/math/func.h index 2127d1a..e97d990 100644 --- a/src/math/func.h +++ b/src/math/func.h @@ -127,6 +127,14 @@ inline float Rand() return static_cast(rand()) / static_cast(RAND_MAX); } +//! Returns the next nearest power of two to \a x +inline int NextPowerOfTwo(int x) +{ + double logbase2 = log(static_cast(x)) / Math::LOG_2; + return static_cast(pow(2, ceil(logbase2)) + 0.5); +} + + //! Returns a normalized angle, that is in other words between 0 and 2 * PI inline float NormAngle(float angle) { diff --git a/src/math/geometry.h b/src/math/geometry.h index 61d1868..3a31ad6 100644 --- a/src/math/geometry.h +++ b/src/math/geometry.h @@ -45,9 +45,9 @@ inline float MidPoint(const Math::Point &a, const Math::Point &b, float px) if (IsEqual(a.x, b.x)) { if (a.y < b.y) - return HUGE; + return Math::HUGE_NUM; else - return -HUGE; + return -Math::HUGE_NUM; } return (b.y-a.y) * (px-a.x) / (b.x-a.x) + a.y; } diff --git a/src/math/intsize.h b/src/math/intsize.h index f4b2431..d53de85 100644 --- a/src/math/intsize.h +++ b/src/math/intsize.h @@ -20,6 +20,9 @@ #pragma once +#include "math/intpoint.h" + + // Math module namespace namespace Math { @@ -31,9 +34,9 @@ namespace Math struct IntSize { //! Width - int w; + long w; //! Height - int h; + long h; //! Constructs a zero size: (0,0) inline IntSize() @@ -42,7 +45,7 @@ struct IntSize } //! Constructs a size from given dimensions: (w,h) - inline explicit IntSize(int w, int h) + inline explicit IntSize(long w, long h) { this->w = w; this->h = h; @@ -53,6 +56,12 @@ struct IntSize { w = h = 0; } + + //! Converts Point to Size + inline static Math::IntSize FromIntPoint(Math::IntPoint p) + { + return Math::IntSize(p.x, p.y); + } }; // struct Size diff --git a/src/math/size.h b/src/math/size.h index 781b9a4..03cffaa 100644 --- a/src/math/size.h +++ b/src/math/size.h @@ -20,6 +20,9 @@ #pragma once +#include "math/point.h" + + // Math module namespace namespace Math { @@ -58,6 +61,12 @@ struct Size { w = h = 0.0f; } + + //! Converts Point to Size + inline static Math::Size FromPoint(Math::Point p) + { + return Math::Size(p.x, p.y); + } }; // struct Size -- cgit v1.2.3-1-g7c22