diff options
Diffstat (limited to 'engine/disp_mapload.cpp')
| -rw-r--r-- | engine/disp_mapload.cpp | 1319 |
1 files changed, 1319 insertions, 0 deletions
diff --git a/engine/disp_mapload.cpp b/engine/disp_mapload.cpp new file mode 100644 index 0000000..11b75e9 --- /dev/null +++ b/engine/disp_mapload.cpp @@ -0,0 +1,1319 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#include "render_pch.h" +#include "modelloader.h" +#include "gl_model_private.h" +#include "gl_lightmap.h" +#include "disp.h" +#include "mathlib/mathlib.h" +#include "gl_rsurf.h" +#include "gl_matsysiface.h" +#include "zone.h" +#include "materialsystem/imesh.h" +#include "materialsystem/ivballoctracker.h" +#include "mathlib/vector.h" +#include "iscratchpad3d.h" +#include "tier0/fasttimer.h" +#include "lowpassstream.h" +#include "con_nprint.h" +#include "tier2/tier2.h" +#include "tier0/dbg.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +void BuildTagData( CCoreDispInfo *pCoreDisp, CDispInfo *pDisp ); +void SmoothDispSurfNormals( CCoreDispInfo **ppListBase, int nListSize ); + +// This makes sure that whoever is creating and deleting CDispInfos frees them all +// (and calls their destructors) before the module is gone. +class CConstructorChecker +{ +public: + CConstructorChecker() {m_nConstructedObjects = 0;} + ~CConstructorChecker() {Assert(m_nConstructedObjects == 0);} + int m_nConstructedObjects; +} g_ConstructorChecker; + + + +//----------------------------------------------------------------------------- +// Static helpers. +//----------------------------------------------------------------------------- + +static void BuildDispGetSurfNormals( Vector points[4], Vector normals[4] ) +{ + // + // calculate the displacement surface normal + // + Vector tmp[2]; + Vector normal; + tmp[0] = points[1] - points[0]; + tmp[1] = points[3] - points[0]; + normal = tmp[1].Cross( tmp[0] ); + VectorNormalize( normal ); + + for( int i = 0; i < 4; i++ ) + { + normals[i] = normal; + } +} + + +static bool FindExtraDependency( unsigned short *pDependencies, int nDependencies, int iDisp ) +{ + for( int i=0; i < nDependencies; i++ ) + { + if ( pDependencies[i] == iDisp ) + return true; + } + return false; +} + + +static CDispGroup* FindCombo( CUtlVector<CDispGroup*> &combos, int idLMPage, IMaterial *pMaterial ) +{ + for( int i=0; i < combos.Size(); i++ ) + { + if( combos[i]->m_LightmapPageID == idLMPage && combos[i]->m_pMaterial == pMaterial ) + return combos[i]; + } + return NULL; +} + + +static CDispGroup* AddCombo( CUtlVector<CDispGroup*> &combos, int idLMPage, IMaterial *pMaterial ) +{ + CDispGroup *pCombo = new CDispGroup; + pCombo->m_LightmapPageID = idLMPage; + pCombo->m_pMaterial = pMaterial; + pCombo->m_nVisible = 0; + combos.AddToTail( pCombo ); + return pCombo; +} + + +static inline CDispInfo* GetModelDisp( model_t const *pWorld, int i ) +{ + return static_cast< CDispInfo* >( + DispInfo_IndexArray( pWorld->brush.pShared->hDispInfos, i ) ); +} + + +static void BuildDispSurfInit( + model_t *pWorld, + CCoreDispInfo *pBuildDisp, + SurfaceHandle_t worldSurfID ) +{ + if( !IS_SURF_VALID( worldSurfID ) ) + return; + ASSERT_SURF_VALID( worldSurfID ); + + Vector surfPoints[4]; + Vector surfNormals[4]; + Vector2D surfTexCoords[4]; + Vector2D surfLightCoords[4][4]; + + if ( MSurf_VertCount( worldSurfID ) != 4 ) + return; + +#ifndef SWDS + BuildMSurfaceVerts( pWorld->brush.pShared, worldSurfID, surfPoints, surfTexCoords, surfLightCoords ); +#endif + BuildDispGetSurfNormals( surfPoints, surfNormals ); + + CCoreDispSurface *pDispSurf = pBuildDisp->GetSurface(); + + int surfFlag = pDispSurf->GetFlags(); + int nLMVects = 1; + if( MSurf_Flags( worldSurfID ) & SURFDRAW_BUMPLIGHT ) + { + surfFlag |= CCoreDispInfo::SURF_BUMPED; + nLMVects = NUM_BUMP_VECTS + 1; + } + + pDispSurf->SetPointCount( 4 ); + for( int i = 0; i < 4; i++ ) + { + pDispSurf->SetPoint( i, surfPoints[i] ); + pDispSurf->SetPointNormal( i, surfNormals[i] ); + pDispSurf->SetTexCoord( i, surfTexCoords[i] ); + + for( int j = 0; j < nLMVects; j++ ) + { + pDispSurf->SetLuxelCoord( j, i, surfLightCoords[i][j] ); + } + } + + Vector vecS = MSurf_TexInfo( worldSurfID )->textureVecsTexelsPerWorldUnits[0].AsVector3D(); + Vector vecT = MSurf_TexInfo( worldSurfID )->textureVecsTexelsPerWorldUnits[1].AsVector3D(); + VectorNormalize( vecS ); + VectorNormalize( vecT ); + pDispSurf->SetSAxis( vecS ); + pDispSurf->SetTAxis( vecT ); + + pDispSurf->SetFlags( surfFlag ); + pDispSurf->FindSurfPointStartIndex(); + pDispSurf->AdjustSurfPointData(); + +#ifndef SWDS + // + // adjust the lightmap coordinates -- this is currently done redundantly! + // the will be fixed correctly when the displacement common code is written. + // This is here to get things running for (GDC, E3) + // + SurfaceCtx_t ctx; + SurfSetupSurfaceContext( ctx, worldSurfID ); + int lightmapWidth = MSurf_LightmapExtents( worldSurfID )[0]; + int lightmapHeight = MSurf_LightmapExtents( worldSurfID )[1]; + + Vector2D uv( 0.0f, 0.0f ); + for ( int ndxLuxel = 0; ndxLuxel < 4; ndxLuxel++ ) + { + switch( ndxLuxel ) + { + case 0: { uv.Init( 0.0f, 0.0f ); break; } + case 1: { uv.Init( 0.0f, ( float )lightmapHeight ); break; } + case 2: { uv.Init( ( float )lightmapWidth, ( float )lightmapHeight ); break; } + case 3: { uv.Init( ( float )lightmapWidth, 0.0f ); break; } + } + + uv.x += 0.5f; + uv.y += 0.5f; + + uv *= ctx.m_Scale; + uv += ctx.m_Offset; + + pDispSurf->SetLuxelCoord( 0, ndxLuxel, uv ); + } +#endif +} + +VertexFormat_t ComputeDisplacementStaticMeshVertexFormat( const IMaterial * pMaterial, const CDispGroup *pCombo, const ddispinfo_t *pMapDisps ) +{ + VertexFormat_t vertexFormat = pMaterial->GetVertexFormat(); + + // FIXME: set VERTEX_FORMAT_COMPRESSED if there are no artifacts and if it saves enough memory (use 'mem_dumpvballocs') + vertexFormat &= ~VERTEX_FORMAT_COMPRESSED; + // FIXME: check for and strip unused vertex elements (TANGENT_S/T?) + + return vertexFormat; +} + +void AddEmptyMesh( + model_t *pWorld, + CDispGroup *pCombo, + const ddispinfo_t *pMapDisps, + int *pDispInfos, + int nDisps, + int nTotalVerts, + int nTotalIndices ) +{ + CMatRenderContextPtr pRenderContext( materials ); + + CGroupMesh *pMesh = new CGroupMesh; + pCombo->m_Meshes.AddToTail( pMesh ); + + VertexFormat_t vertexFormat = ComputeDisplacementStaticMeshVertexFormat( pCombo->m_pMaterial, pCombo, pMapDisps ); + pMesh->m_pMesh = pRenderContext->CreateStaticMesh( vertexFormat, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_DISP ); + pMesh->m_pGroup = pCombo; + pMesh->m_nVisible = 0; + + CMeshBuilder builder; + builder.Begin( pMesh->m_pMesh, MATERIAL_TRIANGLES, nTotalVerts, nTotalIndices ); + + // Just advance the verts and indices and leave the data blank for now. + builder.AdvanceIndices( nTotalIndices ); + builder.AdvanceVertices( nTotalVerts ); + + builder.End(); + + + pMesh->m_DispInfos.SetSize( nDisps ); + pMesh->m_Visible.SetSize( nDisps ); + pMesh->m_VisibleDisps.SetSize( nDisps ); + + int iVertOffset = 0; + int iIndexOffset = 0; + for( int iDisp=0; iDisp < nDisps; iDisp++ ) + { + CDispInfo *pDisp = GetModelDisp( pWorld, pDispInfos[iDisp] ); + const ddispinfo_t *pMapDisp = &pMapDisps[ pDispInfos[iDisp] ]; + + pDisp->m_pMesh = pMesh; + pDisp->m_iVertOffset = iVertOffset; + pDisp->m_iIndexOffset = iIndexOffset; + + int nVerts, nIndices; + CalcMaxNumVertsAndIndices( pMapDisp->power, &nVerts, &nIndices ); + iVertOffset += nVerts; + iIndexOffset += nIndices; + + pMesh->m_DispInfos[iDisp] = pDisp; + } + + Assert( iVertOffset == nTotalVerts ); + Assert( iIndexOffset == nTotalIndices ); +} + + +void FillStaticBuffer( + CGroupMesh *pMesh, + CDispInfo *pDisp, + const CCoreDispInfo *pCoreDisp, + const CDispVert *pVerts, + int nLightmaps ) +{ +#ifndef SWDS + // Put the verts into the buffer. + int nVerts, nIndices; + CalcMaxNumVertsAndIndices( pDisp->GetPower(), &nVerts, &nIndices ); + + CMeshBuilder builder; + builder.BeginModify( pMesh->m_pMesh, pDisp->m_iVertOffset, nVerts, 0, 0 ); + + SurfaceCtx_t ctx; + SurfSetupSurfaceContext( ctx, pDisp->GetParent() ); + + for( int i=0; i < nVerts; i++ ) + { + // NOTE: position comes from our system-memory buffer so when you're restoring + // static buffers (from alt+tab), it includes changes from terrain mods. + const Vector &vPos = pCoreDisp->GetVert( i ); + builder.Position3f( vPos.x, vPos.y, vPos.z ); + + const Vector &vNormal = pCoreDisp->GetNormal( i ); + builder.Normal3f( vNormal.x, vNormal.y, vNormal.z ); + + Vector vec; + pCoreDisp->GetTangentS( i, vec ); + builder.TangentS3f( VectorExpand( vec ) ); + + pCoreDisp->GetTangentT( i, vec ); + builder.TangentT3f( VectorExpand( vec ) ); + + Vector2D texCoord; + pCoreDisp->GetTexCoord( i, texCoord ); + builder.TexCoord2f( 0, texCoord.x, texCoord.y ); + + Vector2D lightCoord; + { + pCoreDisp->GetLuxelCoord( 0, i, lightCoord ); + builder.TexCoord2f( DISP_LMCOORDS_STAGE, lightCoord.x, lightCoord.y ); + } + + float flAlpha = ( ( CCoreDispInfo * )pCoreDisp )->GetAlpha( i ); + flAlpha *= ( 1.0f / 255.0f ); + flAlpha = clamp( flAlpha, 0.0f, 1.0f ); + builder.Color4f( 1.0f, 1.0f, 1.0f, flAlpha ); + + if( nLightmaps > 1 ) + { + SurfComputeLightmapCoordinate( ctx, pDisp->GetParent(), pDisp->m_Verts[i].m_vPos, lightCoord ); + builder.TexCoord2f( 2, ctx.m_BumpSTexCoordOffset, 0.0f ); + } + + builder.AdvanceVertex(); + } + + builder.EndModify(); +#endif +} + + +void CDispInfo::CopyMapDispData( const ddispinfo_t *pBuildDisp ) +{ + m_iLightmapAlphaStart = pBuildDisp->m_iLightmapAlphaStart; + m_Power = pBuildDisp->power; + + Assert( m_Power >= 2 && m_Power <= NUM_POWERINFOS ); + m_pPowerInfo = ::GetPowerInfo( m_Power ); + + // Max # of indices: + // Take the number of triangles (2 * (size-1) * (size-1)) + // and multiply by 3! + // These can be non-null in the case of task switch restore + int size = GetSideLength(); + m_Indices.SetSize( 6 * (size-1) * (size-1) ); + + // Per-node information + if (m_pNodeInfo) + delete[] m_pNodeInfo; + + m_pNodeInfo = new DispNodeInfo_t[m_pPowerInfo->m_NodeCount]; +} + + +void DispInfo_CreateMaterialGroups( model_t *pWorld, const MaterialSystem_SortInfo_t *pSortInfos ) +{ + for ( int iDisp=0; iDisp < pWorld->brush.pShared->numDispInfos; iDisp++ ) + { + CDispInfo *pDisp = GetModelDisp( pWorld, iDisp ); + + int idLMPage = pSortInfos[MSurf_MaterialSortID( pDisp->m_ParentSurfID )].lightmapPageID; + + CDispGroup *pCombo = FindCombo( g_DispGroups, idLMPage, MSurf_TexInfo( pDisp->m_ParentSurfID )->material ); + if( !pCombo ) + pCombo = AddCombo( g_DispGroups, idLMPage, MSurf_TexInfo( pDisp->m_ParentSurfID )->material ); + + MEM_ALLOC_CREDIT(); + pCombo->m_DispInfos.AddToTail( iDisp ); + } +} + + +void DispInfo_LinkToParentFaces( model_t *pWorld, const ddispinfo_t *pMapDisps, int nDisplacements ) +{ + for ( int iDisp=0; iDisp < nDisplacements; iDisp++ ) + { + const ddispinfo_t *pMapDisp = &pMapDisps[iDisp]; + CDispInfo *pDisp = GetModelDisp( pWorld, iDisp ); + + // Set its parent. + SurfaceHandle_t surfID = SurfaceHandleFromIndex( pMapDisp->m_iMapFace ); + Assert( pMapDisp->m_iMapFace >= 0 && pMapDisp->m_iMapFace < pWorld->brush.pShared->numsurfaces ); + Assert( MSurf_Flags( surfID ) & SURFDRAW_HAS_DISP ); + surfID->pDispInfo = pDisp; + pDisp->SetParent( surfID ); + } +} + + +void DispInfo_CreateEmptyStaticBuffers( model_t *pWorld, const ddispinfo_t *pMapDisps ) +{ + // For each combo, create empty buffers. + for( int i=0; i < g_DispGroups.Size(); i++ ) + { + CDispGroup *pCombo = g_DispGroups[i]; + + int nTotalVerts=0, nTotalIndices=0; + int iStart = 0; + for( int iDisp=0; iDisp < pCombo->m_DispInfos.Size(); iDisp++ ) + { + const ddispinfo_t *pMapDisp = &pMapDisps[pCombo->m_DispInfos[iDisp]]; + + int nVerts, nIndices; + CalcMaxNumVertsAndIndices( pMapDisp->power, &nVerts, &nIndices ); + + // If we're going to pass our vertex buffer limit, or we're at the last one, + // make a static buffer and fill it up. + if( (nTotalVerts + nVerts) > MAX_STATIC_BUFFER_VERTS || + (nTotalIndices + nIndices) > MAX_STATIC_BUFFER_INDICES ) + { + AddEmptyMesh( pWorld, pCombo, pMapDisps, &pCombo->m_DispInfos[iStart], iDisp-iStart, nTotalVerts, nTotalIndices ); + Assert( nTotalVerts > 0 && nTotalIndices > 0 ); + + nTotalVerts = nTotalIndices = 0; + iStart = iDisp; + --iDisp; + } + else if( iDisp == pCombo->m_DispInfos.Size()-1 ) + { + AddEmptyMesh( pWorld, pCombo, pMapDisps, &pCombo->m_DispInfos[iStart], iDisp-iStart+1, nTotalVerts+nVerts, nTotalIndices+nIndices ); + break; + } + else + { + nTotalVerts += nVerts; + nTotalIndices += nIndices; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pWorld - +// iDisp - +// *pMapDisp - +// *pCoreDisp - +// *pVerts - +// pWorld - +// iDisp - +// Output : Returns true on success, false on failure. +// Information: Setup the CCoreDispInfo using the ddispinfo_t and have it translate the data +// into a format we'll copy into the rendering structures. This roundaboutness is because +// of legacy code. It should all just be stored in the map file, but it's not a high priority right now. +//----------------------------------------------------------------------------- +bool DispInfo_CreateFromMapDisp( model_t *pWorld, int iDisp, const ddispinfo_t *pMapDisp, CCoreDispInfo *pCoreDisp, const CDispVert *pVerts, + const CDispTri *pTris,const MaterialSystem_SortInfo_t *pSortInfos, bool bRestoring ) +{ + // Get the matching CDispInfo to fill in. + CDispInfo *pDisp = GetModelDisp( pWorld, iDisp ); + + // Initialize the core disp info with data from the map displacement. + pCoreDisp->GetSurface()->SetPointStart( pMapDisp->startPosition ); + pCoreDisp->InitDispInfo( pMapDisp->power, pMapDisp->minTess, pMapDisp->smoothingAngle, pVerts, pTris ); + pCoreDisp->SetNeighborData( pMapDisp->m_EdgeNeighbors, pMapDisp->m_CornerNeighbors ); + + // Copy the allowed verts list. + ErrorIfNot( pCoreDisp->GetAllowedVerts().GetNumDWords() == sizeof( pMapDisp->m_AllowedVerts ) / 4, ( "DispInfo_StoreMapData: size mismatch in 'allowed verts' list" ) ); + for ( int iVert = 0; iVert < pCoreDisp->GetAllowedVerts().GetNumDWords(); ++iVert ) + { + pCoreDisp->GetAllowedVerts().SetDWord( iVert, pMapDisp->m_AllowedVerts[iVert] ); + } + + // Build the reset of the intermediate data from the initial map displacement data. + BuildDispSurfInit( pWorld, pCoreDisp, pDisp->GetParent() ); + if ( !pCoreDisp->Create() ) + return false; + + // Save the point start index - needed for overlays. + pDisp->m_iPointStart = pCoreDisp->GetSurface()->GetPointStartIndex(); + + // Now setup the CDispInfo. + pDisp->m_Index = static_cast<unsigned short>( iDisp ); + + // Store ddispinfo_t data. + pDisp->CopyMapDispData( pMapDisp ); + + // Store CCoreDispInfo data. + if( !pDisp->CopyCoreDispData( pWorld, pSortInfos, pCoreDisp, bRestoring ) ) + return false; + + // Initialize all the active and other verts after setting up neighbors. + pDisp->InitializeActiveVerts(); + pDisp->m_iLightmapSamplePositionStart = pMapDisp->m_iLightmapSamplePositionStart; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pWorld - +// iDisp - +// *pCoreDisp - +// *pVerts - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +void DispInfo_CreateStaticBuffersAndTags( model_t *pWorld, int iDisp, CCoreDispInfo *pCoreDisp, const CDispVert *pVerts ) +{ + // Get the matching CDispInfo to fill in. + CDispInfo *pDisp = GetModelDisp( pWorld, iDisp ); + + // Now copy the CCoreDisp's data into the static buffer. + FillStaticBuffer( pDisp->m_pMesh, pDisp, pCoreDisp, pVerts, pDisp->NumLightMaps() ); + + // Now build the tagged data for visualization. + BuildTagData( pCoreDisp, pDisp ); +} + +// On the xbox, we lock the meshes ahead of time. +void SetupMeshReaders( model_t *pWorld, int nDisplacements ) +{ + for ( int iDisp=0; iDisp < nDisplacements; iDisp++ ) + { + CDispInfo *pDisp = GetModelDisp( pWorld, iDisp ); + + MeshDesc_t desc; + memset( &desc, 0, sizeof( desc ) ); + + desc.m_VertexSize_Position = sizeof( CDispRenderVert ); + desc.m_VertexSize_TexCoord[0] = sizeof( CDispRenderVert ); + desc.m_VertexSize_TexCoord[DISP_LMCOORDS_STAGE] = sizeof( CDispRenderVert ); + desc.m_VertexSize_Normal = sizeof( CDispRenderVert ); + desc.m_VertexSize_TangentS = sizeof( CDispRenderVert ); + desc.m_VertexSize_TangentT = sizeof( CDispRenderVert ); + + CDispRenderVert *pBaseVert = pDisp->m_Verts.Base(); + desc.m_pPosition = (float*)&pBaseVert->m_vPos; + desc.m_pTexCoord[0] = (float*)&pBaseVert->m_vTexCoord; + desc.m_pTexCoord[DISP_LMCOORDS_STAGE] = (float*)&pBaseVert->m_LMCoords; + desc.m_pNormal = (float*)&pBaseVert->m_vNormal; + desc.m_pTangentS = (float*)&pBaseVert->m_vSVector; + desc.m_pTangentT = (float*)&pBaseVert->m_vTVector; + + desc.m_nIndexSize = 1; + desc.m_pIndices = pDisp->m_Indices.Base(); + + pDisp->m_MeshReader.BeginRead_Direct( desc, pDisp->NumVerts(), pDisp->m_nIndices ); + } +} + +void UpdateDispBBoxes( model_t *pWorld, int nDisplacements ) +{ + for ( int iDisp=0; iDisp < nDisplacements; iDisp++ ) + { + CDispInfo *pDisp = GetModelDisp( pWorld, iDisp ); + pDisp->UpdateBoundingBox(); + } +} + + +#include "tier0/memdbgoff.h" +bool DispInfo_LoadDisplacements( model_t *pWorld, bool bRestoring ) +{ + const MaterialSystem_SortInfo_t *pSortInfos = materialSortInfoArray; + + int nDisplacements = CMapLoadHelper::LumpSize( LUMP_DISPINFO ) / sizeof( ddispinfo_t ); + int nLuxels = CMapLoadHelper::LumpSize( LUMP_DISP_LIGHTMAP_ALPHAS ); + int nSamplePositionBytes = CMapLoadHelper::LumpSize( LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS ); + + // Setup the world's list of displacements. + if ( bRestoring ) + { + /* Breakpoint-able: */ + if (pWorld->brush.pShared->numDispInfos != nDisplacements) + { + volatile int a = 0; a = a + 1; + } + + if ( !pWorld->brush.pShared->numDispInfos && nDisplacements ) + { + // Attempting to restore displacements before displacements got loaded + return false; + } + + ErrorIfNot( + pWorld->brush.pShared->numDispInfos == nDisplacements, + ("DispInfo_LoadDisplacments: dispcounts (%d and %d) don't match.", pWorld->brush.pShared->numDispInfos, nDisplacements) + ); + + ErrorIfNot( + g_DispLMAlpha.Count() == nLuxels, + ("DispInfo_LoadDisplacements: lightmap alpha counts (%d and %d) don't match.", g_DispLMAlpha.Count(), nLuxels) + ); + } + else + { + // Create the displacements. + pWorld->brush.pShared->numDispInfos = nDisplacements; + pWorld->brush.pShared->hDispInfos = DispInfo_CreateArray( pWorld->brush.pShared->numDispInfos ); + + // Load lightmap alphas. + { + MEM_ALLOC_CREDIT(); + g_DispLMAlpha.SetSize( nLuxels ); + } + CMapLoadHelper lhDispLMAlphas( LUMP_DISP_LIGHTMAP_ALPHAS ); + lhDispLMAlphas.LoadLumpData( 0, nLuxels, g_DispLMAlpha.Base() ); + + // Load lightmap sample positions. + { + MEM_ALLOC_CREDIT(); + g_DispLightmapSamplePositions.SetSize( nSamplePositionBytes ); + } + CMapLoadHelper lhDispLMPositions( LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS ); + lhDispLMPositions.LoadLumpData( 0, nSamplePositionBytes, g_DispLightmapSamplePositions.Base() ); + } + + // Free old data. + DispInfo_ReleaseMaterialSystemObjects( pWorld ); + + // load the displacement info structures into temporary space + // using temporary storage that is not the stack for compatibility with console stack +#ifndef _X360 + ddispinfo_t tempDisps[MAX_MAP_DISPINFO]; +#else + CUtlMemory< ddispinfo_t > m_DispInfoBuf( 0, MAX_MAP_DISPINFO ); + ddispinfo_t *tempDisps = m_DispInfoBuf.Base(); +#endif + ErrorIfNot( + nDisplacements <= MAX_MAP_DISPINFO, + ("DispInfo_LoadDisplacements: nDisplacements (%d) > MAX_MAP_DISPINFO (%d)", nDisplacements, MAX_MAP_DISPINFO) + ); + CMapLoadHelper lhDispInfo( LUMP_DISPINFO ); + lhDispInfo.LoadLumpData( 0, nDisplacements * sizeof( ddispinfo_t ), tempDisps ); + + // Now hook up the displacements to their parents. + DispInfo_LinkToParentFaces( pWorld, tempDisps, nDisplacements ); + + // First, create "groups" (or "combos") which contain all the displacements that + // use the same material and lightmap. + DispInfo_CreateMaterialGroups( pWorld, pSortInfos ); + + // Now make the static buffers for each material/lightmap combo. + if ( g_VBAllocTracker ) + g_VBAllocTracker->TrackMeshAllocations( "DispInfo_LoadDisplacements" ); + DispInfo_CreateEmptyStaticBuffers( pWorld, tempDisps ); + if ( g_VBAllocTracker ) + g_VBAllocTracker->TrackMeshAllocations( NULL ); + + // Now setup each displacement one at a time. + // using temporary storage that is not the stack for compatibility with console stack +#ifndef _X360 + CDispVert tempVerts[MAX_DISPVERTS]; +#else + CUtlMemory< CDispVert > m_DispVertsBuf( 0, MAX_DISPVERTS ); + CDispVert *tempVerts = m_DispVertsBuf.Base(); +#endif + +#ifndef _X360 + CDispTri tempTris[MAX_DISPTRIS]; +#else + // using temporary storage that is not the stack for compatibility with console stack + CUtlMemory< CDispTri > m_DispTrisBuf( 0, MAX_DISPTRIS ); + CDispTri *tempTris = m_DispTrisBuf.Base(); +#endif + + int iCurVert = 0; + int iCurTri = 0; + + // Core displacement list. + CUtlVector<CCoreDispInfo*> aCoreDisps; + int iDisp = 0; + for ( iDisp = 0; iDisp < nDisplacements; ++iDisp ) + { + CCoreDispInfo *pCoreDisp = new CCoreDispInfo; + aCoreDisps.AddToTail( pCoreDisp ); + } + + CMapLoadHelper lhDispVerts( LUMP_DISP_VERTS ); + CMapLoadHelper lhDispTris( LUMP_DISP_TRIS ); + + for ( iDisp = 0; iDisp < nDisplacements; ++iDisp ) + { + // Get the current map displacement. + ddispinfo_t *pMapDisp = &tempDisps[iDisp]; + if ( !pMapDisp ) + continue; + + // Load the vertices from the file. + int nVerts = NUM_DISP_POWER_VERTS( pMapDisp->power ); + ErrorIfNot( nVerts <= MAX_DISPVERTS, ( "DispInfo_LoadDisplacements: invalid vertex count (%d)", nVerts ) ); + lhDispVerts.LoadLumpData( iCurVert * sizeof(CDispVert), nVerts*sizeof(CDispVert), tempVerts ); + iCurVert += nVerts; + + // Load the triangle indices from the file. + int nTris = NUM_DISP_POWER_TRIS( pMapDisp->power ); + ErrorIfNot( nTris <= MAX_DISPTRIS, ( "DispInfo_LoadDisplacements: invalid tri count (%d)", nTris ) ); + lhDispTris.LoadLumpData( iCurTri * sizeof(CDispTri), nTris*sizeof(CDispTri), tempTris ); + iCurTri += nTris; + + // Now create the CoreDispInfo and the base CDispInfo. + if ( !DispInfo_CreateFromMapDisp( pWorld, iDisp, pMapDisp, aCoreDisps[iDisp], tempVerts, tempTris, pSortInfos, bRestoring ) ) + return false; + } + + // Smooth Normals. + SmoothDispSurfNormals( aCoreDisps.Base(), nDisplacements ); + + // Fill in the static buffers. + for ( iDisp = 0; iDisp < nDisplacements; ++iDisp ) + { + DispInfo_CreateStaticBuffersAndTags( pWorld, iDisp, aCoreDisps[iDisp], tempVerts ); + + // Copy over the now blended normals + CDispInfo *pDisp = GetModelDisp( pWorld, iDisp ); + pDisp->CopyCoreDispVertData( aCoreDisps[iDisp], pDisp->m_BumpSTexCoordOffset ); + + } + + // Destroy core displacement list. + aCoreDisps.PurgeAndDeleteElements(); + + // If we're not using LOD, then maximally tesselate all the displacements and + // make sure they never change. + for ( iDisp=0; iDisp < nDisplacements; iDisp++ ) + { + CDispInfo *pDisp = GetModelDisp( pWorld, iDisp ); + + pDisp->m_ActiveVerts = pDisp->m_AllowedVerts; + } + + for ( iDisp=0; iDisp < nDisplacements; iDisp++ ) + { + CDispInfo *pDisp = GetModelDisp( pWorld, iDisp ); + pDisp->TesselateDisplacement(); + } + + SetupMeshReaders( pWorld, nDisplacements ); + + UpdateDispBBoxes( pWorld, nDisplacements ); + + return true; +} +#include "tier0/memdbgon.h" + + +void DispInfo_ReleaseMaterialSystemObjects( model_t *pWorld ) +{ + CMatRenderContextPtr pRenderContext( materials ); + + // Free all the static meshes. + for( int iGroup=0; iGroup < g_DispGroups.Size(); iGroup++ ) + { + CDispGroup *pGroup = g_DispGroups[iGroup]; + + for( int iMesh=0; iMesh < pGroup->m_Meshes.Size(); iMesh++ ) + { + CGroupMesh *pMesh = pGroup->m_Meshes[iMesh]; + + pRenderContext->DestroyStaticMesh( pMesh->m_pMesh ); + } + + pGroup->m_Meshes.PurgeAndDeleteElements(); + } + + g_DispGroups.PurgeAndDeleteElements(); + + + // Clear pointers in the dispinfos. + if( pWorld ) + { + for( int iDisp=0; iDisp < pWorld->brush.pShared->numDispInfos; iDisp++ ) + { + CDispInfo *pDisp = GetModelDisp( pWorld, iDisp ); + if ( !pDisp ) + { + AssertOnce( 0 ); + continue; + } + + pDisp->m_pMesh = NULL; + pDisp->m_iVertOffset = pDisp->m_iIndexOffset = 0; + } + } +} + + +//----------------------------------------------------------------------------- +// CDispInfo implementation. +//----------------------------------------------------------------------------- + +CDispInfo::CDispInfo() +{ + m_ParentSurfID = SURFACE_HANDLE_INVALID; + + m_bTouched = false; + + ++g_ConstructorChecker.m_nConstructedObjects; + + m_BBoxMin.Init(); + m_BBoxMax.Init(); + + m_idLMPage = -1; + + m_pPowerInfo = NULL; + + m_ViewerSphereCenter.Init( 1e24, 1e24, 1e24 ); + + m_bInUse = false; + + m_pNodeInfo = 0; + + m_pMesh = NULL; + + m_Tag = NULL; + m_pDispArray = NULL; + + m_FirstDecal = DISP_DECAL_HANDLE_INVALID; + m_FirstShadowDecal = DISP_SHADOW_HANDLE_INVALID; + + for ( int i=0; i < 4; i++ ) + { + m_EdgeNeighbors[i].SetInvalid(); + m_CornerNeighbors[i].SetInvalid(); + } +} + + +CDispInfo::~CDispInfo() +{ + if (m_pNodeInfo) + delete[] m_pNodeInfo; + + delete[] m_pWalkIndices; + delete[] m_pBuildIndices; + + --g_ConstructorChecker.m_nConstructedObjects; + + // All the decals should have been freed through + // CModelLoader::Map_UnloadModel -> R_DecalTerm + Assert( m_FirstDecal == DISP_DECAL_HANDLE_INVALID ); + Assert( m_FirstShadowDecal == DISP_SHADOW_HANDLE_INVALID ); +} + + +void CDispInfo::CopyCoreDispVertData( const CCoreDispInfo *pCoreDisp, float bumpSTexCoordOffset ) +{ +#ifndef SWDS + if( NumLightMaps() <= 1 ) + { + bumpSTexCoordOffset = 0.0f; + } + // Copy vertex positions (for backfacing tests). + m_Verts.SetSize( m_pPowerInfo->m_MaxVerts ); + m_BumpSTexCoordOffset = bumpSTexCoordOffset; + for( int i=0; i < NumVerts(); i++ ) + { + pCoreDisp->GetVert( i, m_Verts[i].m_vPos ); + + pCoreDisp->GetTexCoord( i, m_Verts[i].m_vTexCoord ); + pCoreDisp->GetLuxelCoord( 0, i, m_Verts[i].m_LMCoords ); + + // mat_normals needs this as well as the dynamic lighting code + pCoreDisp->GetNormal( i, m_Verts[i].m_vNormal ); + pCoreDisp->GetTangentS( i, m_Verts[i].m_vSVector ); + pCoreDisp->GetTangentT( i, m_Verts[i].m_vTVector ); + } +#endif +} + +bool CDispInfo::CopyCoreDispData( + model_t *pWorld, + const MaterialSystem_SortInfo_t *pSortInfos, + const CCoreDispInfo *pCoreDisp, + bool bRestoring ) +{ + m_idLMPage = pSortInfos[MSurf_MaterialSortID( GetParent() )].lightmapPageID; + +#ifndef SWDS + SurfaceCtx_t ctx; + SurfSetupSurfaceContext( ctx, GetParent() ); +#endif + + // Restoring is only for alt+tabbing, which can't happen on consoles + if ( IsPC() && bRestoring ) + { +#ifndef SWDS + // When restoring, have to recompute lightmap coords + if( NumLightMaps() > 1 ) + { + m_BumpSTexCoordOffset = ctx.m_BumpSTexCoordOffset; + } + else + { + m_BumpSTexCoordOffset = 0.0f; + } + for( int i=0; i < NumVerts(); i++ ) + { + pCoreDisp->GetLuxelCoord( 0, i, m_Verts[i].m_LMCoords ); + } +#endif // SWDS + return true; + } + + // When restoring, leave all this data the same. + const CCoreDispSurface *pSurface = pCoreDisp->GetSurface(); + for( int index=0; index < 4; index++ ) + { + pSurface->GetTexCoord( index, m_BaseSurfaceTexCoords[index] ); + m_BaseSurfacePositions[index] = pSurface->GetPoint( index ); + } + +#ifndef SWDS + CopyCoreDispVertData( pCoreDisp, ctx.m_BumpSTexCoordOffset ); +#endif + + // Copy neighbor info. + for ( int iEdge=0; iEdge < 4; iEdge++ ) + { + m_EdgeNeighbors[iEdge] = *pCoreDisp->GetEdgeNeighbor( iEdge ); + m_CornerNeighbors[iEdge] = *pCoreDisp->GetCornerNeighbors( iEdge ); + } + + // Copy allowed verts. + m_AllowedVerts = pCoreDisp->GetAllowedVerts(); + + m_nIndices = 0; + return true; +} + + +int CDispInfo::NumLightMaps() +{ + return (MSurf_Flags( m_ParentSurfID ) & SURFDRAW_BUMPLIGHT) ? NUM_BUMP_VECTS+1 : 1; +} + +//----------------------------------------------------------------------------- +// Purpose: +// NOTE: You cannot use the builddisp.cpp IsTriWalkable, IsTriBuildable functions +// because the flags are different having been collapsed in vbsp +//----------------------------------------------------------------------------- +void BuildTagData( CCoreDispInfo *pCoreDisp, CDispInfo *pDisp ) +{ + int nWalkTest = 0; + int nBuildTest = 0; + int iTri; + for ( iTri = 0; iTri < pCoreDisp->GetTriCount(); ++iTri ) + { + if ( pCoreDisp->IsTriTag( iTri, DISPTRI_TAG_WALKABLE ) ) + { + nWalkTest++; + } + + if ( pCoreDisp->IsTriTag( iTri, DISPTRI_TAG_BUILDABLE ) ) + { + nBuildTest++; + } + } + + nWalkTest *= 3; + nBuildTest *= 3; + + pDisp->m_pWalkIndices = new unsigned short[nWalkTest]; + pDisp->m_pBuildIndices = new unsigned short[nBuildTest]; + + int nWalkCount = 0; + int nBuildCount = 0; + for ( iTri = 0; iTri < pCoreDisp->GetTriCount(); ++iTri ) + { + if ( pCoreDisp->IsTriTag( iTri, DISPTRI_TAG_WALKABLE ) ) + { + pCoreDisp->GetTriIndices( iTri, + pDisp->m_pWalkIndices[nWalkCount], + pDisp->m_pWalkIndices[nWalkCount+1], + pDisp->m_pWalkIndices[nWalkCount+2] ); + + nWalkCount += 3; + } + + if ( pCoreDisp->IsTriTag( iTri, DISPTRI_TAG_BUILDABLE ) ) + { + pCoreDisp->GetTriIndices( iTri, + pDisp->m_pBuildIndices[nBuildCount], + pDisp->m_pBuildIndices[nBuildCount+1], + pDisp->m_pBuildIndices[nBuildCount+2] ); + + nBuildCount += 3; + } + } + + Assert( nWalkCount == nWalkTest ); + Assert( nBuildCount == nBuildTest ); + + pDisp->m_nWalkIndexCount = nWalkCount; + pDisp->m_nBuildIndexCount = nBuildCount; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pDisp - +// &vecPoint - +// Output : int +//----------------------------------------------------------------------------- +int FindNeighborCornerVert( CCoreDispInfo *pDisp, const Vector &vecPoint ) +{ + CDispUtilsHelper *pDispHelper = pDisp; + + int iClosest = 0; + float flClosest = 1e24; + for ( int iCorner = 0; iCorner < 4; ++iCorner ) + { + + // Has it been touched? + CVertIndex viCornerVert = pDispHelper->GetPowerInfo()->GetCornerPointIndex( iCorner ); + int iCornerVert = pDispHelper->VertIndexToInt( viCornerVert ); + const Vector &vecCornerVert = pDisp->GetVert( iCornerVert ); + + float flDist = vecCornerVert.DistTo( vecPoint ); + if ( flDist < flClosest ) + { + iClosest = iCorner; + flClosest = flDist; + } + } + + if ( flClosest <= 0.1f ) + return iClosest; + else + return -1; +} + +// sets a new normal/tangentS, recomputes tangent T +void UpdateTangentSpace(CCoreDispInfo *pDisp, int iVert, const Vector &vNormal, const Vector &vTanS) +{ + Vector tanT; + pDisp->SetNormal( iVert, vNormal ); + CrossProduct( vTanS, vNormal, tanT ); + pDisp->SetTangentS(iVert, vTanS); + pDisp->SetTangentT(iVert, tanT); +} + +void UpdateTangentSpace(CCoreDispInfo *pDisp, const CVertIndex &index, const Vector &vNormal, const Vector &vTanS) +{ + UpdateTangentSpace(pDisp, pDisp->VertIndexToInt(index), vNormal, vTanS); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : **ppListBase - +// nListSize - +//----------------------------------------------------------------------------- +void BlendSubNeighbors( CCoreDispInfo **ppListBase, int nListSize ) +{ + // Loop through all of the displacements in the list. + for ( int iDisp = 0; iDisp < nListSize; ++iDisp ) + { + // Get the current displacement. + CCoreDispInfo *pDisp = ppListBase[iDisp]; + if ( !pDisp ) + continue; + + // Loop through all the edges of the displacement. + for ( int iEdge = 0; iEdge < 4; ++iEdge ) + { + // Find valid neighbors along the edge. + CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge ); + if ( !pEdge ) + continue; + + // Check to see if we have sub-neighbors - defines a t-junction in this world. If not, + // then the normal blend edges function will catch it all. + if ( !pEdge->m_SubNeighbors[0].IsValid() || !pEdge->m_SubNeighbors[1].IsValid() ) + continue; + + // Get the mid-point of the current displacement. + CVertIndex viMidPoint = pDisp->GetEdgeMidPoint( iEdge ); + int iMidPoint = pDisp->VertIndexToInt( viMidPoint ); + + const Vector &vecMidPoint = pDisp->GetVert( iMidPoint ); + + // Get the current sub-neighbors along the edge. + CCoreDispInfo *pNeighbor1 = ppListBase[pEdge->m_SubNeighbors[0].GetNeighborIndex()]; + CCoreDispInfo *pNeighbor2 = ppListBase[pEdge->m_SubNeighbors[1].GetNeighborIndex()]; + + // Get the current sub-neighbor corners. + int iCorners[2]; + iCorners[0] = FindNeighborCornerVert( pNeighbor1, vecMidPoint ); + iCorners[1] = FindNeighborCornerVert( pNeighbor2, vecMidPoint ); + if ( iCorners[0] != -1 && iCorners[1] != -1 ) + { + CVertIndex viCorners[2] = { pNeighbor1->GetCornerPointIndex( iCorners[0] ),pNeighbor2->GetCornerPointIndex( iCorners[1] ) }; + + // Accumulate the normals at the mid-point of the primary edge and corners of the sub-neighbors. + Vector vecAverage = pDisp->GetNormal( iMidPoint ); + vecAverage += pNeighbor1->GetNormal( viCorners[0] ); + vecAverage += pNeighbor2->GetNormal( viCorners[1] ); + + // Re-normalize. + VectorNormalize( vecAverage ); + Vector vAvgTanS = pDisp->GetTangentS(iMidPoint); + vAvgTanS += pNeighbor1->GetTangentS(viCorners[0]); + vAvgTanS += pNeighbor2->GetTangentS(viCorners[1]); + VectorNormalize(vAvgTanS); + //vecAverage.Init( 0.0f, 0.0f, 1.0f ); + + // Set the new normal value back. + UpdateTangentSpace( pDisp, iMidPoint, vecAverage, vAvgTanS ); + UpdateTangentSpace( pNeighbor1, viCorners[0], vecAverage, vAvgTanS ); + UpdateTangentSpace( pNeighbor2, viCorners[1], vecAverage, vAvgTanS ); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pDisp - +// iNeighbors[512] - +// Output : int +//----------------------------------------------------------------------------- +int GetAllNeighbors( const CCoreDispInfo *pDisp, int iNeighbors[512] ) +{ + int nNeighbors = 0; + + // Check corner neighbors. + for ( int iCorner=0; iCorner < 4; iCorner++ ) + { + const CDispCornerNeighbors *pCorner = pDisp->GetCornerNeighbors( iCorner ); + + for ( int i=0; i < pCorner->m_nNeighbors; i++ ) + { + if ( nNeighbors < 512 ) + iNeighbors[nNeighbors++] = pCorner->m_Neighbors[i]; + } + } + + for ( int iEdge=0; iEdge < 4; iEdge++ ) + { + const CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge ); + + for ( int i=0; i < 2; i++ ) + { + if ( pEdge->m_SubNeighbors[i].IsValid() ) + if ( nNeighbors < 512 ) + iNeighbors[nNeighbors++] = pEdge->m_SubNeighbors[i].GetNeighborIndex(); + } + } + + return nNeighbors; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : **ppListBase - +// listSize - +//----------------------------------------------------------------------------- +void BlendCorners( CCoreDispInfo **ppListBase, int nListSize ) +{ + CUtlVector<int> nbCornerVerts; + + for ( int iDisp = 0; iDisp < nListSize; ++iDisp ) + { + CCoreDispInfo *pDisp = ppListBase[iDisp]; + + int iNeighbors[512]; + int nNeighbors = GetAllNeighbors( pDisp, iNeighbors ); + + // Make sure we have room for all the neighbors. + nbCornerVerts.RemoveAll(); + nbCornerVerts.EnsureCapacity( nNeighbors ); + nbCornerVerts.AddMultipleToTail( nNeighbors ); + + // For each corner. + for ( int iCorner=0; iCorner < 4; iCorner++ ) + { + // Has it been touched? + CVertIndex cornerVert = pDisp->GetCornerPointIndex( iCorner ); + int iCornerVert = pDisp->VertIndexToInt( cornerVert ); + const Vector &vCornerVert = pDisp->GetVert( iCornerVert ); + + // For each displacement sharing this corner.. + Vector vAverage = pDisp->GetNormal( iCornerVert ); + Vector vAvgTanS; + pDisp->GetTangentS( iCornerVert, vAvgTanS ); + + for ( int iNeighbor=0; iNeighbor < nNeighbors; iNeighbor++ ) + { + int iNBListIndex = iNeighbors[iNeighbor]; + CCoreDispInfo *pNeighbor = ppListBase[iNBListIndex]; + + // Find out which vert it is on the neighbor. + int iNBCorner = FindNeighborCornerVert( pNeighbor, vCornerVert ); + if ( iNBCorner == -1 ) + { + nbCornerVerts[iNeighbor] = -1; // remove this neighbor from the list. + } + else + { + CVertIndex viNBCornerVert = pNeighbor->GetCornerPointIndex( iNBCorner ); + int iNBVert = pNeighbor->VertIndexToInt( viNBCornerVert ); + nbCornerVerts[iNeighbor] = iNBVert; + vAverage += pNeighbor->GetNormal( iNBVert ); + vAvgTanS += pNeighbor->GetTangentS( iNBVert ); + } + } + + + // Blend all the neighbor normals with this one. + VectorNormalize( vAverage ); + VectorNormalize( vAvgTanS ); + UpdateTangentSpace(pDisp, iCornerVert, vAverage, vAvgTanS ); + + for ( int iNeighbor=0; iNeighbor < nNeighbors; iNeighbor++ ) + { + int iNBListIndex = iNeighbors[iNeighbor]; + if ( nbCornerVerts[iNeighbor] == -1 ) + continue; + + CCoreDispInfo *pNeighbor = ppListBase[iNBListIndex]; + UpdateTangentSpace(pNeighbor, nbCornerVerts[iNeighbor], vAverage, vAvgTanS); + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : **ppListBase - +// listSize - +//----------------------------------------------------------------------------- +void BlendEdges( CCoreDispInfo **ppListBase, int nListSize ) +{ + // Loop through all the displacements in the list. + for ( int iDisp = 0; iDisp < nListSize; ++iDisp ) + { + // Get the current displacement. + CCoreDispInfo *pDisp = ppListBase[iDisp]; + if ( !pDisp ) + continue; + + // Loop through all of the edges on a displacement. + for ( int iEdge = 0; iEdge < 4; ++iEdge ) + { + // Get the current displacement edge. + CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge ); + if ( !pEdge ) + continue; + + // Check for sub-edges. + for ( int iSubEdge = 0; iSubEdge < 2; ++iSubEdge ) + { + // Get the current sub-edge. + CDispSubNeighbor *pSubEdge = &pEdge->m_SubNeighbors[iSubEdge]; + if ( !pSubEdge->IsValid() ) + continue; + + // Get the current neighbor. + CCoreDispInfo *pNeighbor = ppListBase[pSubEdge->GetNeighborIndex()]; + if ( !pNeighbor ) + continue; + + // Get the edge dimension. + int iEdgeDim = g_EdgeDims[iEdge]; + + CDispSubEdgeIterator it; + it.Start( pDisp, iEdge, iSubEdge, true ); + + // Get setup on the first corner vert. + it.Next(); + CVertIndex viPrevPos = it.GetVertIndex(); + while ( it.Next() ) + { + // Blend the two. + if ( !it.IsLastVert() ) + { + Vector vecAverage = pDisp->GetNormal( it.GetVertIndex() ) + pNeighbor->GetNormal( it.GetNBVertIndex() ); + Vector vAvgTanS = pDisp->GetTangentS( it.GetVertIndex() ) + pNeighbor->GetTangentS( it.GetNBVertIndex() ); + VectorNormalize( vecAverage ); + VectorNormalize( vAvgTanS ); + UpdateTangentSpace(pDisp, it.GetVertIndex(), vecAverage, vAvgTanS ); + UpdateTangentSpace(pNeighbor, it.GetNBVertIndex(), vecAverage, vAvgTanS ); + } + + // Now blend the in-between verts (if this edge is high-res). + int iPrevPos = viPrevPos[!iEdgeDim]; + int iCurPos = it.GetVertIndex()[!iEdgeDim]; + for ( int iTween = iPrevPos+1; iTween < iCurPos; iTween++ ) + { + float flPercent = RemapVal( iTween, iPrevPos, iCurPos, 0, 1 ); + Vector vecNormal; + VectorLerp( pDisp->GetNormal( viPrevPos ), pDisp->GetNormal( it.GetVertIndex() ), flPercent, vecNormal ); + VectorNormalize( vecNormal ); + Vector vAvgTanS; + VectorLerp( pDisp->GetTangentS( viPrevPos ), pDisp->GetTangentS( it.GetVertIndex() ), flPercent, vAvgTanS ); + VectorNormalize( vAvgTanS ); + + CVertIndex viTween; + viTween[iEdgeDim] = it.GetVertIndex()[iEdgeDim]; + viTween[!iEdgeDim] = iTween; + UpdateTangentSpace(pDisp, viTween, vecNormal, vAvgTanS); + } + + viPrevPos = it.GetVertIndex(); + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : **pListBase - +// listSize - +// NOTE: todo - this is almost the same code as found in vrad, should probably +// move it up into common code at some point if the feature +// continues to get used +//----------------------------------------------------------------------------- +void SmoothDispSurfNormals( CCoreDispInfo **ppListBase, int nListSize ) +{ + // Setup helper list for iteration. + for ( int iDisp = 0; iDisp < nListSize; ++iDisp ) + { + ppListBase[iDisp]->SetDispUtilsHelperInfo( ppListBase, nListSize ); + } + + // Blend normals along t-junctions, corners, and edges. + BlendSubNeighbors( ppListBase, nListSize ); + BlendCorners( ppListBase, nListSize ); + BlendEdges( ppListBase, nListSize ); +} |