Counter Strike : Global Offensive Source Code
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.

1802 lines
54 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "vrad.h"
  8. #include "UtlVector.h"
  9. #include "cmodel.h"
  10. #include "BSPTreeData.h"
  11. #include "VRAD_DispColl.h"
  12. #include "CollisionUtils.h"
  13. #include "lightmap.h"
  14. #include "Radial.h"
  15. #include "CollisionUtils.h"
  16. #include "mathlib/bumpvects.h"
  17. #include "UtlRBTree.h"
  18. #include "tier0/fasttimer.h"
  19. #include "disp_vrad.h"
  20. class CBSPDispRayDistanceEnumerator;
  21. //=============================================================================
  22. //
  23. // Displacement/Face List
  24. //
  25. class CBSPDispFaceListEnumerator : public ISpatialLeafEnumerator, public IBSPTreeDataEnumerator
  26. {
  27. public:
  28. //=========================================================================
  29. //
  30. // Construction/Deconstruction
  31. //
  32. CBSPDispFaceListEnumerator() {};
  33. virtual ~CBSPDispFaceListEnumerator()
  34. {
  35. m_DispList.Purge();
  36. m_FaceList.Purge();
  37. }
  38. // ISpatialLeafEnumerator
  39. bool EnumerateLeaf( int ndxLeaf, intp context );
  40. // IBSPTreeDataEnumerator
  41. bool FASTCALL EnumerateElement( int userId, intp context );
  42. public:
  43. CUtlVector<CVRADDispColl*> m_DispList;
  44. CUtlVector<int> m_FaceList;
  45. };
  46. //=============================================================================
  47. //
  48. // RayEnumerator
  49. //
  50. class CBSPDispRayEnumerator : public ISpatialLeafEnumerator, public IBSPTreeDataEnumerator
  51. {
  52. public:
  53. // ISpatialLeafEnumerator
  54. bool EnumerateLeaf( int ndxLeaf, intp context );
  55. // IBSPTreeDataEnumerator
  56. bool FASTCALL EnumerateElement( int userId, intp context );
  57. };
  58. //=============================================================================
  59. //
  60. // VRad Displacement Manager
  61. //
  62. class CVRadDispMgr : public IVRadDispMgr
  63. {
  64. public:
  65. //=========================================================================
  66. //
  67. // Construction/Deconstruction
  68. //
  69. CVRadDispMgr();
  70. virtual ~CVRadDispMgr();
  71. // creation/destruction
  72. void Init( void );
  73. void Shutdown( void );
  74. // "CalcPoints"
  75. bool BuildDispSamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace );
  76. bool BuildDispLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace );
  77. bool BuildDispSamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace );
  78. // patching functions
  79. void MakePatches( void );
  80. void SubdividePatch( int iPatch );
  81. // pre "FinalLightFace"
  82. void InsertSamplesDataIntoHashTable( void );
  83. void InsertPatchSampleDataIntoHashTable( void );
  84. // "FinalLightFace"
  85. radial_t *BuildLuxelRadial( int ndxFace, int ndxStyle, bool bBump );
  86. bool SampleRadial( int ndxFace, radial_t *pRadial, Vector const &vPos, int ndxLxl, LightingValue_t *pLightSample, int sampleCount, bool bPatch );
  87. radial_t *BuildPatchRadial( int ndxFace, bool bBump );
  88. // utility
  89. void GetDispSurfNormal( int ndxFace, Vector &pt, Vector &ptNormal, bool bInside );
  90. void GetDispSurfPointAndNormalFromUV( int ndxFace, Vector &pt, Vector &ptNormal,
  91. Vector2D &uv, bool bInside );
  92. void GetDispSurf( int ndxFace, CVRADDispColl **ppDispTree );
  93. // bsp tree functions
  94. bool ClipRayToDisp( DispTested_t &dispTested, Ray_t const &ray );
  95. bool ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf );
  96. void ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf,
  97. float& dist, dface_t*& pFace, Vector2D& luxelCoord );
  98. void ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
  99. int ndxLeaf, float& dist, Vector *pNormal );
  100. void StartRayTest( DispTested_t &dispTested );
  101. void AddPolysForRayTrace( void );
  102. // general timing -- should be moved!!
  103. void StartTimer( const char *name );
  104. void EndTimer( void );
  105. //=========================================================================
  106. //
  107. // Enumeration Methods
  108. //
  109. bool DispRay_EnumerateLeaf( int ndxLeaf, intp context );
  110. bool DispRay_EnumerateElement( int userId, intp context );
  111. bool DispRayDistance_EnumerateElement( int userId, CBSPDispRayDistanceEnumerator* pEnum );
  112. bool DispFaceList_EnumerateLeaf( int ndxLeaf, intp context );
  113. bool DispFaceList_EnumerateElement( int userId, intp context );
  114. private:
  115. //=========================================================================
  116. //
  117. // BSP Tree Helpers
  118. //
  119. void InsertDispIntoTree( int ndxDisp );
  120. void RemoveDispFromTree( int ndxDisp );
  121. //=========================================================================
  122. //
  123. // Displacement Data Loader (from .bsp)
  124. //
  125. void UnserializeDisps( void );
  126. void DispBuilderInit( CCoreDispInfo *pBuilderDisp, dface_t *pFace, int ndxFace );
  127. //=========================================================================
  128. //
  129. // Sampling Helpers
  130. //
  131. void RadialLuxelBuild( CVRADDispColl *pDispTree, radial_t *pRadial, int ndxStyle, bool bBump );
  132. void RadialLuxelAddSamples( int ndxFace, Vector const &luxelPt, Vector const &luxelNormal, float radius,
  133. radial_t *pRadial, int ndxRadial, bool bBump, int lightStyle );
  134. void RadialPatchBuild( CVRADDispColl *pDispTree, radial_t *pRadial, bool bBump );
  135. void RadialLuxelAddPatch( int ndxFace, Vector const &luxelPt,
  136. Vector const &luxelNormal, float radius,
  137. radial_t *pRadial, int ndxRadial, bool bBump,
  138. CUtlVector<CPatch*> &interestingPatches );
  139. bool IsNeighbor( int iDispFace, int iNeighborFace, bool bCheck2ndDegreeNeighbors = false );
  140. void GetInterestingPatchesForLuxels(
  141. int ndxFace,
  142. CUtlVector<CPatch*> &interestingPatches,
  143. float patchSampleRadius );
  144. private:
  145. struct DispCollTree_t
  146. {
  147. CVRADDispColl *m_pDispTree;
  148. BSPTreeDataHandle_t m_Handle;
  149. };
  150. struct EnumContext_t
  151. {
  152. DispTested_t *m_pDispTested;
  153. Ray_t const *m_pRay;
  154. };
  155. CUtlVector<DispCollTree_t> m_DispTrees;
  156. IBSPTreeData *m_pBSPTreeData;
  157. CBSPDispRayEnumerator m_EnumDispRay;
  158. CBSPDispFaceListEnumerator m_EnumDispFaceList;
  159. int sampleCount;
  160. Vector *m_pSamplePos;
  161. CFastTimer m_Timer;
  162. };
  163. //-----------------------------------------------------------------------------
  164. // Purpose: expose IVRadDispMgr to vrad
  165. //-----------------------------------------------------------------------------
  166. static CVRadDispMgr s_DispMgr;
  167. IVRadDispMgr *StaticDispMgr( void )
  168. {
  169. return &s_DispMgr;
  170. }
  171. //=============================================================================
  172. //
  173. // Displacement/Face List
  174. //
  175. // ISpatialLeafEnumerator
  176. bool CBSPDispFaceListEnumerator::EnumerateLeaf( int ndxLeaf, intp context )
  177. {
  178. return s_DispMgr.DispFaceList_EnumerateLeaf( ndxLeaf, context );
  179. }
  180. // IBSPTreeDataEnumerator
  181. bool FASTCALL CBSPDispFaceListEnumerator::EnumerateElement( int userId, intp context )
  182. {
  183. return s_DispMgr.DispFaceList_EnumerateElement( userId, context );
  184. }
  185. //=============================================================================
  186. //
  187. // RayEnumerator
  188. //
  189. bool CBSPDispRayEnumerator::EnumerateLeaf( int ndxLeaf, intp context )
  190. {
  191. return s_DispMgr.DispRay_EnumerateLeaf( ndxLeaf, context );
  192. }
  193. bool FASTCALL CBSPDispRayEnumerator::EnumerateElement( int userId, intp context )
  194. {
  195. return s_DispMgr.DispRay_EnumerateElement( userId, context );
  196. }
  197. //-----------------------------------------------------------------------------
  198. // Here's an enumerator that we use for testing against disps in a leaf...
  199. //-----------------------------------------------------------------------------
  200. class CBSPDispRayDistanceEnumerator : public IBSPTreeDataEnumerator
  201. {
  202. public:
  203. CBSPDispRayDistanceEnumerator() : m_Distance(1.0f), m_pSurface(0) {}
  204. // IBSPTreeDataEnumerator
  205. bool FASTCALL EnumerateElement( int userId, intp context )
  206. {
  207. return s_DispMgr.DispRayDistance_EnumerateElement( userId, this );
  208. }
  209. float m_Distance;
  210. dface_t* m_pSurface;
  211. DispTested_t *m_pDispTested;
  212. Ray_t const *m_pRay;
  213. Vector2D m_LuxelCoord;
  214. Vector m_Normal;
  215. };
  216. //-----------------------------------------------------------------------------
  217. //-----------------------------------------------------------------------------
  218. CVRadDispMgr::CVRadDispMgr()
  219. {
  220. m_pBSPTreeData = CreateBSPTreeData();
  221. }
  222. //-----------------------------------------------------------------------------
  223. //-----------------------------------------------------------------------------
  224. CVRadDispMgr::~CVRadDispMgr()
  225. {
  226. DestroyBSPTreeData( m_pBSPTreeData );
  227. }
  228. //-----------------------------------------------------------------------------
  229. // Insert a displacement into the tree for collision
  230. //-----------------------------------------------------------------------------
  231. void CVRadDispMgr::InsertDispIntoTree( int ndxDisp )
  232. {
  233. DispCollTree_t &dispTree = m_DispTrees[ndxDisp];
  234. CDispCollTree *pDispTree = dispTree.m_pDispTree;
  235. // get the bounding box of the tree
  236. Vector boxMin, boxMax;
  237. pDispTree->GetBounds( boxMin, boxMax );
  238. // add the displacement to the tree so we will collide against it
  239. dispTree.m_Handle = m_pBSPTreeData->Insert( ndxDisp, boxMin, boxMax );
  240. }
  241. //-----------------------------------------------------------------------------
  242. // Remove a displacement from the tree for collision
  243. //-----------------------------------------------------------------------------
  244. void CVRadDispMgr::RemoveDispFromTree( int ndxDisp )
  245. {
  246. // release the tree handle
  247. if( m_DispTrees[ndxDisp].m_Handle != TREEDATA_INVALID_HANDLE )
  248. {
  249. m_pBSPTreeData->Remove( m_DispTrees[ndxDisp].m_Handle );
  250. m_DispTrees[ndxDisp].m_Handle = TREEDATA_INVALID_HANDLE;
  251. }
  252. }
  253. //-----------------------------------------------------------------------------
  254. //-----------------------------------------------------------------------------
  255. void CVRadDispMgr::Init( void )
  256. {
  257. // initialize the bsp tree
  258. m_pBSPTreeData->Init( ToolBSPTree() );
  259. // read in displacements that have been compiled into the bsp file
  260. UnserializeDisps();
  261. }
  262. //-----------------------------------------------------------------------------
  263. //-----------------------------------------------------------------------------
  264. void CVRadDispMgr::Shutdown( void )
  265. {
  266. // remove all displacements from the tree
  267. for( int ndxDisp = m_DispTrees.Count(); ndxDisp >= 0; ndxDisp-- )
  268. {
  269. RemoveDispFromTree( ndxDisp );
  270. }
  271. // shutdown the bsp tree
  272. m_pBSPTreeData->Shutdown();
  273. // purge the displacement collision tree list
  274. m_DispTrees.Purge();
  275. }
  276. //-----------------------------------------------------------------------------
  277. //-----------------------------------------------------------------------------
  278. void CVRadDispMgr::DispBuilderInit( CCoreDispInfo *pBuilderDisp, dface_t *pFace, int ndxFace )
  279. {
  280. // get the .bsp displacement
  281. ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
  282. if( !pDisp )
  283. return;
  284. //
  285. // initlialize the displacement base surface
  286. //
  287. CCoreDispSurface *pSurf = pBuilderDisp->GetSurface();
  288. pSurf->SetPointCount( 4 );
  289. pSurf->SetHandle( ndxFace );
  290. pSurf->SetContents( pDisp->contents );
  291. Vector pt[4];
  292. int ndxPt;
  293. for( ndxPt = 0; ndxPt < 4; ndxPt++ )
  294. {
  295. int eIndex = dsurfedges[pFace->firstedge+ndxPt];
  296. if( eIndex < 0 )
  297. {
  298. pSurf->SetPoint( ndxPt, dvertexes[dedges[-eIndex].v[1]].point );
  299. }
  300. else
  301. {
  302. pSurf->SetPoint( ndxPt, dvertexes[dedges[eIndex].v[0]].point );
  303. }
  304. VectorCopy( pSurf->GetPoint(ndxPt), pt[ndxPt] );
  305. }
  306. //
  307. // calculate the displacement surface normal
  308. //
  309. Vector vFaceNormal;
  310. pSurf->GetNormal( vFaceNormal );
  311. for( ndxPt = 0; ndxPt < 4; ndxPt++ )
  312. {
  313. pSurf->SetPointNormal( ndxPt, vFaceNormal );
  314. }
  315. // set the surface initial point info
  316. pSurf->SetPointStart( pDisp->startPosition );
  317. pSurf->FindSurfPointStartIndex();
  318. pSurf->AdjustSurfPointData();
  319. Vector vecTmp( texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][0],
  320. texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][1],
  321. texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][2] );
  322. int nLuxelsPerWorldUnit = static_cast<int>( 1.0f / VectorLength( vecTmp ) );
  323. Vector vecU( texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][0],
  324. texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][1],
  325. texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][2] );
  326. Vector vecV( texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[1][0],
  327. texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[1][1],
  328. texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[1][2] );
  329. pSurf->CalcLuxelCoords( nLuxelsPerWorldUnit, false, vecU, vecV );
  330. pBuilderDisp->SetNeighborData( pDisp->m_EdgeNeighbors, pDisp->m_CornerNeighbors );
  331. CDispVert *pVerts = &g_DispVerts[ pDisp->m_iDispVertStart ];
  332. CDispTri *pTris = &g_DispTris[pDisp->m_iDispTriStart];
  333. //
  334. // initialize the displacement data
  335. //
  336. pBuilderDisp->InitDispInfo(
  337. pDisp->power,
  338. pDisp->minTess,
  339. pDisp->smoothingAngle,
  340. pVerts,
  341. pTris,
  342. 0,
  343. NULL );
  344. }
  345. //-----------------------------------------------------------------------------
  346. //-----------------------------------------------------------------------------
  347. void CVRadDispMgr::UnserializeDisps( void )
  348. {
  349. // temporarily create the "builder" displacements
  350. CUtlVector<CCoreDispInfo*> builderDisps;
  351. for ( int iDisp = 0; iDisp < g_dispinfo.Count(); ++iDisp )
  352. {
  353. CCoreDispInfo *pDisp = new CCoreDispInfo;
  354. if ( !pDisp )
  355. {
  356. builderDisps.Purge();
  357. return;
  358. }
  359. int nIndex = builderDisps.AddToTail();
  360. pDisp->SetListIndex( nIndex );
  361. builderDisps[nIndex] = pDisp;
  362. }
  363. // Set them up as CDispUtilsHelpers.
  364. for ( int iDisp = 0; iDisp < g_dispinfo.Count(); ++iDisp )
  365. {
  366. builderDisps[iDisp]->SetDispUtilsHelperInfo( builderDisps.Base(), g_dispinfo.Count() );
  367. }
  368. //
  369. // find all faces with displacement data and initialize
  370. //
  371. for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
  372. {
  373. dface_t *pFace = &g_pFaces[ndxFace];
  374. if( ValidDispFace( pFace ) )
  375. {
  376. DispBuilderInit( builderDisps[pFace->dispinfo], pFace, ndxFace );
  377. }
  378. }
  379. // generate the displacement surfaces
  380. for( int iDisp = 0; iDisp < g_dispinfo.Count(); ++iDisp )
  381. {
  382. builderDisps[iDisp]->Create();
  383. }
  384. // smooth edge normals
  385. SmoothNeighboringDispSurfNormals( builderDisps.Base(), g_dispinfo.Count() );
  386. //
  387. // create the displacement collision tree and add it to the bsp tree
  388. //
  389. CVRADDispColl *pDispTrees = new CVRADDispColl[g_dispinfo.Count()];
  390. if( !pDispTrees )
  391. return;
  392. m_DispTrees.AddMultipleToTail( g_dispinfo.Count() );
  393. for( int iDisp = 0; iDisp < g_dispinfo.Count(); iDisp++ )
  394. {
  395. pDispTrees[iDisp].Create( builderDisps[iDisp] );
  396. m_DispTrees[iDisp].m_pDispTree = &pDispTrees[iDisp];
  397. m_DispTrees[iDisp].m_Handle = TREEDATA_INVALID_HANDLE;
  398. InsertDispIntoTree( iDisp );
  399. }
  400. // free "builder" disps
  401. builderDisps.Purge();
  402. }
  403. //-----------------------------------------------------------------------------
  404. // Purpose: create a set of patches for each displacement surface to transfer
  405. // bounced light around with
  406. //-----------------------------------------------------------------------------
  407. void CVRadDispMgr::MakePatches( void )
  408. {
  409. // Collect stats - keep track of the total displacement surface area.
  410. float flTotalArea = 0.0f;
  411. // Create patches for all of the displacements.
  412. int nTreeCount = m_DispTrees.Count();
  413. for( int iTree = 0; iTree < nTreeCount; ++iTree )
  414. {
  415. // Get the current displacement collision tree.
  416. CVRADDispColl *pDispTree = m_DispTrees[iTree].m_pDispTree;
  417. if( !pDispTree )
  418. continue;
  419. flTotalArea += pDispTree->CreateParentPatches();
  420. }
  421. // Print stats.
  422. qprintf( "%i Displacements\n", nTreeCount );
  423. qprintf( "%i Square Feet [%.2f Square Inches]\n", ( int )( flTotalArea / 144.0f ), flTotalArea );
  424. }
  425. //-----------------------------------------------------------------------------
  426. //-----------------------------------------------------------------------------
  427. void CVRadDispMgr::SubdividePatch( int iPatch )
  428. {
  429. // Get the current patch to subdivide.
  430. CPatch *pPatch = &g_Patches[iPatch];
  431. if ( !pPatch )
  432. return;
  433. // Create children patches.
  434. DispCollTree_t &dispTree = m_DispTrees[g_pFaces[pPatch->faceNumber].dispinfo];
  435. CVRADDispColl *pTree = dispTree.m_pDispTree;
  436. if( pTree )
  437. {
  438. pTree->CreateChildPatches( iPatch, 0 );
  439. }
  440. }
  441. //-----------------------------------------------------------------------------
  442. //-----------------------------------------------------------------------------
  443. void CVRadDispMgr::StartRayTest( DispTested_t &dispTested )
  444. {
  445. if( m_DispTrees.Count() > 0 )
  446. {
  447. if( dispTested.m_pTested == 0 )
  448. {
  449. dispTested.m_pTested = new int[m_DispTrees.Count()];
  450. memset( dispTested.m_pTested, 0, m_DispTrees.Count() * sizeof( int ) );
  451. dispTested.m_Enum = 0;
  452. }
  453. ++dispTested.m_Enum;
  454. }
  455. }
  456. //-----------------------------------------------------------------------------
  457. //-----------------------------------------------------------------------------
  458. bool CVRadDispMgr::ClipRayToDisp( DispTested_t &dispTested, Ray_t const &ray )
  459. {
  460. StartRayTest( dispTested );
  461. EnumContext_t ctx;
  462. ctx.m_pRay = &ray;
  463. ctx.m_pDispTested = &dispTested;
  464. // If it got through without a hit, it returns true
  465. return !m_pBSPTreeData->EnumerateLeavesAlongRay( ray, &m_EnumDispRay, ( int )&ctx );
  466. }
  467. //-----------------------------------------------------------------------------
  468. //-----------------------------------------------------------------------------
  469. bool CVRadDispMgr::ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
  470. int ndxLeaf )
  471. {
  472. EnumContext_t ctx;
  473. ctx.m_pRay = &ray;
  474. ctx.m_pDispTested = &dispTested;
  475. return !m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &m_EnumDispRay, ( int )&ctx );
  476. }
  477. //-----------------------------------------------------------------------------
  478. //-----------------------------------------------------------------------------
  479. void CVRadDispMgr::ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
  480. int ndxLeaf, float& dist, dface_t*& pFace, Vector2D& luxelCoord )
  481. {
  482. CBSPDispRayDistanceEnumerator rayTestEnum;
  483. rayTestEnum.m_pRay = &ray;
  484. rayTestEnum.m_pDispTested = &dispTested;
  485. m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &rayTestEnum, 0 );
  486. dist = rayTestEnum.m_Distance;
  487. pFace = rayTestEnum.m_pSurface;
  488. if (pFace)
  489. {
  490. Vector2DCopy( rayTestEnum.m_LuxelCoord, luxelCoord );
  491. }
  492. }
  493. void CVRadDispMgr::ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
  494. int ndxLeaf, float& dist, Vector *pNormal )
  495. {
  496. CBSPDispRayDistanceEnumerator rayTestEnum;
  497. rayTestEnum.m_pRay = &ray;
  498. rayTestEnum.m_pDispTested = &dispTested;
  499. m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &rayTestEnum, 0 );
  500. dist = rayTestEnum.m_Distance;
  501. if ( rayTestEnum.m_pSurface )
  502. {
  503. *pNormal = rayTestEnum.m_Normal;
  504. }
  505. }
  506. void CVRadDispMgr::AddPolysForRayTrace( void )
  507. {
  508. int nTreeCount = m_DispTrees.Count();
  509. for( int iTree = 0; iTree < nTreeCount; ++iTree )
  510. {
  511. // Get the current displacement collision tree.
  512. CVRADDispColl *pDispTree = m_DispTrees[iTree].m_pDispTree;
  513. // Add the triangles of the tree to the RT environment
  514. pDispTree->AddPolysForRayTrace();
  515. }
  516. }
  517. //-----------------------------------------------------------------------------
  518. //-----------------------------------------------------------------------------
  519. void CVRadDispMgr::GetDispSurfNormal( int ndxFace, Vector &pt, Vector &ptNormal,
  520. bool bInside )
  521. {
  522. // get the displacement surface data
  523. DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
  524. CVRADDispColl *pDispTree = dispTree.m_pDispTree;
  525. // find the parameterized displacement indices
  526. Vector2D uv;
  527. pDispTree->BaseFacePlaneToDispUV( pt, uv );
  528. if( bInside )
  529. {
  530. if( uv[0] < 0.0f || uv[0] > 1.0f ) { Msg( "Disp UV (%f) outside bounds!\n", uv[0] ); }
  531. if( uv[1] < 0.0f || uv[1] > 1.0f ) { Msg( "Disp UV (%f) outside bounds!\n", uv[1] ); }
  532. }
  533. if( uv[0] < 0.0f ) { uv[0] = 0.0f; }
  534. if( uv[0] > 1.0f ) { uv[0] = 1.0f; }
  535. if( uv[1] < 0.0f ) { uv[1] = 0.0f; }
  536. if( uv[1] > 1.0f ) { uv[1] = 1.0f; }
  537. // get the normal at "pt"
  538. pDispTree->DispUVToSurfNormal( uv, ptNormal );
  539. // get the new "pt"
  540. pDispTree->DispUVToSurfPoint( uv, pt, 1.0f );
  541. }
  542. //-----------------------------------------------------------------------------
  543. //-----------------------------------------------------------------------------
  544. void CVRadDispMgr::GetDispSurfPointAndNormalFromUV( int ndxFace, Vector &pt, Vector &ptNormal,
  545. Vector2D &uv, bool bInside )
  546. {
  547. // get the displacement surface data
  548. DispCollTree_t &dispTree = m_DispTrees[ g_pFaces[ ndxFace ].dispinfo ];
  549. CVRADDispColl *pDispTree = dispTree.m_pDispTree;
  550. if ( bInside )
  551. {
  552. if ( uv[ 0 ] < 0.0f || uv[ 0 ] > 1.0f ) { Msg( "Disp UV (%f) outside bounds!\n", uv[ 0 ] ); }
  553. if ( uv[ 1 ] < 0.0f || uv[ 1 ] > 1.0f ) { Msg( "Disp UV (%f) outside bounds!\n", uv[ 1 ] ); }
  554. }
  555. if ( uv[ 0 ] < 0.0f ) { uv[ 0 ] = 0.0f; }
  556. if ( uv[ 0 ] > 1.0f ) { uv[ 0 ] = 1.0f; }
  557. if ( uv[ 1 ] < 0.0f ) { uv[ 1 ] = 0.0f; }
  558. if ( uv[ 1 ] > 1.0f ) { uv[ 1 ] = 1.0f; }
  559. // get the normal at "pt"
  560. pDispTree->DispUVToSurfNormal( uv, ptNormal );
  561. // get the new "pt"
  562. pDispTree->DispUVToSurfPoint( uv, pt, 1.0f );
  563. }
  564. //-----------------------------------------------------------------------------
  565. //-----------------------------------------------------------------------------
  566. void CVRadDispMgr::GetDispSurf( int ndxFace, CVRADDispColl **ppDispTree )
  567. {
  568. DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
  569. *ppDispTree = dispTree.m_pDispTree;
  570. }
  571. //-----------------------------------------------------------------------------
  572. //-----------------------------------------------------------------------------
  573. bool CVRadDispMgr::DispRay_EnumerateLeaf( int ndxLeaf, intp context )
  574. {
  575. return m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &m_EnumDispRay, context );
  576. }
  577. //-----------------------------------------------------------------------------
  578. //-----------------------------------------------------------------------------
  579. bool CVRadDispMgr::DispRay_EnumerateElement( int userId, intp context )
  580. {
  581. DispCollTree_t &dispTree = m_DispTrees[userId];
  582. EnumContext_t *pCtx = ( EnumContext_t* )context;
  583. // don't test twice (check tested value)
  584. if( pCtx->m_pDispTested->m_pTested[userId] == pCtx->m_pDispTested->m_Enum )
  585. return true;
  586. // set the tested value
  587. pCtx->m_pDispTested->m_pTested[userId] = pCtx->m_pDispTested->m_Enum;
  588. // false mean stop iterating -- return false if we hit! (NOTE: opposite return
  589. // result of the collision tree's ray test, thus the !)
  590. CBaseTrace trace;
  591. trace.fraction = 1.0f;
  592. return ( !dispTree.m_pDispTree->AABBTree_Ray( *pCtx->m_pRay, pCtx->m_pRay->InvDelta(), &trace, true ) );
  593. }
  594. //-----------------------------------------------------------------------------
  595. //-----------------------------------------------------------------------------
  596. bool CVRadDispMgr::DispRayDistance_EnumerateElement( int userId, CBSPDispRayDistanceEnumerator* pCtx )
  597. {
  598. DispCollTree_t &dispTree = m_DispTrees[userId];
  599. // don't test twice (check tested value)
  600. if( pCtx->m_pDispTested->m_pTested[userId] == pCtx->m_pDispTested->m_Enum )
  601. return true;
  602. // set the tested value
  603. pCtx->m_pDispTested->m_pTested[userId] = pCtx->m_pDispTested->m_Enum;
  604. // Test the ray, if it's closer than previous tests, use it!
  605. RayDispOutput_t output;
  606. output.ndxVerts[0] = -1;
  607. output.ndxVerts[1] = -1;
  608. output.ndxVerts[2] = -1;
  609. output.ndxVerts[3] = -1;
  610. output.u = -1.0f;
  611. output.v = -1.0f;
  612. output.dist = FLT_MAX;
  613. if (dispTree.m_pDispTree->AABBTree_Ray( *pCtx->m_pRay, output ))
  614. {
  615. if (output.dist < pCtx->m_Distance)
  616. {
  617. pCtx->m_Distance = output.dist;
  618. pCtx->m_pSurface = &g_pFaces[dispTree.m_pDispTree->GetParentIndex()];
  619. // Get the luxel coordinate
  620. ComputePointFromBarycentric(
  621. dispTree.m_pDispTree->GetLuxelCoord(output.ndxVerts[0]),
  622. dispTree.m_pDispTree->GetLuxelCoord(output.ndxVerts[1]),
  623. dispTree.m_pDispTree->GetLuxelCoord(output.ndxVerts[2]),
  624. output.u, output.v, pCtx->m_LuxelCoord );
  625. Vector v0,v1,v2;
  626. dispTree.m_pDispTree->GetVert( output.ndxVerts[0], v0 );
  627. dispTree.m_pDispTree->GetVert( output.ndxVerts[1], v1 );
  628. dispTree.m_pDispTree->GetVert( output.ndxVerts[2], v2 );
  629. Vector e0 = v1-v0;
  630. Vector e1 = v2-v0;
  631. pCtx->m_Normal = CrossProduct( e0, e1 );
  632. VectorNormalize(pCtx->m_Normal);
  633. }
  634. }
  635. return true;
  636. }
  637. //-----------------------------------------------------------------------------
  638. // Test a ray against a particular dispinfo
  639. //-----------------------------------------------------------------------------
  640. /*
  641. float CVRadDispMgr::ClipRayToDisp( Ray_t const &ray, int dispinfo )
  642. {
  643. assert( m_DispTrees.IsValidIndex(dispinfo) );
  644. RayDispOutput_t output;
  645. if (!m_DispTrees[dispinfo].m_pDispTree->AABBTree_Ray( ray, output ))
  646. return 1.0f;
  647. return output.dist;
  648. }
  649. */
  650. //-----------------------------------------------------------------------------
  651. //-----------------------------------------------------------------------------
  652. bool CVRadDispMgr::DispFaceList_EnumerateLeaf( int ndxLeaf, intp context )
  653. {
  654. //
  655. // add the faces found in this leaf to the face list
  656. //
  657. dleaf_t *pLeaf = &dleafs[ndxLeaf];
  658. for( int ndxFace = 0; ndxFace < pLeaf->numleaffaces; ndxFace++ )
  659. {
  660. // get the current face index
  661. int ndxLeafFace = pLeaf->firstleafface + ndxFace;
  662. // check to see if the face already lives in the list
  663. int ndx;
  664. int size = m_EnumDispFaceList.m_FaceList.Count();
  665. for( ndx = 0; ndx < size; ndx++ )
  666. {
  667. if( m_EnumDispFaceList.m_FaceList[ndx] == ndxLeafFace )
  668. break;
  669. }
  670. if( ndx == size )
  671. {
  672. int ndxList = m_EnumDispFaceList.m_FaceList.AddToTail();
  673. m_EnumDispFaceList.m_FaceList[ndxList] = ndxLeafFace;
  674. }
  675. }
  676. return m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &m_EnumDispFaceList, context );
  677. }
  678. //-----------------------------------------------------------------------------
  679. //-----------------------------------------------------------------------------
  680. bool CVRadDispMgr::DispFaceList_EnumerateElement( int userId, intp context )
  681. {
  682. DispCollTree_t &dispTree = m_DispTrees[userId];
  683. CVRADDispColl *pDispTree = dispTree.m_pDispTree;
  684. if( !pDispTree )
  685. return false;
  686. // check to see if the displacement already lives in the list
  687. int ndx;
  688. int size = m_EnumDispFaceList.m_DispList.Count();
  689. for( ndx = 0; ndx < size; ndx++ )
  690. {
  691. if( m_EnumDispFaceList.m_DispList[ndx] == pDispTree )
  692. break;
  693. }
  694. if( ndx == size )
  695. {
  696. int ndxList = m_EnumDispFaceList.m_DispList.AddToTail();
  697. m_EnumDispFaceList.m_DispList[ndxList] = pDispTree;
  698. }
  699. return true;
  700. }
  701. //-----------------------------------------------------------------------------
  702. //-----------------------------------------------------------------------------
  703. inline void GetSampleLight( facelight_t *pFaceLight, int ndxStyle, bool bBumped,
  704. int ndxSample, LightingValue_t *pSampleLight )
  705. {
  706. // SampleLight[0].Init( 20.0f, 10.0f, 10.0f );
  707. // return;
  708. // get sample from bumped lighting data
  709. if( bBumped )
  710. {
  711. for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
  712. {
  713. pSampleLight[ndxBump] = pFaceLight->light[ndxStyle][ndxBump][ndxSample];
  714. }
  715. }
  716. // just a generally lit surface
  717. else
  718. {
  719. pSampleLight[0] = pFaceLight->light[ndxStyle][0][ndxSample];
  720. }
  721. }
  722. //-----------------------------------------------------------------------------
  723. //-----------------------------------------------------------------------------
  724. void AddSampleLightToRadial( Vector const &samplePos, Vector const &sampleNormal,
  725. LightingValue_t *pSampleLight, float sampleRadius2,
  726. Vector const &luxelPos, Vector const &luxelNormal,
  727. radial_t *pRadial, int ndxRadial, bool bBumped,
  728. bool bNeighborBumped )
  729. {
  730. // check normals to see if sample contributes any light at all
  731. float angle = sampleNormal.Dot( luxelNormal );
  732. if ( angle < 0.15f )
  733. return;
  734. // calculate the light vector
  735. Vector vSegment = samplePos - luxelPos;
  736. // get the distance to the light
  737. float dist = vSegment.Length();
  738. float dist2 = dist * dist;
  739. // Check to see if the light is within the influence.
  740. float influence = 1.0f - ( dist2 / ( sampleRadius2 ) );
  741. if( influence <= 0.0f )
  742. return;
  743. influence *= angle;
  744. if( bBumped )
  745. {
  746. if( bNeighborBumped )
  747. {
  748. for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
  749. {
  750. pRadial->light[ndxBump][ndxRadial].AddWeighted( pSampleLight[ndxBump], influence );
  751. }
  752. pRadial->weight[ndxRadial] += influence;
  753. }
  754. else
  755. {
  756. influence *= 0.05f;
  757. for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
  758. {
  759. pRadial->light[ndxBump][ndxRadial].AddWeighted( pSampleLight[0], influence );
  760. }
  761. pRadial->weight[ndxRadial] += influence;
  762. }
  763. }
  764. else
  765. {
  766. pRadial->light[0][ndxRadial].AddWeighted( pSampleLight[0], influence );
  767. pRadial->weight[ndxRadial] += influence;
  768. }
  769. }
  770. //-----------------------------------------------------------------------------
  771. //-----------------------------------------------------------------------------
  772. bool CVRadDispMgr::IsNeighbor( int iFace, int iNeighborFace, bool bCheck2ndDegreeNeighbors )
  773. {
  774. if ( iFace == iNeighborFace )
  775. return true;
  776. faceneighbor_t *pFaceNeighbor = &faceneighbor[iFace];
  777. for ( int iNeighbor = 0; iNeighbor < pFaceNeighbor->numneighbors; iNeighbor++ )
  778. {
  779. if ( pFaceNeighbor->neighbor[iNeighbor] == iNeighborFace )
  780. return true;
  781. }
  782. if ( bCheck2ndDegreeNeighbors )
  783. {
  784. for ( int iNeighbor = 0; iNeighbor < pFaceNeighbor->numneighbors; iNeighbor++ )
  785. {
  786. faceneighbor_t *pFaceNeighbor2 = &faceneighbor[ pFaceNeighbor->neighbor[ iNeighbor ] ];
  787. for ( int iNeighbor2 = 0; iNeighbor2 < pFaceNeighbor2->numneighbors; iNeighbor2++ )
  788. {
  789. if ( pFaceNeighbor2->neighbor[ iNeighbor2 ] == iNeighborFace )
  790. return true;
  791. }
  792. }
  793. }
  794. return false;
  795. }
  796. //-----------------------------------------------------------------------------
  797. //-----------------------------------------------------------------------------
  798. void CVRadDispMgr::RadialLuxelAddSamples( int ndxFace, Vector const &luxelPt, Vector const &luxelNormal, float radius,
  799. radial_t *pRadial, int ndxRadial, bool bBump, int lightStyle )
  800. {
  801. // calculate one over the voxel size
  802. float ooVoxelSize = 1.0f / SAMPLEHASH_VOXEL_SIZE;
  803. //
  804. // find voxel info
  805. //
  806. int voxelMin[3], voxelMax[3];
  807. for( int axis = 0; axis < 3; axis++ )
  808. {
  809. voxelMin[axis] = ( int )( ( luxelPt[axis] - radius ) * ooVoxelSize );
  810. voxelMax[axis] = ( int )( ( luxelPt[axis] + radius ) * ooVoxelSize ) + 1;
  811. }
  812. SampleData_t sampleData;
  813. for( int ndxZ = voxelMin[2]; ndxZ < voxelMax[2] + 1; ndxZ++ )
  814. {
  815. for( int ndxY = voxelMin[1]; ndxY < voxelMax[1] + 1; ndxY++ )
  816. {
  817. for( int ndxX = voxelMin[0]; ndxX < voxelMax[0] + 1; ndxX++ )
  818. {
  819. sampleData.x = ndxX * 100;
  820. sampleData.y = ndxY * 10;
  821. sampleData.z = ndxZ;
  822. UtlHashHandle_t handle = g_SampleHashTable.Find( sampleData );
  823. if( handle != g_SampleHashTable.InvalidHandle() )
  824. {
  825. SampleData_t *pSampleData = &g_SampleHashTable.Element( handle );
  826. int count = pSampleData->m_Samples.Count();
  827. for( int ndx = 0; ndx < count; ndx++ )
  828. {
  829. SampleHandle_t sampleHandle = pSampleData->m_Samples.Element( ndx );
  830. int ndxSample = ( sampleHandle & 0x0000ffff );
  831. int ndxFaceLight = ( ( sampleHandle >> 16 ) & 0x0000ffff );
  832. facelight_t *pFaceLight = &facelight[ndxFaceLight];
  833. if( pFaceLight && IsNeighbor( ndxFace, ndxFaceLight ) )
  834. {
  835. //
  836. // check for similar lightstyles
  837. //
  838. dface_t *pFace = &g_pFaces[ndxFaceLight];
  839. if( pFace )
  840. {
  841. int ndxNeighborStyle = -1;
  842. for( int ndxLightStyle = 0; ndxLightStyle < MAXLIGHTMAPS; ndxLightStyle++ )
  843. {
  844. if( pFace->styles[ndxLightStyle] == lightStyle )
  845. {
  846. ndxNeighborStyle = ndxLightStyle;
  847. break;
  848. }
  849. }
  850. if( ndxNeighborStyle == -1 )
  851. continue;
  852. // is this surface bumped???
  853. bool bNeighborBump = texinfo[pFace->texinfo].flags & SURF_BUMPLIGHT ? true : false;
  854. LightingValue_t sampleLight[NUM_BUMP_VECTS+1];
  855. GetSampleLight( pFaceLight, ndxNeighborStyle, bNeighborBump, ndxSample, sampleLight );
  856. AddSampleLightToRadial( pFaceLight->sample[ndxSample].pos, pFaceLight->sample[ndxSample].normal,
  857. sampleLight, radius*radius, luxelPt, luxelNormal, pRadial, ndxRadial,
  858. bBump, bNeighborBump );
  859. }
  860. }
  861. }
  862. }
  863. }
  864. }
  865. }
  866. }
  867. //-----------------------------------------------------------------------------
  868. //-----------------------------------------------------------------------------
  869. void CVRadDispMgr::RadialLuxelBuild( CVRADDispColl *pDispTree, radial_t *pRadial,
  870. int ndxStyle, bool bBump )
  871. {
  872. //
  873. // get data lighting data
  874. //
  875. int ndxFace = pDispTree->GetParentIndex();
  876. dface_t *pFace = &g_pFaces[ndxFace];
  877. facelight_t *pFaceLight = &facelight[ndxFace];
  878. // get the influence radius
  879. float radius2 = pDispTree->GetSampleRadius2();
  880. float radius = ( float )sqrt( radius2 );
  881. int radialSize = pRadial->w * pRadial->h;
  882. for( int ndxRadial = 0; ndxRadial < radialSize; ndxRadial++ )
  883. {
  884. RadialLuxelAddSamples( ndxFace, pFaceLight->luxel[ndxRadial], pFaceLight->luxelNormals[ndxRadial],
  885. radius, pRadial, ndxRadial, bBump, pFace->styles[ndxStyle] );
  886. }
  887. }
  888. //-----------------------------------------------------------------------------
  889. //-----------------------------------------------------------------------------
  890. radial_t *CVRadDispMgr::BuildLuxelRadial( int ndxFace, int ndxStyle, bool bBump )
  891. {
  892. // allocate the radial
  893. radial_t *pRadial = AllocateRadial( ndxFace );
  894. if( !pRadial )
  895. return NULL;
  896. //
  897. // step 1: get the displacement surface to be lit
  898. //
  899. DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
  900. CVRADDispColl *pDispTree = dispTree.m_pDispTree;
  901. if( !pDispTree )
  902. return NULL;
  903. // step 2: build radial luxels
  904. RadialLuxelBuild( pDispTree, pRadial, ndxStyle, bBump );
  905. // step 3: return the built radial
  906. return pRadial;
  907. }
  908. //-----------------------------------------------------------------------------
  909. //-----------------------------------------------------------------------------
  910. bool CVRadDispMgr::SampleRadial( int ndxFace, radial_t *pRadial, Vector const &vPos, int ndxLxl,
  911. LightingValue_t *pLightSample, int sampleCount, bool bPatch )
  912. {
  913. bool bGoodSample = true;
  914. for ( int count = 0; count < sampleCount; count++ )
  915. {
  916. pLightSample[count].Zero();
  917. if ( pRadial->weight[ndxLxl] > 0.0f )
  918. {
  919. pLightSample[count].AddWeighted( pRadial->light[count][ndxLxl], ( 1.0f / pRadial->weight[ndxLxl] ) );
  920. }
  921. else
  922. {
  923. // error, luxel has no samples (not for patches)
  924. if ( !bPatch )
  925. {
  926. // Yes, 2550 is correct!
  927. // pLightSample[count].Init( 2550.0f, 0.0f, 2550.0f );
  928. if( count == 0 )
  929. bGoodSample = false;
  930. }
  931. }
  932. }
  933. return bGoodSample;
  934. }
  935. //-----------------------------------------------------------------------------
  936. //-----------------------------------------------------------------------------
  937. void GetPatchLight( CPatch *pPatch, bool bBump, Vector *pPatchLight )
  938. {
  939. VectorCopy( pPatch->totallight.light[0], pPatchLight[0] );
  940. if( bBump )
  941. {
  942. for( int ndxBump = 1; ndxBump < ( NUM_BUMP_VECTS + 1 ); ndxBump++ )
  943. {
  944. VectorCopy( pPatch->totallight.light[ndxBump], pPatchLight[ndxBump] );
  945. }
  946. }
  947. }
  948. extern void GetBumpNormals( const float* sVect, const float* tVect, const Vector& flatNormal,
  949. const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] );
  950. extern void PreGetBumpNormalsForDisp( texinfo_t *pTexinfo, Vector &vecU, Vector &vecV, Vector &vecNormal );
  951. //-----------------------------------------------------------------------------
  952. //-----------------------------------------------------------------------------
  953. void AddPatchLightToRadial( Vector const &patchOrigin, Vector const &patchNormal,
  954. Vector *pPatchLight, float patchRadius2,
  955. Vector const &luxelPos, Vector const &luxelNormal,
  956. radial_t *pRadial, int ndxRadial, bool bBump,
  957. bool bNeighborBump )
  958. {
  959. // calculate the light vector
  960. Vector vSegment = patchOrigin - luxelPos;
  961. // get the distance to the light
  962. float dist = vSegment.Length();
  963. float dist2 = dist * dist;
  964. // Check to see if the light is within the sample influence.
  965. float influence = 1.0f - ( dist2 / ( patchRadius2 ) );
  966. if ( influence <= 0.0f )
  967. return;
  968. if( bBump )
  969. {
  970. Vector normals[NUM_BUMP_VECTS+1];
  971. normals[0] = luxelNormal;
  972. texinfo_t *pTexinfo = &texinfo[g_pFaces[pRadial->facenum].texinfo];
  973. Vector vecTexU, vecTexV;
  974. PreGetBumpNormalsForDisp( pTexinfo, vecTexU, vecTexV, normals[0] );
  975. GetBumpNormals( vecTexU, vecTexV, normals[0], normals[0], &normals[1] );
  976. if( bNeighborBump )
  977. {
  978. float flScale = patchNormal.Dot( normals[0] );
  979. flScale = max( 0.0f, flScale );
  980. float flBumpInfluence = influence * flScale;
  981. for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
  982. {
  983. pRadial->light[ndxBump][ndxRadial].AddWeighted( pPatchLight[ndxBump], flBumpInfluence );
  984. }
  985. pRadial->weight[ndxRadial] += flBumpInfluence;
  986. }
  987. else
  988. {
  989. float flScale = patchNormal.Dot( normals[0] );
  990. flScale = max( 0.0f, flScale );
  991. float flBumpInfluence = influence * flScale * 0.05f;
  992. for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
  993. {
  994. pRadial->light[ndxBump][ndxRadial].AddWeighted( pPatchLight[0], flBumpInfluence );
  995. }
  996. pRadial->weight[ndxRadial] += flBumpInfluence;
  997. }
  998. }
  999. else
  1000. {
  1001. float flScale = patchNormal.Dot( luxelNormal );
  1002. flScale = max( 0.0f, flScale );
  1003. influence *= flScale;
  1004. pRadial->light[0][ndxRadial].AddWeighted( pPatchLight[0], influence );
  1005. // add the weight value
  1006. pRadial->weight[ndxRadial] += influence;
  1007. }
  1008. }
  1009. //-----------------------------------------------------------------------------
  1010. //-----------------------------------------------------------------------------
  1011. void CVRadDispMgr::RadialLuxelAddPatch( int ndxFace, Vector const &luxelPt,
  1012. Vector const &luxelNormal, float radius,
  1013. radial_t *pRadial, int ndxRadial, bool bBump,
  1014. CUtlVector<CPatch*> &interestingPatches )
  1015. {
  1016. #ifdef SAMPLEHASH_QUERY_ONCE
  1017. for ( int i=0; i < interestingPatches.Count(); i++ )
  1018. {
  1019. CPatch *pPatch = interestingPatches[i];
  1020. bool bNeighborBump = texinfo[g_pFaces[pPatch->faceNumber].texinfo].flags & SURF_BUMPLIGHT ? true : false;
  1021. Vector patchLight[NUM_BUMP_VECTS+1];
  1022. GetPatchLight( pPatch, bBump, patchLight );
  1023. AddPatchLightToRadial( pPatch->origin, pPatch->normal, patchLight, radius*radius,
  1024. luxelPt, luxelNormal, pRadial, ndxRadial, bBump, bNeighborBump );
  1025. }
  1026. #else
  1027. // calculate one over the voxel size
  1028. float ooVoxelSize = 1.0f / SAMPLEHASH_VOXEL_SIZE;
  1029. //
  1030. // find voxel info
  1031. //
  1032. int voxelMin[3], voxelMax[3];
  1033. for ( int axis = 0; axis < 3; axis++ )
  1034. {
  1035. voxelMin[axis] = ( int )( ( luxelPt[axis] - radius ) * ooVoxelSize );
  1036. voxelMax[axis] = ( int )( ( luxelPt[axis] + radius ) * ooVoxelSize ) + 1;
  1037. }
  1038. unsigned short curIterationKey = IncrementPatchIterationKey();
  1039. PatchSampleData_t patchData;
  1040. for ( int ndxZ = voxelMin[2]; ndxZ < voxelMax[2] + 1; ndxZ++ )
  1041. {
  1042. for ( int ndxY = voxelMin[1]; ndxY < voxelMax[1] + 1; ndxY++ )
  1043. {
  1044. for ( int ndxX = voxelMin[0]; ndxX < voxelMax[0] + 1; ndxX++ )
  1045. {
  1046. patchData.x = ndxX * 100;
  1047. patchData.y = ndxY * 10;
  1048. patchData.z = ndxZ;
  1049. UtlHashHandle_t handle = g_PatchSampleHashTable.Find( patchData );
  1050. if ( handle != g_PatchSampleHashTable.InvalidHandle() )
  1051. {
  1052. PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element( handle );
  1053. int count = pPatchData->m_ndxPatches.Count();
  1054. for ( int ndx = 0; ndx < count; ndx++ )
  1055. {
  1056. int ndxPatch = pPatchData->m_ndxPatches.Element( ndx );
  1057. CPatch *pPatch = &g_Patches.Element( ndxPatch );
  1058. if ( pPatch && pPatch->m_IterationKey != curIterationKey )
  1059. {
  1060. pPatch->m_IterationKey = curIterationKey;
  1061. if ( IsNeighbor( ndxFace, pPatch->faceNumber ) )
  1062. {
  1063. bool bNeighborBump = texinfo[g_pFaces[pPatch->faceNumber].texinfo].flags & SURF_BUMPLIGHT ? true : false;
  1064. Vector patchLight[NUM_BUMP_VECTS+1];
  1065. GetPatchLight( pPatch, bBump, patchLight );
  1066. AddPatchLightToRadial( pPatch->origin, pPatch->normal, patchLight, radius*radius,
  1067. luxelPt, luxelNormal, pRadial, ndxRadial, bBump, bNeighborBump );
  1068. }
  1069. }
  1070. }
  1071. }
  1072. }
  1073. }
  1074. }
  1075. #endif
  1076. }
  1077. void CVRadDispMgr::GetInterestingPatchesForLuxels(
  1078. int ndxFace,
  1079. CUtlVector<CPatch*> &interestingPatches,
  1080. float patchSampleRadius )
  1081. {
  1082. facelight_t *pFaceLight = &facelight[ndxFace];
  1083. // Get the max bounds of all voxels that these luxels touch.
  1084. Vector vLuxelMin( FLT_MAX, FLT_MAX, FLT_MAX );
  1085. Vector vLuxelMax( -FLT_MAX, -FLT_MAX, -FLT_MAX );
  1086. for ( int i=0; i < pFaceLight->numluxels; i++ )
  1087. {
  1088. VectorMin( pFaceLight->luxel[i], vLuxelMin, vLuxelMin );
  1089. VectorMax( pFaceLight->luxel[i], vLuxelMax, vLuxelMax );
  1090. }
  1091. int allVoxelMin[3], allVoxelMax[3];
  1092. for ( int axis = 0; axis < 3; axis++ )
  1093. {
  1094. allVoxelMin[axis] = ( int )( ( vLuxelMin[axis] - patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE );
  1095. allVoxelMax[axis] = ( int )( ( vLuxelMax[axis] + patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE ) + 1;
  1096. }
  1097. int allVoxelSize[3] = { allVoxelMax[0] - allVoxelMin[0], allVoxelMax[1] - allVoxelMin[1], allVoxelMax[2] - allVoxelMin[2] };
  1098. // Now figure out exactly which voxels these luxels touch.
  1099. CUtlVector<unsigned char> voxelBits;
  1100. voxelBits.SetSize( ((allVoxelSize[0] * allVoxelSize[1] * allVoxelSize[2]) + 7) / 8 );
  1101. memset( voxelBits.Base(), 0, voxelBits.Count() );
  1102. for ( int i=0; i < pFaceLight->numluxels; i++ )
  1103. {
  1104. int voxelMin[3], voxelMax[3];
  1105. for ( int axis=0; axis < 3; axis++ )
  1106. {
  1107. voxelMin[axis] = ( int )( ( pFaceLight->luxel[i][axis] - patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE );
  1108. voxelMax[axis] = ( int )( ( pFaceLight->luxel[i][axis] + patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE ) + 1;
  1109. }
  1110. for ( int x=voxelMin[0]; x < voxelMax[0]; x++ )
  1111. {
  1112. for ( int y=voxelMin[1]; y < voxelMax[1]; y++ )
  1113. {
  1114. for ( int z=voxelMin[2]; z < voxelMax[2]; z++ )
  1115. {
  1116. int iBit = (z - allVoxelMin[2])*(allVoxelSize[0]*allVoxelSize[1]) +
  1117. (y-allVoxelMin[1])*allVoxelSize[0] +
  1118. (x-allVoxelMin[0]);
  1119. voxelBits[iBit>>3] |= (1 << (iBit & 7));
  1120. }
  1121. }
  1122. }
  1123. }
  1124. // Now get the list of patches that touch those voxels.
  1125. unsigned short curIterationKey = IncrementPatchIterationKey();
  1126. for ( int x=0; x < allVoxelSize[0]; x++ )
  1127. {
  1128. for ( int y=0; y < allVoxelSize[1]; y++ )
  1129. {
  1130. for ( int z=0; z < allVoxelSize[2]; z++ )
  1131. {
  1132. // Make sure this voxel has any luxels that care about it.
  1133. int iBit = z*(allVoxelSize[0]*allVoxelSize[1]) + y*allVoxelSize[0] + x;
  1134. unsigned char val = voxelBits[iBit>>3] & (1 << (iBit & 7));
  1135. if ( !val )
  1136. continue;
  1137. PatchSampleData_t patchData;
  1138. patchData.x = (x + allVoxelMin[0]) * 100;
  1139. patchData.y = (y + allVoxelMin[1]) * 10;
  1140. patchData.z = (z + allVoxelMin[2]);
  1141. UtlHashHandle_t handle = g_PatchSampleHashTable.Find( patchData );
  1142. if ( handle != g_PatchSampleHashTable.InvalidHandle() )
  1143. {
  1144. PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element( handle );
  1145. // For all patches that touch this hash table element..
  1146. for ( int ndx = 0; ndx < pPatchData->m_ndxPatches.Count(); ndx++ )
  1147. {
  1148. int ndxPatch = pPatchData->m_ndxPatches.Element( ndx );
  1149. CPatch *pPatch = &g_Patches.Element( ndxPatch );
  1150. // If we haven't touched the patch already and it's a valid neighbor, then we want to use it.
  1151. if ( pPatch && pPatch->m_IterationKey != curIterationKey )
  1152. {
  1153. pPatch->m_IterationKey = curIterationKey;
  1154. if ( IsNeighbor( ndxFace, pPatch->faceNumber, g_bLargeDispSampleRadius ) )
  1155. {
  1156. interestingPatches.AddToTail( pPatch );
  1157. }
  1158. }
  1159. }
  1160. }
  1161. }
  1162. }
  1163. }
  1164. }
  1165. //-----------------------------------------------------------------------------
  1166. //-----------------------------------------------------------------------------
  1167. void CVRadDispMgr::RadialPatchBuild( CVRADDispColl *pDispTree, radial_t *pRadial,
  1168. bool bBump )
  1169. {
  1170. //
  1171. // get data lighting data
  1172. //
  1173. int ndxFace = pDispTree->GetParentIndex();
  1174. facelight_t *pFaceLight = &facelight[ndxFace];
  1175. // get the influence radius
  1176. float radius2 = pDispTree->GetPatchSampleRadius2();
  1177. float radius = ( float )sqrt( radius2 );
  1178. CUtlVector<CPatch*> interestingPatches;
  1179. #ifdef SAMPLEHASH_QUERY_ONCE
  1180. GetInterestingPatchesForLuxels( ndxFace, interestingPatches, radius );
  1181. #endif
  1182. int radialSize = pRadial->w * pRadial->h;
  1183. for( int ndxRadial = 0; ndxRadial < radialSize; ndxRadial++ )
  1184. {
  1185. RadialLuxelAddPatch(
  1186. ndxFace,
  1187. pFaceLight->luxel[ndxRadial],
  1188. pFaceLight->luxelNormals[ndxRadial],
  1189. radius,
  1190. pRadial,
  1191. ndxRadial,
  1192. bBump,
  1193. interestingPatches );
  1194. }
  1195. }
  1196. //-----------------------------------------------------------------------------
  1197. //-----------------------------------------------------------------------------
  1198. radial_t *CVRadDispMgr::BuildPatchRadial( int ndxFace, bool bBump )
  1199. {
  1200. // allocate the radial
  1201. radial_t *pRadial = AllocateRadial( ndxFace );
  1202. if( !pRadial )
  1203. return NULL;
  1204. //
  1205. // step 1: get the displacement surface to be lit
  1206. //
  1207. DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
  1208. CVRADDispColl *pDispTree = dispTree.m_pDispTree;
  1209. if( !pDispTree )
  1210. return NULL;
  1211. // step 2: build radial of patch light
  1212. RadialPatchBuild( pDispTree, pRadial, bBump );
  1213. // step 3: return the built radial
  1214. return pRadial;
  1215. }
  1216. //-----------------------------------------------------------------------------
  1217. //-----------------------------------------------------------------------------
  1218. bool SampleInSolid( sample_t *pSample )
  1219. {
  1220. int ndxLeaf = PointLeafnum( pSample->pos );
  1221. return ( dleafs[ndxLeaf].contents == CONTENTS_SOLID );
  1222. }
  1223. //-----------------------------------------------------------------------------
  1224. //-----------------------------------------------------------------------------
  1225. void CVRadDispMgr::InsertSamplesDataIntoHashTable( void )
  1226. {
  1227. int totalSamples = 0;
  1228. #if 0
  1229. int totalSamplesInSolid = 0;
  1230. #endif
  1231. for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
  1232. {
  1233. dface_t *pFace = &g_pFaces[ndxFace];
  1234. facelight_t *pFaceLight = &facelight[ndxFace];
  1235. if( !pFace || !pFaceLight )
  1236. continue;
  1237. if( texinfo[pFace->texinfo].flags & TEX_SPECIAL )
  1238. continue;
  1239. #if 0
  1240. bool bDisp = ( pFace->dispinfo != -1 );
  1241. #endif
  1242. //
  1243. // for each sample
  1244. //
  1245. for( int ndxSample = 0; ndxSample < pFaceLight->numsamples; ndxSample++ )
  1246. {
  1247. sample_t *pSample = &pFaceLight->sample[ndxSample];
  1248. if( pSample )
  1249. {
  1250. #if 0
  1251. if( bDisp )
  1252. {
  1253. // test sample to see if the displacement samples resides in solid
  1254. if( SampleInSolid( pSample ) )
  1255. {
  1256. totalSamplesInSolid++;
  1257. continue;
  1258. }
  1259. }
  1260. #endif
  1261. // create the sample handle
  1262. SampleHandle_t sampleHandle = ndxSample;
  1263. sampleHandle |= ( ndxFace << 16 );
  1264. SampleData_AddSample( pSample, sampleHandle );
  1265. }
  1266. }
  1267. totalSamples += pFaceLight->numsamples;
  1268. }
  1269. #if 0
  1270. // not implemented yet!!!
  1271. Msg( "%d samples in solid\n", totalSamplesInSolid );
  1272. #endif
  1273. // log the distribution
  1274. SampleData_Log();
  1275. }
  1276. //-----------------------------------------------------------------------------
  1277. //-----------------------------------------------------------------------------
  1278. void CVRadDispMgr::InsertPatchSampleDataIntoHashTable( void )
  1279. {
  1280. // don't insert patch samples if we are not bouncing light
  1281. if( numbounce <= 0 )
  1282. return;
  1283. int totalPatchSamples = 0;
  1284. for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
  1285. {
  1286. dface_t *pFace = &g_pFaces[ndxFace];
  1287. facelight_t *pFaceLight = &facelight[ndxFace];
  1288. if( !pFace || !pFaceLight )
  1289. continue;
  1290. if( texinfo[pFace->texinfo].flags & TEX_SPECIAL )
  1291. continue;
  1292. //
  1293. // for each patch
  1294. //
  1295. CPatch *pNextPatch = NULL;
  1296. if( g_FacePatches.Element( ndxFace ) != g_FacePatches.InvalidIndex() )
  1297. {
  1298. for( CPatch *pPatch = &g_Patches.Element( g_FacePatches.Element( ndxFace ) ); pPatch; pPatch = pNextPatch )
  1299. {
  1300. // next patch
  1301. pNextPatch = NULL;
  1302. if( pPatch->ndxNext != g_Patches.InvalidIndex() )
  1303. {
  1304. pNextPatch = &g_Patches.Element( pPatch->ndxNext );
  1305. }
  1306. // skip patches with children
  1307. if( pPatch->child1 != g_Patches.InvalidIndex() )
  1308. continue;
  1309. int ndxPatch = pPatch - g_Patches.Base();
  1310. PatchSampleData_AddSample( pPatch, ndxPatch );
  1311. totalPatchSamples++;
  1312. }
  1313. }
  1314. }
  1315. }
  1316. //-----------------------------------------------------------------------------
  1317. //-----------------------------------------------------------------------------
  1318. void CVRadDispMgr::StartTimer( const char *name )
  1319. {
  1320. Msg( name );
  1321. m_Timer.Start();
  1322. }
  1323. //-----------------------------------------------------------------------------
  1324. //-----------------------------------------------------------------------------
  1325. void CVRadDispMgr::EndTimer( void )
  1326. {
  1327. m_Timer.End();
  1328. CCycleCount duration = m_Timer.GetDuration();
  1329. double seconds = duration.GetSeconds();
  1330. Msg( "Done<%1.4lf sec>\n", seconds );
  1331. }
  1332. //-----------------------------------------------------------------------------
  1333. //-----------------------------------------------------------------------------
  1334. bool CVRadDispMgr::BuildDispSamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
  1335. {
  1336. // get the tree assosciated with the face
  1337. DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
  1338. CVRADDispColl *pDispTree = dispTree.m_pDispTree;
  1339. if( !pDispTree )
  1340. return false;
  1341. // lightmap size
  1342. int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
  1343. int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
  1344. // calculate the steps in uv space
  1345. float stepU = 1.0f / ( float )width;
  1346. float stepV = 1.0f / ( float )height;
  1347. float halfStepU = stepU * 0.5f;
  1348. float halfStepV = stepV * 0.5f;
  1349. //
  1350. // build the winding points (used to generate world space winding and
  1351. // calculate the area of the "sample")
  1352. //
  1353. int ndxU, ndxV;
  1354. CUtlVector<sample_t> samples;
  1355. samples.SetCount( SINGLEMAP );
  1356. sample_t *pSamples = samples.Base();
  1357. CUtlVector<Vector> worldPoints;
  1358. worldPoints.SetCount( SINGLEMAP );
  1359. Vector *pWorldPoints = worldPoints.Base();
  1360. for( ndxV = 0; ndxV < ( height + 1 ); ndxV++ )
  1361. {
  1362. for( ndxU = 0; ndxU < ( width + 1 ); ndxU++ )
  1363. {
  1364. int ndx = ( ndxV * ( width + 1 ) ) + ndxU;
  1365. Vector2D uv( ndxU * stepU, ndxV * stepV );
  1366. pDispTree->DispUVToSurfPoint( uv, pWorldPoints[ndx], 0.0f );
  1367. }
  1368. }
  1369. for( ndxV = 0; ndxV < height; ndxV++ )
  1370. {
  1371. for( ndxU = 0; ndxU < width; ndxU++ )
  1372. {
  1373. // build the winding
  1374. winding_t *pWinding = AllocWinding( 4 );
  1375. if( pWinding )
  1376. {
  1377. pWinding->numpoints = 4;
  1378. pWinding->p[0] = pWorldPoints[(ndxV*(width+1))+ndxU];
  1379. pWinding->p[1] = pWorldPoints[((ndxV+1)*(width+1))+ndxU];
  1380. pWinding->p[2] = pWorldPoints[((ndxV+1)*(width+1))+(ndxU+1)];
  1381. pWinding->p[3] = pWorldPoints[(ndxV*(width+1))+(ndxU+1)];
  1382. // calculate the area
  1383. float area = WindingArea( pWinding );
  1384. int ndxSample = ( ndxV * width ) + ndxU;
  1385. pSamples[ndxSample].w = pWinding;
  1386. pSamples[ndxSample].area = area;
  1387. }
  1388. else
  1389. {
  1390. Msg( "BuildDispSamples: WARNING - failed winding allocation\n" );
  1391. }
  1392. }
  1393. }
  1394. //
  1395. // build the samples points (based on s, t and sampleoffset (center of samples);
  1396. // generates world space position and normal)
  1397. //
  1398. for( ndxV = 0; ndxV < height; ndxV++ )
  1399. {
  1400. for( ndxU = 0; ndxU < width; ndxU++ )
  1401. {
  1402. int ndxSample = ( ndxV * width ) + ndxU;
  1403. pSamples[ndxSample].s = ndxU;
  1404. pSamples[ndxSample].t = ndxV;
  1405. pSamples[ndxSample].coord[0] = ( ndxU * stepU ) + halfStepU;
  1406. pSamples[ndxSample].coord[1] = ( ndxV * stepV ) + halfStepV;
  1407. pDispTree->DispUVToSurfPoint( pSamples[ndxSample].coord, pSamples[ndxSample].pos, 1.0f );
  1408. pDispTree->DispUVToSurfNormal( pSamples[ndxSample].coord, pSamples[ndxSample].normal );
  1409. }
  1410. }
  1411. //
  1412. // copy over samples
  1413. //
  1414. pFaceLight->numsamples = width * height;
  1415. pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
  1416. if( !pFaceLight->sample )
  1417. return false;
  1418. memcpy( pFaceLight->sample, pSamples, pFaceLight->numsamples * sizeof( *pFaceLight->sample ) );
  1419. // statistics - warning?!
  1420. if( pFaceLight->numsamples == 0 )
  1421. {
  1422. Msg( "BuildDispSamples: WARNING - no samples %d\n", pLightInfo->face - g_pFaces );
  1423. }
  1424. return true;
  1425. }
  1426. //-----------------------------------------------------------------------------
  1427. //-----------------------------------------------------------------------------
  1428. bool CVRadDispMgr::BuildDispLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
  1429. {
  1430. // get the tree assosciated with the face
  1431. DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
  1432. CVRADDispColl *pDispTree = dispTree.m_pDispTree;
  1433. if( !pDispTree )
  1434. return false;
  1435. // lightmap size
  1436. int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
  1437. int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
  1438. // calcuate actual luxel points
  1439. pFaceLight->numluxels = width * height;
  1440. pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
  1441. pFaceLight->luxelNormals = ( Vector* )calloc( pFaceLight->numluxels, sizeof( Vector ) );
  1442. if( !pFaceLight->luxel || !pFaceLight->luxelNormals )
  1443. return false;
  1444. float stepU = 1.0f / ( float )( width - 1 );
  1445. float stepV = 1.0f / ( float )( height - 1 );
  1446. for( int ndxV = 0; ndxV < height; ndxV++ )
  1447. {
  1448. for( int ndxU = 0; ndxU < width; ndxU++ )
  1449. {
  1450. int ndxLuxel = ( ndxV * width ) + ndxU;
  1451. Vector2D uv( ndxU * stepU, ndxV * stepV );
  1452. pDispTree->DispUVToSurfPoint( uv, pFaceLight->luxel[ndxLuxel], 1.0f );
  1453. pDispTree->DispUVToSurfNormal( uv, pFaceLight->luxelNormals[ndxLuxel] );
  1454. }
  1455. }
  1456. return true;
  1457. }
  1458. //-----------------------------------------------------------------------------
  1459. //-----------------------------------------------------------------------------
  1460. bool CVRadDispMgr::BuildDispSamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
  1461. {
  1462. // get the tree assosciated with the face
  1463. DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
  1464. CVRADDispColl *pDispTree = dispTree.m_pDispTree;
  1465. if( !pDispTree )
  1466. return false;
  1467. // lightmap size
  1468. int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
  1469. int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
  1470. // calcuate actual luxel points
  1471. pFaceLight->numsamples = width * height;
  1472. pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
  1473. if( !pFaceLight->sample )
  1474. return false;
  1475. pFaceLight->numluxels = width * height;
  1476. pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
  1477. pFaceLight->luxelNormals = ( Vector* )calloc( pFaceLight->numluxels, sizeof( Vector ) );
  1478. if( !pFaceLight->luxel || !pFaceLight->luxelNormals )
  1479. return false;
  1480. float stepU = 1.0f / ( float )( width - 1 );
  1481. float stepV = 1.0f / ( float )( height - 1 );
  1482. float halfStepU = stepU * 0.5f;
  1483. float halfStepV = stepV * 0.5f;
  1484. for( int ndxV = 0; ndxV < height; ndxV++ )
  1485. {
  1486. for( int ndxU = 0; ndxU < width; ndxU++ )
  1487. {
  1488. int ndx = ( ndxV * width ) + ndxU;
  1489. pFaceLight->sample[ndx].s = ndxU;
  1490. pFaceLight->sample[ndx].t = ndxV;
  1491. pFaceLight->sample[ndx].coord[0] = ( ndxU * stepU ) + halfStepU;
  1492. pFaceLight->sample[ndx].coord[1] = ( ndxV * stepV ) + halfStepV;
  1493. pDispTree->DispUVToSurfPoint( pFaceLight->sample[ndx].coord, pFaceLight->sample[ndx].pos, 1.0f );
  1494. pDispTree->DispUVToSurfNormal( pFaceLight->sample[ndx].coord, pFaceLight->sample[ndx].normal );
  1495. pFaceLight->luxel[ndx] = pFaceLight->sample[ndx].pos;
  1496. pFaceLight->luxelNormals[ndx] = pFaceLight->sample[ndx].normal;
  1497. }
  1498. }
  1499. return true;
  1500. }