24eae7bd70
Change-Id: I69247498e13331f6ef84afeb242479f8fb1178a8 Reviewed-on: https://gerrit.libreoffice.org/60068 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
188 lines
7.1 KiB
C++
188 lines
7.1 KiB
C++
/* -*- 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,
|
|
Color nColor, GLfloat fTransparency,
|
|
GLfloat fLineWidth, bool bUseAA)
|
|
: mrVertices(rVertices)
|
|
, mrIndices(rIndices)
|
|
, mR(nColor.GetRed())
|
|
, mG(nColor.GetGreen())
|
|
, mB(nColor.GetBlue())
|
|
, 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(), {
|
|
{rPoint1, glm::vec4{mR, mG, mB, mA}, glm::vec4{-rNormal1.x, -rNormal1.y, -aExtrusion1, mfLineWidthAndAA}},
|
|
{rPoint1, glm::vec4{mR, mG, mB, mA}, glm::vec4{ rNormal1.x, rNormal1.y, aExtrusion1, mfLineWidthAndAA}},
|
|
{rPoint2, glm::vec4{mR, mG, mB, mA}, glm::vec4{-rNormal2.x, -rNormal2.y, -aExtrusion2, mfLineWidthAndAA}},
|
|
{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(), {
|
|
{rPoint, glm::vec4{mR, mG, mB, mA}, glm::vec4{-aNormal.x, -aNormal.y, -aExtrusion, mfLineWidthAndAA}},
|
|
{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 const& point, const glm::vec2& prevLineVector,
|
|
glm::vec2 const& 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 const& point, const glm::vec2& prevLineVector,
|
|
const 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 const& point, const glm::vec2& prevLineVector,
|
|
const 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)
|
|
{
|
|
constexpr 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: */
|