//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include "cbase.h" #include "particles_simple.h" #include "IViewRender.h" #include "ProxyEntity.h" #include "materialsystem/IMaterialVar.h" #include "model_types.h" #include "engine/ivmodelinfo.h" #include "ClientEffectPrecacheSystem.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define MAX_NUM_PANELS 16 extern IVDebugOverlay *debugoverlay; enum WinSide_t { WIN_SIDE_BOTTOM, WIN_SIDE_RIGHT, WIN_SIDE_TOP, WIN_SIDE_LEFT, }; enum WinEdge_t { EDGE_NOT = -1, // No edge EDGE_NONE, // No edge on both sides /##\ EDGE_FULL, // Edge on both sides |##| EDGE_LEFT, // Edge is on left only |##\ EDGE_RIGHT, // Edge is on right only /##| }; #define STYLE_HIGHLIGHT = -1; #define NUM_EDGE_TYPES 4 #define NUM_EDGE_STYLES 3 //================================================== // C_BreakableSurface //================================================== //----------------------------------------------------------------------------- // All the information associated with a particular handle //----------------------------------------------------------------------------- struct Panel_t { char m_nWidth; char m_nHeight; char m_nSide; char m_nEdgeType; char m_nStyle; }; struct EdgeTexture_t { int m_nRenderIndex; int m_nStyle; CMaterialReference m_pMaterialEdge; CTextureReference m_pMaterialEdgeTexture; }; // Bits for m_nPanelBits #define BITS_PANEL_IS_SOLID (1<<0) #define BITS_PANEL_IS_STALE (1<<1) class C_BreakableSurface : public C_BaseEntity, public IBrushRenderer { public: DECLARE_CLIENTCLASS(); DECLARE_CLASS( C_BreakableSurface, C_BaseEntity ); DECLARE_DATADESC(); int m_nNumWide; int m_nNumHigh; float m_flPanelWidth; float m_flPanelHeight; Vector m_vNormal; Vector m_vCorner; bool m_bIsBroken; int m_nSurfaceType; // This is the texture we're going to use to multiply by the cracked base texture ITexture* m_pCurrentDetailTexture; // Stores linked list of edges to render CUtlLinkedList< Panel_t, unsigned short > m_RenderList; C_BreakableSurface(); ~C_BreakableSurface(); public: void InitMaterial(WinEdge_t nEdgeType, int nEdgeStyle, char const* pMaterialName); virtual void OnDataChanged( DataUpdateType_t updateType ); virtual void OnPreDataChanged( DataUpdateType_t updateType ); bool IsTransparent( void ); bool HavePanel(int nWidth, int nHeight); bool RenderBrushModelSurface( IClientEntity* pBaseEntity, IBrushSurface* pBrushSurface ); int DrawModel( int flags ); void DrawSolidBlocks( IBrushSurface* pBrushSurface ); virtual void OnRestore(); virtual bool ShouldReceiveProjectedTextures( int flags ); private: // One bit per pane CNetworkArray( bool, m_RawPanelBitVec, MAX_NUM_PANELS * MAX_NUM_PANELS ); bool m_PrevRawPanelBitVec[ MAX_NUM_PANELS * MAX_NUM_PANELS ]; // 2 bits of flags and 2 bits of edge type byte m_nPanelBits[MAX_NUM_PANELS][MAX_NUM_PANELS]; //UNDONE: allocate this dynamically? CMaterialReference m_pMaterialBox; EdgeTexture_t m_pSolid; EdgeTexture_t m_pEdge[NUM_EDGE_TYPES][NUM_EDGE_STYLES]; inline bool InLegalRange(int nWidth, int nHeight); inline bool IsPanelSolid(int nWidth, int nHeight); inline bool IsPanelStale(int nWidth, int nHeight); inline void SetPanelSolid(int nWidth, int nHeight, bool value); inline void SetPanelStale(int nWidth, int nHeight, bool value); void DrawOneEdge( IBrushSurface* pBrushSurface, IMesh* pMesh, CMeshBuilder *pMeshBuilder, const Vector &vStartPos, const Vector &vWStep, const Vector &vHstep, WinSide_t nEdge); void DrawOneHighlight( IBrushSurface* pBrushSurface, IMesh* pMesh, CMeshBuilder *pMeshBuilder, const Vector &vStartPos, const Vector &vWStep, const Vector &vHstep, WinSide_t nEdge); void DrawOneBlock(IBrushSurface* pBrushSurface, IMesh* pMesh, CMeshBuilder *pMeshBuilder, const Vector &vPosition, const Vector &vWidth, const Vector &vHeight); void DrawRenderList( IBrushSurface* pBrushSurface); void DrawRenderListHighlights( IBrushSurface* pBrushSurface ); int FindRenderPanel(int nWidth, int nHeight, WinSide_t nSide); void AddToRenderList(int nWidth, int nHeight, WinSide_t nSide, WinEdge_t nEdgeType, int forceStyle); int FindFirstRenderTexture(WinEdge_t nEdgeType, int nStyle); inline void SetStyleType( int w, int h, int type ) { Assert( type < NUM_EDGE_STYLES ); Assert( type >= 0 ); // Clear old value m_nPanelBits[ w ][ h ] &= ( ~0x03 << 2 ); // Insert new value m_nPanelBits[ w ][ h ] |= ( type << 2 ); } inline int GetStyleType( int w, int h ) { int value = m_nPanelBits[ w ][ h ]; value = ( value >> 2 ) & 0x03; Assert( value < NUM_EDGE_STYLES ); return value; } // Gets at the cracked version of the material void FindCrackedMaterial(); CMaterialReference m_pCrackedMaterial; CTextureReference m_pMaterialBoxTexture; void UpdateEdgeType(int nWidth, int nHeight, int forceStyle = -1 ); }; BEGIN_DATADESC( C_BreakableSurface ) DEFINE_ARRAY( m_nPanelBits, FIELD_CHARACTER, MAX_NUM_PANELS * MAX_NUM_PANELS ), // DEFINE_FIELD( m_nNumWide, FIELD_INTEGER ), // 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_VECTOR ), // DEFINE_FIELD( m_bIsBroken, FIELD_BOOLEAN ), // DEFINE_FIELD( m_nSurfaceType, FIELD_INTEGER ), // DEFINE_FIELD( m_pCurrentDetailTexture, ITexture* ), // DEFINE_FIELD( m_RenderList, CUtlLinkedList < Panel_t , unsigned short > ), // DEFINE_FIELD( m_pMaterialBox, CMaterialReference ), // DEFINE_FIELD( m_pSolid, EdgeTexture_t ), // DEFINE_ARRAY( m_pEdge, EdgeTexture_t, NUM_EDGE_TYPES][NUM_EDGE_STYLES ), // DEFINE_FIELD( m_pCrackedMaterial, CMaterialReference ), // DEFINE_FIELD( m_pMaterialBoxTexture, CTextureReference ), END_DATADESC() bool C_BreakableSurface::InLegalRange(int nWidth, int nHeight) { return (nWidth < m_nNumWide && nHeight < m_nNumHigh && nWidth >=0 && nHeight >= 0 ); } bool C_BreakableSurface::IsPanelSolid(int nWidth, int nHeight) { return ( BITS_PANEL_IS_SOLID & m_nPanelBits[nWidth][nHeight] )!=0 ; } bool C_BreakableSurface::IsPanelStale(int nWidth, int nHeight) { return ( BITS_PANEL_IS_STALE & m_nPanelBits[nWidth][nHeight] )!=0 ; } void C_BreakableSurface::SetPanelSolid(int nWidth, int nHeight, bool value) { if ( !InLegalRange( nWidth, nHeight ) ) return; if ( value ) { m_nPanelBits[nWidth][nHeight] |= BITS_PANEL_IS_SOLID; } else { m_nPanelBits[nWidth][nHeight] &= ~BITS_PANEL_IS_SOLID; } } void C_BreakableSurface::SetPanelStale(int nWidth, int nHeight, bool value) { if ( !InLegalRange( nWidth, nHeight) ) return; if ( value ) { m_nPanelBits[nWidth][nHeight] |= BITS_PANEL_IS_STALE; } else { m_nPanelBits[nWidth][nHeight] &= ~BITS_PANEL_IS_STALE; } } void C_BreakableSurface::OnRestore() { BaseClass::OnRestore(); // FIXME: This restores the general state, but not the random edge bits // those would need to be serialized separately... // traverse everthing and restore bits // Initialize panels for (int w=0;w<m_nNumWide;w++) { for (int h=0;h<m_nNumHigh;h++) { // Force recomputation SetPanelSolid(w,h,IsPanelSolid(w,h)); SetPanelStale(w,h,true); UpdateEdgeType( w, h, GetStyleType(w,h ) ); } } } //Receive datatable IMPLEMENT_CLIENTCLASS_DT( C_BreakableSurface, DT_BreakableSurface, CBreakableSurface ) RecvPropInt( RECVINFO( m_nNumWide ) ), RecvPropInt( RECVINFO( m_nNumHigh ) ), RecvPropFloat( RECVINFO( m_flPanelWidth) ), RecvPropFloat( RECVINFO( m_flPanelHeight) ), RecvPropVector( RECVINFO( m_vNormal ) ), RecvPropVector( RECVINFO( m_vCorner ) ), RecvPropInt( RECVINFO( m_bIsBroken )), RecvPropInt( RECVINFO( m_nSurfaceType )), RecvPropArray3( RECVINFO_ARRAY(m_RawPanelBitVec), RecvPropInt( RECVINFO( m_RawPanelBitVec[ 0 ] ))), END_RECV_TABLE() //----------------------------------------------------------------------------- // Gets at the cracked version of the material //----------------------------------------------------------------------------- void C_BreakableSurface::FindCrackedMaterial() { m_pCrackedMaterial = 0; // First time we've seen it, get the material on the brush model int materialCount = modelinfo->GetModelMaterialCount( const_cast<model_t*>(GetModel()) ); if( materialCount != 1 ) { Warning( "Encountered func_breakablesurf that has a material applied to more than one surface!\n" ); m_pCrackedMaterial.Init( "debug/debugempty", TEXTURE_GROUP_OTHER ); return; } // Get at the first material; even if there are more than one. IMaterial* pMaterial; modelinfo->GetModelMaterials( const_cast<model_t*>(GetModel()), 1, &pMaterial ); // The material should point to a cracked version of itself bool foundVar; IMaterialVar* pCrackName = pMaterial->FindVar( "$crackmaterial", &foundVar, false ); if (foundVar) { m_pCrackedMaterial.Init( pCrackName->GetStringValue(), TEXTURE_GROUP_CLIENT_EFFECTS ); } else { m_pCrackedMaterial.Init( pMaterial ); } } //----------------------------------------------------------------------------- // Gets at the base texture //----------------------------------------------------------------------------- static ITexture* GetBaseTexture( IMaterial* pMaterial ) { bool foundVar; IMaterialVar* pTextureVar = pMaterial->FindVar( "$basetexture", &foundVar, false ); if (!foundVar) return 0; return pTextureVar->GetTextureValue(); } //------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void C_BreakableSurface::InitMaterial(WinEdge_t nEdgeType, int nEdgeStyle, char const* pMaterialName) { m_pEdge[nEdgeType][nEdgeStyle].m_nRenderIndex = m_RenderList.InvalidIndex(); m_pEdge[nEdgeType][nEdgeStyle].m_nStyle = nEdgeStyle; m_pEdge[nEdgeType][nEdgeStyle].m_pMaterialEdge.Init(pMaterialName, TEXTURE_GROUP_CLIENT_EFFECTS); m_pEdge[nEdgeType][nEdgeStyle].m_pMaterialEdgeTexture.Init( GetBaseTexture( m_pEdge[nEdgeType][nEdgeStyle].m_pMaterialEdge ) ); } //----------------------------------------------------------------------------- // constructor, destructor //----------------------------------------------------------------------------- C_BreakableSurface::C_BreakableSurface() { m_vNormal.Init(); m_vCorner.Init(); m_bIsBroken = false; m_pCurrentDetailTexture = NULL; Q_memset( m_PrevRawPanelBitVec, 0xff, sizeof( m_PrevRawPanelBitVec ) ); } C_BreakableSurface::~C_BreakableSurface() { } void C_BreakableSurface::OnPreDataChanged( DataUpdateType_t updateType ) { BaseClass::OnPreDataChanged( updateType ); if ( updateType == DATA_UPDATE_CREATED ) { // Initialize panels m_nNumWide = MAX_NUM_PANELS; m_nNumHigh = MAX_NUM_PANELS; for (int w=0;w<MAX_NUM_PANELS;w++) { for (int h=0;h<MAX_NUM_PANELS;h++) { SetPanelSolid(w,h,true); SetPanelStale(w,h,false); m_RawPanelBitVec.Set( w + h * MAX_NUM_PANELS, true ); } } } } //----------------------------------------------------------------------------- // Purpose: // Input : bnewentity - //----------------------------------------------------------------------------- void C_BreakableSurface::OnDataChanged( DataUpdateType_t updateType ) { C_BaseEntity::OnDataChanged( updateType ); if ( updateType == DATA_UPDATE_CREATED ) { // Get at the cracked material FindCrackedMaterial(); // Use same solid box for all breakable surfaces m_pMaterialBox.Init( "models/brokenglass/glassbroken_solid", TEXTURE_GROUP_MODEL ); m_pMaterialBoxTexture.Init( GetBaseTexture( m_pMaterialBox ) ); // NOTE: If you add or change this list of materials, change // the precache list on func_breakablesurf.cpp on the server. // Load the edge types and styles for the specific surface type if (m_nSurfaceType == SHATTERSURFACE_TILE) { InitMaterial(EDGE_NONE, 0,"models/brokentile/tilebroken_03a"); InitMaterial(EDGE_FULL, 0,"models/brokentile/tilebroken_03b"); InitMaterial(EDGE_LEFT, 0,"models/brokentile/tilebroken_03c"); InitMaterial(EDGE_RIGHT,0,"models/brokentile/tilebroken_03d"); InitMaterial(EDGE_NONE, 1,"models/brokentile/tilebroken_02a"); InitMaterial(EDGE_FULL, 1,"models/brokentile/tilebroken_02b"); InitMaterial(EDGE_LEFT, 1,"models/brokentile/tilebroken_02c"); InitMaterial(EDGE_RIGHT,1,"models/brokentile/tilebroken_02d"); InitMaterial(EDGE_NONE, 2,"models/brokentile/tilebroken_01a"); InitMaterial(EDGE_FULL, 2,"models/brokentile/tilebroken_01b"); InitMaterial(EDGE_LEFT, 2,"models/brokentile/tilebroken_01c"); InitMaterial(EDGE_RIGHT,2,"models/brokentile/tilebroken_01d"); } else { InitMaterial(EDGE_NONE, 0,"models/brokenglass/glassbroken_03a"); InitMaterial(EDGE_FULL, 0,"models/brokenglass/glassbroken_03b"); InitMaterial(EDGE_LEFT, 0,"models/brokenglass/glassbroken_03c"); InitMaterial(EDGE_RIGHT,0,"models/brokenglass/glassbroken_03d"); InitMaterial(EDGE_NONE, 1,"models/brokenglass/glassbroken_02a"); InitMaterial(EDGE_FULL, 1,"models/brokenglass/glassbroken_02b"); InitMaterial(EDGE_LEFT, 1,"models/brokenglass/glassbroken_02c"); InitMaterial(EDGE_RIGHT,1,"models/brokenglass/glassbroken_02d"); InitMaterial(EDGE_NONE, 2,"models/brokenglass/glassbroken_01a"); InitMaterial(EDGE_FULL, 2,"models/brokenglass/glassbroken_01b"); InitMaterial(EDGE_LEFT, 2,"models/brokenglass/glassbroken_01c"); InitMaterial(EDGE_RIGHT,2,"models/brokenglass/glassbroken_01d"); } } bool changed = false; for ( int j = 0; j < m_nNumHigh; j++ ) { for ( int i = 0; i < m_nNumWide; i++ ) { int offset = i + j * m_nNumWide; bool newVal = m_RawPanelBitVec[ offset ]; bool oldVal = m_PrevRawPanelBitVec[ offset ]; if ( newVal != oldVal ) { changed = true; } SetPanelSolid(i,j,newVal); if ( !newVal && changed ) { // Mark these panels and being stale (need edge type updated) // We update them in one fell swoop rather than as each panel // is updated, so we don't have to do duplicate operations SetPanelStale(i, j ,true); SetPanelStale(i, j+1,true); SetPanelStale(i, j-1,true); SetPanelStale(i-1, j ,true); SetPanelStale(i+1, j ,true); SetPanelStale(i+1, j+1,true); SetPanelStale(i-1, j+1,true); SetPanelStale(i+1, j-1,true); SetPanelStale(i-1, j-1,true); } } } if ( changed ) { for (int width=0;width<m_nNumWide;width++) { for (int height=0;height<m_nNumHigh;height++) { if ( IsPanelStale(width,height) ) { UpdateEdgeType( width, height ); } } } } Q_memcpy( m_PrevRawPanelBitVec, m_RawPanelBitVec.Base(), sizeof( m_PrevRawPanelBitVec ) ); } bool C_BreakableSurface::IsTransparent( void ) { // Not an identity brush if it's broken if (m_bIsBroken) return true; return C_BaseEntity::IsTransparent(); } //------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ int C_BreakableSurface::DrawModel( int flags ) { if ( !m_bReadyToDraw ) return 0; // If I'm not broken draw normally if (m_bIsBroken) render->InstallBrushSurfaceRenderer( this ); // If it's broken, always draw it translucent BaseClass::DrawModel( m_bIsBroken ? flags | STUDIO_TRANSPARENCY : flags ); // Remove our nonstandard brush surface renderer... render->InstallBrushSurfaceRenderer( 0 ); return 0; } bool C_BreakableSurface::RenderBrushModelSurface( IClientEntity* pBaseEntity, IBrushSurface* pBrushSurface ) { // If tile draw highlight for grout if (m_nSurfaceType == SHATTERSURFACE_TILE) { DrawRenderListHighlights(pBrushSurface); } DrawSolidBlocks(pBrushSurface); DrawRenderList(pBrushSurface); // Don't draw decals return false; } //------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void C_BreakableSurface::DrawRenderList(IBrushSurface* pBrushSurface) { // 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; CMeshBuilder pMeshBuilder; IMesh* pMesh = NULL; int nCurStyle = -1; int nCurEdgeType = -1; for( unsigned short i = m_RenderList.Head(); i != m_RenderList.InvalidIndex(); i = m_RenderList.Next(i) ) { if (nCurStyle != m_RenderList[i].m_nStyle || nCurEdgeType != m_RenderList[i].m_nEdgeType ) { nCurStyle = m_RenderList[i].m_nStyle; nCurEdgeType = m_RenderList[i].m_nEdgeType; m_pCurrentDetailTexture = m_pEdge[nCurEdgeType][nCurStyle].m_pMaterialEdgeTexture; materials->Flush(false); materials->Bind(m_pCrackedMaterial, (IClientRenderable*)this); pMesh = materials->GetDynamicMesh( ); } Vector vRenderPos = m_vCorner + (m_RenderList[i].m_nWidth*vWidthStep) + (m_RenderList[i].m_nHeight*vHeightStep); DrawOneEdge(pBrushSurface, pMesh,&pMeshBuilder,vRenderPos,vWidthStep,vHeightStep,(WinSide_t)m_RenderList[i].m_nSide); } } //------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void C_BreakableSurface::DrawRenderListHighlights(IBrushSurface* pBrushSurface) { // 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; CMeshBuilder pMeshBuilder; IMesh* pMesh = NULL; int nCurStyle = -1; int nCurEdgeType = -1; for( unsigned short i = m_RenderList.Head(); i != m_RenderList.InvalidIndex(); i = m_RenderList.Next(i) ) { if (nCurStyle != m_RenderList[i].m_nStyle || nCurEdgeType != m_RenderList[i].m_nEdgeType ) { nCurStyle = m_RenderList[i].m_nStyle; nCurEdgeType = m_RenderList[i].m_nEdgeType; IMaterial *pMat = m_pEdge[nCurEdgeType][nCurStyle].m_pMaterialEdge; pMesh = materials->GetDynamicMesh( true, NULL, NULL, pMat ); } Vector vRenderPos = m_vCorner + (m_RenderList[i].m_nWidth*vWidthStep) + (m_RenderList[i].m_nHeight*vHeightStep) + (0.30*m_vNormal); DrawOneHighlight(pBrushSurface, pMesh,&pMeshBuilder,vRenderPos,vWidthStep,vHeightStep,(WinSide_t)m_RenderList[i].m_nSide); } } //------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ bool C_BreakableSurface::HavePanel(int nWidth, int nHeight) { // If I'm off the edge, always give support if (!InLegalRange(nWidth,nHeight)) { return true; } return (IsPanelSolid(nWidth,nHeight)); } //------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void C_BreakableSurface::UpdateEdgeType(int nWidth, int nHeight, int forceStyle /*=-1*/ ) { Assert( forceStyle < NUM_EDGE_STYLES ); // ----------------------------------- // Check edge conditions // ----------------------------------- if (!InLegalRange(nWidth,nHeight)) { return; } // ---------------------------------- // If solid has no edges // ---------------------------------- if (IsPanelSolid(nWidth,nHeight)) { return; } // Panel is no longer stale SetPanelStale(nWidth, nHeight,false); // ---------------------------------- // Set edge type base on neighbors // ---------------------------------- bool bUp = HavePanel(nWidth, nHeight+1); bool bDown = HavePanel(nWidth, nHeight-1); bool bLeft = HavePanel(nWidth-1, nHeight ); bool bRight = HavePanel(nWidth+1, nHeight ); bool bUpLeft = HavePanel(nWidth-1, nHeight+1); bool bUpRight = HavePanel(nWidth+1, nHeight+1); bool bDownLeft = HavePanel(nWidth-1, nHeight-1); bool bDownRight = HavePanel(nWidth+1, nHeight-1); //------------- // Top //------------- if (bUp) { bool bLeftEdge = !bLeft && bUpLeft; bool bRightEdge = !bRight && bUpRight; if (bLeftEdge && bRightEdge) { AddToRenderList(nWidth, nHeight, WIN_SIDE_TOP, EDGE_FULL, forceStyle ); } else if (bLeftEdge) { AddToRenderList(nWidth, nHeight, WIN_SIDE_TOP, EDGE_LEFT, forceStyle ); } else if (bRightEdge) { AddToRenderList(nWidth, nHeight, WIN_SIDE_TOP, EDGE_RIGHT, forceStyle ); } else { AddToRenderList(nWidth, nHeight, WIN_SIDE_TOP, EDGE_NONE, forceStyle ); } } else { AddToRenderList(nWidth, nHeight, WIN_SIDE_TOP, EDGE_NOT, forceStyle ); } //------------- // Bottom //------------- if (bDown) { bool bLeftEdge = !bLeft && bDownLeft; bool bRightEdge = !bRight && bDownRight; if (bLeftEdge && bRightEdge) { AddToRenderList(nWidth, nHeight, WIN_SIDE_BOTTOM, EDGE_FULL, forceStyle ); } else if (bLeftEdge) { AddToRenderList(nWidth, nHeight, WIN_SIDE_BOTTOM, EDGE_RIGHT, forceStyle ); } else if (bRightEdge) { AddToRenderList(nWidth, nHeight, WIN_SIDE_BOTTOM, EDGE_LEFT, forceStyle ); } else { AddToRenderList(nWidth, nHeight, WIN_SIDE_BOTTOM, EDGE_NONE, forceStyle ); } } else { AddToRenderList(nWidth, nHeight, WIN_SIDE_BOTTOM, EDGE_NOT, forceStyle ); } //------------- // Left //------------- if (bLeft) { bool bTop = !bUp && bUpLeft; bool bBottom = !bDown && bDownLeft; if (bTop && bBottom) { AddToRenderList(nWidth, nHeight, WIN_SIDE_LEFT, EDGE_FULL, forceStyle ); } else if (bTop) { AddToRenderList(nWidth, nHeight, WIN_SIDE_LEFT, EDGE_RIGHT, forceStyle ); } else if (bBottom) { AddToRenderList(nWidth, nHeight, WIN_SIDE_LEFT, EDGE_LEFT, forceStyle ); } else { AddToRenderList(nWidth, nHeight, WIN_SIDE_LEFT, EDGE_NONE, forceStyle ); } } else { AddToRenderList(nWidth, nHeight, WIN_SIDE_LEFT, EDGE_NOT, forceStyle ); } //------------- // Right //------------- if (bRight) { bool bTop = !bUp && bUpRight; bool bBottom = !bDown && bDownRight; if (bTop && bBottom) { AddToRenderList(nWidth, nHeight, WIN_SIDE_RIGHT, EDGE_FULL, forceStyle ); } else if (bTop) { AddToRenderList(nWidth, nHeight, WIN_SIDE_RIGHT, EDGE_LEFT, forceStyle ); } else if (bBottom) { AddToRenderList(nWidth, nHeight, WIN_SIDE_RIGHT, EDGE_RIGHT, forceStyle ); } else { AddToRenderList(nWidth, nHeight, WIN_SIDE_RIGHT, EDGE_NONE, forceStyle ); } } else { AddToRenderList(nWidth, nHeight, WIN_SIDE_RIGHT, EDGE_NOT, forceStyle ); } } //-------------------------------------------------------------------------------- // Purpose : Return index to panel in render list that meets these qualifications // Input : // Output : //-------------------------------------------------------------------------------- int C_BreakableSurface::FindRenderPanel(int nWidth, int nHeight, WinSide_t nWinSide) { for( unsigned short i = m_RenderList.Head(); i != m_RenderList.InvalidIndex(); i = m_RenderList.Next(i) ) { if (m_RenderList[i].m_nSide == nWinSide && m_RenderList[i].m_nWidth == nWidth && m_RenderList[i].m_nHeight == nHeight) { return i; } } return m_RenderList.InvalidIndex(); } //---------------------------------------------------------------------------------- // Purpose : Returns first element in render list with the same edge type and style // Input : // Output : //---------------------------------------------------------------------------------- int C_BreakableSurface::FindFirstRenderTexture(WinEdge_t nEdgeType, int nStyle) { for( unsigned short i = m_RenderList.Head(); i != m_RenderList.InvalidIndex(); i = m_RenderList.Next(i) ) { if (m_RenderList[i].m_nStyle == nStyle && m_RenderList[i].m_nEdgeType == nEdgeType ) { return i; } } return m_RenderList.InvalidIndex(); } //------------------------------------------------------------------------------ // Purpose : Add a edge to be rendered to the render list // Input : // Output : //------------------------------------------------------------------------------ void C_BreakableSurface::AddToRenderList(int nWidth, int nHeight, WinSide_t nSide, WinEdge_t nEdgeType, int forceStyle ) { // ----------------------------------------------------- // Try to find old panel int nOldPanelIndex = FindRenderPanel(nWidth,nHeight,nSide); // ----------------------------------------------------- // If I have an old panel, get it's style and remove it // otherwise randomly chose a style int nStyle; if (m_RenderList.IsValidIndex(nOldPanelIndex) ) { nStyle = m_RenderList[nOldPanelIndex].m_nStyle; m_RenderList.Remove(nOldPanelIndex); } else { nStyle = random->RandomInt(0,NUM_EDGE_STYLES-1); } if ( forceStyle != -1 ) { nStyle = forceStyle; } // ----------------------------------------------------- // If my new panel has an edge, add it to render list if (nEdgeType != EDGE_NOT) { // Renderlist is sorted by texture type. Find first element // that shares the same texture as the new panel unsigned short nTexIndex = FindFirstRenderTexture(nEdgeType, nStyle); // If texture was already in list, add after last use unsigned short nNewIndex; if (m_RenderList.IsValidIndex(nTexIndex)) { nNewIndex = m_RenderList.InsertAfter(nTexIndex); } // Otherwise add to send of render list else { nNewIndex = m_RenderList.AddToTail(); } // Now fill out my data m_RenderList[nNewIndex].m_nHeight = nHeight; m_RenderList[nNewIndex].m_nWidth = nWidth; m_RenderList[nNewIndex].m_nEdgeType = nEdgeType; m_RenderList[nNewIndex].m_nSide = nSide; m_RenderList[nNewIndex].m_nStyle = nStyle; Assert( nStyle < NUM_EDGE_STYLES ); SetStyleType( nWidth, nHeight, nStyle ); } } //------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void C_BreakableSurface::DrawSolidBlocks(IBrushSurface* pBrushSurface) { m_pCurrentDetailTexture = m_pMaterialBoxTexture; // Gotta flush (in a non-stalling way) because we effectively // have a new material due to the new base texture materials->Flush(false); materials->Bind(m_pCrackedMaterial, (IClientRenderable*)this); IMesh* pMesh = materials->GetDynamicMesh( ); CMeshBuilder pMeshBuilder; // --------------- // Create panels // --------------- QAngle vAngles; VectorAngles(-1*m_vNormal,vAngles); Vector vWidthStep,vHeightStep; AngleVectors(vAngles,NULL,&vWidthStep,&vHeightStep); vWidthStep *= m_flPanelWidth; vHeightStep *= m_flPanelHeight; Vector vCurPos = m_vCorner; for (int width=0;width<m_nNumWide;width++) { int height; int nHCount = 0; for (height=0;height<m_nNumHigh;height++) { // Keep count of how many panes there are in a row if (IsPanelSolid(width,height)) { nHCount++; } // Drow the strip and start counting again else if (nHCount > 0) { vCurPos = m_vCorner + vWidthStep*width + vHeightStep*(height-nHCount); DrawOneBlock(pBrushSurface, pMesh, &pMeshBuilder, vCurPos,vWidthStep,vHeightStep*nHCount); nHCount = 0; } } if (nHCount) { vCurPos = m_vCorner + vWidthStep*width + vHeightStep*(height-nHCount); DrawOneBlock(pBrushSurface, pMesh, &pMeshBuilder, vCurPos,vWidthStep,vHeightStep*nHCount); } } } //------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void C_BreakableSurface::DrawOneBlock(IBrushSurface* pBrushSurface, IMesh* pMesh, CMeshBuilder *pMeshBuilder, const Vector &vCurPos, const Vector &vWidthStep, const Vector &vHeightStep) { pMeshBuilder->Begin( pMesh, MATERIAL_QUADS, 1 ); Vector2D texCoord, lightCoord; pBrushSurface->ComputeTextureCoordinate( vCurPos, texCoord ); pBrushSurface->ComputeLightmapCoordinate( vCurPos, lightCoord ); pMeshBuilder->Position3f( vCurPos.x, vCurPos.y, vCurPos.z ); pMeshBuilder->Color4ub( 255, 255, 255, 255 ); pMeshBuilder->TexCoord2f( 0, 0, 1 ); pMeshBuilder->TexCoord2fv( 1, lightCoord.Base() ); pMeshBuilder->TexCoord2fv( 2, texCoord.Base() ); pMeshBuilder->Normal3fv( m_vNormal.Base() ); pMeshBuilder->AdvanceVertex(); Vector vNextPos = vCurPos + vWidthStep; pBrushSurface->ComputeTextureCoordinate( vNextPos, texCoord ); pBrushSurface->ComputeLightmapCoordinate( vNextPos, lightCoord ); pMeshBuilder->Position3f( vNextPos.x, vNextPos.y, vNextPos.z ); pMeshBuilder->Color4ub( 255, 255, 255, 255 ); pMeshBuilder->TexCoord2f( 0, 0, 0 ); pMeshBuilder->TexCoord2fv( 1, lightCoord.Base() ); pMeshBuilder->TexCoord2fv( 2, texCoord.Base() ); pMeshBuilder->Normal3fv( m_vNormal.Base() ); pMeshBuilder->AdvanceVertex(); vNextPos = vNextPos + vHeightStep; pBrushSurface->ComputeTextureCoordinate( vNextPos, texCoord ); pBrushSurface->ComputeLightmapCoordinate( vNextPos, lightCoord ); pMeshBuilder->Position3f( vNextPos.x , vNextPos.y, vNextPos.z ); pMeshBuilder->Color4ub( 255, 255, 255, 255 ); pMeshBuilder->TexCoord2f( 0, 1, 0 ); pMeshBuilder->TexCoord2fv( 1, lightCoord.Base() ); pMeshBuilder->TexCoord2fv( 2, texCoord.Base() ); pMeshBuilder->Normal3fv( m_vNormal.Base() ); pMeshBuilder->AdvanceVertex(); vNextPos = vNextPos - vWidthStep; pBrushSurface->ComputeTextureCoordinate( vNextPos, texCoord ); pBrushSurface->ComputeLightmapCoordinate( vNextPos, lightCoord ); pMeshBuilder->Position3f( vNextPos.x, vNextPos.y , vNextPos.z); pMeshBuilder->Color4ub( 255, 255, 255, 255 ); pMeshBuilder->TexCoord2f( 0, 1, 1 ); pMeshBuilder->TexCoord2fv( 1, lightCoord.Base() ); pMeshBuilder->TexCoord2fv( 2, texCoord.Base() ); pMeshBuilder->Normal3fv( m_vNormal.Base() ); pMeshBuilder->AdvanceVertex(); pMeshBuilder->End(); pMesh->Draw(); } //------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void C_BreakableSurface::DrawOneEdge( IBrushSurface* pBrushSurface, IMesh* pMesh, CMeshBuilder *pMeshBuilder, const Vector &vStartPos, const Vector &vWStep, const Vector &vHStep, WinSide_t nEdge ) { pMeshBuilder->Begin( pMesh, MATERIAL_QUADS, 1 ); Vector2D texCoord, lightCoord; pBrushSurface->ComputeTextureCoordinate( vStartPos, texCoord ); pBrushSurface->ComputeLightmapCoordinate( vStartPos, lightCoord ); pMeshBuilder->Position3f( vStartPos.x, vStartPos.y, vStartPos.z); pMeshBuilder->Normal3fv( m_vNormal.Base() ); pMeshBuilder->Color4ub( 255, 255, 255, 255 ); pMeshBuilder->TexCoord2fv( 1, lightCoord.Base() ); pMeshBuilder->TexCoord2fv( 2, texCoord.Base() ); switch (nEdge) { case WIN_SIDE_RIGHT: pMeshBuilder->TexCoord2f( 0, 1.0f, 0.0f ); break; case WIN_SIDE_BOTTOM: pMeshBuilder->TexCoord2f( 0, 1.0f, 1.0f ); break; case WIN_SIDE_LEFT: pMeshBuilder->TexCoord2f( 0, 0.0f, 1.0f ); break; case WIN_SIDE_TOP: pMeshBuilder->TexCoord2f( 0, 0.0f, 0.0f ); break; } pMeshBuilder->AdvanceVertex(); Vector vNextPos = vStartPos + vWStep; pBrushSurface->ComputeTextureCoordinate( vNextPos, texCoord ); pBrushSurface->ComputeLightmapCoordinate( vNextPos, lightCoord ); pMeshBuilder->Position3f( vNextPos.x , vNextPos.y , vNextPos.z); pMeshBuilder->Normal3fv( m_vNormal.Base() ); pMeshBuilder->Color4ub( 255, 255, 255, 255 ); pMeshBuilder->TexCoord2fv( 1, lightCoord.Base() ); pMeshBuilder->TexCoord2fv( 2, texCoord.Base() ); switch (nEdge) { case WIN_SIDE_RIGHT: pMeshBuilder->TexCoord2f( 0, 1.0f, 1.0f ); break; case WIN_SIDE_BOTTOM: pMeshBuilder->TexCoord2f( 0, 0.0f, 1.0f ); break; case WIN_SIDE_LEFT: pMeshBuilder->TexCoord2f( 0, 0.0f, 0.0f ); break; case WIN_SIDE_TOP: pMeshBuilder->TexCoord2f( 0, 1.0f, 0.0f ); break; } pMeshBuilder->AdvanceVertex(); vNextPos = vNextPos + vHStep; pBrushSurface->ComputeTextureCoordinate( vNextPos, texCoord ); pBrushSurface->ComputeLightmapCoordinate( vNextPos, lightCoord ); pMeshBuilder->Position3f( vNextPos.x, vNextPos.y, vNextPos.z ); pMeshBuilder->Normal3fv( m_vNormal.Base() ); pMeshBuilder->Color4ub( 255, 255, 255, 255 ); pMeshBuilder->TexCoord2fv( 1, lightCoord.Base() ); pMeshBuilder->TexCoord2fv( 2, texCoord.Base() ); switch (nEdge) { case WIN_SIDE_RIGHT: pMeshBuilder->TexCoord2f( 0, 0.0f, 1.0f ); break; case WIN_SIDE_BOTTOM: pMeshBuilder->TexCoord2f( 0, 0.0f, 0.0f ); break; case WIN_SIDE_LEFT: pMeshBuilder->TexCoord2f( 0, 1.0f, 0.0f ); break; case WIN_SIDE_TOP: pMeshBuilder->TexCoord2f( 0, 1.0f, 1.0f ); break; } pMeshBuilder->AdvanceVertex(); vNextPos = vNextPos - vWStep; pBrushSurface->ComputeTextureCoordinate( vNextPos, texCoord ); pBrushSurface->ComputeLightmapCoordinate( vNextPos, lightCoord ); pMeshBuilder->Position3f( vNextPos.x, vNextPos.y, vNextPos.z ); pMeshBuilder->Normal3fv( m_vNormal.Base() ); pMeshBuilder->Color4ub( 255, 255, 255, 255 ); pMeshBuilder->TexCoord2fv( 1, lightCoord.Base() ); pMeshBuilder->TexCoord2fv( 2, texCoord.Base() ); switch (nEdge) { case WIN_SIDE_RIGHT: pMeshBuilder->TexCoord2f( 0, 0.0f, 0.0f ); break; case WIN_SIDE_BOTTOM: pMeshBuilder->TexCoord2f( 0, 1.0f, 0.0f ); break; case WIN_SIDE_LEFT: pMeshBuilder->TexCoord2f( 0, 1.0f, 1.0f ); break; case WIN_SIDE_TOP: pMeshBuilder->TexCoord2f( 0, 0.0f, 1.0f ); break; } pMeshBuilder->AdvanceVertex(); pMeshBuilder->End(); pMesh->Draw(); } //------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void C_BreakableSurface::DrawOneHighlight( IBrushSurface* pBrushSurface, IMesh* pMesh, CMeshBuilder *pMeshBuilder, const Vector &vStartPos, const Vector &vWStep, const Vector &vHStep, WinSide_t nEdge ) { Vector vColor = Vector(0.41,0.35,0.24); pMeshBuilder->Begin( pMesh, MATERIAL_QUADS, 1 ); Vector2D texCoord, lightCoord; pBrushSurface->ComputeTextureCoordinate( vStartPos, texCoord ); pBrushSurface->ComputeLightmapCoordinate( vStartPos, lightCoord ); pMeshBuilder->Position3f( vStartPos.x, vStartPos.y, vStartPos.z); pMeshBuilder->Normal3fv( m_vNormal.Base() ); pMeshBuilder->Color4f( vColor[0], vColor[1], vColor[2], 1.0); pMeshBuilder->TexCoord2fv( 1, lightCoord.Base() ); switch (nEdge) { case WIN_SIDE_RIGHT: pMeshBuilder->TexCoord2f( 0, 1.0f, 0.0f ); break; case WIN_SIDE_BOTTOM: pMeshBuilder->TexCoord2f( 0, 1.0f, 1.0f ); break; case WIN_SIDE_LEFT: pMeshBuilder->TexCoord2f( 0, 0.0f, 1.0f ); break; case WIN_SIDE_TOP: pMeshBuilder->TexCoord2f( 0, 0.0f, 0.0f ); break; } pMeshBuilder->AdvanceVertex(); Vector vNextPos = vStartPos + vWStep; pBrushSurface->ComputeTextureCoordinate( vNextPos, texCoord ); pBrushSurface->ComputeLightmapCoordinate( vNextPos, lightCoord ); pMeshBuilder->Position3f( vNextPos.x , vNextPos.y , vNextPos.z); pMeshBuilder->Normal3fv( m_vNormal.Base() ); pMeshBuilder->Color4f( vColor[0], vColor[1], vColor[2], 1.0); pMeshBuilder->TexCoord2fv( 1, lightCoord.Base() ); switch (nEdge) { case WIN_SIDE_RIGHT: pMeshBuilder->TexCoord2f( 0, 1.0f, 1.0f ); break; case WIN_SIDE_BOTTOM: pMeshBuilder->TexCoord2f( 0, 0.0f, 1.0f ); break; case WIN_SIDE_LEFT: pMeshBuilder->TexCoord2f( 0, 0.0f, 0.0f ); break; case WIN_SIDE_TOP: pMeshBuilder->TexCoord2f( 0, 1.0f, 0.0f ); break; } pMeshBuilder->AdvanceVertex(); vNextPos = vNextPos + vHStep; pBrushSurface->ComputeTextureCoordinate( vNextPos, texCoord ); pBrushSurface->ComputeLightmapCoordinate( vNextPos, lightCoord ); pMeshBuilder->Position3f( vNextPos.x, vNextPos.y, vNextPos.z ); pMeshBuilder->Normal3fv( m_vNormal.Base() ); pMeshBuilder->Color4f( vColor[0], vColor[1], vColor[2], 1.0); pMeshBuilder->TexCoord2fv( 1, lightCoord.Base() ); switch (nEdge) { case WIN_SIDE_RIGHT: pMeshBuilder->TexCoord2f( 0, 0.0f, 1.0f ); break; case WIN_SIDE_BOTTOM: pMeshBuilder->TexCoord2f( 0, 0.0f, 0.0f ); break; case WIN_SIDE_LEFT: pMeshBuilder->TexCoord2f( 0, 1.0f, 0.0f ); break; case WIN_SIDE_TOP: pMeshBuilder->TexCoord2f( 0, 1.0f, 1.0f ); break; } pMeshBuilder->AdvanceVertex(); vNextPos = vNextPos - vWStep; pBrushSurface->ComputeTextureCoordinate( vNextPos, texCoord ); pBrushSurface->ComputeLightmapCoordinate( vNextPos, lightCoord ); pMeshBuilder->Position3f( vNextPos.x, vNextPos.y, vNextPos.z ); pMeshBuilder->Normal3fv( m_vNormal.Base() ); pMeshBuilder->Color4f( vColor[0], vColor[1], vColor[2], 1.0); pMeshBuilder->TexCoord2fv( 1, lightCoord.Base() ); switch (nEdge) { case WIN_SIDE_RIGHT: pMeshBuilder->TexCoord2f( 0, 0.0f, 0.0f ); break; case WIN_SIDE_BOTTOM: pMeshBuilder->TexCoord2f( 0, 1.0f, 0.0f ); break; case WIN_SIDE_LEFT: pMeshBuilder->TexCoord2f( 0, 1.0f, 1.0f ); break; case WIN_SIDE_TOP: pMeshBuilder->TexCoord2f( 0, 0.0f, 1.0f ); break; } pMeshBuilder->AdvanceVertex(); pMeshBuilder->End(); pMesh->Draw(); } bool C_BreakableSurface::ShouldReceiveProjectedTextures( int flags ) { return false; } //------------------------------------------------------------------------------ // A material proxy that resets the texture to use the original surface texture //------------------------------------------------------------------------------ class CBreakableSurfaceProxy : public CEntityMaterialProxy { public: CBreakableSurfaceProxy(); virtual ~CBreakableSurfaceProxy(); virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); virtual void OnBind( C_BaseEntity *pC_BaseEntity ); private: // get at the material whose texture we're going to steal void AcquireSourceMaterial( C_BaseEntity* pEnt ); IMaterialVar* m_BaseTextureVar; }; CBreakableSurfaceProxy::CBreakableSurfaceProxy() { m_BaseTextureVar = NULL; } CBreakableSurfaceProxy::~CBreakableSurfaceProxy() { } bool CBreakableSurfaceProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) { bool foundVar; m_BaseTextureVar = pMaterial->FindVar( "$basetexture", &foundVar, false ); return foundVar; } void CBreakableSurfaceProxy::OnBind( C_BaseEntity *pC_BaseEntity ) { C_BreakableSurface *pEnt = dynamic_cast< C_BreakableSurface * >(pC_BaseEntity); if( !pEnt ) { return; } // Use the current base texture specified by the suface m_BaseTextureVar->SetTextureValue( pEnt->m_pCurrentDetailTexture ); } EXPOSE_INTERFACE( CBreakableSurfaceProxy, IMaterialProxy, "BreakableSurface" IMATERIAL_PROXY_INTERFACE_VERSION );
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 5821 | Knut Wikstrom |
Added Valve Source code. This is NOT to be commited to other than new code from Valve. |