Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1319 lines
39 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include "render_pch.h"
  10. #include "modelloader.h"
  11. #include "gl_model_private.h"
  12. #include "gl_lightmap.h"
  13. #include "disp.h"
  14. #include "mathlib/mathlib.h"
  15. #include "gl_rsurf.h"
  16. #include "gl_matsysiface.h"
  17. #include "zone.h"
  18. #include "materialsystem/imesh.h"
  19. #include "materialsystem/ivballoctracker.h"
  20. #include "mathlib/vector.h"
  21. #include "iscratchpad3d.h"
  22. #include "tier0/fasttimer.h"
  23. #include "lowpassstream.h"
  24. #include "con_nprint.h"
  25. #include "tier2/tier2.h"
  26. #include "tier0/dbg.h"
  27. // memdbgon must be the last include file in a .cpp file!!!
  28. #include "tier0/memdbgon.h"
  29. void BuildTagData( CCoreDispInfo *pCoreDisp, CDispInfo *pDisp );
  30. void SmoothDispSurfNormals( CCoreDispInfo **ppListBase, int nListSize );
  31. // This makes sure that whoever is creating and deleting CDispInfos frees them all
  32. // (and calls their destructors) before the module is gone.
  33. class CConstructorChecker
  34. {
  35. public:
  36. CConstructorChecker() {m_nConstructedObjects = 0;}
  37. ~CConstructorChecker() {Assert(m_nConstructedObjects == 0);}
  38. int m_nConstructedObjects;
  39. } g_ConstructorChecker;
  40. //-----------------------------------------------------------------------------
  41. // Static helpers.
  42. //-----------------------------------------------------------------------------
  43. static void BuildDispGetSurfNormals( Vector points[4], Vector normals[4] )
  44. {
  45. //
  46. // calculate the displacement surface normal
  47. //
  48. Vector tmp[2];
  49. Vector normal;
  50. tmp[0] = points[1] - points[0];
  51. tmp[1] = points[3] - points[0];
  52. normal = tmp[1].Cross( tmp[0] );
  53. VectorNormalize( normal );
  54. for( int i = 0; i < 4; i++ )
  55. {
  56. normals[i] = normal;
  57. }
  58. }
  59. static bool FindExtraDependency( unsigned short *pDependencies, int nDependencies, int iDisp )
  60. {
  61. for( int i=0; i < nDependencies; i++ )
  62. {
  63. if ( pDependencies[i] == iDisp )
  64. return true;
  65. }
  66. return false;
  67. }
  68. static CDispGroup* FindCombo( CUtlVector<CDispGroup*> &combos, int idLMPage, IMaterial *pMaterial )
  69. {
  70. for( int i=0; i < combos.Size(); i++ )
  71. {
  72. if( combos[i]->m_LightmapPageID == idLMPage && combos[i]->m_pMaterial == pMaterial )
  73. return combos[i];
  74. }
  75. return NULL;
  76. }
  77. static CDispGroup* AddCombo( CUtlVector<CDispGroup*> &combos, int idLMPage, IMaterial *pMaterial )
  78. {
  79. CDispGroup *pCombo = new CDispGroup;
  80. pCombo->m_LightmapPageID = idLMPage;
  81. pCombo->m_pMaterial = pMaterial;
  82. pCombo->m_nVisible = 0;
  83. combos.AddToTail( pCombo );
  84. return pCombo;
  85. }
  86. static inline CDispInfo* GetModelDisp( model_t const *pWorld, int i )
  87. {
  88. return static_cast< CDispInfo* >(
  89. DispInfo_IndexArray( pWorld->brush.pShared->hDispInfos, i ) );
  90. }
  91. static void BuildDispSurfInit(
  92. model_t *pWorld,
  93. CCoreDispInfo *pBuildDisp,
  94. SurfaceHandle_t worldSurfID )
  95. {
  96. if( !IS_SURF_VALID( worldSurfID ) )
  97. return;
  98. ASSERT_SURF_VALID( worldSurfID );
  99. Vector surfPoints[4];
  100. Vector surfNormals[4];
  101. Vector2D surfTexCoords[4];
  102. Vector2D surfLightCoords[4][4];
  103. if ( MSurf_VertCount( worldSurfID ) != 4 )
  104. return;
  105. #ifndef SWDS
  106. BuildMSurfaceVerts( pWorld->brush.pShared, worldSurfID, surfPoints, surfTexCoords, surfLightCoords );
  107. #endif
  108. BuildDispGetSurfNormals( surfPoints, surfNormals );
  109. CCoreDispSurface *pDispSurf = pBuildDisp->GetSurface();
  110. int surfFlag = pDispSurf->GetFlags();
  111. int nLMVects = 1;
  112. if( MSurf_Flags( worldSurfID ) & SURFDRAW_BUMPLIGHT )
  113. {
  114. surfFlag |= CCoreDispInfo::SURF_BUMPED;
  115. nLMVects = NUM_BUMP_VECTS + 1;
  116. }
  117. pDispSurf->SetPointCount( 4 );
  118. for( int i = 0; i < 4; i++ )
  119. {
  120. pDispSurf->SetPoint( i, surfPoints[i] );
  121. pDispSurf->SetPointNormal( i, surfNormals[i] );
  122. pDispSurf->SetTexCoord( i, surfTexCoords[i] );
  123. for( int j = 0; j < nLMVects; j++ )
  124. {
  125. pDispSurf->SetLuxelCoord( j, i, surfLightCoords[i][j] );
  126. }
  127. }
  128. Vector vecS = MSurf_TexInfo( worldSurfID )->textureVecsTexelsPerWorldUnits[0].AsVector3D();
  129. Vector vecT = MSurf_TexInfo( worldSurfID )->textureVecsTexelsPerWorldUnits[1].AsVector3D();
  130. VectorNormalize( vecS );
  131. VectorNormalize( vecT );
  132. pDispSurf->SetSAxis( vecS );
  133. pDispSurf->SetTAxis( vecT );
  134. pDispSurf->SetFlags( surfFlag );
  135. pDispSurf->FindSurfPointStartIndex();
  136. pDispSurf->AdjustSurfPointData();
  137. #ifndef SWDS
  138. //
  139. // adjust the lightmap coordinates -- this is currently done redundantly!
  140. // the will be fixed correctly when the displacement common code is written.
  141. // This is here to get things running for (GDC, E3)
  142. //
  143. SurfaceCtx_t ctx;
  144. SurfSetupSurfaceContext( ctx, worldSurfID );
  145. int lightmapWidth = MSurf_LightmapExtents( worldSurfID )[0];
  146. int lightmapHeight = MSurf_LightmapExtents( worldSurfID )[1];
  147. Vector2D uv( 0.0f, 0.0f );
  148. for ( int ndxLuxel = 0; ndxLuxel < 4; ndxLuxel++ )
  149. {
  150. switch( ndxLuxel )
  151. {
  152. case 0: { uv.Init( 0.0f, 0.0f ); break; }
  153. case 1: { uv.Init( 0.0f, ( float )lightmapHeight ); break; }
  154. case 2: { uv.Init( ( float )lightmapWidth, ( float )lightmapHeight ); break; }
  155. case 3: { uv.Init( ( float )lightmapWidth, 0.0f ); break; }
  156. }
  157. uv.x += 0.5f;
  158. uv.y += 0.5f;
  159. uv *= ctx.m_Scale;
  160. uv += ctx.m_Offset;
  161. pDispSurf->SetLuxelCoord( 0, ndxLuxel, uv );
  162. }
  163. #endif
  164. }
  165. VertexFormat_t ComputeDisplacementStaticMeshVertexFormat( const IMaterial * pMaterial, const CDispGroup *pCombo, const ddispinfo_t *pMapDisps )
  166. {
  167. VertexFormat_t vertexFormat = pMaterial->GetVertexFormat();
  168. // FIXME: set VERTEX_FORMAT_COMPRESSED if there are no artifacts and if it saves enough memory (use 'mem_dumpvballocs')
  169. vertexFormat &= ~VERTEX_FORMAT_COMPRESSED;
  170. // FIXME: check for and strip unused vertex elements (TANGENT_S/T?)
  171. return vertexFormat;
  172. }
  173. void AddEmptyMesh(
  174. model_t *pWorld,
  175. CDispGroup *pCombo,
  176. const ddispinfo_t *pMapDisps,
  177. int *pDispInfos,
  178. int nDisps,
  179. int nTotalVerts,
  180. int nTotalIndices )
  181. {
  182. CMatRenderContextPtr pRenderContext( materials );
  183. CGroupMesh *pMesh = new CGroupMesh;
  184. pCombo->m_Meshes.AddToTail( pMesh );
  185. VertexFormat_t vertexFormat = ComputeDisplacementStaticMeshVertexFormat( pCombo->m_pMaterial, pCombo, pMapDisps );
  186. pMesh->m_pMesh = pRenderContext->CreateStaticMesh( vertexFormat, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_DISP );
  187. pMesh->m_pGroup = pCombo;
  188. pMesh->m_nVisible = 0;
  189. CMeshBuilder builder;
  190. builder.Begin( pMesh->m_pMesh, MATERIAL_TRIANGLES, nTotalVerts, nTotalIndices );
  191. // Just advance the verts and indices and leave the data blank for now.
  192. builder.AdvanceIndices( nTotalIndices );
  193. builder.AdvanceVertices( nTotalVerts );
  194. builder.End();
  195. pMesh->m_DispInfos.SetSize( nDisps );
  196. pMesh->m_Visible.SetSize( nDisps );
  197. pMesh->m_VisibleDisps.SetSize( nDisps );
  198. int iVertOffset = 0;
  199. int iIndexOffset = 0;
  200. for( int iDisp=0; iDisp < nDisps; iDisp++ )
  201. {
  202. CDispInfo *pDisp = GetModelDisp( pWorld, pDispInfos[iDisp] );
  203. const ddispinfo_t *pMapDisp = &pMapDisps[ pDispInfos[iDisp] ];
  204. pDisp->m_pMesh = pMesh;
  205. pDisp->m_iVertOffset = iVertOffset;
  206. pDisp->m_iIndexOffset = iIndexOffset;
  207. int nVerts, nIndices;
  208. CalcMaxNumVertsAndIndices( pMapDisp->power, &nVerts, &nIndices );
  209. iVertOffset += nVerts;
  210. iIndexOffset += nIndices;
  211. pMesh->m_DispInfos[iDisp] = pDisp;
  212. }
  213. Assert( iVertOffset == nTotalVerts );
  214. Assert( iIndexOffset == nTotalIndices );
  215. }
  216. void FillStaticBuffer(
  217. CGroupMesh *pMesh,
  218. CDispInfo *pDisp,
  219. const CCoreDispInfo *pCoreDisp,
  220. const CDispVert *pVerts,
  221. int nLightmaps )
  222. {
  223. #ifndef SWDS
  224. // Put the verts into the buffer.
  225. int nVerts, nIndices;
  226. CalcMaxNumVertsAndIndices( pDisp->GetPower(), &nVerts, &nIndices );
  227. CMeshBuilder builder;
  228. builder.BeginModify( pMesh->m_pMesh, pDisp->m_iVertOffset, nVerts, 0, 0 );
  229. SurfaceCtx_t ctx;
  230. SurfSetupSurfaceContext( ctx, pDisp->GetParent() );
  231. for( int i=0; i < nVerts; i++ )
  232. {
  233. // NOTE: position comes from our system-memory buffer so when you're restoring
  234. // static buffers (from alt+tab), it includes changes from terrain mods.
  235. const Vector &vPos = pCoreDisp->GetVert( i );
  236. builder.Position3f( vPos.x, vPos.y, vPos.z );
  237. const Vector &vNormal = pCoreDisp->GetNormal( i );
  238. builder.Normal3f( vNormal.x, vNormal.y, vNormal.z );
  239. Vector vec;
  240. pCoreDisp->GetTangentS( i, vec );
  241. builder.TangentS3f( VectorExpand( vec ) );
  242. pCoreDisp->GetTangentT( i, vec );
  243. builder.TangentT3f( VectorExpand( vec ) );
  244. Vector2D texCoord;
  245. pCoreDisp->GetTexCoord( i, texCoord );
  246. builder.TexCoord2f( 0, texCoord.x, texCoord.y );
  247. Vector2D lightCoord;
  248. {
  249. pCoreDisp->GetLuxelCoord( 0, i, lightCoord );
  250. builder.TexCoord2f( DISP_LMCOORDS_STAGE, lightCoord.x, lightCoord.y );
  251. }
  252. float flAlpha = ( ( CCoreDispInfo * )pCoreDisp )->GetAlpha( i );
  253. flAlpha *= ( 1.0f / 255.0f );
  254. flAlpha = clamp( flAlpha, 0.0f, 1.0f );
  255. builder.Color4f( 1.0f, 1.0f, 1.0f, flAlpha );
  256. if( nLightmaps > 1 )
  257. {
  258. SurfComputeLightmapCoordinate( ctx, pDisp->GetParent(), pDisp->m_Verts[i].m_vPos, lightCoord );
  259. builder.TexCoord2f( 2, ctx.m_BumpSTexCoordOffset, 0.0f );
  260. }
  261. builder.AdvanceVertex();
  262. }
  263. builder.EndModify();
  264. #endif
  265. }
  266. void CDispInfo::CopyMapDispData( const ddispinfo_t *pBuildDisp )
  267. {
  268. m_iLightmapAlphaStart = pBuildDisp->m_iLightmapAlphaStart;
  269. m_Power = pBuildDisp->power;
  270. Assert( m_Power >= 2 && m_Power <= NUM_POWERINFOS );
  271. m_pPowerInfo = ::GetPowerInfo( m_Power );
  272. // Max # of indices:
  273. // Take the number of triangles (2 * (size-1) * (size-1))
  274. // and multiply by 3!
  275. // These can be non-null in the case of task switch restore
  276. int size = GetSideLength();
  277. m_Indices.SetSize( 6 * (size-1) * (size-1) );
  278. // Per-node information
  279. if (m_pNodeInfo)
  280. delete[] m_pNodeInfo;
  281. m_pNodeInfo = new DispNodeInfo_t[m_pPowerInfo->m_NodeCount];
  282. }
  283. void DispInfo_CreateMaterialGroups( model_t *pWorld, const MaterialSystem_SortInfo_t *pSortInfos )
  284. {
  285. for ( int iDisp=0; iDisp < pWorld->brush.pShared->numDispInfos; iDisp++ )
  286. {
  287. CDispInfo *pDisp = GetModelDisp( pWorld, iDisp );
  288. int idLMPage = pSortInfos[MSurf_MaterialSortID( pDisp->m_ParentSurfID )].lightmapPageID;
  289. CDispGroup *pCombo = FindCombo( g_DispGroups, idLMPage, MSurf_TexInfo( pDisp->m_ParentSurfID )->material );
  290. if( !pCombo )
  291. pCombo = AddCombo( g_DispGroups, idLMPage, MSurf_TexInfo( pDisp->m_ParentSurfID )->material );
  292. MEM_ALLOC_CREDIT();
  293. pCombo->m_DispInfos.AddToTail( iDisp );
  294. }
  295. }
  296. void DispInfo_LinkToParentFaces( model_t *pWorld, const ddispinfo_t *pMapDisps, int nDisplacements )
  297. {
  298. for ( int iDisp=0; iDisp < nDisplacements; iDisp++ )
  299. {
  300. const ddispinfo_t *pMapDisp = &pMapDisps[iDisp];
  301. CDispInfo *pDisp = GetModelDisp( pWorld, iDisp );
  302. // Set its parent.
  303. SurfaceHandle_t surfID = SurfaceHandleFromIndex( pMapDisp->m_iMapFace );
  304. Assert( pMapDisp->m_iMapFace >= 0 && pMapDisp->m_iMapFace < pWorld->brush.pShared->numsurfaces );
  305. Assert( MSurf_Flags( surfID ) & SURFDRAW_HAS_DISP );
  306. surfID->pDispInfo = pDisp;
  307. pDisp->SetParent( surfID );
  308. }
  309. }
  310. void DispInfo_CreateEmptyStaticBuffers( model_t *pWorld, const ddispinfo_t *pMapDisps )
  311. {
  312. // For each combo, create empty buffers.
  313. for( int i=0; i < g_DispGroups.Size(); i++ )
  314. {
  315. CDispGroup *pCombo = g_DispGroups[i];
  316. int nTotalVerts=0, nTotalIndices=0;
  317. int iStart = 0;
  318. for( int iDisp=0; iDisp < pCombo->m_DispInfos.Size(); iDisp++ )
  319. {
  320. const ddispinfo_t *pMapDisp = &pMapDisps[pCombo->m_DispInfos[iDisp]];
  321. int nVerts, nIndices;
  322. CalcMaxNumVertsAndIndices( pMapDisp->power, &nVerts, &nIndices );
  323. // If we're going to pass our vertex buffer limit, or we're at the last one,
  324. // make a static buffer and fill it up.
  325. if( (nTotalVerts + nVerts) > MAX_STATIC_BUFFER_VERTS ||
  326. (nTotalIndices + nIndices) > MAX_STATIC_BUFFER_INDICES )
  327. {
  328. AddEmptyMesh( pWorld, pCombo, pMapDisps, &pCombo->m_DispInfos[iStart], iDisp-iStart, nTotalVerts, nTotalIndices );
  329. Assert( nTotalVerts > 0 && nTotalIndices > 0 );
  330. nTotalVerts = nTotalIndices = 0;
  331. iStart = iDisp;
  332. --iDisp;
  333. }
  334. else if( iDisp == pCombo->m_DispInfos.Size()-1 )
  335. {
  336. AddEmptyMesh( pWorld, pCombo, pMapDisps, &pCombo->m_DispInfos[iStart], iDisp-iStart+1, nTotalVerts+nVerts, nTotalIndices+nIndices );
  337. break;
  338. }
  339. else
  340. {
  341. nTotalVerts += nVerts;
  342. nTotalIndices += nIndices;
  343. }
  344. }
  345. }
  346. }
  347. //-----------------------------------------------------------------------------
  348. // Purpose:
  349. // Input : *pWorld -
  350. // iDisp -
  351. // *pMapDisp -
  352. // *pCoreDisp -
  353. // *pVerts -
  354. // pWorld -
  355. // iDisp -
  356. // Output : Returns true on success, false on failure.
  357. // Information: Setup the CCoreDispInfo using the ddispinfo_t and have it translate the data
  358. // into a format we'll copy into the rendering structures. This roundaboutness is because
  359. // of legacy code. It should all just be stored in the map file, but it's not a high priority right now.
  360. //-----------------------------------------------------------------------------
  361. bool DispInfo_CreateFromMapDisp( model_t *pWorld, int iDisp, const ddispinfo_t *pMapDisp, CCoreDispInfo *pCoreDisp, const CDispVert *pVerts,
  362. const CDispTri *pTris,const MaterialSystem_SortInfo_t *pSortInfos, bool bRestoring )
  363. {
  364. // Get the matching CDispInfo to fill in.
  365. CDispInfo *pDisp = GetModelDisp( pWorld, iDisp );
  366. // Initialize the core disp info with data from the map displacement.
  367. pCoreDisp->GetSurface()->SetPointStart( pMapDisp->startPosition );
  368. pCoreDisp->InitDispInfo( pMapDisp->power, pMapDisp->minTess, pMapDisp->smoothingAngle, pVerts, pTris );
  369. pCoreDisp->SetNeighborData( pMapDisp->m_EdgeNeighbors, pMapDisp->m_CornerNeighbors );
  370. // Copy the allowed verts list.
  371. ErrorIfNot( pCoreDisp->GetAllowedVerts().GetNumDWords() == sizeof( pMapDisp->m_AllowedVerts ) / 4, ( "DispInfo_StoreMapData: size mismatch in 'allowed verts' list" ) );
  372. for ( int iVert = 0; iVert < pCoreDisp->GetAllowedVerts().GetNumDWords(); ++iVert )
  373. {
  374. pCoreDisp->GetAllowedVerts().SetDWord( iVert, pMapDisp->m_AllowedVerts[iVert] );
  375. }
  376. // Build the reset of the intermediate data from the initial map displacement data.
  377. BuildDispSurfInit( pWorld, pCoreDisp, pDisp->GetParent() );
  378. if ( !pCoreDisp->Create() )
  379. return false;
  380. // Save the point start index - needed for overlays.
  381. pDisp->m_iPointStart = pCoreDisp->GetSurface()->GetPointStartIndex();
  382. // Now setup the CDispInfo.
  383. pDisp->m_Index = static_cast<unsigned short>( iDisp );
  384. // Store ddispinfo_t data.
  385. pDisp->CopyMapDispData( pMapDisp );
  386. // Store CCoreDispInfo data.
  387. if( !pDisp->CopyCoreDispData( pWorld, pSortInfos, pCoreDisp, bRestoring ) )
  388. return false;
  389. // Initialize all the active and other verts after setting up neighbors.
  390. pDisp->InitializeActiveVerts();
  391. pDisp->m_iLightmapSamplePositionStart = pMapDisp->m_iLightmapSamplePositionStart;
  392. return true;
  393. }
  394. //-----------------------------------------------------------------------------
  395. // Purpose:
  396. // Input : *pWorld -
  397. // iDisp -
  398. // *pCoreDisp -
  399. // *pVerts -
  400. // Output : Returns true on success, false on failure.
  401. //-----------------------------------------------------------------------------
  402. void DispInfo_CreateStaticBuffersAndTags( model_t *pWorld, int iDisp, CCoreDispInfo *pCoreDisp, const CDispVert *pVerts )
  403. {
  404. // Get the matching CDispInfo to fill in.
  405. CDispInfo *pDisp = GetModelDisp( pWorld, iDisp );
  406. // Now copy the CCoreDisp's data into the static buffer.
  407. FillStaticBuffer( pDisp->m_pMesh, pDisp, pCoreDisp, pVerts, pDisp->NumLightMaps() );
  408. // Now build the tagged data for visualization.
  409. BuildTagData( pCoreDisp, pDisp );
  410. }
  411. // On the xbox, we lock the meshes ahead of time.
  412. void SetupMeshReaders( model_t *pWorld, int nDisplacements )
  413. {
  414. for ( int iDisp=0; iDisp < nDisplacements; iDisp++ )
  415. {
  416. CDispInfo *pDisp = GetModelDisp( pWorld, iDisp );
  417. MeshDesc_t desc;
  418. memset( &desc, 0, sizeof( desc ) );
  419. desc.m_VertexSize_Position = sizeof( CDispRenderVert );
  420. desc.m_VertexSize_TexCoord[0] = sizeof( CDispRenderVert );
  421. desc.m_VertexSize_TexCoord[DISP_LMCOORDS_STAGE] = sizeof( CDispRenderVert );
  422. desc.m_VertexSize_Normal = sizeof( CDispRenderVert );
  423. desc.m_VertexSize_TangentS = sizeof( CDispRenderVert );
  424. desc.m_VertexSize_TangentT = sizeof( CDispRenderVert );
  425. CDispRenderVert *pBaseVert = pDisp->m_Verts.Base();
  426. desc.m_pPosition = (float*)&pBaseVert->m_vPos;
  427. desc.m_pTexCoord[0] = (float*)&pBaseVert->m_vTexCoord;
  428. desc.m_pTexCoord[DISP_LMCOORDS_STAGE] = (float*)&pBaseVert->m_LMCoords;
  429. desc.m_pNormal = (float*)&pBaseVert->m_vNormal;
  430. desc.m_pTangentS = (float*)&pBaseVert->m_vSVector;
  431. desc.m_pTangentT = (float*)&pBaseVert->m_vTVector;
  432. desc.m_nIndexSize = 1;
  433. desc.m_pIndices = pDisp->m_Indices.Base();
  434. pDisp->m_MeshReader.BeginRead_Direct( desc, pDisp->NumVerts(), pDisp->m_nIndices );
  435. }
  436. }
  437. void UpdateDispBBoxes( model_t *pWorld, int nDisplacements )
  438. {
  439. for ( int iDisp=0; iDisp < nDisplacements; iDisp++ )
  440. {
  441. CDispInfo *pDisp = GetModelDisp( pWorld, iDisp );
  442. pDisp->UpdateBoundingBox();
  443. }
  444. }
  445. #include "tier0/memdbgoff.h"
  446. bool DispInfo_LoadDisplacements( model_t *pWorld, bool bRestoring )
  447. {
  448. const MaterialSystem_SortInfo_t *pSortInfos = materialSortInfoArray;
  449. int nDisplacements = CMapLoadHelper::LumpSize( LUMP_DISPINFO ) / sizeof( ddispinfo_t );
  450. int nLuxels = CMapLoadHelper::LumpSize( LUMP_DISP_LIGHTMAP_ALPHAS );
  451. int nSamplePositionBytes = CMapLoadHelper::LumpSize( LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS );
  452. // Setup the world's list of displacements.
  453. if ( bRestoring )
  454. {
  455. /* Breakpoint-able: */
  456. if (pWorld->brush.pShared->numDispInfos != nDisplacements)
  457. {
  458. volatile int a = 0; a = a + 1;
  459. }
  460. if ( !pWorld->brush.pShared->numDispInfos && nDisplacements )
  461. {
  462. // Attempting to restore displacements before displacements got loaded
  463. return false;
  464. }
  465. ErrorIfNot(
  466. pWorld->brush.pShared->numDispInfos == nDisplacements,
  467. ("DispInfo_LoadDisplacments: dispcounts (%d and %d) don't match.", pWorld->brush.pShared->numDispInfos, nDisplacements)
  468. );
  469. ErrorIfNot(
  470. g_DispLMAlpha.Count() == nLuxels,
  471. ("DispInfo_LoadDisplacements: lightmap alpha counts (%d and %d) don't match.", g_DispLMAlpha.Count(), nLuxels)
  472. );
  473. }
  474. else
  475. {
  476. // Create the displacements.
  477. pWorld->brush.pShared->numDispInfos = nDisplacements;
  478. pWorld->brush.pShared->hDispInfos = DispInfo_CreateArray( pWorld->brush.pShared->numDispInfos );
  479. // Load lightmap alphas.
  480. {
  481. MEM_ALLOC_CREDIT();
  482. g_DispLMAlpha.SetSize( nLuxels );
  483. }
  484. CMapLoadHelper lhDispLMAlphas( LUMP_DISP_LIGHTMAP_ALPHAS );
  485. lhDispLMAlphas.LoadLumpData( 0, nLuxels, g_DispLMAlpha.Base() );
  486. // Load lightmap sample positions.
  487. {
  488. MEM_ALLOC_CREDIT();
  489. g_DispLightmapSamplePositions.SetSize( nSamplePositionBytes );
  490. }
  491. CMapLoadHelper lhDispLMPositions( LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS );
  492. lhDispLMPositions.LoadLumpData( 0, nSamplePositionBytes, g_DispLightmapSamplePositions.Base() );
  493. }
  494. // Free old data.
  495. DispInfo_ReleaseMaterialSystemObjects( pWorld );
  496. // load the displacement info structures into temporary space
  497. // using temporary storage that is not the stack for compatibility with console stack
  498. #ifndef _X360
  499. ddispinfo_t tempDisps[MAX_MAP_DISPINFO];
  500. #else
  501. CUtlMemory< ddispinfo_t > m_DispInfoBuf( 0, MAX_MAP_DISPINFO );
  502. ddispinfo_t *tempDisps = m_DispInfoBuf.Base();
  503. #endif
  504. ErrorIfNot(
  505. nDisplacements <= MAX_MAP_DISPINFO,
  506. ("DispInfo_LoadDisplacements: nDisplacements (%d) > MAX_MAP_DISPINFO (%d)", nDisplacements, MAX_MAP_DISPINFO)
  507. );
  508. CMapLoadHelper lhDispInfo( LUMP_DISPINFO );
  509. lhDispInfo.LoadLumpData( 0, nDisplacements * sizeof( ddispinfo_t ), tempDisps );
  510. // Now hook up the displacements to their parents.
  511. DispInfo_LinkToParentFaces( pWorld, tempDisps, nDisplacements );
  512. // First, create "groups" (or "combos") which contain all the displacements that
  513. // use the same material and lightmap.
  514. DispInfo_CreateMaterialGroups( pWorld, pSortInfos );
  515. // Now make the static buffers for each material/lightmap combo.
  516. if ( g_VBAllocTracker )
  517. g_VBAllocTracker->TrackMeshAllocations( "DispInfo_LoadDisplacements" );
  518. DispInfo_CreateEmptyStaticBuffers( pWorld, tempDisps );
  519. if ( g_VBAllocTracker )
  520. g_VBAllocTracker->TrackMeshAllocations( NULL );
  521. // Now setup each displacement one at a time.
  522. // using temporary storage that is not the stack for compatibility with console stack
  523. #ifndef _X360
  524. CDispVert tempVerts[MAX_DISPVERTS];
  525. #else
  526. CUtlMemory< CDispVert > m_DispVertsBuf( 0, MAX_DISPVERTS );
  527. CDispVert *tempVerts = m_DispVertsBuf.Base();
  528. #endif
  529. #ifndef _X360
  530. CDispTri tempTris[MAX_DISPTRIS];
  531. #else
  532. // using temporary storage that is not the stack for compatibility with console stack
  533. CUtlMemory< CDispTri > m_DispTrisBuf( 0, MAX_DISPTRIS );
  534. CDispTri *tempTris = m_DispTrisBuf.Base();
  535. #endif
  536. int iCurVert = 0;
  537. int iCurTri = 0;
  538. // Core displacement list.
  539. CUtlVector<CCoreDispInfo*> aCoreDisps;
  540. int iDisp = 0;
  541. for ( iDisp = 0; iDisp < nDisplacements; ++iDisp )
  542. {
  543. CCoreDispInfo *pCoreDisp = new CCoreDispInfo;
  544. aCoreDisps.AddToTail( pCoreDisp );
  545. }
  546. CMapLoadHelper lhDispVerts( LUMP_DISP_VERTS );
  547. CMapLoadHelper lhDispTris( LUMP_DISP_TRIS );
  548. for ( iDisp = 0; iDisp < nDisplacements; ++iDisp )
  549. {
  550. // Get the current map displacement.
  551. ddispinfo_t *pMapDisp = &tempDisps[iDisp];
  552. if ( !pMapDisp )
  553. continue;
  554. // Load the vertices from the file.
  555. int nVerts = NUM_DISP_POWER_VERTS( pMapDisp->power );
  556. ErrorIfNot( nVerts <= MAX_DISPVERTS, ( "DispInfo_LoadDisplacements: invalid vertex count (%d)", nVerts ) );
  557. lhDispVerts.LoadLumpData( iCurVert * sizeof(CDispVert), nVerts*sizeof(CDispVert), tempVerts );
  558. iCurVert += nVerts;
  559. // Load the triangle indices from the file.
  560. int nTris = NUM_DISP_POWER_TRIS( pMapDisp->power );
  561. ErrorIfNot( nTris <= MAX_DISPTRIS, ( "DispInfo_LoadDisplacements: invalid tri count (%d)", nTris ) );
  562. lhDispTris.LoadLumpData( iCurTri * sizeof(CDispTri), nTris*sizeof(CDispTri), tempTris );
  563. iCurTri += nTris;
  564. // Now create the CoreDispInfo and the base CDispInfo.
  565. if ( !DispInfo_CreateFromMapDisp( pWorld, iDisp, pMapDisp, aCoreDisps[iDisp], tempVerts, tempTris, pSortInfos, bRestoring ) )
  566. return false;
  567. }
  568. // Smooth Normals.
  569. SmoothDispSurfNormals( aCoreDisps.Base(), nDisplacements );
  570. // Fill in the static buffers.
  571. for ( iDisp = 0; iDisp < nDisplacements; ++iDisp )
  572. {
  573. DispInfo_CreateStaticBuffersAndTags( pWorld, iDisp, aCoreDisps[iDisp], tempVerts );
  574. // Copy over the now blended normals
  575. CDispInfo *pDisp = GetModelDisp( pWorld, iDisp );
  576. pDisp->CopyCoreDispVertData( aCoreDisps[iDisp], pDisp->m_BumpSTexCoordOffset );
  577. }
  578. // Destroy core displacement list.
  579. aCoreDisps.PurgeAndDeleteElements();
  580. // If we're not using LOD, then maximally tesselate all the displacements and
  581. // make sure they never change.
  582. for ( iDisp=0; iDisp < nDisplacements; iDisp++ )
  583. {
  584. CDispInfo *pDisp = GetModelDisp( pWorld, iDisp );
  585. pDisp->m_ActiveVerts = pDisp->m_AllowedVerts;
  586. }
  587. for ( iDisp=0; iDisp < nDisplacements; iDisp++ )
  588. {
  589. CDispInfo *pDisp = GetModelDisp( pWorld, iDisp );
  590. pDisp->TesselateDisplacement();
  591. }
  592. SetupMeshReaders( pWorld, nDisplacements );
  593. UpdateDispBBoxes( pWorld, nDisplacements );
  594. return true;
  595. }
  596. #include "tier0/memdbgon.h"
  597. void DispInfo_ReleaseMaterialSystemObjects( model_t *pWorld )
  598. {
  599. CMatRenderContextPtr pRenderContext( materials );
  600. // Free all the static meshes.
  601. for( int iGroup=0; iGroup < g_DispGroups.Size(); iGroup++ )
  602. {
  603. CDispGroup *pGroup = g_DispGroups[iGroup];
  604. for( int iMesh=0; iMesh < pGroup->m_Meshes.Size(); iMesh++ )
  605. {
  606. CGroupMesh *pMesh = pGroup->m_Meshes[iMesh];
  607. pRenderContext->DestroyStaticMesh( pMesh->m_pMesh );
  608. }
  609. pGroup->m_Meshes.PurgeAndDeleteElements();
  610. }
  611. g_DispGroups.PurgeAndDeleteElements();
  612. // Clear pointers in the dispinfos.
  613. if( pWorld )
  614. {
  615. for( int iDisp=0; iDisp < pWorld->brush.pShared->numDispInfos; iDisp++ )
  616. {
  617. CDispInfo *pDisp = GetModelDisp( pWorld, iDisp );
  618. if ( !pDisp )
  619. {
  620. AssertOnce( 0 );
  621. continue;
  622. }
  623. pDisp->m_pMesh = NULL;
  624. pDisp->m_iVertOffset = pDisp->m_iIndexOffset = 0;
  625. }
  626. }
  627. }
  628. //-----------------------------------------------------------------------------
  629. // CDispInfo implementation.
  630. //-----------------------------------------------------------------------------
  631. CDispInfo::CDispInfo()
  632. {
  633. m_ParentSurfID = SURFACE_HANDLE_INVALID;
  634. m_bTouched = false;
  635. ++g_ConstructorChecker.m_nConstructedObjects;
  636. m_BBoxMin.Init();
  637. m_BBoxMax.Init();
  638. m_idLMPage = -1;
  639. m_pPowerInfo = NULL;
  640. m_ViewerSphereCenter.Init( 1e24, 1e24, 1e24 );
  641. m_bInUse = false;
  642. m_pNodeInfo = 0;
  643. m_pMesh = NULL;
  644. m_Tag = NULL;
  645. m_pDispArray = NULL;
  646. m_FirstDecal = DISP_DECAL_HANDLE_INVALID;
  647. m_FirstShadowDecal = DISP_SHADOW_HANDLE_INVALID;
  648. for ( int i=0; i < 4; i++ )
  649. {
  650. m_EdgeNeighbors[i].SetInvalid();
  651. m_CornerNeighbors[i].SetInvalid();
  652. }
  653. }
  654. CDispInfo::~CDispInfo()
  655. {
  656. if (m_pNodeInfo)
  657. delete[] m_pNodeInfo;
  658. delete[] m_pWalkIndices;
  659. delete[] m_pBuildIndices;
  660. --g_ConstructorChecker.m_nConstructedObjects;
  661. // All the decals should have been freed through
  662. // CModelLoader::Map_UnloadModel -> R_DecalTerm
  663. Assert( m_FirstDecal == DISP_DECAL_HANDLE_INVALID );
  664. Assert( m_FirstShadowDecal == DISP_SHADOW_HANDLE_INVALID );
  665. }
  666. void CDispInfo::CopyCoreDispVertData( const CCoreDispInfo *pCoreDisp, float bumpSTexCoordOffset )
  667. {
  668. #ifndef SWDS
  669. if( NumLightMaps() <= 1 )
  670. {
  671. bumpSTexCoordOffset = 0.0f;
  672. }
  673. // Copy vertex positions (for backfacing tests).
  674. m_Verts.SetSize( m_pPowerInfo->m_MaxVerts );
  675. m_BumpSTexCoordOffset = bumpSTexCoordOffset;
  676. for( int i=0; i < NumVerts(); i++ )
  677. {
  678. pCoreDisp->GetVert( i, m_Verts[i].m_vPos );
  679. pCoreDisp->GetTexCoord( i, m_Verts[i].m_vTexCoord );
  680. pCoreDisp->GetLuxelCoord( 0, i, m_Verts[i].m_LMCoords );
  681. // mat_normals needs this as well as the dynamic lighting code
  682. pCoreDisp->GetNormal( i, m_Verts[i].m_vNormal );
  683. pCoreDisp->GetTangentS( i, m_Verts[i].m_vSVector );
  684. pCoreDisp->GetTangentT( i, m_Verts[i].m_vTVector );
  685. }
  686. #endif
  687. }
  688. bool CDispInfo::CopyCoreDispData(
  689. model_t *pWorld,
  690. const MaterialSystem_SortInfo_t *pSortInfos,
  691. const CCoreDispInfo *pCoreDisp,
  692. bool bRestoring )
  693. {
  694. m_idLMPage = pSortInfos[MSurf_MaterialSortID( GetParent() )].lightmapPageID;
  695. #ifndef SWDS
  696. SurfaceCtx_t ctx;
  697. SurfSetupSurfaceContext( ctx, GetParent() );
  698. #endif
  699. // Restoring is only for alt+tabbing, which can't happen on consoles
  700. if ( IsPC() && bRestoring )
  701. {
  702. #ifndef SWDS
  703. // When restoring, have to recompute lightmap coords
  704. if( NumLightMaps() > 1 )
  705. {
  706. m_BumpSTexCoordOffset = ctx.m_BumpSTexCoordOffset;
  707. }
  708. else
  709. {
  710. m_BumpSTexCoordOffset = 0.0f;
  711. }
  712. for( int i=0; i < NumVerts(); i++ )
  713. {
  714. pCoreDisp->GetLuxelCoord( 0, i, m_Verts[i].m_LMCoords );
  715. }
  716. #endif // SWDS
  717. return true;
  718. }
  719. // When restoring, leave all this data the same.
  720. const CCoreDispSurface *pSurface = pCoreDisp->GetSurface();
  721. for( int index=0; index < 4; index++ )
  722. {
  723. pSurface->GetTexCoord( index, m_BaseSurfaceTexCoords[index] );
  724. m_BaseSurfacePositions[index] = pSurface->GetPoint( index );
  725. }
  726. #ifndef SWDS
  727. CopyCoreDispVertData( pCoreDisp, ctx.m_BumpSTexCoordOffset );
  728. #endif
  729. // Copy neighbor info.
  730. for ( int iEdge=0; iEdge < 4; iEdge++ )
  731. {
  732. m_EdgeNeighbors[iEdge] = *pCoreDisp->GetEdgeNeighbor( iEdge );
  733. m_CornerNeighbors[iEdge] = *pCoreDisp->GetCornerNeighbors( iEdge );
  734. }
  735. // Copy allowed verts.
  736. m_AllowedVerts = pCoreDisp->GetAllowedVerts();
  737. m_nIndices = 0;
  738. return true;
  739. }
  740. int CDispInfo::NumLightMaps()
  741. {
  742. return (MSurf_Flags( m_ParentSurfID ) & SURFDRAW_BUMPLIGHT) ? NUM_BUMP_VECTS+1 : 1;
  743. }
  744. //-----------------------------------------------------------------------------
  745. // Purpose:
  746. // NOTE: You cannot use the builddisp.cpp IsTriWalkable, IsTriBuildable functions
  747. // because the flags are different having been collapsed in vbsp
  748. //-----------------------------------------------------------------------------
  749. void BuildTagData( CCoreDispInfo *pCoreDisp, CDispInfo *pDisp )
  750. {
  751. int nWalkTest = 0;
  752. int nBuildTest = 0;
  753. int iTri;
  754. for ( iTri = 0; iTri < pCoreDisp->GetTriCount(); ++iTri )
  755. {
  756. if ( pCoreDisp->IsTriTag( iTri, DISPTRI_TAG_WALKABLE ) )
  757. {
  758. nWalkTest++;
  759. }
  760. if ( pCoreDisp->IsTriTag( iTri, DISPTRI_TAG_BUILDABLE ) )
  761. {
  762. nBuildTest++;
  763. }
  764. }
  765. nWalkTest *= 3;
  766. nBuildTest *= 3;
  767. pDisp->m_pWalkIndices = new unsigned short[nWalkTest];
  768. pDisp->m_pBuildIndices = new unsigned short[nBuildTest];
  769. int nWalkCount = 0;
  770. int nBuildCount = 0;
  771. for ( iTri = 0; iTri < pCoreDisp->GetTriCount(); ++iTri )
  772. {
  773. if ( pCoreDisp->IsTriTag( iTri, DISPTRI_TAG_WALKABLE ) )
  774. {
  775. pCoreDisp->GetTriIndices( iTri,
  776. pDisp->m_pWalkIndices[nWalkCount],
  777. pDisp->m_pWalkIndices[nWalkCount+1],
  778. pDisp->m_pWalkIndices[nWalkCount+2] );
  779. nWalkCount += 3;
  780. }
  781. if ( pCoreDisp->IsTriTag( iTri, DISPTRI_TAG_BUILDABLE ) )
  782. {
  783. pCoreDisp->GetTriIndices( iTri,
  784. pDisp->m_pBuildIndices[nBuildCount],
  785. pDisp->m_pBuildIndices[nBuildCount+1],
  786. pDisp->m_pBuildIndices[nBuildCount+2] );
  787. nBuildCount += 3;
  788. }
  789. }
  790. Assert( nWalkCount == nWalkTest );
  791. Assert( nBuildCount == nBuildTest );
  792. pDisp->m_nWalkIndexCount = nWalkCount;
  793. pDisp->m_nBuildIndexCount = nBuildCount;
  794. }
  795. //-----------------------------------------------------------------------------
  796. // Purpose:
  797. // Input : *pDisp -
  798. // &vecPoint -
  799. // Output : int
  800. //-----------------------------------------------------------------------------
  801. int FindNeighborCornerVert( CCoreDispInfo *pDisp, const Vector &vecPoint )
  802. {
  803. CDispUtilsHelper *pDispHelper = pDisp;
  804. int iClosest = 0;
  805. float flClosest = 1e24;
  806. for ( int iCorner = 0; iCorner < 4; ++iCorner )
  807. {
  808. // Has it been touched?
  809. CVertIndex viCornerVert = pDispHelper->GetPowerInfo()->GetCornerPointIndex( iCorner );
  810. int iCornerVert = pDispHelper->VertIndexToInt( viCornerVert );
  811. const Vector &vecCornerVert = pDisp->GetVert( iCornerVert );
  812. float flDist = vecCornerVert.DistTo( vecPoint );
  813. if ( flDist < flClosest )
  814. {
  815. iClosest = iCorner;
  816. flClosest = flDist;
  817. }
  818. }
  819. if ( flClosest <= 0.1f )
  820. return iClosest;
  821. else
  822. return -1;
  823. }
  824. // sets a new normal/tangentS, recomputes tangent T
  825. void UpdateTangentSpace(CCoreDispInfo *pDisp, int iVert, const Vector &vNormal, const Vector &vTanS)
  826. {
  827. Vector tanT;
  828. pDisp->SetNormal( iVert, vNormal );
  829. CrossProduct( vTanS, vNormal, tanT );
  830. pDisp->SetTangentS(iVert, vTanS);
  831. pDisp->SetTangentT(iVert, tanT);
  832. }
  833. void UpdateTangentSpace(CCoreDispInfo *pDisp, const CVertIndex &index, const Vector &vNormal, const Vector &vTanS)
  834. {
  835. UpdateTangentSpace(pDisp, pDisp->VertIndexToInt(index), vNormal, vTanS);
  836. }
  837. //-----------------------------------------------------------------------------
  838. // Purpose:
  839. // Input : **ppListBase -
  840. // nListSize -
  841. //-----------------------------------------------------------------------------
  842. void BlendSubNeighbors( CCoreDispInfo **ppListBase, int nListSize )
  843. {
  844. // Loop through all of the displacements in the list.
  845. for ( int iDisp = 0; iDisp < nListSize; ++iDisp )
  846. {
  847. // Get the current displacement.
  848. CCoreDispInfo *pDisp = ppListBase[iDisp];
  849. if ( !pDisp )
  850. continue;
  851. // Loop through all the edges of the displacement.
  852. for ( int iEdge = 0; iEdge < 4; ++iEdge )
  853. {
  854. // Find valid neighbors along the edge.
  855. CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge );
  856. if ( !pEdge )
  857. continue;
  858. // Check to see if we have sub-neighbors - defines a t-junction in this world. If not,
  859. // then the normal blend edges function will catch it all.
  860. if ( !pEdge->m_SubNeighbors[0].IsValid() || !pEdge->m_SubNeighbors[1].IsValid() )
  861. continue;
  862. // Get the mid-point of the current displacement.
  863. CVertIndex viMidPoint = pDisp->GetEdgeMidPoint( iEdge );
  864. int iMidPoint = pDisp->VertIndexToInt( viMidPoint );
  865. const Vector &vecMidPoint = pDisp->GetVert( iMidPoint );
  866. // Get the current sub-neighbors along the edge.
  867. CCoreDispInfo *pNeighbor1 = ppListBase[pEdge->m_SubNeighbors[0].GetNeighborIndex()];
  868. CCoreDispInfo *pNeighbor2 = ppListBase[pEdge->m_SubNeighbors[1].GetNeighborIndex()];
  869. // Get the current sub-neighbor corners.
  870. int iCorners[2];
  871. iCorners[0] = FindNeighborCornerVert( pNeighbor1, vecMidPoint );
  872. iCorners[1] = FindNeighborCornerVert( pNeighbor2, vecMidPoint );
  873. if ( iCorners[0] != -1 && iCorners[1] != -1 )
  874. {
  875. CVertIndex viCorners[2] = { pNeighbor1->GetCornerPointIndex( iCorners[0] ),pNeighbor2->GetCornerPointIndex( iCorners[1] ) };
  876. // Accumulate the normals at the mid-point of the primary edge and corners of the sub-neighbors.
  877. Vector vecAverage = pDisp->GetNormal( iMidPoint );
  878. vecAverage += pNeighbor1->GetNormal( viCorners[0] );
  879. vecAverage += pNeighbor2->GetNormal( viCorners[1] );
  880. // Re-normalize.
  881. VectorNormalize( vecAverage );
  882. Vector vAvgTanS = pDisp->GetTangentS(iMidPoint);
  883. vAvgTanS += pNeighbor1->GetTangentS(viCorners[0]);
  884. vAvgTanS += pNeighbor2->GetTangentS(viCorners[1]);
  885. VectorNormalize(vAvgTanS);
  886. //vecAverage.Init( 0.0f, 0.0f, 1.0f );
  887. // Set the new normal value back.
  888. UpdateTangentSpace( pDisp, iMidPoint, vecAverage, vAvgTanS );
  889. UpdateTangentSpace( pNeighbor1, viCorners[0], vecAverage, vAvgTanS );
  890. UpdateTangentSpace( pNeighbor2, viCorners[1], vecAverage, vAvgTanS );
  891. }
  892. }
  893. }
  894. }
  895. //-----------------------------------------------------------------------------
  896. // Purpose:
  897. // Input : *pDisp -
  898. // iNeighbors[512] -
  899. // Output : int
  900. //-----------------------------------------------------------------------------
  901. int GetAllNeighbors( const CCoreDispInfo *pDisp, int iNeighbors[512] )
  902. {
  903. int nNeighbors = 0;
  904. // Check corner neighbors.
  905. for ( int iCorner=0; iCorner < 4; iCorner++ )
  906. {
  907. const CDispCornerNeighbors *pCorner = pDisp->GetCornerNeighbors( iCorner );
  908. for ( int i=0; i < pCorner->m_nNeighbors; i++ )
  909. {
  910. if ( nNeighbors < 512 )
  911. iNeighbors[nNeighbors++] = pCorner->m_Neighbors[i];
  912. }
  913. }
  914. for ( int iEdge=0; iEdge < 4; iEdge++ )
  915. {
  916. const CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge );
  917. for ( int i=0; i < 2; i++ )
  918. {
  919. if ( pEdge->m_SubNeighbors[i].IsValid() )
  920. if ( nNeighbors < 512 )
  921. iNeighbors[nNeighbors++] = pEdge->m_SubNeighbors[i].GetNeighborIndex();
  922. }
  923. }
  924. return nNeighbors;
  925. }
  926. //-----------------------------------------------------------------------------
  927. // Purpose:
  928. // Input : **ppListBase -
  929. // listSize -
  930. //-----------------------------------------------------------------------------
  931. void BlendCorners( CCoreDispInfo **ppListBase, int nListSize )
  932. {
  933. CUtlVector<int> nbCornerVerts;
  934. for ( int iDisp = 0; iDisp < nListSize; ++iDisp )
  935. {
  936. CCoreDispInfo *pDisp = ppListBase[iDisp];
  937. int iNeighbors[512];
  938. int nNeighbors = GetAllNeighbors( pDisp, iNeighbors );
  939. // Make sure we have room for all the neighbors.
  940. nbCornerVerts.RemoveAll();
  941. nbCornerVerts.EnsureCapacity( nNeighbors );
  942. nbCornerVerts.AddMultipleToTail( nNeighbors );
  943. // For each corner.
  944. for ( int iCorner=0; iCorner < 4; iCorner++ )
  945. {
  946. // Has it been touched?
  947. CVertIndex cornerVert = pDisp->GetCornerPointIndex( iCorner );
  948. int iCornerVert = pDisp->VertIndexToInt( cornerVert );
  949. const Vector &vCornerVert = pDisp->GetVert( iCornerVert );
  950. // For each displacement sharing this corner..
  951. Vector vAverage = pDisp->GetNormal( iCornerVert );
  952. Vector vAvgTanS;
  953. pDisp->GetTangentS( iCornerVert, vAvgTanS );
  954. for ( int iNeighbor=0; iNeighbor < nNeighbors; iNeighbor++ )
  955. {
  956. int iNBListIndex = iNeighbors[iNeighbor];
  957. CCoreDispInfo *pNeighbor = ppListBase[iNBListIndex];
  958. // Find out which vert it is on the neighbor.
  959. int iNBCorner = FindNeighborCornerVert( pNeighbor, vCornerVert );
  960. if ( iNBCorner == -1 )
  961. {
  962. nbCornerVerts[iNeighbor] = -1; // remove this neighbor from the list.
  963. }
  964. else
  965. {
  966. CVertIndex viNBCornerVert = pNeighbor->GetCornerPointIndex( iNBCorner );
  967. int iNBVert = pNeighbor->VertIndexToInt( viNBCornerVert );
  968. nbCornerVerts[iNeighbor] = iNBVert;
  969. vAverage += pNeighbor->GetNormal( iNBVert );
  970. vAvgTanS += pNeighbor->GetTangentS( iNBVert );
  971. }
  972. }
  973. // Blend all the neighbor normals with this one.
  974. VectorNormalize( vAverage );
  975. VectorNormalize( vAvgTanS );
  976. UpdateTangentSpace(pDisp, iCornerVert, vAverage, vAvgTanS );
  977. for ( int iNeighbor=0; iNeighbor < nNeighbors; iNeighbor++ )
  978. {
  979. int iNBListIndex = iNeighbors[iNeighbor];
  980. if ( nbCornerVerts[iNeighbor] == -1 )
  981. continue;
  982. CCoreDispInfo *pNeighbor = ppListBase[iNBListIndex];
  983. UpdateTangentSpace(pNeighbor, nbCornerVerts[iNeighbor], vAverage, vAvgTanS);
  984. }
  985. }
  986. }
  987. }
  988. //-----------------------------------------------------------------------------
  989. // Purpose:
  990. // Input : **ppListBase -
  991. // listSize -
  992. //-----------------------------------------------------------------------------
  993. void BlendEdges( CCoreDispInfo **ppListBase, int nListSize )
  994. {
  995. // Loop through all the displacements in the list.
  996. for ( int iDisp = 0; iDisp < nListSize; ++iDisp )
  997. {
  998. // Get the current displacement.
  999. CCoreDispInfo *pDisp = ppListBase[iDisp];
  1000. if ( !pDisp )
  1001. continue;
  1002. // Loop through all of the edges on a displacement.
  1003. for ( int iEdge = 0; iEdge < 4; ++iEdge )
  1004. {
  1005. // Get the current displacement edge.
  1006. CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge );
  1007. if ( !pEdge )
  1008. continue;
  1009. // Check for sub-edges.
  1010. for ( int iSubEdge = 0; iSubEdge < 2; ++iSubEdge )
  1011. {
  1012. // Get the current sub-edge.
  1013. CDispSubNeighbor *pSubEdge = &pEdge->m_SubNeighbors[iSubEdge];
  1014. if ( !pSubEdge->IsValid() )
  1015. continue;
  1016. // Get the current neighbor.
  1017. CCoreDispInfo *pNeighbor = ppListBase[pSubEdge->GetNeighborIndex()];
  1018. if ( !pNeighbor )
  1019. continue;
  1020. // Get the edge dimension.
  1021. int iEdgeDim = g_EdgeDims[iEdge];
  1022. CDispSubEdgeIterator it;
  1023. it.Start( pDisp, iEdge, iSubEdge, true );
  1024. // Get setup on the first corner vert.
  1025. it.Next();
  1026. CVertIndex viPrevPos = it.GetVertIndex();
  1027. while ( it.Next() )
  1028. {
  1029. // Blend the two.
  1030. if ( !it.IsLastVert() )
  1031. {
  1032. Vector vecAverage = pDisp->GetNormal( it.GetVertIndex() ) + pNeighbor->GetNormal( it.GetNBVertIndex() );
  1033. Vector vAvgTanS = pDisp->GetTangentS( it.GetVertIndex() ) + pNeighbor->GetTangentS( it.GetNBVertIndex() );
  1034. VectorNormalize( vecAverage );
  1035. VectorNormalize( vAvgTanS );
  1036. UpdateTangentSpace(pDisp, it.GetVertIndex(), vecAverage, vAvgTanS );
  1037. UpdateTangentSpace(pNeighbor, it.GetNBVertIndex(), vecAverage, vAvgTanS );
  1038. }
  1039. // Now blend the in-between verts (if this edge is high-res).
  1040. int iPrevPos = viPrevPos[!iEdgeDim];
  1041. int iCurPos = it.GetVertIndex()[!iEdgeDim];
  1042. for ( int iTween = iPrevPos+1; iTween < iCurPos; iTween++ )
  1043. {
  1044. float flPercent = RemapVal( iTween, iPrevPos, iCurPos, 0, 1 );
  1045. Vector vecNormal;
  1046. VectorLerp( pDisp->GetNormal( viPrevPos ), pDisp->GetNormal( it.GetVertIndex() ), flPercent, vecNormal );
  1047. VectorNormalize( vecNormal );
  1048. Vector vAvgTanS;
  1049. VectorLerp( pDisp->GetTangentS( viPrevPos ), pDisp->GetTangentS( it.GetVertIndex() ), flPercent, vAvgTanS );
  1050. VectorNormalize( vAvgTanS );
  1051. CVertIndex viTween;
  1052. viTween[iEdgeDim] = it.GetVertIndex()[iEdgeDim];
  1053. viTween[!iEdgeDim] = iTween;
  1054. UpdateTangentSpace(pDisp, viTween, vecNormal, vAvgTanS);
  1055. }
  1056. viPrevPos = it.GetVertIndex();
  1057. }
  1058. }
  1059. }
  1060. }
  1061. }
  1062. //-----------------------------------------------------------------------------
  1063. // Purpose:
  1064. // Input : **pListBase -
  1065. // listSize -
  1066. // NOTE: todo - this is almost the same code as found in vrad, should probably
  1067. // move it up into common code at some point if the feature
  1068. // continues to get used
  1069. //-----------------------------------------------------------------------------
  1070. void SmoothDispSurfNormals( CCoreDispInfo **ppListBase, int nListSize )
  1071. {
  1072. // Setup helper list for iteration.
  1073. for ( int iDisp = 0; iDisp < nListSize; ++iDisp )
  1074. {
  1075. ppListBase[iDisp]->SetDispUtilsHelperInfo( ppListBase, nListSize );
  1076. }
  1077. // Blend normals along t-junctions, corners, and edges.
  1078. BlendSubNeighbors( ppListBase, nListSize );
  1079. BlendCorners( ppListBase, nListSize );
  1080. BlendEdges( ppListBase, nListSize );
  1081. }