summaryrefslogtreecommitdiff
path: root/game/client/tf2/ground_line.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/tf2/ground_line.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/tf2/ground_line.cpp')
-rw-r--r--game/client/tf2/ground_line.cpp418
1 files changed, 418 insertions, 0 deletions
diff --git a/game/client/tf2/ground_line.cpp b/game/client/tf2/ground_line.cpp
new file mode 100644
index 0000000..b59cb33
--- /dev/null
+++ b/game/client/tf2/ground_line.cpp
@@ -0,0 +1,418 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "ground_line.h"
+#include "mathlib/vplane.h"
+#include "beamdraw.h"
+#include "bitvec.h"
+#include "clientmode_commander.h"
+#include <vgui_controls/Controls.h>
+#include <vgui/ISurface.h>
+#include "clienteffectprecachesystem.h"
+#include "tier0/vprof.h"
+
+#define MAX_DOWN_DIST 300
+#define MAX_UP_DIST 300
+#define XY_PER_SEGMENT 100
+
+CLIENTEFFECT_REGISTER_BEGIN( PrecacheGroundLine )
+CLIENTEFFECT_MATERIAL( "player/support/mortarline" )
+CLIENTEFFECT_REGISTER_END()
+
+static CUtlLinkedList< CGroundLine*, unsigned short > s_GroundLines;
+
+// ---------------------------------------------------------------------- //
+// Helpers.
+// ---------------------------------------------------------------------- //
+
+VPlane VPlaneFromCPlane(const cplane_t &plane)
+{
+ if(plane.signbits)
+ return VPlane(-plane.normal, -plane.dist);
+ else
+ return VPlane(plane.normal, plane.dist);
+}
+
+
+Vector ClipEndPos(const Vector &vStart, const Vector &vEnd, float clipDist, VPlane *pPlane)
+{
+ trace_t trace;
+
+ UTIL_TraceLine(vStart, vEnd, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace);
+ if(trace.fraction < 1)
+ {
+ *pPlane = VPlaneFromCPlane(trace.plane);
+ return trace.endpos + pPlane->m_Normal * clipDist;
+ }
+ else
+ {
+ pPlane->m_Normal.Init(0,0,1);
+ pPlane->m_Dist = DotProduct(pPlane->m_Normal, vEnd);
+ return vEnd;
+ }
+}
+
+// Tries to find the closest surface point to the specified point.
+Vector FindBestSurfacePoint(const Vector &vPos)
+{
+ static float stepDist = 500;
+ static float flHeightAboveGround = 20;
+
+ // First, find an inside point.
+
+ // Test upwards.
+ trace_t trace;
+ UTIL_TraceLine(
+ Vector(vPos[0], vPos[1], vPos[2] + stepDist),
+ vPos,
+ MASK_SOLID_BRUSHONLY,
+ NULL,
+ COLLISION_GROUP_NONE,
+ &trace);
+ if(trace.fraction < 1 && trace.fraction != 0)
+ {
+ return Vector(trace.endpos[0], trace.endpos[1], trace.endpos[2] + flHeightAboveGround );
+ }
+
+ // Test down.
+ UTIL_TraceLine(
+ vPos,
+ Vector(vPos[0], vPos[1], vPos[2] - stepDist),
+ MASK_SOLID_BRUSHONLY,
+ NULL,
+ COLLISION_GROUP_NONE,
+ &trace);
+ if(trace.fraction < 1 && trace.fraction != 0)
+ {
+ return Vector(trace.endpos[0], trace.endpos[1], trace.endpos[2] + flHeightAboveGround );
+ }
+
+ return vPos;
+}
+
+
+// Tries to find the place in the world geometry which blocks vStart from the line segment (vEnd1, vEnd2).
+// Uses a binary search so your error is |vEnd2 - vEnd1| ^ (1 / nIterations)
+bool BinSearchSegments(const Vector &vStart, const Vector &vEnd1, const Vector &vEnd2, int nIterations, Vector *out)
+{
+ trace_t trace;
+
+ // If what was passed into us already intersects then there's nothing we can do.
+ UTIL_TraceLine(vStart, vEnd2, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace);
+ if(trace.fraction < 1)
+ return false;
+
+ Vector vecs[2] = {vEnd1, vEnd2};
+ int iIntersect = 0; // Which vector intersects.
+ for(int i=0; i < nIterations; i++)
+ {
+ // Test the midpoint.
+ Vector mid = (vecs[0] + vecs[1]) * 0.5f;
+ UTIL_TraceLine(vStart, mid, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace);
+ if(trace.fraction < 1)
+ vecs[iIntersect] = mid;
+ else
+ vecs[!iIntersect] = mid;
+ }
+
+ *out = (vecs[0] + vecs[1]) * 0.5f;
+ return true;
+}
+
+// Tries to snap the point to its underlying plane's z.
+Vector SnapToPlane(const Vector &v)
+{
+return v;
+
+ trace_t trace;
+ UTIL_TraceLine(Vector(v[0], v[1], v[2] + 50), Vector(v[0], v[1], v[2] - 50),
+ MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace);
+ if(trace.fraction < 1)
+ return Vector(v[0], v[1], trace.endpos[2] + 3);
+ else
+ return v;
+}
+
+
+
+// ---------------------------------------------------------------------- //
+// CGroundLine implementation.
+// ---------------------------------------------------------------------- //
+
+CGroundLine::CGroundLine()
+: BaseClass( NULL, "CGroundLine" )
+{
+ m_pMaterial = NULL;
+
+ m_ListHandle = s_GroundLines.AddToHead( this );
+ SetParent( CMinimapPanel::MinimapRootPanel() );
+
+ m_nPoints = 0;
+ SetVisible( true );
+ SetPaintBackgroundEnabled( false );
+}
+
+
+CGroundLine::~CGroundLine()
+{
+ s_GroundLines.Remove( m_ListHandle );
+
+ m_vStart.Init();
+ m_vEnd.Init();
+ m_LineWidth = 1;
+}
+
+
+bool CGroundLine::Init(const char *pMaterialName)
+{
+ m_pMaterial = materials->FindMaterial(pMaterialName, TEXTURE_GROUP_CLIENT_EFFECTS);
+ return !!m_pMaterial;
+}
+
+
+void CGroundLine::SetParameters(
+ const Vector &vStart,
+ const Vector &vEnd,
+ const Vector &vStartColor, // Color values 0-1
+ const Vector &vEndColor,
+ float alpha,
+ float lineWidth
+ )
+{
+ m_vStart = vStart;
+ m_vEnd = vEnd;
+ m_vStartColor = vStartColor;
+ m_vEndColor = vEndColor;
+ m_Alpha = alpha;
+ m_LineWidth = lineWidth;
+
+ Vector vTo( vEnd.x - vStart.x, vEnd.y - vStart.y, 0 );
+ float flXYLen = vTo.Length();
+
+ // Recalculate our segment list.
+ unsigned int nSteps = (int)flXYLen / XY_PER_SEGMENT;
+ nSteps = clamp( nSteps, 8, MAX_GROUNDLINE_SEGMENTS ) & ~1;
+ unsigned int nMaxSteps = nSteps / 2;
+
+ // First generate the sequence. We generate every other point here so it can insert fixup points to prevent
+ // it from crossing world geometry.
+ Vector pt[MAX_GROUNDLINE_SEGMENTS];
+ Vector vStep = (Vector(m_vEnd[0], m_vEnd[1], 0) - Vector(m_vStart[0], m_vStart[1], 0)) / (nMaxSteps-1);
+
+ pt[0] = FindBestSurfacePoint(m_vStart);
+
+ unsigned int i;
+ for(i=1; i < nMaxSteps; i++)
+ pt[i<<1] = FindBestSurfacePoint(pt[(i-1)<<1] + vStep);
+
+
+ CBitVec<MAX_GROUNDLINE_SEGMENTS> pointsUsed;
+ pointsUsed.ClearAll();
+
+ // Now try to make sure they don't intersect the geometry.
+ for(i=0; i < nMaxSteps-1; i++)
+ {
+ Vector &a = pt[i<<1];
+ Vector &b = pt[(i+1)<<1];
+
+ trace_t trace;
+ UTIL_TraceLine(a, b, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace);
+ if(trace.fraction < 1)
+ {
+ int cIndex = (i<<1)+1;
+ Vector &c = pt[cIndex];
+
+ // Ok, this line segment intersects the world. Do a binary search to try to find the
+ // point of intersection.
+ Vector hi, lo;
+ if(a.z < b.z)
+ {
+ hi = b;
+ lo = a;
+ }
+ else
+ {
+ hi = a;
+ lo = b;
+ }
+
+ if(BinSearchSegments(lo, hi, Vector(lo[0],lo[1],hi[2]), 15, &c))
+ {
+ pointsUsed.Set( cIndex );
+ }
+ else if(BinSearchSegments(lo, hi, Vector(hi[0],hi[1],hi[2]+500), 15, &c))
+ {
+ pointsUsed.Set( cIndex );
+ }
+ }
+ }
+
+ // Export the points.
+ m_nPoints = 0;
+ for(i=0; i < nSteps; i++)
+ {
+ // Every other point is always active.
+ if( pointsUsed.Get( i ) || !(i & 1) )
+ {
+ m_Points[m_nPoints] = pt[i];
+ ++m_nPoints;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the visibility of the groundline
+//-----------------------------------------------------------------------------
+void CGroundLine::SetVisible( bool bVisible )
+{
+ m_bVisible = bVisible;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if the groundline's visible
+//-----------------------------------------------------------------------------
+bool CGroundLine::IsVisible( void )
+{
+ return m_bVisible;
+}
+
+void CGroundLine::DrawAllGroundLines()
+{
+ VPROF("CGroundLine::DrawAllGroundLines()");
+ unsigned short i;
+ for( i = s_GroundLines.Head(); i != s_GroundLines.InvalidIndex(); i = s_GroundLines.Next(i) )
+ {
+ s_GroundLines[i]->Draw();
+ }
+}
+
+void CGroundLine::Draw()
+{
+ if ( !m_pMaterial || m_nPoints < 2 )
+ return;
+ if ( !IsVisible() )
+ return;
+
+ float flAlpha = m_Alpha;
+ if( g_pClientMode == ClientModeCommander() )
+ {
+ flAlpha = 1; // draw bright..
+ }
+
+ CBeamSegDraw beamDraw;
+ beamDraw.Start( m_nPoints, m_pMaterial );
+
+ for( unsigned int i=0; i < m_nPoints; i++ )
+ {
+ float t = (float)i / (m_nPoints - 1);
+
+ CBeamSeg seg;
+ seg.m_vPos = m_Points[i];
+ VectorLerp( m_vStartColor, m_vEndColor, t, seg.m_vColor );
+ seg.m_flTexCoord = 0;
+ seg.m_flWidth = m_LineWidth;
+ seg.m_flAlpha = m_Alpha;
+
+ beamDraw.NextSeg( &seg );
+ }
+
+ beamDraw.End();
+}
+
+
+static inline bool ClipLine( float &x1, float &y1, float &x2, float &y2, float xClip, float sign )
+{
+ if( x1*sign < (xClip-0.001f)*sign )
+ {
+ if( x2*sign > (xClip+0.001f)*sign )
+ {
+ float t = (xClip-x1) / (x2 - x1);
+ x1 = x1 + (x2 - x1) * t;
+ y1 = y1 + (y2 - y1) * t;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else if( x2*sign < (xClip-0.001f)*sign )
+ {
+ if( x1*sign > (xClip+0.001f)*sign )
+ {
+ float t = (xClip-x1) / (x2 - x1);
+ x2 = x1 + (x2 - x1) * t;
+ y2 = y1 + (y2 - y1) * t;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+void CGroundLine::Paint( )
+{
+ vgui::Panel *pPanel = GetParent();
+ int wide, tall;
+ pPanel->GetSize( wide, tall );
+
+ float tPrev = 0;
+ float xPrev, yPrev;
+ CMinimapPanel::MinimapPanel()->WorldToMinimap( MINIMAP_NOCLIP, m_vStart, xPrev, yPrev );
+
+ int nSegs = 20;
+ for( int iSeg=1; iSeg <= nSegs; iSeg++ )
+ {
+ float t = (float)iSeg / nSegs;
+
+ Vector v3DPos;
+ VectorLerp( m_vStart, m_vEnd, t, v3DPos );
+
+ float x, y;
+ CMinimapPanel::MinimapPanel()->WorldToMinimap( MINIMAP_NOCLIP, v3DPos, x, y );
+
+ // Clip the line segment on X, then Y.
+ if( ClipLine( xPrev, yPrev, x, y, 0, 1 ) &&
+ ClipLine( xPrev, yPrev, x, y, wide, -1 ) &&
+ ClipLine( yPrev, xPrev, y, x, 0, 1 ) &&
+ ClipLine( yPrev, xPrev, y, x, tall, -1 ) )
+ {
+ Vector vColor;
+ VectorLerp( m_vStartColor, m_vEndColor, t, vColor );
+ vColor *= 255.9f;
+
+ vgui::surface()->DrawSetColor(
+ (unsigned char)RoundFloatToInt( vColor.x ),
+ (unsigned char)RoundFloatToInt( vColor.y ),
+ (unsigned char)RoundFloatToInt( vColor.z ),
+ 255 );
+
+ vgui::surface()->DrawLine( xPrev, yPrev, x, y );
+ }
+
+ tPrev = t;
+ xPrev = x;
+ yPrev = y;
+ }
+
+ // Draw a marker at the endpoint.
+ float xEnd, yEnd;
+ if( CMinimapPanel::MinimapPanel()->WorldToMinimap( MINIMAP_NOCLIP, m_vEnd, xEnd, yEnd ) )
+ {
+ int ix = RoundFloatToInt( xEnd );
+ int iy = RoundFloatToInt( yEnd );
+ int rectSize=1;
+
+ vgui::surface()->DrawSetColor( 255, 255, 255, 255 );
+ vgui::surface()->DrawOutlinedRect( ix-rectSize, iy-rectSize, ix+rectSize, iy+rectSize );
+ }
+}
+
+