2023-01-06 01:39:25 +01:00
/* Copyright (C) 2023 Wildfire Games.
2023-07-27 22:54:46 +02:00
* This file is part of 0 A . D .
2013-12-06 01:42:50 +01:00
*
2023-07-27 22:54:46 +02:00
* 0 A . D . is free software : you can redistribute it and / or modify
2013-12-06 01:42:50 +01:00
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 2 of the License , or
* ( at your option ) any later version .
*
2023-07-27 22:54:46 +02:00
* 0 A . D . is distributed in the hope that it will be useful ,
2013-12-06 01:42:50 +01:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2023-07-27 22:54:46 +02:00
* along with 0 A . D . If not , see < http : //www.gnu.org/licenses/>.
2013-12-06 01:42:50 +01:00
*/
# include "precompiled.h"
# include "TexturedLineRData.h"
2021-05-15 19:27:01 +02:00
# include "graphics/ShaderProgram.h"
2013-12-06 01:42:50 +01:00
# include "graphics/Terrain.h"
2021-03-29 19:28:13 +02:00
# include "maths/Frustum.h"
2013-12-06 01:42:50 +01:00
# include "maths/MathUtil.h"
# include "maths/Quaternion.h"
2021-05-18 13:09:54 +02:00
# include "ps/CStrInternStatic.h"
2013-12-06 01:42:50 +01:00
# include "renderer/OverlayRenderer.h"
# include "renderer/Renderer.h"
# include "simulation2/Simulation2.h"
# include "simulation2/system/SimContext.h"
# include "simulation2/components/ICmpWaterManager.h"
/* Note: this implementation uses g_VBMan directly rather than access it through the nicer VertexArray interface,
* because it allows you to work with variable amounts of vertices and indices more easily . New code should prefer
* to use VertexArray where possible , though . */
2023-01-06 01:39:25 +01:00
// static
Renderer : : Backend : : IVertexInputLayout * CTexturedLineRData : : GetVertexInputLayout ( )
{
const uint32_t stride = sizeof ( CTexturedLineRData : : SVertex ) ;
const std : : array < Renderer : : Backend : : SVertexAttributeFormat , 3 > attributes { {
{ Renderer : : Backend : : VertexAttributeStream : : POSITION ,
Renderer : : Backend : : Format : : R32G32B32_SFLOAT ,
offsetof ( CTexturedLineRData : : SVertex , m_Position ) , stride ,
Renderer : : Backend : : VertexAttributeRate : : PER_VERTEX , 0 } ,
{ Renderer : : Backend : : VertexAttributeStream : : UV0 ,
Renderer : : Backend : : Format : : R32G32_SFLOAT ,
offsetof ( CTexturedLineRData : : SVertex , m_UV ) , stride ,
Renderer : : Backend : : VertexAttributeRate : : PER_VERTEX , 0 } ,
{ Renderer : : Backend : : VertexAttributeStream : : UV1 ,
Renderer : : Backend : : Format : : R32G32_SFLOAT ,
offsetof ( CTexturedLineRData : : SVertex , m_UV ) , stride ,
Renderer : : Backend : : VertexAttributeRate : : PER_VERTEX , 0 }
} } ;
return g_Renderer . GetVertexInputLayout ( attributes ) ;
}
2022-02-13 20:30:28 +01:00
void CTexturedLineRData : : Render (
2022-05-09 00:02:46 +02:00
Renderer : : Backend : : IDeviceCommandContext * deviceCommandContext ,
2023-01-06 01:39:25 +01:00
Renderer : : Backend : : IVertexInputLayout * vertexInputLayout ,
2022-05-02 22:57:22 +02:00
const SOverlayTexturedLine & line , Renderer : : Backend : : IShaderProgram * shader )
2013-12-06 01:42:50 +01:00
{
if ( ! m_VB | | ! m_VBIndices )
return ; // might have failed to allocate
// -- render main line quad strip ----------------------
2022-02-13 20:30:28 +01:00
line . m_TextureBase - > UploadBackendTextureIfNeeded ( deviceCommandContext ) ;
line . m_TextureMask - > UploadBackendTextureIfNeeded ( deviceCommandContext ) ;
2022-03-03 08:09:59 +01:00
2022-10-30 01:20:04 +02:00
ENSURE ( ! m_VB - > m_Owner - > GetBuffer ( ) - > IsDynamic ( ) ) ;
ENSURE ( ! m_VBIndices - > m_Owner - > GetBuffer ( ) - > IsDynamic ( ) ) ;
2022-03-03 08:09:59 +01:00
2022-05-02 22:57:22 +02:00
deviceCommandContext - > SetTexture (
shader - > GetBindingSlot ( str_baseTex ) , line . m_TextureBase - > GetBackendTexture ( ) ) ;
deviceCommandContext - > SetTexture (
shader - > GetBindingSlot ( str_maskTex ) , line . m_TextureMask - > GetBackendTexture ( ) ) ;
deviceCommandContext - > SetUniform (
shader - > GetBindingSlot ( str_objectColor ) , line . m_Color . AsFloatArray ( ) ) ;
2013-12-06 01:42:50 +01:00
2023-01-06 01:39:25 +01:00
deviceCommandContext - > SetVertexInputLayout ( vertexInputLayout ) ;
2022-04-23 22:11:14 +02:00
2022-10-09 13:47:34 +02:00
deviceCommandContext - > SetVertexBuffer ( 0 , m_VB - > m_Owner - > GetBuffer ( ) , 0 ) ;
2013-12-06 01:42:50 +01:00
2022-03-03 08:09:59 +01:00
deviceCommandContext - > SetIndexBuffer ( m_VBIndices - > m_Owner - > GetBuffer ( ) ) ;
deviceCommandContext - > DrawIndexed ( m_VBIndices - > m_Index , m_VBIndices - > m_Count , 0 ) ;
2013-12-06 01:42:50 +01:00
g_Renderer . GetStats ( ) . m_DrawCalls + + ;
2016-11-23 14:02:58 +01:00
g_Renderer . GetStats ( ) . m_OverlayTris + = m_VBIndices - > m_Count / 3 ;
2013-12-06 01:42:50 +01:00
}
void CTexturedLineRData : : Update ( const SOverlayTexturedLine & line )
{
2022-01-10 17:51:43 +01:00
m_VBIndices . Reset ( ) ;
m_VB . Reset ( ) ;
2013-12-06 01:42:50 +01:00
if ( ! line . m_SimContext )
{
debug_warn ( L " [TexturedLineRData] No SimContext set for textured overlay line, cannot render (no terrain data) " ) ;
return ;
}
float v = 0.f ;
std : : vector < SVertex > vertices ;
std : : vector < u16 > indices ;
2020-11-08 15:47:25 +01:00
const size_t n = line . m_Coords . size ( ) ; // number of line points
2013-12-06 01:42:50 +01:00
bool closed = line . m_Closed ;
ENSURE ( n > = 2 ) ; // minimum needed to avoid errors (also minimum value to make sense, can't draw a line between 1 point)
// In each iteration, p1 is the position of vertex i, p0 is i-1, p2 is i+1.
// To avoid slightly expensive terrain computations we cycle these around and
// recompute p2 at the end of each iteration.
CVector3D p0 ;
2020-11-08 15:47:25 +01:00
CVector3D p1 ( line . m_Coords [ 0 ] . X , 0 , line . m_Coords [ 0 ] . Y ) ;
CVector3D p2 ( line . m_Coords [ 1 ] . X , 0 , line . m_Coords [ 1 ] . Y ) ;
2013-12-06 01:42:50 +01:00
if ( closed )
// grab the ending point so as to close the loop
2020-11-08 15:47:25 +01:00
p0 = CVector3D ( line . m_Coords [ n - 1 ] . X , 0 , line . m_Coords [ n - 1 ] . Y ) ;
2013-12-06 01:42:50 +01:00
else
2016-11-23 14:02:58 +01:00
// we don't want to loop around and use the direction towards the other end of the line, so create an artificial p0 that
2013-12-06 01:42:50 +01:00
// extends the p2 -> p1 direction, and use that point instead
p0 = p1 + ( p1 - p2 ) ;
bool p1floating = false ;
bool p2floating = false ;
// Compute terrain heights, clamped to the water height (and remember whether
// each point was floating on water, for normal computation later)
// TODO: if we ever support more than one water level per map, recompute this per point
2017-03-24 22:32:49 +01:00
CmpPtr < ICmpWaterManager > cmpWaterManager ( * line . m_SimContext , SYSTEM_ENTITY ) ;
float w = cmpWaterManager ? cmpWaterManager - > GetExactWaterLevel ( p0 . X , p0 . Z ) : 0.f ;
const CTerrain & terrain = line . m_SimContext - > GetTerrain ( ) ;
2013-12-06 01:42:50 +01:00
p0 . Y = terrain . GetExactGroundLevel ( p0 . X , p0 . Z ) ;
if ( p0 . Y < w )
p0 . Y = w ;
p1 . Y = terrain . GetExactGroundLevel ( p1 . X , p1 . Z ) ;
if ( p1 . Y < w )
{
p1 . Y = w ;
p1floating = true ;
}
p2 . Y = terrain . GetExactGroundLevel ( p2 . X , p2 . Z ) ;
if ( p2 . Y < w )
{
p2 . Y = w ;
p2floating = true ;
}
for ( size_t i = 0 ; i < n ; + + i )
{
// For vertex i, compute bisector of lines (i-1)..(i) and (i)..(i+1)
// perpendicular to terrain normal
// Normal is vertical if on water, else computed from terrain
CVector3D norm ;
if ( p1floating )
norm = CVector3D ( 0 , 1 , 0 ) ;
else
norm = terrain . CalcExactNormal ( p1 . X , p1 . Z ) ;
CVector3D b = ( ( p1 - p0 ) . Normalized ( ) + ( p2 - p1 ) . Normalized ( ) ) . Cross ( norm ) ;
// Adjust bisector length to match the line thickness, along the line's width
float l = b . Dot ( ( p2 - p1 ) . Normalized ( ) . Cross ( norm ) ) ;
if ( fabs ( l ) > 0.000001f ) // avoid unlikely divide-by-zero
b * = line . m_Thickness / l ;
// Push vertices and indices for each quad in GL_TRIANGLES order. The two triangles of each quad are indexed using
2017-06-26 15:04:21 +02:00
// the winding orders (BR, BL, TR) and (TR, BL, TL) (where BR is bottom-right of this iteration's quad, TR top-right etc).
2022-06-06 23:46:37 +02:00
SVertex vertex1 ( p1 + b + norm * OverlayRenderer : : OVERLAY_VOFFSET , CVector2D ( 0.f , v ) ) ;
SVertex vertex2 ( p1 - b + norm * OverlayRenderer : : OVERLAY_VOFFSET , CVector2D ( 1.f , v ) ) ;
2013-12-06 01:42:50 +01:00
vertices . push_back ( vertex1 ) ;
vertices . push_back ( vertex2 ) ;
2020-11-26 23:28:50 +01:00
u16 vertexCount = static_cast < u16 > ( vertices . size ( ) ) ;
u16 index1 = vertexCount - 2 ; // index of vertex1 in this iteration (TR of this quad)
u16 index2 = vertexCount - 1 ; // index of the vertex2 in this iteration (TL of this quad)
2013-12-06 01:42:50 +01:00
if ( i = = 0 )
{
// initial two vertices to continue building triangles from (n must be >= 2 for this to work)
indices . push_back ( index1 ) ;
indices . push_back ( index2 ) ;
}
2016-11-23 14:02:58 +01:00
else
2013-12-06 01:42:50 +01:00
{
2020-11-26 23:28:50 +01:00
u16 index1Prev = vertexCount - 4 ; // index of the vertex1 in the previous iteration (BR of this quad)
u16 index2Prev = vertexCount - 3 ; // index of the vertex2 in the previous iteration (BL of this quad)
ENSURE ( index1Prev < vertexCount ) ;
ENSURE ( index2Prev < vertexCount ) ;
2013-12-06 01:42:50 +01:00
// Add two corner points from last iteration and join with one of our own corners to create triangle 1
// (don't need to do this if i == 1 because i == 0 are the first two ones, they don't need to be copied)
if ( i > 1 )
{
indices . push_back ( index1Prev ) ;
indices . push_back ( index2Prev ) ;
}
indices . push_back ( index1 ) ; // complete triangle 1
// create triangle 2, specifying the adjacent side's vertices in the opposite order from triangle 1
indices . push_back ( index1 ) ;
indices . push_back ( index2Prev ) ;
indices . push_back ( index2 ) ;
}
// alternate V coordinate for debugging
v = 1 - v ;
// cycle the p's and compute the new p2
p0 = p1 ;
p1 = p2 ;
p1floating = p2floating ;
// if in closed mode, wrap around the coordinate array for p2 -- otherwise, extend linearly
if ( ! closed & & i = = n - 2 )
// next iteration is the last point of the line, so create an artificial p2 that extends the p0 -> p1 direction
p2 = p1 + ( p1 - p0 ) ;
else
2020-11-08 15:47:25 +01:00
p2 = CVector3D ( line . m_Coords [ ( i + 2 ) % n ] . X , 0 , line . m_Coords [ ( i + 2 ) % n ] . Y ) ;
2013-12-06 01:42:50 +01:00
p2 . Y = terrain . GetExactGroundLevel ( p2 . X , p2 . Z ) ;
if ( p2 . Y < w )
{
p2 . Y = w ;
p2floating = true ;
}
else
p2floating = false ;
}
if ( closed )
{
// close the path
2017-06-26 15:04:21 +02:00
if ( n % 2 = = 0 )
{
2020-11-26 23:28:50 +01:00
u16 vertexCount = static_cast < u16 > ( vertices . size ( ) ) ;
indices . push_back ( vertexCount - 2 ) ;
indices . push_back ( vertexCount - 1 ) ;
2017-06-26 15:04:21 +02:00
indices . push_back ( 0 ) ;
2013-12-06 01:42:50 +01:00
2017-06-26 15:04:21 +02:00
indices . push_back ( 0 ) ;
2020-11-26 23:28:50 +01:00
indices . push_back ( vertexCount - 1 ) ;
2017-06-26 15:04:21 +02:00
indices . push_back ( 1 ) ;
}
else
{
// add two vertices to have the good UVs for the last quad
2022-06-06 23:46:37 +02:00
SVertex vertex1 ( vertices [ 0 ] . m_Position , CVector2D ( 0.f , 1.f ) ) ;
SVertex vertex2 ( vertices [ 1 ] . m_Position , CVector2D ( 1.f , 1.f ) ) ;
2017-06-26 15:04:21 +02:00
vertices . push_back ( vertex1 ) ;
vertices . push_back ( vertex2 ) ;
2020-11-26 23:28:50 +01:00
u16 vertexCount = static_cast < u16 > ( vertices . size ( ) ) ;
indices . push_back ( vertexCount - 4 ) ;
indices . push_back ( vertexCount - 3 ) ;
indices . push_back ( vertexCount - 2 ) ;
2017-06-26 15:04:21 +02:00
2020-11-26 23:28:50 +01:00
indices . push_back ( vertexCount - 2 ) ;
indices . push_back ( vertexCount - 3 ) ;
indices . push_back ( vertexCount - 1 ) ;
2017-06-26 15:04:21 +02:00
}
2013-12-06 01:42:50 +01:00
}
else
{
// Create start and end caps. On either end, this is done by taking the centroid between the last and second-to-last pair of
2016-11-23 14:02:58 +01:00
// vertices that was generated along the path (i.e. the vertex1's and vertex2's from above), taking a directional vector
2013-12-06 01:42:50 +01:00
// between them, and drawing the line cap in the plane given by the two butt-end corner points plus said vector.
std : : vector < u16 > capIndices ;
std : : vector < SVertex > capVertices ;
// create end cap
CreateLineCap (
line ,
// the order of these vertices is important here, swapping them produces caps at the wrong side
vertices [ vertices . size ( ) - 2 ] . m_Position , // top-right vertex of last quad
vertices [ vertices . size ( ) - 1 ] . m_Position , // top-left vertex of last quad
// directional vector between centroids of last vertex pair and second-to-last vertex pair
( Centroid ( vertices [ vertices . size ( ) - 2 ] , vertices [ vertices . size ( ) - 1 ] ) - Centroid ( vertices [ vertices . size ( ) - 4 ] , vertices [ vertices . size ( ) - 3 ] ) ) . Normalized ( ) ,
line . m_EndCapType ,
capVertices ,
capIndices
) ;
for ( unsigned i = 0 ; i < capIndices . size ( ) ; i + + )
2020-11-26 23:28:50 +01:00
capIndices [ i ] + = static_cast < u16 > ( vertices . size ( ) ) ;
2013-12-06 01:42:50 +01:00
vertices . insert ( vertices . end ( ) , capVertices . begin ( ) , capVertices . end ( ) ) ;
indices . insert ( indices . end ( ) , capIndices . begin ( ) , capIndices . end ( ) ) ;
capIndices . clear ( ) ;
capVertices . clear ( ) ;
// create start cap
CreateLineCap (
line ,
// the order of these vertices is important here, swapping them produces caps at the wrong side
vertices [ 1 ] . m_Position ,
vertices [ 0 ] . m_Position ,
// directional vector between centroids of first vertex pair and second vertex pair
( Centroid ( vertices [ 1 ] , vertices [ 0 ] ) - Centroid ( vertices [ 3 ] , vertices [ 2 ] ) ) . Normalized ( ) ,
line . m_StartCapType ,
capVertices ,
capIndices
) ;
for ( unsigned i = 0 ; i < capIndices . size ( ) ; i + + )
2020-11-26 23:28:50 +01:00
capIndices [ i ] + = static_cast < u16 > ( vertices . size ( ) ) ;
2013-12-06 01:42:50 +01:00
vertices . insert ( vertices . end ( ) , capVertices . begin ( ) , capVertices . end ( ) ) ;
indices . insert ( indices . end ( ) , capIndices . begin ( ) , capIndices . end ( ) ) ;
}
2022-02-27 21:21:07 +01:00
if ( vertices . empty ( ) | | indices . empty ( ) )
return ;
// Indices for triangles, so must be multiple of 3.
ENSURE ( indices . size ( ) % 3 = = 0 ) ;
2013-12-06 01:42:50 +01:00
2019-12-11 00:13:37 +01:00
m_BoundingBox = CBoundingBoxAligned ( ) ;
for ( const SVertex & vertex : vertices )
m_BoundingBox + = vertex . m_Position ;
2022-02-18 18:33:12 +01:00
m_VB = g_VBMan . AllocateChunk (
2022-05-09 00:02:46 +02:00
sizeof ( SVertex ) , vertices . size ( ) , Renderer : : Backend : : IBuffer : : Type : : VERTEX , false ) ;
2022-04-27 22:04:56 +02:00
// Allocation might fail (e.g. due to too many vertices).
if ( m_VB )
2013-12-06 01:42:50 +01:00
{
2022-04-27 22:04:56 +02:00
// Copy data into backend buffer.
m_VB - > m_Owner - > UpdateChunkVertices ( m_VB . Get ( ) , & vertices [ 0 ] ) ;
2013-12-06 01:42:50 +01:00
for ( size_t k = 0 ; k < indices . size ( ) ; + + k )
2020-11-26 23:28:50 +01:00
indices [ k ] + = static_cast < u16 > ( m_VB - > m_Index ) ;
2013-12-06 01:42:50 +01:00
2022-02-18 18:33:12 +01:00
m_VBIndices = g_VBMan . AllocateChunk (
2022-05-09 00:02:46 +02:00
sizeof ( u16 ) , indices . size ( ) , Renderer : : Backend : : IBuffer : : Type : : INDEX , false ) ;
2013-12-06 01:42:50 +01:00
if ( m_VBIndices )
2022-01-10 17:51:43 +01:00
m_VBIndices - > m_Owner - > UpdateChunkVertices ( m_VBIndices . Get ( ) , & indices [ 0 ] ) ;
2013-12-06 01:42:50 +01:00
}
}
void CTexturedLineRData : : CreateLineCap ( const SOverlayTexturedLine & line , const CVector3D & corner1 , const CVector3D & corner2 ,
const CVector3D & lineDirectionNormal , SOverlayTexturedLine : : LineCapType endCapType , std : : vector < SVertex > & verticesOut ,
std : : vector < u16 > & indicesOut )
{
if ( endCapType = = SOverlayTexturedLine : : LINECAP_FLAT )
return ; // no action needed, this is the default
// When not in closed mode, we've created artificial points for the start- and endpoints that extend the line in the
// direction of the first and the last segment, respectively. Thus, we know both the start and endpoints have perpendicular
// butt endings, i.e. the end corner vertices on either side of the line extend perpendicularly from the segment direction.
// That is to say, when viewed from the top, we will have something like
// .
// this: and not like this: /|
// ----+ / |
// | / .
// | /
// ----+ /
//
int roundCapPoints = 8 ; // amount of points to sample along the semicircle for rounded caps (including corner points)
float radius = line . m_Thickness ;
CVector3D centerPoint = ( corner1 + corner2 ) * 0.5f ;
2022-06-06 23:46:37 +02:00
SVertex centerVertex ( centerPoint , CVector2D ( 0.5f , 0.5f ) ) ;
2020-11-26 23:28:50 +01:00
u16 indexOffset = static_cast < u16 > ( verticesOut . size ( ) ) ; // index offset in verticesOut from where we start adding our vertices
2013-12-06 01:42:50 +01:00
switch ( endCapType )
{
case SOverlayTexturedLine : : LINECAP_SHARP :
{
roundCapPoints = 3 ; // creates only one point directly ahead
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)
2022-06-06 23:46:37 +02:00
centerVertex . m_UV . X = 0.480f ; // slight visual correction to make the texture match up better at the corner points
2013-12-06 01:42:50 +01:00
}
2017-09-01 22:04:53 +02:00
FALLTHROUGH ;
2013-12-06 01:42:50 +01:00
case SOverlayTexturedLine : : LINECAP_ROUND :
{
2016-11-23 14:02:58 +01:00
// Draw a rounded line cap in the 3D plane of the line specified by the two corner points and the normal vector of the
2013-12-06 01:42:50 +01:00
// line's direction. The terrain normal at the centroid between the two corner points is perpendicular to this plane.
2016-11-23 14:02:58 +01:00
// The way this works is by taking a vector from the corner points' centroid to one of the corner points (which is then
// of radius length), and rotate it around the terrain normal vector in that centroid. This will rotate the vector in
2013-12-06 01:42:50 +01:00
// the line's plane, producing the desired rounded cap.
2016-11-23 14:02:58 +01:00
// To please OpenGL's winding order, this angle needs to be negated depending on whether we start rotating from
// the (center -> corner1) or (center -> corner2) vector. For the (center -> corner2) vector, we apparently need to use
2013-12-06 01:42:50 +01:00
// the negated angle.
float stepAngle = - ( float ) ( M_PI / ( roundCapPoints - 1 ) ) ;
// Push the vertices in triangle fan order (easy to generate GL_TRIANGLES indices for afterwards)
2016-11-23 14:02:58 +01:00
// Note that we're manually adding the corner vertices instead of having them be generated by the rotating vector.
2013-12-06 01:42:50 +01:00
// This is because we want to support an overly large radius to make the sharp line ending look sharper.
2016-11-23 14:02:58 +01:00
verticesOut . push_back ( centerVertex ) ;
2022-06-06 23:46:37 +02:00
verticesOut . push_back ( SVertex ( corner2 , CVector2D ( ) ) ) ;
2013-12-06 01:42:50 +01:00
// Get the base vector that we will incrementally rotate in the cap plane to produce the radial sample points.
2016-11-23 14:02:58 +01:00
// Normally corner2 - centerPoint would suffice for this since it is of radius length, but we want to support custom
2013-12-06 01:42:50 +01:00
// radii to support tuning the 'sharpness' of sharp end caps (see above)
CVector3D rotationBaseVector = ( corner2 - centerPoint ) . Normalized ( ) * radius ;
// Calculate the normal vector of the plane in which we're going to be drawing the line cap. This is the vector that
// is perpendicular to both baseVector and the 'lineDirectionNormal' vector indicating the direction of the line.
// Note that we shouldn't use terrain->CalcExactNormal() here because if the line is being rendered on top of water,
// then CalcExactNormal will return the normal vector of the terrain that's underwater (which can be quite funky).
CVector3D capPlaneNormal = lineDirectionNormal . Cross ( rotationBaseVector ) . Normalized ( ) ;
for ( int i = 1 ; i < roundCapPoints - 1 ; + + i )
{
// Rotate the centerPoint -> corner vector by i*stepAngle radians around the cap plane normal at the center point.
CQuaternion quatRotation ;
quatRotation . FromAxisAngle ( capPlaneNormal , i * stepAngle ) ;
CVector3D worldPos3D = centerPoint + quatRotation . Rotate ( rotationBaseVector ) ;
2016-11-23 14:02:58 +01:00
// 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
2013-12-06 01:42:50 +01:00
// of the texture around the edge of the semicircle)
float u = 0.f ;
2019-09-18 16:44:31 +02:00
float v = Clamp ( ( i / static_cast < float > ( roundCapPoints - 1 ) ) , 0.f , 1.f ) ; // pos, u, v
2022-06-06 23:46:37 +02:00
verticesOut . push_back ( SVertex ( worldPos3D , CVector2D ( u , v ) ) ) ;
2013-12-06 01:42:50 +01:00
}
2016-11-23 14:02:58 +01:00
// connect back to the other butt-end corner point to complete the semicircle
2022-06-06 23:46:37 +02:00
verticesOut . push_back ( SVertex ( corner1 , CVector2D ( 0.f , 1.f ) ) ) ;
2013-12-06 01:42:50 +01:00
2016-11-23 14:02:58 +01:00
// now push indices in GL_TRIANGLES order; vertices[indexOffset] is the center vertex, vertices[indexOffset + 1] is the
// first corner point, then a bunch of radial samples, and then at the end we have the other corner point again. So:
2013-12-06 01:42:50 +01:00
for ( int i = 1 ; i < roundCapPoints ; + + i )
{
2016-11-23 14:02:58 +01:00
indicesOut . push_back ( indexOffset ) ; // center vertex
indicesOut . push_back ( indexOffset + i ) ;
indicesOut . push_back ( indexOffset + i + 1 ) ;
2013-12-06 01:42:50 +01:00
}
}
break ;
case SOverlayTexturedLine : : LINECAP_SQUARE :
{
2016-11-23 14:02:58 +01:00
// Extend the (corner1 -> corner2) vector along the direction normal and draw a square line ending consisting of
2013-12-06 01:42:50 +01:00
// three triangles (sort of like a triangle fan)
// NOTE: The order in which the vertices are pushed out determines the visibility, as they
// are rendered only one-sided; the wrong order of vertices will make the cap visible only from the bottom.
verticesOut . push_back ( centerVertex ) ;
2022-06-06 23:46:37 +02:00
verticesOut . push_back ( SVertex ( corner2 , CVector2D ( ) ) ) ;
verticesOut . push_back ( SVertex ( corner2 + ( lineDirectionNormal * ( line . m_Thickness ) ) , CVector2D ( 0.f , 0.33333f ) ) ) ; // extend butt corner point 2 along the normal vector
verticesOut . push_back ( SVertex ( corner1 + ( lineDirectionNormal * ( line . m_Thickness ) ) , CVector2D ( 0.f , 0.66666f ) ) ) ; // extend butt corner point 1 along the normal vector
verticesOut . push_back ( SVertex ( corner1 , CVector2D ( 0.f , 1.0f ) ) ) ; // push butt corner point 1
2016-11-23 14:02:58 +01:00
for ( int i = 1 ; i < 4 ; + + i )
{
indicesOut . push_back ( indexOffset ) ; // center point
indicesOut . push_back ( indexOffset + i ) ;
2013-12-06 01:42:50 +01:00
indicesOut . push_back ( indexOffset + i + 1 ) ;
2016-11-23 14:02:58 +01:00
}
2013-12-06 01:42:50 +01:00
}
break ;
default :
break ;
}
}
2019-12-11 00:13:37 +01:00
bool CTexturedLineRData : : IsVisibleInFrustum ( const CFrustum & frustum ) const
{
return frustum . IsBoxVisible ( m_BoundingBox ) ;
}