diff --git a/src/framework/core/engine.cpp b/src/framework/core/engine.cpp index f528b61c..34116cb7 100644 --- a/src/framework/core/engine.cpp +++ b/src/framework/core/engine.cpp @@ -30,6 +30,7 @@ #include "dispatcher.h" #include "net/connections.h" #include "ui/uicontainer.h" +#include "graphics/fonts.h" Engine g_engine; @@ -52,7 +53,10 @@ void Engine::terminate() void Engine::run() { + std::string fpsText; + Size fpsTextSize; Font *defaultFont = g_fonts.getDefaultFont(); + m_lastFrameTicks = Platform::getTicks(); int lastFpsTicks = m_lastFrameTicks; int frameCount = 0; @@ -60,14 +64,14 @@ void Engine::run() m_running = true; while(!m_stopping) { + m_lastFrameTicks = Platform::getTicks(); + // poll platform events Platform::poll(); // poll network events g_connections.poll(); - m_lastFrameTicks = Platform::getTicks(); - // poll diaptcher tasks g_dispatcher.poll(); @@ -80,17 +84,18 @@ void Engine::run() lastFpsTicks = m_lastFrameTicks; fps = frameCount; frameCount = 0; + + // update fps text + fpsText = format("FPS: %d", fps); + fpsTextSize = defaultFont->calculateTextRectSize(fpsText); } } render(); // render fps - if(m_calculateFps) { - std::string fpsText = format("FPS: %d", fps); - Size textSize = defaultFont->calculateTextRectSize(fpsText); - defaultFont->renderText(fpsText, Point(g_graphics.getScreenSize().width() - textSize.width() - 10, 10)); - } + if(m_calculateFps) + defaultFont->renderText(fpsText, Point(g_graphics.getScreenSize().width() - fpsTextSize.width() - 10, 10)); // swap buffers Platform::swapBuffers(); diff --git a/src/framework/graphics/font.cpp b/src/framework/graphics/font.cpp index 617b9051..baa32415 100644 --- a/src/framework/graphics/font.cpp +++ b/src/framework/graphics/font.cpp @@ -26,7 +26,6 @@ #include "core/resources.h" #include "textures.h" #include "graphics.h" -#include "textarea.h" void Font::calculateGlyphsWidthsAutomatically(const Size& glyphSize) { @@ -136,7 +135,7 @@ void Font::renderText(const std::string& text, { Size boxSize = g_graphics.getScreenSize() - startPos.toSize(); Rect screenCoords(startPos, boxSize); - Font::renderText(text, screenCoords, ALIGN_TOP_LEFT, color); + renderText(text, screenCoords, ALIGN_TOP_LEFT, color); } @@ -145,34 +144,100 @@ void Font::renderText(const std::string& text, int align, const Color& color) { - TextArea textArea(this, text, screenCoords, align, color); - textArea.draw(); + // prevent glitches from invalid rects + if(!screenCoords.isValid()) + return; + + int textLenght = text.length(); + + // map glyphs positions + Size textBoxSize; + const std::vector& glyphsPositions = calculateGlyphsPositions(text, align, &textBoxSize); + + for(int i = 0; i < textLenght; ++i) { + int glyph = (uchar)text[i]; + + // skip invalid glyphs + if(glyph < 32) + continue; + + // calculate initial glyph rect and texture coords + Rect glyphScreenCoords(glyphsPositions[i], m_glyphsSize[glyph]); + Rect glyphTextureCoords = m_glyphsTextureCoords[glyph]; + + // first translate to align position + if(align & ALIGN_BOTTOM) { + glyphScreenCoords.translate(0, screenCoords.height() - textBoxSize.height()); + } else if(align & ALIGN_VERTICAL_CENTER) { + glyphScreenCoords.translate(0, (screenCoords.height() - textBoxSize.height()) / 2); + } else { // ALIGN_TOP + // nothing to do + } + + if(align & ALIGN_RIGHT) { + glyphScreenCoords.translate(screenCoords.width() - textBoxSize.width(), 0); + } else if(align & ALIGN_HORIZONTAL_CENTER) { + glyphScreenCoords.translate((screenCoords.width() - textBoxSize.width()) / 2, 0); + } else { // ALIGN_TOP + // nothing to do + } + + // translate rect to screen coords + glyphScreenCoords.translate(screenCoords.topLeft()); + + // only render if glyph rect is visible on screenCoords + if(!screenCoords.intersects(glyphScreenCoords)) + continue; + + // bound glyph bottomRight to screenCoords bottomRight + if(glyphScreenCoords.bottom() > screenCoords.bottom()) { + glyphTextureCoords.setBottom(glyphTextureCoords.bottom() + (screenCoords.bottom() - glyphScreenCoords.bottom())); + glyphScreenCoords.setBottom(screenCoords.bottom()); + } + if(glyphScreenCoords.right() > screenCoords.right()) { + glyphTextureCoords.setRight(glyphTextureCoords.right() + (screenCoords.right() - glyphScreenCoords.right())); + glyphScreenCoords.setRight(screenCoords.right()); + } + + // render glyph + g_graphics.drawTexturedRect(glyphScreenCoords, m_texture, glyphTextureCoords, color); + } } -std::vector Font::calculateGlyphsPositions(const std::string& text, int align, Size *textBoxSize) +const std::vector& Font::calculateGlyphsPositions(const std::string& text, int align, Size *textBoxSize) const { - int numGlyphs = text.length(); - std::vector glyphsPositions(numGlyphs); - if(numGlyphs == 0) { - if(textBoxSize) - textBoxSize->setSize(0,0); - return glyphsPositions; - } + // for performance reasons we use statics vectors that are allocated on demand + static std::vector glyphsPositions(1); + static std::vector lineWidths(1); - std::vector lineWidths(numGlyphs); + int textLength = text.length(); int maxLineWidth = 0; int lines = 0; int glyph; int i; + // return if there is no text + if(textLength == 0) { + if(textBoxSize) + textBoxSize->setSize(0,0); + return glyphsPositions; + } + + // resize glyphsPositions vector, if needed + if(textLength > (int)glyphsPositions.size()) + glyphsPositions.resize(textLength); + // calculate lines width if((align & ALIGN_RIGHT || align & ALIGN_HORIZONTAL_CENTER) || textBoxSize) { lineWidths[0] = 0; - for(i = 0; i< numGlyphs; ++i) { + for(i = 0; i< textLength; ++i) { glyph = (uchar)text[i]; if(glyph == (uchar)'\n') { - lineWidths[++lines] = 0; + lines++; + if(lines+1 > (int)lineWidths.size()) + lineWidths.resize(lines+1); + lineWidths[lines] = 0; } else if(glyph >= 32) { lineWidths[lines] += m_glyphsSize[glyph].width() + m_glyphSpacing.width(); maxLineWidth = std::max(maxLineWidth, lineWidths[lines]); @@ -182,7 +247,7 @@ std::vector Font::calculateGlyphsPositions(const std::string& text, int a Point virtualPos(0, m_topMargin); lines = 0; - for(i = 0; i < numGlyphs; ++i) { + for(i = 0; i < textLength; ++i) { glyph = (uchar)text[i]; // store current glyph topLeft diff --git a/src/framework/graphics/font.h b/src/framework/graphics/font.h index 9580c20b..38fd05cd 100644 --- a/src/framework/graphics/font.h +++ b/src/framework/graphics/font.h @@ -42,8 +42,6 @@ enum EAlign { ALIGN_BOTTOM_LEFT = ALIGN_BOTTOM | ALIGN_LEFT }; -class TextArea; - class Font { public: @@ -55,22 +53,19 @@ public: /// Load font from file bool load(const std::string &file); - /// Simple text render starting at pos + /// Simple text render starting at startPos void renderText(const std::string& text, const Point& startPos, const Color& color = Color::white); - /** Advanced text render - * screenCoords is the rect that will be filled on the screen - * startRenderPosition is the postion to start rendering relative to the text rect - */ + /// Advanced text render void renderText(const std::string& text, const Rect& screenCoords, int align = ALIGN_TOP_LEFT, const Color& color = Color::white); /// Calculate glyphs positions to use on render, also calculates textBoxSize if wanted - std::vector calculateGlyphsPositions(const std::string& text, int align = ALIGN_TOP_LEFT, Size *textBoxSize = NULL); + const std::vector& calculateGlyphsPositions(const std::string& text, int align = ALIGN_TOP_LEFT, Size *textBoxSize = NULL) const; /// Simulate render and calculate text size Size calculateTextRectSize(const std::string& text); diff --git a/src/framework/graphics/graphics.cpp b/src/framework/graphics/graphics.cpp index ee648e35..2cd05681 100644 --- a/src/framework/graphics/graphics.cpp +++ b/src/framework/graphics/graphics.cpp @@ -48,7 +48,7 @@ void Graphics::init() void Graphics::terminate() { - + m_bindedTexture = TexturePtr(); } bool Graphics::isExtensionSupported(const char *extension) @@ -88,6 +88,8 @@ void Graphics::resize(const Size& size) void Graphics::restoreViewport() { + disableDrawing(); + const int& width = m_screenSize.width(); const int& height = m_screenSize.height(); @@ -116,11 +118,23 @@ void Graphics::beginRender() glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); + + // quads is the default drawing mode + glBegin(GL_QUADS); + m_drawMode = DRAW_QUADS; } void Graphics::endRender() { + // end last drawing + glEnd(); + m_drawMode = DRAW_NONE; +} +void Graphics::disableDrawing() +{ + glEnd(); + m_drawMode = DRAW_NONE; } void Graphics::drawTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords, const Color& color) @@ -128,8 +142,6 @@ void Graphics::drawTexturedRect(const Rect& screenCoords, const TexturePtr& text if(screenCoords.isEmpty()) return; - glColor4ubv(color.rgbaPtr()); - // rect correction for opengl int right = screenCoords.right() + 1; int bottom = screenCoords.bottom() + 1; @@ -149,15 +161,11 @@ void Graphics::drawTexturedRect(const Rect& screenCoords, const TexturePtr& text textureLeft = (float)textureCoords.left() / textureSize.width(); } - glBindTexture(GL_TEXTURE_2D, texture->getTextureId()); - glBegin(GL_QUADS); - + bindTexture(texture, color); glTexCoord2f(textureLeft, textureTop); glVertex2i(left, top); glTexCoord2f(textureLeft, textureBottom); glVertex2i(left, bottom); glTexCoord2f(textureRight, textureBottom); glVertex2i(right, bottom); glTexCoord2f(textureRight, textureTop); glVertex2i(right, top); - - glEnd(); } void Graphics::drawRepeatedTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords, const Color& color) @@ -193,24 +201,17 @@ void Graphics::drawFilledRect(const Rect& screenCoords, const Color& color) if(screenCoords.isEmpty()) return; - glDisable(GL_TEXTURE_2D); - - glColor4ubv(color.rgbaPtr()); - // rect correction for opengl int right = screenCoords.right() + 1; int bottom = screenCoords.bottom() + 1; int top = screenCoords.top(); int left = screenCoords.left(); - glBegin(GL_QUADS); + bindColor(color); glVertex2i(left, top); glVertex2i(left, bottom); glVertex2i(right, bottom); glVertex2i(right, top); - glEnd(); - - glEnable(GL_TEXTURE_2D); } @@ -219,41 +220,71 @@ void Graphics::drawBoundingRect(const Rect& screenCoords, const Color& color, in if(2 * innerLineWidth > screenCoords.height() || screenCoords.isEmpty()) return; - glDisable(GL_TEXTURE_2D); - - glColor4ubv(color.rgbaPtr()); - // rect correction for opengl int right = screenCoords.right()+1; int bottom = screenCoords.bottom()+1; int top = screenCoords.top(); int left = screenCoords.left(); - glBegin(GL_QUADS); - // top line - glVertex2i(left, top); - glVertex2i(left, top + innerLineWidth); - glVertex2i(right, top + innerLineWidth); - glVertex2i(right, top); - - // left - glVertex2i(left, screenCoords.top() + innerLineWidth); - glVertex2i(left, bottom - innerLineWidth); - glVertex2i(left + innerLineWidth, bottom - innerLineWidth); - glVertex2i(left + innerLineWidth, screenCoords.top() + innerLineWidth); - - // bottom line - glVertex2i(left, bottom); - glVertex2i(left, bottom - innerLineWidth); - glVertex2i(right, bottom - innerLineWidth); - glVertex2i(right, bottom); - - // right line - glVertex2i(right , top + innerLineWidth); - glVertex2i(right , bottom - innerLineWidth); - glVertex2i(right - innerLineWidth, bottom - innerLineWidth); - glVertex2i(right - innerLineWidth, top + innerLineWidth); - glEnd(); + bindColor(color); + + // top line + glVertex2i(left, top); + glVertex2i(left, top + innerLineWidth); + glVertex2i(right, top + innerLineWidth); + glVertex2i(right, top); + + // left + glVertex2i(left, screenCoords.top() + innerLineWidth); + glVertex2i(left, bottom - innerLineWidth); + glVertex2i(left + innerLineWidth, bottom - innerLineWidth); + glVertex2i(left + innerLineWidth, screenCoords.top() + innerLineWidth); + + // bottom line + glVertex2i(left, bottom); + glVertex2i(left, bottom - innerLineWidth); + glVertex2i(right, bottom - innerLineWidth); + glVertex2i(right, bottom); + + // right line + glVertex2i(right , top + innerLineWidth); + glVertex2i(right , bottom - innerLineWidth); + glVertex2i(right - innerLineWidth, bottom - innerLineWidth); + glVertex2i(right - innerLineWidth, top + innerLineWidth); +} + +void Graphics::bindColor(const Color& color) +{ + // switch drawing to colored quads + if(m_drawMode != DRAW_COLOR_QUADS || m_bindedColor != color) { + if(m_drawMode != DRAW_NONE) + glEnd(); + glDisable(GL_TEXTURE_2D); + if(m_bindedColor != color) { + glColor4ubv(color.rgbaPtr()); + m_bindedColor = color; + } + glBegin(GL_QUADS); + m_drawMode = DRAW_COLOR_QUADS; + } +} - glEnable(GL_TEXTURE_2D); +void Graphics::bindTexture(const TexturePtr& texture, const Color& color) +{ + // switch drawing to textured quads + if(m_drawMode != DRAW_TEXTURE_QUADS || m_bindedTexture != texture) { + if(m_drawMode != DRAW_NONE) + glEnd(); + glEnable(GL_TEXTURE_2D); + if(m_bindedTexture != texture) { + glBindTexture(GL_TEXTURE_2D, texture->getTextureId()); + m_bindedTexture = texture; + } + if(m_bindedColor != color) { + glColor4ubv(color.rgbaPtr()); + m_bindedColor = color; + } + glBegin(GL_QUADS); + m_drawMode = DRAW_TEXTURE_QUADS; + } } diff --git a/src/framework/graphics/graphics.h b/src/framework/graphics/graphics.h index 7104457f..52f75bad 100644 --- a/src/framework/graphics/graphics.h +++ b/src/framework/graphics/graphics.h @@ -30,8 +30,17 @@ class Graphics { + enum EDrawMode { + DRAW_NONE = 0, + DRAW_QUADS = 1, + DRAW_TEXTURE = 2, + DRAW_COLORED = 4, + DRAW_COLOR_QUADS = DRAW_QUADS | DRAW_COLORED, + DRAW_TEXTURE_QUADS = DRAW_QUADS | DRAW_TEXTURE | DRAW_COLORED + }; + public: - Graphics() { } + Graphics() : m_drawMode(DRAW_NONE) { } /// Initialize graphics void init(); @@ -56,13 +65,23 @@ public: const Size& getScreenSize() const { return m_screenSize; } + void disableDrawing(); + void enableDrawing(); + void drawTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords = Rect(), const Color& color = Color::white); void drawRepeatedTexturedRect(const Rect& screenCoords, const TexturePtr& texture, const Rect& textureCoords, const Color& color = Color::white); void drawFilledRect(const Rect& screenCoords, const Color& color); void drawBoundingRect(const Rect& screenCoords, const Color& color = Color::green, int innerLineWidth = 1); private: + void bindTexture(const TexturePtr& texture, const Color& color = Color::white); + void bindColor(const Color& color); + + TexturePtr m_bindedTexture; + Color m_bindedColor; Size m_screenSize; + EDrawMode m_drawMode; + EDrawMode m_lastDrawMode; }; extern Graphics g_graphics; diff --git a/src/framework/graphics/textarea.cpp b/src/framework/graphics/textarea.cpp index b62be4e5..c6a2e1fd 100644 --- a/src/framework/graphics/textarea.cpp +++ b/src/framework/graphics/textarea.cpp @@ -82,22 +82,24 @@ void TextArea::draw() void TextArea::recalculate() { - // prevent glitches from invalid rects - if(!m_screenCoords.isValid()) - return; - int textLenght = m_text.length(); - m_glyphsCoords.clear(); - m_glyphsTexCoords.clear(); - m_glyphsCoords.resize(textLenght); - m_glyphsTexCoords.resize(textLenght); + // prevent glitches + if(!m_screenCoords.isValid() || !m_font) + return; // map glyphs positions Size textBoxSize; - std::vector glyphsPositions = m_font->calculateGlyphsPositions(m_text, m_align, &textBoxSize); + const std::vector& glyphsPositions = m_font->calculateGlyphsPositions(m_text, m_align, &textBoxSize); const Rect *glyphsTextureCoords = m_font->getGlyphsTextureCoords(); const Size *glyphsSize = m_font->getGlyphsSize(); + int glyph; + + // resize just on demand + if(textLenght > (int)m_glyphsCoords.size()) { + m_glyphsCoords.resize(textLenght); + m_glyphsTexCoords.resize(textLenght); + } // readjust start view area based on cursor position if(m_cursorPos >= 0 && textLenght > 0) { @@ -111,7 +113,7 @@ void TextArea::recalculate() { Rect virtualRect(m_startInternalPos, m_screenCoords.size()); // previus rendered virtual rect int pos = m_cursorPos - 1; // element before cursor - int glyph = (uchar)m_text[pos]; // glyph of the element before cursor + glyph = (uchar)m_text[pos]; // glyph of the element before cursor Rect glyphRect(glyphsPositions[pos], glyphsSize[glyph]); // if the cursor is not on the previus rendered virtual rect we need to update it @@ -148,8 +150,8 @@ void TextArea::recalculate() m_drawArea.setBottom(m_screenCoords.bottom()); for(int i = 0; i < textLenght; ++i) { - int glyph = (uchar)m_text[i]; - m_glyphsCoords[i] = Rect(); + glyph = (uchar)m_text[i]; + m_glyphsCoords[i].clear(); // skip invalid glyphs if(glyph < 32) @@ -218,41 +220,51 @@ void TextArea::recalculate() void TextArea::setFont(Font* font) { - m_font = font; - recalculate(); + if(m_font != font) { + m_font = font; + recalculate(); + } } void TextArea::setText(const std::string& text) { - m_text = text; - if(m_cursorPos >= 0) { - m_cursorPos = 0; - m_cursorTicks = g_engine.getLastFrameTicks(); + if(m_text != text) { + m_text = text; + if(m_cursorPos >= 0) { + m_cursorPos = 0; + m_cursorTicks = g_engine.getLastFrameTicks(); + } + recalculate(); } - recalculate(); } -void TextArea::setScreenCoords(Rect screenCoords) +void TextArea::setScreenCoords(const Rect& screenCoords) { - m_screenCoords = screenCoords; - recalculate(); + if(screenCoords != m_screenCoords) { + m_screenCoords = screenCoords; + recalculate(); + } } void TextArea::setAlign(int align) { - m_align = align; - recalculate(); + if(m_align != align) { + m_align = align; + recalculate(); + } } void TextArea::setCursorPos(int pos) { - if(pos < 0) - m_cursorPos = 0; - else if((uint)pos >= m_text.length()) - m_cursorPos = m_text.length(); - else - m_cursorPos = pos; - recalculate(); + if(pos != m_cursorPos) { + if(pos < 0) + m_cursorPos = 0; + else if((uint)pos >= m_text.length()) + m_cursorPos = m_text.length(); + else + m_cursorPos = pos; + recalculate(); + } } void TextArea::enableCursor(bool enable) diff --git a/src/framework/graphics/textarea.h b/src/framework/graphics/textarea.h index fa88e568..3b0ce272 100644 --- a/src/framework/graphics/textarea.h +++ b/src/framework/graphics/textarea.h @@ -42,7 +42,7 @@ public: void setFont(Font *font); void setText(const std::string& text); - void setScreenCoords(Rect screenCoords); + void setScreenCoords(const Rect& screenCoords); void setAlign(int align); void setColor(const Color& color) { m_color = color; } void setCursorPos(int pos); @@ -55,8 +55,9 @@ public: const std::string& getText() const { return m_text; } + Font *getFont() const { return m_font; } int getTextPos(Point pos); - + private: void recalculate(); @@ -76,4 +77,6 @@ private: std::vector m_glyphsTexCoords; }; +typedef boost::shared_ptr