diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/mapoverlay.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'hammer/mapoverlay.cpp')
| -rw-r--r-- | hammer/mapoverlay.cpp | 2749 |
1 files changed, 2749 insertions, 0 deletions
diff --git a/hammer/mapoverlay.cpp b/hammer/mapoverlay.cpp new file mode 100644 index 0000000..e45c7cc --- /dev/null +++ b/hammer/mapoverlay.cpp @@ -0,0 +1,2749 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include <stdafx.h> +#include "MapOverlay.h" +#include "MapFace.h" +#include "MapSolid.h" +#include "MapWorld.h" +#include "MainFrm.h" +#include "GlobalFunctions.h" +#include "MapDoc.h" +#include "TextureSystem.h" +#include "Material.h" +#include "materialsystem/imesh.h" +#include "Box3D.h" +#include "MapDefs.h" +#include "CollisionUtils.h" +#include "MapSideList.h" +#include "MapDisp.h" +#include "ToolManager.h" +#include "objectproperties.h" +#include "ChunkFile.h" +#include "mapview.h" +#include "options.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + +IMPLEMENT_MAPCLASS( CMapOverlay ) + +#define OVERLAY_INITSIZE 25.0f // x2 + +#define OVERLAY_BASIS_U 0 +#define OVERLAY_BASIS_V 1 +#define OVERLAY_BASIS_NORMAL 2 + +#define OVERLAY_HANDLES_COUNT 4 + +#define OVERLAY_WORLDSPACE_EPSILON 0.03125f +#define OVERLAY_DISPSPACE_EPSILON 0.000001f +#define OVERLAY_BARYCENTRIC_EPSILON 0.001f + +#define OVERLAY_BLENDTYPE_VERT 0 +#define OVERLAY_BLENDTYPE_EDGE 1 +#define OVERLAY_BLENDTYPE_BARY 2 +#define OVERLAY_ANGLE0 1 +#define OVERLAY_ANGLE45 2 +#define OVERLAY_ANGLE90 3 +#define OVERLAY_ANGLE135 4 + +#define OVERLAY_INVALID_VALUE -99999.9f + +//============================================================================= +// +// Basis Functions +// + +//----------------------------------------------------------------------------- +// Purpose: Initialize the basis data. +//----------------------------------------------------------------------------- +void CMapOverlay::Basis_Clear( void ) +{ + m_Basis.m_pFace = NULL; + m_Basis.m_vecOrigin.Init(); + + for( int iAxis = 0; iAxis < 3; iAxis++ ) + { + m_Basis.m_vecAxes[iAxis].Init( OVERLAY_INVALID_VALUE, OVERLAY_INVALID_VALUE, OVERLAY_INVALID_VALUE ); + m_Basis.m_nAxesFlip[iAxis] = 0; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Build the overlay basis given an entity and base face (CMapFace). +//----------------------------------------------------------------------------- +void CMapOverlay::Basis_Init( CMapFace *pFace ) +{ + // Valid face? + Assert( pFace != NULL ); + if( !pFace ) + return; + + // Set the face the basis are derived from. + Basis_SetFace( pFace ); + + // Set the basis origin. + Basis_UpdateOrigin(); + + // Setup the basis axes. + Basis_BuildAxes(); + + // Initialize the texture coordinates - based on basis. + Material_TexCoordInit(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Basis_UpdateOrigin( void ) +{ + CMapEntity *pEntity = static_cast<CMapEntity*>( GetParent() ); + if ( pEntity ) + { + Vector vecEntityOrigin; + pEntity->GetOrigin( vecEntityOrigin ); + + Vector vecPoint( 0.0f, 0.0f, 0.0f ); + if ( !EntityOnSurfFromListToBaseFacePlane( vecEntityOrigin, vecPoint ) ) + { + vecPoint = vecEntityOrigin; + } + + m_Basis.m_vecOrigin = vecPoint; + } + + // Update the property box. + Basis_UpdateParentKey(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Basis_BuildAxes( void ) +{ + // Valid face? + if( !m_Basis.m_pFace ) + return; + + // Build the basis axes. + Vector vecFaceNormal; + m_Basis.m_pFace->GetFaceNormal( vecFaceNormal ); + VectorNormalize( vecFaceNormal ); + VectorCopy( vecFaceNormal, m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL] ); + + Basis_SetInitialUAxis( vecFaceNormal ); + + m_Basis.m_vecAxes[OVERLAY_BASIS_V] = m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL].Cross( m_Basis.m_vecAxes[OVERLAY_BASIS_U] ); + VectorNormalize( m_Basis.m_vecAxes[OVERLAY_BASIS_V] ); + + m_Basis.m_vecAxes[OVERLAY_BASIS_U] = m_Basis.m_vecAxes[OVERLAY_BASIS_V].Cross( m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL] ); + VectorNormalize( m_Basis.m_vecAxes[OVERLAY_BASIS_U] ); + + // Flip uvn axes? + for ( int iAxis = 0; iAxis < 3; ++iAxis ) + { + for ( int iComp = 0; iComp < 3; ++iComp ) + { + if ( Basis_IsFlipped( iAxis, iComp ) ) + { + m_Basis.m_vecAxes[iAxis][iComp] = -m_Basis.m_vecAxes[iAxis][iComp]; + } + } + } + + Basis_UpdateParentKey(); +} + +//----------------------------------------------------------------------------- +// Purpose: A basis building helper function that finds the best guess u-axis +// given a base face (CMapFace) normal. +// Input: vecNormal - the base face normal +//----------------------------------------------------------------------------- +void CMapOverlay::Basis_SetInitialUAxis( Vector const &vecNormal ) +{ + // Find the major vector component. + int nMajorAxis = 0; + float flAxisValue = vecNormal[0]; + if ( FloatMakePositive( vecNormal[1] ) > FloatMakePositive( flAxisValue ) ) + { + nMajorAxis = 1; + flAxisValue = vecNormal[1]; + } + if ( FloatMakePositive( vecNormal[2] ) > FloatMakePositive( flAxisValue ) ) + { + nMajorAxis = 2; + } + + if ( ( nMajorAxis == 1 ) || ( nMajorAxis == 2 ) ) + { + m_Basis.m_vecAxes[OVERLAY_BASIS_U].Init( 1.0f, 0.0f, 0.0f ); + } + else + { + m_Basis.m_vecAxes[OVERLAY_BASIS_U].Init( 0.0f, 1.0f, 0.0f ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CMapOverlay::Basis_IsValid( void ) +{ + for ( int iBasis = 0; iBasis < 3; ++iBasis ) + { + for ( int iAxis = 0; iAxis < 3; ++iAxis ) + { + if ( m_Basis.m_vecAxes[iBasis][iAxis] == OVERLAY_INVALID_VALUE ) + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Basis_SetFace( CMapFace *pFace ) +{ + // Verify face. + if ( !pFace ) + return; + + m_Basis.m_pFace = pFace; +} + +//----------------------------------------------------------------------------- +// Purpose: Copy the basis data from the source into the destination. +// Input: pSrc - the basis source data +// pDst (Output) - destination for the basis data +//----------------------------------------------------------------------------- +void CMapOverlay::Basis_Copy( Basis_t *pSrc, Basis_t *pDst ) +{ + pDst->m_pFace = pSrc->m_pFace; + pDst->m_vecOrigin = pSrc->m_vecOrigin; + + for ( int iAxis = 0; iAxis < 3; iAxis++ ) + { + pDst->m_vecAxes[iAxis] = pSrc->m_vecAxes[iAxis]; + pDst->m_nAxesFlip[iAxis] = pSrc->m_nAxesFlip[iAxis]; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Basis_UpdateParentKey( void ) +{ + char szValue[80]; + + CMapEntity *pEntity = ( CMapEntity* )GetParent(); + if ( pEntity ) + { + sprintf( szValue, "%g %g %g", m_Basis.m_vecOrigin.x, m_Basis.m_vecOrigin.y, m_Basis.m_vecOrigin.z ); + pEntity->NotifyChildKeyChanged( this, "BasisOrigin", szValue ); + + sprintf( szValue, "%g %g %g", m_Basis.m_vecAxes[OVERLAY_BASIS_U].x, m_Basis.m_vecAxes[OVERLAY_BASIS_U].y, m_Basis.m_vecAxes[OVERLAY_BASIS_U].z ); + pEntity->NotifyChildKeyChanged( this, "BasisU", szValue ); + + sprintf( szValue, "%g %g %g", m_Basis.m_vecAxes[OVERLAY_BASIS_V].x, m_Basis.m_vecAxes[OVERLAY_BASIS_V].y, m_Basis.m_vecAxes[OVERLAY_BASIS_V].z ); + pEntity->NotifyChildKeyChanged( this, "BasisV", szValue ); + + sprintf( szValue, "%g %g %g", m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL].x, m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL].y, m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL].z ); + pEntity->NotifyChildKeyChanged( this, "BasisNormal", szValue ); + } +} + +//============================================================================= +// +// Basis - Legacy support! +// + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Basis_BuildFromSideList( void ) +{ + // Initialization (don't have or couldn't find the basis face) + if ( m_Faces.Count() > 0 ) + { + Basis_Init( m_Faces.Element( 0 ) ); + } + else + { + m_Basis.m_pFace = NULL; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input: iAxis - 0, 1, 2 (u, v, n) +// iComponet - 0, 1, 2 (x, y, z) +//----------------------------------------------------------------------------- +void CMapOverlay::Basis_ToggleAxesFlip( int iAxis, int iComponent ) +{ + if ( iAxis < 0 || iAxis > 2 || iComponent < 0 || iComponent > 2 ) + return; + + int nValue = ( 1 << iComponent ); + m_Basis.m_nAxesFlip[iAxis] ^= nValue; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CMapOverlay::Basis_IsFlipped( int iAxis, int iComponent ) +{ + if ( iAxis < 0 || iAxis > 2 || iComponent < 0 || iComponent > 2 ) + return false; + + int nValue = ( 1 << iComponent ); + return ( ( m_Basis.m_nAxesFlip[iAxis] & nValue ) != 0 ); +} + +//============================================================================= +// +// Handles Functions +// + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Handles_Clear( void ) +{ + m_Handles.m_iHit = -1; + + for ( int iHandle = 0; iHandle < OVERLAY_HANDLES_COUNT; iHandle++ ) + { + m_Handles.m_vec3D[iHandle].Init(); + } + + m_Handles.m_vecBasisCoords[0].Init( -OVERLAY_INITSIZE, -OVERLAY_INITSIZE ); + m_Handles.m_vecBasisCoords[1].Init( -OVERLAY_INITSIZE, OVERLAY_INITSIZE ); + m_Handles.m_vecBasisCoords[2].Init( OVERLAY_INITSIZE, OVERLAY_INITSIZE ); + m_Handles.m_vecBasisCoords[3].Init( OVERLAY_INITSIZE, -OVERLAY_INITSIZE ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Handles_Init( CMapFace *pFace ) +{ + IEditorTexture *pTexture = g_Textures.FindActiveTexture( GetDefaultTextureName() ); + int nWidth = pTexture->GetImageWidth(); + int nHeight = pTexture->GetImageHeight(); + + // Half-height (width) and 1/4 scale + int nWidthHalf = nWidth / 8; + int nHeightHalf = nHeight / 8; + + m_Handles.m_vecBasisCoords[0].Init( -nWidthHalf, -nHeightHalf ); + m_Handles.m_vecBasisCoords[1].Init( -nWidthHalf, nHeightHalf ); + m_Handles.m_vecBasisCoords[2].Init( nWidthHalf, nHeightHalf ); + m_Handles.m_vecBasisCoords[3].Init( nWidthHalf, -nHeightHalf ); + + Handles_Build3D(); + + Handles_UpdateParentKey(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Handles_Build3D( void ) +{ + // Verify that we have a valid basis to build the handles from. + if ( !Basis_IsValid() ) + return; + + for ( int iHandle = 0; iHandle < OVERLAY_HANDLES_COUNT; iHandle++ ) + { + Vector vecHandle; + OverlayUVToOverlayPlane( m_Handles.m_vecBasisCoords[iHandle], vecHandle ); + OverlayPlaneToSurfFromList( vecHandle, m_Handles.m_vec3D[iHandle] ); + } + + Handles_FixOrder(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Handles_Render3D( CRender3D *pRender ) +{ + // Set the render mode to "flat." + pRender->PushRenderMode( RENDER_MODE_FLAT ); + + // Set the color, should be based on selection. + unsigned char ucColor[4]; + ucColor[0] = ucColor[1] = ucColor[2] = ucColor[3] = 255; + + unsigned char ucSelectColor[4]; + ucSelectColor[0] = ucSelectColor[3] = 255; + ucSelectColor[1] = ucSelectColor[2] = 0; + + pRender->SetHandleStyle( HANDLE_RADIUS, CRender::HANDLE_SQUARE ); + + for ( int iHandle = 0; iHandle < OVERLAY_HANDLES_COUNT; iHandle++ ) + { + pRender->BeginRenderHitTarget( this, iHandle ); + if ( m_Handles.m_iHit == iHandle ) + { + pRender->SetHandleColor( ucSelectColor[0], ucSelectColor[1], ucSelectColor[2] ); + } + else + { + pRender->SetHandleColor( ucColor[0], ucColor[1], ucColor[2] ); + } + + pRender->DrawHandle( m_Handles.m_vec3D[iHandle] ); + + pRender->EndRenderHitTarget(); + } + + pRender->PopRenderMode(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Handles_SurfToOverlayPlane( CMapFace *pFace, Vector const &vecSurf, Vector &vecPoint ) +{ + Vector vecWorld; + if ( pFace->HasDisp() ) + { + EditDispHandle_t handle = pFace->GetDisp(); + CMapDisp *pDisp = EditDispMgr()->GetDisp( handle ); + pDisp->SurfToBaseFacePlane( vecSurf, vecWorld ); + } + else + { + vecWorld = vecSurf; + } + + WorldToOverlayPlane( vecWorld, vecPoint ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Handles_Copy( Handles_t *pSrc, Handles_t *pDst ) +{ + pDst->m_iHit = pSrc->m_iHit; + + for ( int iHandle = 0; iHandle < OVERLAY_HANDLES_COUNT; ++iHandle ) + { + pDst->m_vecBasisCoords[iHandle] = pSrc->m_vecBasisCoords[iHandle]; + pDst->m_vec3D[iHandle] = pSrc->m_vec3D[iHandle]; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Handles_UpdateParentKey( void ) +{ + char szValue[80]; + + CMapEntity *pEntity = ( CMapEntity* )GetParent(); + if ( pEntity ) + { + sprintf( szValue, "%g %g %g", m_Handles.m_vecBasisCoords[0].x, m_Handles.m_vecBasisCoords[0].y, ( float )m_Basis.m_nAxesFlip[0] ); + pEntity->NotifyChildKeyChanged( this, "uv0", szValue ); + + sprintf( szValue, "%g %g %g", m_Handles.m_vecBasisCoords[1].x, m_Handles.m_vecBasisCoords[1].y, ( float )m_Basis.m_nAxesFlip[1] ); + pEntity->NotifyChildKeyChanged( this, "uv1", szValue ); + + sprintf( szValue, "%g %g %g", m_Handles.m_vecBasisCoords[2].x, m_Handles.m_vecBasisCoords[2].y, ( float )m_Basis.m_nAxesFlip[2] ); + pEntity->NotifyChildKeyChanged( this, "uv2", szValue ); + + sprintf( szValue, "%g %g %g", m_Handles.m_vecBasisCoords[3].x, m_Handles.m_vecBasisCoords[3].y, 0.0f ); + pEntity->NotifyChildKeyChanged( this, "uv3", szValue ); + } +} + +//============================================================================= +// +// ClipFace Functions +// + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CMapOverlay::ClipFace_t *CMapOverlay::ClipFace_Create( int nSize ) +{ + ClipFace_t *pClipFace = new ClipFace_t; + if ( pClipFace ) + { + pClipFace->m_nPointCount = nSize; + if ( nSize > 0 ) + { + pClipFace->m_aPoints.SetSize( nSize ); + pClipFace->m_aDispPointUVs.SetSize( nSize ); + + for ( int iCoord = 0; iCoord < NUM_CLIPFACE_TEXCOORDS; iCoord++ ) + { + pClipFace->m_aTexCoords[iCoord].SetSize( nSize ); + } + + pClipFace->m_aBlends.SetSize( nSize ); + + for ( int iPoint = 0; iPoint < nSize; iPoint++ ) + { + pClipFace->m_aPoints[iPoint].Init(); + pClipFace->m_aDispPointUVs[iPoint].Init(); + pClipFace->m_aBlends[iPoint].Init(); + + for ( int iCoord = 0; iCoord < NUM_CLIPFACE_TEXCOORDS; iCoord++ ) + { + pClipFace->m_aTexCoords[iCoord][iPoint].Init(); + } + } + } + } + + return pClipFace; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::ClipFace_Destroy( ClipFace_t **ppClipFace ) +{ + if( *ppClipFace ) + { + delete *ppClipFace; + *ppClipFace = NULL; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CMapOverlay::ClipFace_t *CMapOverlay::ClipFace_Copy( ClipFace_t *pSrc ) +{ + ClipFace_t *pDst = ClipFace_Create( pSrc->m_nPointCount ); + if ( pDst ) + { + for ( int iPoint = 0; iPoint < pSrc->m_nPointCount; iPoint++ ) + { + pDst->m_aPoints[iPoint] = pSrc->m_aPoints[iPoint]; + pDst->m_aDispPointUVs[iPoint] = pSrc->m_aDispPointUVs[iPoint]; + for ( int iTexCoord=0; iTexCoord < NUM_CLIPFACE_TEXCOORDS; iTexCoord++ ) + { + pDst->m_aTexCoords[iTexCoord][iPoint] = pSrc->m_aTexCoords[iTexCoord][iPoint]; + } + + pDst->m_aBlends[iPoint].m_nType = pSrc->m_aBlends[iPoint].m_nType; + for ( int iBlend = 0; iBlend < 3; iBlend++ ) + { + pDst->m_aBlends[iPoint].m_iPoints[iBlend] = pSrc->m_aBlends[iPoint].m_iPoints[iBlend]; + pDst->m_aBlends[iPoint].m_flBlends[iBlend] = pSrc->m_aBlends[iPoint].m_flBlends[iBlend]; + } + } + } + + return pDst; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::ClipFace_GetBounds( ClipFace_t *pClipFace, Vector &vecMin, Vector &vecMax ) +{ + if ( pClipFace ) + { + vecMin = vecMax = pClipFace->m_aPoints.Element( 0 ); + + for ( int iPoints = 1; iPoints < pClipFace->m_nPointCount; iPoints++ ) + { + Vector vecPoint = pClipFace->m_aPoints.Element( iPoints ); + + // Min + if ( vecMin.x > vecPoint.x ) { vecMin.x = vecPoint.x; } + if ( vecMin.y > vecPoint.y ) { vecMin.y = vecPoint.y; } + if ( vecMin.z > vecPoint.z ) { vecMin.z = vecPoint.z; } + + // Max + if ( vecMax.x < vecPoint.x ) { vecMax.x = vecPoint.x; } + if ( vecMax.y < vecPoint.y ) { vecMax.y = vecPoint.y; } + if ( vecMax.z < vecPoint.z ) { vecMax.z = vecPoint.z; } + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::ClipFace_Clip( ClipFace_t *pClipFace, cplane_t *pClipPlane, float flEpsilon, + ClipFace_t **ppFront, ClipFace_t **ppBack ) +{ + if ( !pClipFace ) + return; + + float flDists[128]; + int nSides[128]; + int nSideCounts[3]; + + // Initialize + *ppFront = *ppBack = NULL; + + // Determine "sidedness" of all the polygon points. + nSideCounts[0] = nSideCounts[1] = nSideCounts[2] = 0; + int iPoint; + for ( iPoint = 0; iPoint < pClipFace->m_nPointCount; iPoint++ ) + { + flDists[iPoint] = pClipPlane->normal.Dot( pClipFace->m_aPoints.Element( iPoint ) ) - pClipPlane->dist; + + if ( flDists[iPoint] > flEpsilon ) + { + nSides[iPoint] = SIDE_FRONT; + } + else if ( flDists[iPoint] < -flEpsilon ) + { + nSides[iPoint] = SIDE_BACK; + } + else + { + nSides[iPoint] = SIDE_ON; + } + + nSideCounts[nSides[iPoint]]++; + } + + // Wrap around (close the polygon). + nSides[iPoint] = nSides[0]; + flDists[iPoint] = flDists[0]; + + // All points in back - no split (copy face to back). + if( !nSideCounts[SIDE_FRONT] ) + { + *ppBack = ClipFace_Copy( pClipFace ); + return; + } + + // All points in front - no split (copy face to front). + if( !nSideCounts[SIDE_BACK] ) + { + *ppFront = ClipFace_Copy( pClipFace ); + return; + } + + // Build new front and back faces. Leave room for two extra points on each side because any + // point might be on the plane, which would put it into both the front and back sides, and then + // we need to allow for an additional vertex created by clipping. + ClipFace_t *pFront = ClipFace_Create( pClipFace->m_nPointCount + 2 ); + ClipFace_t *pBack = ClipFace_Create( pClipFace->m_nPointCount + 2 ); + if ( !pFront || !pBack ) + { + ClipFace_Destroy( &pFront ); + ClipFace_Destroy( &pBack ); + return; + } + + // Reset the counts as they are used to build the surface. + pFront->m_nPointCount = 0; + pBack->m_nPointCount = 0; + + // For every point on the face being clipped, determine which side of the clipping plane it is on + // and add it to a either a front list or a back list. Points that are on the plane are added to + // both lists. + for ( iPoint = 0; iPoint < pClipFace->m_nPointCount; iPoint++ ) + { + // "On" clip plane. + if ( nSides[iPoint] == SIDE_ON ) + { + pFront->m_aPoints[pFront->m_nPointCount] = pClipFace->m_aPoints[iPoint]; + for ( int iTexCoord=0; iTexCoord < NUM_CLIPFACE_TEXCOORDS; iTexCoord++ ) + pFront->m_aTexCoords[iTexCoord][pFront->m_nPointCount] = pClipFace->m_aTexCoords[iTexCoord][iPoint]; + pFront->m_nPointCount++; + + pBack->m_aPoints[pBack->m_nPointCount] = pClipFace->m_aPoints[iPoint]; + for ( int iTexCoord=0; iTexCoord < NUM_CLIPFACE_TEXCOORDS; iTexCoord++ ) + pBack->m_aTexCoords[iTexCoord][pBack->m_nPointCount] = pClipFace->m_aTexCoords[iTexCoord][iPoint]; + pBack->m_nPointCount++; + + continue; + } + + // "In back" of clip plane. + if ( nSides[iPoint] == SIDE_BACK ) + { + pBack->m_aPoints[pBack->m_nPointCount] = pClipFace->m_aPoints[iPoint]; + for ( int iTexCoord=0; iTexCoord < NUM_CLIPFACE_TEXCOORDS; iTexCoord++ ) + pBack->m_aTexCoords[iTexCoord][pBack->m_nPointCount] = pClipFace->m_aTexCoords[iTexCoord][iPoint]; + pBack->m_nPointCount++; + } + + // "In front" of clip plane. + if ( nSides[iPoint] == SIDE_FRONT ) + { + pFront->m_aPoints[pFront->m_nPointCount] = pClipFace->m_aPoints[iPoint]; + for ( int iTexCoord=0; iTexCoord < NUM_CLIPFACE_TEXCOORDS; iTexCoord++ ) + pFront->m_aTexCoords[iTexCoord][pFront->m_nPointCount] = pClipFace->m_aTexCoords[iTexCoord][iPoint]; + pFront->m_nPointCount++; + } + + if ( nSides[iPoint+1] == SIDE_ON || nSides[iPoint+1] == nSides[iPoint] ) + continue; + + // Split! + float fraction = flDists[iPoint] / ( flDists[iPoint] - flDists[iPoint+1] ); + + Vector vecPoint = pClipFace->m_aPoints[iPoint] + ( pClipFace->m_aPoints[(iPoint+1)%pClipFace->m_nPointCount] - pClipFace->m_aPoints[iPoint] ) * fraction; + for ( int iTexCoord=0; iTexCoord < NUM_CLIPFACE_TEXCOORDS; iTexCoord++ ) + { + Vector2D vecTexCoord = pClipFace->m_aTexCoords[iTexCoord][iPoint] + ( pClipFace->m_aTexCoords[iTexCoord][(iPoint+1)%pClipFace->m_nPointCount] - pClipFace->m_aTexCoords[iTexCoord][iPoint] ) * fraction; + pFront->m_aTexCoords[iTexCoord][pFront->m_nPointCount] = vecTexCoord; + pBack->m_aTexCoords[iTexCoord][pBack->m_nPointCount] = vecTexCoord; + } + + pFront->m_aPoints[pFront->m_nPointCount] = vecPoint; + pFront->m_nPointCount++; + + pBack->m_aPoints[pBack->m_nPointCount] = vecPoint; + pBack->m_nPointCount++; + } + + *ppFront = pFront; + *ppBack = pBack; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::ClipFace_ClipBarycentric( ClipFace_t *pClipFace, cplane_t *pClipPlane, float flEpsilon, + int iClip, CMapDisp *pDisp, + ClipFace_t **ppFront, ClipFace_t **ppBack ) +{ + if ( !pClipFace ) + return; + + float flDists[128]; + int nSides[128]; + int nSideCounts[3]; + + // Determine "sidedness" of all the polygon points. + nSideCounts[0] = nSideCounts[1] = nSideCounts[2] = 0; + int iPoint; + for ( iPoint = 0; iPoint < pClipFace->m_nPointCount; iPoint++ ) + { + flDists[iPoint] = pClipPlane->normal.Dot( pClipFace->m_aDispPointUVs.Element( iPoint ) ) - pClipPlane->dist; + + if ( flDists[iPoint] > flEpsilon ) + { + nSides[iPoint] = SIDE_FRONT; + } + else if ( flDists[iPoint] < -flEpsilon ) + { + nSides[iPoint] = SIDE_BACK; + } + else + { + nSides[iPoint] = SIDE_ON; + } + + nSideCounts[nSides[iPoint]]++; + } + + // Wrap around (close the polygon). + nSides[iPoint] = nSides[0]; + flDists[iPoint] = flDists[0]; + + // All points in back - no split (copy face to back). + if( !nSideCounts[SIDE_FRONT] ) + { + *ppBack = ClipFace_Copy( pClipFace ); + return; + } + + // All points in front - no split (copy face to front). + if( !nSideCounts[SIDE_BACK] ) + { + *ppFront = ClipFace_Copy( pClipFace ); + return; + } + + // Build new front and back faces. + // NOTE: We are allowing to go over by 2 and then destroy the surface later. The old system + // allowed for some bad data and we need to be able to load the map and destroy the surface! + int nMaxPointCount = pClipFace->m_nPointCount + 1; + ClipFace_t *pFront = ClipFace_Create( nMaxPointCount + 2 ); + ClipFace_t *pBack = ClipFace_Create( nMaxPointCount + 2 ); + if ( !pFront || !pBack ) + { + ClipFace_Destroy( &pFront ); + ClipFace_Destroy( &pBack ); + return; + } + + // Reset the counts as they are used to build the surface. + pFront->m_nPointCount = 0; + pBack->m_nPointCount = 0; + + for ( iPoint = 0; iPoint < pClipFace->m_nPointCount; iPoint++ ) + { + // "On" clip plane. + if ( nSides[iPoint] == SIDE_ON ) + { + pFront->m_aPoints[pFront->m_nPointCount] = pClipFace->m_aPoints[iPoint]; + pFront->m_aDispPointUVs[pFront->m_nPointCount] = pClipFace->m_aDispPointUVs[iPoint]; + + for ( int iTexCoord=0; iTexCoord < NUM_CLIPFACE_TEXCOORDS; iTexCoord++ ) + pFront->m_aTexCoords[iTexCoord][pFront->m_nPointCount] = pClipFace->m_aTexCoords[iTexCoord][iPoint]; + + ClipFace_CopyBlendFrom( pFront, &pClipFace->m_aBlends[iPoint] ); + pFront->m_nPointCount++; + + pBack->m_aPoints[pBack->m_nPointCount] = pClipFace->m_aPoints[iPoint]; + pBack->m_aDispPointUVs[pBack->m_nPointCount] = pClipFace->m_aDispPointUVs[iPoint]; + + for ( int iTexCoord=0; iTexCoord < NUM_CLIPFACE_TEXCOORDS; iTexCoord++ ) + pBack->m_aTexCoords[iTexCoord][pBack->m_nPointCount] = pClipFace->m_aTexCoords[iTexCoord][iPoint]; + + ClipFace_CopyBlendFrom( pBack, &pClipFace->m_aBlends[iPoint] ); + pBack->m_nPointCount++; + + continue; + } + + // "In back" of clip plane. + if ( nSides[iPoint] == SIDE_BACK ) + { + pBack->m_aPoints[pBack->m_nPointCount] = pClipFace->m_aPoints[iPoint]; + pBack->m_aDispPointUVs[pBack->m_nPointCount] = pClipFace->m_aDispPointUVs[iPoint]; + + for ( int iTexCoord=0; iTexCoord < NUM_CLIPFACE_TEXCOORDS; iTexCoord++ ) + pBack->m_aTexCoords[iTexCoord][pBack->m_nPointCount] = pClipFace->m_aTexCoords[iTexCoord][iPoint]; + + ClipFace_CopyBlendFrom( pBack, &pClipFace->m_aBlends[iPoint] ); + pBack->m_nPointCount++; + } + + // "In front" of clip plane. + if ( nSides[iPoint] == SIDE_FRONT ) + { + pFront->m_aPoints[pFront->m_nPointCount] = pClipFace->m_aPoints[iPoint]; + pFront->m_aDispPointUVs[pFront->m_nPointCount] = pClipFace->m_aDispPointUVs[iPoint]; + + for ( int iTexCoord=0; iTexCoord < NUM_CLIPFACE_TEXCOORDS; iTexCoord++ ) + pFront->m_aTexCoords[iTexCoord][pFront->m_nPointCount] = pClipFace->m_aTexCoords[iTexCoord][iPoint]; + + ClipFace_CopyBlendFrom( pFront, &pClipFace->m_aBlends[iPoint] ); + pFront->m_nPointCount++; + } + + if ( nSides[iPoint+1] == SIDE_ON || nSides[iPoint+1] == nSides[iPoint] ) + continue; + + // Split! + float fraction = flDists[iPoint] / ( flDists[iPoint] - flDists[iPoint+1] ); + + Vector vecPoint = pClipFace->m_aPoints[iPoint] + ( pClipFace->m_aPoints[(iPoint+1)%pClipFace->m_nPointCount] - pClipFace->m_aPoints[iPoint] ) * fraction; + Vector vecDispPointUV = pClipFace->m_aDispPointUVs[iPoint] + ( pClipFace->m_aDispPointUVs[(iPoint+1)%pClipFace->m_nPointCount] - pClipFace->m_aDispPointUVs[iPoint] ) * fraction; + + Vector2D vecUV, vecTexCoord; + PointInQuadToBarycentric( m_pOverlayFace->m_aPoints[0], m_pOverlayFace->m_aPoints[3], + m_pOverlayFace->m_aPoints[2], m_pOverlayFace->m_aPoints[1], + vecPoint, vecUV ); + + vecUV.x = clamp( vecUV.x, 0.0f, 1.0f ); + vecUV.y = clamp( vecUV.y, 0.0f, 1.0f ); + + for ( int iTexCoord=0; iTexCoord < NUM_CLIPFACE_TEXCOORDS; iTexCoord++ ) + { + TexCoordInQuadFromBarycentric( m_pOverlayFace->m_aTexCoords[iTexCoord][0], m_pOverlayFace->m_aTexCoords[iTexCoord][3], + m_pOverlayFace->m_aTexCoords[iTexCoord][2], m_pOverlayFace->m_aTexCoords[iTexCoord][1], + vecUV, vecTexCoord ); + + pFront->m_aTexCoords[iTexCoord][pFront->m_nPointCount] = vecTexCoord; + pBack->m_aTexCoords[iTexCoord][pBack->m_nPointCount] = vecTexCoord; + } + + pFront->m_aPoints[pFront->m_nPointCount] = vecPoint; + pFront->m_aDispPointUVs[pFront->m_nPointCount] = vecDispPointUV; + ClipFace_BuildBlend( pFront, pDisp, pClipPlane, iClip, vecDispPointUV, vecPoint ); + pFront->m_nPointCount++; + + pBack->m_aPoints[pBack->m_nPointCount] = vecPoint; + pBack->m_aDispPointUVs[pBack->m_nPointCount] = vecDispPointUV; + ClipFace_BuildBlend( pBack, pDisp, pClipPlane, iClip, vecDispPointUV, vecPoint ); + pBack->m_nPointCount++; + } + + // Check for a bad surface. + if ( ( pFront->m_nPointCount > nMaxPointCount ) || ( pBack->m_nPointCount > nMaxPointCount ) ) + return; + + *ppFront = pFront; + *ppBack = pBack; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::ClipFace_PreClipDisp( ClipFace_t *pClipFace, CMapDisp *pDisp ) +{ + // Valid clip face and/or displacement surface. + if ( !pClipFace || !pDisp ) + return; + + // Transform all of the overlay points into disp uv space. + for ( int iPoint = 0; iPoint < pClipFace->m_nPointCount; iPoint++ ) + { + Vector2D vecTmp; + pDisp->BaseFacePlaneToDispUV( pClipFace->m_aPoints[iPoint], vecTmp ); + + pClipFace->m_aDispPointUVs[iPoint].x = clamp(vecTmp.x, 0.0f, 1.0f); + pClipFace->m_aDispPointUVs[iPoint].y = clamp(vecTmp.y, 0.0f, 1.0f); + pClipFace->m_aDispPointUVs[iPoint].z = 0.0f; + } + + // Set initial point barycentric blend types. + for ( int iPoint = 0; iPoint < pClipFace->m_nPointCount; ++iPoint ) + { + Vector2D vecDispUV; + vecDispUV.x = pClipFace->m_aDispPointUVs[iPoint].x; + vecDispUV.y = pClipFace->m_aDispPointUVs[iPoint].y; + + int iTris[3]; + Vector2D vecVertsUV[3]; + GetTriVerts( pDisp, vecDispUV, iTris, vecVertsUV ); + + float flCoefs[3]; + if ( ClipFace_CalcBarycentricCooefs( pDisp, vecVertsUV, vecDispUV, flCoefs ) ) + { + ClipFace_ResolveBarycentricClip( pDisp, pClipFace, iPoint, vecDispUV, flCoefs, iTris, vecVertsUV ); + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::ClipFace_PostClipDisp( void ) +{ +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CMapOverlay::ClipFace_CalcBarycentricCooefs( CMapDisp *pDisp, Vector2D *pVertsUV, + const Vector2D &vecPointUV, float *pCoefs ) +{ + // Area in disp UV space is always the same. + float flTotalArea = 0.5f; + float flOOTotalArea = 1.0f / flTotalArea; + + int nInterval = pDisp->GetWidth(); + Vector2D vecScaledPointUV = vecPointUV * ( nInterval - 1.000001f ); + + Vector2D vecSegment0, vecSegment1; + + // Get the area for cooeficient 0 (pt, v1, v2). + vecSegment0 = pVertsUV[1] - vecScaledPointUV; + vecSegment1 = pVertsUV[2] - vecScaledPointUV; + // Cross + float flSubArea = ( ( vecSegment1.x * vecSegment0.y ) - ( vecSegment0.x * vecSegment1.y ) ) * 0.5f; + pCoefs[0] = flSubArea * flOOTotalArea; + + // Get the area for cooeficient 1 (v0, pt, v2). + vecSegment0 = vecScaledPointUV - pVertsUV[0]; + vecSegment1 = pVertsUV[2] - pVertsUV[0]; + // Cross + flSubArea = ( ( vecSegment1.x * vecSegment0.y ) - ( vecSegment0.x * vecSegment1.y ) ) * 0.5f; + pCoefs[1] = flSubArea * flOOTotalArea; + + // Get the area for cooeficient 2 (v0, v1, pt). + vecSegment0 = pVertsUV[1] - pVertsUV[0]; + vecSegment1 = vecScaledPointUV - pVertsUV[0]; + // Cross + flSubArea = ( ( vecSegment1.x * vecSegment0.y ) - ( vecSegment0.x * vecSegment1.y ) ) * 0.5f; + pCoefs[2] = flSubArea * flOOTotalArea; + + float flCoefTotal = pCoefs[0] + pCoefs[1] + pCoefs[2]; + if ( FloatMakePositive( 1.0f - flCoefTotal ) < 0.00001f ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::ClipFace_ResolveBarycentricClip( CMapDisp *pDisp, ClipFace_t *pClipFace, int iClipFacePoint, + const Vector2D &vecPointUV, float *pCoefs, + int *pTris, Vector2D *pVertsUV ) +{ + int nInterval = pDisp->GetWidth(); + Vector2D vecScaledPointUV = vecPointUV * ( nInterval - 1.000001f ); + + // Find the number of coefficients "equal" to zero. + int nZeroCount = 0; + bool bZeroPoint[3]; + for ( int iVert = 0; iVert < 3; ++iVert ) + { + bZeroPoint[iVert] = false; + if ( fabs( pCoefs[iVert] ) < OVERLAY_BARYCENTRIC_EPSILON ) + { + nZeroCount++; + bZeroPoint[iVert] = true; + } + } + + // Check for points - set to a point. + if ( nZeroCount == 2 ) + { + for ( int iVert = 0; iVert < 3; ++iVert ) + { + if ( !bZeroPoint[iVert] ) + { + pClipFace->m_aBlends[iClipFacePoint].m_nType = OVERLAY_BLENDTYPE_VERT; + pClipFace->m_aBlends[iClipFacePoint].m_iPoints[0] = pTris[iVert]; + return; + } + } + } + + // Check for edges - setup edge blend. + if ( nZeroCount == 1 ) + { + for ( int iVert = 0; iVert < 3; ++iVert ) + { + if ( bZeroPoint[iVert] ) + { + pClipFace->m_aBlends[iClipFacePoint].m_nType = OVERLAY_BLENDTYPE_EDGE; + pClipFace->m_aBlends[iClipFacePoint].m_iPoints[0] = pTris[(iVert+1)%3]; + pClipFace->m_aBlends[iClipFacePoint].m_iPoints[1] = pTris[(iVert+2)%3]; + + Vector2D vecLength1, vecLength2; + vecLength1 = vecScaledPointUV - pVertsUV[(iVert+1)%3]; + vecLength2 = pVertsUV[(iVert+2)%3] - pVertsUV[(iVert+1)%3]; + float flBlend = vecLength1.Length() / vecLength2.Length(); + pClipFace->m_aBlends[iClipFacePoint].m_flBlends[0] = flBlend; + return; + } + } + } + + // Lies inside triangles - setup full barycentric blend. + pClipFace->m_aBlends[iClipFacePoint].m_nType = OVERLAY_BLENDTYPE_BARY; + pClipFace->m_aBlends[iClipFacePoint].m_iPoints[0] = pTris[0]; + pClipFace->m_aBlends[iClipFacePoint].m_iPoints[1] = pTris[1]; + pClipFace->m_aBlends[iClipFacePoint].m_iPoints[2] = pTris[2]; + pClipFace->m_aBlends[iClipFacePoint].m_flBlends[0] = pCoefs[0]; + pClipFace->m_aBlends[iClipFacePoint].m_flBlends[1] = pCoefs[1]; + pClipFace->m_aBlends[iClipFacePoint].m_flBlends[2] = pCoefs[2]; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CMapOverlay::ClipFace_GetAxisType( cplane_t *pClipPlane ) +{ + if ( pClipPlane->normal[0] == 1.0f ) { return OVERLAY_ANGLE90; } + if ( pClipPlane->normal[1] == 1.0f ) { return OVERLAY_ANGLE0; } + if ( ( pClipPlane->normal[0] == 0.707f ) && ( pClipPlane->normal[1] == 0.707f ) ) { return OVERLAY_ANGLE45; } + if ( ( pClipPlane->normal[0] == -0.707f ) && ( pClipPlane->normal[1] == 0.707f ) ) { return OVERLAY_ANGLE135; } + + return OVERLAY_ANGLE0; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::ClipFace_BuildBlend( ClipFace_t *pClipFace, CMapDisp *pDisp, + cplane_t *pClipPlane, int iClip, + const Vector &vecUV, const Vector &vecPoint ) +{ + // Get the displacement space interval. + int nWidth = pDisp->GetWidth(); + int nHeight = pDisp->GetHeight(); + + float flU = vecUV.x * ( nWidth - 1.000001f ); + float flV = vecUV.y * ( nHeight - 1.000001f ); + + // find the triangle the "uv spot" resides in + int nSnapU = static_cast<int>( flU ); + int nSnapV = static_cast<int>( flV ); + if ( nSnapU == ( nWidth - 1 ) ) { --nSnapU; } + if ( nSnapV == ( nHeight - 1 ) ) { --nSnapV; } + int nNextU = nSnapU + 1; + int nNextV = nSnapV + 1; + + float flFracU = flU - static_cast<float>( nSnapU ); + float flFracV = flV - static_cast<float>( nSnapV ); + + int iAxisType = ClipFace_GetAxisType( pClipPlane ); + switch( iAxisType ) + { + case OVERLAY_ANGLE0: + { + // Vert type + if ( fabs( flFracU ) < OVERLAY_DISPSPACE_EPSILON ) + { + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_nType = OVERLAY_BLENDTYPE_VERT; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_iPoints[0] = ( nWidth * iClip ) + nSnapU; + } + // Edge type + else + { + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_nType = OVERLAY_BLENDTYPE_EDGE; + int iPoint0 = ( nWidth * iClip ) + nSnapU; + int iPoint1 = ( nWidth * iClip ) + nNextU; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_iPoints[0] = iPoint0; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_iPoints[1] = iPoint1; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_flBlends[0] = flFracU; + } + return; + } + case OVERLAY_ANGLE45: + { + // Vert type + if ( ( fabs( flFracU ) < OVERLAY_DISPSPACE_EPSILON ) && + ( fabs( flFracV ) < OVERLAY_DISPSPACE_EPSILON ) ) + { + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_nType = OVERLAY_BLENDTYPE_VERT; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_iPoints[0] = ( nWidth * nSnapV ) + nSnapU; + } + // Edge type + else + { + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_nType = OVERLAY_BLENDTYPE_EDGE; + int iPoint0 = ( nWidth * nNextV ) + nSnapU; + int iPoint1 = ( nWidth * nSnapV ) + nNextU; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_iPoints[0] = iPoint0; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_iPoints[1] = iPoint1; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_flBlends[0] = flFracU; + } + return; + } + case OVERLAY_ANGLE90: + { + // Vert type + if ( fabs( flFracV ) < OVERLAY_DISPSPACE_EPSILON ) + { + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_nType = OVERLAY_BLENDTYPE_VERT; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_iPoints[0] = ( nWidth * nSnapV ) + iClip; + } + // Edge type + else + { + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_nType = OVERLAY_BLENDTYPE_EDGE; + int iPoint0 = ( nWidth * nSnapV ) + iClip; + int iPoint1 = ( nWidth * nNextV ) + iClip; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_iPoints[0] = iPoint0; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_iPoints[1] = iPoint1; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_flBlends[0] = flFracV; + } + return; + } + case OVERLAY_ANGLE135: + { + // Vert type + if ( ( fabs( flFracU ) < OVERLAY_DISPSPACE_EPSILON ) && + ( fabs( flFracV ) < OVERLAY_DISPSPACE_EPSILON ) ) + { + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_nType = OVERLAY_BLENDTYPE_VERT; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_iPoints[0] = ( nWidth * nSnapV ) + nSnapU; + } + // Edge type + else + { + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_nType = OVERLAY_BLENDTYPE_EDGE; + int iPoint0 = ( nWidth * nSnapV ) + nSnapU; + int iPoint1 = ( nWidth * nNextV ) + nNextU; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_iPoints[0] = iPoint0; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_iPoints[1] = iPoint1; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_flBlends[0] = flFracU; + } + return; + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::ClipFace_CopyBlendFrom( ClipFace_t *pClipFace, BlendData_t *pBlendFrom ) +{ + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_nType = pBlendFrom->m_nType; + for ( int iPoint = 0; iPoint < 3; iPoint++ ) + { + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_iPoints[iPoint] = pBlendFrom->m_iPoints[iPoint]; + pClipFace->m_aBlends[pClipFace->m_nPointCount].m_flBlends[iPoint] = pBlendFrom->m_flBlends[iPoint]; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::ClipFace_BuildFacesFromBlendedData( ClipFace_t *pClipFace ) +{ + if( pClipFace->m_pBuildFace->HasDisp() ) + { + EditDispHandle_t handle = pClipFace->m_pBuildFace->GetDisp(); + CMapDisp *pDisp = EditDispMgr()->GetDisp( handle ); + + Vector vecPos[3]; + for ( int iPoint = 0; iPoint < pClipFace->m_nPointCount; iPoint++ ) + { + if ( pClipFace->m_aBlends[iPoint].m_nType == OVERLAY_BLENDTYPE_VERT ) + { + pDisp->GetVert( pClipFace->m_aBlends[iPoint].m_iPoints[0], vecPos[0] ); + pClipFace->m_aPoints[iPoint] = vecPos[0]; + } + else if ( pClipFace->m_aBlends[iPoint].m_nType == OVERLAY_BLENDTYPE_EDGE ) + { + pDisp->GetVert( pClipFace->m_aBlends[iPoint].m_iPoints[0], vecPos[0] ); + pDisp->GetVert( pClipFace->m_aBlends[iPoint].m_iPoints[1], vecPos[1] ); + pClipFace->m_aPoints[iPoint] = vecPos[0] + ( vecPos[1] - vecPos[0] ) * pClipFace->m_aBlends[iPoint].m_flBlends[0]; + } + else if ( pClipFace->m_aBlends[iPoint].m_nType == OVERLAY_BLENDTYPE_BARY ) + { + pDisp->GetVert( pClipFace->m_aBlends[iPoint].m_iPoints[0], vecPos[0] ); + pDisp->GetVert( pClipFace->m_aBlends[iPoint].m_iPoints[1], vecPos[1] ); + pDisp->GetVert( pClipFace->m_aBlends[iPoint].m_iPoints[2], vecPos[2] ); + pClipFace->m_aPoints[iPoint] = ( vecPos[0] * pClipFace->m_aBlends[iPoint].m_flBlends[0] ) + + ( vecPos[1] * pClipFace->m_aBlends[iPoint].m_flBlends[1] ) + + ( vecPos[2] * pClipFace->m_aBlends[iPoint].m_flBlends[2] ); + } + } + } +} + + +//============================================================================= +// +// CMapOverlay Material Functions +// + +int MaxComponent( const Vector &v0 ) +{ + int nMax = 0; + if ( FloatMakePositive( v0[1] ) > FloatMakePositive( v0[nMax] ) ) + { + nMax = 1; + } + + if ( FloatMakePositive( v0[2] ) > FloatMakePositive( v0[nMax] ) ) + { + nMax = 2; + } + + return nMax; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::Material_Clear( void ) +{ + m_Material.m_pTexture = NULL; + m_Material.m_vecTextureU.Init( 0.0f, 1.0f ); + m_Material.m_vecTextureV.Init( 0.0f, 1.0f ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Material_TexCoordInit( void ) +{ + int nMaxU = MaxComponent( m_Basis.m_vecAxes[OVERLAY_BASIS_U] ); + int nMaxV = MaxComponent( m_Basis.m_vecAxes[OVERLAY_BASIS_V] ); + + bool bUPos = m_Basis.m_vecAxes[OVERLAY_BASIS_U][nMaxU] >= 0.0f; + bool bVPos = m_Basis.m_vecAxes[OVERLAY_BASIS_V][nMaxV] >= 0.0f; + + m_Material.m_vecTextureU.Init( 0.0f, 1.0f ); + m_Material.m_vecTextureV.Init( 1.0f, 0.0f ); + + if ( ( bUPos && !bVPos ) || ( !bUPos && bVPos ) ) + { + m_Material.m_vecTextureU.Init( 1.0f, 0.0f ); + m_Material.m_vecTextureV.Init( 0.0f, 1.0f ); + } + + Material_UpdateParentKey(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::Material_Copy( Material_t *pSrc, Material_t *pDst ) +{ + pDst->m_pTexture = pSrc->m_pTexture; + pDst->m_vecTextureU = pSrc->m_vecTextureU; + pDst->m_vecTextureV = pSrc->m_vecTextureV; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Material_UpdateParentKey( void ) +{ + char szValue[80]; + + CMapEntity *pEntity = ( CMapEntity* )GetParent(); + if ( pEntity ) + { + sprintf( szValue, "%g", m_Material.m_vecTextureU.x ); + pEntity->NotifyChildKeyChanged( this, "StartU", szValue ); + + sprintf( szValue, "%g", m_Material.m_vecTextureU.y ); + pEntity->NotifyChildKeyChanged( this, "EndU", szValue ); + + sprintf( szValue, "%g", m_Material.m_vecTextureV.x ); + pEntity->NotifyChildKeyChanged( this, "StartV", szValue ); + + sprintf( szValue, "%g", m_Material.m_vecTextureV.y ); + pEntity->NotifyChildKeyChanged( this, "EndV", szValue ); + } +} + +//============================================================================= +// +// CMapOverlay Functions +// + +//----------------------------------------------------------------------------- +// Purpose: Construct a CMapOverlay instance. +//----------------------------------------------------------------------------- +CMapOverlay::CMapOverlay() : CMapSideList( "sides" ) +{ + Basis_Clear(); + Handles_Clear(); + Material_Clear(); + + m_bLoaded = false; + m_pOverlayFace = NULL; + m_uiFlags = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Destruct a CMapOverlay instance. +//----------------------------------------------------------------------------- +CMapOverlay::~CMapOverlay() +{ + ClipFace_Destroy( &m_pOverlayFace ); + m_aRenderFaces.PurgeAndDeleteElements(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CMapClass *CMapOverlay::CreateMapOverlay( CHelperInfo *pInfo, CMapEntity *pParent ) +{ + CMapOverlay *pOverlay = new CMapOverlay; + return pOverlay; +} + +//----------------------------------------------------------------------------- +// Purpose: Called after the entire map has been loaded. This allows the object +// to perform any linking with other map objects or to do other operations +// that require all world objects to be present. +// Input : pWorld - The world that we are in. +//----------------------------------------------------------------------------- +void CMapOverlay::PostloadWorld( CMapWorld *pWorld ) +{ + CMapSideList::PostloadWorld( pWorld ); + + // Support older overlay versions which didn't have specific basis axes. + if ( !Basis_IsValid() ) + { + Basis_BuildFromSideList(); + } + + Handles_Build3D(); + DoClip(); + CalcBounds(); + m_bLoaded = true; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CMapClass *CMapOverlay::Copy( bool bUpdateDependencies ) +{ + CMapOverlay *pCopy = new CMapOverlay; + if ( pCopy ) + { + pCopy->CopyFrom( this, bUpdateDependencies ); + } + + return pCopy; +} + +void CMapOverlay::Handles_FixOrder() +{ + static bool s_FixingHandles = false; + + // make sure that handle order and plane normal are in sync so CCW culling works correctly + Vector vNormal = GetNormalFromPoints( m_Handles.m_vec3D[0], m_Handles.m_vec3D[1], m_Handles.m_vec3D[2] ); + + if ( DotProduct( vNormal, m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL]) < 0.5 ) + { + // dont try to fix twice + if ( s_FixingHandles ) + { + Assert( !s_FixingHandles ); + return; + } + + s_FixingHandles = true; + + // Flip handles. + Vector2D vecCoords[OVERLAY_HANDLES_COUNT]; + for ( int iHandle = 0; iHandle < OVERLAY_HANDLES_COUNT; iHandle++ ) + { + vecCoords[4-iHandle-1] = m_Handles.m_vecBasisCoords[iHandle]; + } + + for ( int iHandle = 0; iHandle < OVERLAY_HANDLES_COUNT; iHandle++ ) + { + m_Handles.m_vecBasisCoords[iHandle] = vecCoords[iHandle]; + } + + // rebuild handles + + Handles_Build3D(); + + s_FixingHandles = false; + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CMapClass *CMapOverlay::CopyFrom( CMapClass *pObject, bool bUpdateDependencies ) +{ + // Verify the object is of the correct type and cast. + Assert( pObject->IsMapClass( MAPCLASS_TYPE( CMapOverlay ) ) ); + CMapOverlay *pFrom = ( CMapOverlay* )pObject; + if ( pFrom ) + { + // Copy the parent class data. + CMapSideList::CopyFrom( pObject, bUpdateDependencies ); + + // Copy basis data. + Basis_Copy( &pFrom->m_Basis, &m_Basis ); + + // Copy handle data. + Handles_Copy( &pFrom->m_Handles, &m_Handles ); + + // Copy material data. + Material_Copy( &pFrom->m_Material, &m_Material ); + } + + return this; +} + +//----------------------------------------------------------------------------- +// Purpose: Notify me when a key has had a data change, so the overlay can +// update itself appropriately. +// Input: szKey - the key that changed +// szValue - the new value (key/data pair) +//----------------------------------------------------------------------------- +void CMapOverlay::OnParentKeyChanged( const char* szKey, const char* szValue ) +{ + // Pass this to the sidelist first. + CMapSideList::OnParentKeyChanged( szKey, szValue ); + + // Read side data. + if ( !stricmp( szKey, "sides" ) ) + { + if ( m_Faces.Count() > 0 ) + { + Basis_SetFace( m_Faces.Element( 0 ) ); + } + } + + // Read geometry data. + float flDummy; + if ( !stricmp( szKey, "uv0" ) ) + { + sscanf( szValue, "%f %f %f", &m_Handles.m_vecBasisCoords[0].x, &m_Handles.m_vecBasisCoords[0].y, &flDummy ); + m_Basis.m_nAxesFlip[0] = ( int )flDummy; + } + if ( !stricmp( szKey, "uv1" ) ) + { + sscanf( szValue, "%f %f %f", &m_Handles.m_vecBasisCoords[1].x, &m_Handles.m_vecBasisCoords[1].y, &flDummy ); + m_Basis.m_nAxesFlip[1] = ( int )flDummy; + } + if ( !stricmp( szKey, "uv2" ) ) + { + sscanf( szValue, "%f %f %f", &m_Handles.m_vecBasisCoords[2].x, &m_Handles.m_vecBasisCoords[2].y, &flDummy ); + m_Basis.m_nAxesFlip[2] = ( int )flDummy; + } + if ( !stricmp( szKey, "uv3" ) ) + { + sscanf( szValue, "%f %f %f", &m_Handles.m_vecBasisCoords[3].x, &m_Handles.m_vecBasisCoords[3].y, &flDummy ); + } + + // Read basis data. + if ( !stricmp( szKey, "BasisOrigin" ) ) + { + sscanf( szValue, "%f %f %f", &m_Basis.m_vecOrigin.x, &m_Basis.m_vecOrigin.y, &m_Basis.m_vecOrigin.z ); + } + + if ( !stricmp( szKey, "BasisU" ) ) + { + sscanf( szValue, "%f %f %f", &m_Basis.m_vecAxes[OVERLAY_BASIS_U].x, &m_Basis.m_vecAxes[OVERLAY_BASIS_U].y, &m_Basis.m_vecAxes[OVERLAY_BASIS_U].z ); + } + + if ( !stricmp( szKey, "BasisV" ) ) + { + sscanf( szValue, "%f %f %f", &m_Basis.m_vecAxes[OVERLAY_BASIS_V].x, &m_Basis.m_vecAxes[OVERLAY_BASIS_V].y, &m_Basis.m_vecAxes[OVERLAY_BASIS_V].z ); + } + + if ( !stricmp( szKey, "BasisNormal" ) ) + { + sscanf( szValue, "%f %f %f", &m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL].x, &m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL].y, &m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL].z ); + } + + // Read material data. + if ( !stricmp( szKey, "material" ) ) + { + // Get the new material. + IEditorTexture *pTex = g_Textures.FindActiveTexture( szValue ); + if ( !pTex ) + return; + + // Save the new material. + m_Material.m_pTexture = pTex; + } + + if ( !stricmp( szKey, "StartU" ) ) + { + m_Material.m_vecTextureU.x = atof( szValue ); + } + if ( !stricmp( szKey, "EndU" ) ) + { + m_Material.m_vecTextureU.y = atof( szValue ); + } + if ( !stricmp( szKey, "StartV" ) ) + { + m_Material.m_vecTextureV.x = atof( szValue ); + } + if ( !stricmp( szKey, "EndV" ) ) + { + m_Material.m_vecTextureV.y = atof( szValue ); + } + + if ( m_bLoaded ) + { + // Clip - this needs to be done for everything other than a material change, so go ahead. + DoClip(); + + // Post updated. + PostUpdate( Notify_Changed ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::OnUndoRedo( void ) +{ + PostModified(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::CalcBounds( BOOL bFullUpdate ) +{ + // Pass the info along. + CMapSideList::CalcBounds( bFullUpdate ); + + // Verify that we have valid data. + if ( !Basis_IsValid() ) + return; + + // Calculate the 2d bounds. + Vector vecMins, vecMaxs; + vecMins = m_Origin - Vector( 2.0f, 2.0f, 2.0f ); + vecMaxs = m_Origin + Vector( 2.0f, 2.0f, 2.0f ); + + // Reset bounds + m_CullBox.ResetBounds(); + m_Render2DBox.ResetBounds(); + + for ( int iHandle = 0; iHandle < 4; ++iHandle ) + { + for ( int iAxis = 0; iAxis < 3; ++iAxis ) + { + // Min + if ( m_Handles.m_vec3D[iHandle][iAxis] < vecMins[iAxis] ) + { + vecMins[iAxis] = m_Handles.m_vec3D[iHandle][iAxis]; + } + + // Max + if ( m_Handles.m_vec3D[iHandle][iAxis] > vecMaxs[iAxis] ) + { + vecMaxs[iAxis] = m_Handles.m_vec3D[iHandle][iAxis]; + } + } + } + + // Don't allow for NULL bounds. + for ( int iAxis = 0; iAxis < 3; ++iAxis ) + { + if( ( vecMaxs[iAxis] - vecMins[iAxis] ) == 0.0f ) + { + vecMins[iAxis] -= 0.5f; + vecMaxs[iAxis] += 0.5f; + } + } + + // Update the bounds. + m_CullBox.UpdateBounds( vecMins, vecMaxs ); + m_BoundingBox = m_CullBox; + m_Render2DBox.UpdateBounds( vecMins, vecMaxs ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::PostModified( void ) +{ + // update face and origin + if ( m_Faces.Count() > 0 ) + { + Basis_SetFace( m_Faces.Element( 0 ) ); + Basis_UpdateOrigin(); + } + else + { + m_Basis.m_pFace = NULL; + } + + Handles_Build3D(); + DoClip(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pTransBox - +//----------------------------------------------------------------------------- +void CMapOverlay::DoTransform( const VMatrix &matrix ) +{ + BaseClass::DoTransform( matrix ); + + VMatrix tmpMatrix = matrix; + + // erase move component + tmpMatrix.SetTranslation( vec3_origin ); + + // check if matrix would still change something + if ( !tmpMatrix.IsIdentity() ) + { + // make sure axes are normalized (they should be anyways) + m_Basis.m_vecAxes[OVERLAY_BASIS_U].NormalizeInPlace(); + m_Basis.m_vecAxes[OVERLAY_BASIS_V].NormalizeInPlace(); + + Vector vecU = m_Basis.m_vecAxes[OVERLAY_BASIS_U]; + Vector vecV = m_Basis.m_vecAxes[OVERLAY_BASIS_V]; + Vector vecNormal = m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL]; + + TransformPoint( tmpMatrix, vecU ); + TransformPoint( tmpMatrix, vecV ); + TransformPoint( tmpMatrix, vecNormal ); + + float fScaleU = vecU.Length(); + float fScaleV = vecV.Length(); + float flScaleNormal = vecNormal.Length(); + + bool bIsUnit = ( fequal( fScaleU, 1.0f, 0.0001 ) && fequal( fScaleV, 1.0f, 0.0001 ) && fequal( flScaleNormal, 1.0f, 0.0001 ) ); + bool bIsPerp = ( fequal( DotProduct( vecU, vecV ), 0.0f, 0.0025 ) && fequal( DotProduct( vecU, vecNormal ), 0.0f, 0.0025 ) && fequal( DotProduct( vecV, vecNormal ), 0.0f, 0.0025 ) ); + +// if ( fequal(fScaleU,1,0.0001) && fequal(fScaleV,1,0.0001) && fequal(DotProduct( vecU, vecV ),0,0.0025) ) + if ( bIsUnit && bIsPerp ) + { + // transformation doesnt scale or shear anything, so just update base axes + m_Basis.m_vecAxes[OVERLAY_BASIS_U] = vecU; + m_Basis.m_vecAxes[OVERLAY_BASIS_V] = vecV; + m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL] = vecNormal; + } + else + { + // more complex transformation, move UV coordinates, but leave base axes + for ( int iHandle=0; iHandle<OVERLAY_HANDLES_COUNT;iHandle++) + { + Vector2D vecUV = m_Handles.m_vecBasisCoords[iHandle]; + Vector vecPos = ( vecUV.x * m_Basis.m_vecAxes[OVERLAY_BASIS_U] + vecUV.y * m_Basis.m_vecAxes[OVERLAY_BASIS_V] ); + + // to transform in world space + TransformPoint( tmpMatrix, vecPos ); + + vecUV.x = m_Basis.m_vecAxes[OVERLAY_BASIS_U].Dot( vecPos ); + vecUV.y = m_Basis.m_vecAxes[OVERLAY_BASIS_V].Dot( vecPos ); + + m_Handles.m_vecBasisCoords[iHandle] = vecUV; + } + + if ( !Options.IsLockingTextures() ) + { + // scale textures if locking is off + m_Material.m_vecTextureU *= fScaleU; + m_Material.m_vecTextureV *= fScaleV; + Material_UpdateParentKey(); + } + } + } + + // Send modified notice. + PostModified(); + + Handles_UpdateParentKey(); +} + +//----------------------------------------------------------------------------- +// Purpose: Notifies us that a copy of ourselves was pasted. +//----------------------------------------------------------------------------- +void CMapOverlay::OnPaste( CMapClass *pCopy, CMapWorld *pSourceWorld, CMapWorld *pDestWorld, + const CMapObjectList &OriginalList, CMapObjectList &NewList) +{ + // + // NOTE: currently pCopy is the Overlay being pasted into the world, "this" is + // what is being copied from + // + CMapSideList::OnPaste( pCopy, pSourceWorld, pDestWorld, OriginalList, NewList ); + CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pCopy ); + if ( pOverlay ) + { + pOverlay->Basis_BuildFromSideList(); + pOverlay->PostModified(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Notifies us that we created a copy of ourselves (a clone). +//----------------------------------------------------------------------------- +void CMapOverlay::OnClone( CMapClass *pClone, CMapWorld *pWorld, + const CMapObjectList &OriginalList, CMapObjectList &NewList ) +{ + CMapSideList::OnClone( pClone, pWorld, OriginalList, NewList ); + CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pClone ); + if ( pOverlay ) + { + if ( ( GetOverlayType() && OVERLAY_TYPE_SHORE ) == 0 ) + { + // Update the clone's solid dependencies (this doesn't happen on clone generally). + int nFaceCount = pOverlay->GetFaceCount(); + for ( int iFace = 0; iFace < nFaceCount; ++iFace ) + { + CMapFace *pFace = pOverlay->GetFace( iFace ); + CMapSolid *pSolid = ( CMapSolid* )pFace->GetParent(); + pOverlay->UpdateDependency( NULL, pSolid ); + } + } + + pOverlay->PostModified(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Notifys this decal of a change to a solid that it is attached to. +//----------------------------------------------------------------------------- +void CMapOverlay::OnNotifyDependent( CMapClass *pObject, Notify_Dependent_t eNotifyType ) +{ + // Chain to base class FIRST so it can rebuild the face list if necessary. + CMapSideList::OnNotifyDependent( pObject, eNotifyType ); + + // + // NOTE: the solid moving (changing) can update the overlay/solid(face) dependency + // so "rebuild" the overlay + // + switch ( eNotifyType ) + { + case Notify_Changed: + case Notify_Undo: + case Notify_Transform: + { + PostModified(); + break; + } + case Notify_Removed: + case Notify_Clipped: + { + m_aRenderFaces.Purge(); + PostModified(); + break; + } + case Notify_Rebuild: + { + UpdateDispBarycentric(); + break; + } + case Notify_Rebuild_Full: + { + DoClip(); + CenterEntity(); + Handles_Build3D(); + break; + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::Render3D( CRender3D *pRender ) +{ + int nFaceCount = m_aRenderFaces.Count(); + + if ( nFaceCount != 0 ) + { + // dont draw textured during manipulating + if ( GetSelectionState() != SELECT_MODIFY ) + { + + // Bind the matrial -- if there is one!! + bool bTextured = false; + if ( m_Material.m_pTexture ) + { + pRender->BindTexture( m_Material.m_pTexture ); + pRender->PushRenderMode( RENDER_MODE_TEXTURED ); + bTextured = true; + } + else + { + // Default state. + pRender->PushRenderMode( RENDER_MODE_FLAT ); + } + + for ( int iFace = 0; iFace < nFaceCount; iFace++ ) + { + ClipFace_t *pRenderFace = m_aRenderFaces.Element( iFace ); + if( !pRenderFace ) + continue; + + MaterialPrimitiveType_t type = MATERIAL_POLYGON; + + // Get a dynamic mesh. + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh* pMesh = pRenderContext->GetDynamicMesh(); + + meshBuilder.Begin( pMesh, type, pRenderFace->m_nPointCount ); + for ( int iPoint = 0; iPoint < pRenderFace->m_nPointCount; iPoint++ ) + { + if ( !bTextured ) + { + meshBuilder.Color3ub( 0, 128, 0 ); + } + else + { + meshBuilder.TexCoord2f( 0, pRenderFace->m_aTexCoords[0][iPoint].x, pRenderFace->m_aTexCoords[0][iPoint].y ); + meshBuilder.TexCoord2f( 2, pRenderFace->m_aTexCoords[1][iPoint].x, pRenderFace->m_aTexCoords[1][iPoint].y ); + meshBuilder.Color4ub( 255, 255, 255, 255 ); + } + meshBuilder.Position3f( pRenderFace->m_aPoints[iPoint].x, pRenderFace->m_aPoints[iPoint].y, pRenderFace->m_aPoints[iPoint].z ); + meshBuilder.AdvanceVertex(); + } + meshBuilder.End(); + + pMesh->Draw(); + } + + pRender->PopRenderMode(); + } + + // Render wireframe on top when seleted. + if ( GetSelectionState() != SELECT_NONE ) + { + pRender->PushRenderMode( RENDER_MODE_WIREFRAME ); + for ( int iFace = 0; iFace < nFaceCount; iFace++ ) + { + ClipFace_t *pRenderFace = m_aRenderFaces.Element( iFace ); + if( !pRenderFace ) + continue; + + MaterialPrimitiveType_t type = MATERIAL_LINE_LOOP; + + // get a dynamic mesh + CMeshBuilder meshBuilder; + CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); + IMesh* pMesh = pRenderContext->GetDynamicMesh(); + + meshBuilder.Begin( pMesh, type, pRenderFace->m_nPointCount ); + for( int iPoint = 0; iPoint < pRenderFace->m_nPointCount; iPoint++ ) + { + meshBuilder.Color3ub( 0, 255, 0 ); + meshBuilder.Position3f( pRenderFace->m_aPoints[iPoint].x, pRenderFace->m_aPoints[iPoint].y, pRenderFace->m_aPoints[iPoint].z ); + meshBuilder.AdvanceVertex(); + } + meshBuilder.End(); + + pMesh->Draw(); + } + pRender->PopRenderMode(); + } + } + + // Render the handles - if selected or in overlay tool mode. + if ( ( ToolManager()->GetActiveToolID() == TOOL_OVERLAY ) && Basis_IsValid() && IsSelected() ) + { + Handles_Render3D( pRender ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Clip the overlay "face" to all of the faces in the overlay sidelist. +// The sidelist defines all faces affected by the "overlay." +//----------------------------------------------------------------------------- +void CMapOverlay::DoClip( void ) +{ + // Check to see if we have any faces to clip against. + int nFaceCount = m_Faces.Count(); + if( nFaceCount == 0 ) + return; + + // Destroy the render face cache. + m_aRenderFaces.Purge(); + + // clip the overlay against all faces in the sidelist + for ( int iFace = 0; iFace < nFaceCount; iFace++ ) + { + CMapFace *pFace = m_Faces.Element( iFace ); + if ( pFace ) + { + PreClip(); + DoClipFace( pFace ); + PostClip(); + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::PreClip( void ) +{ + // + // Create the initial face to be clipped - the overlay. + // + m_pOverlayFace = ClipFace_Create( OVERLAY_HANDLES_COUNT ); + if ( m_pOverlayFace ) + { + for ( int iPoint = 0; iPoint < OVERLAY_HANDLES_COUNT; iPoint++ ) + { + OverlayUVToOverlayPlane( m_Handles.m_vecBasisCoords[iPoint], m_pOverlayFace->m_aPoints[iPoint] ); + + // translate texture UV to texture coords: + Vector2D vTexCoord; + switch( iPoint ) + { + case 0 : vTexCoord = Vector2D(m_Material.m_vecTextureU.x, m_Material.m_vecTextureV.x); break; + case 1 : vTexCoord = Vector2D(m_Material.m_vecTextureU.x, m_Material.m_vecTextureV.y); break; + case 2 : vTexCoord = Vector2D(m_Material.m_vecTextureU.y, m_Material.m_vecTextureV.y); break; + case 3 : vTexCoord = Vector2D(m_Material.m_vecTextureU.y, m_Material.m_vecTextureV.x); break; + default : Assert( iPoint <= OVERLAY_HANDLES_COUNT); + } + + m_pOverlayFace->m_aTexCoords[0][iPoint] = vTexCoord; + + if ( m_Basis.m_pFace->HasDisp() ) + { + EditDispHandle_t handle = m_Basis.m_pFace->GetDisp(); + CMapDisp *pDisp = EditDispMgr()->GetDisp( handle ); + if ( pDisp ) + { + Vector2D vecTmp; + pDisp->BaseFacePlaneToDispUV( m_pOverlayFace->m_aPoints[iPoint], vecTmp ); + m_pOverlayFace->m_aDispPointUVs[iPoint].x = vecTmp.x; + m_pOverlayFace->m_aDispPointUVs[iPoint].y = vecTmp.y; + m_pOverlayFace->m_aDispPointUVs[iPoint].z = 0.0f; + } + } + } + // The second set of texcoords on the overlay is used for alpha by certain shaders, + // and they want to stretch the texture across the whole overlay. + m_pOverlayFace->m_aTexCoords[1][0].Init( 0, 0 ); + m_pOverlayFace->m_aTexCoords[1][1].Init( 0, 1 ); + m_pOverlayFace->m_aTexCoords[1][2].Init( 1, 1 ); + m_pOverlayFace->m_aTexCoords[1][3].Init( 1, 0 ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::PostClip( void ) +{ + ClipFace_Destroy( &m_pOverlayFace ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::DoClipFace( CMapFace *pFace ) +{ + // Valid face? + Assert( pFace != NULL ); + if( !pFace ) + return; + + // Copy the original overlay to the "clipped" overlay. + ClipFace_t *pClippedFace = ClipFace_Copy( m_pOverlayFace ); + if ( !pClippedFace ) + return; + + // + // Project all face points into the overlay plane. + // + int nPointCount = pFace->nPoints; + Vector *pPoints = new Vector[nPointCount]; + int nEdgePlaneCount = nPointCount; + cplane_t *pEdgePlanes = new cplane_t[nEdgePlaneCount]; + if ( !pPoints || !pEdgePlanes ) + { + delete [] pPoints; + delete [] pEdgePlanes; + return; + } + + for ( int iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + WorldToOverlayPlane( pFace->Points[iPoint], pPoints[iPoint] ); + } + + // Create the face clipping planes (edges cross overlay plane normal). + BuildEdgePlanes( pPoints, nPointCount, pEdgePlanes, nEdgePlaneCount ); + + // + // Clip overlay against all the edge planes. + // + for ( int iClipPlane = 0; iClipPlane < nEdgePlaneCount; iClipPlane++ ) + { + ClipFace_t *pFront = NULL; + ClipFace_t *pBack = NULL; + + if ( pClippedFace ) + { + // Clip the overlay and delete the data (we are done with it - we are only interested in what is left). + ClipFace_Clip( pClippedFace, &pEdgePlanes[iClipPlane], OVERLAY_WORLDSPACE_EPSILON, &pFront, &pBack ); + ClipFace_Destroy( &pClippedFace ); + + // Keep the backside -- if it exists and continue clipping. + if ( pBack ) + { + pClippedFace = pBack; + } + + // Destroy the front side -- if it exists. + if ( pFront ) + { + ClipFace_Destroy( &pFront ); + } + } + } + + // + // Free temporary memory (clip planes and point). + // + delete [] pPoints; + delete [] pEdgePlanes; + + + // + // If it exists, move points from the overlay plane back into + // the base face plane. + // + if ( !pClippedFace ) + return; + + for ( int iPoint = 0; iPoint < pClippedFace->m_nPointCount; iPoint++ ) + { + Vector2D vecUV; + PointInQuadToBarycentric( m_pOverlayFace->m_aPoints[0], m_pOverlayFace->m_aPoints[3], + m_pOverlayFace->m_aPoints[2], m_pOverlayFace->m_aPoints[1], + pClippedFace->m_aPoints[iPoint], vecUV ); + + Vector vecTmp; + OverlayPlaneToWorld( pFace, pClippedFace->m_aPoints[iPoint], vecTmp ); + pClippedFace->m_aPoints[iPoint] = vecTmp; + + Vector2D vecTexCoord; + for ( int iTexCoord=0; iTexCoord < NUM_CLIPFACE_TEXCOORDS; iTexCoord++ ) + { + TexCoordInQuadFromBarycentric( m_pOverlayFace->m_aTexCoords[iTexCoord][0], m_pOverlayFace->m_aTexCoords[iTexCoord][3], + m_pOverlayFace->m_aTexCoords[iTexCoord][2], m_pOverlayFace->m_aTexCoords[iTexCoord][1], + vecUV, vecTexCoord ); + + pClippedFace->m_aTexCoords[iTexCoord][iPoint] = vecTexCoord; + } + } + + // + // If the face has a displacement map -- continue clipping. + // + if( pFace->HasDisp() ) + { + DoClipDisp( pFace, pClippedFace ); + } + // Done - save it! + else + { + pClippedFace->m_pBuildFace = pFace; + m_aRenderFaces.AddToTail( pClippedFace ); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CMapOverlay::BuildEdgePlanes( Vector const *pPoints, int nPointCount, + cplane_t *pEdgePlanes, int nEdgePlaneCount ) +{ + for ( int iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + Vector vecEdge; + vecEdge = pPoints[(iPoint+1)%nPointCount] - pPoints[iPoint]; + VectorNormalize( vecEdge ); + + pEdgePlanes[iPoint].normal = m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL].Cross( vecEdge ); + pEdgePlanes[iPoint].dist = pEdgePlanes[iPoint].normal.Dot( pPoints[iPoint] ); + + // Check normal facing. + float flDist = pEdgePlanes[iPoint].normal.Dot( pPoints[(iPoint+2)%nPointCount] ) - pEdgePlanes[iPoint].dist; + if( flDist > 0.0f ) + { + // flip + pEdgePlanes[iPoint].normal.Negate(); + pEdgePlanes[iPoint].dist = -pEdgePlanes[iPoint].dist; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Disp_ClipFragments( CMapDisp *pDisp, ClipFaces_t &aDispFragments ) +{ + cplane_t clipPlane; + + // Cache the displacement interval. + int nInterval = pDisp->GetWidth() - 1; + + // Displacement-space clipping in V. + clipPlane.normal.Init( 1.0f, 0.0f, 0.0f ); + Disp_DoClip( pDisp, aDispFragments, clipPlane, 1.0f, nInterval, 1, nInterval, 1 ); + + // Displacement-space clipping in U. + clipPlane.normal.Init( 0.0f, 1.0f, 0.0f ); + Disp_DoClip( pDisp, aDispFragments, clipPlane, 1.0f, nInterval, 1, nInterval, 1 ); + + // Displacement-space clipping UV from top-left to bottom-right. + clipPlane.normal.Init( 0.707f, 0.707f, 0.0f ); // 45 degrees + Disp_DoClip( pDisp, aDispFragments, clipPlane, 0.707f, nInterval, 2, ( nInterval * 2 - 1 ), 2 ); + + // Displacement-space clipping UV from bottom-left to top-right. + clipPlane.normal.Init( -0.707f, 0.707f, 0.0f ); // 135 degrees + Disp_DoClip( pDisp, aDispFragments, clipPlane, 0.707f, nInterval, -( nInterval - 2 ), ( nInterval - 1 ), 2 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::Disp_DoClip( CMapDisp *pDisp, ClipFaces_t &aDispFragments, + cplane_t &clipPlane, float clipDistStart, int nInterval, + int nLoopStart, int nLoopEnd, int nLoopInc ) +{ + // Setup interval information. + float flInterval = static_cast<float>( nInterval ); + float flOOInterval = 1.0f / flInterval; + + // Holds the current set of clipped faces. + ClipFaces_t aClippedFragments; + + for ( int iInterval = nLoopStart; iInterval < nLoopEnd; iInterval += nLoopInc ) + { + // Copy the current list to clipped face list. + aClippedFragments.CopyArray( aDispFragments.Base(), aDispFragments.Count() ); + aDispFragments.Purge(); + + // Clip in V. + int nFragCount = aClippedFragments.Count(); + for ( int iFrag = 0; iFrag < nFragCount; iFrag++ ) + { + ClipFace_t *pClipFrag = aClippedFragments[iFrag]; + if ( pClipFrag ) + { + ClipFace_t *pFront = NULL, *pBack = NULL; + + clipPlane.dist = clipDistStart * ( ( float )iInterval * flOOInterval ); + ClipFace_ClipBarycentric( pClipFrag, &clipPlane, OVERLAY_DISPSPACE_EPSILON, iInterval, pDisp, &pFront, &pBack ); + ClipFace_Destroy( &pClipFrag ); + + if ( pFront ) + { + aDispFragments.AddToTail( pFront ); + } + + if ( pBack ) + { + aDispFragments.AddToTail( pBack ); + } + } + } + } + + // Clean up! + aClippedFragments.Purge(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::DoClipDisp( CMapFace *pFace, ClipFace_t *pClippedFace ) +{ + // Get the displacement data. + EditDispHandle_t handle = pFace->GetDisp(); + CMapDisp *pDisp = EditDispMgr()->GetDisp( handle ); + + // Initialize local clip data. + ClipFace_PreClipDisp( pClippedFace, pDisp ); + + // Setup clipped face lists. + ClipFaces_t aCurrentFaces; + aCurrentFaces.AddToTail( pClippedFace ); + + Disp_ClipFragments( pDisp, aCurrentFaces ); + + // + // Project points back onto the displacement surface. + // + int nFaceCount = aCurrentFaces.Count(); + for( int iFace = 0; iFace < nFaceCount; iFace++ ) + { + ClipFace_t *pClipFace = aCurrentFaces[iFace]; + if ( pClipFace ) + { + // Save for re-building later! + pClipFace->m_pBuildFace = pFace; + m_aRenderFaces.AddToTail( aCurrentFaces[iFace] ); + ClipFace_BuildFacesFromBlendedData( pClipFace ); + } + } + + // Clean up! + aCurrentFaces.Purge(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::HandlesReset( void ) +{ + m_Handles.m_iHit = -1; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CMapOverlay::HandlesHitTest( CMapView *pView, const Vector2D &vPoint ) +{ + int handleRadius = 8; + + for ( int iPoint = 0; iPoint < 4; iPoint++ ) + { + Vector2D vHandle; + + pView->WorldToClient( vHandle, m_Handles.m_vec3D[iPoint] ); + + if ( vPoint.x < (vHandle.x-handleRadius) || vPoint.x > ( vHandle.x+handleRadius) ) + continue; + + if ( vPoint.y < (vHandle.y-handleRadius) || vPoint.y > ( vHandle.y+handleRadius) ) + continue; + + m_Handles.m_iHit = iPoint; + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::HandlesDragTo( Vector &vecImpact, CMapFace *pFace ) +{ + // Check handle index range. + if ( ( m_Handles.m_iHit < 0 ) || ( m_Handles.m_iHit > 3 ) ) + return; + + // Save + m_Handles.m_vec3D[m_Handles.m_iHit] = vecImpact; + + // Project the point into the overlay plane (from face/disp). + Vector vecOverlay; + Vector2D vecUVOverlay; + Handles_SurfToOverlayPlane( pFace, vecImpact, vecOverlay ); + OverlayPlaneToOverlayUV( vecOverlay, vecUVOverlay ); + m_Handles.m_vecBasisCoords[m_Handles.m_iHit] = vecUVOverlay; +} +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::HandleMoveTo( int iHandle, Vector &vecPoint, CMapFace *pFace ) +{ + if ( ( iHandle < 0 ) || ( iHandle > 3 ) ) + return; + + m_Handles.m_vec3D[iHandle] = vecPoint; + + // Project the point into the overlay plane (from face/disp). + Vector vecOverlay; + Vector2D vecUVOverlay; + Handles_SurfToOverlayPlane( pFace, vecPoint, vecOverlay ); + OverlayPlaneToOverlayUV( vecOverlay, vecUVOverlay ); + m_Handles.m_vecBasisCoords[iHandle] = vecUVOverlay; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::SetTexCoords( Vector2D vecTexCoords[4] ) +{ + m_Material.m_vecTextureU.x = vecTexCoords[0][0]; + m_Material.m_vecTextureV.x = vecTexCoords[0][1]; +// m_Material.m_vecTextureU.x = vecTexCoord[1][0]; + m_Material.m_vecTextureV.y = vecTexCoords[1][1]; + m_Material.m_vecTextureU.y = vecTexCoords[2][0]; +// m_Material.m_vecTextureV.y = vecTexCoord[2][1]; +// m_Material.m_vecTextureU.y = vecTexCoord[3][0]; +// m_Material.m_vecTextureV.x = vecTexCoord[3][1]; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::UpdateDispBarycentric( void ) +{ + // + // Project points back onto the displacement surface. + // + int nFaceCount = m_aRenderFaces.Count(); + for ( int iFace = 0; iFace < nFaceCount; iFace++ ) + { + // Get the current face and remove it from the list. + ClipFace_t *pClipFace = m_aRenderFaces[iFace]; + if ( pClipFace ) + { + if ( pClipFace->m_pBuildFace->HasDisp() ) + { + ClipFace_BuildFacesFromBlendedData( pClipFace ); + } + } + } + + // Update the entity position. + CenterEntity(); + + // Update the handles. + Handles_Build3D(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::CenterEntity( void ) +{ + // Center in overlay plane. + Vector vecTotal; + Vector vecHandle; + + vecTotal.Init(); + for( int iHandle = 0; iHandle < OVERLAY_HANDLES_COUNT; ++iHandle ) + { + OverlayUVToOverlayPlane( m_Handles.m_vecBasisCoords[iHandle], vecHandle ); + vecTotal += vecHandle; + } + vecTotal *= 0.25f; + + // Center in overlay uv-space. + Vector2D vecNewCenter; + OverlayPlaneToOverlayUV( vecTotal, vecNewCenter ); + for( int iHandle = 0; iHandle < OVERLAY_HANDLES_COUNT; ++iHandle ) + { + m_Handles.m_vecBasisCoords[iHandle] -= vecNewCenter; + } + + // Update the entity's origin. + m_Basis.m_vecOrigin = vecTotal; + + CMapEntity *pEntity = ( CMapEntity* )GetParent(); + if ( pEntity ) + { + Vector vecSurfPoint; + OverlayPlaneToSurfFromList( vecTotal, vecSurfPoint ); + pEntity->SetOrigin( vecSurfPoint ); + } + + // Update the property box. + Basis_UpdateParentKey(); + Handles_UpdateParentKey(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::GetPlane( cplane_t &plane ) +{ + plane.normal = m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL]; + plane.dist = plane.normal.Dot( m_Basis.m_vecOrigin ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::GetHandlePos( int iHandle, Vector &vecPos ) +{ + Assert( iHandle >= 0 ); + Assert( iHandle < 4 ); + + vecPos = m_Handles.m_vec3D[iHandle]; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::SideList_Init( CMapFace *pFace ) +{ + // Valid face? + if ( !pFace ) + return; + + // Purge side list as this should be the initial face! + m_Faces.Purge(); + m_Faces.AddToTail( pFace ); + + if ( ( GetOverlayType() && OVERLAY_TYPE_SHORE ) == 0 ) + { + // Update dependencies. + UpdateDependency( NULL, ( CMapSolid* )pFace->GetParent() ); + UpdateParentKey(); + } + + // Initialize the overlay. + Basis_Init( pFace ); + PostModified(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::SideList_AddFace( CMapFace *pFace ) +{ + // Valid face? + if ( !pFace ) + return; + + // Purge side list as this should be the initial face! + m_Faces.AddToTail( pFace ); + + if ( ( GetOverlayType() && OVERLAY_TYPE_SHORE ) == 0 ) + { + // Update dependencies. + UpdateDependency( NULL, ( CMapSolid* )pFace->GetParent() ); + UpdateParentKey(); + } + + PostModified(); +} + +//============================================================================= +// +// Overlay Utility Functions +// + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::OverlayUVToOverlayPlane( const Vector2D &vecUV, Vector &vecOverlayPoint ) +{ + vecOverlayPoint = ( vecUV.x * m_Basis.m_vecAxes[OVERLAY_BASIS_U] + + vecUV.y * m_Basis.m_vecAxes[OVERLAY_BASIS_V] ); + vecOverlayPoint += m_Basis.m_vecOrigin; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::OverlayPlaneToOverlayUV( const Vector &vecOverlayPoint, Vector2D &vecUV ) +{ + Vector vecDelta; + vecDelta = vecOverlayPoint - m_Basis.m_vecOrigin; + vecUV.x = m_Basis.m_vecAxes[OVERLAY_BASIS_U].Dot( vecDelta ); + vecUV.y = m_Basis.m_vecAxes[OVERLAY_BASIS_V].Dot( vecDelta ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::WorldToOverlayPlane( const Vector &vecWorldPoint, Vector &vecOverlayPoint ) +{ + Vector vecDelta = vecWorldPoint - m_Basis.m_vecOrigin; + float flDist = m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL].Dot( vecDelta ); + vecOverlayPoint = vecWorldPoint - ( m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL] * flDist ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::OverlayPlaneToWorld( CMapFace *pFace, const Vector &vecOverlayPoint, + Vector &vecWorldPoint ) +{ + // Create the overlay plane - the base face plane. + cplane_t surfacePlane; + pFace->GetFaceNormal( surfacePlane.normal ); + VectorNormalize( surfacePlane.normal ); + Vector vecPoint; + pFace->GetPoint( vecPoint, 0 ); + surfacePlane.dist = surfacePlane.normal.Dot( vecPoint ); + + float flDistToSurface = surfacePlane.normal.Dot( vecOverlayPoint ) - surfacePlane.dist; + float flDist = flDistToSurface; + float flDot = surfacePlane.normal.Dot( m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL] ); + if ( flDot != 0.0f ) + { + flDist = ( 1.0f / flDot ) * flDistToSurface; + } + + vecWorldPoint = vecOverlayPoint - ( m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL] * flDist ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMapOverlay::OverlayPlaneToSurfFromList( const Vector &vecOverlayPoint, Vector &vecSurfPoint ) +{ + // Initialize the point with the overlay point. + vecSurfPoint = vecOverlayPoint; + + int nFaceCount = GetFaceCount(); + CUtlVector<Vector> aPoints; + CUtlVector<cplane_t> aPlanes; + + for ( int iFace = 0; iFace < nFaceCount; ++iFace ) + { + CMapFace *pFace = GetFace( iFace ); + if ( !pFace ) + continue; + + // Set points. + aPoints.Purge(); + aPoints.SetSize( pFace->nPoints ); + aPlanes.Purge(); + aPlanes.SetSize( pFace->nPoints ); + + // Project all the face points into the overlay plane. + for ( int iPoint = 0; iPoint < pFace->nPoints; ++iPoint ) + { + WorldToOverlayPlane( pFace->Points[iPoint], aPoints[iPoint] ); + } + + // Create edge planes for clipping. + BuildEdgePlanes( aPoints.Base(), aPoints.Count(), aPlanes.Base(), aPlanes.Count() ); + + // Check to see if a point lies behind all of the edge planes - this is our face. + int iPlane; + for ( iPlane = 0; iPlane < aPlanes.Count(); ++iPlane ) + { + float flDist = aPlanes[iPlane].normal.Dot( vecOverlayPoint ) - aPlanes[iPlane].dist; + if( flDist >= 0.0f ) + break; + } + + // Point lies outside off at least one plane. + if( iPlane != aPlanes.Count() ) + { + continue; + } + + // Project the point up to the base face plane (displacement if necessary). + OverlayPlaneToWorld( pFace, vecOverlayPoint, vecSurfPoint ); + + if( pFace->HasDisp() ) + { + Vector2D vecTmp; + EditDispHandle_t handle = pFace->GetDisp(); + CMapDisp *pDisp = EditDispMgr()->GetDisp( handle ); + pDisp->BaseFacePlaneToDispUV( vecSurfPoint, vecTmp ); + pDisp->DispUVToSurf( vecTmp, vecSurfPoint, NULL, NULL ); + } + + // Clean-up. + aPoints.Purge(); + aPlanes.Purge(); + return; + } + + // Clean-up. + aPoints.Purge(); + aPlanes.Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CMapOverlay::EntityOnSurfFromListToBaseFacePlane( const Vector &vecWorldPoint, Vector &vecBasePoint ) +{ + int nFaceCount = GetFaceCount(); + for ( int iFace = 0; iFace < nFaceCount; ++iFace ) + { + CMapFace *pFace = GetFace( iFace ); + if ( !pFace ) + continue; + + if ( !pFace->HasDisp() ) + continue; + + EditDispHandle_t handle = pFace->GetDisp(); + CMapDisp *pDisp = EditDispMgr()->GetDisp( handle ); + + if ( pDisp->SurfToBaseFacePlane( vecWorldPoint, vecBasePoint ) ) + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::GetTriVerts( CMapDisp *pDisp, const Vector2D &vecSurfUV, int *pTris, Vector2D *pVertsUV ) +{ + // Get the displacement width. + int nWidth = pDisp->GetWidth(); + int nHeight = pDisp->GetHeight(); + + // scale the u, v coordinates the displacement grid size + float flU = vecSurfUV.x * ( nWidth - 1.000001f ); + float flV = vecSurfUV.y * ( nHeight - 1.000001f ); + + // find the triangle the "uv spot" resides in + int nSnapU = static_cast<int>( flU ); + int nSnapV = static_cast<int>( flV ); + if ( nSnapU == ( nWidth - 1 ) ) { --nSnapU; } + if ( nSnapV == ( nHeight - 1 ) ) { --nSnapV; } + int nNextU = nSnapU + 1; + int nNextV = nSnapV + 1; + + // Fractional portion + float flFracU = flU - static_cast<float>( nSnapU ); + float flFracV = flV - static_cast<float>( nSnapV ); + + bool bOdd = ( ( ( nSnapV * nWidth ) + nSnapU ) % 2 ) == 1; + if ( bOdd ) + { + if( ( flFracU + flFracV ) >= ( 1.0f + OVERLAY_DISPSPACE_EPSILON ) ) + { + pVertsUV[0].x = nSnapU; pVertsUV[0].y = nNextV; + pVertsUV[1].x = nNextU; pVertsUV[1].y = nNextV; + pVertsUV[2].x = nNextU; pVertsUV[2].y = nSnapV; + } + else + { + pVertsUV[0].x = nSnapU; pVertsUV[0].y = nSnapV; + pVertsUV[1].x = nSnapU; pVertsUV[1].y = nNextV; + pVertsUV[2].x = nNextU; pVertsUV[2].y = nSnapV; + } + } + else + { + if ( flFracU < flFracV ) + { + pVertsUV[0].x = nSnapU; pVertsUV[0].y = nSnapV; + pVertsUV[1].x = nSnapU; pVertsUV[1].y = nNextV; + pVertsUV[2].x = nNextU; pVertsUV[2].y = nNextV; + } + else + { + pVertsUV[0].x = nSnapU; pVertsUV[0].y = nSnapV; + pVertsUV[1].x = nNextU; pVertsUV[1].y = nNextV; + pVertsUV[2].x = nNextU; pVertsUV[2].y = nSnapV; + } + } + + // Calculate the triangle indices. + for( int iVert = 0; iVert < 3; ++iVert ) + { + pTris[iVert] = pVertsUV[iVert].y * nWidth + pVertsUV[iVert].x; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapOverlay::SetMaterial( const char *szMaterialName ) +{ + // Get the new material. + IEditorTexture *pTex = g_Textures.FindActiveTexture( szMaterialName ); + if ( !pTex ) + return; + + // Save the new material. + m_Material.m_pTexture = pTex; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +ChunkFileResult_t CMapOverlay::SaveDataToVMF( CChunkFile *pFile, CSaveInfo *pSaveInfo ) +{ + ChunkFileResult_t eResult = pFile->BeginChunk("overlaydata"); + + // Save the material name. + if ( eResult == ChunkFile_Ok ) + { + eResult = pFile->WriteKeyValue( "material", m_Material.m_pTexture->GetName() ); + } + + // Save the u,v data. + if ( eResult == ChunkFile_Ok ) + { + eResult = pFile->WriteKeyValueFloat( "StartU", m_Material.m_vecTextureU.x ); + } + if ( eResult == ChunkFile_Ok ) + { + eResult = pFile->WriteKeyValueFloat( "EndU", m_Material.m_vecTextureU.y ); + } + + if ( eResult == ChunkFile_Ok ) + { + eResult = pFile->WriteKeyValueFloat( "StartV", m_Material.m_vecTextureV.x ); + } + if ( eResult == ChunkFile_Ok ) + { + eResult = pFile->WriteKeyValueFloat( "EndV", m_Material.m_vecTextureV.y ); + } + + // Basis data. + if ( eResult == ChunkFile_Ok ) + { + eResult = pFile->WriteKeyValueVector3( "BasisOrigin", m_Basis.m_vecOrigin ); + } + if ( eResult == ChunkFile_Ok ) + { + eResult = pFile->WriteKeyValueVector3( "BasisU", m_Basis.m_vecAxes[OVERLAY_BASIS_U] ); + } + if ( eResult == ChunkFile_Ok ) + { + eResult = pFile->WriteKeyValueVector3( "BasisV", m_Basis.m_vecAxes[OVERLAY_BASIS_V] ); + } + if ( eResult == ChunkFile_Ok ) + { + eResult = pFile->WriteKeyValueVector3( "BasisNormal", m_Basis.m_vecAxes[OVERLAY_BASIS_NORMAL] ); + } + + if ( eResult == ChunkFile_Ok ) + { + Vector vecTmp( m_Handles.m_vecBasisCoords[0].x, m_Handles.m_vecBasisCoords[0].y, ( float )m_Basis.m_nAxesFlip[0] ); + eResult = pFile->WriteKeyValueVector3( "uv0", vecTmp ); + } + if ( eResult == ChunkFile_Ok ) + { + Vector vecTmp( m_Handles.m_vecBasisCoords[1].x, m_Handles.m_vecBasisCoords[1].y, ( float )m_Basis.m_nAxesFlip[1] ); + eResult = pFile->WriteKeyValueVector3( "uv1", vecTmp ); + } + if ( eResult == ChunkFile_Ok ) + { + Vector vecTmp( m_Handles.m_vecBasisCoords[2].x, m_Handles.m_vecBasisCoords[2].y, ( float )m_Basis.m_nAxesFlip[2] ); + eResult = pFile->WriteKeyValueVector3( "uv2", vecTmp ); + } + if ( eResult == ChunkFile_Ok ) + { + Vector vecTmp( m_Handles.m_vecBasisCoords[3].x, m_Handles.m_vecBasisCoords[3].y, 0.0f ); + eResult = pFile->WriteKeyValueVector3( "uv3", vecTmp ); + } + + // Sidelist. + if ( eResult == ChunkFile_Ok ) + { + char szSetValue[KEYVALUE_MAX_VALUE_LENGTH]; + CMapWorld::FaceID_FaceListsToString( szSetValue, sizeof( szSetValue ), &m_Faces, NULL ); + eResult = pFile->WriteKeyValue( "sides", szSetValue ); + } + + if ( eResult == ChunkFile_Ok ) + { + eResult = pFile->EndChunk(); + } + + return eResult; +} |