diff --git a/src/game/client/c_func_breakablesurf.cpp b/src/game/client/c_func_breakablesurf.cpp index 8a9d2d1da..00295b4b0 100644 --- a/src/game/client/c_func_breakablesurf.cpp +++ b/src/game/client/c_func_breakablesurf.cpp @@ -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; +#endif bool m_bIsBroken; int m_nSurfaceType; @@ -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 ] ))), @@ -548,6 +556,12 @@ 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); @@ -555,6 +569,7 @@ void C_BreakableSurface::DrawRenderList(IBrushSurface* pBrushSurface) AngleVectors(vAngles,NULL,&vWidthStep,&vHeightStep); vWidthStep *= m_flPanelWidth; vHeightStep *= m_flPanelHeight; +#endif CMeshBuilder pMeshBuilder; IMesh* pMesh = NULL; @@ -592,6 +607,12 @@ 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); @@ -599,6 +620,7 @@ void C_BreakableSurface::DrawRenderListHighlights(IBrushSurface* pBrushSurface) AngleVectors(vAngles,NULL,&vWidthStep,&vHeightStep); vWidthStep *= m_flPanelWidth; vHeightStep *= m_flPanelHeight; +#endif CMeshBuilder pMeshBuilder; @@ -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 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, @@ -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, @@ -627,6 +647,20 @@ 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 ) @@ -634,21 +668,18 @@ void CBreakableSurface::Die( CBaseEntity *pBreaker, const Vector &vAttackDir ) 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()); @@ -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 ); + 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 @@ -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 ); @@ -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 ); @@ -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; @@ -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, @@ -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); diff --git a/src/game/server/func_breakablesurf.h b/src/game/server/func_breakablesurf.h index 58e4f0f24..a210dc5fc 100644 --- a/src/game/server/func_breakablesurf.h +++ b/src/game/server/func_breakablesurf.h @@ -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;