diff options
author | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2016-06-07 20:45:14 +0900 |
---|---|---|
committer | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2016-06-08 17:12:28 +0900 |
commit | ce72e34b359c323ef7ff96a70500810a9cd8703a (patch) | |
tree | 7436ef509b6670c4331d2e2f0c552728e13ea1f8 /vcl/opengl/LineRenderUtils.cxx | |
parent | 4bdbfdd6426460c562746c67c29a3b5e2bef8563 (diff) |
opengl: batch drawing of polylines
To get polylines to draw in a batch it was necessary to refactor
the polyline code to work with GL_TRIANGLES instead of the previous
used GL_TRIANGLE_STRIP. For this and to make the code easier to
handle a new class was introduced: LineBuilder, which purpose is
to assemble vertices for a polyline (line ends, line joints).
In addition we need to know the line width, anti-aliasing (AA) per
vertex basis (in addition to color, normal and extrusion) so we
can draw many polylines with one draw call. This info is now
stored in Vertex struct which is used when drawing lines or
triangles (fills).
Uploading of vertices has also been changed, previously we
uploaded the vertices with the drawcall. a convention in Modern
OpenGL is however to use VBO (Vertex Buffer Object) for this.
With this we can upload the to the GPU vertices independently
and not upload them if this is not needed (which is currently
not used yet). A vector of Vertex structs is now uploaded to the
GPU using a VBO which is handeled with a new VertexBufferObject
class.
In addition to reduce the ammount of duplicated vertices, we use
a index vector (handled by IndexBufferObject class) where we only
define the indices of the vertex buffer which should be drawn.
Change-Id: I49dc9c6260b459f4f4ce3a5e4fa4c8ad05a7b878
Diffstat (limited to 'vcl/opengl/LineRenderUtils.cxx')
-rw-r--r-- | vcl/opengl/LineRenderUtils.cxx | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/vcl/opengl/LineRenderUtils.cxx b/vcl/opengl/LineRenderUtils.cxx new file mode 100644 index 000000000000..1b7fb9919490 --- /dev/null +++ b/vcl/opengl/LineRenderUtils.cxx @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include "opengl/LineRenderUtils.hxx" + +namespace vcl +{ + +LineBuilder::LineBuilder(std::vector<Vertex>& rVertices, std::vector<GLuint>& rIndices, + SalColor nColor, GLfloat fTransparency, + GLfloat fLineWidth, bool bUseAA) + : mrVertices(rVertices) + , mrIndices(rIndices) + , mR(SALCOLOR_RED(nColor)) + , mG(SALCOLOR_GREEN(nColor)) + , mB(SALCOLOR_BLUE(nColor)) + , mA((1.0f - fTransparency) * 255.0f) + , mfLineWidth(fLineWidth) + , mfLineWidthAndAA(bUseAA ? fLineWidth : -fLineWidth) + , mnInitialIndexSize(rIndices.size()) + , mbIncomplete(false) +{ +} + +void LineBuilder::appendLineSegment(const glm::vec2& rPoint1, const glm::vec2& rNormal1, GLfloat aExtrusion1, + const glm::vec2& rPoint2, const glm::vec2& rNormal2, GLfloat aExtrusion2) +{ + GLuint zero = mrVertices.size(); + + mrVertices.insert(mrVertices.end(), { + Vertex{rPoint1, glm::vec4{mR, mG, mB, mA}, glm::vec4{-rNormal1.x, -rNormal1.y, -aExtrusion1, mfLineWidthAndAA}}, + Vertex{rPoint1, glm::vec4{mR, mG, mB, mA}, glm::vec4{ rNormal1.x, rNormal1.y, aExtrusion1, mfLineWidthAndAA}}, + Vertex{rPoint2, glm::vec4{mR, mG, mB, mA}, glm::vec4{-rNormal2.x, -rNormal2.y, -aExtrusion2, mfLineWidthAndAA}}, + Vertex{rPoint2, glm::vec4{mR, mG, mB, mA}, glm::vec4{ rNormal2.x, rNormal2.y, aExtrusion2, mfLineWidthAndAA}}, + }); + + mrIndices.insert(mrIndices.end(), { + zero + 0, zero + 1, zero + 2, + zero + 2, zero + 1, zero + 3 + }); + +} + +void LineBuilder::appendLine(const glm::vec2& rPoint1, const glm::vec2& rPoint2) +{ + glm::vec2 aLineVector = vcl::vertex::normalize(rPoint2 - rPoint1); + glm::vec2 aNormal = vcl::vertex::perpendicular(aLineVector); + + appendLineSegment(rPoint1, aNormal, 1.0f, + rPoint2, aNormal, 1.0f); +} + +void LineBuilder::appendAndConnectLinePoint(const glm::vec2& rPoint, const glm::vec2& aNormal, GLfloat aExtrusion) +{ + GLuint zero = mrVertices.size(); + + mrVertices.insert(mrVertices.end(), { + Vertex{rPoint, glm::vec4{mR, mG, mB, mA}, glm::vec4{-aNormal.x, -aNormal.y, -aExtrusion, mfLineWidthAndAA}}, + Vertex{rPoint, glm::vec4{mR, mG, mB, mA}, glm::vec4{ aNormal.x, aNormal.y, aExtrusion, mfLineWidthAndAA}}, + }); + + if (mnInitialIndexSize == mrIndices.size()) + { + mrIndices.insert(mrIndices.end(), { + zero + 0, zero + 1 + }); + mbIncomplete = true; + } + else + { + if (mbIncomplete) + { + mrIndices.insert(mrIndices.end(), { + zero + 0, + zero + 0, zero - 1, zero + 1 + }); + mbIncomplete = false; + } + else + { + mrIndices.insert(mrIndices.end(), { + zero - 2, zero - 1, zero + 0, + zero + 0, zero - 1, zero + 1 + }); + } + } +} + +void LineBuilder::appendMiterJoint(glm::vec2 point, glm::vec2 prevLineVector, glm::vec2 nextLineVector) +{ + // With miter join we calculate the extrusion vector by adding normals of + // previous and next line segment. The vector shows the way but we also + // need the length (otherwise the line will be deformed). Length factor is + // calculated as dot product of extrusion vector and one of the normals. + // The value we get is the inverse length (used in the shader): + // length = line_width / dot(extrusionVector, normal) + + glm::vec2 normal(-prevLineVector.y, prevLineVector.x); + + glm::vec2 tangent = vcl::vertex::normalize(nextLineVector + prevLineVector); + glm::vec2 extrusionVector(-tangent.y, tangent.x); + GLfloat length = glm::dot(extrusionVector, normal); + + appendAndConnectLinePoint(point, extrusionVector, length); +} + +void LineBuilder::appendBevelJoint(glm::vec2 point, glm::vec2 prevLineVector, glm::vec2 nextLineVector) +{ + // For bevel join we just add 2 additional vertices and use previous + // line segment normal and next line segment normal as extrusion vector. + // All the magic is done by the fact that we draw triangle strips, so we + // cover the joins correctly. + + glm::vec2 prevNormal = glm::vec2(-prevLineVector.y, prevLineVector.x); + glm::vec2 nextNormal = glm::vec2(-nextLineVector.y, nextLineVector.x); + + appendAndConnectLinePoint(point, prevNormal, 1.0f); + appendAndConnectLinePoint(point, nextNormal, 1.0f); +} + +void LineBuilder::appendRoundJoint(glm::vec2 point, glm::vec2 prevLineVector, glm::vec2 nextLineVector) +{ + // For round join we do a similar thing as in bevel, we add more intermediate + // vertices and add normals to get extrusion vectors in the between the + // both normals. + + // 3 additional extrusion vectors + normals are enough to make most + // line joins look round. Ideally the number of vectors could be + // calculated. + + glm::vec2 prevNormal = glm::vec2(-prevLineVector.y, prevLineVector.x); + glm::vec2 nextNormal = glm::vec2(-nextLineVector.y, nextLineVector.x); + + glm::vec2 middle = vcl::vertex::normalize(prevNormal + nextNormal); + glm::vec2 middleLeft = vcl::vertex::normalize(prevNormal + middle); + glm::vec2 middleRight = vcl::vertex::normalize(middle + nextNormal); + + appendAndConnectLinePoint(point, prevNormal, 1.0f); + appendAndConnectLinePoint(point, middleLeft, 1.0f); + appendAndConnectLinePoint(point, middle, 1.0f); + appendAndConnectLinePoint(point, middleRight, 1.0f); + appendAndConnectLinePoint(point, nextNormal, 1.0f); +} + +void LineBuilder::appendRoundLineCapVertices(const glm::vec2& rPoint1, const glm::vec2& rPoint2) +{ + SAL_CONSTEXPR const int nRoundCapIteration = 12; + + glm::vec2 lineVector = vcl::vertex::normalize(rPoint2 - rPoint1); + glm::vec2 normal = glm::vec2(-lineVector.y, lineVector.x); + glm::vec2 previousRoundNormal = normal; + + for (int nFactor = 1; nFactor <= nRoundCapIteration; nFactor++) + { + float angle = float(nFactor) * (M_PI / float(nRoundCapIteration)); + glm::vec2 roundNormal(normal.x * glm::cos(angle) - normal.y * glm::sin(angle), + normal.x * glm::sin(angle) + normal.y * glm::cos(angle)); + + appendLineSegment(rPoint1, previousRoundNormal, 1.0f, + rPoint1, roundNormal, 1.0f); + previousRoundNormal = roundNormal; + } +} + +void LineBuilder::appendSquareLineCapVertices(const glm::vec2& rPoint1, const glm::vec2& rPoint2) +{ + glm::vec2 lineVector = vcl::vertex::normalize(rPoint2 - rPoint1); + glm::vec2 normal = glm::vec2(-lineVector.y, lineVector.x); + + glm::vec2 extrudedPoint = rPoint1 + -lineVector * (mfLineWidth / 2.0f); + + appendLineSegment(extrudedPoint, normal, 1.0f, + rPoint1, normal, 1.0f); +} + +} // end vcl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |