You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
419 lines
9.8 KiB
419 lines
9.8 KiB
![]()
4 years ago
|
//========= 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 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|