diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 6a78c0a73..a0840366e 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -53,6 +53,13 @@ void gammaCorrectColor(Colorf &c) } } +Colorf gammaCorrectColor(const Colorf &c) +{ + Colorf r = c; + gammaCorrectColor(r); + return r; +} + void unGammaCorrectColor(Colorf &c) { if (isGammaCorrect()) @@ -63,6 +70,13 @@ void unGammaCorrectColor(Colorf &c) } } +Colorf unGammaCorrectColor(const Colorf &c) +{ + Colorf r = c; + unGammaCorrectColor(r); + return r; +} + love::Type Graphics::type("graphics", &Module::type); Graphics::Graphics() @@ -92,7 +106,7 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawRequest & if (req.primitiveMode != state.primitiveMode || req.formats[0] != state.formats[0] || req.formats[1] != state.formats[1] || ((req.indexMode != TriangleIndexMode::NONE) != (state.indexCount > 0)) - || req.texture != state.texture) + || req.texture != state.texture || req.textureHandle != state.textureHandle) { shouldflush = true; } @@ -156,6 +170,7 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawRequest & state.formats[0] = req.formats[0]; state.formats[1] = req.formats[1]; state.texture = req.texture; + state.textureHandle = req.textureHandle; } if (shouldresize) diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 6b63c9c06..390aaf938 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -66,6 +66,9 @@ void gammaCorrectColor(Colorf &c); **/ void unGammaCorrectColor(Colorf &c); +Colorf gammaCorrectColor(const Colorf &c); +Colorf unGammaCorrectColor(const Colorf &c); + class Graphics : public Module { public: @@ -236,6 +239,10 @@ public: int vertexCount = 0; Texture *texture = nullptr; + // FIXME: This is only needed for fonts. We should just change fonts to + // use love.graphics Images instead of raw OpenGL textures. + ptrdiff_t textureHandle = 0; + StreamDrawRequest() { // VS2013 can't initialize arrays in the above manner... @@ -455,6 +462,7 @@ protected: vertex::PrimitiveMode primitiveMode; vertex::CommonFormat formats[2]; StrongRef texture; + ptrdiff_t textureHandle = 0; int vertexCount; int indexCount; }; diff --git a/src/modules/graphics/opengl/Font.cpp b/src/modules/graphics/opengl/Font.cpp index 67e33e843..4ab23f50c 100644 --- a/src/modules/graphics/opengl/Font.cpp +++ b/src/modules/graphics/opengl/Font.cpp @@ -141,6 +141,9 @@ GLenum Font::getTextureFormat(FontType fontType, GLenum *internalformat) const void Font::createTexture() { + auto gfx = Module::getInstance(Module::M_GRAPHICS); + gfx->flushStreamDraws(); + OpenGL::TempDebugGroup debuggroup("Font create texture"); size_t bpp = fontType == FONT_TRUETYPE ? 2 : 4; @@ -413,7 +416,7 @@ float Font::getHeight() const return (float) height; } -std::vector Font::generateVertices(const ColoredCodepoints &codepoints, std::vector &vertices, float extra_spacing, Vector offset, TextInfo *info) +std::vector Font::generateVertices(const ColoredCodepoints &codepoints, const Colorf &constantcolor, std::vector &vertices, float extra_spacing, Vector offset, TextInfo *info) { // Spacing counter and newline handling. float dx = offset.x; @@ -431,9 +434,9 @@ std::vector Font::generateVertices(const ColoredCodepoints &c uint32 prevglyph = 0; - Color initialcolor(255, 255, 255, 255); + Colorf linearconstantcolor = gammaCorrectColor(constantcolor); - Color curcolor = initialcolor; + Color curcolor = toColor(constantcolor); int curcolori = -1; int ncolors = (int) codepoints.colors.size(); @@ -443,7 +446,11 @@ std::vector Font::generateVertices(const ColoredCodepoints &c if (curcolori + 1 < ncolors && codepoints.colors[curcolori + 1].index == i) { - curcolor = toColor(codepoints.colors[++curcolori].color); + Colorf color = gammaCorrectColor(codepoints.colors[++curcolori].color); + color *= linearconstantcolor; + unGammaCorrectColor(color); + + curcolor = toColor(color); } if (g == '\n') @@ -476,7 +483,7 @@ std::vector Font::generateVertices(const ColoredCodepoints &c vertices.resize(vertstartsize); prevglyph = 0; curcolori = -1; - curcolor = initialcolor; + curcolor = toColor(constantcolor); continue; } @@ -516,7 +523,6 @@ std::vector Font::generateVertices(const ColoredCodepoints &c dx = floorf(dx + extra_spacing); prevglyph = g; - } const auto drawsort = [](const DrawCommand &a, const DrawCommand &b) -> bool @@ -535,21 +541,14 @@ std::vector Font::generateVertices(const ColoredCodepoints &c if (info != nullptr) { - info->width = maxwidth - offset.x;; + info->width = maxwidth - offset.x; info->height = (int) dy + (dx > 0.0f ? floorf(getHeight() * getLineHeight() + 0.5f) : 0) - offset.y; } return commands; } -std::vector Font::generateVertices(const std::string &text, std::vector &vertices, float extra_spacing, Vector offset, TextInfo *info) -{ - ColoredCodepoints codepoints; - getCodepointsFromString(text, codepoints.cps); - return generateVertices(codepoints, vertices, extra_spacing, offset, info); -} - -std::vector Font::generateVerticesFormatted(const ColoredCodepoints &text, float wrap, AlignMode align, std::vector &vertices, TextInfo *info) +std::vector Font::generateVerticesFormatted(const ColoredCodepoints &text, const Colorf &constantcolor, float wrap, AlignMode align, std::vector &vertices, TextInfo *info) { wrap = std::max(wrap, 0.0f); @@ -598,7 +597,7 @@ std::vector Font::generateVerticesFormatted(const ColoredCode break; } - std::vector newcommands = generateVertices(line, vertices, extraspacing, offset); + std::vector newcommands = generateVertices(line, constantcolor, vertices, extraspacing, offset); if (!newcommands.empty()) { @@ -633,98 +632,55 @@ std::vector Font::generateVerticesFormatted(const ColoredCode if (cacheid != textureCacheID) { vertices.clear(); - drawcommands = generateVerticesFormatted(text, wrap, align, vertices); + drawcommands = generateVerticesFormatted(text, constantcolor, wrap, align, vertices); } return drawcommands; } -void Font::drawVertices(const std::vector &drawcommands, bool bufferedvertices) -{ - // Vertex attribute pointers need to be set before calling this function. - // This assumes that the attribute pointers are constant for all vertices. - - int totalverts = 0; - for (const DrawCommand &cmd : drawcommands) - totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts); - - if ((size_t) totalverts / 4 > quadIndices.getSize()) - quadIndices = QuadIndices((size_t) totalverts / 4); - - gl.prepareDraw(); - - const GLenum gltype = quadIndices.getType(); - const size_t elemsize = quadIndices.getElementSize(); - - // We only get indices from the index buffer if we're also using vertex - // buffers, because at least one graphics driver (the one for Kepler nvidia - // GPUs in OS X 10.11) fails to render geometry if an index buffer is used - // with client-side vertex arrays. - if (bufferedvertices) - quadIndices.getBuffer()->bind(); - else - gl.bindBuffer(BUFFER_INDEX, 0); - - // We need a separate draw call for every section of the text which uses a - // different texture than the previous section. - for (const DrawCommand &cmd : drawcommands) - { - GLsizei count = (cmd.vertexcount / 4) * 6; - size_t offset = (cmd.startvertex / 4) * 6 * elemsize; - - // TODO: Use glDrawElementsBaseVertex when supported? - gl.bindTextureToUnit(cmd.texture, 0, false); - - if (bufferedvertices) - gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getPointer(offset)); - else - gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getIndices(offset)); - } -} - -void Font::printv(const Matrix4 &t, const std::vector &drawcommands, const std::vector &vertices) +void Font::printv(graphics::Graphics *gfx, const Matrix4 &t, const std::vector &drawcommands, const std::vector &vertices) { if (vertices.empty() || drawcommands.empty()) return; - auto gfx = Module::getInstance(Module::M_GRAPHICS); + Matrix4 m(gfx->getTransform(), t); - gfx->flushStreamDraws(); + for (const DrawCommand &cmd : drawcommands) + { + Graphics::StreamDrawRequest req; + req.formats[0] = vertex::CommonFormat::XYf_STus_RGBAub; + req.indexMode = vertex::TriangleIndexMode::QUADS; + req.vertexCount = cmd.vertexcount; + req.textureHandle = cmd.texture; - OpenGL::TempDebugGroup debuggroup("Font print"); + Graphics::StreamVertexData data = gfx->requestStreamDraw(req); + GlyphVertex *vertexdata = (GlyphVertex *) data.stream[0]; - Graphics::TempTransform transform(gfx, t); - - gl.bindBuffer(BUFFER_VERTEX, 0); - glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].x); - glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(GlyphVertex), &vertices[0].s); - glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GlyphVertex), &vertices[0].color.r); - - gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR); - - drawVertices(drawcommands, false); + memcpy(vertexdata, &vertices[cmd.startvertex], sizeof(GlyphVertex) * cmd.vertexcount); + m.transform(vertexdata, &vertices[cmd.startvertex], cmd.vertexcount); + } } -void Font::print(const std::vector &text, const Matrix4 &m) +void Font::print(graphics::Graphics *gfx, const std::vector &text, const Matrix4 &m, const Colorf &constantcolor) { ColoredCodepoints codepoints; getCodepointsFromString(text, codepoints); std::vector vertices; - std::vector drawcommands = generateVertices(codepoints, vertices); + std::vector drawcommands = generateVertices(codepoints, constantcolor, vertices); - printv(m, drawcommands, vertices); + printv(gfx, m, drawcommands, vertices); } -void Font::printf(const std::vector &text, float wrap, AlignMode align, const Matrix4 &m) +void Font::printf(graphics::Graphics *gfx, const std::vector &text, float wrap, AlignMode align, const Matrix4 &m, const Colorf &constantcolor) { ColoredCodepoints codepoints; getCodepointsFromString(text, codepoints); std::vector vertices; - std::vector drawcommands = generateVerticesFormatted(codepoints, wrap, align, vertices); + std::vector drawcommands = generateVerticesFormatted(codepoints, constantcolor, wrap, align, vertices); - printv(m, drawcommands, vertices); + printv(gfx, m, drawcommands, vertices); } int Font::getWidth(const std::string &str) diff --git a/src/modules/graphics/opengl/Font.h b/src/modules/graphics/opengl/Font.h index 62d6e3ac1..bdfb8a8bb 100644 --- a/src/modules/graphics/opengl/Font.h +++ b/src/modules/graphics/opengl/Font.h @@ -35,6 +35,7 @@ #include "font/Rasterizer.h" #include "graphics/Texture.h" #include "graphics/Volatile.h" +#include "graphics/vertex.h" #include "GLBuffer.h" #include "OpenGL.h" @@ -43,6 +44,9 @@ namespace love { namespace graphics { + +class Graphics; + namespace opengl { @@ -53,6 +57,7 @@ public: static love::Type type; typedef std::vector Codepoints; + typedef vertex::XYf_STus_RGBAub GlyphVertex; enum AlignMode { @@ -81,13 +86,6 @@ public: std::vector colors; }; - struct GlyphVertex - { - float x, y; - uint16 s, t; - Color color; - }; - struct TextInfo { int width; @@ -106,21 +104,20 @@ public: virtual ~Font(); - std::vector generateVertices(const ColoredCodepoints &codepoints, std::vector &vertices, float extra_spacing = 0.0f, Vector offset = {}, TextInfo *info = nullptr); - std::vector generateVertices(const std::string &text, std::vector &vertices, float extra_spacing = 0.0f, Vector offset = Vector(), TextInfo *info = nullptr); + std::vector generateVertices(const ColoredCodepoints &codepoints, const Colorf &constantColor, std::vector &vertices, + float extra_spacing = 0.0f, Vector offset = {}, TextInfo *info = nullptr); - std::vector generateVerticesFormatted(const ColoredCodepoints &text, float wrap, AlignMode align, std::vector &vertices, TextInfo *info = nullptr); - - void drawVertices(const std::vector &drawcommands, bool bufferedvertices); + std::vector generateVerticesFormatted(const ColoredCodepoints &text, const Colorf &constantColor, float wrap, AlignMode align, + std::vector &vertices, TextInfo *info = nullptr); static void getCodepointsFromString(const std::string &str, Codepoints &codepoints); static void getCodepointsFromString(const std::vector &strs, ColoredCodepoints &codepoints); /** - * Draws the text at the designated position with a transformation applied. + * Draws the specified text. **/ - void print(const std::vector &text, const Matrix4 &m); - void printf(const std::vector &text, float wrap, AlignMode align, const Matrix4 &m); + void print(graphics::Graphics *gfx, const std::vector &text, const Matrix4 &m, const Colorf &constantColor); + void printf(graphics::Graphics *gfx, const std::vector &text, float wrap, AlignMode align, const Matrix4 &m, const Colorf &constantColor); /** * Returns the height of the font. @@ -218,7 +215,7 @@ private: const Glyph &addGlyph(uint32 glyph); const Glyph &findGlyph(uint32 glyph); float getKerning(uint32 leftglyph, uint32 rightglyph); - void printv(const Matrix4 &t, const std::vector &drawcommands, const std::vector &vertices); + void printv(graphics::Graphics *gfx, const Matrix4 &t, const std::vector &drawcommands, const std::vector &vertices); std::vector> rasterizers; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index ec62a39a6..d2bef6272 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -468,7 +468,10 @@ void Graphics::flushStreamDraws() gl.prepareDraw(); - gl.bindTextureToUnit(sbstate.texture, 0, false); + if (sbstate.textureHandle != 0) + gl.bindTextureToUnit((GLuint) sbstate.textureHandle, 0, false); + else + gl.bindTextureToUnit(sbstate.texture, 0, false); gl.useVertexAttribArrays(attribs); @@ -1730,7 +1733,7 @@ void Graphics::print(const std::vector &str, const Matrix4 DisplayState &state = states.back(); if (state.font.get() != nullptr) - state.font->print(str, m); + state.font->print(this, str, m, state.color); } void Graphics::printf(const std::vector &str, float wrap, Font::AlignMode align, const Matrix4 &m) @@ -1740,7 +1743,7 @@ void Graphics::printf(const std::vector &str, float wrap, F DisplayState &state = states.back(); if (state.font.get() != nullptr) - state.font->printf(str, wrap, align, m); + state.font->printf(this, str, wrap, align, m, state.color); } /** diff --git a/src/modules/graphics/opengl/Text.cpp b/src/modules/graphics/opengl/Text.cpp index a00fd1190..d97aee49a 100644 --- a/src/modules/graphics/opengl/Text.cpp +++ b/src/modules/graphics/opengl/Text.cpp @@ -36,6 +36,7 @@ love::Type Text::type("Text", &Drawable::type); Text::Text(Font *font, const std::vector &text) : font(font) , vbo(nullptr) + , quadIndices(20) , vert_offset(0) , texture_cache_id((uint32) -1) { @@ -114,11 +115,13 @@ void Text::addTextData(const TextData &t) Font::TextInfo text_info; + Colorf constantcolor = Colorf(1.0f, 1.0f, 1.0f, 1.0f); + // We only have formatted text if the align mode is valid. if (t.align == Font::ALIGN_MAX_ENUM) - new_commands = font->generateVertices(t.codepoints, vertices, 0.0f, Vector(0.0f, 0.0f), &text_info); + new_commands = font->generateVertices(t.codepoints, constantcolor, vertices, 0.0f, Vector(0.0f, 0.0f), &text_info); else - new_commands = font->generateVerticesFormatted(t.codepoints, t.wrap, t.align, vertices, &text_info); + new_commands = font->generateVerticesFormatted(t.codepoints, constantcolor, t.wrap, t.align, vertices, &text_info); if (t.use_matrix) t.matrix.transform(&vertices[0], &vertices[0], (int) vertices.size()); @@ -221,24 +224,48 @@ void Text::draw(Graphics *gfx, const Matrix4 &m) if (font->getTextureCacheID() != texture_cache_id) regenerateVertices(); + int totalverts = 0; + for (const Font::DrawCommand &cmd : draw_commands) + totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts); + + if ((size_t) totalverts / 4 > quadIndices.getSize()) + quadIndices = QuadIndices((size_t) totalverts / 4); + const size_t pos_offset = offsetof(Font::GlyphVertex, x); const size_t tex_offset = offsetof(Font::GlyphVertex, s); const size_t color_offset = offsetof(Font::GlyphVertex, color.r); const size_t stride = sizeof(Font::GlyphVertex); + const GLenum gltype = quadIndices.getType(); + const size_t elemsize = quadIndices.getElementSize(); + Graphics::TempTransform transform(gfx, m); + gl.prepareDraw(); + vbo->bind(); vbo->unmap(); // Make sure all pending data is flushed to the GPU. - // Font::drawVertices expects AttribPointer calls to be done already. glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, vbo->getPointer(pos_offset)); glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, vbo->getPointer(tex_offset)); glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, vbo->getPointer(color_offset)); gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR); - font->drawVertices(draw_commands, true); + quadIndices.getBuffer()->bind(); + + // We need a separate draw call for every section of the text which uses a + // different texture than the previous section. + for (const Font::DrawCommand &cmd : draw_commands) + { + GLsizei count = (cmd.vertexcount / 4) * 6; + size_t offset = (cmd.startvertex / 4) * 6 * elemsize; + + // TODO: Use glDrawElementsBaseVertex when supported? + gl.bindTextureToUnit(cmd.texture, 0, false); + + gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getPointer(offset)); + } } void Text::setFont(Font *f) diff --git a/src/modules/graphics/opengl/Text.h b/src/modules/graphics/opengl/Text.h index 9e2387eb1..706a567df 100644 --- a/src/modules/graphics/opengl/Text.h +++ b/src/modules/graphics/opengl/Text.h @@ -86,6 +86,7 @@ private: StrongRef font; GLBuffer *vbo; + QuadIndices quadIndices; std::vector draw_commands; diff --git a/src/modules/graphics/vertex.h b/src/modules/graphics/vertex.h index 46396cb90..ba9998870 100644 --- a/src/modules/graphics/vertex.h +++ b/src/modules/graphics/vertex.h @@ -39,13 +39,6 @@ enum BufferType BUFFER_MAX_ENUM }; -struct Vertex -{ - float x, y; - float s, t; - Color color; -}; - namespace vertex { @@ -88,9 +81,9 @@ struct XYf_STf_RGBAub struct XYf_STus_RGBAub { - float x, y; + float x, y; uint16 s, t; - Color color; + Color color; }; size_t getFormatStride(CommonFormat format); @@ -101,5 +94,8 @@ void fillIndices(TriangleIndexMode mode, uint16 vertexStart, uint16 vertexCount, void fillIndices(TriangleIndexMode mode, uint32 vertexStart, uint32 vertexCount, uint32 *indices); } // vertex + +typedef vertex::XYf_STf_RGBAub Vertex; + } // graphics } // love