Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/game/client/c_func_breakablesurf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ class C_BreakableSurface : public C_BaseEntity, public IBrushRenderer
float m_flPanelHeight;
Vector m_vNormal;
Vector m_vCorner;
#ifdef NEO
Vector m_vWidthDir;
Vector m_vHeightDir;
Copy link
Copy Markdown
Contributor

@sunzenshen sunzenshen Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to zero initialize these new members in

void C_BreakableSurface::InitMaterial(WinEdge_t nEdgeType, int nEdgeStyle, char const* pMaterialName)

? (or some other initialization function)

https://github.com/Masterkatze/neo/blame/e931664b2fd54c99ecfdbf6bd4b6be20c6b07cba/src/game/client/c_func_breakablesurf.cpp#L344

e.g. put what you have in other places into one place in an initialization function like this:

Vector vWidth  = m_vLLVertex - m_vLRVertex;
Vector vHeight = m_vLLVertex - m_vULVertex;
m_vWidthDir  = vWidth;  VectorNormalize( m_vWidthDir.GetForModify() );
m_vHeightDir = vHeight; VectorNormalize( m_vHeightDir.GetForModify() );

#endif
bool m_bIsBroken;
int m_nSurfaceType;

Expand Down Expand Up @@ -274,6 +278,10 @@ IMPLEMENT_CLIENTCLASS_DT( C_BreakableSurface, DT_BreakableSurface, CBreakableSur
RecvPropFloat( RECVINFO( m_flPanelHeight) ),
RecvPropVector( RECVINFO( m_vNormal ) ),
RecvPropVector( RECVINFO( m_vCorner ) ),
#ifdef NEO
RecvPropVector( RECVINFO( m_vWidthDir ) ),
RecvPropVector( RECVINFO( m_vHeightDir ) ),
#endif
RecvPropInt( RECVINFO( m_bIsBroken )),
RecvPropInt( RECVINFO( m_nSurfaceType )),
RecvPropArray3( RECVINFO_ARRAY(m_RawPanelBitVec), RecvPropInt( RECVINFO( m_RawPanelBitVec[ 0 ] ))),
Expand Down Expand Up @@ -548,13 +556,20 @@ bool C_BreakableSurface::RenderBrushModelSurface( IClientEntity* pBaseEntity, IB
//------------------------------------------------------------------------------
void C_BreakableSurface::DrawRenderList(IBrushSurface* pBrushSurface)
{
#ifdef NEO
// Width and height directions are sent at full float precision by the server,
// so no cross product or derivation is needed here.
Vector vWidthStep = m_vWidthDir * m_flPanelWidth;
Vector vHeightStep = m_vHeightDir * m_flPanelHeight;
#else
// Get width and height steps
QAngle vAngles;
VectorAngles(-1*m_vNormal,vAngles);
Vector vWidthStep,vHeightStep;
AngleVectors(vAngles,NULL,&vWidthStep,&vHeightStep);
vWidthStep *= m_flPanelWidth;
vHeightStep *= m_flPanelHeight;
#endif

CMeshBuilder pMeshBuilder;
IMesh* pMesh = NULL;
Expand Down Expand Up @@ -592,13 +607,20 @@ void C_BreakableSurface::DrawRenderList(IBrushSurface* pBrushSurface)
//------------------------------------------------------------------------------
void C_BreakableSurface::DrawRenderListHighlights(IBrushSurface* pBrushSurface)
{
#ifdef NEO
// Width and height directions are sent at full float precision by the server,
// so no cross product or derivation is needed here.
Vector vWidthStep = m_vWidthDir * m_flPanelWidth;
Vector vHeightStep = m_vHeightDir * m_flPanelHeight;
#else
// Get width and height steps
QAngle vAngles;
VectorAngles(-1*m_vNormal,vAngles);
Vector vWidthStep,vHeightStep;
AngleVectors(vAngles,NULL,&vWidthStep,&vHeightStep);
vWidthStep *= m_flPanelWidth;
vHeightStep *= m_flPanelHeight;
#endif


CMeshBuilder pMeshBuilder;
Expand Down Expand Up @@ -924,12 +946,19 @@ void C_BreakableSurface::DrawSolidBlocks(IBrushSurface* pBrushSurface)
// ---------------
// Create panels
// ---------------
#ifdef NEO
// Get width and height steps from the networked width direction.
// vHeightDir = Cross(m_vNormal, m_vWidthDir) is guaranteed correct by the server.
Vector vWidthStep = m_vWidthDir * m_flPanelWidth;
Vector vHeightStep = m_vHeightDir * m_flPanelHeight;
#else
QAngle vAngles;
VectorAngles(-1*m_vNormal,vAngles);
Vector vWidthStep,vHeightStep;
AngleVectors(vAngles,NULL,&vWidthStep,&vHeightStep);
vWidthStep *= m_flPanelWidth;
vHeightStep *= m_flPanelHeight;
#endif

Vector vCurPos = m_vCorner;
for (int width=0;width<m_nNumWide;width++)
Expand Down
153 changes: 138 additions & 15 deletions src/game/server/func_breakablesurf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,12 @@ BEGIN_DATADESC( CBreakableSurface )
DEFINE_FIELD( m_nNumHigh, FIELD_INTEGER),
DEFINE_FIELD( m_flPanelWidth, FIELD_FLOAT),
DEFINE_FIELD( m_flPanelHeight, FIELD_FLOAT),
DEFINE_FIELD( m_vNormal, FIELD_VECTOR),
DEFINE_FIELD( m_vCorner, FIELD_POSITION_VECTOR),
DEFINE_FIELD( m_vNormal, FIELD_VECTOR),
DEFINE_FIELD( m_vCorner, FIELD_POSITION_VECTOR),
#ifdef NEO
DEFINE_FIELD( m_vWidthDir, FIELD_VECTOR),
DEFINE_FIELD( m_vHeightDir, FIELD_VECTOR),
#endif
DEFINE_FIELD( m_bIsBroken, FIELD_BOOLEAN),
DEFINE_FIELD( m_nNumBrokenPanes, FIELD_INTEGER),

Expand All @@ -193,6 +197,10 @@ IMPLEMENT_SERVERCLASS_ST(CBreakableSurface, DT_BreakableSurface)
SendPropFloat(SENDINFO(m_flPanelHeight), 0, SPROP_NOSCALE),
SendPropVector(SENDINFO(m_vNormal), -1, SPROP_COORD),
SendPropVector(SENDINFO(m_vCorner), -1, SPROP_COORD),
#ifdef NEO
SendPropVector(SENDINFO(m_vWidthDir), -1, SPROP_NOSCALE),
SendPropVector(SENDINFO(m_vHeightDir), -1, SPROP_NOSCALE),
#endif
SendPropInt(SENDINFO(m_bIsBroken), 1, SPROP_UNSIGNED),
SendPropInt(SENDINFO(m_nSurfaceType), 2, SPROP_UNSIGNED),
SendPropArray3(SENDINFO_ARRAY3(m_RawPanelBitVec), SendPropInt( SENDINFO_ARRAY( m_RawPanelBitVec ), 1, SPROP_UNSIGNED ) ),
Expand Down Expand Up @@ -550,8 +558,12 @@ void CBreakableSurface::TraceAttack( const CTakeDamageInfo &info, const Vector &
{
QAngle vAngles;
VectorAngles(-1*m_vNormal,vAngles);
#ifdef NEO
Vector vHeightDir = m_vHeightDir;
#else
Vector vWidthDir,vHeightDir;
AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir);
#endif

for (int width=0;width<m_nNumWide;width++)
{
Expand All @@ -567,8 +579,12 @@ void CBreakableSurface::TraceAttack( const CTakeDamageInfo &info, const Vector &
// Shatter the strip and start counting again
else if (nHCount > 0)
{
Vector vBreakPos = m_vCorner +
(width*vWidthDir*m_flPanelWidth) +
Vector vBreakPos = m_vCorner +
#ifdef NEO
(width*m_vWidthDir*m_flPanelWidth) +
#else
(width*vWidthDir*m_flPanelWidth) +
#endif
((height-nHCount)*vHeightDir*m_flPanelHeight);

CreateShards(vBreakPos, vAngles,
Expand All @@ -581,8 +597,12 @@ void CBreakableSurface::TraceAttack( const CTakeDamageInfo &info, const Vector &
}
if (nHCount)
{
Vector vBreakPos = m_vCorner +
(width*vWidthDir*m_flPanelWidth) +
Vector vBreakPos = m_vCorner +
#ifdef NEO
(width*m_vWidthDir*m_flPanelWidth) +
#else
(width*vWidthDir*m_flPanelWidth) +
#endif
((height-nHCount)*vHeightDir*m_flPanelHeight);

CreateShards(vBreakPos, vAngles,
Expand Down Expand Up @@ -627,28 +647,39 @@ void CBreakableSurface::Die( CBaseEntity *pBreaker, const Vector &vAttackDir )
m_OnBreak.FireOutput( this, this );
}

#ifdef NEO
// -------------------------------------------------------
// The surface has two sides, when we are killed pick
// the side that the damage came from.
// Compute the normal first so the dot product check works.
// -------------------------------------------------------
Vector vWidth = m_vLLVertex - m_vLRVertex;
Vector vHeight = m_vLLVertex - m_vULVertex;

// Compute normal before the flip check so the dot product is valid.
CrossProduct( vWidth, vHeight, m_vNormal.GetForModify() );
VectorNormalize(m_vNormal.GetForModify());
#endif

float flDir = -1;

if ( vAttackDir.LengthSqr() > 0.001 )
{
float flDot = DotProduct( m_vNormal, vAttackDir );
if (flDot < 0)
{
#ifndef NEO
m_vLLVertex += m_vNormal;
m_vLRVertex += m_vNormal;
m_vULVertex += m_vNormal;
m_vURVertex += m_vNormal;
#endif
m_vNormal *= -1;
flDir = 1;
}
}

// -------------------------------------------------------
// The surface has two sides, when we are killed pick
// the side that the damage came from
// -------------------------------------------------------
Vector vWidth = m_vLLVertex - m_vLRVertex;
Vector vHeight = m_vLLVertex - m_vULVertex;
#ifndef NEO
CrossProduct( vWidth, vHeight, m_vNormal.GetForModify() );
VectorNormalize(m_vNormal.GetForModify());

Expand Down Expand Up @@ -677,10 +708,74 @@ void CBreakableSurface::Die( CBaseEntity *pBreaker, const Vector &vAttackDir )
{
m_vCorner = bLower ? m_vLLVertex : m_vULVertex;
}
else
else
{
m_vCorner = bLower ? m_vLRVertex : m_vURVertex;
}
#else
// ---------------------------------------------------
// Use the original canonical axis directions for robust corner
// selection (handles any vertex label order from the BSP compiler),
// then compute precise m_vWidthDir/m_vHeightDir from vertex positions.
//
// m_vNormal * flDir == -N0 in all cases (N0 = unflipped normal), so
// VectorAngles sees the same argument as the #ifndef NEO path which
// always calls VectorAngles(-1*m_vNormal) with m_vNormal = N0.
// ---------------------------------------------------
QAngle vAngles;
VectorAngles( m_vNormal * flDir, vAngles );
Vector vWidthDir,vHeightDir;
AngleVectors( vAngles, NULL, &vWidthDir, &vHeightDir );

// Swap vWidth/vHeight if the canonical width axis is more aligned with
// the geometric height edge. Unlike the non-NEO path we do NOT scale by
// flDir here: in NEO we never shift the vertices, so flDir has no bearing
// on the edge vectors themselves.
float flWDist = DotProduct( vWidthDir, vWidth );
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be a good idea to normalize wWidth since wWidthDir already seems normalized by this point. That way the dot product would be done with both normalized parameters. Something like this:

Vector vWidthNorm = vWidth;
VectorNormalize( vWidthNorm );
float flWDist = DotProduct( vWidthDir, vWidthNorm );
bool bSwapped = ( fabs(flWDist) < 0.5f );  // < 0.5 ≡ angle > 60° from canonical width

bool bSwapped = ( fabs(flWDist) < 0.5f );
if ( bSwapped )
{
Vector vSaveHeight = vHeight;
vHeight = vWidth;
vWidth = vSaveHeight;
}

// -------------------------------------------------
// Find which corner to use.
// When swapped, vWidth spans the old BSP-height axis (LLV↔ULV) and
// vHeight spans the old BSP-width axis (LLV↔LRV), so the vertex-label
// mapping must be transposed relative to the no-swap case.
// -------------------------------------------------
bool bLeft = (DotProduct(vWidthDir,vWidth) < 0);
bool bLower = (DotProduct(vHeightDir,vHeight) < 0);
if (!bSwapped)
{
if (bLeft)
m_vCorner = bLower ? m_vLLVertex : m_vULVertex;
else
m_vCorner = bLower ? m_vLRVertex : m_vURVertex;
}
else
{
// After the swap "left" selects on the old-height axis → {LLV,LRV},
// and "lower" selects on the old-width axis → {LLV,ULV}.
if (bLeft)
m_vCorner = bLower ? m_vLLVertex : m_vLRVertex;
else
m_vCorner = bLower ? m_vULVertex : m_vURVertex;
}

// Compute precise direction vectors from vertex positions.
// These point FROM the corner TOWARD the adjacent corner in each
// dimension, so panel (w,h) rendering always starts at the correct vertex.
Vector vWN = vWidth;
VectorNormalize( vWN );
m_vWidthDir = vWN * ( bLeft ? -1.0f : 1.0f );

Vector vHN = vHeight;
VectorNormalize( vHN );
m_vHeightDir = vHN * ( bLower ? -1.0f : 1.0f );
#endif

// -------------------------------------------------
// Calculate the number of panels
Expand Down Expand Up @@ -750,10 +845,14 @@ void CBreakableSurface::InputShatter( inputdata_t &inputdata )
if (nMaxY > m_nNumHigh)
nMaxY = m_nNumHigh;

#ifdef NEO
Vector vHeightDir = m_vHeightDir;
#else
QAngle vAngles;
VectorAngles(-1*m_vNormal,vAngles);
Vector vWidthDir,vHeightDir;
AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir);
#endif

// Blow out a roughly circular of tile with some randomness
Vector2D vecActualCenter( flCenterX * m_flPanelWidth, flCenterY * m_flPanelHeight );
Expand All @@ -764,8 +863,12 @@ void CBreakableSurface::InputShatter( inputdata_t &inputdata )
Vector2D pt( (width + 0.5f) * m_flPanelWidth, (height + 0.5f) * m_flPanelWidth );
if ( pt.DistToSqr(vecActualCenter) <= vecShatterInfo.z * vecShatterInfo.z )
{
Vector vBreakPos = m_vCorner +
(width*vWidthDir*m_flPanelWidth) +
Vector vBreakPos = m_vCorner +
#ifdef NEO
(width*m_vWidthDir*m_flPanelWidth) +
#else
(width*vWidthDir*m_flPanelWidth) +
#endif
(height*vHeightDir*m_flPanelHeight);

ShatterPane( width, height, m_vNormal * 500, vBreakPos );
Expand Down Expand Up @@ -1020,12 +1123,18 @@ void CBreakableSurface::BreakThink(void)
void CBreakableSurface::PanePos(const Vector &vPos, float *flWidth, float *flHeight)
{
Vector vAttackVec = vPos - m_vCorner;
#ifdef NEO
Vector vHeightDir = m_vHeightDir;
float flWDist = DotProduct(m_vWidthDir,vAttackVec);
float flHDist = DotProduct(vHeightDir,vAttackVec);
#else
QAngle vAngles;
VectorAngles(-1*m_vNormal,vAngles);
Vector vWidthDir,vHeightDir;
AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir);
float flWDist = DotProduct(vWidthDir,vAttackVec);
float flHDist = DotProduct(vHeightDir,vAttackVec);
#endif

// Figure out which quadrent I'm in
*flWidth = flWDist/m_flPanelWidth;
Expand Down Expand Up @@ -1089,12 +1198,19 @@ void CBreakableSurface::DropPane(int nWidth, int nHeight)

QAngle vAngles;
VectorAngles(-1*m_vNormal,vAngles);
#ifdef NEO
Vector vHeightDir = m_vHeightDir;
Vector vBreakPos = m_vCorner +
(nWidth*m_vWidthDir*m_flPanelWidth) +
(nHeight*vHeightDir*m_flPanelHeight);
#else

Vector vWidthDir,vHeightDir;
AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir);
Vector vBreakPos = m_vCorner +
(nWidth*vWidthDir*m_flPanelWidth) +
(nHeight*vHeightDir*m_flPanelHeight);
#endif

CreateShards(vBreakPos, vAngles, vec3_origin, vec3_origin,
WINDOW_PANEL_SIZE, WINDOW_PANEL_SIZE,
Expand Down Expand Up @@ -1172,11 +1288,18 @@ bool CBreakableSurface::ShatterPane(int nWidth, int nHeight, const Vector &vForc

QAngle vAngles;
VectorAngles(-1*m_vNormal,vAngles);
#ifdef NEO
Vector vHeightDir = m_vHeightDir;
Vector vBreakPos = m_vCorner +
(nWidth*m_vWidthDir*m_flPanelWidth) +
(nHeight*vHeightDir*m_flPanelHeight);
#else
Vector vWidthDir,vHeightDir;
AngleVectors(vAngles,NULL,&vWidthDir,&vHeightDir);
Vector vBreakPos = m_vCorner +
(nWidth*vWidthDir*m_flPanelWidth) +
(nHeight*vHeightDir*m_flPanelHeight);
#endif

CreateShards(vBreakPos, vAngles,vForce, vForcePos, m_flPanelWidth, m_flPanelHeight, WINDOW_SMALL_SHARD_SIZE);

Expand Down
4 changes: 4 additions & 0 deletions src/game/server/func_breakablesurf.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ class CBreakableSurface : public CBreakable
CNetworkVar( float, m_flPanelHeight );
CNetworkVector( m_vNormal );
CNetworkVector( m_vCorner );
#ifdef NEO
CNetworkVector( m_vWidthDir );
CNetworkVector( m_vHeightDir );
#endif
CNetworkVar( bool, m_bIsBroken );
CNetworkVar( ShatterSurface_t, m_nSurfaceType );
int m_nNumBrokenPanes;
Expand Down
Loading