LCOV - code coverage report
Current view: top level - source/renderer - TexturedLineRData.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 0 205 0.0 %
Date: 2022-03-08 13:03:03 Functions: 0 4 0.0 %

          Line data    Source code
       1             : /* Copyright (C) 2022 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "TexturedLineRData.h"
      21             : 
      22             : #include "graphics/ShaderProgram.h"
      23             : #include "graphics/Terrain.h"
      24             : #include "maths/Frustum.h"
      25             : #include "maths/MathUtil.h"
      26             : #include "maths/Quaternion.h"
      27             : #include "ps/CStrInternStatic.h"
      28             : #include "renderer/OverlayRenderer.h"
      29             : #include "renderer/Renderer.h"
      30             : #include "simulation2/Simulation2.h"
      31             : #include "simulation2/system/SimContext.h"
      32             : #include "simulation2/components/ICmpWaterManager.h"
      33             : 
      34             : /* Note: this implementation uses g_VBMan directly rather than access it through the nicer VertexArray interface,
      35             :  * because it allows you to work with variable amounts of vertices and indices more easily. New code should prefer
      36             :  * to use VertexArray where possible, though. */
      37             : 
      38           0 : void CTexturedLineRData::Render(
      39             :     Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext,
      40             :     const SOverlayTexturedLine& line, const CShaderProgramPtr& shader)
      41             : {
      42           0 :     if (!m_VB || !m_VBIndices)
      43             :         return; // might have failed to allocate
      44             : 
      45             :     // -- render main line quad strip ----------------------
      46             : 
      47           0 :     const int streamFlags = shader->GetStreamFlags();
      48             : 
      49           0 :     line.m_TextureBase->UploadBackendTextureIfNeeded(deviceCommandContext);
      50           0 :     line.m_TextureMask->UploadBackendTextureIfNeeded(deviceCommandContext);
      51             : 
      52           0 :     m_VBIndices->m_Owner->UploadIfNeeded(deviceCommandContext);
      53             : 
      54           0 :     shader->BindTexture(str_baseTex, line.m_TextureBase->GetBackendTexture());
      55           0 :     shader->BindTexture(str_maskTex, line.m_TextureMask->GetBackendTexture());
      56           0 :     shader->Uniform(str_objectColor, line.m_Color);
      57             : 
      58           0 :     GLsizei stride = sizeof(CTexturedLineRData::SVertex);
      59           0 :     CTexturedLineRData::SVertex* vertexBase =
      60           0 :         reinterpret_cast<CTexturedLineRData::SVertex*>(m_VB->m_Owner->Bind(deviceCommandContext));
      61             : 
      62           0 :     if (streamFlags & STREAM_POS)
      63           0 :         shader->VertexPointer(Renderer::Backend::Format::R32G32B32_SFLOAT, stride, &vertexBase->m_Position[0]);
      64             : 
      65           0 :     if (streamFlags & STREAM_UV0)
      66           0 :         shader->TexCoordPointer(GL_TEXTURE0, Renderer::Backend::Format::R32G32_SFLOAT, stride, &vertexBase->m_UVs[0]);
      67             : 
      68           0 :     if (streamFlags & STREAM_UV1)
      69           0 :         shader->TexCoordPointer(GL_TEXTURE1, Renderer::Backend::Format::R32G32_SFLOAT, stride, &vertexBase->m_UVs[0]);
      70             : 
      71           0 :     shader->AssertPointersBound();
      72             : 
      73           0 :     deviceCommandContext->SetIndexBuffer(m_VBIndices->m_Owner->GetBuffer());
      74           0 :     deviceCommandContext->DrawIndexed(m_VBIndices->m_Index, m_VBIndices->m_Count, 0);
      75             : 
      76           0 :     g_Renderer.GetStats().m_DrawCalls++;
      77           0 :     g_Renderer.GetStats().m_OverlayTris += m_VBIndices->m_Count/3;
      78             : }
      79             : 
      80           0 : void CTexturedLineRData::Update(const SOverlayTexturedLine& line)
      81             : {
      82           0 :     m_VBIndices.Reset();
      83           0 :     m_VB.Reset();
      84             : 
      85           0 :     if (!line.m_SimContext)
      86             :     {
      87           0 :         debug_warn(L"[TexturedLineRData] No SimContext set for textured overlay line, cannot render (no terrain data)");
      88           0 :         return;
      89             :     }
      90             : 
      91           0 :     float v = 0.f;
      92           0 :     std::vector<SVertex> vertices;
      93           0 :     std::vector<u16> indices;
      94             : 
      95           0 :     const size_t n = line.m_Coords.size(); // number of line points
      96           0 :     bool closed = line.m_Closed;
      97             : 
      98           0 :     ENSURE(n >= 2); // minimum needed to avoid errors (also minimum value to make sense, can't draw a line between 1 point)
      99             : 
     100             :     // In each iteration, p1 is the position of vertex i, p0 is i-1, p2 is i+1.
     101             :     // To avoid slightly expensive terrain computations we cycle these around and
     102             :     // recompute p2 at the end of each iteration.
     103             : 
     104           0 :     CVector3D p0;
     105           0 :     CVector3D p1(line.m_Coords[0].X, 0, line.m_Coords[0].Y);
     106           0 :     CVector3D p2(line.m_Coords[1].X, 0, line.m_Coords[1].Y);
     107             : 
     108           0 :     if (closed)
     109             :         // grab the ending point so as to close the loop
     110           0 :         p0 = CVector3D(line.m_Coords[n - 1].X, 0, line.m_Coords[n - 1].Y);
     111             :     else
     112             :         // we don't want to loop around and use the direction towards the other end of the line, so create an artificial p0 that
     113             :         // extends the p2 -> p1 direction, and use that point instead
     114           0 :         p0 = p1 + (p1 - p2);
     115             : 
     116           0 :     bool p1floating = false;
     117           0 :     bool p2floating = false;
     118             : 
     119             :     // Compute terrain heights, clamped to the water height (and remember whether
     120             :     // each point was floating on water, for normal computation later)
     121             : 
     122             :     // TODO: if we ever support more than one water level per map, recompute this per point
     123           0 :     CmpPtr<ICmpWaterManager> cmpWaterManager(*line.m_SimContext, SYSTEM_ENTITY);
     124           0 :     float w = cmpWaterManager ? cmpWaterManager->GetExactWaterLevel(p0.X, p0.Z) : 0.f;
     125             : 
     126           0 :     const CTerrain& terrain = line.m_SimContext->GetTerrain();
     127             : 
     128           0 :     p0.Y = terrain.GetExactGroundLevel(p0.X, p0.Z);
     129           0 :     if (p0.Y < w)
     130           0 :         p0.Y = w;
     131             : 
     132           0 :     p1.Y = terrain.GetExactGroundLevel(p1.X, p1.Z);
     133           0 :     if (p1.Y < w)
     134             :     {
     135           0 :         p1.Y = w;
     136           0 :         p1floating = true;
     137             :     }
     138             : 
     139           0 :     p2.Y = terrain.GetExactGroundLevel(p2.X, p2.Z);
     140           0 :     if (p2.Y < w)
     141             :     {
     142           0 :         p2.Y = w;
     143           0 :         p2floating = true;
     144             :     }
     145             : 
     146           0 :     for (size_t i = 0; i < n; ++i)
     147             :     {
     148             :         // For vertex i, compute bisector of lines (i-1)..(i) and (i)..(i+1)
     149             :         // perpendicular to terrain normal
     150             : 
     151             :         // Normal is vertical if on water, else computed from terrain
     152           0 :         CVector3D norm;
     153           0 :         if (p1floating)
     154             :             norm = CVector3D(0, 1, 0);
     155             :         else
     156           0 :             norm = terrain.CalcExactNormal(p1.X, p1.Z);
     157             : 
     158           0 :         CVector3D b = ((p1 - p0).Normalized() + (p2 - p1).Normalized()).Cross(norm);
     159             : 
     160             :         // Adjust bisector length to match the line thickness, along the line's width
     161           0 :         float l = b.Dot((p2 - p1).Normalized().Cross(norm));
     162           0 :         if (fabs(l) > 0.000001f) // avoid unlikely divide-by-zero
     163           0 :             b *= line.m_Thickness / l;
     164             : 
     165             :         // Push vertices and indices for each quad in GL_TRIANGLES order. The two triangles of each quad are indexed using
     166             :         // the winding orders (BR, BL, TR) and (TR, BL, TL) (where BR is bottom-right of this iteration's quad, TR top-right etc).
     167           0 :         SVertex vertex1(p1 + b + norm*OverlayRenderer::OVERLAY_VOFFSET, 0.f, v);
     168           0 :         SVertex vertex2(p1 - b + norm*OverlayRenderer::OVERLAY_VOFFSET, 1.f, v);
     169           0 :         vertices.push_back(vertex1);
     170           0 :         vertices.push_back(vertex2);
     171             : 
     172           0 :         u16 vertexCount = static_cast<u16>(vertices.size());
     173           0 :         u16 index1 = vertexCount - 2; // index of vertex1 in this iteration (TR of this quad)
     174           0 :         u16 index2 = vertexCount - 1; // index of the vertex2 in this iteration (TL of this quad)
     175             : 
     176           0 :         if (i == 0)
     177             :         {
     178             :             // initial two vertices to continue building triangles from (n must be >= 2 for this to work)
     179           0 :             indices.push_back(index1);
     180           0 :             indices.push_back(index2);
     181             :         }
     182             :         else
     183             :         {
     184           0 :             u16 index1Prev = vertexCount - 4; // index of the vertex1 in the previous iteration (BR of this quad)
     185           0 :             u16 index2Prev = vertexCount - 3; // index of the vertex2 in the previous iteration (BL of this quad)
     186           0 :             ENSURE(index1Prev < vertexCount);
     187           0 :             ENSURE(index2Prev < vertexCount);
     188             :             // Add two corner points from last iteration and join with one of our own corners to create triangle 1
     189             :             // (don't need to do this if i == 1 because i == 0 are the first two ones, they don't need to be copied)
     190           0 :             if (i > 1)
     191             :             {
     192           0 :                 indices.push_back(index1Prev);
     193           0 :                 indices.push_back(index2Prev);
     194             :             }
     195           0 :             indices.push_back(index1); // complete triangle 1
     196             : 
     197             :             // create triangle 2, specifying the adjacent side's vertices in the opposite order from triangle 1
     198           0 :             indices.push_back(index1);
     199           0 :             indices.push_back(index2Prev);
     200           0 :             indices.push_back(index2);
     201             :         }
     202             : 
     203             :         // alternate V coordinate for debugging
     204           0 :         v = 1 - v;
     205             : 
     206             :         // cycle the p's and compute the new p2
     207           0 :         p0 = p1;
     208           0 :         p1 = p2;
     209           0 :         p1floating = p2floating;
     210             : 
     211             :         // if in closed mode, wrap around the coordinate array for p2 -- otherwise, extend linearly
     212           0 :         if (!closed && i == n-2)
     213             :             // next iteration is the last point of the line, so create an artificial p2 that extends the p0 -> p1 direction
     214           0 :             p2 = p1 + (p1 - p0);
     215             :         else
     216           0 :             p2 = CVector3D(line.m_Coords[(i + 2) % n].X, 0, line.m_Coords[(i + 2) % n].Y);
     217             : 
     218           0 :         p2.Y = terrain.GetExactGroundLevel(p2.X, p2.Z);
     219           0 :         if (p2.Y < w)
     220             :         {
     221           0 :             p2.Y = w;
     222           0 :             p2floating = true;
     223             :         }
     224             :         else
     225             :             p2floating = false;
     226             :     }
     227             : 
     228           0 :     if (closed)
     229             :     {
     230             :         // close the path
     231           0 :         if (n % 2 == 0)
     232             :         {
     233           0 :             u16 vertexCount = static_cast<u16>(vertices.size());
     234           0 :             indices.push_back(vertexCount - 2);
     235           0 :             indices.push_back(vertexCount - 1);
     236           0 :             indices.push_back(0);
     237             : 
     238           0 :             indices.push_back(0);
     239           0 :             indices.push_back(vertexCount - 1);
     240           0 :             indices.push_back(1);
     241             :         }
     242             :         else
     243             :         {
     244             :             // add two vertices to have the good UVs for the last quad
     245           0 :             SVertex vertex1(vertices[0].m_Position, 0.f, 1.f);
     246           0 :             SVertex vertex2(vertices[1].m_Position, 1.f, 1.f);
     247           0 :             vertices.push_back(vertex1);
     248           0 :             vertices.push_back(vertex2);
     249             : 
     250           0 :             u16 vertexCount = static_cast<u16>(vertices.size());
     251           0 :             indices.push_back(vertexCount - 4);
     252           0 :             indices.push_back(vertexCount - 3);
     253           0 :             indices.push_back(vertexCount - 2);
     254             : 
     255           0 :             indices.push_back(vertexCount - 2);
     256           0 :             indices.push_back(vertexCount - 3);
     257           0 :             indices.push_back(vertexCount - 1);
     258             :         }
     259             :     }
     260             :     else
     261             :     {
     262             :         // Create start and end caps. On either end, this is done by taking the centroid between the last and second-to-last pair of
     263             :         // vertices that was generated along the path (i.e. the vertex1's and vertex2's from above), taking a directional vector
     264             :         // between them, and drawing the line cap in the plane given by the two butt-end corner points plus said vector.
     265           0 :         std::vector<u16> capIndices;
     266           0 :         std::vector<SVertex> capVertices;
     267             : 
     268             :         // create end cap
     269           0 :         CreateLineCap(
     270             :             line,
     271             :             // the order of these vertices is important here, swapping them produces caps at the wrong side
     272           0 :             vertices[vertices.size()-2].m_Position, // top-right vertex of last quad
     273           0 :             vertices[vertices.size()-1].m_Position, // top-left vertex of last quad
     274             :             // directional vector between centroids of last vertex pair and second-to-last vertex pair
     275           0 :             (Centroid(vertices[vertices.size()-2], vertices[vertices.size()-1]) - Centroid(vertices[vertices.size()-4], vertices[vertices.size()-3])).Normalized(),
     276           0 :             line.m_EndCapType,
     277             :             capVertices,
     278             :             capIndices
     279             :         );
     280             : 
     281           0 :         for (unsigned i = 0; i < capIndices.size(); i++)
     282           0 :             capIndices[i] += static_cast<u16>(vertices.size());
     283             : 
     284           0 :         vertices.insert(vertices.end(), capVertices.begin(), capVertices.end());
     285           0 :         indices.insert(indices.end(), capIndices.begin(), capIndices.end());
     286             : 
     287           0 :         capIndices.clear();
     288           0 :         capVertices.clear();
     289             : 
     290             :         // create start cap
     291           0 :         CreateLineCap(
     292             :             line,
     293             :             // the order of these vertices is important here, swapping them produces caps at the wrong side
     294           0 :             vertices[1].m_Position,
     295           0 :             vertices[0].m_Position,
     296             :             // directional vector between centroids of first vertex pair and second vertex pair
     297           0 :             (Centroid(vertices[1], vertices[0]) - Centroid(vertices[3], vertices[2])).Normalized(),
     298           0 :             line.m_StartCapType,
     299             :             capVertices,
     300             :             capIndices
     301             :         );
     302             : 
     303           0 :         for (unsigned i = 0; i < capIndices.size(); i++)
     304           0 :             capIndices[i] += static_cast<u16>(vertices.size());
     305             : 
     306           0 :         vertices.insert(vertices.end(), capVertices.begin(), capVertices.end());
     307           0 :         indices.insert(indices.end(), capIndices.begin(), capIndices.end());
     308             :     }
     309             : 
     310           0 :     if (vertices.empty() || indices.empty())
     311           0 :         return;
     312             : 
     313             :     // Indices for triangles, so must be multiple of 3.
     314           0 :     ENSURE(indices.size() % 3 == 0);
     315             : 
     316           0 :     m_BoundingBox = CBoundingBoxAligned();
     317           0 :     for (const SVertex& vertex : vertices)
     318           0 :         m_BoundingBox += vertex.m_Position;
     319             : 
     320           0 :     m_VB = g_VBMan.AllocateChunk(
     321           0 :         sizeof(SVertex), vertices.size(), Renderer::Backend::GL::CBuffer::Type::VERTEX, false);
     322           0 :     if (m_VB) // allocation might fail (e.g. due to too many vertices)
     323             :     {
     324           0 :         m_VB->m_Owner->UpdateChunkVertices(m_VB.Get(), &vertices[0]); // copy data into VBO
     325             : 
     326           0 :         for (size_t k = 0; k < indices.size(); ++k)
     327           0 :             indices[k] += static_cast<u16>(m_VB->m_Index);
     328             : 
     329           0 :         m_VBIndices = g_VBMan.AllocateChunk(
     330           0 :             sizeof(u16), indices.size(), Renderer::Backend::GL::CBuffer::Type::INDEX, false);
     331           0 :         if (m_VBIndices)
     332           0 :             m_VBIndices->m_Owner->UpdateChunkVertices(m_VBIndices.Get(), &indices[0]);
     333             :     }
     334             : 
     335             : }
     336             : 
     337           0 : void CTexturedLineRData::CreateLineCap(const SOverlayTexturedLine& line, const CVector3D& corner1, const CVector3D& corner2,
     338             :     const CVector3D& lineDirectionNormal, SOverlayTexturedLine::LineCapType endCapType, std::vector<SVertex>& verticesOut,
     339             :     std::vector<u16>& indicesOut)
     340             : {
     341           0 :     if (endCapType == SOverlayTexturedLine::LINECAP_FLAT)
     342           0 :         return; // no action needed, this is the default
     343             : 
     344             :     // When not in closed mode, we've created artificial points for the start- and endpoints that extend the line in the
     345             :     // direction of the first and the last segment, respectively. Thus, we know both the start and endpoints have perpendicular
     346             :     // butt endings, i.e. the end corner vertices on either side of the line extend perpendicularly from the segment direction.
     347             :     // That is to say, when viewed from the top, we will have something like
     348             :     //                                                 .
     349             :     //  this:                     and not like this:  /|
     350             :     //         ----+                                 / |
     351             :     //             |                                /  .
     352             :     //             |                                  /
     353             :     //         ----+                                 /
     354             :     //
     355             : 
     356           0 :     int roundCapPoints = 8; // amount of points to sample along the semicircle for rounded caps (including corner points)
     357           0 :     float radius = line.m_Thickness;
     358             : 
     359           0 :     CVector3D centerPoint = (corner1 + corner2) * 0.5f;
     360           0 :     SVertex centerVertex(centerPoint, 0.5f, 0.5f);
     361           0 :     u16 indexOffset = static_cast<u16>(verticesOut.size()); // index offset in verticesOut from where we start adding our vertices
     362             : 
     363           0 :     switch (endCapType)
     364             :     {
     365           0 :     case SOverlayTexturedLine::LINECAP_SHARP:
     366           0 :         {
     367           0 :             roundCapPoints = 3; // creates only one point directly ahead
     368           0 :             radius *= 1.5f; // make it a bit sharper (note that we don't use the radius for the butt-end corner points so it should be ok)
     369           0 :             centerVertex.m_UVs[0] = 0.480f; // slight visual correction to make the texture match up better at the corner points
     370             :         }
     371           0 :         FALLTHROUGH;
     372           0 :     case SOverlayTexturedLine::LINECAP_ROUND:
     373           0 :         {
     374             :             // Draw a rounded line cap in the 3D plane of the line specified by the two corner points and the normal vector of the
     375             :             // line's direction. The terrain normal at the centroid between the two corner points is perpendicular to this plane.
     376             :             // The way this works is by taking a vector from the corner points' centroid to one of the corner points (which is then
     377             :             // of radius length), and rotate it around the terrain normal vector in that centroid. This will rotate the vector in
     378             :             // the line's plane, producing the desired rounded cap.
     379             : 
     380             :             // To please OpenGL's winding order, this angle needs to be negated depending on whether we start rotating from
     381             :             // the (center -> corner1) or (center -> corner2) vector. For the (center -> corner2) vector, we apparently need to use
     382             :             // the negated angle.
     383           0 :             float stepAngle = -(float)(M_PI/(roundCapPoints-1));
     384             : 
     385             :             // Push the vertices in triangle fan order (easy to generate GL_TRIANGLES indices for afterwards)
     386             :             // Note that we're manually adding the corner vertices instead of having them be generated by the rotating vector.
     387             :             // This is because we want to support an overly large radius to make the sharp line ending look sharper.
     388           0 :             verticesOut.push_back(centerVertex);
     389           0 :             verticesOut.push_back(SVertex(corner2, 0.f, 0.f));
     390             : 
     391             :             // Get the base vector that we will incrementally rotate in the cap plane to produce the radial sample points.
     392             :             // Normally corner2 - centerPoint would suffice for this since it is of radius length, but we want to support custom
     393             :             // radii to support tuning the 'sharpness' of sharp end caps (see above)
     394           0 :             CVector3D rotationBaseVector = (corner2 - centerPoint).Normalized() * radius;
     395             :             // Calculate the normal vector of the plane in which we're going to be drawing the line cap. This is the vector that
     396             :             // is perpendicular to both baseVector and the 'lineDirectionNormal' vector indicating the direction of the line.
     397             :             // Note that we shouldn't use terrain->CalcExactNormal() here because if the line is being rendered on top of water,
     398             :             // then CalcExactNormal will return the normal vector of the terrain that's underwater (which can be quite funky).
     399           0 :             CVector3D capPlaneNormal = lineDirectionNormal.Cross(rotationBaseVector).Normalized();
     400             : 
     401           0 :             for (int i = 1; i < roundCapPoints - 1; ++i)
     402             :             {
     403             :                 // Rotate the centerPoint -> corner vector by i*stepAngle radians around the cap plane normal at the center point.
     404           0 :                 CQuaternion quatRotation;
     405           0 :                 quatRotation.FromAxisAngle(capPlaneNormal, i * stepAngle);
     406           0 :                 CVector3D worldPos3D = centerPoint + quatRotation.Rotate(rotationBaseVector);
     407             : 
     408             :                 // Let v range from 0 to 1 as we move along the semi-circle, keep u fixed at 0 (i.e. curve the left vertical edge
     409             :                 // of the texture around the edge of the semicircle)
     410           0 :                 float u = 0.f;
     411           0 :                 float v = Clamp((i / static_cast<float>(roundCapPoints - 1)), 0.f, 1.f); // pos, u, v
     412           0 :                 verticesOut.push_back(SVertex(worldPos3D, u, v));
     413             :             }
     414             : 
     415             :             // connect back to the other butt-end corner point to complete the semicircle
     416           0 :             verticesOut.push_back(SVertex(corner1, 0.f, 1.f));
     417             : 
     418             :             // now push indices in GL_TRIANGLES order; vertices[indexOffset] is the center vertex, vertices[indexOffset + 1] is the
     419             :             // first corner point, then a bunch of radial samples, and then at the end we have the other corner point again. So:
     420           0 :             for (int i=1; i < roundCapPoints; ++i)
     421             :             {
     422           0 :                 indicesOut.push_back(indexOffset); // center vertex
     423           0 :                 indicesOut.push_back(indexOffset + i);
     424           0 :                 indicesOut.push_back(indexOffset + i + 1);
     425           0 :             }
     426             :         }
     427           0 :         break;
     428             : 
     429           0 :     case SOverlayTexturedLine::LINECAP_SQUARE:
     430           0 :         {
     431             :             // Extend the (corner1 -> corner2) vector along the direction normal and draw a square line ending consisting of
     432             :             // three triangles (sort of like a triangle fan)
     433             :             // NOTE: The order in which the vertices are pushed out determines the visibility, as they
     434             :             // are rendered only one-sided; the wrong order of vertices will make the cap visible only from the bottom.
     435           0 :             verticesOut.push_back(centerVertex);
     436           0 :             verticesOut.push_back(SVertex(corner2, 0.f, 0.f));
     437           0 :             verticesOut.push_back(SVertex(corner2 + (lineDirectionNormal * (line.m_Thickness)), 0.f, 0.33333f)); // extend butt corner point 2 along the normal vector
     438           0 :             verticesOut.push_back(SVertex(corner1 + (lineDirectionNormal * (line.m_Thickness)), 0.f, 0.66666f)); // extend butt corner point 1 along the normal vector
     439           0 :             verticesOut.push_back(SVertex(corner1, 0.f, 1.0f)); // push butt corner point 1
     440             : 
     441           0 :             for (int i=1; i < 4; ++i)
     442             :             {
     443           0 :                 indicesOut.push_back(indexOffset); // center point
     444           0 :                 indicesOut.push_back(indexOffset + i);
     445           0 :                 indicesOut.push_back(indexOffset + i + 1);
     446           0 :             }
     447             :         }
     448             :         break;
     449             : 
     450             :     default:
     451             :         break;
     452             :     }
     453             : 
     454             : }
     455             : 
     456           0 : bool CTexturedLineRData::IsVisibleInFrustum(const CFrustum& frustum) const
     457             : {
     458           0 :     return frustum.IsBoxVisible(m_BoundingBox);
     459             : }

Generated by: LCOV version 1.13