| @@ -0,0 +1,174 @@ | |||
| 1 | + | #include "atlas.h" | |
| 2 | + | #include "framebuffermanager.h" | |
| 3 | + | #include "painter.h" | |
| 4 | + | #include "graphics.h" | |
| 5 | + | #include <framework/core/clock.h> | |
| 6 | + | #include <framework/platform/platform.h> | |
| 7 | + | ||
| 8 | + | Atlas g_atlas; | |
| 9 | + | ||
| 10 | + | void Atlas::init() | |
| 11 | + | { | |
| 12 | + | // If you don't care about players with old computers (~5% of players) you can change 4096 to 6144 or8192 | |
| 13 | + | m_size = std::min<size_t>(8192, g_graphics.getMaxTextureSize()); | |
| 14 | + | g_logger.info(stdext::format("[Atlas] Texture size is: %ix%i (max: %ix%i)", m_size, m_size, g_graphics.getMaxTextureSize(), g_graphics.getMaxTextureSize())); | |
| 15 | + | ||
| 16 | + | for(size_t i = 0; i < 2; ++i) { | |
| 17 | + | m_atlas[i] = g_framebuffers.createFrameBuffer(); | |
| 18 | + | if (i == 0) { | |
| 19 | + | m_atlas[i]->resize(Size(m_size, m_size)); | |
| 20 | + | } else { // text atlas | |
| 21 | + | m_atlas[i]->setSmooth(false); | |
| 22 | + | #ifdef BIG_FONTS | |
| 23 | + | m_atlas[i]->resize(Size(m_size, m_size)); | |
| 24 | + | #else | |
| 25 | + | m_atlas[i]->resize(Size(2048, 2048)); | |
| 26 | + | #endif | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | glActiveTexture(GL_TEXTURE6 + i); | |
| 30 | + | glBindTexture(GL_TEXTURE_2D, m_atlas[i]->getTexture()->getId()); | |
| 31 | + | glActiveTexture(GL_TEXTURE0); | |
| 32 | + | } | |
| 33 | + | reset(); | |
| 34 | + | resetAtlas(1); | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | void Atlas::reset() | |
| 38 | + | { | |
| 39 | + | m_doReset = false; | |
| 40 | + | resetAtlas(0); | |
| 41 | + | m_cache.clear(); | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | void Atlas::reload() | |
| 45 | + | { | |
| 46 | + | reset(); | |
| 47 | + | resetAtlas(1); | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | void Atlas::resetAtlas(int location) { | |
| 51 | + | if (!m_atlas[location]) | |
| 52 | + | return; | |
| 53 | + | for (auto& location : m_locations[location]) | |
| 54 | + | location.clear(); | |
| 55 | + | ||
| 56 | + | size_t size = m_atlas[location]->getSize().width(); | |
| 57 | + | for (size_t i = 0; i < size; i += 2048) { | |
| 58 | + | m_locations[location][6].push_back(Point(i, i)); | |
| 59 | + | for (size_t x = 0; x < i; x += 2048) { | |
| 60 | + | m_locations[location][6].push_back(Point(i, x)); | |
| 61 | + | m_locations[location][6].push_back(Point(x, i)); | |
| 62 | + | } | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | m_atlas[location]->bind(); | |
| 66 | + | g_painter->clear(Color::alpha); | |
| 67 | + | m_atlas[location]->release(); | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | ||
| 71 | + | void Atlas::terminate() { | |
| 72 | + | for(auto& it : m_atlas) | |
| 73 | + | it = nullptr; | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | Point Atlas::cache(uint64_t hash, const Size& size, bool& draw) | |
| 77 | + | { | |
| 78 | + | if (m_doReset) { | |
| 79 | + | reset(); | |
| 80 | + | } | |
| 81 | + | auto it = m_cache.find(hash); | |
| 82 | + | if (it != m_cache.end()) { | |
| 83 | + | return it->second; | |
| 84 | + | } | |
| 85 | + | ||
| 86 | + | int index = calculateIndex(size); | |
| 87 | + | if (index < 0 || index > 4 || (index == 4 && m_size == 2048)) { // too big to be cached, max 512x512 | |
| 88 | + | draw = false; | |
| 89 | + | return Point(-1, -1); | |
| 90 | + | } | |
| 91 | + | ||
| 92 | + | if (m_locations[0][index].empty() && !findSpace(0, index)) { | |
| 93 | + | draw = false; | |
| 94 | + | m_doReset = true; | |
| 95 | + | return Point(-1, -1); | |
| 96 | + | } | |
| 97 | + | ||
| 98 | + | Point location = m_locations[0][index].front(); | |
| 99 | + | m_locations[0][index].pop_front(); | |
| 100 | + | m_cache.emplace(hash, location); | |
| 101 | + | draw = true; | |
| 102 | + | return location; | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | void Atlas::bind() | |
| 106 | + | { | |
| 107 | + | m_atlas[0]->bind(); | |
| 108 | + | g_painter->setCompositionMode(Painter::CompositionMode_Replace); | |
| 109 | + | } | |
| 110 | + | ||
| 111 | + | void Atlas::release() | |
| 112 | + | { | |
| 113 | + | m_atlas[0]->release(); | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | Point Atlas::cacheFont(const TexturePtr& fontTexture) | |
| 117 | + | { | |
| 118 | + | fontTexture->update(); | |
| 119 | + | int index = calculateIndex(fontTexture->getSize()); | |
| 120 | + | if (index < 0) { | |
| 121 | + | g_logger.fatal("[Atlas] Too big font texture. Max is 2048x2048"); | |
| 122 | + | } | |
| 123 | + | if (m_locations[1][index].empty() && !findSpace(1, index)) { | |
| 124 | + | g_logger.fatal("[Atlas] Out of space for new fonts, compile with BIG_FONTS or DONT_CACHE_FONTS definition"); | |
| 125 | + | } | |
| 126 | + | Point location = m_locations[1][index].front(); | |
| 127 | + | m_locations[1][index].pop_front(); | |
| 128 | + | m_atlas[1]->bind(); | |
| 129 | + | g_painter->setCompositionMode(Painter::CompositionMode_Replace); | |
| 130 | + | g_painter->drawTexturedRect(Rect(location, fontTexture->getSize()), fontTexture); | |
| 131 | + | m_atlas[1]->release(); | |
| 132 | + | return location; | |
| 133 | + | } | |
| 134 | + | ||
| 135 | + | int Atlas::calculateIndex(const Size& size) | |
| 136 | + | { | |
| 137 | + | int s = std::max<int>(size.width(), size.height()); | |
| 138 | + | if (s <= 64) | |
| 139 | + | return s <= 32 ? 0 : 1; | |
| 140 | + | if (s <= 256) | |
| 141 | + | return s <= 128 ? 2 : 3; | |
| 142 | + | if (s <= 1024) | |
| 143 | + | return s <= 512 ? 4 : 5; | |
| 144 | + | return s <= 2048 ? 6 : -1; | |
| 145 | + | } | |
| 146 | + | ||
| 147 | + | bool Atlas::findSpace(int location, int index) { | |
| 148 | + | static const size_t sizes[7] = { 32, 64, 128, 256, 512, 1024, 2048 }; | |
| 149 | + | if (location >= 2 || index >= 6) { | |
| 150 | + | return false; | |
| 151 | + | } | |
| 152 | + | if (m_locations[location][index + 1].size() == 0 && !findSpace(location, index + 1)) { | |
| 153 | + | return false; | |
| 154 | + | } | |
| 155 | + | auto pos = m_locations[location][index + 1].front(); | |
| 156 | + | m_locations[location][index + 1].pop_front(); | |
| 157 | + | m_locations[location][index].push_back(pos); | |
| 158 | + | m_locations[location][index].push_back(Point(pos.x, pos.y + sizes[index])); | |
| 159 | + | m_locations[location][index].push_back(Point(pos.x + sizes[index], pos.y)); | |
| 160 | + | m_locations[location][index].push_back(Point(pos.x + sizes[index], pos.y + sizes[index])); | |
| 161 | + | return true; | |
| 162 | + | } | |
| 163 | + | ||
| 164 | + | std::string Atlas::getStats() { | |
| 165 | + | std::stringstream ss; | |
| 166 | + | for (int l = 0; l < 2; ++l) { | |
| 167 | + | for (auto& it : m_locations[l]) { | |
| 168 | + | ss << it.size() << " "; | |
| 169 | + | } | |
| 170 | + | ss << "| "; | |
| 171 | + | } | |
| 172 | + | ss << "(" << m_size << "|" << g_graphics.getMaxTextureSize() << ")"; | |
| 173 | + | return ss.str(); | |
| 174 | + | } |
| @@ -0,0 +1,39 @@ | |||
| 1 | + | #ifndef ATLAS_H | |
| 2 | + | #define ATLAS_H | |
| 3 | + | ||
| 4 | + | #include "drawqueue.h" | |
| 5 | + | #include "framebuffer.h" | |
| 6 | + | #include <map> | |
| 7 | + | #include <vector> | |
| 8 | + | ||
| 9 | + | class Atlas { | |
| 10 | + | public: | |
| 11 | + | void init(); | |
| 12 | + | void terminate(); | |
| 13 | + | void reload(); | |
| 14 | + | ||
| 15 | + | Point cache(uint64_t hash, const Size& size, bool& draw); | |
| 16 | + | Point cacheFont(const TexturePtr& fontTexture); | |
| 17 | + | ||
| 18 | + | TexturePtr get(int location) { return m_atlas[location]->getTexture(); } | |
| 19 | + | void bind(); | |
| 20 | + | void release(); | |
| 21 | + | ||
| 22 | + | std::string getStats(); // not thread safe! | |
| 23 | + | ||
| 24 | + | private: | |
| 25 | + | void reset(); | |
| 26 | + | void resetAtlas(int location); | |
| 27 | + | bool findSpace(int location, int index); | |
| 28 | + | inline int calculateIndex(const Size& size); | |
| 29 | + | ||
| 30 | + | FrameBufferPtr m_atlas[2]; | |
| 31 | + | std::map<uint64_t, Point> m_cache; | |
| 32 | + | std::list<Point> m_locations[2][7]; | |
| 33 | + | size_t m_size; | |
| 34 | + | bool m_doReset = false; | |
| 35 | + | }; | |
| 36 | + | ||
| 37 | + | extern Atlas g_atlas; | |
| 38 | + | ||
| 39 | + | #endif |
| @@ -0,0 +1,389 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "atlas.h" | |
| 24 | + | #include "bitmapfont.h" | |
| 25 | + | #include "texturemanager.h" | |
| 26 | + | #include "graphics.h" | |
| 27 | + | #include "image.h" | |
| 28 | + | ||
| 29 | + | #include <framework/core/eventdispatcher.h> | |
| 30 | + | #include <framework/otml/otml.h> | |
| 31 | + | #include <framework/util/extras.h> | |
| 32 | + | ||
| 33 | + | void BitmapFont::load(const OTMLNodePtr& fontNode) | |
| 34 | + | { | |
| 35 | + | OTMLNodePtr textureNode = fontNode->at("texture"); | |
| 36 | + | std::string textureFile = stdext::resolve_path(textureNode->value(), textureNode->source()); | |
| 37 | + | Size glyphSize = fontNode->valueAt<Size>("glyph-size"); | |
| 38 | + | m_glyphHeight = fontNode->valueAt<int>("height"); | |
| 39 | + | m_yOffset = fontNode->valueAt("y-offset", 0); | |
| 40 | + | m_firstGlyph = fontNode->valueAt("first-glyph", 32); | |
| 41 | + | m_glyphSpacing = fontNode->valueAt("spacing", Size(0,0)); | |
| 42 | + | int spaceWidth = fontNode->valueAt("space-width", glyphSize.width()); | |
| 43 | + | ||
| 44 | + | if(OTMLNodePtr node = fontNode->get("fixed-glyph-width")) { | |
| 45 | + | for(int glyph = m_firstGlyph; glyph < 256; ++glyph) | |
| 46 | + | m_glyphsSize[glyph] = Size(node->value<int>(), m_glyphHeight); | |
| 47 | + | } else { | |
| 48 | + | calculateGlyphsWidthsAutomatically(Image::load(textureFile), glyphSize); | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | // 32 is space | |
| 52 | + | m_glyphsSize[32].setWidth(spaceWidth); | |
| 53 | + | ||
| 54 | + | // use 127 as spacer [Width: 1], Important for the current NPC highlighting system | |
| 55 | + | m_glyphsSize[127].setWidth(1); | |
| 56 | + | ||
| 57 | + | // new line actually has a size that will be useful in multiline algorithm | |
| 58 | + | m_glyphsSize[(uchar)'\n'] = Size(1, m_glyphHeight); | |
| 59 | + | ||
| 60 | + | // read custom widths | |
| 61 | + | /* | |
| 62 | + | if(OTMLNodePtr node = fontNode->get("glyph-widths")) { | |
| 63 | + | for(const OTMLNodePtr& child : node->children()) | |
| 64 | + | m_glyphsSize[stdext::safe_cast<int>(child->tag())].setWidth(child->value<int>()); | |
| 65 | + | } | |
| 66 | + | */ | |
| 67 | + | ||
| 68 | + | // load font texture | |
| 69 | + | m_texture = g_textures.getTexture(textureFile); | |
| 70 | + | if (!m_texture) | |
| 71 | + | return; | |
| 72 | + | m_texture->update(); | |
| 73 | + | ||
| 74 | + | int numHorizontalGlyphs = m_texture->getSize().width() / glyphSize.width(); | |
| 75 | + | #ifdef DONT_CACHE_FONTS | |
| 76 | + | Point offset(0, 0); | |
| 77 | + | #else | |
| 78 | + | Point offset = g_atlas.cacheFont(m_texture); | |
| 79 | + | m_texture = g_atlas.get(1); | |
| 80 | + | #endif | |
| 81 | + | for (int glyph = m_firstGlyph; glyph < 256; ++glyph) { | |
| 82 | + | m_glyphsTextureCoords[glyph].setRect(((glyph - m_firstGlyph) % numHorizontalGlyphs) * glyphSize.width() + offset.x, | |
| 83 | + | ((glyph - m_firstGlyph) / numHorizontalGlyphs) * glyphSize.height() + offset.y, | |
| 84 | + | m_glyphsSize[glyph].width(), | |
| 85 | + | m_glyphHeight); | |
| 86 | + | } | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | void BitmapFont::drawText(const std::string& text, const Point& startPos, const Color& color, bool shadow) | |
| 90 | + | { | |
| 91 | + | Size boxSize = g_painter->getResolution() - startPos.toSize(); | |
| 92 | + | Rect screenCoords(startPos, boxSize); | |
| 93 | + | drawText(text, screenCoords, Fw::AlignTopLeft, shadow); | |
| 94 | + | } | |
| 95 | + | ||
| 96 | + | void BitmapFont::drawText(const std::string& text, const Rect& screenCoords, Fw::AlignmentFlag align, const Color& color, bool shadow) | |
| 97 | + | { | |
| 98 | + | g_drawQueue->addText(static_self_cast<BitmapFont>(), text, screenCoords, align, color, shadow); | |
| 99 | + | } | |
| 100 | + | ||
| 101 | + | void BitmapFont::drawColoredText(const std::string& text, const Rect& screenCoords, Fw::AlignmentFlag align, const std::vector<std::pair<int, Color>>& colors, bool shadow) | |
| 102 | + | { | |
| 103 | + | g_drawQueue->addColoredText(static_self_cast<BitmapFont>(), text, screenCoords, align, colors, shadow); | |
| 104 | + | } | |
| 105 | + | ||
| 106 | + | void BitmapFont::calculateDrawTextCoords(CoordsBuffer& coordsBuffer, const std::string& text, const Rect& screenCoords, Fw::AlignmentFlag align) | |
| 107 | + | { | |
| 108 | + | // prevent glitches from invalid rects | |
| 109 | + | if (!screenCoords.isValid() || !m_texture) | |
| 110 | + | return; | |
| 111 | + | ||
| 112 | + | int textLenght = text.length(); | |
| 113 | + | ||
| 114 | + | // map glyphs positions | |
| 115 | + | Size textBoxSize; | |
| 116 | + | const std::vector<Point>& glyphsPositions = calculateGlyphsPositions(text, align, &textBoxSize); | |
| 117 | + | ||
| 118 | + | for (int i = 0; i < textLenght; ++i) { | |
| 119 | + | int glyph = (uchar)text[i]; | |
| 120 | + | ||
| 121 | + | // skip invalid glyphs | |
| 122 | + | if (glyph < 32) | |
| 123 | + | continue; | |
| 124 | + | ||
| 125 | + | // calculate initial glyph rect and texture coords | |
| 126 | + | Rect glyphScreenCoords(glyphsPositions[i], m_glyphsSize[glyph]); | |
| 127 | + | Rect glyphTextureCoords = m_glyphsTextureCoords[glyph]; | |
| 128 | + | ||
| 129 | + | // first translate to align position | |
| 130 | + | if (align & Fw::AlignBottom) { | |
| 131 | + | glyphScreenCoords.translate(0, screenCoords.height() - textBoxSize.height()); | |
| 132 | + | } else if (align & Fw::AlignVerticalCenter) { | |
| 133 | + | glyphScreenCoords.translate(0, (screenCoords.height() - textBoxSize.height()) / 2); | |
| 134 | + | } else { // AlignTop | |
| 135 | + | // nothing to do | |
| 136 | + | } | |
| 137 | + | ||
| 138 | + | if (align & Fw::AlignRight) { | |
| 139 | + | glyphScreenCoords.translate(screenCoords.width() - textBoxSize.width(), 0); | |
| 140 | + | } else if (align & Fw::AlignHorizontalCenter) { | |
| 141 | + | glyphScreenCoords.translate((screenCoords.width() - textBoxSize.width()) / 2, 0); | |
| 142 | + | } else { // AlignLeft | |
| 143 | + | // nothing to do | |
| 144 | + | } | |
| 145 | + | ||
| 146 | + | // only render glyphs that are after 0, 0 | |
| 147 | + | if (glyphScreenCoords.bottom() < 0 || glyphScreenCoords.right() < 0) | |
| 148 | + | continue; | |
| 149 | + | ||
| 150 | + | // bound glyph topLeft to 0,0 if needed | |
| 151 | + | if (glyphScreenCoords.top() < 0) { | |
| 152 | + | glyphTextureCoords.setTop(glyphTextureCoords.top() - glyphScreenCoords.top()); | |
| 153 | + | glyphScreenCoords.setTop(0); | |
| 154 | + | } | |
| 155 | + | if (glyphScreenCoords.left() < 0) { | |
| 156 | + | glyphTextureCoords.setLeft(glyphTextureCoords.left() - glyphScreenCoords.left()); | |
| 157 | + | glyphScreenCoords.setLeft(0); | |
| 158 | + | } | |
| 159 | + | ||
| 160 | + | // translate rect to screen coords | |
| 161 | + | glyphScreenCoords.translate(screenCoords.topLeft()); | |
| 162 | + | ||
| 163 | + | // only render if glyph rect is visible on screenCoords | |
| 164 | + | if (!screenCoords.intersects(glyphScreenCoords)) | |
| 165 | + | continue; | |
| 166 | + | ||
| 167 | + | // bound glyph bottomRight to screenCoords bottomRight | |
| 168 | + | if (glyphScreenCoords.bottom() > screenCoords.bottom()) { | |
| 169 | + | glyphTextureCoords.setBottom(glyphTextureCoords.bottom() + (screenCoords.bottom() - glyphScreenCoords.bottom())); | |
| 170 | + | glyphScreenCoords.setBottom(screenCoords.bottom()); | |
| 171 | + | } | |
| 172 | + | if (glyphScreenCoords.right() > screenCoords.right()) { | |
| 173 | + | glyphTextureCoords.setRight(glyphTextureCoords.right() + (screenCoords.right() - glyphScreenCoords.right())); | |
| 174 | + | glyphScreenCoords.setRight(screenCoords.right()); | |
| 175 | + | } | |
| 176 | + | ||
| 177 | + | // render glyph | |
| 178 | + | coordsBuffer.addRect(glyphScreenCoords, glyphTextureCoords); | |
| 179 | + | } | |
| 180 | + | } | |
| 181 | + | ||
| 182 | + | const std::vector<Point>& BitmapFont::calculateGlyphsPositions(const std::string& text, | |
| 183 | + | Fw::AlignmentFlag align, | |
| 184 | + | Size *textBoxSize) | |
| 185 | + | { | |
| 186 | + | // for performance reasons we use statics vectors that are allocated on demand | |
| 187 | + | static thread_local std::vector<Point> glyphsPositions(1); | |
| 188 | + | static thread_local std::vector<int> lineWidths(1); | |
| 189 | + | ||
| 190 | + | int textLength = text.length(); | |
| 191 | + | int maxLineWidth = 0; | |
| 192 | + | int lines = 0; | |
| 193 | + | int glyph; | |
| 194 | + | int i; | |
| 195 | + | ||
| 196 | + | // return if there is no text | |
| 197 | + | if(textLength == 0) { | |
| 198 | + | if(textBoxSize) | |
| 199 | + | textBoxSize->resize(0,m_glyphHeight); | |
| 200 | + | return glyphsPositions; | |
| 201 | + | } | |
| 202 | + | ||
| 203 | + | // resize glyphsPositions vector when needed | |
| 204 | + | if(textLength > (int)glyphsPositions.size()) | |
| 205 | + | glyphsPositions.resize(textLength); | |
| 206 | + | ||
| 207 | + | // calculate lines width | |
| 208 | + | if((align & Fw::AlignRight || align & Fw::AlignHorizontalCenter) || textBoxSize) { | |
| 209 | + | lineWidths[0] = 0; | |
| 210 | + | for(i = 0; i< textLength; ++i) { | |
| 211 | + | glyph = (uchar)text[i]; | |
| 212 | + | ||
| 213 | + | if(glyph == (uchar)'\n') { | |
| 214 | + | lines++; | |
| 215 | + | if(lines+1 > (int)lineWidths.size()) | |
| 216 | + | lineWidths.resize(lines+1); | |
| 217 | + | lineWidths[lines] = 0; | |
| 218 | + | } else if(glyph >= 32) { | |
| 219 | + | lineWidths[lines] += m_glyphsSize[glyph].width() ; | |
| 220 | + | if((i+1 != textLength && text[i+1] != '\n')) // only add space if letter is not the last or before a \n. | |
| 221 | + | lineWidths[lines] += m_glyphSpacing.width(); | |
| 222 | + | maxLineWidth = std::max<int>(maxLineWidth, lineWidths[lines]); | |
| 223 | + | } | |
| 224 | + | } | |
| 225 | + | } | |
| 226 | + | ||
| 227 | + | Point virtualPos(0, m_yOffset); | |
| 228 | + | lines = 0; | |
| 229 | + | for(i = 0; i < textLength; ++i) { | |
| 230 | + | glyph = (uchar)text[i]; | |
| 231 | + | ||
| 232 | + | // new line or first glyph | |
| 233 | + | if(glyph == (uchar)'\n' || i == 0) { | |
| 234 | + | if(glyph == (uchar)'\n') { | |
| 235 | + | virtualPos.y += m_glyphHeight + m_glyphSpacing.height(); | |
| 236 | + | lines++; | |
| 237 | + | } | |
| 238 | + | ||
| 239 | + | // calculate start x pos | |
| 240 | + | if(align & Fw::AlignRight) { | |
| 241 | + | virtualPos.x = (maxLineWidth - lineWidths[lines]); | |
| 242 | + | } else if(align & Fw::AlignHorizontalCenter) { | |
| 243 | + | virtualPos.x = (maxLineWidth - lineWidths[lines]) / 2; | |
| 244 | + | } else { // AlignLeft | |
| 245 | + | virtualPos.x = 0; | |
| 246 | + | } | |
| 247 | + | } | |
| 248 | + | ||
| 249 | + | // store current glyph topLeft | |
| 250 | + | glyphsPositions[i] = virtualPos; | |
| 251 | + | ||
| 252 | + | // render only if the glyph is valid | |
| 253 | + | if(glyph >= 32 && glyph != (uchar)'\n') { | |
| 254 | + | virtualPos.x += m_glyphsSize[glyph].width() + m_glyphSpacing.width(); | |
| 255 | + | } | |
| 256 | + | } | |
| 257 | + | ||
| 258 | + | if(textBoxSize) { | |
| 259 | + | textBoxSize->setWidth(maxLineWidth); | |
| 260 | + | textBoxSize->setHeight(virtualPos.y + m_glyphHeight); | |
| 261 | + | } | |
| 262 | + | ||
| 263 | + | return glyphsPositions; | |
| 264 | + | } | |
| 265 | + | ||
| 266 | + | Size BitmapFont::calculateTextRectSize(const std::string& text) | |
| 267 | + | { | |
| 268 | + | Size size; | |
| 269 | + | calculateGlyphsPositions(text, Fw::AlignTopLeft, &size); | |
| 270 | + | return size; | |
| 271 | + | } | |
| 272 | + | ||
| 273 | + | std::string BitmapFont::wrapText(const std::string& text, int maxWidth, std::vector<std::pair<int, Color>>* colors) | |
| 274 | + | { | |
| 275 | + | std::string outText; | |
| 276 | + | outText.reserve(text.size() * 2); // string append optimization | |
| 277 | + | ||
| 278 | + | int lastSeparator = 0, lastColorSeparator = 0, lineLength = 0, wordLength = 0; | |
| 279 | + | for (size_t i = 0, c = 0; i < text.size(); ++i) { | |
| 280 | + | uchar glyph = (uchar)text[i]; | |
| 281 | + | if (text[i] == '\n' || text[i] == ' ') { | |
| 282 | + | lineLength += wordLength; | |
| 283 | + | if (lineLength > maxWidth) { // too long line with this word | |
| 284 | + | if (text[lastSeparator] == ' ') { | |
| 285 | + | c -= 1; | |
| 286 | + | updateColors(colors, lastColorSeparator, -1); | |
| 287 | + | lastSeparator += 1; | |
| 288 | + | lastColorSeparator += 1; | |
| 289 | + | } | |
| 290 | + | outText += '\n'; | |
| 291 | + | lineLength = wordLength; | |
| 292 | + | } | |
| 293 | + | for (size_t j = lastSeparator; j < i; ++j) { // copy word | |
| 294 | + | outText += text[j]; | |
| 295 | + | } | |
| 296 | + | if (text[i] == '\n') { // if new line was added reset line length | |
| 297 | + | outText += '\n'; | |
| 298 | + | wordLength = 0; | |
| 299 | + | lineLength = 0; | |
| 300 | + | lastSeparator = i + 1; | |
| 301 | + | lastColorSeparator = c; | |
| 302 | + | } else { // space | |
| 303 | + | wordLength = m_glyphsSize[glyph].width() + m_glyphSpacing.width(); // space | |
| 304 | + | lastSeparator = i; | |
| 305 | + | lastColorSeparator = c; | |
| 306 | + | c += 1; | |
| 307 | + | } | |
| 308 | + | continue; | |
| 309 | + | } | |
| 310 | + | ||
| 311 | + | if (glyph < 32) // invalid character | |
| 312 | + | continue; | |
| 313 | + | ||
| 314 | + | wordLength += m_glyphsSize[glyph].width() + m_glyphSpacing.width(); | |
| 315 | + | if (wordLength > maxWidth) { // too long word, split it | |
| 316 | + | if (lineLength != 0) { // add new line if current one is not empty | |
| 317 | + | outText += '\n'; | |
| 318 | + | } | |
| 319 | + | if (text[lastSeparator] == ' ') { // ignore space if it's first character in new line | |
| 320 | + | c -= 1; | |
| 321 | + | lastSeparator += 1; | |
| 322 | + | lastColorSeparator += 1; | |
| 323 | + | } | |
| 324 | + | for (size_t j = lastSeparator; j < i; ++j) { // copy word | |
| 325 | + | outText += text[j]; | |
| 326 | + | } | |
| 327 | + | updateColors(colors, lastColorSeparator, 1); | |
| 328 | + | outText += '-'; // word continuation | |
| 329 | + | outText += '\n'; // new line | |
| 330 | + | ||
| 331 | + | wordLength = m_glyphsSize[glyph].width() + m_glyphSpacing.width(); | |
| 332 | + | lineLength = 0; | |
| 333 | + | lastSeparator = i; | |
| 334 | + | lastColorSeparator = c; | |
| 335 | + | } | |
| 336 | + | c += 1; | |
| 337 | + | } | |
| 338 | + | ||
| 339 | + | lineLength += wordLength; | |
| 340 | + | if (lineLength > maxWidth) { // too long line with this word | |
| 341 | + | updateColors(colors, lastColorSeparator, 1); | |
| 342 | + | outText += '\n'; | |
| 343 | + | lineLength = wordLength; | |
| 344 | + | } | |
| 345 | + | for (size_t j = lastSeparator; j < text.size(); ++j) { // copy word | |
| 346 | + | outText += text[j]; | |
| 347 | + | } | |
| 348 | + | return outText; | |
| 349 | + | } | |
| 350 | + | ||
| 351 | + | void BitmapFont::calculateGlyphsWidthsAutomatically(const ImagePtr& image, const Size& glyphSize) | |
| 352 | + | { | |
| 353 | + | if (!image) | |
| 354 | + | return; | |
| 355 | + | ||
| 356 | + | int numHorizontalGlyphs = image->getSize().width() / glyphSize.width(); | |
| 357 | + | auto texturePixels = image->getPixels(); | |
| 358 | + | ||
| 359 | + | // small AI to auto calculate pixels widths | |
| 360 | + | for (int glyph = m_firstGlyph; glyph < 256; ++glyph) { | |
| 361 | + | Rect glyphCoords(((glyph - m_firstGlyph) % numHorizontalGlyphs) * glyphSize.width(), | |
| 362 | + | ((glyph - m_firstGlyph) / numHorizontalGlyphs) * glyphSize.height(), | |
| 363 | + | glyphSize.width(), | |
| 364 | + | m_glyphHeight); | |
| 365 | + | int width = glyphSize.width(); | |
| 366 | + | for (int x = glyphCoords.left(); x <= glyphCoords.right(); ++x) { | |
| 367 | + | int filledPixels = 0; | |
| 368 | + | // check if all vertical pixels are alpha | |
| 369 | + | for (int y = glyphCoords.top(); y <= glyphCoords.bottom(); ++y) { | |
| 370 | + | if (texturePixels[(y * image->getSize().width() * 4) + (x * 4) + 3] != 0) | |
| 371 | + | filledPixels++; | |
| 372 | + | } | |
| 373 | + | if (filledPixels > 0) | |
| 374 | + | width = x - glyphCoords.left() + 1; | |
| 375 | + | } | |
| 376 | + | // store glyph size | |
| 377 | + | m_glyphsSize[glyph].resize(width, m_glyphHeight); | |
| 378 | + | } | |
| 379 | + | } | |
| 380 | + | ||
| 381 | + | void BitmapFont::updateColors(std::vector<std::pair<int, Color>>* colors, int pos, int newTextLen) | |
| 382 | + | { | |
| 383 | + | if (!colors) return; | |
| 384 | + | for (auto& it : *colors) { | |
| 385 | + | if (it.first > pos) { | |
| 386 | + | it.first += newTextLen; | |
| 387 | + | } | |
| 388 | + | } | |
| 389 | + | } |
| @@ -0,0 +1,89 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef BITMAPFONT_H | |
| 24 | + | #define BITMAPFONT_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | #include "texture.h" | |
| 28 | + | ||
| 29 | + | #include <framework/otml/declarations.h> | |
| 30 | + | #include <framework/graphics/coordsbuffer.h> | |
| 31 | + | ||
| 32 | + | class BitmapFont : public stdext::shared_object | |
| 33 | + | { | |
| 34 | + | public: | |
| 35 | + | BitmapFont(const std::string& name) : m_name(name) { | |
| 36 | + | static int id = 1; | |
| 37 | + | m_id = id++; | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | /// Load font from otml node | |
| 41 | + | void load(const OTMLNodePtr& fontNode); | |
| 42 | + | ||
| 43 | + | /// Simple text render starting at startPos | |
| 44 | + | void drawText(const std::string& text, const Point& startPos, const Color& color = Color::white, bool shadow = false); | |
| 45 | + | ||
| 46 | + | /// Advanced text render delimited by a screen region and alignment | |
| 47 | + | void drawText(const std::string& text, const Rect& screenCoords, Fw::AlignmentFlag align = Fw::AlignTopLeft, const Color& color = Color::white, bool shadow = false); | |
| 48 | + | void drawColoredText(const std::string& text, const Rect& screenCoords, Fw::AlignmentFlag align, const std::vector<std::pair<int, Color>>& colors, bool shadow = false); | |
| 49 | + | ||
| 50 | + | void calculateDrawTextCoords(CoordsBuffer& coordsBuffer, const std::string& text, const Rect& screenCoords, Fw::AlignmentFlag align = Fw::AlignTopLeft); | |
| 51 | + | ||
| 52 | + | /// Calculate glyphs positions to use on render, also calculates textBoxSize if wanted | |
| 53 | + | const std::vector<Point>& calculateGlyphsPositions(const std::string& text, | |
| 54 | + | Fw::AlignmentFlag align = Fw::AlignTopLeft, | |
| 55 | + | Size* textBoxSize = NULL); | |
| 56 | + | ||
| 57 | + | /// Simulate render and calculate text size | |
| 58 | + | Size calculateTextRectSize(const std::string& text); | |
| 59 | + | ||
| 60 | + | std::string wrapText(const std::string& text, int maxWidth, std::vector<std::pair<int, Color>>* colors = nullptr); | |
| 61 | + | ||
| 62 | + | int getId() { return m_id; } | |
| 63 | + | std::string getName() { return m_name; } | |
| 64 | + | int getGlyphHeight() { return m_glyphHeight; } | |
| 65 | + | const Rect* getGlyphsTextureCoords() { return m_glyphsTextureCoords; } | |
| 66 | + | const Size* getGlyphsSize() { return m_glyphsSize; } | |
| 67 | + | const TexturePtr& getTexture() { return m_texture; } | |
| 68 | + | int getYOffset() { return m_yOffset; } | |
| 69 | + | Size getGlyphSpacing() { return m_glyphSpacing; } | |
| 70 | + | ||
| 71 | + | private: | |
| 72 | + | /// Calculates each font character by inspecting font bitmap | |
| 73 | + | void calculateGlyphsWidthsAutomatically(const ImagePtr& image, const Size& glyphSize); | |
| 74 | + | void updateColors(std::vector<std::pair<int, Color>>* colors, int pos, int newTextLen); | |
| 75 | + | ||
| 76 | + | std::string m_name; | |
| 77 | + | int m_glyphHeight; | |
| 78 | + | int m_firstGlyph; | |
| 79 | + | int m_yOffset; | |
| 80 | + | int m_id; | |
| 81 | + | Size m_glyphSpacing; | |
| 82 | + | TexturePtr m_texture; | |
| 83 | + | Rect m_glyphsTextureCoords[256]; | |
| 84 | + | Size m_glyphsSize[256]; | |
| 85 | + | }; | |
| 86 | + | ||
| 87 | + | ||
| 88 | + | #endif | |
| 89 | + |
| @@ -0,0 +1,81 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "cachedtext.h" | |
| 24 | + | #include "painter.h" | |
| 25 | + | #include "fontmanager.h" | |
| 26 | + | #include "bitmapfont.h" | |
| 27 | + | ||
| 28 | + | CachedText::CachedText() | |
| 29 | + | { | |
| 30 | + | m_font = g_fonts.getDefaultFont(); | |
| 31 | + | m_align = Fw::AlignCenter; | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | void CachedText::draw(const Rect& rect, const Color& color) | |
| 35 | + | { | |
| 36 | + | if(!m_font) | |
| 37 | + | return; | |
| 38 | + | ||
| 39 | + | if(m_textMustRecache || m_textCachedScreenCoords != rect) { | |
| 40 | + | m_textMustRecache = false; | |
| 41 | + | m_textCachedScreenCoords = rect; | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | if (m_textColors.empty()) { | |
| 45 | + | m_font->drawText(m_text, m_textCachedScreenCoords, Fw::AlignCenter, color); | |
| 46 | + | } else { | |
| 47 | + | m_font->drawColoredText(m_text, m_textCachedScreenCoords, Fw::AlignCenter, m_textColors); | |
| 48 | + | } | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | void CachedText::setColoredText(const std::vector<std::string>& texts) | |
| 52 | + | { | |
| 53 | + | m_text = ""; | |
| 54 | + | m_textColors.clear(); | |
| 55 | + | for (size_t i = 0, p = 0; i < texts.size() - 1; i += 2) { | |
| 56 | + | Color c(Color::white); | |
| 57 | + | stdext::cast<Color>(texts[i + 1], c); | |
| 58 | + | m_text += texts[i]; | |
| 59 | + | for (auto& c : texts[i]) { | |
| 60 | + | if ((uint8)c >= 32) | |
| 61 | + | p += 1; | |
| 62 | + | } | |
| 63 | + | m_textColors.push_back(std::make_pair(p, c)); | |
| 64 | + | } | |
| 65 | + | update(); | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | void CachedText::update() | |
| 69 | + | { | |
| 70 | + | if(m_font) | |
| 71 | + | m_textSize = m_font->calculateTextRectSize(m_text); | |
| 72 | + | m_textMustRecache = true; | |
| 73 | + | } | |
| 74 | + | ||
| 75 | + | void CachedText::wrapText(int maxWidth) | |
| 76 | + | { | |
| 77 | + | if(m_font) { | |
| 78 | + | m_text = m_font->wrapText(m_text, maxWidth, m_textColors.empty() ? nullptr : &m_textColors); | |
| 79 | + | update(); | |
| 80 | + | } | |
| 81 | + | } |
| @@ -0,0 +1,62 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef CACHEDTEXT_H | |
| 24 | + | #define CACHEDTEXT_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | #include "coordsbuffer.h" | |
| 28 | + | #include "drawqueue.h" | |
| 29 | + | ||
| 30 | + | class CachedText | |
| 31 | + | { | |
| 32 | + | public: | |
| 33 | + | CachedText(); | |
| 34 | + | ||
| 35 | + | void draw(const Rect& rect, const Color& color); | |
| 36 | + | ||
| 37 | + | void wrapText(int maxWidth); | |
| 38 | + | void setFont(const BitmapFontPtr& font) { m_font = font; update(); } | |
| 39 | + | void setText(const std::string& text) { m_textColors.clear(); m_text = text; update(); } | |
| 40 | + | void setColoredText(const std::vector<std::string>& texts); | |
| 41 | + | void setAlign(Fw::AlignmentFlag align) { m_align = align; update(); } | |
| 42 | + | ||
| 43 | + | Size getTextSize() { return m_textSize; } | |
| 44 | + | std::string getText() const { return m_text; } | |
| 45 | + | BitmapFontPtr getFont() const { return m_font; } | |
| 46 | + | Fw::AlignmentFlag getAlign() { return m_align; } | |
| 47 | + | ||
| 48 | + | bool hasText() { return !m_text.empty(); } | |
| 49 | + | ||
| 50 | + | private: | |
| 51 | + | void update(); | |
| 52 | + | ||
| 53 | + | std::string m_text; | |
| 54 | + | std::vector<std::pair<int, Color>> m_textColors; | |
| 55 | + | Size m_textSize; | |
| 56 | + | stdext::boolean<true> m_textMustRecache; | |
| 57 | + | Rect m_textCachedScreenCoords; | |
| 58 | + | BitmapFontPtr m_font; | |
| 59 | + | Fw::AlignmentFlag m_align; | |
| 60 | + | }; | |
| 61 | + | ||
| 62 | + | #endif |
| @@ -0,0 +1,46 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef COLORARRAY_H | |
| 24 | + | #define COLORARRAY_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | #include <framework/util/databuffer.h> | |
| 28 | + | ||
| 29 | + | class ColorArray | |
| 30 | + | { | |
| 31 | + | public: | |
| 32 | + | inline void addColor(float r, float g, float b, float a) { m_buffer << r << g << b << a; } | |
| 33 | + | inline void addColor(const Color& c) { addColor(c.rF(), c.gF(), c.bF(), c.aF()); } | |
| 34 | + | ||
| 35 | + | void clear() { m_buffer.reset(); } | |
| 36 | + | float *colors() const { return m_buffer.data(); } | |
| 37 | + | float *data() const { return m_buffer.data(); } | |
| 38 | + | int colorCount() const { return m_buffer.size() / 4; } | |
| 39 | + | int count() const { return m_buffer.size() / 4; } | |
| 40 | + | int size() const { return m_buffer.size(); } | |
| 41 | + | ||
| 42 | + | private: | |
| 43 | + | DataBuffer<float> m_buffer; | |
| 44 | + | }; | |
| 45 | + | ||
| 46 | + | #endif |
| @@ -0,0 +1,115 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "coordsbuffer.h" | |
| 24 | + | #include "graphics.h" | |
| 25 | + | ||
| 26 | + | CoordsBuffer::CoordsBuffer() | |
| 27 | + | { | |
| 28 | + | m_vertexArray = std::make_shared<VertexArray>(); | |
| 29 | + | m_textureCoordArray = std::make_shared<VertexArray>(); | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | CoordsBuffer::~CoordsBuffer() | |
| 33 | + | { | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | void CoordsBuffer::addBoudingRect(const Rect& dest, int innerLineWidth) | |
| 37 | + | { | |
| 38 | + | if (m_locked) | |
| 39 | + | unlock(); | |
| 40 | + | ||
| 41 | + | int left = dest.left(); | |
| 42 | + | int right = dest.right(); | |
| 43 | + | int top = dest.top(); | |
| 44 | + | int bottom = dest.bottom(); | |
| 45 | + | int width = dest.width(); | |
| 46 | + | int height = dest.height(); | |
| 47 | + | int w = innerLineWidth; | |
| 48 | + | ||
| 49 | + | addRect(Rect(left, top, width - w, w)); // top | |
| 50 | + | addRect(Rect(right - w + 1, top, w, height - w)); // right | |
| 51 | + | addRect(Rect(left + w, bottom - w + 1, width - w, w)); // bottom | |
| 52 | + | addRect(Rect(left, top + w, w, height - w)); // left | |
| 53 | + | } | |
| 54 | + | ||
| 55 | + | void CoordsBuffer::addRepeatedRects(const Rect& dest, const Rect& src) | |
| 56 | + | { | |
| 57 | + | if(dest.isEmpty() || src.isEmpty()) | |
| 58 | + | return; | |
| 59 | + | if (m_locked) | |
| 60 | + | unlock(); | |
| 61 | + | ||
| 62 | + | Rect virtualDest(0, 0, dest.size()); | |
| 63 | + | for(int y = 0; y <= virtualDest.height(); y += src.height()) { | |
| 64 | + | for(int x = 0; x <= virtualDest.width(); x += src.width()) { | |
| 65 | + | Rect partialDest(x, y, src.size()); | |
| 66 | + | Rect partialSrc(src); | |
| 67 | + | ||
| 68 | + | // partialCoords to screenCoords bottomRight | |
| 69 | + | if(partialDest.bottom() > virtualDest.bottom()) { | |
| 70 | + | partialSrc.setBottom(partialSrc.bottom() + (virtualDest.bottom() - partialDest.bottom())); | |
| 71 | + | partialDest.setBottom(virtualDest.bottom()); | |
| 72 | + | } | |
| 73 | + | if(partialDest.right() > virtualDest.right()) { | |
| 74 | + | partialSrc.setRight(partialSrc.right() + (virtualDest.right() - partialDest.right())); | |
| 75 | + | partialDest.setRight(virtualDest.right()); | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | partialDest.translate(dest.topLeft()); | |
| 79 | + | m_vertexArray->addRect(partialDest); | |
| 80 | + | m_textureCoordArray->addRect(partialSrc); | |
| 81 | + | } | |
| 82 | + | } | |
| 83 | + | } | |
| 84 | + | ||
| 85 | + | void CoordsBuffer::unlock(bool clear) | |
| 86 | + | { | |
| 87 | + | m_locked = false; | |
| 88 | + | if (clear) { | |
| 89 | + | m_vertexArray = std::make_shared<VertexArray>(); | |
| 90 | + | m_textureCoordArray = std::make_shared<VertexArray>(); | |
| 91 | + | } else { | |
| 92 | + | m_vertexArray = std::make_shared<VertexArray>(*m_vertexArray); | |
| 93 | + | m_textureCoordArray = std::make_shared<VertexArray>(*m_textureCoordArray); | |
| 94 | + | } | |
| 95 | + | } | |
| 96 | + | ||
| 97 | + | Rect CoordsBuffer::getTextureRect() | |
| 98 | + | { | |
| 99 | + | float* vertices = getTextureCoordArray(); | |
| 100 | + | int size = getTextureCoordCount() * 2; | |
| 101 | + | float x1 = 0, y1 = 0, x2 = 0, y2 = 0; | |
| 102 | + | for (int i = 0; i < size; i += 2) { | |
| 103 | + | float x = vertices[i]; | |
| 104 | + | float y = vertices[i + 1]; | |
| 105 | + | if (x < x1) | |
| 106 | + | x1 = x; | |
| 107 | + | else if (x > x2) | |
| 108 | + | x2 = x; | |
| 109 | + | if (y < y1) | |
| 110 | + | y1 = y; | |
| 111 | + | else if (y > y2) | |
| 112 | + | y2 = y; | |
| 113 | + | } | |
| 114 | + | return Rect(Point(x1, y1), Point(x2, y2)); | |
| 115 | + | } |
| @@ -0,0 +1,109 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef COORDSBUFFER_H | |
| 24 | + | #define COORDSBUFFER_H | |
| 25 | + | ||
| 26 | + | #include "vertexarray.h" | |
| 27 | + | ||
| 28 | + | class CoordsBuffer | |
| 29 | + | { | |
| 30 | + | public: | |
| 31 | + | CoordsBuffer(); | |
| 32 | + | ~CoordsBuffer(); | |
| 33 | + | CoordsBuffer(CoordsBuffer& c) = delete; | |
| 34 | + | CoordsBuffer& operator=(CoordsBuffer& c) = delete; | |
| 35 | + | CoordsBuffer(CoordsBuffer&& c) noexcept : m_vertexArray(c.m_vertexArray), m_textureCoordArray(c.m_textureCoordArray) | |
| 36 | + | { | |
| 37 | + | m_locked = c.m_locked = true; | |
| 38 | + | }; | |
| 39 | + | ||
| 40 | + | void clear() { | |
| 41 | + | if (m_locked) | |
| 42 | + | unlock(true); | |
| 43 | + | m_textureCoordArray->clear(); | |
| 44 | + | m_vertexArray->clear(); | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | void addTriangle(const Point& a, const Point& b, const Point& c) { | |
| 48 | + | if (m_locked) | |
| 49 | + | unlock(); | |
| 50 | + | m_vertexArray->addTriangle(a, b, c); | |
| 51 | + | } | |
| 52 | + | void addRect(const Rect& dest) { | |
| 53 | + | if (m_locked) | |
| 54 | + | unlock(); | |
| 55 | + | m_vertexArray->addRect(dest); | |
| 56 | + | } | |
| 57 | + | void addRect(const Rect& dest, const Rect& src) | |
| 58 | + | { | |
| 59 | + | if (m_locked) | |
| 60 | + | unlock(); | |
| 61 | + | m_vertexArray->addRect(dest); | |
| 62 | + | m_textureCoordArray->addRect(src); | |
| 63 | + | } | |
| 64 | + | void addRect(const RectF& dest, const RectF& src) | |
| 65 | + | { | |
| 66 | + | if (m_locked) | |
| 67 | + | unlock(); | |
| 68 | + | m_vertexArray->addRect(dest); | |
| 69 | + | m_textureCoordArray->addRect(src); | |
| 70 | + | } | |
| 71 | + | void addQuad(const Rect& dest, const Rect& src) { | |
| 72 | + | if (m_locked) | |
| 73 | + | unlock(); | |
| 74 | + | m_vertexArray->addQuad(dest); | |
| 75 | + | m_textureCoordArray->addQuad(src); | |
| 76 | + | } | |
| 77 | + | void addUpsideDownQuad(const Rect& dest, const Rect& src) { | |
| 78 | + | if (m_locked) | |
| 79 | + | unlock(); | |
| 80 | + | m_vertexArray->addUpsideDownQuad(dest); | |
| 81 | + | m_textureCoordArray->addQuad(src); | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | void addBoudingRect(const Rect& dest, int innerLineWidth); | |
| 85 | + | void addRepeatedRects(const Rect& dest, const Rect& src); | |
| 86 | + | ||
| 87 | + | float *getVertexArray() { return m_vertexArray->vertices(); } | |
| 88 | + | float *getTextureCoordArray() { return m_textureCoordArray->vertices(); } | |
| 89 | + | int getVertexCount() { return m_vertexArray->vertexCount(); } | |
| 90 | + | int getTextureCoordCount() { return m_textureCoordArray->vertexCount(); } | |
| 91 | + | HardwareBuffer* getVertexHardwareCache() { return m_vertexArray->getHardwareCache(); } | |
| 92 | + | HardwareBuffer* getTextureHardwareCache() { return m_textureCoordArray->getHardwareCache(); } | |
| 93 | + | ||
| 94 | + | void unlock(bool clear = false); | |
| 95 | + | void cache() | |
| 96 | + | { | |
| 97 | + | m_locked = true; | |
| 98 | + | m_vertexArray->cache(); | |
| 99 | + | m_textureCoordArray->cache(); | |
| 100 | + | } | |
| 101 | + | Rect getTextureRect(); | |
| 102 | + | ||
| 103 | + | private: | |
| 104 | + | bool m_locked = false; | |
| 105 | + | std::shared_ptr<VertexArray> m_vertexArray; | |
| 106 | + | std::shared_ptr<VertexArray> m_textureCoordArray; | |
| 107 | + | }; | |
| 108 | + | ||
| 109 | + | #endif |
| @@ -0,0 +1,52 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef FRAMEWORK_GRAPHICS_DECLARATIONS_H | |
| 24 | + | #define FRAMEWORK_GRAPHICS_DECLARATIONS_H | |
| 25 | + | ||
| 26 | + | #include <framework/global.h> | |
| 27 | + | #include "glutil.h" | |
| 28 | + | ||
| 29 | + | class Texture; | |
| 30 | + | class TextureManager; | |
| 31 | + | class Image; | |
| 32 | + | class AnimatedTexture; | |
| 33 | + | class BitmapFont; | |
| 34 | + | class CachedText; | |
| 35 | + | class FrameBuffer; | |
| 36 | + | class FrameBufferManager; | |
| 37 | + | class Shader; | |
| 38 | + | class ShaderProgram; | |
| 39 | + | class PainterShaderProgram; | |
| 40 | + | ||
| 41 | + | typedef stdext::shared_object_ptr<Image> ImagePtr; | |
| 42 | + | typedef stdext::shared_object_ptr<Texture> TexturePtr; | |
| 43 | + | typedef stdext::shared_object_ptr<AnimatedTexture> AnimatedTexturePtr; | |
| 44 | + | typedef stdext::shared_object_ptr<BitmapFont> BitmapFontPtr; | |
| 45 | + | typedef stdext::shared_object_ptr<CachedText> CachedTextPtr; | |
| 46 | + | typedef stdext::shared_object_ptr<FrameBuffer> FrameBufferPtr; | |
| 47 | + | typedef stdext::shared_object_ptr<Shader> ShaderPtr; | |
| 48 | + | typedef stdext::shared_object_ptr<ShaderProgram> ShaderProgramPtr; | |
| 49 | + | typedef stdext::shared_object_ptr<PainterShaderProgram> PainterShaderProgramPtr; | |
| 50 | + | typedef std::vector<ShaderPtr> ShaderList; | |
| 51 | + | ||
| 52 | + | #endif |
| @@ -0,0 +1,45 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef DEPTHARRAY_H | |
| 24 | + | #define DEPTHARRAY_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | #include <framework/util/databuffer.h> | |
| 28 | + | ||
| 29 | + | class DepthArray | |
| 30 | + | { | |
| 31 | + | public: | |
| 32 | + | inline void addDepth(float depth) { m_buffer << depth; } | |
| 33 | + | ||
| 34 | + | void clear() { m_buffer.reset(); } | |
| 35 | + | float *depths() const { return m_buffer.data(); } | |
| 36 | + | float *data() const { return m_buffer.data(); } | |
| 37 | + | int depthCount() const { return m_buffer.size(); } | |
| 38 | + | int count() const { return m_buffer.size(); } | |
| 39 | + | int size() const { return m_buffer.size(); } | |
| 40 | + | ||
| 41 | + | private: | |
| 42 | + | DataBuffer<float> m_buffer; | |
| 43 | + | }; | |
| 44 | + | ||
| 45 | + | #endif |
| @@ -0,0 +1,65 @@ | |||
| 1 | + | #include "drawcache.h" | |
| 2 | + | ||
| 3 | + | DrawCache g_drawCache; | |
| 4 | + | ||
| 5 | + | void DrawCache::draw() | |
| 6 | + | { | |
| 7 | + | release(); | |
| 8 | + | if (m_size == 0) return; | |
| 9 | + | g_painter->drawCache(m_destCoord, m_srcCoord, m_color, m_size); | |
| 10 | + | m_size = 0; | |
| 11 | + | } | |
| 12 | + | ||
| 13 | + | void DrawCache::bind() | |
| 14 | + | { | |
| 15 | + | if (m_bound) return; | |
| 16 | + | g_atlas.bind(); | |
| 17 | + | m_bound = true; | |
| 18 | + | } | |
| 19 | + | ||
| 20 | + | void DrawCache::release() | |
| 21 | + | { | |
| 22 | + | if (!m_bound) return; | |
| 23 | + | g_atlas.release(); | |
| 24 | + | m_bound = false; | |
| 25 | + | } | |
| 26 | + | ||
| 27 | + | void DrawCache::addRect(const Rect& dest, const Color& color) | |
| 28 | + | { | |
| 29 | + | static Rect emptyRect(Point(-10, -10), Point(-10, -10)); | |
| 30 | + | addRectRaw(m_destCoord.data() + (m_size * 2), dest); | |
| 31 | + | addRectRaw(m_srcCoord.data() + (m_size * 2), emptyRect); | |
| 32 | + | addColorRaw(color, 6); | |
| 33 | + | m_size += 6; | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | void DrawCache::addTexturedRect(const Rect& dest, const Rect& src, const Color& color) | |
| 37 | + | { | |
| 38 | + | addRectRaw(m_destCoord.data() + (m_size * 2), dest); | |
| 39 | + | addRectRaw(m_srcCoord.data() + (m_size * 2), src); | |
| 40 | + | addColorRaw(color, 6); | |
| 41 | + | m_size += 6; | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | void DrawCache::addCoords(CoordsBuffer& coords, const Color& color) | |
| 45 | + | { | |
| 46 | + | int size = coords.getVertexCount(); | |
| 47 | + | memcpy(m_destCoord.data() + m_size * 2, coords.getVertexArray(), size * 2 * sizeof(float)); | |
| 48 | + | for (int start = m_size * 2, end = (m_size + size) * 2; start < end; ++start) | |
| 49 | + | m_srcCoord[start] = -10; | |
| 50 | + | addColorRaw(color, size); | |
| 51 | + | m_size += size; | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | void DrawCache::addTexturedCoords(CoordsBuffer& coords, const Point& offset, const Color& color) | |
| 55 | + | { | |
| 56 | + | int size = coords.getVertexCount(); | |
| 57 | + | float* src = coords.getTextureCoordArray(); | |
| 58 | + | memcpy(m_destCoord.data() + m_size * 2, coords.getVertexArray(), size * 2 * sizeof(float)); | |
| 59 | + | for (int i = m_size * 2, j = 0, end = (m_size + size) * 2; i < end; ) { | |
| 60 | + | m_srcCoord[i++] = src[j++] + offset.x; | |
| 61 | + | m_srcCoord[i++] = src[j++] + offset.y; | |
| 62 | + | } | |
| 63 | + | addColorRaw(color, size); | |
| 64 | + | m_size += size; | |
| 65 | + | } |
| @@ -0,0 +1,55 @@ | |||
| 1 | + | #ifndef DRAW_CACHE | |
| 2 | + | #define DRAW_CACHE | |
| 3 | + | ||
| 4 | + | #include "atlas.h" | |
| 5 | + | #include "coordsbuffer.h" | |
| 6 | + | #include "graphics.h" | |
| 7 | + | #include "painter.h" | |
| 8 | + | ||
| 9 | + | class DrawCache { | |
| 10 | + | public: | |
| 11 | + | static const int MAX_SIZE = 65536; | |
| 12 | + | static const int HALF_MAX_SIZE = MAX_SIZE / 2; | |
| 13 | + | ||
| 14 | + | void draw(); | |
| 15 | + | void bind(); | |
| 16 | + | void release(); | |
| 17 | + | bool hasSpace(int size) { | |
| 18 | + | return size + m_size < MAX_SIZE; | |
| 19 | + | } | |
| 20 | + | inline int getSize() { return m_size; } | |
| 21 | + | void addRect(const Rect& dest, const Color& color); | |
| 22 | + | void addTexturedRect(const Rect& dest, const Rect& src, const Color& color); | |
| 23 | + | void addCoords(CoordsBuffer& coords, const Color& color); | |
| 24 | + | void addTexturedCoords(CoordsBuffer& coords, const Point& offset, const Color& color); | |
| 25 | + | ||
| 26 | + | private: | |
| 27 | + | inline void addRectRaw(float* dest, const Rect& rect) | |
| 28 | + | { | |
| 29 | + | dest[0] = dest[4] = dest[6] = rect.left(); | |
| 30 | + | dest[1] = dest[3] = dest[9] = rect.top(); | |
| 31 | + | dest[2] = dest[8] = dest[10] = rect.right() + 1; | |
| 32 | + | dest[5] = dest[7] = dest[11] = rect.bottom() + 1; | |
| 33 | + | } | |
| 34 | + | inline void addColorRaw(const Color& color, int count) | |
| 35 | + | { | |
| 36 | + | static float c[4]; | |
| 37 | + | c[0] = color.rF(); | |
| 38 | + | c[1] = color.gF(); | |
| 39 | + | c[2] = color.bF(); | |
| 40 | + | c[3] = color.aF(); | |
| 41 | + | for (int start = m_size * 4, end = (m_size + count) * 4; start < end; start += 4) { | |
| 42 | + | memcpy(m_color.data() + start, c, 4 * sizeof(float)); | |
| 43 | + | } | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | std::vector<float> m_destCoord = std::vector<float>(MAX_SIZE * 2); | |
| 47 | + | std::vector<float> m_srcCoord = std::vector<float>(MAX_SIZE * 2); | |
| 48 | + | std::vector<float> m_color = std::vector<float>(MAX_SIZE * 4); | |
| 49 | + | bool m_bound = false; | |
| 50 | + | int m_size = 0; | |
| 51 | + | }; | |
| 52 | + | ||
| 53 | + | extern DrawCache g_drawCache; | |
| 54 | + | ||
| 55 | + | #endif |
| @@ -0,0 +1,374 @@ | |||
| 1 | + | #include <client/outfit.h> | |
| 2 | + | #include <client/spritemanager.h> | |
| 3 | + | #include <framework/graphics/atlas.h> | |
| 4 | + | #include <framework/graphics/drawcache.h> | |
| 5 | + | #include <framework/graphics/drawqueue.h> | |
| 6 | + | #include <framework/graphics/framebuffermanager.h> | |
| 7 | + | #include <framework/graphics/graphics.h> | |
| 8 | + | #include <framework/graphics/image.h> | |
| 9 | + | #include <framework/graphics/painter.h> | |
| 10 | + | #include <framework/graphics/shadermanager.h> | |
| 11 | + | #include <framework/graphics/textrender.h> | |
| 12 | + | #include <stack> | |
| 13 | + | ||
| 14 | + | std::shared_ptr<DrawQueue> g_drawQueue; | |
| 15 | + | ||
| 16 | + | void DrawQueueItemTextureCoords::draw() { | |
| 17 | + | g_painter->setColor(m_color); | |
| 18 | + | g_painter->drawTextureCoords(m_coordsBuffer, m_texture); | |
| 19 | + | } | |
| 20 | + | ||
| 21 | + | bool DrawQueueItemTextureCoords::cache() { | |
| 22 | + | if (!m_texture->canCache()) | |
| 23 | + | return false; | |
| 24 | + | m_texture->update(); | |
| 25 | + | ||
| 26 | + | uint64_t hash = 100 + m_texture->getUniqueId(); | |
| 27 | + | bool drawNow = false; | |
| 28 | + | Point atlasPos = g_atlas.cache(hash, m_texture->getSize(), drawNow); | |
| 29 | + | if (atlasPos.x < 0) { | |
| 30 | + | return false; | |
| 31 | + | } // can't be cached | |
| 32 | + | if (drawNow) { | |
| 33 | + | g_drawCache.bind(); | |
| 34 | + | draw(atlasPos); | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | int size = m_coordsBuffer.getVertexCount(); | |
| 38 | + | if (!g_drawCache.hasSpace(size)) | |
| 39 | + | return false; | |
| 40 | + | ||
| 41 | + | g_drawCache.addTexturedCoords(m_coordsBuffer, atlasPos, m_color); | |
| 42 | + | return true; | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | void DrawQueueItemTextureCoords::draw(const Point &pos) { | |
| 46 | + | g_painter->resetColor(); | |
| 47 | + | g_painter->drawTexturedRect(Rect(pos, m_texture->getSize()), m_texture); | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | void DrawQueueItemColoredTextureCoords::draw() { | |
| 51 | + | g_painter->drawTextureCoords(m_coordsBuffer, m_texture, &m_colors); | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | void DrawQueueItemImageWithShader::draw() { | |
| 55 | + | if (!m_texture) | |
| 56 | + | return; | |
| 57 | + | PainterShaderProgramPtr shader = g_shaders.getShader(m_shader); | |
| 58 | + | if (!shader) | |
| 59 | + | return; | |
| 60 | + | ||
| 61 | + | g_painter->setShaderProgram(shader); | |
| 62 | + | shader->bindMultiTextures(); | |
| 63 | + | g_painter->setColor(m_color); | |
| 64 | + | g_painter->drawTextureCoords(m_coordsBuffer, m_texture); | |
| 65 | + | g_painter->resetShaderProgram(); | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | void DrawQueueItemImageWithShader::draw(const Point &pos) { | |
| 69 | + | if (!m_texture) | |
| 70 | + | return; | |
| 71 | + | PainterShaderProgramPtr shader = g_shaders.getShader(m_shader); | |
| 72 | + | if (!shader) | |
| 73 | + | return; | |
| 74 | + | ||
| 75 | + | g_painter->setShaderProgram(shader); | |
| 76 | + | shader->bindMultiTextures(); | |
| 77 | + | g_painter->resetColor(); | |
| 78 | + | g_painter->drawTexturedRect(Rect(pos, m_texture->getSize()), m_texture); | |
| 79 | + | g_painter->resetShaderProgram(); | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | void DrawQueueItemTexturedRect::draw() { | |
| 83 | + | g_painter->setColor(m_color); | |
| 84 | + | g_painter->drawTexturedRect(m_dest, m_texture, m_src); | |
| 85 | + | } | |
| 86 | + | ||
| 87 | + | bool DrawQueueItemTexturedRect::cache() { | |
| 88 | + | if (m_dest.size() > m_src.size()) // upscaling may create artifacts | |
| 89 | + | return false; | |
| 90 | + | if (!m_texture->canCache()) | |
| 91 | + | return false; | |
| 92 | + | ||
| 93 | + | m_texture->update(); | |
| 94 | + | uint64_t hash = 100 + m_texture->getUniqueId(); | |
| 95 | + | bool drawNow = false; | |
| 96 | + | Point atlasPos = g_atlas.cache(hash, m_texture->getSize(), drawNow); | |
| 97 | + | if (atlasPos.x < 0) { | |
| 98 | + | return false; | |
| 99 | + | } // can't be cached | |
| 100 | + | if (drawNow) { | |
| 101 | + | g_drawCache.bind(); | |
| 102 | + | draw(atlasPos); | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | if (!g_drawCache.hasSpace(6)) | |
| 106 | + | return false; | |
| 107 | + | ||
| 108 | + | g_drawCache.addTexturedRect(m_dest, m_src + atlasPos, m_color); | |
| 109 | + | return true; | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | void DrawQueueItemTexturedRect::draw(const Point &pos) { | |
| 113 | + | g_painter->resetColor(); | |
| 114 | + | g_painter->drawTexturedRect(Rect(pos, m_texture->getSize()), m_texture); | |
| 115 | + | } | |
| 116 | + | ||
| 117 | + | bool DrawQueueItemFilledRect::cache() { | |
| 118 | + | if (!g_drawCache.hasSpace(6)) | |
| 119 | + | return false; | |
| 120 | + | g_drawCache.addRect(m_dest, m_color); | |
| 121 | + | return true; | |
| 122 | + | } | |
| 123 | + | ||
| 124 | + | void DrawQueueItemClearRect::draw() { g_painter->clearRect(m_color, m_dest); } | |
| 125 | + | ||
| 126 | + | bool DrawQueueItemFillCoords::cache() { | |
| 127 | + | int size = m_coordsBuffer.getVertexCount(); | |
| 128 | + | if (!g_drawCache.hasSpace(size)) | |
| 129 | + | return false; | |
| 130 | + | ||
| 131 | + | g_drawCache.addCoords(m_coordsBuffer, m_color); | |
| 132 | + | return true; | |
| 133 | + | } | |
| 134 | + | ||
| 135 | + | void DrawQueueItemText::draw() { | |
| 136 | + | g_text.drawText(m_point, m_hash, m_color, m_shadow); | |
| 137 | + | } | |
| 138 | + | ||
| 139 | + | void DrawQueueItemTextColored::draw() { | |
| 140 | + | g_text.drawColoredText(m_point, m_hash, m_colors, m_shadow); | |
| 141 | + | } | |
| 142 | + | ||
| 143 | + | void ::DrawQueueItemLine::draw() { | |
| 144 | + | g_painter->setColor(m_color); | |
| 145 | + | static std::vector<float> vertices(1024, 0); | |
| 146 | + | if (vertices.size() < m_points.size()) | |
| 147 | + | vertices.resize(m_points.size()); | |
| 148 | + | int i = 0; | |
| 149 | + | for (Point &point : m_points) { | |
| 150 | + | vertices[i++] = point.x; | |
| 151 | + | vertices[i++] = point.y; | |
| 152 | + | } | |
| 153 | + | g_painter->drawLine(vertices, i / 2, m_width); | |
| 154 | + | } | |
| 155 | + | ||
| 156 | + | void DrawQueueConditionClip::start(DrawQueue *) { | |
| 157 | + | m_prevClip = g_painter->getClipRect(); | |
| 158 | + | g_painter->setClipRect(m_rect); | |
| 159 | + | } | |
| 160 | + | ||
| 161 | + | void DrawQueueConditionClip::end(DrawQueue *) { | |
| 162 | + | g_painter->setClipRect(m_prevClip); | |
| 163 | + | } | |
| 164 | + | ||
| 165 | + | void DrawQueueConditionRotation::start(DrawQueue *) { | |
| 166 | + | g_painter->pushTransformMatrix(); | |
| 167 | + | g_painter->rotate(m_center, m_angle); | |
| 168 | + | } | |
| 169 | + | ||
| 170 | + | void DrawQueueConditionRotation::end(DrawQueue *) { | |
| 171 | + | g_painter->popTransformMatrix(); | |
| 172 | + | } | |
| 173 | + | ||
| 174 | + | void DrawQueueConditionMark::start(DrawQueue *) { | |
| 175 | + | // nothing | |
| 176 | + | } | |
| 177 | + | ||
| 178 | + | void DrawQueueConditionMark::end(DrawQueue *queue) { | |
| 179 | + | g_painter->setDrawColorOnTextureShaderProgram(); | |
| 180 | + | g_painter->setColor(m_color); | |
| 181 | + | for (size_t i = m_start; i < m_end; ++i) { | |
| 182 | + | DrawQueueItemTexturedRect *texture = | |
| 183 | + | dynamic_cast<DrawQueueItemTexturedRect *>(queue->m_queue[i]); | |
| 184 | + | if (texture) | |
| 185 | + | g_painter->drawTexturedRect(texture->m_dest, texture->m_texture, | |
| 186 | + | texture->m_src); | |
| 187 | + | } | |
| 188 | + | g_painter->resetShaderProgram(); | |
| 189 | + | } | |
| 190 | + | ||
| 191 | + | void DrawQueue::setFrameBuffer(const Rect &dest, const Size &size, | |
| 192 | + | const Rect &src) { | |
| 193 | + | m_useFrameBuffer = true; | |
| 194 | + | m_frameBufferSize = size; | |
| 195 | + | m_frameBufferDest = dest; | |
| 196 | + | m_frameBufferSrc = src; | |
| 197 | + | size_t max_size = | |
| 198 | + | std::max(m_frameBufferSize.width(), m_frameBufferSize.height()); | |
| 199 | + | while (max_size > 2048u) { | |
| 200 | + | max_size /= 2; | |
| 201 | + | m_scaling /= 2.f; | |
| 202 | + | } | |
| 203 | + | if (m_scaling < 0.99f) { | |
| 204 | + | m_frameBufferSize = Size(2048, 2048); | |
| 205 | + | m_frameBufferSrc = m_frameBufferSrc * m_scaling; | |
| 206 | + | } | |
| 207 | + | } | |
| 208 | + | ||
| 209 | + | void DrawQueue::addText(BitmapFontPtr font, const std::string &text, | |
| 210 | + | const Rect &screenCoords, Fw::AlignmentFlag align, | |
| 211 | + | const Color &color, bool shadow) { | |
| 212 | + | if (!font || text.empty()) | |
| 213 | + | return; | |
| 214 | + | uint64_t hash = g_text.addText(font, text, screenCoords.size(), align); | |
| 215 | + | m_queue.push_back(new DrawQueueItemText( | |
| 216 | + | screenCoords.topLeft(), font->getTexture(), hash, color, shadow)); | |
| 217 | + | } | |
| 218 | + | ||
| 219 | + | void DrawQueue::addColoredText(BitmapFontPtr font, const std::string &text, | |
| 220 | + | const Rect &screenCoords, | |
| 221 | + | Fw::AlignmentFlag align, | |
| 222 | + | const std::vector<std::pair<int, Color>> &colors, | |
| 223 | + | bool shadow) { | |
| 224 | + | if (!font || text.empty()) | |
| 225 | + | return; | |
| 226 | + | uint64_t hash = g_text.addText(font, text, screenCoords.size(), align); | |
| 227 | + | m_queue.push_back(new DrawQueueItemTextColored( | |
| 228 | + | screenCoords.topLeft(), font->getTexture(), hash, colors, shadow)); | |
| 229 | + | } | |
| 230 | + | ||
| 231 | + | void DrawQueue::correctOutfit(const Rect &dest, int fromPos, bool oldScaling) { | |
| 232 | + | std::vector<Rect *> rects; | |
| 233 | + | if (!oldScaling) { | |
| 234 | + | bool center = false; | |
| 235 | + | int centerX = 0; | |
| 236 | + | int centerY = 0; | |
| 237 | + | for (size_t i = fromPos; i < m_queue.size(); ++i) { | |
| 238 | + | if (DrawQueueItemOutfit *texture = | |
| 239 | + | dynamic_cast<DrawQueueItemOutfit *>(m_queue[i])) { | |
| 240 | + | rects.push_back(&texture->m_dest); | |
| 241 | + | if (!center) { | |
| 242 | + | center = texture->m_doCenter; | |
| 243 | + | } | |
| 244 | + | ||
| 245 | + | if (texture->m_doCenter) { | |
| 246 | + | centerX = std::max<int>(centerX, texture->m_dest.center().x); | |
| 247 | + | centerY = std::max<int>(centerY, texture->m_dest.center().y); | |
| 248 | + | } | |
| 249 | + | } else if (DrawQueueItemOutfitWithShader *texture = | |
| 250 | + | dynamic_cast<DrawQueueItemOutfitWithShader *>( | |
| 251 | + | m_queue[i])) { | |
| 252 | + | rects.push_back(&texture->m_dest); | |
| 253 | + | if (!center) { | |
| 254 | + | center = texture->m_doCenter; | |
| 255 | + | } | |
| 256 | + | ||
| 257 | + | if (texture->m_doCenter) { | |
| 258 | + | centerX = std::max<int>(centerX, texture->m_dest.center().x); | |
| 259 | + | } | |
| 260 | + | } else if (DrawQueueItemTexturedRect *texture = | |
| 261 | + | dynamic_cast<DrawQueueItemTexturedRect *>(m_queue[i])) { | |
| 262 | + | rects.push_back(&texture->m_dest); | |
| 263 | + | } | |
| 264 | + | } | |
| 265 | + | ||
| 266 | + | int x1 = -g_sprites.spriteSize(), y1 = -g_sprites.spriteSize(), | |
| 267 | + | x2 = g_sprites.spriteSize(), y2 = g_sprites.spriteSize(); | |
| 268 | + | float scale = std::min<float>((float)dest.height() / (y2 - y1), | |
| 269 | + | (float)dest.width() / (x2 - x1)); | |
| 270 | + | for (auto &rect : rects) { | |
| 271 | + | int x = rect->left() - x1 - centerX, | |
| 272 | + | y = rect->top() - y1 - centerY; // offset | |
| 273 | + | *rect = Rect(dest.left() + x * scale, dest.top() + y * scale, | |
| 274 | + | rect->size() * scale); | |
| 275 | + | } | |
| 276 | + | } else { | |
| 277 | + | for (size_t i = fromPos; i < m_queue.size(); ++i) { | |
| 278 | + | if (DrawQueueItemTexturedRect *texture = | |
| 279 | + | dynamic_cast<DrawQueueItemTexturedRect *>(m_queue[i])) | |
| 280 | + | rects.push_back(&texture->m_dest); | |
| 281 | + | } | |
| 282 | + | ||
| 283 | + | int x1 = 0, y1 = 1, x2 = 0, y2 = 0; | |
| 284 | + | for (auto &rect : rects) { | |
| 285 | + | x1 = std::min<int>(x1, rect->left()); | |
| 286 | + | y1 = std::min<int>(y1, rect->top()); | |
| 287 | + | x2 = std::max<int>(x2, rect->right()); | |
| 288 | + | y2 = std::max<int>(y2, rect->bottom()); | |
| 289 | + | } | |
| 290 | + | if (x1 == x2 || y1 == y2) | |
| 291 | + | return; | |
| 292 | + | ||
| 293 | + | float scale = std::min<float>((float)dest.height() / (y2 - y1), | |
| 294 | + | (float)dest.width() / (x2 - x1)); | |
| 295 | + | for (auto &rect : rects) { | |
| 296 | + | int x = rect->left() - x1, y = rect->top() - y1; // offset | |
| 297 | + | *rect = Rect(dest.left() + x * scale, dest.top() + y * scale, | |
| 298 | + | rect->size() * scale); | |
| 299 | + | } | |
| 300 | + | } | |
| 301 | + | } | |
| 302 | + | ||
| 303 | + | void DrawQueue::draw(DrawType drawType) { | |
| 304 | + | size_t start = 0; | |
| 305 | + | size_t end = m_queue.size(); | |
| 306 | + | if (drawType == DRAW_BEFORE_MAP) { | |
| 307 | + | end = mapPosition; | |
| 308 | + | } else if (drawType == DRAW_AFTER_MAP) { | |
| 309 | + | start = mapPosition; | |
| 310 | + | } | |
| 311 | + | ||
| 312 | + | std::sort( | |
| 313 | + | m_conditions.begin(), m_conditions.end(), | |
| 314 | + | [](const DrawQueueCondition *a, const DrawQueueCondition *b) -> bool { | |
| 315 | + | return a->m_start == b->m_start ? a->m_end < b->m_end | |
| 316 | + | : a->m_start < b->m_start; | |
| 317 | + | }); | |
| 318 | + | ||
| 319 | + | Size originalResolution = g_painter->getResolution(); | |
| 320 | + | if (m_scaling > 0.f && m_scaling < 0.99f) { | |
| 321 | + | Size resolution = originalResolution * (1.f / m_scaling); | |
| 322 | + | Matrix3 projectionMatrix = {2.0f / resolution.width(), | |
| 323 | + | 0.0f, | |
| 324 | + | 0.0f, | |
| 325 | + | 0.0f, | |
| 326 | + | -2.0f / resolution.height(), | |
| 327 | + | 0.0f, | |
| 328 | + | -1.0f, | |
| 329 | + | 1.0f, | |
| 330 | + | 1.0f}; | |
| 331 | + | g_painter->setProjectionMatrix(projectionMatrix); | |
| 332 | + | } | |
| 333 | + | ||
| 334 | + | auto condition = m_conditions.begin(); | |
| 335 | + | std::stack<DrawQueueCondition *> activeConditions; | |
| 336 | + | // skip conditions | |
| 337 | + | while (condition != m_conditions.end() && (*condition)->m_end <= start) | |
| 338 | + | ++condition; | |
| 339 | + | // execute conditions & draw | |
| 340 | + | for (size_t i = start; i < end; ++i) { | |
| 341 | + | while (!activeConditions.empty() && activeConditions.top()->m_end <= i) { | |
| 342 | + | g_drawCache.draw(); | |
| 343 | + | activeConditions.top()->end(this); | |
| 344 | + | activeConditions.pop(); | |
| 345 | + | } | |
| 346 | + | while (condition != m_conditions.end() && (*condition)->m_start <= i) { | |
| 347 | + | g_drawCache.draw(); | |
| 348 | + | (*condition)->start(this); | |
| 349 | + | activeConditions.push(*condition); | |
| 350 | + | ++condition; | |
| 351 | + | } | |
| 352 | + | ||
| 353 | + | if (!m_queue[i]->cache()) { | |
| 354 | + | g_drawCache.draw(); | |
| 355 | + | if (!m_queue[i]->cache()) { // try to cache again, now g_drawCache should | |
| 356 | + | // be empty, maybe there's new space | |
| 357 | + | m_queue[i]->draw(); | |
| 358 | + | } | |
| 359 | + | } | |
| 360 | + | if (g_drawCache.getSize() >= g_drawCache.HALF_MAX_SIZE) { | |
| 361 | + | g_drawCache.draw(); | |
| 362 | + | } | |
| 363 | + | } | |
| 364 | + | g_drawCache.draw(); | |
| 365 | + | // end all actibe conditions | |
| 366 | + | while (!activeConditions.empty()) { | |
| 367 | + | activeConditions.top()->end(this); | |
| 368 | + | activeConditions.pop(); | |
| 369 | + | } | |
| 370 | + | ||
| 371 | + | g_painter->setResolution(originalResolution); | |
| 372 | + | g_painter->resetState(); | |
| 373 | + | g_graphics.checkForError(__FUNCTION__, __FILE__, __LINE__); | |
| 374 | + | } |
| @@ -0,0 +1,334 @@ | |||
| 1 | + | #ifndef DRAWQUEUE_H | |
| 2 | + | #define DRAWQUEUE_H | |
| 3 | + | ||
| 4 | + | #include <framework/graphics/colorarray.h> | |
| 5 | + | #include <framework/graphics/coordsbuffer.h> | |
| 6 | + | #include <framework/graphics/declarations.h> | |
| 7 | + | #include <framework/graphics/deptharray.h> | |
| 8 | + | #include <framework/graphics/paintershaderprogram.h> | |
| 9 | + | #include <framework/graphics/texture.h> | |
| 10 | + | #include <framework/ui/uiwidget.h> | |
| 11 | + | #include <vector> | |
| 12 | + | ||
| 13 | + | class DrawQueue; | |
| 14 | + | struct DrawQueueItem; | |
| 15 | + | ||
| 16 | + | enum DrawType : uint8_t { | |
| 17 | + | DRAW_ALL = 0, | |
| 18 | + | DRAW_BEFORE_MAP = 1, | |
| 19 | + | DRAW_AFTER_MAP = 2 | |
| 20 | + | }; | |
| 21 | + | ||
| 22 | + | struct DrawQueueItem { | |
| 23 | + | DrawQueueItem(const TexturePtr &texture, const Color &color = Color::white) | |
| 24 | + | : m_texture(texture), m_color(color) {} | |
| 25 | + | virtual ~DrawQueueItem() = default; | |
| 26 | + | virtual void draw() {} | |
| 27 | + | virtual void draw(const Point &pos) {} | |
| 28 | + | virtual bool cache() { return false; } | |
| 29 | + | ||
| 30 | + | TexturePtr m_texture; | |
| 31 | + | Color m_color; | |
| 32 | + | }; | |
| 33 | + | ||
| 34 | + | struct DrawQueueItemTexturedRect : public DrawQueueItem { | |
| 35 | + | DrawQueueItemTexturedRect() : DrawQueueItem(nullptr) {} | |
| 36 | + | DrawQueueItemTexturedRect(const Rect &dest, const TexturePtr &texture, | |
| 37 | + | const Rect &src, const Color &color) | |
| 38 | + | : DrawQueueItem(texture, color), m_dest(dest), m_src(src) {}; | |
| 39 | + | virtual ~DrawQueueItemTexturedRect() = default; | |
| 40 | + | ||
| 41 | + | virtual void draw(); | |
| 42 | + | virtual void draw(const Point &pos); | |
| 43 | + | virtual bool cache(); | |
| 44 | + | ||
| 45 | + | Rect m_dest; | |
| 46 | + | Rect m_src; | |
| 47 | + | }; | |
| 48 | + | ||
| 49 | + | struct DrawQueueItemTextureCoords : public DrawQueueItem { | |
| 50 | + | DrawQueueItemTextureCoords(CoordsBuffer &coordsBuffer, | |
| 51 | + | const TexturePtr &texture, const Color &color) | |
| 52 | + | : DrawQueueItem(texture, color), | |
| 53 | + | m_coordsBuffer(std::move(coordsBuffer)) {}; | |
| 54 | + | ||
| 55 | + | void draw(); | |
| 56 | + | void draw(const Point &pos); | |
| 57 | + | bool cache(); | |
| 58 | + | ||
| 59 | + | CoordsBuffer m_coordsBuffer; | |
| 60 | + | }; | |
| 61 | + | ||
| 62 | + | struct DrawQueueItemColoredTextureCoords : public DrawQueueItem { | |
| 63 | + | DrawQueueItemColoredTextureCoords( | |
| 64 | + | CoordsBuffer &coordsBuffer, const TexturePtr &texture, | |
| 65 | + | const std::vector<std::pair<int, Color>> &colors) | |
| 66 | + | : DrawQueueItem(texture), m_coordsBuffer(std::move(coordsBuffer)), | |
| 67 | + | m_colors(colors) {}; | |
| 68 | + | ||
| 69 | + | void draw(); | |
| 70 | + | ||
| 71 | + | CoordsBuffer m_coordsBuffer; | |
| 72 | + | std::vector<std::pair<int, Color>> m_colors; | |
| 73 | + | }; | |
| 74 | + | ||
| 75 | + | struct DrawQueueItemImageWithShader : public DrawQueueItemTextureCoords { | |
| 76 | + | DrawQueueItemImageWithShader(CoordsBuffer &coords, const TexturePtr &texture, | |
| 77 | + | const Color &color, const std::string &shader) | |
| 78 | + | : DrawQueueItemTextureCoords(coords, texture, color), m_shader(shader) {}; | |
| 79 | + | ||
| 80 | + | void draw() override; | |
| 81 | + | void draw(const Point &pos) override; | |
| 82 | + | bool cache() override { return false; } | |
| 83 | + | ||
| 84 | + | std::string m_shader; | |
| 85 | + | }; | |
| 86 | + | ||
| 87 | + | struct DrawQueueItemFilledRect : public DrawQueueItem { | |
| 88 | + | DrawQueueItemFilledRect(const Rect &rect, const Color &color) | |
| 89 | + | : DrawQueueItem(nullptr, color), m_dest(rect) {}; | |
| 90 | + | bool cache(); | |
| 91 | + | ||
| 92 | + | Rect m_dest; | |
| 93 | + | }; | |
| 94 | + | ||
| 95 | + | struct DrawQueueItemClearRect : public DrawQueueItem { | |
| 96 | + | DrawQueueItemClearRect(const Rect &rect, const Color &color) | |
| 97 | + | : DrawQueueItem(nullptr, color), m_dest(rect) {}; | |
| 98 | + | void draw(); | |
| 99 | + | ||
| 100 | + | Rect m_dest; | |
| 101 | + | }; | |
| 102 | + | ||
| 103 | + | struct DrawQueueItemFillCoords : public DrawQueueItem { | |
| 104 | + | DrawQueueItemFillCoords(CoordsBuffer &coordsBuffer, const Color &color) | |
| 105 | + | : DrawQueueItem(nullptr, color), | |
| 106 | + | m_coordsBuffer(std::move(coordsBuffer)) {}; | |
| 107 | + | bool cache(); | |
| 108 | + | ||
| 109 | + | CoordsBuffer m_coordsBuffer; | |
| 110 | + | }; | |
| 111 | + | ||
| 112 | + | struct DrawQueueItemText : public DrawQueueItem { | |
| 113 | + | DrawQueueItemText(const Point &point, const TexturePtr &texture, | |
| 114 | + | uint64_t hash, const Color &color, bool shadow = false) | |
| 115 | + | : DrawQueueItem(texture, color), m_point(point), m_hash(hash), | |
| 116 | + | m_shadow(shadow) {}; | |
| 117 | + | void draw(); | |
| 118 | + | ||
| 119 | + | Point m_point; | |
| 120 | + | uint64_t m_hash; | |
| 121 | + | bool m_shadow = false; | |
| 122 | + | }; | |
| 123 | + | ||
| 124 | + | struct DrawQueueItemTextColored : public DrawQueueItem { | |
| 125 | + | DrawQueueItemTextColored(const Point &point, const TexturePtr &texture, | |
| 126 | + | uint64_t hash, | |
| 127 | + | const std::vector<std::pair<int, Color>> &colors, | |
| 128 | + | bool shadow = false) | |
| 129 | + | : DrawQueueItem(texture), m_point(point), m_hash(hash), m_colors(colors), | |
| 130 | + | m_shadow(shadow) {}; | |
| 131 | + | void draw(); | |
| 132 | + | ||
| 133 | + | Point m_point; | |
| 134 | + | uint64_t m_hash; | |
| 135 | + | std::vector<std::pair<int, Color>> m_colors; | |
| 136 | + | bool m_shadow = false; | |
| 137 | + | }; | |
| 138 | + | ||
| 139 | + | struct DrawQueueItemLine : public DrawQueueItem { | |
| 140 | + | DrawQueueItemLine(const std::vector<Point> &points, int width, | |
| 141 | + | const Color &color) | |
| 142 | + | : DrawQueueItem(nullptr, color), m_points(points), m_width(width) {}; | |
| 143 | + | void draw(); | |
| 144 | + | ||
| 145 | + | std::vector<Point> m_points; | |
| 146 | + | int m_width; | |
| 147 | + | }; | |
| 148 | + | ||
| 149 | + | struct DrawQueueCondition { | |
| 150 | + | DrawQueueCondition(size_t start, size_t end) : m_start(start), m_end(end) {} | |
| 151 | + | virtual ~DrawQueueCondition() = default; | |
| 152 | + | ||
| 153 | + | virtual void start(DrawQueue *) = 0; | |
| 154 | + | virtual void end(DrawQueue *) = 0; | |
| 155 | + | ||
| 156 | + | size_t m_start; | |
| 157 | + | size_t m_end; | |
| 158 | + | }; | |
| 159 | + | ||
| 160 | + | struct DrawQueueConditionClip : public DrawQueueCondition { | |
| 161 | + | DrawQueueConditionClip(size_t start, size_t end, const Rect &rect) | |
| 162 | + | : DrawQueueCondition(start, end), m_rect(rect) {} | |
| 163 | + | ||
| 164 | + | void start(DrawQueue *queue) override; | |
| 165 | + | void end(DrawQueue *queue) override; | |
| 166 | + | ||
| 167 | + | Rect m_rect; | |
| 168 | + | Rect m_prevClip; | |
| 169 | + | }; | |
| 170 | + | ||
| 171 | + | struct DrawQueueConditionRotation : public DrawQueueCondition { | |
| 172 | + | DrawQueueConditionRotation(size_t start, size_t end, const Point ¢er, | |
| 173 | + | float angle) | |
| 174 | + | : DrawQueueCondition(start, end), m_center(center), m_angle(angle) {} | |
| 175 | + | ||
| 176 | + | void start(DrawQueue *queue) override; | |
| 177 | + | void end(DrawQueue *queue) override; | |
| 178 | + | ||
| 179 | + | Point m_center; | |
| 180 | + | float m_angle; | |
| 181 | + | }; | |
| 182 | + | ||
| 183 | + | struct DrawQueueConditionMark : public DrawQueueCondition { | |
| 184 | + | DrawQueueConditionMark(size_t start, size_t end, const Color &color) | |
| 185 | + | : DrawQueueCondition(start, end), m_color(color) {} | |
| 186 | + | ||
| 187 | + | void start(DrawQueue *queue) override; | |
| 188 | + | void end(DrawQueue *queue) override; | |
| 189 | + | ||
| 190 | + | Color m_color; | |
| 191 | + | }; | |
| 192 | + | ||
| 193 | + | class DrawQueue { | |
| 194 | + | public: | |
| 195 | + | DrawQueue() = default; | |
| 196 | + | DrawQueue(const DrawQueue &) = delete; | |
| 197 | + | DrawQueue &operator=(const DrawQueue &) = delete; | |
| 198 | + | ~DrawQueue() { | |
| 199 | + | for (auto &item : m_queue) | |
| 200 | + | delete item; | |
| 201 | + | m_queue.clear(); | |
| 202 | + | for (auto &condition : m_conditions) | |
| 203 | + | delete condition; | |
| 204 | + | m_conditions.clear(); | |
| 205 | + | } | |
| 206 | + | ||
| 207 | + | void draw(DrawType drawType = DRAW_ALL); | |
| 208 | + | ||
| 209 | + | void add(DrawQueueItem *item) { | |
| 210 | + | if (!item) | |
| 211 | + | return; | |
| 212 | + | m_queue.push_back(item); | |
| 213 | + | } | |
| 214 | + | DrawQueueItemTexturedRect * | |
| 215 | + | addTexturedRect(const Rect &dest, const TexturePtr &texture, const Rect &src, | |
| 216 | + | const Color &color = Color::white) { | |
| 217 | + | DrawQueueItemTexturedRect *item( | |
| 218 | + | new DrawQueueItemTexturedRect(dest, texture, src, color)); | |
| 219 | + | m_queue.push_back(item); | |
| 220 | + | return item; | |
| 221 | + | } | |
| 222 | + | void addTextureCoords(CoordsBuffer &coords, const TexturePtr &texture, | |
| 223 | + | const Color &color = Color::white) { | |
| 224 | + | m_queue.push_back(new DrawQueueItemTextureCoords(coords, texture, color)); | |
| 225 | + | } | |
| 226 | + | void | |
| 227 | + | addColoredTextureCoords(CoordsBuffer &coords, const TexturePtr &texture, | |
| 228 | + | const std::vector<std::pair<int, Color>> &colors) { | |
| 229 | + | m_queue.push_back( | |
| 230 | + | new DrawQueueItemColoredTextureCoords(coords, texture, colors)); | |
| 231 | + | } | |
| 232 | + | void addFilledRect(const Rect &dest, const Color &color = Color::white) { | |
| 233 | + | m_queue.push_back(new DrawQueueItemFilledRect(dest, color)); | |
| 234 | + | } | |
| 235 | + | void addFillCoords(CoordsBuffer &coords, const Color &color = Color::white) { | |
| 236 | + | m_queue.push_back(new DrawQueueItemFillCoords(coords, color)); | |
| 237 | + | } | |
| 238 | + | void addClearRect(const Rect &dest, const Color &color = Color::white) { | |
| 239 | + | m_queue.push_back(new DrawQueueItemClearRect(dest, color)); | |
| 240 | + | } | |
| 241 | + | void addText(BitmapFontPtr font, const std::string &text, | |
| 242 | + | const Rect &screenCoords, | |
| 243 | + | Fw::AlignmentFlag align = Fw::AlignTopLeft, | |
| 244 | + | const Color &color = Color::white, bool shadow = false); | |
| 245 | + | void addColoredText(BitmapFontPtr font, const std::string &text, | |
| 246 | + | const Rect &screenCoords, Fw::AlignmentFlag align, | |
| 247 | + | const std::vector<std::pair<int, Color>> &colors, | |
| 248 | + | bool shadow = false); | |
| 249 | + | ||
| 250 | + | void addFilledTriangle(const Point &a, const Point &b, const Point &c, | |
| 251 | + | const Color &color = Color::white) { | |
| 252 | + | if (a == b || a == c || b == c) | |
| 253 | + | return; | |
| 254 | + | ||
| 255 | + | CoordsBuffer coordsBuffer; | |
| 256 | + | coordsBuffer.addTriangle(a, b, c); | |
| 257 | + | addFillCoords(coordsBuffer, color); | |
| 258 | + | } | |
| 259 | + | void addBoundingRect(const Rect &dest, int innerLineWidth, | |
| 260 | + | const Color &color = Color::white) { | |
| 261 | + | if (dest.isEmpty() || innerLineWidth == 0) | |
| 262 | + | return; | |
| 263 | + | ||
| 264 | + | CoordsBuffer coordsBuffer; | |
| 265 | + | coordsBuffer.addBoudingRect(dest, innerLineWidth); | |
| 266 | + | addFillCoords(coordsBuffer, color); | |
| 267 | + | } | |
| 268 | + | ||
| 269 | + | void addLine(const std::vector<Point> &points, int width, | |
| 270 | + | const Color &color = Color::white) { | |
| 271 | + | if (points.empty() || width < 0) | |
| 272 | + | return; | |
| 273 | + | ||
| 274 | + | m_queue.push_back(new DrawQueueItemLine(points, width, color)); | |
| 275 | + | } | |
| 276 | + | ||
| 277 | + | void setFrameBuffer(const Rect &dest, const Size &size, const Rect &src); | |
| 278 | + | bool hasFrameBuffer() { return m_useFrameBuffer; } | |
| 279 | + | Rect getFrameBufferDest() { return m_frameBufferDest; } | |
| 280 | + | Size getFrameBufferSize() { return m_frameBufferSize; } | |
| 281 | + | Rect getFrameBufferSrc() { return m_frameBufferSrc; } | |
| 282 | + | ||
| 283 | + | size_t size() { return m_queue.size(); } | |
| 284 | + | ||
| 285 | + | void setOpacity(size_t start, float opacity) { | |
| 286 | + | for (size_t i = start; i < m_queue.size(); ++i) { | |
| 287 | + | m_queue[i]->m_color = m_queue[i]->m_color.opacity(opacity); | |
| 288 | + | } | |
| 289 | + | } | |
| 290 | + | ||
| 291 | + | void setClip(size_t start, const Rect &clip) { | |
| 292 | + | if (start == m_queue.size()) | |
| 293 | + | return; | |
| 294 | + | m_conditions.push_back( | |
| 295 | + | new DrawQueueConditionClip(start, m_queue.size(), clip)); | |
| 296 | + | } | |
| 297 | + | ||
| 298 | + | void setRotation(size_t start, const Point ¢er, float angle) { | |
| 299 | + | if (start == m_queue.size() || angle == 0) | |
| 300 | + | return; | |
| 301 | + | m_conditions.push_back( | |
| 302 | + | new DrawQueueConditionRotation(start, m_queue.size(), center, angle)); | |
| 303 | + | } | |
| 304 | + | ||
| 305 | + | void setMark(size_t start, const Color &color) { | |
| 306 | + | if (start == m_queue.size()) | |
| 307 | + | return; | |
| 308 | + | m_conditions.push_back( | |
| 309 | + | new DrawQueueConditionMark(start, m_queue.size(), color)); | |
| 310 | + | } | |
| 311 | + | ||
| 312 | + | void markMapPosition() { mapPosition = m_queue.size(); } | |
| 313 | + | void correctOutfit(const Rect &dest, int fromPos, bool oldScaling); | |
| 314 | + | ||
| 315 | + | void setShader(const std::string &shader) { m_shader = shader; } | |
| 316 | + | ||
| 317 | + | std::string getShader() { return m_shader; } | |
| 318 | + | ||
| 319 | + | private: | |
| 320 | + | std::vector<DrawQueueItem *> m_queue; | |
| 321 | + | std::vector<DrawQueueCondition *> m_conditions; | |
| 322 | + | Size m_frameBufferSize; | |
| 323 | + | Rect m_frameBufferDest, m_frameBufferSrc; | |
| 324 | + | size_t mapPosition = 0; | |
| 325 | + | bool m_useFrameBuffer = false; | |
| 326 | + | float m_scaling = 1.f; | |
| 327 | + | std::string m_shader; | |
| 328 | + | ||
| 329 | + | friend struct DrawQueueConditionMark; | |
| 330 | + | }; | |
| 331 | + | ||
| 332 | + | extern std::shared_ptr<DrawQueue> g_drawQueue; | |
| 333 | + | ||
| 334 | + | #endif |
| @@ -0,0 +1,107 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "atlas.h" | |
| 24 | + | #include "fontmanager.h" | |
| 25 | + | #include "texture.h" | |
| 26 | + | ||
| 27 | + | #include <framework/core/eventdispatcher.h> | |
| 28 | + | #include <framework/core/resourcemanager.h> | |
| 29 | + | #include <framework/otml/otml.h> | |
| 30 | + | ||
| 31 | + | FontManager g_fonts; | |
| 32 | + | ||
| 33 | + | FontManager::FontManager() | |
| 34 | + | { | |
| 35 | + | m_defaultFont = BitmapFontPtr(new BitmapFont("emptyfont")); | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | void FontManager::terminate() | |
| 39 | + | { | |
| 40 | + | m_fonts.clear(); | |
| 41 | + | m_defaultFont = nullptr; | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | void FontManager::clearFonts() | |
| 45 | + | { | |
| 46 | + | m_fonts.clear(); | |
| 47 | + | m_defaultFont = BitmapFontPtr(new BitmapFont("emptyfont")); | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | void FontManager::importFont(std::string file) | |
| 51 | + | { | |
| 52 | + | if (g_graphicsThreadId != std::this_thread::get_id()) { | |
| 53 | + | g_graphicsDispatcher.addEvent(std::bind(&FontManager::importFont, this, file)); | |
| 54 | + | return; | |
| 55 | + | } | |
| 56 | + | try { | |
| 57 | + | file = g_resources.guessFilePath(file, "otfont"); | |
| 58 | + | ||
| 59 | + | OTMLDocumentPtr doc = OTMLDocument::parse(file); | |
| 60 | + | OTMLNodePtr fontNode = doc->at("Font"); | |
| 61 | + | ||
| 62 | + | std::string name = fontNode->valueAt("name"); | |
| 63 | + | if (fontExists(name)) | |
| 64 | + | return; | |
| 65 | + | ||
| 66 | + | // remove any font with the same name | |
| 67 | + | for(auto it = m_fonts.begin(); it != m_fonts.end(); ++it) { | |
| 68 | + | if((*it)->getName() == name) { | |
| 69 | + | m_fonts.erase(it); | |
| 70 | + | break; | |
| 71 | + | } | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | BitmapFontPtr font(new BitmapFont(name)); | |
| 75 | + | font->load(fontNode); | |
| 76 | + | m_fonts.push_back(font); | |
| 77 | + | ||
| 78 | + | // set as default if needed | |
| 79 | + | if(!m_defaultFont || fontNode->valueAt<bool>("default", false)) | |
| 80 | + | m_defaultFont = font; | |
| 81 | + | ||
| 82 | + | } catch(stdext::exception& e) { | |
| 83 | + | g_logger.error(stdext::format("Unable to load font from file '%s': %s", file, e.what())); | |
| 84 | + | } | |
| 85 | + | } | |
| 86 | + | ||
| 87 | + | bool FontManager::fontExists(const std::string& fontName) | |
| 88 | + | { | |
| 89 | + | for(const BitmapFontPtr& font : m_fonts) { | |
| 90 | + | if(font->getName() == fontName) | |
| 91 | + | return true; | |
| 92 | + | } | |
| 93 | + | return false; | |
| 94 | + | } | |
| 95 | + | ||
| 96 | + | BitmapFontPtr FontManager::getFont(const std::string& fontName) | |
| 97 | + | { | |
| 98 | + | // find font by name | |
| 99 | + | for(const BitmapFontPtr& font : m_fonts) { | |
| 100 | + | if(font->getName() == fontName) | |
| 101 | + | return font; | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | // when not found, fallback to default font | |
| 105 | + | g_logger.error(stdext::format("font '%s' not found", fontName)); | |
| 106 | + | return getDefaultFont(); | |
| 107 | + | } |
| @@ -0,0 +1,52 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef FONTMANAGER_H | |
| 24 | + | #define FONTMANAGER_H | |
| 25 | + | ||
| 26 | + | #include "bitmapfont.h" | |
| 27 | + | ||
| 28 | + | //@bindsingleton g_fonts | |
| 29 | + | class FontManager | |
| 30 | + | { | |
| 31 | + | public: | |
| 32 | + | FontManager(); | |
| 33 | + | ||
| 34 | + | void terminate(); | |
| 35 | + | void clearFonts(); | |
| 36 | + | ||
| 37 | + | void importFont(std::string file); | |
| 38 | + | ||
| 39 | + | bool fontExists(const std::string& fontName); | |
| 40 | + | BitmapFontPtr getFont(const std::string& fontName); | |
| 41 | + | BitmapFontPtr getDefaultFont() { return m_defaultFont; } | |
| 42 | + | ||
| 43 | + | void setDefaultFont(const std::string& fontName) { m_defaultFont = getFont(fontName); } | |
| 44 | + | ||
| 45 | + | private: | |
| 46 | + | std::vector<BitmapFontPtr> m_fonts; | |
| 47 | + | BitmapFontPtr m_defaultFont; | |
| 48 | + | }; | |
| 49 | + | ||
| 50 | + | extern FontManager g_fonts; | |
| 51 | + | ||
| 52 | + | #endif |
| @@ -0,0 +1,239 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "framebuffer.h" | |
| 24 | + | #include "graphics.h" | |
| 25 | + | #include "texture.h" | |
| 26 | + | ||
| 27 | + | #include <framework/platform/platformwindow.h> | |
| 28 | + | #include <framework/core/application.h> | |
| 29 | + | #include <framework/core/eventdispatcher.h> | |
| 30 | + | #include <framework/core/asyncdispatcher.h> | |
| 31 | + | #include <framework/graphics/image.h> | |
| 32 | + | ||
| 33 | + | uint FrameBuffer::boundFbo = 0; | |
| 34 | + | ||
| 35 | + | FrameBuffer::FrameBuffer(bool withDepth) | |
| 36 | + | { | |
| 37 | + | #ifdef WITH_DEPTH_BUFFER | |
| 38 | + | m_depth = withDepth; | |
| 39 | + | #else | |
| 40 | + | if(withDepth) | |
| 41 | + | g_logger.fatal("Depth buffer is not supported. Compile OTCv8 with WITH_DEPTH_BUFFER definition."); | |
| 42 | + | #endif | |
| 43 | + | internalCreate(); | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | void FrameBuffer::internalCreate() | |
| 47 | + | { | |
| 48 | + | m_prevBoundFbo = 0; | |
| 49 | + | m_fbo = 0; | |
| 50 | + | glGenFramebuffers(1, &m_fbo); | |
| 51 | + | if (!m_fbo) | |
| 52 | + | g_logger.fatal("Unable to create framebuffer object"); | |
| 53 | + | #ifdef WITH_DEPTH_BUFFER | |
| 54 | + | if (m_depth) { | |
| 55 | + | glGenRenderbuffers(1, &m_depthRbo); | |
| 56 | + | if (!m_depthRbo) | |
| 57 | + | g_logger.fatal("Unable to create renderbuffer object"); | |
| 58 | + | } | |
| 59 | + | #endif | |
| 60 | + | g_graphics.checkForError(__FUNCTION__, __FILE__, __LINE__); | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | FrameBuffer::~FrameBuffer() | |
| 64 | + | { | |
| 65 | + | VALIDATE(!g_app.isTerminated()); | |
| 66 | + | if (g_graphics.ok() && m_fbo != 0) { | |
| 67 | + | if (m_fbo != 0) | |
| 68 | + | glDeleteFramebuffers(1, &m_fbo); | |
| 69 | + | #ifdef WITH_DEPTH_BUFFER | |
| 70 | + | if (m_depthRbo != 0) | |
| 71 | + | glDeleteRenderbuffers(1, &m_depthRbo); | |
| 72 | + | #endif | |
| 73 | + | } | |
| 74 | + | g_graphics.checkForError(__FUNCTION__, __FILE__, __LINE__); | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | void FrameBuffer::resize(const Size& size) | |
| 78 | + | { | |
| 79 | + | if (!size.isValid()) | |
| 80 | + | g_logger.fatal(stdext::format("Invalid framebuffer size: %ix%i", size.width(), size.height())); | |
| 81 | + | ||
| 82 | + | if (m_texture && m_texture->getSize() == size) | |
| 83 | + | return; | |
| 84 | + | ||
| 85 | + | m_texture = TexturePtr(new Texture(size, false, m_smooth, true)); | |
| 86 | + | m_texture->update(); | |
| 87 | + | ||
| 88 | + | #ifdef WITH_DEPTH_BUFFER | |
| 89 | + | if (m_depth) { | |
| 90 | + | glBindRenderbuffer(GL_RENDERBUFFER, m_depthRbo); | |
| 91 | + | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height()); | |
| 92 | + | } | |
| 93 | + | #endif | |
| 94 | + | ||
| 95 | + | internalBind(); | |
| 96 | + | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture->getId(), 0); | |
| 97 | + | ||
| 98 | + | #ifdef WITH_DEPTH_BUFFER | |
| 99 | + | if (m_depth) { | |
| 100 | + | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthRbo); | |
| 101 | + | } | |
| 102 | + | #endif | |
| 103 | + | ||
| 104 | + | GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); | |
| 105 | + | if (status != GL_FRAMEBUFFER_COMPLETE) | |
| 106 | + | g_logger.fatal(stdext::format("Unable to setup framebuffer object - %i - %ix%i", status, size.width(), size.height())); | |
| 107 | + | internalRelease(); | |
| 108 | + | g_graphics.checkForError(__FUNCTION__, __FILE__, __LINE__); | |
| 109 | + | } | |
| 110 | + | ||
| 111 | + | void FrameBuffer::bind(const FrameBufferPtr& depthFramebuffer) | |
| 112 | + | { | |
| 113 | + | g_painter->saveAndResetState(); | |
| 114 | + | internalBind(); | |
| 115 | + | g_painter->setResolution(m_texture->getSize()); | |
| 116 | + | #ifdef WITH_DEPTH_BUFFER | |
| 117 | + | if (!m_depth && depthFramebuffer && depthFramebuffer->hasDepth()) { | |
| 118 | + | m_depthRbo = depthFramebuffer->getDepthRenderBuffer(); | |
| 119 | + | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthRbo); | |
| 120 | + | } | |
| 121 | + | #endif | |
| 122 | + | g_graphics.checkForError(__FUNCTION__, __FILE__, __LINE__); | |
| 123 | + | } | |
| 124 | + | ||
| 125 | + | void FrameBuffer::release() | |
| 126 | + | { | |
| 127 | + | #ifdef WITH_DEPTH_BUFFER | |
| 128 | + | if (!m_depth && m_depthRbo) { | |
| 129 | + | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); | |
| 130 | + | m_depthRbo = 0; | |
| 131 | + | } | |
| 132 | + | #endif | |
| 133 | + | internalRelease(); | |
| 134 | + | g_painter->restoreSavedState(); | |
| 135 | + | g_graphics.checkForError(__FUNCTION__, __FILE__, __LINE__); | |
| 136 | + | } | |
| 137 | + | ||
| 138 | + | void FrameBuffer::draw() | |
| 139 | + | { | |
| 140 | + | Rect rect(0, 0, getSize()); | |
| 141 | + | g_painter->drawTexturedRect(rect, m_texture, rect); | |
| 142 | + | } | |
| 143 | + | ||
| 144 | + | void FrameBuffer::draw(const Rect& dest, const Rect& src) | |
| 145 | + | { | |
| 146 | + | g_painter->drawTexturedRect(dest, m_texture, src); | |
| 147 | + | } | |
| 148 | + | ||
| 149 | + | void FrameBuffer::draw(const Rect& dest) | |
| 150 | + | { | |
| 151 | + | g_painter->drawTexturedRect(dest, m_texture, Rect(0, 0, getSize())); | |
| 152 | + | } | |
| 153 | + | ||
| 154 | + | std::vector<uint32_t> FrameBuffer::readPixels() | |
| 155 | + | { | |
| 156 | + | internalBind(); | |
| 157 | + | Size size = getSize(); | |
| 158 | + | int width = size.width(); | |
| 159 | + | int height = size.height(); | |
| 160 | + | std::vector<uint32_t> ret(width * height * sizeof(GLubyte), 0); | |
| 161 | + | glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, (GLubyte*)(ret.data())); | |
| 162 | + | internalRelease(); | |
| 163 | + | int halfWidth = width / 2; | |
| 164 | + | for (int y = 0; y < height; ++y) { | |
| 165 | + | for (int x = 0; x < halfWidth; ++x) { | |
| 166 | + | std::swap(ret[y * width + x], ret[y * width + width - x - 1]); | |
| 167 | + | } | |
| 168 | + | } | |
| 169 | + | for (int y = 0; y < height; ++y) { | |
| 170 | + | for (int x = 0; x < halfWidth; ++x) { | |
| 171 | + | std::swap(ret[y * width + x], ret[(height - y - 1) * width + width - x - 1]); | |
| 172 | + | } | |
| 173 | + | } | |
| 174 | + | return ret; | |
| 175 | + | } | |
| 176 | + | ||
| 177 | + | void FrameBuffer::internalBind() | |
| 178 | + | { | |
| 179 | + | VALIDATE(boundFbo != m_fbo); | |
| 180 | + | glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); | |
| 181 | + | m_prevBoundFbo = boundFbo; | |
| 182 | + | boundFbo = m_fbo; | |
| 183 | + | } | |
| 184 | + | ||
| 185 | + | void FrameBuffer::internalRelease() | |
| 186 | + | { | |
| 187 | + | VALIDATE(boundFbo == m_fbo); | |
| 188 | + | glBindFramebuffer(GL_FRAMEBUFFER, m_prevBoundFbo); | |
| 189 | + | boundFbo = m_prevBoundFbo; | |
| 190 | + | } | |
| 191 | + | ||
| 192 | + | Size FrameBuffer::getSize() | |
| 193 | + | { | |
| 194 | + | return m_texture->getSize(); | |
| 195 | + | } | |
| 196 | + | ||
| 197 | + | void FrameBuffer::setSmooth(bool value) | |
| 198 | + | { | |
| 199 | + | if (!m_texture || m_smooth == value) return; | |
| 200 | + | ||
| 201 | + | m_smooth = value; | |
| 202 | + | m_texture->setSmooth(value); | |
| 203 | + | } | |
| 204 | + | ||
| 205 | + | void FrameBuffer::doScreenshot(std::string fileName) | |
| 206 | + | { | |
| 207 | + | if (g_mainThreadId != std::this_thread::get_id()) { | |
| 208 | + | g_graphicsDispatcher.addEvent(std::bind(&FrameBuffer::doScreenshot, this, fileName)); | |
| 209 | + | return; | |
| 210 | + | } | |
| 211 | + | ||
| 212 | + | if (fileName.empty()) { | |
| 213 | + | fileName = "screenshot_map.png"; | |
| 214 | + | } | |
| 215 | + | ||
| 216 | + | internalBind(); | |
| 217 | + | Size size = getSize(); | |
| 218 | + | int width = size.width(); | |
| 219 | + | int height = size.height(); | |
| 220 | + | auto pixels = std::make_shared<std::vector<uint8_t>>(width * height * 4 * sizeof(GLubyte), 0); | |
| 221 | + | glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, (GLubyte*)(pixels->data())); | |
| 222 | + | internalRelease(); | |
| 223 | + | ||
| 224 | + | g_asyncDispatcher.dispatch([size, pixels, fileName] { | |
| 225 | + | for (int line = 0, h = size.height(), w = size.width(); line != h / 2; ++line) { | |
| 226 | + | std::swap_ranges( | |
| 227 | + | pixels->begin() + 4 * w * line, | |
| 228 | + | pixels->begin() + 4 * w * (line + 1), | |
| 229 | + | pixels->begin() + 4 * w * (h - line - 1)); | |
| 230 | + | } | |
| 231 | + | try { | |
| 232 | + | Image image(size, 4, pixels->data()); | |
| 233 | + | image.savePNG(fileName); | |
| 234 | + | } | |
| 235 | + | catch (stdext::exception& e) { | |
| 236 | + | g_logger.error(std::string("Can't do map screenshot: ") + e.what()); | |
| 237 | + | } | |
| 238 | + | }); | |
| 239 | + | } |
| @@ -0,0 +1,82 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef FRAMEBUFFER_H | |
| 24 | + | #define FRAMEBUFFER_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | #include "texture.h" | |
| 28 | + | ||
| 29 | + | class FrameBuffer : public stdext::shared_object | |
| 30 | + | { | |
| 31 | + | public: | |
| 32 | + | FrameBuffer(bool withDepth = false); | |
| 33 | + | ||
| 34 | + | protected: | |
| 35 | + | friend class FrameBufferManager; | |
| 36 | + | ||
| 37 | + | public: | |
| 38 | + | virtual ~FrameBuffer(); | |
| 39 | + | ||
| 40 | + | void resize(const Size& size); | |
| 41 | + | void bind(const FrameBufferPtr& depthFramebuffer = nullptr); | |
| 42 | + | void release(); | |
| 43 | + | void draw(); | |
| 44 | + | void draw(const Rect& dest); | |
| 45 | + | void draw(const Rect& dest, const Rect& src); | |
| 46 | + | ||
| 47 | + | void setSmooth(bool enabled); | |
| 48 | + | ||
| 49 | + | TexturePtr getTexture() { return m_texture; } | |
| 50 | + | Size getSize(); | |
| 51 | + | bool isSmooth() { return m_smooth; } | |
| 52 | + | ||
| 53 | + | #ifdef WITH_DEPTH_BUFFER | |
| 54 | + | uint getDepthRenderBuffer() { return m_depthRbo; } | |
| 55 | + | bool hasDepth() { return m_depth; } | |
| 56 | + | #endif | |
| 57 | + | ||
| 58 | + | std::vector<uint32_t> readPixels(); | |
| 59 | + | ||
| 60 | + | void doScreenshot(std::string fileName); | |
| 61 | + | ||
| 62 | + | private: | |
| 63 | + | void internalCreate(); | |
| 64 | + | void internalBind(); | |
| 65 | + | void internalRelease(); | |
| 66 | + | ||
| 67 | + | TexturePtr m_texture; | |
| 68 | + | TexturePtr m_screenBackup; | |
| 69 | + | Size m_oldViewportSize; | |
| 70 | + | uint m_fbo; | |
| 71 | + | uint m_prevBoundFbo; | |
| 72 | + | bool m_smooth = true; | |
| 73 | + | ||
| 74 | + | #ifdef WITH_DEPTH_BUFFER | |
| 75 | + | uint m_depthRbo = 0; | |
| 76 | + | bool m_depth = false; | |
| 77 | + | #endif | |
| 78 | + | ||
| 79 | + | static uint boundFbo; | |
| 80 | + | }; | |
| 81 | + | ||
| 82 | + | #endif |
| @@ -0,0 +1,48 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | ||
| 24 | + | #include "framebuffermanager.h" | |
| 25 | + | ||
| 26 | + | FrameBufferManager g_framebuffers; | |
| 27 | + | ||
| 28 | + | void FrameBufferManager::init() | |
| 29 | + | { | |
| 30 | + | m_temporaryFramebuffer = FrameBufferPtr(new FrameBuffer()); | |
| 31 | + | m_temporaryFramebuffer->setSmooth(true); | |
| 32 | + | m_drawQueueTemporaryFramebuffer = FrameBufferPtr(new FrameBuffer()); | |
| 33 | + | m_drawQueueTemporaryFramebuffer->setSmooth(true); | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | void FrameBufferManager::terminate() | |
| 37 | + | { | |
| 38 | + | m_framebuffers.clear(); | |
| 39 | + | m_temporaryFramebuffer = nullptr; | |
| 40 | + | m_drawQueueTemporaryFramebuffer = nullptr; | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | FrameBufferPtr FrameBufferManager::createFrameBuffer(bool withDepth) | |
| 44 | + | { | |
| 45 | + | FrameBufferPtr fbo = FrameBufferPtr(new FrameBuffer(withDepth)); | |
| 46 | + | m_framebuffers.push_back(fbo); | |
| 47 | + | return fbo; | |
| 48 | + | } |
| @@ -0,0 +1,48 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | ||
| 24 | + | #ifndef FRAMEBUFFERMANAGER_H | |
| 25 | + | #define FRAMEBUFFERMANAGER_H | |
| 26 | + | ||
| 27 | + | #include "framebuffer.h" | |
| 28 | + | ||
| 29 | + | class FrameBufferManager | |
| 30 | + | { | |
| 31 | + | public: | |
| 32 | + | void init(); | |
| 33 | + | void terminate(); | |
| 34 | + | void clear(); | |
| 35 | + | ||
| 36 | + | FrameBufferPtr createFrameBuffer(bool withDepth = false); | |
| 37 | + | const FrameBufferPtr& getTemporaryFrameBuffer() { return m_temporaryFramebuffer; } | |
| 38 | + | const FrameBufferPtr& getDrawQueueTemporaryFrameBuffer() { return m_drawQueueTemporaryFramebuffer; } | |
| 39 | + | ||
| 40 | + | protected: | |
| 41 | + | FrameBufferPtr m_temporaryFramebuffer; | |
| 42 | + | FrameBufferPtr m_drawQueueTemporaryFramebuffer; | |
| 43 | + | std::vector<FrameBufferPtr> m_framebuffers; | |
| 44 | + | }; | |
| 45 | + | ||
| 46 | + | extern FrameBufferManager g_framebuffers; | |
| 47 | + | ||
| 48 | + | #endif |
| @@ -0,0 +1,52 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | ||
| 24 | + | #ifndef GLUTIL_H | |
| 25 | + | #define GLUTIL_H | |
| 26 | + | ||
| 27 | + | #if defined(ANDROID) || defined(__EMSCRIPTEN__) | |
| 28 | + | #include <GLES2/gl2.h> | |
| 29 | + | #include <GLES2/gl2ext.h> | |
| 30 | + | #include <EGL/egl.h> | |
| 31 | + | #include <EGL/eglext.h> | |
| 32 | + | #elif OPENGL_ES | |
| 33 | + | #define EGL_EGL_PROTOTYPES 1 | |
| 34 | + | #define GL_GLEXT_PROTOTYPES 1 | |
| 35 | + | #define EGL_EGLEXT_PROTOTYPES 1 | |
| 36 | + | // for static linking | |
| 37 | + | #define GL_APICALL | |
| 38 | + | #define EGLAPI | |
| 39 | + | ||
| 40 | + | #include <GLES2/gl2.h> | |
| 41 | + | #include <GLES2/gl2ext.h> | |
| 42 | + | #include <EGL/egl.h> | |
| 43 | + | #include <EGL/eglext.h> | |
| 44 | + | #include <EGL/eglext_angle.h> | |
| 45 | + | #else | |
| 46 | + | #ifndef _MSC_VER | |
| 47 | + | #define GLEW_STATIC | |
| 48 | + | #endif | |
| 49 | + | #include <GL/glew.h> | |
| 50 | + | #endif | |
| 51 | + | ||
| 52 | + | #endif |
| @@ -0,0 +1,94 @@ | |||
| 1 | + | #include "graph.h" | |
| 2 | + | #include "painter.h" | |
| 3 | + | #include "textrender.h" | |
| 4 | + | #include "fontmanager.h" | |
| 5 | + | #include <framework/core/eventdispatcher.h> | |
| 6 | + | ||
| 7 | + | Graph g_graphs[GRAPH_LAST + 1] = { | |
| 8 | + | {"Total frame time"}, | |
| 9 | + | {"CPU render time"}, | |
| 10 | + | {"GPU calls"}, | |
| 11 | + | {"GPU draws"}, | |
| 12 | + | {"Processing poll time"}, | |
| 13 | + | {"Graphics poll time"}, | |
| 14 | + | {"Dispatcher events"}, | |
| 15 | + | {"Graphics events"}, | |
| 16 | + | {"Latency"} | |
| 17 | + | }; | |
| 18 | + | ||
| 19 | + | Graph::Graph(const std::string& name, size_t capacity) : m_name(name), m_capacity(capacity) | |
| 20 | + | { | |
| 21 | + | if (capacity > MAX_CAPACITY) | |
| 22 | + | g_logger.fatal(stdext::format("Max Graph capacity is %i, tried to create graph with capacity: %i", MAX_CAPACITY, capacity)); | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | void Graph::draw(const Rect& dest) | |
| 26 | + | { | |
| 27 | + | VALIDATE_GRAPHICS_THREAD(); | |
| 28 | + | static std::vector<float> vertices(MAX_CAPACITY * 2, 0); // max 512 lines | |
| 29 | + | if (dest.width() < 50) return; | |
| 30 | + | float offsetX = dest.left() + 2; | |
| 31 | + | float offsetY = dest.top() + 17; | |
| 32 | + | size_t elements = std::min<size_t>(m_values.size(), dest.width() / 2 - 2); | |
| 33 | + | ||
| 34 | + | vertices[0] = dest.left(); | |
| 35 | + | vertices[1] = offsetY; | |
| 36 | + | vertices[2] = dest.right(); | |
| 37 | + | vertices[3] = offsetY; | |
| 38 | + | vertices[4] = dest.right(); | |
| 39 | + | vertices[5] = dest.bottom(); | |
| 40 | + | vertices[6] = dest.left(); | |
| 41 | + | vertices[7] = dest.bottom(); | |
| 42 | + | vertices[8] = dest.left(); | |
| 43 | + | vertices[9] = offsetY; | |
| 44 | + | ||
| 45 | + | g_painter->drawLine(vertices, 5); | |
| 46 | + | g_text.drawText(dest, m_name, g_fonts.getDefaultFont()); | |
| 47 | + | ||
| 48 | + | m_mutex.lock(); | |
| 49 | + | size_t start = m_values.size() - elements; | |
| 50 | + | int minVal = 0xFFFFFF, maxVal = -0xFFFFFF; | |
| 51 | + | for (size_t i = start; i < m_values.size(); ++i) { | |
| 52 | + | if (minVal > m_values[i]) | |
| 53 | + | minVal = m_values[i]; | |
| 54 | + | if (maxVal < m_values[i]) | |
| 55 | + | maxVal = m_values[i]; | |
| 56 | + | } | |
| 57 | + | // round | |
| 58 | + | maxVal = (1 + maxVal / 10) * 10; | |
| 59 | + | minVal = (minVal / 10) * 10; | |
| 60 | + | float step = (float)(dest.height() - 21) / std::max<float>(1, (maxVal - minVal)); | |
| 61 | + | for (size_t i = start, j = 0; i < m_values.size(); ++i) { | |
| 62 | + | vertices[j] = offsetX + j; | |
| 63 | + | vertices[j + 1] = offsetY + 1 + (maxVal - m_values[i]) * step; | |
| 64 | + | j += 2; | |
| 65 | + | } | |
| 66 | + | m_mutex.unlock(); | |
| 67 | + | ||
| 68 | + | if (elements > 0) { | |
| 69 | + | g_text.drawText(dest, std::to_string(m_values.back()), g_fonts.getDefaultFont(), Color::white, Fw::AlignTopRight); | |
| 70 | + | g_text.drawText(Rect(dest.left() + 1, offsetY, 50, 15), std::to_string(maxVal), g_fonts.getDefaultFont()); | |
| 71 | + | g_text.drawText(Rect(dest.left() + 1, dest.bottom() - 14, 50, 15), std::to_string(minVal), g_fonts.getDefaultFont()); | |
| 72 | + | } | |
| 73 | + | g_painter->setColor(Color::white); | |
| 74 | + | g_painter->drawLine(vertices, elements); | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | void Graph::clear() | |
| 78 | + | { | |
| 79 | + | std::lock_guard<std::mutex> lock(m_mutex); | |
| 80 | + | m_values.clear(); | |
| 81 | + | } | |
| 82 | + | ||
| 83 | + | void Graph::addValue(int value, bool ignoreSmallValues) | |
| 84 | + | { | |
| 85 | + | std::lock_guard<std::mutex> lock(m_mutex); | |
| 86 | + | if (ignoreSmallValues) { | |
| 87 | + | if (!m_values.empty() && m_values.back() <= 2 && value <= 2 && ++m_ignores <= 10) | |
| 88 | + | return; | |
| 89 | + | m_ignores = 0; | |
| 90 | + | } | |
| 91 | + | m_values.push_back(value); | |
| 92 | + | if (m_values.size() > m_capacity) | |
| 93 | + | m_values.pop_front(); | |
| 94 | + | } |
| @@ -0,0 +1,43 @@ | |||
| 1 | + | // Graph is a simple, thread-safe class to draw debug informations about FPS, CPU time per frame, Ping, etc. | |
| 2 | + | // It's not optimized, so should be used only as debugging tool | |
| 3 | + | ||
| 4 | + | #ifndef GRAPH_H | |
| 5 | + | #define GRAPH_H | |
| 6 | + | ||
| 7 | + | #include <framework/global.h> | |
| 8 | + | ||
| 9 | + | enum GraphType { | |
| 10 | + | GRAPH_TOTAL_FRAME_TIME = 0, | |
| 11 | + | GRAPH_CPU_FRAME_TIME = 1, | |
| 12 | + | GRAPH_GPU_CALLS = 2, | |
| 13 | + | GRAPH_GPU_DRAWS = 3, | |
| 14 | + | GRAPH_PROCESSING_POLL = 4, | |
| 15 | + | GRAPH_GRAPHICS_POLL = 5, | |
| 16 | + | GRAPH_DISPATCHER_EVENTS = 6, | |
| 17 | + | GRAPH_GRAPHICS_EVENTS = 7, | |
| 18 | + | GRAPH_LATENCY = 8, | |
| 19 | + | GRAPH_LAST = GRAPH_LATENCY | |
| 20 | + | }; | |
| 21 | + | ||
| 22 | + | class Graph | |
| 23 | + | { | |
| 24 | + | static constexpr int MAX_CAPACITY = 512; | |
| 25 | + | public: | |
| 26 | + | Graph(const std::string& name, size_t capacity = 100); | |
| 27 | + | ||
| 28 | + | void draw(const Rect& dest); | |
| 29 | + | ||
| 30 | + | void clear(); | |
| 31 | + | void addValue(int value, bool ignoreSmallValues = false); | |
| 32 | + | ||
| 33 | + | private: | |
| 34 | + | std::string m_name; | |
| 35 | + | size_t m_capacity; | |
| 36 | + | size_t m_ignores = 0; | |
| 37 | + | std::deque<int> m_values; | |
| 38 | + | std::mutex m_mutex; | |
| 39 | + | }; | |
| 40 | + | ||
| 41 | + | extern Graph g_graphs[GRAPH_LAST + 1]; | |
| 42 | + | ||
| 43 | + | #endif |
| @@ -0,0 +1,159 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "fontmanager.h" | |
| 24 | + | ||
| 25 | + | #include "painter.h" | |
| 26 | + | ||
| 27 | + | #include <framework/graphics/atlas.h> | |
| 28 | + | #include <framework/graphics/graphics.h> | |
| 29 | + | #include <framework/graphics/texture.h> | |
| 30 | + | #include "texturemanager.h" | |
| 31 | + | #include "framebuffermanager.h" | |
| 32 | + | #include <framework/platform/platformwindow.h> | |
| 33 | + | #include <framework/graphics/textrender.h> | |
| 34 | + | ||
| 35 | + | Graphics g_graphics; | |
| 36 | + | ||
| 37 | + | Graphics::Graphics() | |
| 38 | + | { | |
| 39 | + | m_maxTextureSize = 2048; | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | void Graphics::init() | |
| 43 | + | { | |
| 44 | + | g_graphics.checkForError(__FUNCTION__, __FILE__, __LINE__); | |
| 45 | + | glGetString(GL_NONE); glGetError(); // REQUIRED, DON'T REMOVE | |
| 46 | + | m_vendor = std::string((const char*)glGetString(GL_VENDOR) ? (const char*)glGetString(GL_VENDOR) : ""); | |
| 47 | + | m_renderer = std::string((const char*)glGetString(GL_RENDERER) ? (const char*)glGetString(GL_RENDERER) : ""); | |
| 48 | + | m_version = std::string((const char*)glGetString(GL_VERSION) ? (const char*)glGetString(GL_VERSION) : ""); | |
| 49 | + | m_extensions = std::string((const char*)glGetString(GL_EXTENSIONS) ? (const char*)glGetString(GL_EXTENSIONS) : ""); | |
| 50 | + | ||
| 51 | + | g_logger.info(stdext::format("GPU %s (%s)", m_renderer, m_vendor)); | |
| 52 | + | g_logger.info(stdext::format("OpenGL %s", m_version)); | |
| 53 | + | ||
| 54 | + | // init GL extensions | |
| 55 | + | #ifndef OPENGL_ES | |
| 56 | + | float glVersion = 1.0; | |
| 57 | + | try { | |
| 58 | + | glVersion = std::atof(m_version.c_str()); | |
| 59 | + | } | |
| 60 | + | catch (const std::exception&) {} | |
| 61 | + | ||
| 62 | + | if (glVersion < 2.0) { | |
| 63 | + | g_logger.fatal(stdext::format("Your device doesn't support OpenGL >= 2.0, try to use DX version or install graphics drivers. GPU: %s OpenGL: %s (%f), Ext: %s", | |
| 64 | + | m_renderer, m_version, glVersion, m_extensions)); | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | GLenum err = glewInit(); | |
| 68 | + | if(err != GLEW_OK) | |
| 69 | + | g_logger.fatal(stdext::format("Unable to init GLEW: %s", glewGetErrorString(err))); | |
| 70 | + | ||
| 71 | + | // overwrite framebuffer API if needed | |
| 72 | + | if(GLEW_EXT_framebuffer_object && !GLEW_ARB_framebuffer_object) { | |
| 73 | + | glGenFramebuffers = glGenFramebuffersEXT; | |
| 74 | + | glDeleteFramebuffers = glDeleteFramebuffersEXT; | |
| 75 | + | glBindFramebuffer = glBindFramebufferEXT; | |
| 76 | + | glFramebufferTexture2D = glFramebufferTexture2DEXT; | |
| 77 | + | glCheckFramebufferStatus = glCheckFramebufferStatusEXT; | |
| 78 | + | glGenerateMipmap = glGenerateMipmapEXT; | |
| 79 | + | } | |
| 80 | + | #endif | |
| 81 | + | ||
| 82 | + | // blending is always enabled | |
| 83 | + | glEnable(GL_BLEND); | |
| 84 | + | ||
| 85 | + | int maxTextureSize = 0; | |
| 86 | + | glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); | |
| 87 | + | if (m_maxTextureSize == -1 || m_maxTextureSize < maxTextureSize) | |
| 88 | + | m_maxTextureSize = maxTextureSize; | |
| 89 | + | ||
| 90 | + | #ifdef WITH_DEPTH_BUFFER | |
| 91 | + | checkDepthSupport(); | |
| 92 | + | #endif | |
| 93 | + | g_graphics.checkForError(__FUNCTION__, __FILE__, __LINE__); | |
| 94 | + | ||
| 95 | + | m_ok = true; | |
| 96 | + | ||
| 97 | + | g_painter = new Painter(); | |
| 98 | + | g_painter->bind(); | |
| 99 | + | ||
| 100 | + | g_textures.init(); | |
| 101 | + | g_framebuffers.init(); | |
| 102 | + | g_atlas.init(); | |
| 103 | + | g_text.init(); | |
| 104 | + | g_graphics.checkForError(__FUNCTION__, __FILE__, __LINE__); | |
| 105 | + | } | |
| 106 | + | ||
| 107 | + | void Graphics::terminate() | |
| 108 | + | { | |
| 109 | + | g_fonts.terminate(); | |
| 110 | + | g_framebuffers.terminate(); | |
| 111 | + | g_textures.terminate(); | |
| 112 | + | g_atlas.terminate(); | |
| 113 | + | g_text.terminate(); | |
| 114 | + | ||
| 115 | + | if (g_painter) { | |
| 116 | + | g_painter->unbind(); | |
| 117 | + | delete g_painter; | |
| 118 | + | g_painter = nullptr; | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | m_ok = false; | |
| 122 | + | } | |
| 123 | + | ||
| 124 | + | void Graphics::resize(const Size& size) | |
| 125 | + | { | |
| 126 | + | m_viewportSize = size; | |
| 127 | + | if(g_painter) | |
| 128 | + | g_painter->setResolution(size); | |
| 129 | + | } | |
| 130 | + | ||
| 131 | + | #ifdef WITH_DEPTH_BUFFER | |
| 132 | + | void Graphics::checkDepthSupport() | |
| 133 | + | { | |
| 134 | + | glGetError(); // clear error | |
| 135 | + | uint32 rbo = 0; | |
| 136 | + | glGenRenderbuffers(1, &rbo); | |
| 137 | + | if (rbo) { | |
| 138 | + | glBindRenderbuffer(GL_RENDERBUFFER, rbo); | |
| 139 | + | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 64, 64); | |
| 140 | + | glDeleteRenderbuffers(1, &rbo); | |
| 141 | + | if (glGetError() == GL_NO_ERROR) | |
| 142 | + | return; | |
| 143 | + | } | |
| 144 | + | g_logger.fatal("Depth buffer is not supported. Your computer or graphics drivers are too old to use this program.") | |
| 145 | + | } | |
| 146 | + | #endif | |
| 147 | + | ||
| 148 | + | void Graphics::checkForError(const std::string& function, const std::string& file, int line) | |
| 149 | + | { | |
| 150 | + | auto error = glGetError(); | |
| 151 | + | if (error != GL_NO_ERROR) { | |
| 152 | + | #ifndef NDEBUG | |
| 153 | + | g_logger.fatal( | |
| 154 | + | #else | |
| 155 | + | g_logger.error( | |
| 156 | + | #endif | |
| 157 | + | stdext::format("Render error: %i in %s (%s:%i)", error, function, file, line)); | |
| 158 | + | } | |
| 159 | + | } |