| @@ -0,0 +1,74 @@ | |||
| 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 GRAPHICS_H | |
| 24 | + | #define GRAPHICS_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | #include "painter.h" | |
| 28 | + | ||
| 29 | + | class Painter; | |
| 30 | + | ||
| 31 | + | // @bindsingleton g_graphics | |
| 32 | + | class Graphics | |
| 33 | + | { | |
| 34 | + | public: | |
| 35 | + | Graphics(); | |
| 36 | + | ||
| 37 | + | // @dontbind | |
| 38 | + | void init(); | |
| 39 | + | // @dontbind | |
| 40 | + | void terminate(); | |
| 41 | + | ||
| 42 | + | void resize(const Size& size); | |
| 43 | + | void checkDepthSupport(); | |
| 44 | + | ||
| 45 | + | int getMaxTextureSize() { return m_maxTextureSize; } | |
| 46 | + | const Size& getViewportSize() { return m_viewportSize; } | |
| 47 | + | ||
| 48 | + | std::string getVendor() { return m_vendor; } | |
| 49 | + | std::string getRenderer() { return m_renderer; } | |
| 50 | + | std::string getVersion() { return m_version; } | |
| 51 | + | std::string getExtensions() { return m_extensions; } | |
| 52 | + | ||
| 53 | + | bool ok() { return m_ok; } | |
| 54 | + | void checkForError(const std::string& function, const std::string& file, int line); | |
| 55 | + | ||
| 56 | + | private: | |
| 57 | + | #ifdef WITH_DEPTH_BUFFER | |
| 58 | + | void checkDepthSupport(); | |
| 59 | + | #endif | |
| 60 | + | ||
| 61 | + | Size m_viewportSize; | |
| 62 | + | std::string m_vendor; | |
| 63 | + | std::string m_renderer; | |
| 64 | + | std::string m_version; | |
| 65 | + | std::string m_extensions; | |
| 66 | + | ||
| 67 | + | int m_maxTextureSize; | |
| 68 | + | int m_alphaBits; | |
| 69 | + | stdext::boolean<false> m_ok; | |
| 70 | + | }; | |
| 71 | + | ||
| 72 | + | extern Graphics g_graphics; | |
| 73 | + | ||
| 74 | + | #endif |
| @@ -0,0 +1,47 @@ | |||
| 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 "hardwarebuffer.h" | |
| 24 | + | #include "graphics.h" | |
| 25 | + | ||
| 26 | + | #include <framework/core/application.h> | |
| 27 | + | #include <framework/core/eventdispatcher.h> | |
| 28 | + | #include <framework/core/logger.h> | |
| 29 | + | ||
| 30 | + | HardwareBuffer::HardwareBuffer(Type type) | |
| 31 | + | { | |
| 32 | + | m_type = type; | |
| 33 | + | m_id = 0; | |
| 34 | + | glGenBuffers(1, &m_id); | |
| 35 | + | if(!m_id) | |
| 36 | + | g_logger.fatal("Unable to create hardware buffer."); | |
| 37 | + | g_graphics.checkForError(__FUNCTION__, __FILE__, __LINE__); | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | HardwareBuffer::~HardwareBuffer() | |
| 41 | + | { | |
| 42 | + | VALIDATE(!g_app.isTerminated()); | |
| 43 | + | uint id = m_id; | |
| 44 | + | g_graphicsDispatcher.addEvent([id] { | |
| 45 | + | glDeleteBuffers(1, &id); | |
| 46 | + | }); | |
| 47 | + | } |
| @@ -0,0 +1,55 @@ | |||
| 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 HARDWAREBUFFER_H | |
| 25 | + | #define HARDWAREBUFFER_H | |
| 26 | + | ||
| 27 | + | #include "declarations.h" | |
| 28 | + | ||
| 29 | + | class HardwareBuffer | |
| 30 | + | { | |
| 31 | + | public: | |
| 32 | + | enum Type { | |
| 33 | + | VertexBuffer = GL_ARRAY_BUFFER, | |
| 34 | + | IndexBuffer = GL_ELEMENT_ARRAY_BUFFER | |
| 35 | + | }; | |
| 36 | + | ||
| 37 | + | enum UsagePattern { | |
| 38 | + | StreamDraw = GL_STREAM_DRAW, | |
| 39 | + | StaticDraw = GL_STATIC_DRAW, | |
| 40 | + | DynamicDraw = GL_DYNAMIC_DRAW | |
| 41 | + | }; | |
| 42 | + | ||
| 43 | + | HardwareBuffer(Type type); | |
| 44 | + | ~HardwareBuffer(); | |
| 45 | + | ||
| 46 | + | void bind() { glBindBuffer(m_type, m_id); } | |
| 47 | + | static void unbind(Type type) { glBindBuffer(type, 0); } | |
| 48 | + | void write(void *data, int count, UsagePattern usage) { glBufferData(m_type, count, data, usage); } | |
| 49 | + | ||
| 50 | + | private: | |
| 51 | + | Type m_type; | |
| 52 | + | uint m_id; | |
| 53 | + | }; | |
| 54 | + | ||
| 55 | + | #endif |
| @@ -0,0 +1,306 @@ | |||
| 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 "image.h" | |
| 25 | + | ||
| 26 | + | #include <framework/core/resourcemanager.h> | |
| 27 | + | #include <framework/core/filestream.h> | |
| 28 | + | #include <framework/graphics/apngloader.h> | |
| 29 | + | #include <framework/util/qrcodegen.h> | |
| 30 | + | ||
| 31 | + | Image::Image(const Size& size, int bpp, uint8 *pixels) | |
| 32 | + | { | |
| 33 | + | m_size = size; | |
| 34 | + | m_bpp = bpp; | |
| 35 | + | m_pixels.resize(size.area() * bpp, 0); | |
| 36 | + | if(pixels) | |
| 37 | + | memcpy(&m_pixels[0], pixels, m_pixels.size()); | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | ImagePtr Image::load(std::string file) | |
| 41 | + | { | |
| 42 | + | ImagePtr image; | |
| 43 | + | try { | |
| 44 | + | file = g_resources.guessFilePath(file, "png"); | |
| 45 | + | ||
| 46 | + | // load image file data | |
| 47 | + | image = loadPNG(file); | |
| 48 | + | } catch(stdext::exception& e) { | |
| 49 | + | g_logger.error(stdext::format("unable to load image '%s': %s", file, e.what())); | |
| 50 | + | } | |
| 51 | + | return image; | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | ImagePtr Image::loadPNG(const std::string& file) | |
| 55 | + | { | |
| 56 | + | std::stringstream fin; | |
| 57 | + | g_resources.readFileStream(file, fin); | |
| 58 | + | ImagePtr image; | |
| 59 | + | apng_data apng; | |
| 60 | + | if(load_apng(fin, &apng) == 0) { | |
| 61 | + | image = ImagePtr(new Image(Size(apng.width, apng.height), apng.bpp, apng.pdata)); | |
| 62 | + | free_apng(&apng); | |
| 63 | + | } | |
| 64 | + | return image; | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | ImagePtr Image::loadPNG(const void* data, uint32_t size) | |
| 68 | + | { | |
| 69 | + | std::string temp((char*)data, size); | |
| 70 | + | std::stringstream fin(temp); | |
| 71 | + | ImagePtr image; | |
| 72 | + | apng_data apng; | |
| 73 | + | if (load_apng(fin, &apng) == 0) { | |
| 74 | + | image = ImagePtr(new Image(Size(apng.width, apng.height), apng.bpp, apng.pdata)); | |
| 75 | + | free_apng(&apng); | |
| 76 | + | } | |
| 77 | + | return image; | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | void Image::savePNG(const std::string& fileName) | |
| 81 | + | { | |
| 82 | + | FileStreamPtr fin = g_resources.createFile(fileName); | |
| 83 | + | if(!fin) | |
| 84 | + | stdext::throw_exception(stdext::format("failed to open file '%s' for write", fileName)); | |
| 85 | + | ||
| 86 | + | std::stringstream data; | |
| 87 | + | save_png(data, m_size.width(), m_size.height(), 4, (unsigned char*)getPixelData()); | |
| 88 | + | fin->write(data.str().c_str(), data.str().length()); | |
| 89 | + | fin->flush(); | |
| 90 | + | fin->close(); | |
| 91 | + | } | |
| 92 | + | ||
| 93 | + | void Image::blit(const Point& dest, const ImagePtr& other) | |
| 94 | + | { | |
| 95 | + | VALIDATE(m_bpp == 4); | |
| 96 | + | ||
| 97 | + | if (!other) | |
| 98 | + | return; | |
| 99 | + | ||
| 100 | + | int width = other->getWidth(), height = other->getHeight(); | |
| 101 | + | uint8* otherPixels = other->getPixelData(); | |
| 102 | + | for (int y = 0, p = 0; y < height; ++y) { | |
| 103 | + | int pos = ((dest.y + y) * m_size.width() + dest.x) * 4; | |
| 104 | + | for (int x = 0; x < width; ++x) { | |
| 105 | + | if (otherPixels[p + 3] != 0) { | |
| 106 | + | *(uint32_t*)&m_pixels[pos] = *(uint32_t*)&otherPixels[p]; | |
| 107 | + | } | |
| 108 | + | pos += 4; | |
| 109 | + | p += 4; | |
| 110 | + | } | |
| 111 | + | } | |
| 112 | + | } | |
| 113 | + | ||
| 114 | + | void Image::paste(const ImagePtr& other) | |
| 115 | + | { | |
| 116 | + | VALIDATE(m_bpp == 4); | |
| 117 | + | ||
| 118 | + | if (!other) | |
| 119 | + | return; | |
| 120 | + | ||
| 121 | + | uint8* otherPixels = other->getPixelData(); | |
| 122 | + | for (int p = 0; p < other->getPixelCount(); ++p) { | |
| 123 | + | int x = p % other->getWidth(); | |
| 124 | + | int y = p / other->getWidth(); | |
| 125 | + | int pos = (y * m_size.width() + x) * 4; | |
| 126 | + | ||
| 127 | + | m_pixels[pos + 0] = otherPixels[p * 4 + 0]; | |
| 128 | + | m_pixels[pos + 1] = otherPixels[p * 4 + 1]; | |
| 129 | + | m_pixels[pos + 2] = otherPixels[p * 4 + 2]; | |
| 130 | + | m_pixels[pos + 3] = otherPixels[p * 4 + 3]; | |
| 131 | + | } | |
| 132 | + | } | |
| 133 | + | ||
| 134 | + | ImagePtr Image::upscale() | |
| 135 | + | { | |
| 136 | + | VALIDATE(m_bpp == 4); | |
| 137 | + | ||
| 138 | + | ImagePtr newImage(new Image(m_size * 2)); | |
| 139 | + | ||
| 140 | + | uint8* otherPixels = newImage->getPixelData(); | |
| 141 | + | ||
| 142 | + | for (int p = 0; p < getPixelCount(); ++p) { | |
| 143 | + | int x = p % getWidth(); | |
| 144 | + | int y = p / getWidth(); | |
| 145 | + | int srcPos = (y * getWidth() + x) * 4; | |
| 146 | + | for (int xx = 0; xx < 2; ++xx) { | |
| 147 | + | for (int yy = 0; yy < 2; ++yy) { | |
| 148 | + | int dstPos = ((y * 2 + yy) * getWidth() * 2 + x * 2 + xx) * 4; | |
| 149 | + | for (int i = 0; i < 4; ++i) { | |
| 150 | + | otherPixels[dstPos + i] = m_pixels[srcPos + i]; | |
| 151 | + | } | |
| 152 | + | } | |
| 153 | + | } | |
| 154 | + | } | |
| 155 | + | ||
| 156 | + | return newImage; | |
| 157 | + | } | |
| 158 | + | ||
| 159 | + | bool Image::nextMipmap() | |
| 160 | + | { | |
| 161 | + | VALIDATE(m_bpp == 4); | |
| 162 | + | VALIDATE(stdext::is_power_of_two(m_size.width()) && stdext::is_power_of_two(m_size.height())); | |
| 163 | + | ||
| 164 | + | int iw = m_size.width(); | |
| 165 | + | int ih = m_size.height(); | |
| 166 | + | if(iw == 1 && ih == 1) | |
| 167 | + | return false; | |
| 168 | + | ||
| 169 | + | int ow = iw > 1 ? iw/2 : 1; | |
| 170 | + | int oh = ih > 1 ? ih/2 : 1; | |
| 171 | + | ||
| 172 | + | std::vector<uint8> pixels(ow*oh*4, 0xFF); | |
| 173 | + | ||
| 174 | + | //FIXME: calculate mipmaps for 8x1, 4x1, 2x1 ... | |
| 175 | + | if(iw != 1 && ih != 1) { | |
| 176 | + | for(int x=0;x<ow;++x) { | |
| 177 | + | for(int y=0;y<oh;++y) { | |
| 178 | + | uint8 *inPixel[4]; | |
| 179 | + | inPixel[0] = &m_pixels[((y*2)*iw + (x*2))*4]; | |
| 180 | + | inPixel[1] = &m_pixels[((y*2)*iw + (x*2)+1)*4]; | |
| 181 | + | inPixel[2] = &m_pixels[((y*2+1)*iw + (x*2))*4]; | |
| 182 | + | inPixel[3] = &m_pixels[((y*2+1)*iw + (x*2)+1)*4]; | |
| 183 | + | uint8 *outPixel = &pixels[(y*ow + x)*4]; | |
| 184 | + | ||
| 185 | + | int pixelsSum[4]; | |
| 186 | + | for(int i=0;i<4;++i) | |
| 187 | + | pixelsSum[i] = 0; | |
| 188 | + | ||
| 189 | + | int usedPixels = 0; | |
| 190 | + | for(int j=0;j<4;++j) { | |
| 191 | + | // ignore colors of complete alpha pixels | |
| 192 | + | if(inPixel[j][3] < 16) | |
| 193 | + | continue; | |
| 194 | + | ||
| 195 | + | for(int i=0;i<4;++i) | |
| 196 | + | pixelsSum[i] += inPixel[j][i]; | |
| 197 | + | ||
| 198 | + | usedPixels++; | |
| 199 | + | } | |
| 200 | + | ||
| 201 | + | // try to guess the alpha pixel more accurately | |
| 202 | + | for(int i=0;i<4;++i) { | |
| 203 | + | if(usedPixels > 0) | |
| 204 | + | outPixel[i] = pixelsSum[i] / usedPixels; | |
| 205 | + | else | |
| 206 | + | outPixel[i] = 0; | |
| 207 | + | } | |
| 208 | + | outPixel[3] = pixelsSum[3]/4; | |
| 209 | + | } | |
| 210 | + | } | |
| 211 | + | } | |
| 212 | + | ||
| 213 | + | m_pixels = pixels; | |
| 214 | + | m_size = Size(ow, oh); | |
| 215 | + | return true; | |
| 216 | + | } | |
| 217 | + | ||
| 218 | + | /* | |
| 219 | + | * | |
| 220 | + | ||
| 221 | + | void Texture::generateSoftwareMipmaps(std::vector<uint8> inPixels) | |
| 222 | + | { | |
| 223 | + | bind(); | |
| 224 | + | ||
| 225 | + | VALIDATE(stdext::is_power_of_two(m_glSize.width()) && stdext::is_power_of_two(m_glSize.height())); | |
| 226 | + | ||
| 227 | + | Size inSize = m_glSize; | |
| 228 | + | Size outSize = inSize / 2; | |
| 229 | + | std::vector<uint8> outPixels; | |
| 230 | + | ||
| 231 | + | int mipmap = 1; | |
| 232 | + | while(true) { | |
| 233 | + | outPixels.resize(outSize.area()*4, 0); | |
| 234 | + | ||
| 235 | + | // this is a simple bilinear filtering algorithm, it combines every 4 pixels in one pixel | |
| 236 | + | for(int x=0;x<outSize.width();++x) { | |
| 237 | + | for(int y=0;y<outSize.height();++y) { | |
| 238 | + | uint8 *inPixel[4]; | |
| 239 | + | inPixel[0] = &inPixels[((y*2)*inSize.width() + (x*2))*4]; | |
| 240 | + | inPixel[1] = &inPixels[((y*2)*inSize.width() + (x*2)+1)*4]; | |
| 241 | + | inPixel[2] = &inPixels[((y*2+1)*inSize.width() + (x*2))*4]; | |
| 242 | + | inPixel[3] = &inPixels[((y*2+1)*inSize.width() + (x*2)+1)*4]; | |
| 243 | + | uint8 *outPixel = &outPixels[(y*outSize.width() + x)*4]; | |
| 244 | + | ||
| 245 | + | int pixelsSum[4]; | |
| 246 | + | for(int i=0;i<4;++i) | |
| 247 | + | pixelsSum[i] = 0; | |
| 248 | + | ||
| 249 | + | int usedPixels = 0; | |
| 250 | + | for(int j=0;j<4;++j) { | |
| 251 | + | // ignore colors of complete alpha pixels | |
| 252 | + | if(inPixel[j][3] < 16) | |
| 253 | + | continue; | |
| 254 | + | ||
| 255 | + | for(int i=0;i<4;++i) | |
| 256 | + | pixelsSum[i] += inPixel[j][i]; | |
| 257 | + | ||
| 258 | + | usedPixels++; | |
| 259 | + | } | |
| 260 | + | ||
| 261 | + | // try to guess the alpha pixel more accurately | |
| 262 | + | for(int i=0;i<4;++i) { | |
| 263 | + | if(usedPixels > 0) | |
| 264 | + | outPixel[i] = pixelsSum[i] / usedPixels; | |
| 265 | + | else | |
| 266 | + | outPixel[i] = 0; | |
| 267 | + | } | |
| 268 | + | outPixel[3] = pixelsSum[3]/4; | |
| 269 | + | } | |
| 270 | + | } | |
| 271 | + | ||
| 272 | + | glTexImage2D(GL_TEXTURE_2D, mipmap++, GL_RGBA, outSize.width(), outSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, &outPixels[0]); | |
| 273 | + | ||
| 274 | + | if(inSize.width() == 1 || inSize.height() == 1) | |
| 275 | + | break; | |
| 276 | + | ||
| 277 | + | inPixels = std::move(outPixels); | |
| 278 | + | inSize /= 2; | |
| 279 | + | outSize /= 2; | |
| 280 | + | } | |
| 281 | + | ||
| 282 | + | if(!m_hasMipmaps) { | |
| 283 | + | m_hasMipmaps = true; | |
| 284 | + | setupFilters(); | |
| 285 | + | } | |
| 286 | + | } | |
| 287 | + | */ | |
| 288 | + | ||
| 289 | + | ImagePtr Image::fromQRCode(const std::string& code, int border) | |
| 290 | + | { | |
| 291 | + | enum qrcodegen_Ecc errCorLvl = qrcodegen_Ecc_HIGH; // Error correction level | |
| 292 | + | ||
| 293 | + | uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; | |
| 294 | + | uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; | |
| 295 | + | bool ok = qrcodegen_encodeText(code.c_str(), tempBuffer, qrcode, errCorLvl, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); | |
| 296 | + | if (!ok) | |
| 297 | + | return nullptr; | |
| 298 | + | int size = qrcodegen_getSize(qrcode); | |
| 299 | + | ImagePtr image(new Image(Size(size + border * 2, size + border * 2))); | |
| 300 | + | for (int x = 0; x < size + border * 2; ++x) { | |
| 301 | + | for (int y = 0; y < size + border * 2; ++y) { | |
| 302 | + | image->setPixel(x, y, qrcodegen_getModule(qrcode, x - border, y - border) ? Color::black : Color::white); | |
| 303 | + | } | |
| 304 | + | } | |
| 305 | + | return image; | |
| 306 | + | } |
| @@ -0,0 +1,72 @@ | |||
| 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 IMAGE_H | |
| 24 | + | #define IMAGE_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | #include <framework/util/databuffer.h> | |
| 28 | + | ||
| 29 | + | class Image : public stdext::shared_object | |
| 30 | + | { | |
| 31 | + | public: | |
| 32 | + | Image(const Size& size, int bpp = 4, uint8 *pixels = nullptr); | |
| 33 | + | ||
| 34 | + | static ImagePtr load(std::string file); | |
| 35 | + | static ImagePtr loadPNG(const std::string& file); | |
| 36 | + | static ImagePtr loadPNG(const void* data, uint32_t size); | |
| 37 | + | ||
| 38 | + | void savePNG(const std::string& fileName); | |
| 39 | + | ||
| 40 | + | void blit(const Point& dest, const ImagePtr& other); | |
| 41 | + | void paste(const ImagePtr& other); | |
| 42 | + | ImagePtr upscale(); | |
| 43 | + | void resize(const Size& size) { m_size = size; m_pixels.resize(size.area() * m_bpp, 0); } | |
| 44 | + | bool nextMipmap(); | |
| 45 | + | ||
| 46 | + | void setPixel(int x, int y, uint8 *pixel) { memcpy(&m_pixels[(y * m_size.width() + x) * m_bpp], pixel, m_bpp);} | |
| 47 | + | void setPixel(int x, int y, uint32_t argb) { setPixel(x, y, (uint8*)&argb); } | |
| 48 | + | void setPixel(int x, int y, const Color& color) { | |
| 49 | + | m_pixels[(y * m_size.width() + x) * m_bpp] = color.r(); | |
| 50 | + | m_pixels[(y * m_size.width() + x) * m_bpp + 1] = color.g(); | |
| 51 | + | m_pixels[(y * m_size.width() + x) * m_bpp + 2] = color.b(); | |
| 52 | + | m_pixels[(y * m_size.width() + x) * m_bpp + 3] = color.a(); | |
| 53 | + | } | |
| 54 | + | ||
| 55 | + | std::vector<uint8>& getPixels() { return m_pixels; } | |
| 56 | + | uint8* getPixelData() { return &m_pixels[0]; } | |
| 57 | + | int getPixelCount() { return m_size.area(); } | |
| 58 | + | const Size& getSize() { return m_size; } | |
| 59 | + | int getWidth() { return m_size.width(); } | |
| 60 | + | int getHeight() { return m_size.height(); } | |
| 61 | + | int getBpp() { return m_bpp; } | |
| 62 | + | uint8* getPixel(int x, int y) { return &m_pixels[(y * m_size.width() + x) * m_bpp]; } | |
| 63 | + | ||
| 64 | + | static ImagePtr fromQRCode(const std::string& code, int border); | |
| 65 | + | ||
| 66 | + | private: | |
| 67 | + | std::vector<uint8> m_pixels; | |
| 68 | + | Size m_size; | |
| 69 | + | int m_bpp; | |
| 70 | + | }; | |
| 71 | + | ||
| 72 | + | #endif |
| @@ -0,0 +1,778 @@ | |||
| 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 <framework/graphics/atlas.h> | |
| 24 | + | #include <framework/graphics/painter.h> | |
| 25 | + | #include <framework/graphics/graphics.h> | |
| 26 | + | #include <framework/graphics/colorarray.h> | |
| 27 | + | #include <framework/graphics/deptharray.h> | |
| 28 | + | #include <framework/platform/platformwindow.h> | |
| 29 | + | ||
| 30 | + | #include <framework/graphics/shaders/shaders.h> | |
| 31 | + | #include <framework/platform/platformwindow.h> | |
| 32 | + | #include <framework/util/extras.h> | |
| 33 | + | ||
| 34 | + | Painter* g_painter = nullptr; | |
| 35 | + | ||
| 36 | + | Painter::Painter() | |
| 37 | + | { | |
| 38 | + | m_glTextureId = 0; | |
| 39 | + | m_oldStateIndex = 0; | |
| 40 | + | m_color = Color::white; | |
| 41 | + | m_compositionMode = CompositionMode_Normal; | |
| 42 | + | m_blendEquation = BlendEquation_Add; | |
| 43 | + | m_shaderProgram = nullptr; | |
| 44 | + | m_texture = nullptr; | |
| 45 | + | m_alphaWriting = false; | |
| 46 | + | #ifdef WITH_DEPTH_BUFFER | |
| 47 | + | m_depth = 0; | |
| 48 | + | m_depthFunc = DepthFunc_None; | |
| 49 | + | #endif | |
| 50 | + | setResolution(g_window.getSize()); | |
| 51 | + | ||
| 52 | + | m_drawProgram = nullptr; | |
| 53 | + | resetState(); | |
| 54 | + | ||
| 55 | + | m_drawTexturedProgram = PainterShaderProgram::create("drawTexturedProgram", glslMainWithTexCoordsVertexShader + glslPositionOnlyVertexShader, | |
| 56 | + | glslMainFragmentShader + glslTextureSrcFragmentShader); | |
| 57 | + | m_drawSolidColorProgram = PainterShaderProgram::create("drawSolidColorProgram", glslMainVertexShader + glslPositionOnlyVertexShader, | |
| 58 | + | glslMainFragmentShader + glslSolidColorFragmentShader); | |
| 59 | + | m_drawSolidColorOnTextureProgram = PainterShaderProgram::create("drawSolidColorOnTextureProgram", glslMainWithTexCoordsVertexShader + glslPositionOnlyVertexShader, | |
| 60 | + | glslMainFragmentShader + glslSolidColorOnTextureFragmentShader); | |
| 61 | + | m_drawOutfitLayersProgram = PainterShaderProgram::create("drawOutfitLayersProgram", glslOutfitVertexShader, glslOutfitFragmentShader, true); | |
| 62 | + | m_drawNewProgram = PainterShaderProgram::create("drawNewProgram", newVertexShader, newFragmentShader); | |
| 63 | + | m_drawTextProgram = PainterShaderProgram::create("drawTextProgram", textVertexShader, textFragmentShader); | |
| 64 | + | m_drawLineProgram = PainterShaderProgram::create("drawLineProgram", lineVertexShader, lineFragmentShader); | |
| 65 | + | ||
| 66 | + | if (!m_drawTexturedProgram || !m_drawSolidColorProgram || !m_drawSolidColorOnTextureProgram || !m_drawOutfitLayersProgram || | |
| 67 | + | !m_drawNewProgram || !m_drawTextProgram || !m_drawLineProgram) { | |
| 68 | + | g_logger.fatal("Can't setup default shaders, check log file for details"); | |
| 69 | + | } | |
| 70 | + | ||
| 71 | + | PainterShaderProgram::release(); | |
| 72 | + | g_graphics.checkForError(__FUNCTION__, __FILE__, __LINE__); | |
| 73 | + | } | |
| 74 | + | ||
| 75 | + | void Painter::bind() | |
| 76 | + | { | |
| 77 | + | refreshState(); | |
| 78 | + | ||
| 79 | + | // vertex and texture coord attributes are always enabled | |
| 80 | + | // to avoid massive enable/disables, thus improving frame rate | |
| 81 | + | PainterShaderProgram::enableAttributeArray(PainterShaderProgram::VERTEX_ATTR); | |
| 82 | + | PainterShaderProgram::enableAttributeArray(PainterShaderProgram::TEXCOORD_ATTR); | |
| 83 | + | } | |
| 84 | + | ||
| 85 | + | void Painter::unbind() | |
| 86 | + | { | |
| 87 | + | PainterShaderProgram::disableAttributeArray(PainterShaderProgram::VERTEX_ATTR); | |
| 88 | + | PainterShaderProgram::disableAttributeArray(PainterShaderProgram::TEXCOORD_ATTR); | |
| 89 | + | PainterShaderProgram::release(); | |
| 90 | + | } | |
| 91 | + | ||
| 92 | + | void Painter::resetState() | |
| 93 | + | { | |
| 94 | + | resetColor(); | |
| 95 | + | resetCompositionMode(); | |
| 96 | + | resetBlendEquation(); | |
| 97 | + | resetClipRect(); | |
| 98 | + | resetShaderProgram(); | |
| 99 | + | resetTexture(); | |
| 100 | + | resetAlphaWriting(); | |
| 101 | + | resetTransformMatrix(); | |
| 102 | + | #ifdef WITH_DEPTH_BUFFER | |
| 103 | + | resetDepth(); | |
| 104 | + | resetDepthFunc(); | |
| 105 | + | #endif | |
| 106 | + | } | |
| 107 | + | ||
| 108 | + | void Painter::refreshState() | |
| 109 | + | { | |
| 110 | + | updateGlViewport(); | |
| 111 | + | updateGlCompositionMode(); | |
| 112 | + | updateGlBlendEquation(); | |
| 113 | + | updateGlClipRect(); | |
| 114 | + | updateGlTexture(); | |
| 115 | + | updateGlAlphaWriting(); | |
| 116 | + | #ifdef WITH_DEPTH_BUFFER | |
| 117 | + | updateDepthFunc(); | |
| 118 | + | #endif | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | void Painter::saveState() | |
| 122 | + | { | |
| 123 | + | VALIDATE(m_oldStateIndex < 10); | |
| 124 | + | m_olderStates[m_oldStateIndex].resolution = m_resolution; | |
| 125 | + | m_olderStates[m_oldStateIndex].transformMatrix = m_transformMatrix; | |
| 126 | + | m_olderStates[m_oldStateIndex].projectionMatrix = m_projectionMatrix; | |
| 127 | + | m_olderStates[m_oldStateIndex].textureMatrix = m_textureMatrix; | |
| 128 | + | m_olderStates[m_oldStateIndex].color = m_color; | |
| 129 | + | m_olderStates[m_oldStateIndex].compositionMode = m_compositionMode; | |
| 130 | + | m_olderStates[m_oldStateIndex].blendEquation = m_blendEquation; | |
| 131 | + | m_olderStates[m_oldStateIndex].clipRect = m_clipRect; | |
| 132 | + | m_olderStates[m_oldStateIndex].shaderProgram = m_shaderProgram; | |
| 133 | + | m_olderStates[m_oldStateIndex].texture = m_texture; | |
| 134 | + | m_olderStates[m_oldStateIndex].alphaWriting = m_alphaWriting; | |
| 135 | + | #ifdef WITH_DEPTH_BUFFER | |
| 136 | + | m_olderStates[m_oldStateIndex].depth = m_depth; | |
| 137 | + | m_olderStates[m_oldStateIndex].depthFunc = m_depthFunc; | |
| 138 | + | #endif | |
| 139 | + | m_oldStateIndex++; | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | void Painter::saveAndResetState() | |
| 143 | + | { | |
| 144 | + | saveState(); | |
| 145 | + | resetState(); | |
| 146 | + | } | |
| 147 | + | ||
| 148 | + | void Painter::restoreSavedState() | |
| 149 | + | { | |
| 150 | + | m_oldStateIndex--; | |
| 151 | + | setResolution(m_olderStates[m_oldStateIndex].resolution); | |
| 152 | + | setTransformMatrix(m_olderStates[m_oldStateIndex].transformMatrix); | |
| 153 | + | setProjectionMatrix(m_olderStates[m_oldStateIndex].projectionMatrix); | |
| 154 | + | setTextureMatrix(m_olderStates[m_oldStateIndex].textureMatrix); | |
| 155 | + | setColor(m_olderStates[m_oldStateIndex].color); | |
| 156 | + | setCompositionMode(m_olderStates[m_oldStateIndex].compositionMode); | |
| 157 | + | setBlendEquation(m_olderStates[m_oldStateIndex].blendEquation); | |
| 158 | + | setClipRect(m_olderStates[m_oldStateIndex].clipRect); | |
| 159 | + | setShaderProgram(m_olderStates[m_oldStateIndex].shaderProgram); | |
| 160 | + | setTexture(m_olderStates[m_oldStateIndex].texture); | |
| 161 | + | setAlphaWriting(m_olderStates[m_oldStateIndex].alphaWriting); | |
| 162 | + | #ifdef WITH_DEPTH_BUFFER | |
| 163 | + | setDepth(m_olderStates[m_oldStateIndex].depth); | |
| 164 | + | setDepthFunc(m_olderStates[m_oldStateIndex].depthFunc); | |
| 165 | + | #endif | |
| 166 | + | } | |
| 167 | + | ||
| 168 | + | void Painter::clear(const Color& color) | |
| 169 | + | { | |
| 170 | + | glClearColor(color.rF(), color.gF(), color.bF(), color.aF()); | |
| 171 | + | #ifdef WITH_DEPTH_BUFFER | |
| 172 | + | #ifdef OPENGL_ES | |
| 173 | + | glClearDepthf(0.99f); | |
| 174 | + | #else | |
| 175 | + | glClearDepth(0.99f); | |
| 176 | + | #endif | |
| 177 | + | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
| 178 | + | #else | |
| 179 | + | glClear(GL_COLOR_BUFFER_BIT); | |
| 180 | + | #endif | |
| 181 | + | } | |
| 182 | + | ||
| 183 | + | void Painter::clearRect(const Color& color, const Rect& rect) | |
| 184 | + | { | |
| 185 | + | Rect oldClipRect = m_clipRect; | |
| 186 | + | setClipRect(rect); | |
| 187 | + | glClearColor(color.rF(), color.gF(), color.bF(), color.aF()); | |
| 188 | + | #ifdef WITH_DEPTH_BUFFER | |
| 189 | + | #ifdef OPENGL_ES | |
| 190 | + | glClearDepthf(0.99f); | |
| 191 | + | #else | |
| 192 | + | glClearDepth(0.99f); | |
| 193 | + | #endif | |
| 194 | + | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
| 195 | + | #else | |
| 196 | + | glClear(GL_COLOR_BUFFER_BIT); | |
| 197 | + | #endif | |
| 198 | + | setClipRect(oldClipRect); | |
| 199 | + | } | |
| 200 | + | ||
| 201 | + | void Painter::setCompositionMode(Painter::CompositionMode compositionMode) | |
| 202 | + | { | |
| 203 | + | if (m_compositionMode == compositionMode) | |
| 204 | + | return; | |
| 205 | + | m_compositionMode = compositionMode; | |
| 206 | + | updateGlCompositionMode(); | |
| 207 | + | } | |
| 208 | + | ||
| 209 | + | void Painter::setBlendEquation(Painter::BlendEquation blendEquation) | |
| 210 | + | { | |
| 211 | + | if (m_blendEquation == blendEquation) | |
| 212 | + | return; | |
| 213 | + | m_blendEquation = blendEquation; | |
| 214 | + | updateGlBlendEquation(); | |
| 215 | + | } | |
| 216 | + | ||
| 217 | + | #ifdef WITH_DEPTH_BUFFER | |
| 218 | + | void Painter::setDepthFunc(DepthFunc func) | |
| 219 | + | { | |
| 220 | + | if (m_depthFunc == func) | |
| 221 | + | return; | |
| 222 | + | m_depthFunc = func; | |
| 223 | + | updateDepthFunc(); | |
| 224 | + | } | |
| 225 | + | #endif | |
| 226 | + | ||
| 227 | + | void Painter::setClipRect(const Rect& clipRect) | |
| 228 | + | { | |
| 229 | + | if (m_clipRect == clipRect) | |
| 230 | + | return; | |
| 231 | + | m_clipRect = clipRect; | |
| 232 | + | updateGlClipRect(); | |
| 233 | + | } | |
| 234 | + | ||
| 235 | + | void Painter::setTexture(const TexturePtr& texture) | |
| 236 | + | { | |
| 237 | + | if (m_texture == texture) | |
| 238 | + | return; | |
| 239 | + | ||
| 240 | + | m_texture = texture; | |
| 241 | + | if (m_texture) | |
| 242 | + | m_texture->update(); | |
| 243 | + | ||
| 244 | + | uint glTextureId; | |
| 245 | + | if (texture) { | |
| 246 | + | setTextureMatrix(texture->getTransformMatrix()); | |
| 247 | + | glTextureId = texture->getId(); | |
| 248 | + | } else | |
| 249 | + | glTextureId = 0; | |
| 250 | + | ||
| 251 | + | if (m_glTextureId != glTextureId) { | |
| 252 | + | m_glTextureId = glTextureId; | |
| 253 | + | updateGlTexture(); | |
| 254 | + | } | |
| 255 | + | } | |
| 256 | + | ||
| 257 | + | void Painter::setAlphaWriting(bool enable) | |
| 258 | + | { | |
| 259 | + | if (m_alphaWriting == enable) | |
| 260 | + | return; | |
| 261 | + | ||
| 262 | + | m_alphaWriting = enable; | |
| 263 | + | updateGlAlphaWriting(); | |
| 264 | + | } | |
| 265 | + | ||
| 266 | + | void Painter::setResolution(const Size& resolution) | |
| 267 | + | { | |
| 268 | + | // The projection matrix converts from Painter's coordinate system to GL's coordinate system | |
| 269 | + | // * GL's viewport is 2x2, Painter's is width x height | |
| 270 | + | // * GL has +y -> -y going from bottom -> top, Painter is the other way round | |
| 271 | + | // * GL has [0,0] in the center, Painter has it in the top-left | |
| 272 | + | // | |
| 273 | + | // This results in the Projection matrix below. | |
| 274 | + | // | |
| 275 | + | // Projection Matrix | |
| 276 | + | // Painter Coord ------------------------------------------------ GL Coord | |
| 277 | + | // ------------- | 2.0 / width | 0.0 | 0.0 | --------------- | |
| 278 | + | // | x y 1 | * | 0.0 | -2.0 / height | 0.0 | = | x' y' 1 | | |
| 279 | + | // ------------- | -1.0 | 1.0 | 1.0 | --------------- | |
| 280 | + | ||
| 281 | + | Matrix3 projectionMatrix = { 2.0f / resolution.width(), 0.0f, 0.0f, | |
| 282 | + | 0.0f, -2.0f / resolution.height(), 0.0f, | |
| 283 | + | -1.0f, 1.0f, 1.0f }; | |
| 284 | + | ||
| 285 | + | m_resolution = resolution; | |
| 286 | + | ||
| 287 | + | setProjectionMatrix(projectionMatrix); | |
| 288 | + | if (g_painter == this) | |
| 289 | + | updateGlViewport(); | |
| 290 | + | } | |
| 291 | + | ||
| 292 | + | void Painter::scale(float x, float y) | |
| 293 | + | { | |
| 294 | + | Matrix3 scaleMatrix = { | |
| 295 | + | x, 0.0f, 0.0f, | |
| 296 | + | 0.0f, y, 0.0f, | |
| 297 | + | 0.0f, 0.0f, 1.0f | |
| 298 | + | }; | |
| 299 | + | ||
| 300 | + | setTransformMatrix(m_transformMatrix * scaleMatrix.transposed()); | |
| 301 | + | } | |
| 302 | + | ||
| 303 | + | void Painter::translate(float x, float y) | |
| 304 | + | { | |
| 305 | + | Matrix3 translateMatrix = { | |
| 306 | + | 1.0f, 0.0f, x, | |
| 307 | + | 0.0f, 1.0f, y, | |
| 308 | + | 0.0f, 0.0f, 1.0f | |
| 309 | + | }; | |
| 310 | + | ||
| 311 | + | setTransformMatrix(m_transformMatrix * translateMatrix.transposed()); | |
| 312 | + | } | |
| 313 | + | ||
| 314 | + | void Painter::rotate(float angle) | |
| 315 | + | { | |
| 316 | + | Matrix3 rotationMatrix = { | |
| 317 | + | std::cos(angle), -std::sin(angle), 0.0f, | |
| 318 | + | std::sin(angle), std::cos(angle), 0.0f, | |
| 319 | + | 0.0f, 0.0f, 1.0f | |
| 320 | + | }; | |
| 321 | + | ||
| 322 | + | setTransformMatrix(m_transformMatrix * rotationMatrix.transposed()); | |
| 323 | + | } | |
| 324 | + | ||
| 325 | + | void Painter::rotate(float x, float y, float angle) | |
| 326 | + | { | |
| 327 | + | translate(-x, -y); | |
| 328 | + | rotate(angle); | |
| 329 | + | translate(x, y); | |
| 330 | + | } | |
| 331 | + | ||
| 332 | + | void Painter::pushTransformMatrix() | |
| 333 | + | { | |
| 334 | + | m_transformMatrixStack.push_back(m_transformMatrix); | |
| 335 | + | VALIDATE(m_transformMatrixStack.size() < 100); | |
| 336 | + | } | |
| 337 | + | ||
| 338 | + | void Painter::popTransformMatrix() | |
| 339 | + | { | |
| 340 | + | VALIDATE(m_transformMatrixStack.size() > 0); | |
| 341 | + | setTransformMatrix(m_transformMatrixStack.back()); | |
| 342 | + | m_transformMatrixStack.pop_back(); | |
| 343 | + | } | |
| 344 | + | ||
| 345 | + | void Painter::updateGlTexture() | |
| 346 | + | { | |
| 347 | + | if (m_glTextureId != 0) | |
| 348 | + | glBindTexture(GL_TEXTURE_2D, m_glTextureId); | |
| 349 | + | } | |
| 350 | + | ||
| 351 | + | void Painter::updateGlCompositionMode() | |
| 352 | + | { | |
| 353 | + | switch (m_compositionMode) { | |
| 354 | + | case CompositionMode_Normal: | |
| 355 | + | glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); | |
| 356 | + | break; | |
| 357 | + | case CompositionMode_Multiply: | |
| 358 | + | glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); | |
| 359 | + | break; | |
| 360 | + | case CompositionMode_Add: | |
| 361 | + | glBlendFunc(GL_ONE, GL_ONE); | |
| 362 | + | break; | |
| 363 | + | case CompositionMode_Replace: | |
| 364 | + | glBlendFunc(GL_ONE, GL_ZERO); | |
| 365 | + | break; | |
| 366 | + | case CompositionMode_DestBlending: | |
| 367 | + | glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA); | |
| 368 | + | break; | |
| 369 | + | case CompositionMode_Light: | |
| 370 | + | glBlendFunc(GL_ZERO, GL_SRC_COLOR); | |
| 371 | + | break; | |
| 372 | + | case CompositionMode_AlphaZeroing: | |
| 373 | + | glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_ONE, GL_ZERO); | |
| 374 | + | break; | |
| 375 | + | case CompositionMode_AlphaRestoring: | |
| 376 | + | glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_ONE, GL_ONE); | |
| 377 | + | break; | |
| 378 | + | case CompositionMode_ZeroAlphaOverrite: | |
| 379 | + | glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA, GL_ONE, GL_ONE); | |
| 380 | + | break; | |
| 381 | + | } | |
| 382 | + | } | |
| 383 | + | ||
| 384 | + | void Painter::updateGlBlendEquation() | |
| 385 | + | { | |
| 386 | + | if (m_blendEquation == BlendEquation_Add) | |
| 387 | + | glBlendEquation(GL_FUNC_ADD); // GL_FUNC_ADD | |
| 388 | + | else if (m_blendEquation == BlendEquation_Max) | |
| 389 | + | glBlendEquation(0x8008); // GL_MAX | |
| 390 | + | else if (m_blendEquation == BlendEquation_Subtract) | |
| 391 | + | glBlendEquation(GL_FUNC_SUBTRACT); // GL_MAX | |
| 392 | + | } | |
| 393 | + | ||
| 394 | + | #ifdef WITH_DEPTH_BUFFER | |
| 395 | + | void Painter::updateDepthFunc() | |
| 396 | + | { | |
| 397 | + | if (m_depthFunc != DepthFunc_None) { | |
| 398 | + | glEnable(GL_DEPTH_TEST); | |
| 399 | + | } | |
| 400 | + | ||
| 401 | + | switch (m_depthFunc) { | |
| 402 | + | case DepthFunc_None: | |
| 403 | + | glDisable(GL_DEPTH_TEST); | |
| 404 | + | break; | |
| 405 | + | case DepthFunc_ALWAYS: | |
| 406 | + | glDepthFunc(GL_ALWAYS); | |
| 407 | + | glDepthMask(GL_TRUE); | |
| 408 | + | break; | |
| 409 | + | case DepthFunc_ALWAYS_READ: | |
| 410 | + | glDepthFunc(GL_ALWAYS); | |
| 411 | + | glDepthMask(GL_FALSE); | |
| 412 | + | break; | |
| 413 | + | case DepthFunc_EQUAL: | |
| 414 | + | glDepthFunc(GL_EQUAL); | |
| 415 | + | glDepthMask(GL_TRUE); | |
| 416 | + | break; | |
| 417 | + | case DepthFunc_LEQUAL: | |
| 418 | + | glDepthFunc(GL_LEQUAL); | |
| 419 | + | glDepthMask(GL_TRUE); | |
| 420 | + | break; | |
| 421 | + | case DepthFunc_LEQUAL_READ: | |
| 422 | + | glDepthFunc(GL_LEQUAL); | |
| 423 | + | glDepthMask(GL_FALSE); | |
| 424 | + | break; | |
| 425 | + | case DepthFunc_LESS: | |
| 426 | + | glDepthFunc(GL_LESS); | |
| 427 | + | glDepthMask(GL_TRUE); | |
| 428 | + | break; | |
| 429 | + | case DepthFunc_LESS_READ: | |
| 430 | + | glDepthFunc(GL_LESS); | |
| 431 | + | glDepthMask(GL_FALSE); | |
| 432 | + | break; | |
| 433 | + | } | |
| 434 | + | } | |
| 435 | + | #endif | |
| 436 | + | ||
| 437 | + | void Painter::updateGlClipRect() | |
| 438 | + | { | |
| 439 | + | if (m_clipRect.isValid()) { | |
| 440 | + | glEnable(GL_SCISSOR_TEST); | |
| 441 | + | glScissor(m_clipRect.left(), m_resolution.height() - m_clipRect.bottom() - 1, m_clipRect.width(), m_clipRect.height()); | |
| 442 | + | } else { | |
| 443 | + | glScissor(0, 0, m_resolution.width(), m_resolution.height()); | |
| 444 | + | glDisable(GL_SCISSOR_TEST); | |
| 445 | + | } | |
| 446 | + | } | |
| 447 | + | ||
| 448 | + | void Painter::updateGlAlphaWriting() | |
| 449 | + | { | |
| 450 | + | if (m_alphaWriting) | |
| 451 | + | glColorMask(1, 1, 1, 1); | |
| 452 | + | else | |
| 453 | + | glColorMask(1, 1, 1, 0); | |
| 454 | + | } | |
| 455 | + | ||
| 456 | + | void Painter::updateGlViewport() | |
| 457 | + | { | |
| 458 | + | glViewport(0, 0, m_resolution.width(), m_resolution.height()); | |
| 459 | + | } | |
| 460 | + | ||
| 461 | + | void Painter::drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode, ColorArray* colorArray, const std::vector<std::pair<int, Color>>* colors) | |
| 462 | + | { | |
| 463 | + | coordsBuffer.cache(); | |
| 464 | + | int vertexCount = coordsBuffer.getVertexCount(); | |
| 465 | + | if (vertexCount == 0) | |
| 466 | + | return; | |
| 467 | + | ||
| 468 | + | bool textured = coordsBuffer.getTextureCoordCount() > 0 && m_texture; | |
| 469 | + | ||
| 470 | + | // skip drawing of empty textures | |
| 471 | + | if (textured && m_texture->isEmpty()) | |
| 472 | + | return; | |
| 473 | + | ||
| 474 | + | // update shader with the current painter state | |
| 475 | + | m_drawProgram->bind(); | |
| 476 | + | m_drawProgram->setTransformMatrix(m_transformMatrix); | |
| 477 | + | m_drawProgram->setProjectionMatrix(m_projectionMatrix); | |
| 478 | + | if (textured) { | |
| 479 | + | m_drawProgram->setTextureMatrix(m_textureMatrix); | |
| 480 | + | m_drawProgram->bindMultiTextures(); | |
| 481 | + | } | |
| 482 | + | ||
| 483 | + | #ifdef WITH_DEPTH_BUFFER | |
| 484 | + | m_drawProgram->setDepth(m_depth); | |
| 485 | + | #endif | |
| 486 | + | if (m_drawProgram != m_drawOutfitLayersProgram) { | |
| 487 | + | m_drawProgram->setColor(m_color); | |
| 488 | + | } else { | |
| 489 | + | m_drawProgram->setMatrixColor(m_matrixColor); | |
| 490 | + | } | |
| 491 | + | m_drawProgram->setResolution(m_resolution); | |
| 492 | + | m_drawProgram->updateTime(); | |
| 493 | + | ||
| 494 | + | // only set texture coords arrays when needed | |
| 495 | + | if (textured) { | |
| 496 | + | HardwareBuffer* hardwareCache = coordsBuffer.getTextureHardwareCache(); | |
| 497 | + | if (hardwareCache) { | |
| 498 | + | hardwareCache->bind(); | |
| 499 | + | m_drawProgram->setAttributeArray(PainterShaderProgram::TEXCOORD_ATTR, nullptr, 2); | |
| 500 | + | HardwareBuffer::unbind(HardwareBuffer::VertexBuffer); | |
| 501 | + | } else { | |
| 502 | + | m_drawProgram->setAttributeArray(PainterShaderProgram::TEXCOORD_ATTR, coordsBuffer.getTextureCoordArray(), 2); | |
| 503 | + | } | |
| 504 | + | } else | |
| 505 | + | PainterShaderProgram::disableAttributeArray(PainterShaderProgram::TEXCOORD_ATTR); | |
| 506 | + | ||
| 507 | + | if (colorArray) { | |
| 508 | + | PainterShaderProgram::enableAttributeArray(PainterShaderProgram::COLOR_ATTR); | |
| 509 | + | m_drawProgram->setAttributeArray(PainterShaderProgram::COLOR_ATTR, colorArray->colors(), 4); | |
| 510 | + | } | |
| 511 | + | ||
| 512 | + | HardwareBuffer* hardwareCache = coordsBuffer.getVertexHardwareCache(); | |
| 513 | + | if (hardwareCache) { | |
| 514 | + | hardwareCache->bind(); | |
| 515 | + | m_drawProgram->setAttributeArray(PainterShaderProgram::VERTEX_ATTR, nullptr, 2); | |
| 516 | + | HardwareBuffer::unbind(HardwareBuffer::VertexBuffer); | |
| 517 | + | } else { | |
| 518 | + | m_drawProgram->setAttributeArray(PainterShaderProgram::VERTEX_ATTR, coordsBuffer.getVertexArray(), 2); | |
| 519 | + | } | |
| 520 | + | ||
| 521 | + | if (drawMode == Triangles) { | |
| 522 | + | if (colors) { | |
| 523 | + | int s = 0; | |
| 524 | + | for (auto& cp : *colors) { | |
| 525 | + | m_drawProgram->setColor(cp.second); | |
| 526 | + | glDrawArrays(GL_TRIANGLES, s * 6, (cp.first - s) * 6); | |
| 527 | + | s = cp.first; | |
| 528 | + | } | |
| 529 | + | } else { | |
| 530 | + | glDrawArrays(GL_TRIANGLES, 0, vertexCount); | |
| 531 | + | } | |
| 532 | + | } else if (drawMode == TriangleStrip) { | |
| 533 | + | glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount); | |
| 534 | + | } | |
| 535 | + | m_draws += vertexCount; | |
| 536 | + | m_calls += 1; | |
| 537 | + | ||
| 538 | + | if (!textured) | |
| 539 | + | PainterShaderProgram::enableAttributeArray(PainterShaderProgram::TEXCOORD_ATTR); | |
| 540 | + | if (colorArray) | |
| 541 | + | PainterShaderProgram::disableAttributeArray(PainterShaderProgram::COLOR_ATTR); | |
| 542 | + | } | |
| 543 | + | ||
| 544 | + | void Painter::drawFillCoords(CoordsBuffer& coordsBuffer) | |
| 545 | + | { | |
| 546 | + | setDrawProgram(m_shaderProgram ? m_shaderProgram : m_drawSolidColorProgram.get()); | |
| 547 | + | setTexture(nullptr); | |
| 548 | + | drawCoords(coordsBuffer); | |
| 549 | + | } | |
| 550 | + | ||
| 551 | + | void Painter::drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture, const std::vector<std::pair<int, Color>>* colors) | |
| 552 | + | { | |
| 553 | + | if (texture && texture->isEmpty()) | |
| 554 | + | return; | |
| 555 | + | ||
| 556 | + | setDrawProgram(m_shaderProgram ? m_shaderProgram : m_drawTexturedProgram.get()); | |
| 557 | + | setTexture(texture); | |
| 558 | + | drawCoords(coordsBuffer, Painter::Triangles, nullptr, colors); | |
| 559 | + | } | |
| 560 | + | ||
| 561 | + | void Painter::drawTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src) | |
| 562 | + | { | |
| 563 | + | if (dest.isEmpty() || src.isEmpty() || texture->isEmpty()) | |
| 564 | + | return; | |
| 565 | + | ||
| 566 | + | setDrawProgram(m_shaderProgram ? m_shaderProgram : m_drawTexturedProgram.get()); | |
| 567 | + | setTexture(texture); | |
| 568 | + | ||
| 569 | + | m_coordsBuffer.clear(); | |
| 570 | + | m_coordsBuffer.addQuad(dest, src); | |
| 571 | + | drawCoords(m_coordsBuffer, TriangleStrip); | |
| 572 | + | } | |
| 573 | + | ||
| 574 | + | void Painter::drawColorOnTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src) | |
| 575 | + | { | |
| 576 | + | if (dest.isEmpty() || src.isEmpty() || texture->isEmpty()) | |
| 577 | + | return; | |
| 578 | + | ||
| 579 | + | setDrawProgram(m_shaderProgram ? m_shaderProgram : m_drawSolidColorOnTextureProgram.get()); | |
| 580 | + | setTexture(texture); | |
| 581 | + | ||
| 582 | + | m_coordsBuffer.clear(); | |
| 583 | + | m_coordsBuffer.addQuad(dest, src); | |
| 584 | + | drawCoords(m_coordsBuffer, TriangleStrip); | |
| 585 | + | } | |
| 586 | + | ||
| 587 | + | void Painter::drawUpsideDownTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src) | |
| 588 | + | { | |
| 589 | + | if (dest.isEmpty() || src.isEmpty() || texture->isEmpty()) | |
| 590 | + | return; | |
| 591 | + | ||
| 592 | + | setDrawProgram(m_shaderProgram ? m_shaderProgram : m_drawTexturedProgram.get()); | |
| 593 | + | setTexture(texture); | |
| 594 | + | ||
| 595 | + | m_coordsBuffer.clear(); | |
| 596 | + | m_coordsBuffer.addUpsideDownQuad(dest, src); | |
| 597 | + | drawCoords(m_coordsBuffer, TriangleStrip); | |
| 598 | + | } | |
| 599 | + | ||
| 600 | + | void Painter::drawRepeatedTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src) | |
| 601 | + | { | |
| 602 | + | if (dest.isEmpty() || src.isEmpty() || texture->isEmpty()) | |
| 603 | + | return; | |
| 604 | + | ||
| 605 | + | setDrawProgram(m_shaderProgram ? m_shaderProgram : m_drawTexturedProgram.get()); | |
| 606 | + | setTexture(texture); | |
| 607 | + | ||
| 608 | + | m_coordsBuffer.clear(); | |
| 609 | + | m_coordsBuffer.addRepeatedRects(dest, src); | |
| 610 | + | drawCoords(m_coordsBuffer); | |
| 611 | + | } | |
| 612 | + | ||
| 613 | + | void Painter::drawFilledRect(const Rect& dest) | |
| 614 | + | { | |
| 615 | + | if (dest.isEmpty()) | |
| 616 | + | return; | |
| 617 | + | ||
| 618 | + | setDrawProgram(m_shaderProgram ? m_shaderProgram : m_drawSolidColorProgram.get()); | |
| 619 | + | ||
| 620 | + | m_coordsBuffer.clear(); | |
| 621 | + | m_coordsBuffer.addRect(dest); | |
| 622 | + | drawCoords(m_coordsBuffer); | |
| 623 | + | } | |
| 624 | + | ||
| 625 | + | // new render | |
| 626 | + | void Painter::drawText(const Point& pos, CoordsBuffer& coordsBuffer, const Color& color, const TexturePtr& texture) | |
| 627 | + | { | |
| 628 | + | setTexture(texture); | |
| 629 | + | // update shader with the current painter state | |
| 630 | + | m_drawTextProgram->bind(); | |
| 631 | + | m_drawTextProgram->setTransformMatrix(m_transformMatrix); | |
| 632 | + | m_drawTextProgram->setProjectionMatrix(m_projectionMatrix); | |
| 633 | + | m_drawTextProgram->setTextureMatrix(m_textureMatrix); | |
| 634 | + | m_drawTextProgram->setOffset(pos); | |
| 635 | + | m_drawTextProgram->setColor(color); | |
| 636 | + | ||
| 637 | + | HardwareBuffer* hardwareCache = coordsBuffer.getVertexHardwareCache(); | |
| 638 | + | if (hardwareCache) { | |
| 639 | + | hardwareCache->bind(); | |
| 640 | + | m_drawTextProgram->setAttributeArray(PainterShaderProgram::VERTEX_ATTR, nullptr, 2); | |
| 641 | + | HardwareBuffer::unbind(HardwareBuffer::VertexBuffer); | |
| 642 | + | } else { | |
| 643 | + | m_drawTextProgram->setAttributeArray(PainterShaderProgram::VERTEX_ATTR, coordsBuffer.getVertexArray(), 2); | |
| 644 | + | } | |
| 645 | + | ||
| 646 | + | HardwareBuffer* texHardwareCache = coordsBuffer.getTextureHardwareCache(); | |
| 647 | + | if (texHardwareCache) { | |
| 648 | + | texHardwareCache->bind(); | |
| 649 | + | m_drawTextProgram->setAttributeArray(PainterShaderProgram::TEXCOORD_ATTR, nullptr, 2); | |
| 650 | + | HardwareBuffer::unbind(HardwareBuffer::VertexBuffer); | |
| 651 | + | } else { | |
| 652 | + | m_drawTextProgram->setAttributeArray(PainterShaderProgram::TEXCOORD_ATTR, coordsBuffer.getTextureCoordArray(), 2); | |
| 653 | + | } | |
| 654 | + | ||
| 655 | + | glDrawArrays(GL_TRIANGLES, 0, coordsBuffer.getVertexCount()); | |
| 656 | + | m_draws += coordsBuffer.getVertexCount(); | |
| 657 | + | m_calls += 1; | |
| 658 | + | } | |
| 659 | + | ||
| 660 | + | void Painter::drawText(const Point& pos, CoordsBuffer& coordsBuffer, const std::vector<std::pair<int, Color>>& colors, const TexturePtr& texture) | |
| 661 | + | { | |
| 662 | + | setTexture(texture); | |
| 663 | + | // update shader with the current painter state | |
| 664 | + | m_drawTextProgram->bind(); | |
| 665 | + | m_drawTextProgram->setTransformMatrix(m_transformMatrix); | |
| 666 | + | m_drawTextProgram->setProjectionMatrix(m_projectionMatrix); | |
| 667 | + | m_drawTextProgram->setTextureMatrix(m_textureMatrix); | |
| 668 | + | m_drawTextProgram->setOffset(pos); | |
| 669 | + | ||
| 670 | + | HardwareBuffer* hardwareCache = coordsBuffer.getVertexHardwareCache(); | |
| 671 | + | if (hardwareCache) { | |
| 672 | + | hardwareCache->bind(); | |
| 673 | + | m_drawTextProgram->setAttributeArray(PainterShaderProgram::VERTEX_ATTR, nullptr, 2); | |
| 674 | + | HardwareBuffer::unbind(HardwareBuffer::VertexBuffer); | |
| 675 | + | } else { | |
| 676 | + | m_drawTextProgram->setAttributeArray(PainterShaderProgram::VERTEX_ATTR, coordsBuffer.getVertexArray(), 2); | |
| 677 | + | } | |
| 678 | + | ||
| 679 | + | HardwareBuffer* texHardwareCache = coordsBuffer.getTextureHardwareCache(); | |
| 680 | + | if (texHardwareCache) { | |
| 681 | + | texHardwareCache->bind(); | |
| 682 | + | m_drawTextProgram->setAttributeArray(PainterShaderProgram::TEXCOORD_ATTR, nullptr, 2); | |
| 683 | + | HardwareBuffer::unbind(HardwareBuffer::VertexBuffer); | |
| 684 | + | } else { | |
| 685 | + | m_drawTextProgram->setAttributeArray(PainterShaderProgram::TEXCOORD_ATTR, coordsBuffer.getTextureCoordArray(), 2); | |
| 686 | + | } | |
| 687 | + | ||
| 688 | + | int s = 0; | |
| 689 | + | for (auto& cp : colors) { | |
| 690 | + | m_drawTextProgram->setColor(cp.second); | |
| 691 | + | glDrawArrays(GL_TRIANGLES, s * 6, (cp.first - s) * 6); | |
| 692 | + | s = cp.first; | |
| 693 | + | } | |
| 694 | + | m_draws += coordsBuffer.getVertexCount(); | |
| 695 | + | m_calls += colors.size(); | |
| 696 | + | } | |
| 697 | + | ||
| 698 | + | void Painter::drawLine(const std::vector<float>& vertex, int size, int width) | |
| 699 | + | { | |
| 700 | + | m_drawLineProgram->bind(); | |
| 701 | + | m_drawLineProgram->setTransformMatrix(m_transformMatrix); | |
| 702 | + | m_drawLineProgram->setProjectionMatrix(m_projectionMatrix); | |
| 703 | + | m_drawLineProgram->setTextureMatrix(m_textureMatrix); | |
| 704 | + | m_drawLineProgram->setColor(m_color); | |
| 705 | + | glLineWidth(width); | |
| 706 | + | ||
| 707 | + | PainterShaderProgram::disableAttributeArray(PainterShaderProgram::TEXCOORD_ATTR); | |
| 708 | + | m_drawTextProgram->setAttributeArray(PainterShaderProgram::VERTEX_ATTR, vertex.data(), 2); | |
| 709 | + | ||
| 710 | + | glDrawArrays(GL_LINE_STRIP, 0, size); | |
| 711 | + | ||
| 712 | + | PainterShaderProgram::enableAttributeArray(PainterShaderProgram::TEXCOORD_ATTR); | |
| 713 | + | ||
| 714 | + | m_draws += size; | |
| 715 | + | m_calls += 1; | |
| 716 | + | } | |
| 717 | + | ||
| 718 | + | void Painter::setAtlasTextures(const TexturePtr& atlas) | |
| 719 | + | { | |
| 720 | + | static uint activeTexture = 0; | |
| 721 | + | if (activeTexture == atlas->getId()) | |
| 722 | + | return; | |
| 723 | + | activeTexture = atlas ? atlas->getId() : 0; | |
| 724 | + | glActiveTexture(GL_TEXTURE6); // atlas | |
| 725 | + | glBindTexture(GL_TEXTURE_2D, activeTexture); | |
| 726 | + | if (atlas) | |
| 727 | + | m_atlasTextureMatrix = atlas->getTransformMatrix(); | |
| 728 | + | glActiveTexture(GL_TEXTURE0); | |
| 729 | + | } | |
| 730 | + | ||
| 731 | + | void Painter::setSecondTexture(const TexturePtr& texture) | |
| 732 | + | { | |
| 733 | + | static uint activeTexture = 0; | |
| 734 | + | if (texture) | |
| 735 | + | texture->update(); | |
| 736 | + | if (activeTexture == texture->getId()) | |
| 737 | + | return; | |
| 738 | + | activeTexture = texture ? texture->getId() : 0; | |
| 739 | + | glActiveTexture(GL_TEXTURE1); // u_Tex1 | |
| 740 | + | glBindTexture(GL_TEXTURE_2D, activeTexture); | |
| 741 | + | glActiveTexture(GL_TEXTURE0); | |
| 742 | + | } | |
| 743 | + | ||
| 744 | + | void Painter::setOffset(const Point& offset) | |
| 745 | + | { | |
| 746 | + | if (!m_shaderProgram) return; | |
| 747 | + | m_shaderProgram->setOffset(offset); | |
| 748 | + | } | |
| 749 | + | ||
| 750 | + | void Painter::drawCache(const std::vector<float>& vertex, const std::vector<float>& texture, const std::vector<float>& color, int size) | |
| 751 | + | { | |
| 752 | + | setTexture(g_atlas.get(0)); // todo: remove it | |
| 753 | + | // update shader with the current painter state | |
| 754 | + | m_drawNewProgram->bind(); | |
| 755 | + | m_drawNewProgram->setTransformMatrix(m_transformMatrix); | |
| 756 | + | m_drawNewProgram->setProjectionMatrix(m_projectionMatrix); | |
| 757 | + | m_drawNewProgram->setTextureMatrix(m_textureMatrix); | |
| 758 | + | ||
| 759 | + | PainterShaderProgram::enableAttributeArray(PainterShaderProgram::COLOR_ATTR); | |
| 760 | + | ||
| 761 | + | m_drawNewProgram->setAttributeArray(PainterShaderProgram::VERTEX_ATTR, vertex.data(), 2); | |
| 762 | + | m_drawNewProgram->setAttributeArray(PainterShaderProgram::TEXCOORD_ATTR, texture.data(), 2); | |
| 763 | + | m_drawNewProgram->setAttributeArray(PainterShaderProgram::COLOR_ATTR, color.data(), 4); | |
| 764 | + | ||
| 765 | + | glDrawArrays(GL_TRIANGLES, 0, size); | |
| 766 | + | m_draws += size; | |
| 767 | + | m_calls += 1; | |
| 768 | + | ||
| 769 | + | PainterShaderProgram::disableAttributeArray(PainterShaderProgram::COLOR_ATTR); | |
| 770 | + | ||
| 771 | + | #ifdef FREE_VERSION_LIB | |
| 772 | + | // crash after 1h | |
| 773 | + | if (stdext::millis() > 4000'000) { | |
| 774 | + | m_drawNewProgram = nullptr; | |
| 775 | + | } | |
| 776 | + | #endif | |
| 777 | + | } | |
| 778 | + |
| @@ -0,0 +1,259 @@ | |||
| 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 PAINTER_H | |
| 24 | + | #define PAINTER_H | |
| 25 | + | ||
| 26 | + | #include <framework/graphics/declarations.h> | |
| 27 | + | #include <framework/graphics/coordsbuffer.h> | |
| 28 | + | #include <framework/graphics/paintershaderprogram.h> | |
| 29 | + | #include <framework/graphics/texture.h> | |
| 30 | + | #include <framework/graphics/colorarray.h> | |
| 31 | + | #include <framework/graphics/drawqueue.h> | |
| 32 | + | ||
| 33 | + | class Painter { | |
| 34 | + | public: | |
| 35 | + | enum BlendEquation { | |
| 36 | + | BlendEquation_Add, | |
| 37 | + | BlendEquation_Max, | |
| 38 | + | BlendEquation_Subtract | |
| 39 | + | }; | |
| 40 | + | enum CompositionMode { | |
| 41 | + | CompositionMode_Normal, | |
| 42 | + | CompositionMode_Multiply, | |
| 43 | + | CompositionMode_Add, | |
| 44 | + | CompositionMode_Replace, | |
| 45 | + | CompositionMode_DestBlending, | |
| 46 | + | CompositionMode_Light, | |
| 47 | + | CompositionMode_AlphaZeroing, | |
| 48 | + | CompositionMode_AlphaRestoring, | |
| 49 | + | CompositionMode_ZeroAlphaOverrite | |
| 50 | + | ||
| 51 | + | }; | |
| 52 | + | enum DepthFunc { | |
| 53 | + | DepthFunc_None, | |
| 54 | + | DepthFunc_LESS, | |
| 55 | + | DepthFunc_LESS_READ, | |
| 56 | + | DepthFunc_LEQUAL, | |
| 57 | + | DepthFunc_LEQUAL_READ, | |
| 58 | + | DepthFunc_EQUAL, | |
| 59 | + | DepthFunc_ALWAYS, | |
| 60 | + | DepthFunc_ALWAYS_READ | |
| 61 | + | }; | |
| 62 | + | enum DrawMode { | |
| 63 | + | Triangles = GL_TRIANGLES, | |
| 64 | + | TriangleStrip = GL_TRIANGLE_STRIP | |
| 65 | + | }; | |
| 66 | + | ||
| 67 | + | struct PainterState { | |
| 68 | + | Size resolution; | |
| 69 | + | Matrix3 transformMatrix; | |
| 70 | + | Matrix3 projectionMatrix; | |
| 71 | + | Matrix3 textureMatrix; | |
| 72 | + | Color color; | |
| 73 | + | Painter::CompositionMode compositionMode; | |
| 74 | + | Painter::BlendEquation blendEquation; | |
| 75 | + | Rect clipRect; | |
| 76 | + | TexturePtr texture; | |
| 77 | + | PainterShaderProgram* shaderProgram; | |
| 78 | + | bool alphaWriting; | |
| 79 | + | #ifdef WITH_DEPTH_BUFFER | |
| 80 | + | Painter::DepthFunc depthFunc; | |
| 81 | + | float depth; | |
| 82 | + | #endif | |
| 83 | + | }; | |
| 84 | + | ||
| 85 | + | Painter(); | |
| 86 | + | ~Painter() {} | |
| 87 | + | ||
| 88 | + | void bind(); | |
| 89 | + | void unbind(); | |
| 90 | + | ||
| 91 | + | void resetState(); | |
| 92 | + | void refreshState(); | |
| 93 | + | void saveState(); | |
| 94 | + | void saveAndResetState(); | |
| 95 | + | void restoreSavedState(); | |
| 96 | + | ||
| 97 | + | void clear(const Color& color); | |
| 98 | + | void clearRect(const Color& color, const Rect& rect); | |
| 99 | + | ||
| 100 | + | void setTransformMatrix(const Matrix3& transformMatrix) { m_transformMatrix = transformMatrix; } | |
| 101 | + | void setProjectionMatrix(const Matrix3& projectionMatrix) { m_projectionMatrix = projectionMatrix; } | |
| 102 | + | void setTextureMatrix(const Matrix3& textureMatrix) { m_textureMatrix = textureMatrix; } | |
| 103 | + | void setCompositionMode(CompositionMode compositionMode); | |
| 104 | + | void setBlendEquation(BlendEquation blendEquation); | |
| 105 | + | #ifdef WITH_DEPTH_BUFFER | |
| 106 | + | void setDepthFunc(DepthFunc func); | |
| 107 | + | #endif | |
| 108 | + | void setClipRect(const Rect& clipRect); | |
| 109 | + | void setShaderProgram(PainterShaderProgram* shaderProgram) { m_shaderProgram = shaderProgram; } | |
| 110 | + | void setTexture(const TexturePtr& texture); | |
| 111 | + | void setAlphaWriting(bool enable); | |
| 112 | + | ||
| 113 | + | void setResolution(const Size& resolution); | |
| 114 | + | ||
| 115 | + | void scale(float x, float y); | |
| 116 | + | void translate(float x, float y); | |
| 117 | + | void rotate(float angle); | |
| 118 | + | void rotate(float x, float y, float angle); | |
| 119 | + | ||
| 120 | + | void pushTransformMatrix(); | |
| 121 | + | void popTransformMatrix(); | |
| 122 | + | ||
| 123 | + | Matrix3 getTransformMatrix() { return m_transformMatrix; } | |
| 124 | + | Matrix3 getProjectionMatrix() { return m_projectionMatrix; } | |
| 125 | + | Matrix3 getTextureMatrix() { return m_textureMatrix; } | |
| 126 | + | BlendEquation getBlendEquation() { return m_blendEquation; } | |
| 127 | + | PainterShaderProgram* getShaderProgram() { return m_shaderProgram; } | |
| 128 | + | bool getAlphaWriting() { return m_alphaWriting; } | |
| 129 | + | ||
| 130 | + | void resetBlendEquation() { setBlendEquation(BlendEquation_Add); } | |
| 131 | + | void resetTexture() { setTexture(nullptr); } | |
| 132 | + | void resetAlphaWriting() { setAlphaWriting(true); } | |
| 133 | + | void resetTransformMatrix() { setTransformMatrix(Matrix3()); } | |
| 134 | + | ||
| 135 | + | /* org */ | |
| 136 | + | void drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode = Triangles, ColorArray* colorBuffer = nullptr, const std::vector<std::pair<int, Color>>* colors = nullptr); | |
| 137 | + | void drawFillCoords(CoordsBuffer& coordsBuffer); | |
| 138 | + | void drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture, const std::vector<std::pair<int, Color>>* colors = nullptr); | |
| 139 | + | void drawTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src); | |
| 140 | + | inline void drawTexturedRect(const Rect& dest, const TexturePtr& texture) { drawTexturedRect(dest, texture, Rect(Point(0, 0), texture->getSize())); } | |
| 141 | + | void drawColorOnTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src); | |
| 142 | + | void drawUpsideDownTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src); | |
| 143 | + | void drawRepeatedTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src); | |
| 144 | + | void drawFilledRect(const Rect& dest); | |
| 145 | + | ||
| 146 | + | void setDrawProgram(PainterShaderProgram* drawProgram) { m_drawProgram = drawProgram; } | |
| 147 | + | bool hasShaders() { return true; } | |
| 148 | + | ||
| 149 | + | void drawText(const Point& pos, CoordsBuffer& coordsBuffer, const Color& color, const TexturePtr& texture); | |
| 150 | + | void drawText(const Point& pos, CoordsBuffer& coordsBuffer, const std::vector<std::pair<int, Color>>& colors, const TexturePtr& texture); | |
| 151 | + | ||
| 152 | + | void drawLine(const std::vector<float>& vertex, int size, int width = 1); | |
| 153 | + | ||
| 154 | + | void setSecondTexture(const TexturePtr& texture); | |
| 155 | + | void setOffset(const Point& offset); | |
| 156 | + | ||
| 157 | + | void setAtlasTextures(const TexturePtr& atlas); | |
| 158 | + | void drawCache(const std::vector<float>& vertex, const std::vector<float>& texture, const std::vector<float>& color, int size); | |
| 159 | + | ||
| 160 | + | void setColor(const Color& color) { m_color = color; } | |
| 161 | + | void setShaderProgram(const PainterShaderProgramPtr& shaderProgram) { setShaderProgram(shaderProgram.get()); } | |
| 162 | + | ||
| 163 | + | void scale(float factor) { scale(factor, factor); } | |
| 164 | + | void translate(const Point& p) { translate(p.x, p.y); } | |
| 165 | + | void rotate(const Point& p, float angle) { rotate(p.x, p.y, angle); } | |
| 166 | + | ||
| 167 | + | #ifdef WITH_DEPTH_BUFFER | |
| 168 | + | void setDepth(float depth) { m_depth = depth; } | |
| 169 | + | float getDepth() { return m_depth; } | |
| 170 | + | DepthFunc getDepthFunc() { return m_depthFunc; } | |
| 171 | + | void resetDepth() { return setDepth(0.0f); } | |
| 172 | + | void resetDepthFunc() { setDepthFunc(DepthFunc_None); } | |
| 173 | + | #endif | |
| 174 | + | ||
| 175 | + | Size getResolution() { return m_resolution; } | |
| 176 | + | Color getColor() { return m_color; } | |
| 177 | + | Rect getClipRect() { return m_clipRect; } | |
| 178 | + | CompositionMode getCompositionMode() { return m_compositionMode; } | |
| 179 | + | ||
| 180 | + | void resetClipRect() { setClipRect(Rect()); } | |
| 181 | + | void resetCompositionMode() { setCompositionMode(CompositionMode_Normal); } | |
| 182 | + | void resetColor() { setColor(Color::white); } | |
| 183 | + | void resetShaderProgram() { setShaderProgram(nullptr); } | |
| 184 | + | ||
| 185 | + | int draws() { return m_draws; } | |
| 186 | + | int calls() { return m_calls; } | |
| 187 | + | void resetDraws() { m_draws = m_calls = 0; } | |
| 188 | + | ||
| 189 | + | void setDrawColorOnTextureShaderProgram() | |
| 190 | + | { | |
| 191 | + | setShaderProgram(m_drawSolidColorOnTextureProgram); | |
| 192 | + | } | |
| 193 | + | ||
| 194 | + | void setMatrixColor(const Matrix4& mat4) | |
| 195 | + | { | |
| 196 | + | m_matrixColor = mat4; | |
| 197 | + | } | |
| 198 | + | void setDrawOutfitLayersProgram() | |
| 199 | + | { | |
| 200 | + | setShaderProgram(m_drawOutfitLayersProgram); | |
| 201 | + | } | |
| 202 | + | ||
| 203 | + | protected: | |
| 204 | + | void updateGlTexture(); | |
| 205 | + | void updateGlCompositionMode(); | |
| 206 | + | void updateGlBlendEquation(); | |
| 207 | + | void updateGlClipRect(); | |
| 208 | + | void updateGlAlphaWriting(); | |
| 209 | + | void updateGlViewport(); | |
| 210 | + | #ifdef WITH_DEPTH_BUFFER | |
| 211 | + | void updateDepthFunc(); | |
| 212 | + | #endif | |
| 213 | + | ||
| 214 | + | CoordsBuffer m_coordsBuffer; | |
| 215 | + | ||
| 216 | + | std::vector<Matrix3> m_transformMatrixStack; | |
| 217 | + | Matrix3 m_transformMatrix; | |
| 218 | + | Matrix3 m_projectionMatrix; | |
| 219 | + | Matrix3 m_textureMatrix; | |
| 220 | + | Matrix3 m_atlasTextureMatrix; | |
| 221 | + | ||
| 222 | + | BlendEquation m_blendEquation; | |
| 223 | + | TexturePtr m_texture; | |
| 224 | + | bool m_alphaWriting; | |
| 225 | + | ||
| 226 | + | PainterState m_olderStates[10]; | |
| 227 | + | int m_oldStateIndex; | |
| 228 | + | ||
| 229 | + | uint m_glTextureId; | |
| 230 | + | ||
| 231 | + | PainterShaderProgram* m_shaderProgram; | |
| 232 | + | CompositionMode m_compositionMode; | |
| 233 | + | Color m_color; | |
| 234 | + | Matrix4 m_matrixColor; | |
| 235 | + | Size m_resolution; | |
| 236 | + | Rect m_clipRect; | |
| 237 | + | #ifdef WITH_DEPTH_BUFFER | |
| 238 | + | DepthFunc m_depthFunc; | |
| 239 | + | float m_depth; | |
| 240 | + | #endif | |
| 241 | + | int m_draws = 0; | |
| 242 | + | int m_calls = 0; | |
| 243 | + | ||
| 244 | + | private: | |
| 245 | + | PainterShaderProgram* m_drawProgram; | |
| 246 | + | PainterShaderProgramPtr m_drawTexturedProgram; | |
| 247 | + | PainterShaderProgramPtr m_drawSolidColorProgram; | |
| 248 | + | PainterShaderProgramPtr m_drawSolidColorOnTextureProgram; | |
| 249 | + | PainterShaderProgramPtr m_drawOutfitLayersProgram; | |
| 250 | + | ||
| 251 | + | PainterShaderProgramPtr m_drawNewProgram; | |
| 252 | + | ||
| 253 | + | PainterShaderProgramPtr m_drawTextProgram; | |
| 254 | + | PainterShaderProgramPtr m_drawLineProgram; | |
| 255 | + | }; | |
| 256 | + | ||
| 257 | + | extern Painter* g_painter; | |
| 258 | + | ||
| 259 | + | #endif |
| @@ -0,0 +1,226 @@ | |||
| 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 "paintershaderprogram.h" | |
| 24 | + | #include "graphics.h" | |
| 25 | + | #include "painter.h" | |
| 26 | + | #include "texture.h" | |
| 27 | + | #include "texturemanager.h" | |
| 28 | + | #include <framework/core/clock.h> | |
| 29 | + | #include <framework/platform/platformwindow.h> | |
| 30 | + | ||
| 31 | + | PainterShaderProgram::PainterShaderProgram(const std::string &name) | |
| 32 | + | : ShaderProgram(name) { | |
| 33 | + | m_startTime = g_clock.seconds(); | |
| 34 | + | m_depth = 0; | |
| 35 | + | m_color = Color::white; | |
| 36 | + | m_time = 0; | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | void PainterShaderProgram::setupUniforms() { | |
| 40 | + | bindUniformLocation(TRANSFORM_MATRIX_UNIFORM, "u_TransformMatrix"); | |
| 41 | + | bindUniformLocation(PROJECTION_MATRIX_UNIFORM, "u_ProjectionMatrix"); | |
| 42 | + | bindUniformLocation(TEXTURE_MATRIX_UNIFORM, "u_TextureMatrix"); | |
| 43 | + | ||
| 44 | + | bindUniformLocation(COLOR_UNIFORM, "u_Color"); | |
| 45 | + | bindUniformLocation(DEPTH_UNIFORM, "u_Depth"); | |
| 46 | + | bindUniformLocation(TIME_UNIFORM, "u_Time"); | |
| 47 | + | ||
| 48 | + | bindUniformLocation(TEX0_UNIFORM, "u_Tex0"); | |
| 49 | + | bindUniformLocation(TEX1_UNIFORM, "u_Tex1"); | |
| 50 | + | bindUniformLocation(TEX2_UNIFORM, "u_Tex2"); | |
| 51 | + | bindUniformLocation(TEX3_UNIFORM, "u_Tex3"); | |
| 52 | + | ||
| 53 | + | bindUniformLocation(ATLAS_TEX0_UNIFORM, "u_Atlas"); | |
| 54 | + | bindUniformLocation(ATLAS_TEX1_UNIFORM, "u_Fonts"); | |
| 55 | + | ||
| 56 | + | bindUniformLocation(RESOLUTION_UNIFORM, "u_Resolution"); | |
| 57 | + | bindUniformLocation(OFFSET_UNIFORM, "u_Offset"); | |
| 58 | + | bindUniformLocation(CENTER_UNIFORM, "u_Center"); | |
| 59 | + | ||
| 60 | + | // VALUES | |
| 61 | + | setUniformValue(TRANSFORM_MATRIX_UNIFORM, m_transformMatrix); | |
| 62 | + | setUniformValue(PROJECTION_MATRIX_UNIFORM, m_projectionMatrix); | |
| 63 | + | setUniformValue(TEXTURE_MATRIX_UNIFORM, m_textureMatrix); | |
| 64 | + | ||
| 65 | + | if (!m_useColorMatrix) { | |
| 66 | + | setUniformValue(COLOR_UNIFORM, m_color); | |
| 67 | + | } | |
| 68 | + | setUniformValue(TIME_UNIFORM, m_time); | |
| 69 | + | setUniformValue(DEPTH_UNIFORM, m_depth); | |
| 70 | + | ||
| 71 | + | setUniformValue(TEX0_UNIFORM, 0); | |
| 72 | + | setUniformValue(TEX1_UNIFORM, 1); | |
| 73 | + | setUniformValue(TEX2_UNIFORM, 2); | |
| 74 | + | setUniformValue(TEX3_UNIFORM, 3); | |
| 75 | + | ||
| 76 | + | setUniformValue(ATLAS_TEX0_UNIFORM, 6); | |
| 77 | + | setUniformValue(ATLAS_TEX1_UNIFORM, 7); | |
| 78 | + | ||
| 79 | + | setUniformValue(RESOLUTION_UNIFORM, (float)m_resolution.width(), | |
| 80 | + | (float)m_resolution.height()); | |
| 81 | + | setUniformValue(OFFSET_UNIFORM, (float)m_offset.x, (float)m_offset.y); | |
| 82 | + | setUniformValue(CENTER_UNIFORM, (float)m_center.x, (float)m_center.y); | |
| 83 | + | } | |
| 84 | + | ||
| 85 | + | bool PainterShaderProgram::link() { | |
| 86 | + | m_startTime = g_clock.seconds(); | |
| 87 | + | bindAttributeLocation(VERTEX_ATTR, "a_Vertex"); | |
| 88 | + | bindAttributeLocation(TEXCOORD_ATTR, "a_TexCoord"); | |
| 89 | + | bindAttributeLocation(DEPTH_ATTR, "a_Depth"); | |
| 90 | + | bindAttributeLocation(COLOR_ATTR, "a_Color"); | |
| 91 | + | bindAttributeLocation(DEPTH_TEXCOORD_ATTR, "a_DepthTexCoord"); | |
| 92 | + | if (!ShaderProgram::link()) | |
| 93 | + | return false; | |
| 94 | + | bind(); | |
| 95 | + | setupUniforms(); | |
| 96 | + | release(); | |
| 97 | + | g_graphics.checkForError(stdext::format("%s (%s)", __FUNCTION__, getName()), | |
| 98 | + | __FILE__, __LINE__); | |
| 99 | + | return true; | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | void PainterShaderProgram::setTransformMatrix(const Matrix3 &transformMatrix) { | |
| 103 | + | if (transformMatrix == m_transformMatrix) | |
| 104 | + | return; | |
| 105 | + | ||
| 106 | + | bind(); | |
| 107 | + | setUniformValue(TRANSFORM_MATRIX_UNIFORM, transformMatrix); | |
| 108 | + | m_transformMatrix = transformMatrix; | |
| 109 | + | } | |
| 110 | + | ||
| 111 | + | void PainterShaderProgram::setProjectionMatrix( | |
| 112 | + | const Matrix3 &projectionMatrix) { | |
| 113 | + | if (projectionMatrix == m_projectionMatrix) | |
| 114 | + | return; | |
| 115 | + | ||
| 116 | + | bind(); | |
| 117 | + | setUniformValue(PROJECTION_MATRIX_UNIFORM, projectionMatrix); | |
| 118 | + | m_projectionMatrix = projectionMatrix; | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | void PainterShaderProgram::setTextureMatrix(const Matrix3 &textureMatrix) { | |
| 122 | + | if (textureMatrix == m_textureMatrix) | |
| 123 | + | return; | |
| 124 | + | ||
| 125 | + | bind(); | |
| 126 | + | setUniformValue(TEXTURE_MATRIX_UNIFORM, textureMatrix); | |
| 127 | + | m_textureMatrix = textureMatrix; | |
| 128 | + | } | |
| 129 | + | ||
| 130 | + | void PainterShaderProgram::setColor(const Color &color) { | |
| 131 | + | if (color == m_color || m_useColorMatrix) | |
| 132 | + | return; | |
| 133 | + | ||
| 134 | + | bind(); | |
| 135 | + | setUniformValue(COLOR_UNIFORM, color); | |
| 136 | + | m_color = color; | |
| 137 | + | } | |
| 138 | + | ||
| 139 | + | void PainterShaderProgram::setMatrixColor(const Matrix4 &colors) { | |
| 140 | + | bind(); | |
| 141 | + | setUniformValue(COLOR_UNIFORM, colors); | |
| 142 | + | } | |
| 143 | + | ||
| 144 | + | #ifdef WITH_DEPTH_BUFFER | |
| 145 | + | void PainterShaderProgram::setDepth(float depth) { | |
| 146 | + | if (depth < 0.) | |
| 147 | + | depth = 0.; | |
| 148 | + | ||
| 149 | + | if (m_depth == depth) | |
| 150 | + | return; | |
| 151 | + | ||
| 152 | + | bind(); | |
| 153 | + | setUniformValue(DEPTH_UNIFORM, depth); | |
| 154 | + | m_depth = depth; | |
| 155 | + | } | |
| 156 | + | #endif | |
| 157 | + | ||
| 158 | + | void PainterShaderProgram::setResolution(const Size &resolution) { | |
| 159 | + | if (m_resolution == resolution) | |
| 160 | + | return; | |
| 161 | + | ||
| 162 | + | bind(); | |
| 163 | + | setUniformValue(RESOLUTION_UNIFORM, (float)resolution.width(), | |
| 164 | + | (float)resolution.height()); | |
| 165 | + | m_resolution = resolution; | |
| 166 | + | } | |
| 167 | + | ||
| 168 | + | void PainterShaderProgram::setOffset(const Point &offset) { | |
| 169 | + | if (m_offset == offset) | |
| 170 | + | return; | |
| 171 | + | ||
| 172 | + | bind(); | |
| 173 | + | m_offset = offset; | |
| 174 | + | setUniformValue(OFFSET_UNIFORM, (float)m_offset.x, (float)m_offset.y); | |
| 175 | + | } | |
| 176 | + | ||
| 177 | + | void PainterShaderProgram::setCenter(const Point ¢er) { | |
| 178 | + | if (m_center == center) | |
| 179 | + | return; | |
| 180 | + | ||
| 181 | + | bind(); | |
| 182 | + | m_center = center; | |
| 183 | + | setUniformValue(CENTER_UNIFORM, (float)m_center.x, (float)m_center.y); | |
| 184 | + | } | |
| 185 | + | ||
| 186 | + | void PainterShaderProgram::updateTime() { | |
| 187 | + | float time = g_clock.seconds() - m_startTime; | |
| 188 | + | if (m_time == time) | |
| 189 | + | return; | |
| 190 | + | ||
| 191 | + | bind(); | |
| 192 | + | setUniformValue(TIME_UNIFORM, time); | |
| 193 | + | m_time = time; | |
| 194 | + | } | |
| 195 | + | ||
| 196 | + | void PainterShaderProgram::addMultiTexture(const std::string &file) { | |
| 197 | + | if (m_multiTextures.size() > 3) | |
| 198 | + | g_logger.error(stdext::format( | |
| 199 | + | "cannot add more multi textures to shader, the max is 3 - %s", | |
| 200 | + | getName())); | |
| 201 | + | ||
| 202 | + | TexturePtr texture = g_textures.getTexture(file); | |
| 203 | + | if (!texture) | |
| 204 | + | return; | |
| 205 | + | ||
| 206 | + | texture->setSmooth(true); | |
| 207 | + | texture->setRepeat(true); | |
| 208 | + | ||
| 209 | + | m_multiTextures.push_back(texture); | |
| 210 | + | } | |
| 211 | + | ||
| 212 | + | void PainterShaderProgram::bindMultiTextures() { | |
| 213 | + | if (m_multiTextures.size() == 0) | |
| 214 | + | return; | |
| 215 | + | ||
| 216 | + | int i = 1; | |
| 217 | + | for (const TexturePtr &tex : m_multiTextures) { | |
| 218 | + | tex->update(); | |
| 219 | + | glActiveTexture(GL_TEXTURE0 + i++); | |
| 220 | + | glBindTexture(GL_TEXTURE_2D, tex->getId()); | |
| 221 | + | } | |
| 222 | + | ||
| 223 | + | glActiveTexture(GL_TEXTURE0); | |
| 224 | + | } | |
| 225 | + | ||
| 226 | + | void PainterShaderProgram::clearMultiTextures() { m_multiTextures.clear(); } |
| @@ -0,0 +1,101 @@ | |||
| 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 PAINTERSHADERPROGRAM_H | |
| 24 | + | #define PAINTERSHADERPROGRAM_H | |
| 25 | + | ||
| 26 | + | #include "coordsbuffer.h" | |
| 27 | + | #include "shaderprogram.h" | |
| 28 | + | #include <framework/core/timer.h> | |
| 29 | + | ||
| 30 | + | class PainterShaderProgram : public ShaderProgram { | |
| 31 | + | protected: | |
| 32 | + | enum { | |
| 33 | + | VERTEX_ATTR = 0, | |
| 34 | + | TEXCOORD_ATTR = 1, | |
| 35 | + | DEPTH_ATTR = 2, | |
| 36 | + | COLOR_ATTR = 3, | |
| 37 | + | DEPTH_TEXCOORD_ATTR = 4, | |
| 38 | + | ||
| 39 | + | PROJECTION_MATRIX_UNIFORM = 0, | |
| 40 | + | TEXTURE_MATRIX_UNIFORM = 1, | |
| 41 | + | TRANSFORM_MATRIX_UNIFORM = 2, | |
| 42 | + | ||
| 43 | + | COLOR_UNIFORM = 3, | |
| 44 | + | TIME_UNIFORM = 5, | |
| 45 | + | DEPTH_UNIFORM = 6, | |
| 46 | + | ||
| 47 | + | TEX0_UNIFORM = 7, | |
| 48 | + | TEX1_UNIFORM = 8, | |
| 49 | + | TEX2_UNIFORM = 9, | |
| 50 | + | TEX3_UNIFORM = 10, | |
| 51 | + | ATLAS_TEX0_UNIFORM = 11, | |
| 52 | + | ATLAS_TEX1_UNIFORM = 12, | |
| 53 | + | ||
| 54 | + | RESOLUTION_UNIFORM = 13, | |
| 55 | + | OFFSET_UNIFORM = 14, | |
| 56 | + | CENTER_UNIFORM = 15 | |
| 57 | + | }; | |
| 58 | + | ||
| 59 | + | friend class Painter; | |
| 60 | + | ||
| 61 | + | virtual void setupUniforms(); | |
| 62 | + | ||
| 63 | + | public: | |
| 64 | + | PainterShaderProgram(const std::string &name); | |
| 65 | + | ||
| 66 | + | bool link(); | |
| 67 | + | ||
| 68 | + | void setTransformMatrix(const Matrix3 &transformMatrix); | |
| 69 | + | void setProjectionMatrix(const Matrix3 &projectionMatrix); | |
| 70 | + | void setTextureMatrix(const Matrix3 &textureMatrix); | |
| 71 | + | void setColor(const Color &color); | |
| 72 | + | void setMatrixColor(const Matrix4 &colors); | |
| 73 | + | void setDepth(float depth); | |
| 74 | + | void setResolution(const Size &resolution); | |
| 75 | + | void setOffset(const Point &offset); | |
| 76 | + | void setCenter(const Point ¢er); | |
| 77 | + | void updateTime(); | |
| 78 | + | ||
| 79 | + | void addMultiTexture(const std::string &file); | |
| 80 | + | void bindMultiTextures(); | |
| 81 | + | void clearMultiTextures(); | |
| 82 | + | ||
| 83 | + | void enableColorMatrix() { m_useColorMatrix = true; } | |
| 84 | + | ||
| 85 | + | private: | |
| 86 | + | float m_startTime; | |
| 87 | + | ||
| 88 | + | Color m_color; | |
| 89 | + | float m_depth; | |
| 90 | + | Matrix3 m_transformMatrix; | |
| 91 | + | Matrix3 m_projectionMatrix; | |
| 92 | + | Matrix3 m_textureMatrix; | |
| 93 | + | Size m_resolution; | |
| 94 | + | Point m_offset; | |
| 95 | + | Point m_center; | |
| 96 | + | float m_time; | |
| 97 | + | std::vector<TexturePtr> m_multiTextures; | |
| 98 | + | bool m_useColorMatrix = false; | |
| 99 | + | }; | |
| 100 | + | ||
| 101 | + | #endif |
| @@ -0,0 +1,93 @@ | |||
| 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 "shader.h" | |
| 24 | + | #include "graphics.h" | |
| 25 | + | ||
| 26 | + | #include <framework/core/application.h> | |
| 27 | + | #include <framework/core/resourcemanager.h> | |
| 28 | + | ||
| 29 | + | Shader::Shader(Shader::ShaderType shaderType) | |
| 30 | + | { | |
| 31 | + | m_shaderType = shaderType; | |
| 32 | + | switch(shaderType) { | |
| 33 | + | case Vertex: | |
| 34 | + | m_shaderId = glCreateShader(GL_VERTEX_SHADER); | |
| 35 | + | break; | |
| 36 | + | case Fragment: | |
| 37 | + | m_shaderId = glCreateShader(GL_FRAGMENT_SHADER); | |
| 38 | + | break; | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | if(!m_shaderId) | |
| 42 | + | g_logger.fatal("Unable to create GL shader"); | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | Shader::~Shader() | |
| 46 | + | { | |
| 47 | + | VALIDATE(!g_app.isTerminated()); | |
| 48 | + | if(g_graphics.ok()) | |
| 49 | + | glDeleteShader(m_shaderId); | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | bool Shader::compileSourceCode(const std::string& sourceCode) | |
| 53 | + | { | |
| 54 | + | #ifdef OPENGL_ES | |
| 55 | + | static const char *qualifierDefines = | |
| 56 | + | "precision highp float;\n"; | |
| 57 | + | std::string code = qualifierDefines; | |
| 58 | + | code.append(sourceCode); | |
| 59 | + | const char* c_source = code.c_str(); | |
| 60 | + | #else | |
| 61 | + | const char* c_source = sourceCode.c_str(); | |
| 62 | + | #endif | |
| 63 | + | glShaderSource(m_shaderId, 1, &c_source, NULL); | |
| 64 | + | glCompileShader(m_shaderId); | |
| 65 | + | ||
| 66 | + | int res = GL_FALSE; | |
| 67 | + | glGetShaderiv(m_shaderId, GL_COMPILE_STATUS, &res); | |
| 68 | + | return (res == GL_TRUE); | |
| 69 | + | } | |
| 70 | + | ||
| 71 | + | bool Shader::compileSourceFile(const std::string& sourceFile) | |
| 72 | + | { | |
| 73 | + | try { | |
| 74 | + | std::string sourceCode = g_resources.readFileContents(sourceFile); | |
| 75 | + | return compileSourceCode(sourceCode); | |
| 76 | + | } catch(stdext::exception& e) { | |
| 77 | + | g_logger.error(stdext::format("unable to load shader source form file '%s': %s", sourceFile, e.what())); | |
| 78 | + | } | |
| 79 | + | return false; | |
| 80 | + | } | |
| 81 | + | ||
| 82 | + | std::string Shader::log() | |
| 83 | + | { | |
| 84 | + | std::string infoLog; | |
| 85 | + | int infoLogLength = 0; | |
| 86 | + | glGetShaderiv(m_shaderId, GL_INFO_LOG_LENGTH, &infoLogLength); | |
| 87 | + | if(infoLogLength > 1) { | |
| 88 | + | std::vector<char> buf(infoLogLength); | |
| 89 | + | glGetShaderInfoLog(m_shaderId, infoLogLength-1, NULL, &buf[0]); | |
| 90 | + | infoLog = &buf[0]; | |
| 91 | + | } | |
| 92 | + | return infoLog; | |
| 93 | + | } |
| @@ -0,0 +1,51 @@ | |||
| 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 SHADER_H | |
| 24 | + | #define SHADER_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | ||
| 28 | + | class Shader : public stdext::shared_object | |
| 29 | + | { | |
| 30 | + | public: | |
| 31 | + | enum ShaderType { | |
| 32 | + | Vertex, | |
| 33 | + | Fragment | |
| 34 | + | }; | |
| 35 | + | ||
| 36 | + | Shader(ShaderType shaderType); | |
| 37 | + | ~Shader(); | |
| 38 | + | ||
| 39 | + | bool compileSourceCode(const std::string& sourceCode); | |
| 40 | + | bool compileSourceFile(const std::string& sourceFile); | |
| 41 | + | std::string log(); | |
| 42 | + | ||
| 43 | + | uint getShaderId() { return m_shaderId; } | |
| 44 | + | ShaderType getShaderType() { return m_shaderType; } | |
| 45 | + | ||
| 46 | + | private: | |
| 47 | + | uint m_shaderId; | |
| 48 | + | ShaderType m_shaderType; | |
| 49 | + | }; | |
| 50 | + | ||
| 51 | + | #endif |
| @@ -0,0 +1,76 @@ | |||
| 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 "shadermanager.h" | |
| 24 | + | #include <framework/graphics/paintershaderprogram.h> | |
| 25 | + | #include <framework/graphics/graphics.h> | |
| 26 | + | #include <framework/core/resourcemanager.h> | |
| 27 | + | #include <framework/core/eventdispatcher.h> | |
| 28 | + | ||
| 29 | + | ShaderManager g_shaders; | |
| 30 | + | ||
| 31 | + | void ShaderManager::init() | |
| 32 | + | { | |
| 33 | + | PainterShaderProgram::release(); | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | void ShaderManager::terminate() | |
| 37 | + | { | |
| 38 | + | m_shaders.clear(); | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | void ShaderManager::createShader(const std::string& name, std::string vertex, std::string fragment, bool colorMatrix) | |
| 42 | + | { | |
| 43 | + | if (vertex.find("\n") == std::string::npos) { // file | |
| 44 | + | vertex = g_resources.guessFilePath(vertex, "frag"); | |
| 45 | + | vertex = g_resources.readFileContents(vertex); | |
| 46 | + | } | |
| 47 | + | if (fragment.find("\n") == std::string::npos) { // file | |
| 48 | + | fragment = g_resources.guessFilePath(fragment, "frag"); | |
| 49 | + | fragment = g_resources.readFileContents(fragment); | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | g_graphicsDispatcher.addEventEx("createShader", [&, name, vertex, fragment, colorMatrix] { | |
| 53 | + | auto program = PainterShaderProgram::create(name, vertex, fragment, colorMatrix); | |
| 54 | + | if (program) | |
| 55 | + | m_shaders[name] = program; | |
| 56 | + | }); | |
| 57 | + | } | |
| 58 | + | ||
| 59 | + | void ShaderManager::addTexture(const std::string& name, const std::string& file) | |
| 60 | + | { | |
| 61 | + | g_graphicsDispatcher.addEventEx("addTexture", [&, name, file] { | |
| 62 | + | auto program = getShader(name); | |
| 63 | + | if (program) | |
| 64 | + | program->addMultiTexture(file); | |
| 65 | + | }); | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | PainterShaderProgramPtr ShaderManager::getShader(const std::string& name) | |
| 69 | + | { | |
| 70 | + | VALIDATE_GRAPHICS_THREAD(); | |
| 71 | + | auto it = m_shaders.find(name); | |
| 72 | + | if(it != m_shaders.end()) | |
| 73 | + | return it->second; | |
| 74 | + | return nullptr; | |
| 75 | + | } | |
| 76 | + |
| @@ -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 SHADERMANAGER_H | |
| 24 | + | #define SHADERMANAGER_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | #include <framework/graphics/paintershaderprogram.h> | |
| 28 | + | ||
| 29 | + | //@bindsingleton g_shaders | |
| 30 | + | class ShaderManager | |
| 31 | + | { | |
| 32 | + | public: | |
| 33 | + | void init(); | |
| 34 | + | void terminate(); | |
| 35 | + | ||
| 36 | + | void createShader(const std::string& name, std::string vertex, std::string fragment, bool colorMatrix = false); | |
| 37 | + | void createOutfitShader(const std::string& name, std::string vertex, std::string fragment) | |
| 38 | + | { | |
| 39 | + | return createShader(name, vertex, fragment, true); | |
| 40 | + | } | |
| 41 | + | void addTexture(const std::string& name, const std::string& file); | |
| 42 | + | PainterShaderProgramPtr getShader(const std::string& name); | |
| 43 | + | ||
| 44 | + | private: | |
| 45 | + | std::unordered_map<std::string, PainterShaderProgramPtr> m_shaders; | |
| 46 | + | }; | |
| 47 | + | ||
| 48 | + | ||
| 49 | + | extern ShaderManager g_shaders; | |
| 50 | + | ||
| 51 | + | #endif | |
| 52 | + |
| @@ -0,0 +1,184 @@ | |||
| 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 "shaderprogram.h" | |
| 24 | + | #include "graphics.h" | |
| 25 | + | ||
| 26 | + | #include <framework/core/application.h> | |
| 27 | + | ||
| 28 | + | uint ShaderProgram::m_currentProgram = 0; | |
| 29 | + | ||
| 30 | + | ShaderProgram::ShaderProgram(const std::string& name) : m_name(name) | |
| 31 | + | { | |
| 32 | + | m_linked = false; | |
| 33 | + | m_programId = glCreateProgram(); | |
| 34 | + | m_uniformLocations.fill(-1); | |
| 35 | + | if(!m_programId) | |
| 36 | + | g_logger.fatal("Unable to create GL shader program"); | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | ShaderProgram::~ShaderProgram() | |
| 40 | + | { | |
| 41 | + | VALIDATE(!g_app.isTerminated()); | |
| 42 | + | if(g_graphics.ok()) | |
| 43 | + | glDeleteProgram(m_programId); | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | PainterShaderProgramPtr ShaderProgram::create(const std::string& name, const std::string& vertexShader, const std::string& fragmentShader, bool colorMatrix) | |
| 47 | + | { | |
| 48 | + | PainterShaderProgramPtr program(new PainterShaderProgram(name)); | |
| 49 | + | if (!program) { | |
| 50 | + | g_logger.error(stdext::format("Cant creatre shader: %s", program->getName())); | |
| 51 | + | return nullptr; | |
| 52 | + | } | |
| 53 | + | if (!program->addShaderFromSourceCode(Shader::Vertex, vertexShader)) | |
| 54 | + | return nullptr; | |
| 55 | + | if(!program->addShaderFromSourceCode(Shader::Fragment, fragmentShader)) | |
| 56 | + | return nullptr; | |
| 57 | + | ||
| 58 | + | if (colorMatrix) { | |
| 59 | + | program->enableColorMatrix(); | |
| 60 | + | } | |
| 61 | + | if (!program->link()) { | |
| 62 | + | g_graphics.checkForError(stdext::format("%s (%s)", __FUNCTION__, program->getName()), vertexShader + "\n" + fragmentShader, __LINE__); | |
| 63 | + | return nullptr; | |
| 64 | + | } | |
| 65 | + | g_graphics.checkForError(stdext::format("%s (%s)", __FUNCTION__, program->getName()), vertexShader + "\n" + fragmentShader, __LINE__); | |
| 66 | + | return program; | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | ||
| 70 | + | bool ShaderProgram::addShader(const ShaderPtr& shader) { | |
| 71 | + | glAttachShader(m_programId, shader->getShaderId()); | |
| 72 | + | m_linked = false; | |
| 73 | + | m_shaders.push_back(shader); | |
| 74 | + | return true; | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | bool ShaderProgram::addShaderFromSourceCode(Shader::ShaderType shaderType, const std::string& sourceCode) { | |
| 78 | + | ShaderPtr shader(new Shader(shaderType)); | |
| 79 | + | if(!shader->compileSourceCode(sourceCode)) { | |
| 80 | + | g_logger.error(stdext::format("failed to compile shader: %s\n%s", getName(), shader->log())); | |
| 81 | + | return false; | |
| 82 | + | } | |
| 83 | + | return addShader(shader); | |
| 84 | + | } | |
| 85 | + | ||
| 86 | + | bool ShaderProgram::addShaderFromSourceFile(Shader::ShaderType shaderType, const std::string& sourceFile) { | |
| 87 | + | ShaderPtr shader(new Shader(shaderType)); | |
| 88 | + | if(!shader->compileSourceFile(sourceFile)) { | |
| 89 | + | g_logger.error(stdext::format("failed to compile shader %s (%s): %s", getName(), sourceFile, shader->log())); | |
| 90 | + | return false; | |
| 91 | + | } | |
| 92 | + | return addShader(shader); | |
| 93 | + | } | |
| 94 | + | ||
| 95 | + | void ShaderProgram::removeShader(const ShaderPtr& shader) | |
| 96 | + | { | |
| 97 | + | auto it = std::find(m_shaders.begin(), m_shaders.end(), shader); | |
| 98 | + | if(it == m_shaders.end()) | |
| 99 | + | return; | |
| 100 | + | ||
| 101 | + | glDetachShader(m_programId, shader->getShaderId()); | |
| 102 | + | m_shaders.erase(it); | |
| 103 | + | m_linked = false; | |
| 104 | + | } | |
| 105 | + | ||
| 106 | + | void ShaderProgram::removeAllShaders() | |
| 107 | + | { | |
| 108 | + | while(!m_shaders.empty()) | |
| 109 | + | removeShader(m_shaders.front()); | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | bool ShaderProgram::link() | |
| 113 | + | { | |
| 114 | + | if(m_linked) | |
| 115 | + | return true; | |
| 116 | + | ||
| 117 | + | glLinkProgram(m_programId); | |
| 118 | + | ||
| 119 | + | int value = GL_FALSE; | |
| 120 | + | glGetProgramiv(m_programId, GL_LINK_STATUS, &value); | |
| 121 | + | m_linked = (value != GL_FALSE); | |
| 122 | + | if (m_linked) { | |
| 123 | + | return true; | |
| 124 | + | } | |
| 125 | + | ||
| 126 | + | GLint maxLength = 0; | |
| 127 | + | glGetProgramiv(m_programId, GL_INFO_LOG_LENGTH, &maxLength); | |
| 128 | + | std::vector<GLchar> infoLog(maxLength); | |
| 129 | + | glGetProgramInfoLog(m_programId, maxLength, &maxLength, &infoLog[0]); | |
| 130 | + | g_logger.error(stdext::format("Program %i, %s linking error (%i): %s - %s - %s %s\nExtensions: %s", m_programId, getName(), infoLog.size(), | |
| 131 | + | std::string(infoLog.begin(), infoLog.end()).c_str(), log().c_str(), | |
| 132 | + | g_graphics.getRenderer(), g_graphics.getVersion(), g_graphics.getExtensions())); | |
| 133 | + | return false; | |
| 134 | + | } | |
| 135 | + | ||
| 136 | + | bool ShaderProgram::bind() | |
| 137 | + | { | |
| 138 | + | if(m_currentProgram != m_programId) { | |
| 139 | + | if (!m_linked) { | |
| 140 | + | link(); | |
| 141 | + | } | |
| 142 | + | glUseProgram(m_programId); | |
| 143 | + | m_currentProgram = m_programId; | |
| 144 | + | } | |
| 145 | + | return true; | |
| 146 | + | } | |
| 147 | + | ||
| 148 | + | void ShaderProgram::release() | |
| 149 | + | { | |
| 150 | + | if(m_currentProgram != 0) { | |
| 151 | + | m_currentProgram = 0; | |
| 152 | + | glUseProgram(0); | |
| 153 | + | } | |
| 154 | + | } | |
| 155 | + | ||
| 156 | + | std::string ShaderProgram::log() | |
| 157 | + | { | |
| 158 | + | std::string infoLog; | |
| 159 | + | int infoLogLength = 0; | |
| 160 | + | glGetProgramiv(m_programId, GL_INFO_LOG_LENGTH, &infoLogLength); | |
| 161 | + | if(infoLogLength > 1) { | |
| 162 | + | std::vector<char> buf(infoLogLength); | |
| 163 | + | glGetShaderInfoLog(m_programId, infoLogLength-1, NULL, &buf[0]); | |
| 164 | + | infoLog = &buf[0]; | |
| 165 | + | } | |
| 166 | + | return infoLog; | |
| 167 | + | } | |
| 168 | + | ||
| 169 | + | int ShaderProgram::getAttributeLocation(const char* name) | |
| 170 | + | { | |
| 171 | + | return glGetAttribLocation(m_programId, name); | |
| 172 | + | } | |
| 173 | + | ||
| 174 | + | void ShaderProgram::bindAttributeLocation(int location, const char* name) | |
| 175 | + | { | |
| 176 | + | return glBindAttribLocation(m_programId, location, name); | |
| 177 | + | } | |
| 178 | + | ||
| 179 | + | void ShaderProgram::bindUniformLocation(int location, const char* name) | |
| 180 | + | { | |
| 181 | + | VALIDATE(m_linked); | |
| 182 | + | VALIDATE(location >= 0 && location < MAX_UNIFORM_LOCATIONS); | |
| 183 | + | m_uniformLocations[location] = glGetUniformLocation(m_programId, name); | |
| 184 | + | } |
| @@ -0,0 +1,106 @@ | |||
| 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 SHADERPROGRAM_H | |
| 24 | + | #define SHADERPROGRAM_H | |
| 25 | + | ||
| 26 | + | #include "shader.h" | |
| 27 | + | #include <framework/luaengine/luaobject.h> | |
| 28 | + | ||
| 29 | + | // @bindclass | |
| 30 | + | class ShaderProgram : public LuaObject { | |
| 31 | + | enum { | |
| 32 | + | MAX_UNIFORM_LOCATIONS = 30 | |
| 33 | + | }; | |
| 34 | + | ||
| 35 | + | public: | |
| 36 | + | ShaderProgram(const std::string& name); | |
| 37 | + | ~ShaderProgram(); | |
| 38 | + | ||
| 39 | + | static PainterShaderProgramPtr create(const std::string& name, const std::string& vertexShader, const std::string& fragmentShader, bool colorMatrix = false); | |
| 40 | + | ||
| 41 | + | bool addShader(const ShaderPtr& shader); | |
| 42 | + | bool addShaderFromSourceCode(Shader::ShaderType shaderType, const std::string& sourceCode); | |
| 43 | + | bool addShaderFromSourceFile(Shader::ShaderType shaderType, const std::string& sourceFile); | |
| 44 | + | void removeShader(const ShaderPtr& shader); | |
| 45 | + | void removeAllShaders(); | |
| 46 | + | ||
| 47 | + | virtual bool link(); | |
| 48 | + | bool bind(); | |
| 49 | + | static void release(); | |
| 50 | + | std::string log(); | |
| 51 | + | ||
| 52 | + | static void disableAttributeArray(int location) { glDisableVertexAttribArray(location); } | |
| 53 | + | static void enableAttributeArray(int location) { glEnableVertexAttribArray(location); } | |
| 54 | + | void disableAttributeArray(const char* name) { glDisableVertexAttribArray(getAttributeLocation(name)); } | |
| 55 | + | void enableAttributeArray(const char* name) { glEnableVertexAttribArray(getAttributeLocation(name)); } | |
| 56 | + | ||
| 57 | + | int getAttributeLocation(const char* name); | |
| 58 | + | void bindAttributeLocation(int location, const char* name); | |
| 59 | + | void bindUniformLocation(int location, const char* name); | |
| 60 | + | ||
| 61 | + | void setAttributeArray(int location, const float* values, int size, int stride = 0) { glVertexAttribPointer(location, size, GL_FLOAT, GL_FALSE, stride, values); } | |
| 62 | + | void setAttributeValue(int location, float value) { glVertexAttrib1f(location, value); } | |
| 63 | + | void setAttributeValue(int location, float x, float y) { glVertexAttrib2f(location, x, y); } | |
| 64 | + | void setAttributeValue(int location, float x, float y, float z) { glVertexAttrib3f(location, x, y, z); } | |
| 65 | + | void setAttributeArray(const char* name, const float* values, int size, int stride = 0) { glVertexAttribPointer(getAttributeLocation(name), size, GL_FLOAT, GL_FALSE, stride, values); } | |
| 66 | + | void setAttributeValue(const char* name, float value) { glVertexAttrib1f(getAttributeLocation(name), value); } | |
| 67 | + | void setAttributeValue(const char* name, float x, float y) { glVertexAttrib2f(getAttributeLocation(name), x, y); } | |
| 68 | + | void setAttributeValue(const char* name, float x, float y, float z) { glVertexAttrib3f(getAttributeLocation(name), x, y, z); } | |
| 69 | + | ||
| 70 | + | void setUniformValue(int location, const Color& color) { glUniform4f(m_uniformLocations[location], color.rF(), color.gF(), color.bF(), color.aF()); } | |
| 71 | + | void setUniformValue(int location, int value) { glUniform1i(m_uniformLocations[location], value); } | |
| 72 | + | void setUniformValue(int location, float value) { glUniform1f(m_uniformLocations[location], value); } | |
| 73 | + | void setUniformValue(int location, float x, float y) { glUniform2f(m_uniformLocations[location], x, y); } | |
| 74 | + | void setUniformValue(int location, float x, float y, float z) { glUniform3f(m_uniformLocations[location], x, y, z); } | |
| 75 | + | void setUniformValue(int location, float x, float y, float z, float w) { glUniform4f(m_uniformLocations[location], x, y, z, w); } | |
| 76 | + | void setUniformValue(int location, const Matrix2& mat) { glUniformMatrix2fv(m_uniformLocations[location], 1, GL_FALSE, mat.data()); } | |
| 77 | + | void setUniformValue(int location, const Matrix3& mat) { glUniformMatrix3fv(m_uniformLocations[location], 1, GL_FALSE, mat.data()); } | |
| 78 | + | void setUniformValue(int location, const Matrix4& mat) { glUniformMatrix4fv(m_uniformLocations[location], 1, GL_FALSE, mat.data()); } | |
| 79 | + | void setUniformValue(int location, int count, const int* value) { glUniform1iv(m_uniformLocations[location], count, value); } | |
| 80 | + | void setUniformValue(const char* name, const Color& color) { glUniform4f(glGetUniformLocation(m_programId, name), color.rF(), color.gF(), color.bF(), color.aF()); } | |
| 81 | + | void setUniformValue(const char* name, int value) { glUniform1i(glGetUniformLocation(m_programId, name), value); } | |
| 82 | + | void setUniformValue(const char* name, float value) { glUniform1f(glGetUniformLocation(m_programId, name), value); } | |
| 83 | + | void setUniformValue(const char* name, float x, float y) { glUniform2f(glGetUniformLocation(m_programId, name), x, y); } | |
| 84 | + | void setUniformValue(const char* name, float x, float y, float z) { glUniform3f(glGetUniformLocation(m_programId, name), x, y, z); } | |
| 85 | + | void setUniformValue(const char* name, float x, float y, float z, float w) { glUniform4f(glGetUniformLocation(m_programId, name), x, y, z, w); } | |
| 86 | + | void setUniformValue(const char* name, const Matrix2& mat) { glUniformMatrix2fv(glGetUniformLocation(m_programId, name), 1, GL_FALSE, mat.data()); } | |
| 87 | + | void setUniformValue(const char* name, const Matrix3& mat) { glUniformMatrix3fv(glGetUniformLocation(m_programId, name), 1, GL_FALSE, mat.data()); } | |
| 88 | + | void setUniformValue(const char* name, const Matrix4& mat) { glUniformMatrix4fv(glGetUniformLocation(m_programId, name), 1, GL_FALSE, mat.data()); } | |
| 89 | + | // TODO: Point, PointF, Color, Size, SizeF ? | |
| 90 | + | ||
| 91 | + | bool isLinked() { return m_linked; } | |
| 92 | + | uint getProgramId() { return m_programId; } | |
| 93 | + | ShaderList getShaders() { return m_shaders; } | |
| 94 | + | ||
| 95 | + | std::string getName() { return m_name; } | |
| 96 | + | ||
| 97 | + | private: | |
| 98 | + | std::string m_name; | |
| 99 | + | bool m_linked; | |
| 100 | + | uint m_programId; | |
| 101 | + | static uint m_currentProgram; | |
| 102 | + | ShaderList m_shaders; | |
| 103 | + | std::array<int, MAX_UNIFORM_LOCATIONS> m_uniformLocations; | |
| 104 | + | }; | |
| 105 | + | ||
| 106 | + | #endif |
| @@ -0,0 +1,79 @@ | |||
| 1 | + | #ifndef NEWSHADER_H | |
| 2 | + | #define NEWSHADER_H | |
| 3 | + | ||
| 4 | + | #include <string> | |
| 5 | + | // VERTEX | |
| 6 | + | static const std::string newVertexShader = "\n\ | |
| 7 | + | attribute vec2 a_Vertex;\n\ | |
| 8 | + | attribute vec2 a_TexCoord;\n\ | |
| 9 | + | attribute vec4 a_Color;\n\ | |
| 10 | + | \n\ | |
| 11 | + | uniform mat3 u_ProjectionMatrix;\n\ | |
| 12 | + | uniform mat3 u_TransformMatrix;\n\ | |
| 13 | + | uniform mat3 u_TextureMatrix;\n\ | |
| 14 | + | \n\ | |
| 15 | + | varying vec2 v_TexCoord;\n\ | |
| 16 | + | varying vec4 v_Color;\n\ | |
| 17 | + | void main()\n\ | |
| 18 | + | {\n\ | |
| 19 | + | gl_Position = vec4((u_ProjectionMatrix * u_TransformMatrix * vec3(a_Vertex.xy, 1.0)).xy, 1.0, 1.0);\n\ | |
| 20 | + | v_TexCoord = (u_TextureMatrix * vec3(a_TexCoord,1.0)).xy;\n\ | |
| 21 | + | v_Color = a_Color;\n\ | |
| 22 | + | }\n"; | |
| 23 | + | ||
| 24 | + | // TEXT | |
| 25 | + | static const std::string textVertexShader = "\n\ | |
| 26 | + | attribute vec2 a_TexCoord;\n\ | |
| 27 | + | uniform mat3 u_TextureMatrix;\n\ | |
| 28 | + | varying vec2 v_TexCoord;\n\ | |
| 29 | + | attribute vec2 a_Vertex;\n\ | |
| 30 | + | uniform mat3 u_TransformMatrix;\n\ | |
| 31 | + | uniform mat3 u_ProjectionMatrix;\n\ | |
| 32 | + | uniform float u_Depth;\n\ | |
| 33 | + | uniform vec2 u_Offset;\n\ | |
| 34 | + | void main()\n\ | |
| 35 | + | {\n\ | |
| 36 | + | gl_Position = vec4((u_ProjectionMatrix * u_TransformMatrix * vec3(a_Vertex.xy + u_Offset, 1.0)).xy, 1.0, 1.0);\n\ | |
| 37 | + | v_TexCoord = (u_TextureMatrix * vec3(a_TexCoord,1.0)).xy;\n\ | |
| 38 | + | }\n"; | |
| 39 | + | ||
| 40 | + | // LINE | |
| 41 | + | static const std::string lineVertexShader = "\n\ | |
| 42 | + | attribute vec2 a_Vertex;\n\ | |
| 43 | + | uniform mat3 u_TransformMatrix;\n\ | |
| 44 | + | uniform mat3 u_ProjectionMatrix;\n\ | |
| 45 | + | void main()\n\ | |
| 46 | + | {\n\ | |
| 47 | + | gl_Position = vec4((u_ProjectionMatrix * u_TransformMatrix * vec3(a_Vertex.xy, 1.0)).xy, 1.0, 1.0);\n\ | |
| 48 | + | }\n"; | |
| 49 | + | ||
| 50 | + | // FRAGMENT | |
| 51 | + | static const std::string newFragmentShader = "\n\ | |
| 52 | + | varying vec2 v_TexCoord;\n\ | |
| 53 | + | varying vec4 v_Color;\n\ | |
| 54 | + | uniform sampler2D u_Atlas;\n\ | |
| 55 | + | void main()\n\ | |
| 56 | + | {\n\ | |
| 57 | + | if(v_TexCoord.x < 0.0) { gl_FragColor = v_Color; return; }\n\ | |
| 58 | + | gl_FragColor = texture2D(u_Atlas, v_TexCoord) * v_Color;\n\ | |
| 59 | + | }\n"; | |
| 60 | + | ||
| 61 | + | // TEXT | |
| 62 | + | static const std::string textFragmentShader = "\n\ | |
| 63 | + | varying vec2 v_TexCoord;\n\ | |
| 64 | + | uniform vec4 u_Color;\n\ | |
| 65 | + | uniform sampler2D u_Tex0;\n\ | |
| 66 | + | void main()\n\ | |
| 67 | + | {\n\ | |
| 68 | + | gl_FragColor = texture2D(u_Tex0, v_TexCoord) * u_Color;\n\ | |
| 69 | + | }\n"; | |
| 70 | + | ||
| 71 | + | // LINE | |
| 72 | + | static const std::string lineFragmentShader = "\n\ | |
| 73 | + | uniform vec4 u_Color;\n\ | |
| 74 | + | void main()\n\ | |
| 75 | + | {\n\ | |
| 76 | + | gl_FragColor = u_Color;\n\ | |
| 77 | + | }\n"; | |
| 78 | + | ||
| 79 | + | #endif |
| @@ -0,0 +1,39 @@ | |||
| 1 | + | #ifndef OUTFITS_H | |
| 2 | + | #define OUTFITS_H | |
| 3 | + | ||
| 4 | + | static const std::string glslAdvancedOutfitVertexShader = "\n\ | |
| 5 | + | attribute vec2 a_TexCoord;\n\ | |
| 6 | + | attribute vec2 a_Vertex;\n\ | |
| 7 | + | uniform mat3 u_TextureMatrix;\n\ | |
| 8 | + | varying vec2 v_TexCoord;\n\ | |
| 9 | + | varying vec2 v_TexCoord2;\n\ | |
| 10 | + | varying vec2 v_TexCoord3;\n\ | |
| 11 | + | uniform mat3 u_TransformMatrix;\n\ | |
| 12 | + | uniform mat3 u_ProjectionMatrix;\n\ | |
| 13 | + | uniform vec2 u_Offset;\n\ | |
| 14 | + | uniform vec2 u_Resolution;\n\ | |
| 15 | + | uniform float u_Depth;\n\ | |
| 16 | + | void main()\n\ | |
| 17 | + | {\n\ | |
| 18 | + | gl_Position = vec4((u_ProjectionMatrix * u_TransformMatrix * vec3(a_Vertex.xy, 1.0)).xy, u_Depth / 16384.0, 1.0);\n\ | |
| 19 | + | v_TexCoord = (u_TextureMatrix * vec3(a_TexCoord,1.0)).xy;\n\ | |
| 20 | + | v_TexCoord2 = (u_TextureMatrix * vec3(a_TexCoord + u_Offset,1.0)).xy;\n\ | |
| 21 | + | v_TexCoord3 = (u_TextureMatrix * vec3(u_Resolution,1.0)).xy;\n\ | |
| 22 | + | }\n"; | |
| 23 | + | ||
| 24 | + | static const std::string glslAdvancedOutfitFragmentShader = "\n\ | |
| 25 | + | uniform float u_Depth;\n\ | |
| 26 | + | uniform mat4 u_Color;\n\ | |
| 27 | + | varying vec2 v_TexCoord;\n\ | |
| 28 | + | varying vec2 v_TexCoord2;\n\ | |
| 29 | + | uniform sampler2D u_Tex0;\n\ | |
| 30 | + | uniform sampler2D u_Tex1;\n\ | |
| 31 | + | void main()\n\ | |
| 32 | + | {\n\ | |
| 33 | + | gl_FragColor = texture2D(u_Tex0, v_TexCoord);\n\ | |
| 34 | + | if(texture2D(u_Tex0, v_TexCoord2).a > 0.01)\n\ | |
| 35 | + | gl_FragColor *= vec4(1.0, 1.0, 0.0, 1.0);\n\ | |
| 36 | + | if(gl_FragColor.a < 0.01) discard;\n\ | |
| 37 | + | }\n"; | |
| 38 | + | ||
| 39 | + | #endif |
| @@ -0,0 +1,8 @@ | |||
| 1 | + | #ifndef SHADERS_H | |
| 2 | + | #define SHADERS_H | |
| 3 | + | ||
| 4 | + | #include "newshader.h" | |
| 5 | + | #include "shadersources.h" | |
| 6 | + | #include "outfits.h" | |
| 7 | + | ||
| 8 | + | #endif |
| @@ -0,0 +1,123 @@ | |||
| 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 PAINTEROGL2_SHADERSOURCES_H | |
| 24 | + | #define PAINTEROGL2_SHADERSOURCES_H | |
| 25 | + | ||
| 26 | + | static const std::string glslMainVertexShader = "\n\ | |
| 27 | + | vec4 calculatePosition();\n\ | |
| 28 | + | void main() {\n\ | |
| 29 | + | gl_Position = calculatePosition();\n\ | |
| 30 | + | }\n"; | |
| 31 | + | ||
| 32 | + | static const std::string glslMainWithTexCoordsVertexShader = "\n\ | |
| 33 | + | attribute vec2 a_TexCoord;\n\ | |
| 34 | + | uniform mat3 u_TextureMatrix;\n\ | |
| 35 | + | varying vec2 v_TexCoord;\n\ | |
| 36 | + | vec4 calculatePosition();\n\ | |
| 37 | + | void main()\n\ | |
| 38 | + | {\n\ | |
| 39 | + | gl_Position = calculatePosition();\n\ | |
| 40 | + | v_TexCoord = (u_TextureMatrix * vec3(a_TexCoord,1.0)).xy;\n\ | |
| 41 | + | }\n"; | |
| 42 | + | ||
| 43 | + | static std::string glslPositionOnlyVertexShader = "\n\ | |
| 44 | + | attribute vec2 a_Vertex;\n\ | |
| 45 | + | uniform mat3 u_TransformMatrix;\n\ | |
| 46 | + | uniform mat3 u_ProjectionMatrix;\n\ | |
| 47 | + | uniform float u_Depth;\n\ | |
| 48 | + | vec4 calculatePosition() {\n\ | |
| 49 | + | return vec4((u_ProjectionMatrix * u_TransformMatrix * vec3(a_Vertex.xy, 1.0)).xy, u_Depth / 16384.0, 1.0);\n\ | |
| 50 | + | }\n"; | |
| 51 | + | ||
| 52 | + | static const std::string glslMainFragmentShader = "\n\ | |
| 53 | + | uniform float u_Depth;\n\ | |
| 54 | + | vec4 calculatePixel();\n\ | |
| 55 | + | void main()\n\ | |
| 56 | + | {\n\ | |
| 57 | + | gl_FragColor = calculatePixel();\n\ | |
| 58 | + | if(gl_FragColor.a < 0.01 && u_Depth > 0.0)\n\ | |
| 59 | + | discard;\n\ | |
| 60 | + | }\n"; | |
| 61 | + | ||
| 62 | + | static const std::string glslTextureSrcFragmentShader = "\n\ | |
| 63 | + | varying vec2 v_TexCoord;\n\ | |
| 64 | + | uniform vec4 u_Color;\n\ | |
| 65 | + | uniform sampler2D u_Tex0;\n\ | |
| 66 | + | vec4 calculatePixel() {\n\ | |
| 67 | + | return texture2D(u_Tex0, v_TexCoord) * u_Color;\n\ | |
| 68 | + | }\n"; | |
| 69 | + | ||
| 70 | + | ||
| 71 | + | static const std::string glslSolidColorFragmentShader = "\n\ | |
| 72 | + | uniform vec4 u_Color;\n\ | |
| 73 | + | vec4 calculatePixel() {\n\ | |
| 74 | + | return u_Color;\n\ | |
| 75 | + | }\n"; | |
| 76 | + | ||
| 77 | + | static const std::string glslSolidColorOnTextureFragmentShader = "\n\ | |
| 78 | + | uniform vec4 u_Color;\n\ | |
| 79 | + | varying vec2 v_TexCoord;\n\ | |
| 80 | + | uniform sampler2D u_Tex0;\n\ | |
| 81 | + | vec4 calculatePixel() {\n\ | |
| 82 | + | if(texture2D(u_Tex0, v_TexCoord).a > 0.01)\n\ | |
| 83 | + | return u_Color;\n\ | |
| 84 | + | return vec4(0,0,0,0);\n\ | |
| 85 | + | }\n"; | |
| 86 | + | ||
| 87 | + | ||
| 88 | + | static const std::string glslOutfitVertexShader = "\n\ | |
| 89 | + | attribute vec2 a_TexCoord;\n\ | |
| 90 | + | uniform mat3 u_TextureMatrix;\n\ | |
| 91 | + | varying vec2 v_TexCoord;\n\ | |
| 92 | + | varying vec2 v_TexCoord2;\n\ | |
| 93 | + | attribute vec2 a_Vertex;\n\ | |
| 94 | + | uniform mat3 u_TransformMatrix;\n\ | |
| 95 | + | uniform mat3 u_ProjectionMatrix;\n\ | |
| 96 | + | uniform float u_Depth;\n\ | |
| 97 | + | uniform vec2 u_Offset;\n\ | |
| 98 | + | void main()\n\ | |
| 99 | + | {\n\ | |
| 100 | + | gl_Position = vec4((u_ProjectionMatrix * u_TransformMatrix * vec3(a_Vertex.xy, 1.0)).xy, u_Depth / 16384.0, 1.0);\n\ | |
| 101 | + | v_TexCoord = (u_TextureMatrix * vec3(a_TexCoord,1.0)).xy;\n\ | |
| 102 | + | v_TexCoord2 = (u_TextureMatrix * vec3(a_TexCoord + u_Offset,1.0)).xy;\n\ | |
| 103 | + | }\n"; | |
| 104 | + | ||
| 105 | + | static const std::string glslOutfitFragmentShader = "\n\ | |
| 106 | + | uniform mat4 u_Color;\n\ | |
| 107 | + | varying vec2 v_TexCoord;\n\ | |
| 108 | + | varying vec2 v_TexCoord2;\n\ | |
| 109 | + | uniform sampler2D u_Tex0;\n\ | |
| 110 | + | void main()\n\ | |
| 111 | + | {\n\ | |
| 112 | + | gl_FragColor = texture2D(u_Tex0, v_TexCoord);\n\ | |
| 113 | + | vec4 texcolor = texture2D(u_Tex0, v_TexCoord2);\n\ | |
| 114 | + | if(texcolor.r > 0.9)\n\ | |
| 115 | + | gl_FragColor *= texcolor.g > 0.9 ? u_Color[0] : u_Color[1];\n\ | |
| 116 | + | else if(texcolor.g > 0.9)\n\ | |
| 117 | + | gl_FragColor *= u_Color[2];\n\ | |
| 118 | + | else if(texcolor.b > 0.9)\n\ | |
| 119 | + | gl_FragColor *= u_Color[3];\n\ | |
| 120 | + | if(gl_FragColor.a < 0.01) discard;\n\ | |
| 121 | + | }\n"; | |
| 122 | + | ||
| 123 | + | #endif |
| @@ -0,0 +1,129 @@ | |||
| 1 | + | #include "painter.h" | |
| 2 | + | #include "textrender.h" | |
| 3 | + | #include <framework/core/logger.h> | |
| 4 | + | #include <framework/core/eventdispatcher.h> | |
| 5 | + | ||
| 6 | + | TextRender g_text; | |
| 7 | + | ||
| 8 | + | void TextRender::init() | |
| 9 | + | { | |
| 10 | + | ||
| 11 | + | } | |
| 12 | + | ||
| 13 | + | void TextRender::terminate() | |
| 14 | + | { | |
| 15 | + | for (auto& cache : m_cache) { | |
| 16 | + | cache.clear(); | |
| 17 | + | } | |
| 18 | + | } | |
| 19 | + | ||
| 20 | + | void TextRender::poll() | |
| 21 | + | { | |
| 22 | + | static int iteration = 0; | |
| 23 | + | int index = (iteration++) % INDEXES; | |
| 24 | + | std::lock_guard<std::mutex> lock(m_mutex[index]); | |
| 25 | + | auto& cache = m_cache[index]; | |
| 26 | + | if (cache.size() < 100) | |
| 27 | + | return; | |
| 28 | + | ||
| 29 | + | ticks_t dropPoint = g_clock.millis(); | |
| 30 | + | if (cache.size() > 500) | |
| 31 | + | dropPoint -= 10; | |
| 32 | + | else if (cache.size() > 250) | |
| 33 | + | dropPoint -= 100; | |
| 34 | + | else | |
| 35 | + | dropPoint -= 1000; | |
| 36 | + | ||
| 37 | + | for (auto it = cache.begin(); it != cache.end(); ) { | |
| 38 | + | if (it->second->lastUse < dropPoint) { | |
| 39 | + | it = cache.erase(it); | |
| 40 | + | continue; | |
| 41 | + | } | |
| 42 | + | ++it; | |
| 43 | + | } | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | uint64_t TextRender::addText(BitmapFontPtr font, const std::string& text, const Size& size, Fw::AlignmentFlag align) | |
| 47 | + | { | |
| 48 | + | if (!font || text.empty() || !size.isValid()) | |
| 49 | + | return 0; | |
| 50 | + | uint64_t hash = 1125899906842597ULL; | |
| 51 | + | for (size_t i = 0; i < text.length(); ++i) { | |
| 52 | + | hash = hash * 31 + text[i]; | |
| 53 | + | } | |
| 54 | + | hash = hash * 31 + size.width(); | |
| 55 | + | hash = hash * 31 + size.height(); | |
| 56 | + | hash = hash * 31 + (uint64_t)align; | |
| 57 | + | hash = hash * 31 + (uint64_t)font->getId(); | |
| 58 | + | ||
| 59 | + | int index = hash % INDEXES; | |
| 60 | + | m_mutex[index].lock(); | |
| 61 | + | auto it = m_cache[index].find(hash); | |
| 62 | + | if (it == m_cache[index].end()) { | |
| 63 | + | m_cache[index][hash] = std::shared_ptr<TextRenderCache>(new TextRenderCache{ font, text, size, align, font->getTexture(), CoordsBuffer(), g_clock.millis() }); | |
| 64 | + | } | |
| 65 | + | m_mutex[index].unlock(); | |
| 66 | + | return hash; | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | void TextRender::drawText(const Rect& rect, const std::string& text, BitmapFontPtr font, const Color& color, Fw::AlignmentFlag align, bool shadow) | |
| 70 | + | { | |
| 71 | + | VALIDATE_GRAPHICS_THREAD(); | |
| 72 | + | uint64_t hash = addText(font, text, rect.size(), align); | |
| 73 | + | drawText(rect.topLeft(), hash, color, shadow); | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | void TextRender::drawText(const Point& pos, uint64_t hash, const Color& color, bool shadow) | |
| 77 | + | { | |
| 78 | + | VALIDATE_GRAPHICS_THREAD(); | |
| 79 | + | int index = hash % INDEXES; | |
| 80 | + | m_mutex[index].lock(); | |
| 81 | + | auto _it = m_cache[index].find(hash); | |
| 82 | + | if (_it == m_cache[index].end()) { | |
| 83 | + | m_mutex[index].unlock(); | |
| 84 | + | return; | |
| 85 | + | } | |
| 86 | + | auto it = _it->second; | |
| 87 | + | it->lastUse = g_clock.millis(); | |
| 88 | + | m_mutex[index].unlock(); | |
| 89 | + | if (it->font) { // calculate text coords | |
| 90 | + | it->font->calculateDrawTextCoords(it->coords, it->text, Rect(0, 0, it->size), it->align); | |
| 91 | + | it->coords.cache(); | |
| 92 | + | it->text.clear(); | |
| 93 | + | it->font.reset(); | |
| 94 | + | } | |
| 95 | + | ||
| 96 | + | if (shadow) { | |
| 97 | + | auto shadowPos = Point(pos); | |
| 98 | + | shadowPos.x += 1; | |
| 99 | + | shadowPos.y += 1; | |
| 100 | + | g_painter->drawText(shadowPos, it->coords, Color::black, it->texture); | |
| 101 | + | } | |
| 102 | + | ||
| 103 | + | g_painter->drawText(pos, it->coords, color, it->texture); | |
| 104 | + | } | |
| 105 | + | ||
| 106 | + | void TextRender::drawColoredText(const Point& pos, uint64_t hash, const std::vector<std::pair<int, Color>>& colors, bool shadow) | |
| 107 | + | { | |
| 108 | + | VALIDATE_GRAPHICS_THREAD(); | |
| 109 | + | if (colors.empty()) | |
| 110 | + | return drawText(pos, hash, Color::white); | |
| 111 | + | int index = hash % INDEXES; | |
| 112 | + | m_mutex[index].lock(); | |
| 113 | + | auto _it = m_cache[index].find(hash); | |
| 114 | + | if (_it == m_cache[index].end()) { | |
| 115 | + | m_mutex[index].unlock(); | |
| 116 | + | return; | |
| 117 | + | } | |
| 118 | + | auto it = _it->second; | |
| 119 | + | it->lastUse = g_clock.millis(); | |
| 120 | + | m_mutex[index].unlock(); | |
| 121 | + | if (it->font) { // calculate text coords | |
| 122 | + | it->font->calculateDrawTextCoords(it->coords, it->text, Rect(0, 0, it->size), it->align); | |
| 123 | + | it->coords.cache(); | |
| 124 | + | it->text.clear(); | |
| 125 | + | it->font.reset(); | |
| 126 | + | } | |
| 127 | + | g_painter->drawText(pos, it->coords, colors, it->texture); | |
| 128 | + | } | |
| 129 | + |
| @@ -0,0 +1,39 @@ | |||
| 1 | + | #ifndef TEXTRENDER_H | |
| 2 | + | #define TEXTRENDER_H | |
| 3 | + | ||
| 4 | + | #include <map> | |
| 5 | + | #include <mutex> | |
| 6 | + | #include "bitmapfont.h" | |
| 7 | + | #include "coordsbuffer.h" | |
| 8 | + | #include <framework/core/clock.h> | |
| 9 | + | ||
| 10 | + | struct TextRenderCache { | |
| 11 | + | BitmapFontPtr font; | |
| 12 | + | std::string text; | |
| 13 | + | Size size; | |
| 14 | + | Fw::AlignmentFlag align; | |
| 15 | + | TexturePtr texture; | |
| 16 | + | CoordsBuffer coords; | |
| 17 | + | ticks_t lastUse; | |
| 18 | + | }; | |
| 19 | + | ||
| 20 | + | class TextRender | |
| 21 | + | { | |
| 22 | + | static const int INDEXES = 10; | |
| 23 | + | public: | |
| 24 | + | void init(); | |
| 25 | + | void terminate(); | |
| 26 | + | void poll(); | |
| 27 | + | uint64_t addText(BitmapFontPtr font, const std::string& text, const Size& size, Fw::AlignmentFlag align = Fw::AlignTopLeft); | |
| 28 | + | void drawText(const Rect& rect, const std::string& text, BitmapFontPtr font, const Color& color = Color::white, Fw::AlignmentFlag align = Fw::AlignTopLeft, bool shadow = false); | |
| 29 | + | void drawText(const Point& pos, uint64_t hash, const Color& color, bool shadow = false); | |
| 30 | + | void drawColoredText(const Point& pos, uint64_t hash, const std::vector<std::pair<int, Color>>& colors, bool shadow = false); | |
| 31 | + | ||
| 32 | + | private: | |
| 33 | + | std::map<uint64_t, std::shared_ptr<TextRenderCache>> m_cache[INDEXES]; | |
| 34 | + | std::mutex m_mutex[INDEXES]; | |
| 35 | + | }; | |
| 36 | + | ||
| 37 | + | extern TextRender g_text; | |
| 38 | + | ||
| 39 | + | #endif |