aboutsummaryrefslogtreecommitdiff
path: root/mp/src/utils/vrad
diff options
context:
space:
mode:
authorNarendra Umate <[email protected]>2013-12-02 23:36:05 -0800
committerNarendra Umate <[email protected]>2013-12-02 23:36:05 -0800
commit8737f191f3b59f001a77bf6c08091109211c1c9f (patch)
treedbbf05c004d9b026f2c1f23f06600fe0add82c36 /mp/src/utils/vrad
parentUpdate .gitignore. (diff)
parentMake .xcconfigs text files too. (diff)
downloadsource-sdk-2013-8737f191f3b59f001a77bf6c08091109211c1c9f.tar.xz
source-sdk-2013-8737f191f3b59f001a77bf6c08091109211c1c9f.zip
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'mp/src/utils/vrad')
-rw-r--r--mp/src/utils/vrad/disp_vrad.cpp664
-rw-r--r--mp/src/utils/vrad/disp_vrad.h44
-rw-r--r--mp/src/utils/vrad/iincremental.h142
-rw-r--r--mp/src/utils/vrad/imagepacker.cpp282
-rw-r--r--mp/src/utils/vrad/imagepacker.h102
-rw-r--r--mp/src/utils/vrad/incremental.cpp1532
-rw-r--r--mp/src/utils/vrad/incremental.h350
-rw-r--r--mp/src/utils/vrad/leaf_ambient_lighting.cpp1416
-rw-r--r--mp/src/utils/vrad/leaf_ambient_lighting.h34
-rw-r--r--mp/src/utils/vrad/lightmap.cpp7154
-rw-r--r--mp/src/utils/vrad/lightmap.h282
-rw-r--r--mp/src/utils/vrad/macro_texture.cpp332
-rw-r--r--mp/src/utils/vrad/macro_texture.h48
-rw-r--r--mp/src/utils/vrad/mpivrad.cpp992
-rw-r--r--mp/src/utils/vrad/mpivrad.h72
-rw-r--r--mp/src/utils/vrad/origface.cpp102
-rw-r--r--mp/src/utils/vrad/radial.cpp1764
-rw-r--r--mp/src/utils/vrad/radial.h152
-rw-r--r--mp/src/utils/vrad/samplehash.cpp460
-rw-r--r--mp/src/utils/vrad/trace.cpp1306
-rw-r--r--mp/src/utils/vrad/vismat.cpp966
-rw-r--r--mp/src/utils/vrad/vismat.h68
-rw-r--r--mp/src/utils/vrad/vrad.cpp5872
-rw-r--r--mp/src/utils/vrad/vrad.h1220
-rw-r--r--mp/src/utils/vrad/vrad_dispcoll.cpp2158
-rw-r--r--mp/src/utils/vrad/vrad_dispcoll.h158
-rw-r--r--mp/src/utils/vrad/vrad_dll.vpc450
-rw-r--r--mp/src/utils/vrad/vraddetailprops.cpp2070
-rw-r--r--mp/src/utils/vrad/vraddetailprops.h68
-rw-r--r--mp/src/utils/vrad/vraddisps.cpp3518
-rw-r--r--mp/src/utils/vrad/vraddll.cpp486
-rw-r--r--mp/src/utils/vrad/vraddll.h68
-rw-r--r--mp/src/utils/vrad/vradstaticprops.cpp3830
33 files changed, 19081 insertions, 19081 deletions
diff --git a/mp/src/utils/vrad/disp_vrad.cpp b/mp/src/utils/vrad/disp_vrad.cpp
index e1ef3a2e..b254b9d7 100644
--- a/mp/src/utils/vrad/disp_vrad.cpp
+++ b/mp/src/utils/vrad/disp_vrad.cpp
@@ -1,332 +1,332 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "disp_vrad.h"
-#include "utllinkedlist.h"
-#include "utlvector.h"
-#include "iscratchpad3d.h"
-#include "scratchpadutils.h"
-
-
-//#define USE_SCRATCHPAD
-#if defined( USE_SCRATCHPAD )
- static IScratchPad3D *g_pPad = 0;
-#endif
-
-
-int FindNeighborCornerVert( CCoreDispInfo *pDisp, const Vector &vTest )
-{
- CDispUtilsHelper *pDispHelper = pDisp;
-
- int iClosest = 0;
- float flClosest = 1e24;
- for ( int iCorner=0; iCorner < 4; iCorner++ )
- {
- // Has it been touched?
- CVertIndex cornerVert = pDispHelper->GetPowerInfo()->GetCornerPointIndex( iCorner );
- int iCornerVert = pDispHelper->VertIndexToInt( cornerVert );
- const Vector &vCornerVert = pDisp->GetVert( iCornerVert );
-
- float flDist = vCornerVert.DistTo( vTest );
- if ( flDist < flClosest )
- {
- iClosest = iCorner;
- flClosest = flDist;
- }
- }
-
- if ( flClosest <= 0.1f )
- return iClosest;
- else
- return -1;
-}
-
-
-int GetAllNeighbors( const CCoreDispInfo *pDisp, int iNeighbors[512] )
-{
- int nNeighbors = 0;
-
- // Check corner neighbors.
- for ( int iCorner=0; iCorner < 4; iCorner++ )
- {
- const CDispCornerNeighbors *pCorner = pDisp->GetCornerNeighbors( iCorner );
-
- for ( int i=0; i < pCorner->m_nNeighbors; i++ )
- {
- if ( nNeighbors < _ARRAYSIZE( iNeighbors ) )
- iNeighbors[nNeighbors++] = pCorner->m_Neighbors[i];
- }
- }
-
- for ( int iEdge=0; iEdge < 4; iEdge++ )
- {
- const CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge );
-
- for ( int i=0; i < 2; i++ )
- {
- if ( pEdge->m_SubNeighbors[i].IsValid() )
- if ( nNeighbors < 512 )
- iNeighbors[nNeighbors++] = pEdge->m_SubNeighbors[i].GetNeighborIndex();
- }
- }
-
- return nNeighbors;
-}
-
-
-void BlendCorners( CCoreDispInfo **ppListBase, int listSize )
-{
- CUtlVector<int> nbCornerVerts;
-
- for ( int iDisp=0; iDisp < listSize; iDisp++ )
- {
- CCoreDispInfo *pDisp = ppListBase[iDisp];
-
- int iNeighbors[512];
- int nNeighbors = GetAllNeighbors( pDisp, iNeighbors );
-
- // Make sure we have room for all the neighbors.
- nbCornerVerts.RemoveAll();
- nbCornerVerts.EnsureCapacity( nNeighbors );
- nbCornerVerts.AddMultipleToTail( nNeighbors );
-
- // For each corner.
- for ( int iCorner=0; iCorner < 4; iCorner++ )
- {
- // Has it been touched?
- CVertIndex cornerVert = pDisp->GetCornerPointIndex( iCorner );
- int iCornerVert = pDisp->VertIndexToInt( cornerVert );
- const Vector &vCornerVert = pDisp->GetVert( iCornerVert );
-
- // For each displacement sharing this corner..
- Vector vAverage = pDisp->GetNormal( iCornerVert );
-
- for ( int iNeighbor=0; iNeighbor < nNeighbors; iNeighbor++ )
- {
- int iNBListIndex = iNeighbors[iNeighbor];
- CCoreDispInfo *pNeighbor = ppListBase[iNBListIndex];
-
- // Find out which vert it is on the neighbor.
- int iNBCorner = FindNeighborCornerVert( pNeighbor, vCornerVert );
- if ( iNBCorner == -1 )
- {
- nbCornerVerts[iNeighbor] = -1; // remove this neighbor from the list.
- }
- else
- {
- CVertIndex viNBCornerVert = pNeighbor->GetCornerPointIndex( iNBCorner );
- int iNBVert = pNeighbor->VertIndexToInt( viNBCornerVert );
- nbCornerVerts[iNeighbor] = iNBVert;
- vAverage += pNeighbor->GetNormal( iNBVert );
- }
- }
-
-
- // Blend all the neighbor normals with this one.
- VectorNormalize( vAverage );
- pDisp->SetNormal( iCornerVert, vAverage );
-
-#if defined( USE_SCRATCHPAD )
- ScratchPad_DrawArrowSimple(
- g_pPad,
- pDisp->GetVert( iCornerVert ),
- pDisp->GetNormal( iCornerVert ),
- Vector( 0, 0, 1 ),
- 25 );
-#endif
-
- for ( int iNeighbor=0; iNeighbor < nNeighbors; iNeighbor++ )
- {
- int iNBListIndex = iNeighbors[iNeighbor];
- if ( nbCornerVerts[iNeighbor] == -1 )
- continue;
-
- CCoreDispInfo *pNeighbor = ppListBase[iNBListIndex];
- pNeighbor->SetNormal( nbCornerVerts[iNeighbor], vAverage );
- }
- }
- }
-}
-
-
-void BlendTJuncs( CCoreDispInfo **ppListBase, int listSize )
-{
- for ( int iDisp=0; iDisp < listSize; iDisp++ )
- {
- CCoreDispInfo *pDisp = ppListBase[iDisp];
-
- for ( int iEdge=0; iEdge < 4; iEdge++ )
- {
- CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge );
-
- CVertIndex viMidPoint = pDisp->GetEdgeMidPoint( iEdge );
- int iMidPoint = pDisp->VertIndexToInt( viMidPoint );
-
- if ( pEdge->m_SubNeighbors[0].IsValid() && pEdge->m_SubNeighbors[1].IsValid() )
- {
- const Vector &vMidPoint = pDisp->GetVert( iMidPoint );
-
- CCoreDispInfo *pNeighbor1 = ppListBase[pEdge->m_SubNeighbors[0].GetNeighborIndex()];
- CCoreDispInfo *pNeighbor2 = ppListBase[pEdge->m_SubNeighbors[1].GetNeighborIndex()];
-
- int iNBCorners[2];
- iNBCorners[0] = FindNeighborCornerVert( pNeighbor1, vMidPoint );
- iNBCorners[1] = FindNeighborCornerVert( pNeighbor2, vMidPoint );
-
- if ( iNBCorners[0] != -1 && iNBCorners[1] != -1 )
- {
- CVertIndex viNBCorners[2] =
- {
- pNeighbor1->GetCornerPointIndex( iNBCorners[0] ),
- pNeighbor2->GetCornerPointIndex( iNBCorners[1] )
- };
-
- Vector vAverage = pDisp->GetNormal( iMidPoint );
- vAverage += pNeighbor1->GetNormal( viNBCorners[0] );
- vAverage += pNeighbor2->GetNormal( viNBCorners[1] );
-
- VectorNormalize( vAverage );
- pDisp->SetNormal( iMidPoint, vAverage );
- pNeighbor1->SetNormal( viNBCorners[0], vAverage );
- pNeighbor2->SetNormal( viNBCorners[1], vAverage );
-
-#if defined( USE_SCRATCHPAD )
- ScratchPad_DrawArrowSimple( g_pPad, pDisp->GetVert( iMidPoint ), pDisp->GetNormal( iMidPoint ), Vector( 0, 1, 1 ), 25 );
-#endif
- }
- }
- }
- }
-}
-
-
-void BlendEdges( CCoreDispInfo **ppListBase, int listSize )
-{
- for ( int iDisp=0; iDisp < listSize; iDisp++ )
- {
- CCoreDispInfo *pDisp = ppListBase[iDisp];
-
- for ( int iEdge=0; iEdge < 4; iEdge++ )
- {
- CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge );
-
- for ( int iSub=0; iSub < 2; iSub++ )
- {
- CDispSubNeighbor *pSub = &pEdge->m_SubNeighbors[iSub];
- if ( !pSub->IsValid() )
- continue;
-
- CCoreDispInfo *pNeighbor = ppListBase[ pSub->GetNeighborIndex() ];
-
- int iEdgeDim = g_EdgeDims[iEdge];
-
- CDispSubEdgeIterator it;
- it.Start( pDisp, iEdge, iSub, true );
-
- // Get setup on the first corner vert.
- it.Next();
- CVertIndex viPrevPos = it.GetVertIndex();
-
- while ( it.Next() )
- {
- // Blend the two.
- if ( !it.IsLastVert() )
- {
- Vector vAverage = pDisp->GetNormal( it.GetVertIndex() ) + pNeighbor->GetNormal( it.GetNBVertIndex() );
- VectorNormalize( vAverage );
-
- pDisp->SetNormal( it.GetVertIndex(), vAverage );
- pNeighbor->SetNormal( it.GetNBVertIndex(), vAverage );
-
-#if defined( USE_SCRATCHPAD )
- ScratchPad_DrawArrowSimple( g_pPad, pDisp->GetVert( it.GetVertIndex() ), pDisp->GetNormal( it.GetVertIndex() ), Vector( 1, 0, 0 ), 25 );
-#endif
- }
-
- // Now blend the in-between verts (if this edge is high-res).
- int iPrevPos = viPrevPos[ !iEdgeDim ];
- int iCurPos = it.GetVertIndex()[ !iEdgeDim ];
-
- for ( int iTween = iPrevPos+1; iTween < iCurPos; iTween++ )
- {
- float flPercent = RemapVal( iTween, iPrevPos, iCurPos, 0, 1 );
- Vector vNormal;
- VectorLerp( pDisp->GetNormal( viPrevPos ), pDisp->GetNormal( it.GetVertIndex() ), flPercent, vNormal );
- VectorNormalize( vNormal );
-
- CVertIndex viTween;
- viTween[iEdgeDim] = it.GetVertIndex()[ iEdgeDim ];
- viTween[!iEdgeDim] = iTween;
- pDisp->SetNormal( viTween, vNormal );
-
-#if defined( USE_SCRATCHPAD )
- ScratchPad_DrawArrowSimple( g_pPad, pDisp->GetVert( viTween ), pDisp->GetNormal( viTween ), Vector( 1, 0.5, 0 ), 25 );
-#endif
- }
-
- viPrevPos = it.GetVertIndex();
- }
- }
- }
- }
-}
-
-
-#if defined( USE_SCRATCHPAD )
- void ScratchPad_DrawOriginalNormals( const CCoreDispInfo *pListBase, int listSize )
- {
- for ( int i=0; i < listSize; i++ )
- {
- const CCoreDispInfo *pDisp = &pListBase[i];
- const CPowerInfo *pPowerInfo = pDisp->GetPowerInfo();
-
- // Draw the triangles.
- for ( int iTri=0; iTri < pPowerInfo->GetNumTriInfos(); iTri++ )
- {
- const CTriInfo *pTriInfo = pPowerInfo->GetTriInfo( iTri );
-
- for ( int iLine=0; iLine < 3; iLine++ )
- {
- const Vector &v1 = pDisp->GetVert( pTriInfo->m_Indices[iLine] );
- const Vector &v2 = pDisp->GetVert( pTriInfo->m_Indices[(iLine+1)%3] );
-
- g_pPad->DrawLine( CSPVert( v1 ), CSPVert( v2 ) );
- }
- }
-
- // Draw the normals.
- CDispCircumferenceIterator it( pPowerInfo->GetSideLength() );
- while ( it.Next() )
- {
- ScratchPad_DrawArrowSimple(
- g_pPad,
- pDisp->GetVert( it.GetVertIndex() ),
- pDisp->GetNormal( it.GetVertIndex() ),
- Vector( 0, 1, 0 ),
- 15 );
- }
- }
- }
-#endif
-
-
-void SmoothNeighboringDispSurfNormals( CCoreDispInfo **ppListBase, int listSize )
-{
-//#if defined( USE_SCRATCHPAD )
-// g_pPad = ScratchPad3D_Create();
-// ScratchPad_DrawOriginalNormals( pListBase, listSize );
-//#endif
-
- BlendTJuncs( ppListBase, listSize );
-
- BlendCorners( ppListBase, listSize );
-
- BlendEdges( ppListBase, listSize );
-}
-
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "disp_vrad.h"
+#include "utllinkedlist.h"
+#include "utlvector.h"
+#include "iscratchpad3d.h"
+#include "scratchpadutils.h"
+
+
+//#define USE_SCRATCHPAD
+#if defined( USE_SCRATCHPAD )
+ static IScratchPad3D *g_pPad = 0;
+#endif
+
+
+int FindNeighborCornerVert( CCoreDispInfo *pDisp, const Vector &vTest )
+{
+ CDispUtilsHelper *pDispHelper = pDisp;
+
+ int iClosest = 0;
+ float flClosest = 1e24;
+ for ( int iCorner=0; iCorner < 4; iCorner++ )
+ {
+ // Has it been touched?
+ CVertIndex cornerVert = pDispHelper->GetPowerInfo()->GetCornerPointIndex( iCorner );
+ int iCornerVert = pDispHelper->VertIndexToInt( cornerVert );
+ const Vector &vCornerVert = pDisp->GetVert( iCornerVert );
+
+ float flDist = vCornerVert.DistTo( vTest );
+ if ( flDist < flClosest )
+ {
+ iClosest = iCorner;
+ flClosest = flDist;
+ }
+ }
+
+ if ( flClosest <= 0.1f )
+ return iClosest;
+ else
+ return -1;
+}
+
+
+int GetAllNeighbors( const CCoreDispInfo *pDisp, int iNeighbors[512] )
+{
+ int nNeighbors = 0;
+
+ // Check corner neighbors.
+ for ( int iCorner=0; iCorner < 4; iCorner++ )
+ {
+ const CDispCornerNeighbors *pCorner = pDisp->GetCornerNeighbors( iCorner );
+
+ for ( int i=0; i < pCorner->m_nNeighbors; i++ )
+ {
+ if ( nNeighbors < _ARRAYSIZE( iNeighbors ) )
+ iNeighbors[nNeighbors++] = pCorner->m_Neighbors[i];
+ }
+ }
+
+ for ( int iEdge=0; iEdge < 4; iEdge++ )
+ {
+ const CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge );
+
+ for ( int i=0; i < 2; i++ )
+ {
+ if ( pEdge->m_SubNeighbors[i].IsValid() )
+ if ( nNeighbors < 512 )
+ iNeighbors[nNeighbors++] = pEdge->m_SubNeighbors[i].GetNeighborIndex();
+ }
+ }
+
+ return nNeighbors;
+}
+
+
+void BlendCorners( CCoreDispInfo **ppListBase, int listSize )
+{
+ CUtlVector<int> nbCornerVerts;
+
+ for ( int iDisp=0; iDisp < listSize; iDisp++ )
+ {
+ CCoreDispInfo *pDisp = ppListBase[iDisp];
+
+ int iNeighbors[512];
+ int nNeighbors = GetAllNeighbors( pDisp, iNeighbors );
+
+ // Make sure we have room for all the neighbors.
+ nbCornerVerts.RemoveAll();
+ nbCornerVerts.EnsureCapacity( nNeighbors );
+ nbCornerVerts.AddMultipleToTail( nNeighbors );
+
+ // For each corner.
+ for ( int iCorner=0; iCorner < 4; iCorner++ )
+ {
+ // Has it been touched?
+ CVertIndex cornerVert = pDisp->GetCornerPointIndex( iCorner );
+ int iCornerVert = pDisp->VertIndexToInt( cornerVert );
+ const Vector &vCornerVert = pDisp->GetVert( iCornerVert );
+
+ // For each displacement sharing this corner..
+ Vector vAverage = pDisp->GetNormal( iCornerVert );
+
+ for ( int iNeighbor=0; iNeighbor < nNeighbors; iNeighbor++ )
+ {
+ int iNBListIndex = iNeighbors[iNeighbor];
+ CCoreDispInfo *pNeighbor = ppListBase[iNBListIndex];
+
+ // Find out which vert it is on the neighbor.
+ int iNBCorner = FindNeighborCornerVert( pNeighbor, vCornerVert );
+ if ( iNBCorner == -1 )
+ {
+ nbCornerVerts[iNeighbor] = -1; // remove this neighbor from the list.
+ }
+ else
+ {
+ CVertIndex viNBCornerVert = pNeighbor->GetCornerPointIndex( iNBCorner );
+ int iNBVert = pNeighbor->VertIndexToInt( viNBCornerVert );
+ nbCornerVerts[iNeighbor] = iNBVert;
+ vAverage += pNeighbor->GetNormal( iNBVert );
+ }
+ }
+
+
+ // Blend all the neighbor normals with this one.
+ VectorNormalize( vAverage );
+ pDisp->SetNormal( iCornerVert, vAverage );
+
+#if defined( USE_SCRATCHPAD )
+ ScratchPad_DrawArrowSimple(
+ g_pPad,
+ pDisp->GetVert( iCornerVert ),
+ pDisp->GetNormal( iCornerVert ),
+ Vector( 0, 0, 1 ),
+ 25 );
+#endif
+
+ for ( int iNeighbor=0; iNeighbor < nNeighbors; iNeighbor++ )
+ {
+ int iNBListIndex = iNeighbors[iNeighbor];
+ if ( nbCornerVerts[iNeighbor] == -1 )
+ continue;
+
+ CCoreDispInfo *pNeighbor = ppListBase[iNBListIndex];
+ pNeighbor->SetNormal( nbCornerVerts[iNeighbor], vAverage );
+ }
+ }
+ }
+}
+
+
+void BlendTJuncs( CCoreDispInfo **ppListBase, int listSize )
+{
+ for ( int iDisp=0; iDisp < listSize; iDisp++ )
+ {
+ CCoreDispInfo *pDisp = ppListBase[iDisp];
+
+ for ( int iEdge=0; iEdge < 4; iEdge++ )
+ {
+ CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge );
+
+ CVertIndex viMidPoint = pDisp->GetEdgeMidPoint( iEdge );
+ int iMidPoint = pDisp->VertIndexToInt( viMidPoint );
+
+ if ( pEdge->m_SubNeighbors[0].IsValid() && pEdge->m_SubNeighbors[1].IsValid() )
+ {
+ const Vector &vMidPoint = pDisp->GetVert( iMidPoint );
+
+ CCoreDispInfo *pNeighbor1 = ppListBase[pEdge->m_SubNeighbors[0].GetNeighborIndex()];
+ CCoreDispInfo *pNeighbor2 = ppListBase[pEdge->m_SubNeighbors[1].GetNeighborIndex()];
+
+ int iNBCorners[2];
+ iNBCorners[0] = FindNeighborCornerVert( pNeighbor1, vMidPoint );
+ iNBCorners[1] = FindNeighborCornerVert( pNeighbor2, vMidPoint );
+
+ if ( iNBCorners[0] != -1 && iNBCorners[1] != -1 )
+ {
+ CVertIndex viNBCorners[2] =
+ {
+ pNeighbor1->GetCornerPointIndex( iNBCorners[0] ),
+ pNeighbor2->GetCornerPointIndex( iNBCorners[1] )
+ };
+
+ Vector vAverage = pDisp->GetNormal( iMidPoint );
+ vAverage += pNeighbor1->GetNormal( viNBCorners[0] );
+ vAverage += pNeighbor2->GetNormal( viNBCorners[1] );
+
+ VectorNormalize( vAverage );
+ pDisp->SetNormal( iMidPoint, vAverage );
+ pNeighbor1->SetNormal( viNBCorners[0], vAverage );
+ pNeighbor2->SetNormal( viNBCorners[1], vAverage );
+
+#if defined( USE_SCRATCHPAD )
+ ScratchPad_DrawArrowSimple( g_pPad, pDisp->GetVert( iMidPoint ), pDisp->GetNormal( iMidPoint ), Vector( 0, 1, 1 ), 25 );
+#endif
+ }
+ }
+ }
+ }
+}
+
+
+void BlendEdges( CCoreDispInfo **ppListBase, int listSize )
+{
+ for ( int iDisp=0; iDisp < listSize; iDisp++ )
+ {
+ CCoreDispInfo *pDisp = ppListBase[iDisp];
+
+ for ( int iEdge=0; iEdge < 4; iEdge++ )
+ {
+ CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge );
+
+ for ( int iSub=0; iSub < 2; iSub++ )
+ {
+ CDispSubNeighbor *pSub = &pEdge->m_SubNeighbors[iSub];
+ if ( !pSub->IsValid() )
+ continue;
+
+ CCoreDispInfo *pNeighbor = ppListBase[ pSub->GetNeighborIndex() ];
+
+ int iEdgeDim = g_EdgeDims[iEdge];
+
+ CDispSubEdgeIterator it;
+ it.Start( pDisp, iEdge, iSub, true );
+
+ // Get setup on the first corner vert.
+ it.Next();
+ CVertIndex viPrevPos = it.GetVertIndex();
+
+ while ( it.Next() )
+ {
+ // Blend the two.
+ if ( !it.IsLastVert() )
+ {
+ Vector vAverage = pDisp->GetNormal( it.GetVertIndex() ) + pNeighbor->GetNormal( it.GetNBVertIndex() );
+ VectorNormalize( vAverage );
+
+ pDisp->SetNormal( it.GetVertIndex(), vAverage );
+ pNeighbor->SetNormal( it.GetNBVertIndex(), vAverage );
+
+#if defined( USE_SCRATCHPAD )
+ ScratchPad_DrawArrowSimple( g_pPad, pDisp->GetVert( it.GetVertIndex() ), pDisp->GetNormal( it.GetVertIndex() ), Vector( 1, 0, 0 ), 25 );
+#endif
+ }
+
+ // Now blend the in-between verts (if this edge is high-res).
+ int iPrevPos = viPrevPos[ !iEdgeDim ];
+ int iCurPos = it.GetVertIndex()[ !iEdgeDim ];
+
+ for ( int iTween = iPrevPos+1; iTween < iCurPos; iTween++ )
+ {
+ float flPercent = RemapVal( iTween, iPrevPos, iCurPos, 0, 1 );
+ Vector vNormal;
+ VectorLerp( pDisp->GetNormal( viPrevPos ), pDisp->GetNormal( it.GetVertIndex() ), flPercent, vNormal );
+ VectorNormalize( vNormal );
+
+ CVertIndex viTween;
+ viTween[iEdgeDim] = it.GetVertIndex()[ iEdgeDim ];
+ viTween[!iEdgeDim] = iTween;
+ pDisp->SetNormal( viTween, vNormal );
+
+#if defined( USE_SCRATCHPAD )
+ ScratchPad_DrawArrowSimple( g_pPad, pDisp->GetVert( viTween ), pDisp->GetNormal( viTween ), Vector( 1, 0.5, 0 ), 25 );
+#endif
+ }
+
+ viPrevPos = it.GetVertIndex();
+ }
+ }
+ }
+ }
+}
+
+
+#if defined( USE_SCRATCHPAD )
+ void ScratchPad_DrawOriginalNormals( const CCoreDispInfo *pListBase, int listSize )
+ {
+ for ( int i=0; i < listSize; i++ )
+ {
+ const CCoreDispInfo *pDisp = &pListBase[i];
+ const CPowerInfo *pPowerInfo = pDisp->GetPowerInfo();
+
+ // Draw the triangles.
+ for ( int iTri=0; iTri < pPowerInfo->GetNumTriInfos(); iTri++ )
+ {
+ const CTriInfo *pTriInfo = pPowerInfo->GetTriInfo( iTri );
+
+ for ( int iLine=0; iLine < 3; iLine++ )
+ {
+ const Vector &v1 = pDisp->GetVert( pTriInfo->m_Indices[iLine] );
+ const Vector &v2 = pDisp->GetVert( pTriInfo->m_Indices[(iLine+1)%3] );
+
+ g_pPad->DrawLine( CSPVert( v1 ), CSPVert( v2 ) );
+ }
+ }
+
+ // Draw the normals.
+ CDispCircumferenceIterator it( pPowerInfo->GetSideLength() );
+ while ( it.Next() )
+ {
+ ScratchPad_DrawArrowSimple(
+ g_pPad,
+ pDisp->GetVert( it.GetVertIndex() ),
+ pDisp->GetNormal( it.GetVertIndex() ),
+ Vector( 0, 1, 0 ),
+ 15 );
+ }
+ }
+ }
+#endif
+
+
+void SmoothNeighboringDispSurfNormals( CCoreDispInfo **ppListBase, int listSize )
+{
+//#if defined( USE_SCRATCHPAD )
+// g_pPad = ScratchPad3D_Create();
+// ScratchPad_DrawOriginalNormals( pListBase, listSize );
+//#endif
+
+ BlendTJuncs( ppListBase, listSize );
+
+ BlendCorners( ppListBase, listSize );
+
+ BlendEdges( ppListBase, listSize );
+}
+
+
+
diff --git a/mp/src/utils/vrad/disp_vrad.h b/mp/src/utils/vrad/disp_vrad.h
index ec6bb838..0976c574 100644
--- a/mp/src/utils/vrad/disp_vrad.h
+++ b/mp/src/utils/vrad/disp_vrad.h
@@ -1,22 +1,22 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef DISP_VRAD_H
-#define DISP_VRAD_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-#include "builddisp.h"
-
-
-// Blend the normals of neighboring displacement surfaces so they match at edges and corners.
-void SmoothNeighboringDispSurfNormals( CCoreDispInfo **ppListBase, int listSize );
-
-
-#endif // DISP_VRAD_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef DISP_VRAD_H
+#define DISP_VRAD_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "builddisp.h"
+
+
+// Blend the normals of neighboring displacement surfaces so they match at edges and corners.
+void SmoothNeighboringDispSurfNormals( CCoreDispInfo **ppListBase, int listSize );
+
+
+#endif // DISP_VRAD_H
diff --git a/mp/src/utils/vrad/iincremental.h b/mp/src/utils/vrad/iincremental.h
index fbde8c86..c80854f4 100644
--- a/mp/src/utils/vrad/iincremental.h
+++ b/mp/src/utils/vrad/iincremental.h
@@ -1,71 +1,71 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef IINCREMENTAL_H
-#define IINCREMENTAL_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-#include "mathlib/vector.h"
-#include "utlvector.h"
-
-
-typedef unsigned short IncrementalLightID;
-
-
-// Incremental lighting manager.
-class IIncremental
-{
-// IIncremental overrides.
-public:
-
- virtual ~IIncremental() {}
-
- // Sets up for incremental mode. The BSP file (in bsplib) should be loaded
- // already so it can detect if the incremental file is up to date.
- virtual bool Init( char const *pBSPFilename, char const *pIncrementalFilename ) = 0;
-
- // Prepare to light. You must call Init once, but then you can
- // do as many Prepare/AddLight/Finalize phases as you want.
- virtual bool PrepareForLighting() = 0;
-
- // Called every time light is added to a face.
- // NOTE: This is the ONLY threadsafe function in IIncremental.
- virtual void AddLightToFace(
- IncrementalLightID lightID,
- int iFace,
- int iSample,
- int lmSize,
- float dot,
- int iThread ) = 0;
-
- // Called when it's done applying light from the specified light to the specified face.
- virtual void FinishFace (
- IncrementalLightID lightID,
- int iFace,
- int iThread ) = 0;
-
- // For each face that was changed during the lighting process, save out
- // new data for it in the incremental file.
- // Returns false if the incremental lighting isn't active.
- virtual bool Finalize() = 0;
-
- // Grows touched to a size of 'numfaces' and sets each byte to 0 or 1 telling
- // if the face's lightmap was updated in Finalize.
- virtual void GetFacesTouched( CUtlVector<unsigned char> &touched ) = 0;
-
- // This saves the .r0 file and updates the lighting in the BSP file.
- virtual bool Serialize() = 0;
-};
-
-
-extern IIncremental* GetIncremental();
-
-
-#endif // IINCREMENTAL_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef IINCREMENTAL_H
+#define IINCREMENTAL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "mathlib/vector.h"
+#include "utlvector.h"
+
+
+typedef unsigned short IncrementalLightID;
+
+
+// Incremental lighting manager.
+class IIncremental
+{
+// IIncremental overrides.
+public:
+
+ virtual ~IIncremental() {}
+
+ // Sets up for incremental mode. The BSP file (in bsplib) should be loaded
+ // already so it can detect if the incremental file is up to date.
+ virtual bool Init( char const *pBSPFilename, char const *pIncrementalFilename ) = 0;
+
+ // Prepare to light. You must call Init once, but then you can
+ // do as many Prepare/AddLight/Finalize phases as you want.
+ virtual bool PrepareForLighting() = 0;
+
+ // Called every time light is added to a face.
+ // NOTE: This is the ONLY threadsafe function in IIncremental.
+ virtual void AddLightToFace(
+ IncrementalLightID lightID,
+ int iFace,
+ int iSample,
+ int lmSize,
+ float dot,
+ int iThread ) = 0;
+
+ // Called when it's done applying light from the specified light to the specified face.
+ virtual void FinishFace (
+ IncrementalLightID lightID,
+ int iFace,
+ int iThread ) = 0;
+
+ // For each face that was changed during the lighting process, save out
+ // new data for it in the incremental file.
+ // Returns false if the incremental lighting isn't active.
+ virtual bool Finalize() = 0;
+
+ // Grows touched to a size of 'numfaces' and sets each byte to 0 or 1 telling
+ // if the face's lightmap was updated in Finalize.
+ virtual void GetFacesTouched( CUtlVector<unsigned char> &touched ) = 0;
+
+ // This saves the .r0 file and updates the lighting in the BSP file.
+ virtual bool Serialize() = 0;
+};
+
+
+extern IIncremental* GetIncremental();
+
+
+#endif // IINCREMENTAL_H
diff --git a/mp/src/utils/vrad/imagepacker.cpp b/mp/src/utils/vrad/imagepacker.cpp
index 612b0d57..f1cb5ed6 100644
--- a/mp/src/utils/vrad/imagepacker.cpp
+++ b/mp/src/utils/vrad/imagepacker.cpp
@@ -1,141 +1,141 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// The copyright to the contents herein is the property of Valve, L.L.C.
-// The contents may be used and/or copied only with the written permission of
-// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
-// the agreement/contract under which the contents have been supplied.
-//
-// Purpose:
-//
-// $Workfile: $
-// $Date: $
-// $NoKeywords: $
-//=============================================================================
-
-#include "vrad.h"
-#include "imagepacker.h"
-
-
-bool CImagePacker::Reset( int maxLightmapWidth, int maxLightmapHeight )
-{
- int i;
-
- Assert( maxLightmapWidth <= MAX_MAX_LIGHTMAP_WIDTH );
-
- m_MaxLightmapWidth = maxLightmapWidth;
- m_MaxLightmapHeight = maxLightmapHeight;
-
- m_MaxBlockWidth = maxLightmapWidth + 1;
- m_MaxBlockHeight = maxLightmapHeight + 1;
-
- m_AreaUsed = 0;
- m_MinimumHeight = -1;
- for( i = 0; i < m_MaxLightmapWidth; i++ )
- {
- m_pLightmapWavefront[i] = -1;
- }
- return true;
-}
-
-
-inline int CImagePacker::GetMaxYIndex( int firstX, int width )
-{
- int maxY = -1;
- int maxYIndex = 0;
- for( int x = firstX; x < firstX + width; ++x )
- {
- // NOTE: Want the equals here since we'll never be able to fit
- // in between the multiple instances of maxY
- if( m_pLightmapWavefront[x] >= maxY )
- {
- maxY = m_pLightmapWavefront[x];
- maxYIndex = x;
- }
- }
- return maxYIndex;
-}
-
-
-bool CImagePacker::AddBlock( int width, int height, int *returnX, int *returnY )
-{
- // If we've already determined that a block this big couldn't fit
- // then blow off checking again...
- if ( ( width >= m_MaxBlockWidth ) && ( height >= m_MaxBlockHeight ) )
- return false;
-
- int bestX = -1;
- int maxYIdx;
- int outerX = 0;
- int outerMinY = m_MaxLightmapHeight;
- int lastX = m_MaxLightmapWidth - width;
- int lastMaxYVal = -2;
- while (outerX <= lastX)
- {
- // Skip all tiles that have the last Y value, these
- // aren't going to change our min Y value
- if (m_pLightmapWavefront[outerX] == lastMaxYVal)
- {
- ++outerX;
- continue;
- }
-
- maxYIdx = GetMaxYIndex( outerX, width );
- lastMaxYVal = m_pLightmapWavefront[maxYIdx];
- if (outerMinY > lastMaxYVal)
- {
- outerMinY = lastMaxYVal;
- bestX = outerX;
- }
- outerX = maxYIdx + 1;
- }
-
- if( bestX == -1 )
- {
- // If we failed to add it, remember the block size that failed
- // *only if both dimensions are smaller*!!
- // Just because a 1x10 block failed, doesn't mean a 10x1 block will fail
- if ( ( width <= m_MaxBlockWidth ) && ( height <= m_MaxBlockHeight ) )
- {
- m_MaxBlockWidth = width;
- m_MaxBlockHeight = height;
- }
-
- return false;
- }
-
- // Set the return positions for the block.
- *returnX = bestX;
- *returnY = outerMinY + 1;
-
- // Check if it actually fit height-wise.
- // hack
- // if( *returnY + height > maxLightmapHeight )
- if( *returnY + height >= m_MaxLightmapHeight - 1 )
- {
- if ( ( width <= m_MaxBlockWidth ) && ( height <= m_MaxBlockHeight ) )
- {
- m_MaxBlockWidth = width;
- m_MaxBlockHeight = height;
- }
-
- return false;
- }
-
- // It fit!
- // Keep up with the smallest possible size for the image so far.
- if( *returnY + height > m_MinimumHeight )
- m_MinimumHeight = *returnY + height;
-
- // Update the wavefront info.
- int x;
- for( x = bestX; x < bestX + width; x++ )
- {
- m_pLightmapWavefront[x] = outerMinY + height;
- }
-
- // AddBlockToLightmapImage( *returnX, *returnY, width, height );
- m_AreaUsed += width * height;
-
- return true;
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================
+
+#include "vrad.h"
+#include "imagepacker.h"
+
+
+bool CImagePacker::Reset( int maxLightmapWidth, int maxLightmapHeight )
+{
+ int i;
+
+ Assert( maxLightmapWidth <= MAX_MAX_LIGHTMAP_WIDTH );
+
+ m_MaxLightmapWidth = maxLightmapWidth;
+ m_MaxLightmapHeight = maxLightmapHeight;
+
+ m_MaxBlockWidth = maxLightmapWidth + 1;
+ m_MaxBlockHeight = maxLightmapHeight + 1;
+
+ m_AreaUsed = 0;
+ m_MinimumHeight = -1;
+ for( i = 0; i < m_MaxLightmapWidth; i++ )
+ {
+ m_pLightmapWavefront[i] = -1;
+ }
+ return true;
+}
+
+
+inline int CImagePacker::GetMaxYIndex( int firstX, int width )
+{
+ int maxY = -1;
+ int maxYIndex = 0;
+ for( int x = firstX; x < firstX + width; ++x )
+ {
+ // NOTE: Want the equals here since we'll never be able to fit
+ // in between the multiple instances of maxY
+ if( m_pLightmapWavefront[x] >= maxY )
+ {
+ maxY = m_pLightmapWavefront[x];
+ maxYIndex = x;
+ }
+ }
+ return maxYIndex;
+}
+
+
+bool CImagePacker::AddBlock( int width, int height, int *returnX, int *returnY )
+{
+ // If we've already determined that a block this big couldn't fit
+ // then blow off checking again...
+ if ( ( width >= m_MaxBlockWidth ) && ( height >= m_MaxBlockHeight ) )
+ return false;
+
+ int bestX = -1;
+ int maxYIdx;
+ int outerX = 0;
+ int outerMinY = m_MaxLightmapHeight;
+ int lastX = m_MaxLightmapWidth - width;
+ int lastMaxYVal = -2;
+ while (outerX <= lastX)
+ {
+ // Skip all tiles that have the last Y value, these
+ // aren't going to change our min Y value
+ if (m_pLightmapWavefront[outerX] == lastMaxYVal)
+ {
+ ++outerX;
+ continue;
+ }
+
+ maxYIdx = GetMaxYIndex( outerX, width );
+ lastMaxYVal = m_pLightmapWavefront[maxYIdx];
+ if (outerMinY > lastMaxYVal)
+ {
+ outerMinY = lastMaxYVal;
+ bestX = outerX;
+ }
+ outerX = maxYIdx + 1;
+ }
+
+ if( bestX == -1 )
+ {
+ // If we failed to add it, remember the block size that failed
+ // *only if both dimensions are smaller*!!
+ // Just because a 1x10 block failed, doesn't mean a 10x1 block will fail
+ if ( ( width <= m_MaxBlockWidth ) && ( height <= m_MaxBlockHeight ) )
+ {
+ m_MaxBlockWidth = width;
+ m_MaxBlockHeight = height;
+ }
+
+ return false;
+ }
+
+ // Set the return positions for the block.
+ *returnX = bestX;
+ *returnY = outerMinY + 1;
+
+ // Check if it actually fit height-wise.
+ // hack
+ // if( *returnY + height > maxLightmapHeight )
+ if( *returnY + height >= m_MaxLightmapHeight - 1 )
+ {
+ if ( ( width <= m_MaxBlockWidth ) && ( height <= m_MaxBlockHeight ) )
+ {
+ m_MaxBlockWidth = width;
+ m_MaxBlockHeight = height;
+ }
+
+ return false;
+ }
+
+ // It fit!
+ // Keep up with the smallest possible size for the image so far.
+ if( *returnY + height > m_MinimumHeight )
+ m_MinimumHeight = *returnY + height;
+
+ // Update the wavefront info.
+ int x;
+ for( x = bestX; x < bestX + width; x++ )
+ {
+ m_pLightmapWavefront[x] = outerMinY + height;
+ }
+
+ // AddBlockToLightmapImage( *returnX, *returnY, width, height );
+ m_AreaUsed += width * height;
+
+ return true;
+}
+
diff --git a/mp/src/utils/vrad/imagepacker.h b/mp/src/utils/vrad/imagepacker.h
index 0ee1f50e..93a4421a 100644
--- a/mp/src/utils/vrad/imagepacker.h
+++ b/mp/src/utils/vrad/imagepacker.h
@@ -1,51 +1,51 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// The copyright to the contents herein is the property of Valve, L.L.C.
-// The contents may be used and/or copied only with the written permission of
-// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
-// the agreement/contract under which the contents have been supplied.
-//
-// Purpose:
-//
-// $Workfile: $
-// $Date: $
-// $NoKeywords: $
-//=============================================================================
-
-#ifndef IMAGEPACKER_H
-#define IMAGEPACKER_H
-
-#ifdef _WIN32
-#pragma once
-#endif
-
-#define MAX_MAX_LIGHTMAP_WIDTH 2048
-
-
-//-----------------------------------------------------------------------------
-// This packs a single lightmap
-//-----------------------------------------------------------------------------
-class CImagePacker
-{
-public:
- bool Reset( int maxLightmapWidth, int maxLightmapHeight );
- bool AddBlock( int width, int height, int *returnX, int *returnY );
-
-protected:
- int GetMaxYIndex( int firstX, int width );
-
- int m_MaxLightmapWidth;
- int m_MaxLightmapHeight;
- int m_pLightmapWavefront[MAX_MAX_LIGHTMAP_WIDTH];
- int m_AreaUsed;
- int m_MinimumHeight;
-
- // For optimization purposes:
- // These store the width + height of the first image
- // that was unable to be stored in this image
- int m_MaxBlockWidth;
- int m_MaxBlockHeight;
-};
-
-
-#endif // IMAGEPACKER_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef IMAGEPACKER_H
+#define IMAGEPACKER_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#define MAX_MAX_LIGHTMAP_WIDTH 2048
+
+
+//-----------------------------------------------------------------------------
+// This packs a single lightmap
+//-----------------------------------------------------------------------------
+class CImagePacker
+{
+public:
+ bool Reset( int maxLightmapWidth, int maxLightmapHeight );
+ bool AddBlock( int width, int height, int *returnX, int *returnY );
+
+protected:
+ int GetMaxYIndex( int firstX, int width );
+
+ int m_MaxLightmapWidth;
+ int m_MaxLightmapHeight;
+ int m_pLightmapWavefront[MAX_MAX_LIGHTMAP_WIDTH];
+ int m_AreaUsed;
+ int m_MinimumHeight;
+
+ // For optimization purposes:
+ // These store the width + height of the first image
+ // that was unable to be stored in this image
+ int m_MaxBlockWidth;
+ int m_MaxBlockHeight;
+};
+
+
+#endif // IMAGEPACKER_H
diff --git a/mp/src/utils/vrad/incremental.cpp b/mp/src/utils/vrad/incremental.cpp
index 9dd877e0..ad46d04d 100644
--- a/mp/src/utils/vrad/incremental.cpp
+++ b/mp/src/utils/vrad/incremental.cpp
@@ -1,766 +1,766 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-#include "incremental.h"
-#include "lightmap.h"
-
-
-
-static bool g_bFileError = false;
-
-
-// -------------------------------------------------------------------------------- //
-// Static helpers.
-// -------------------------------------------------------------------------------- //
-
-static bool CompareLights( dworldlight_t *a, dworldlight_t *b )
-{
- static float flEpsilon = 1e-7;
-
- bool a1 = VectorsAreEqual( a->origin, b->origin, flEpsilon );
- bool a2 = VectorsAreEqual( a->intensity, b->intensity, 1.1f ); // intensities are huge numbers
- bool a3 = VectorsAreEqual( a->normal, b->normal, flEpsilon );
- bool a4 = fabs( a->constant_attn - b->constant_attn ) < flEpsilon;
- bool a5 = fabs( a->linear_attn - b->linear_attn ) < flEpsilon;
- bool a6 = fabs( a->quadratic_attn - b->quadratic_attn ) < flEpsilon;
- bool a7 = fabs( float( a->flags - b->flags ) ) < flEpsilon;
- bool a8 = fabs( a->stopdot - b->stopdot ) < flEpsilon;
- bool a9 = fabs( a->stopdot2 - b->stopdot2 ) < flEpsilon;
- bool a10 = fabs( a->exponent - b->exponent ) < flEpsilon;
- bool a11 = fabs( a->radius - b->radius ) < flEpsilon;
-
- return a1 && a2 && a3 && a4 && a5 && a6 && a7 && a8 && a9 && a10 && a11;
-}
-
-
-long FileOpen( char const *pFilename, bool bRead )
-{
- g_bFileError = false;
- return (long)g_pFileSystem->Open( pFilename, bRead ? "rb" : "wb" );
-}
-
-
-void FileClose( long fp )
-{
- if( fp )
- g_pFileSystem->Close( (FILE*)fp );
-}
-
-
-// Returns true if there was an error reading from the file.
-bool FileError()
-{
- return g_bFileError;
-}
-
-static inline void FileRead( long fp, void *pOut, int size )
-{
- if( g_bFileError || g_pFileSystem->Read( pOut, size, (FileHandle_t)fp ) != size )
- {
- g_bFileError = true;
- memset( pOut, 0, size );
- }
-}
-
-
-template<class T>
-static inline void FileRead( long fp, T &out )
-{
- FileRead( fp, &out, sizeof(out) );
-}
-
-
-static inline void FileWrite( long fp, void const *pData, int size )
-{
- if( g_bFileError || g_pFileSystem->Write( pData, size, (FileHandle_t)fp ) != size )
- {
- g_bFileError = true;
- }
-}
-
-
-template<class T>
-static inline void FileWrite( long fp, T out )
-{
- FileWrite( fp, &out, sizeof(out) );
-}
-
-
-IIncremental* GetIncremental()
-{
- static CIncremental inc;
- return &inc;
-}
-
-
-// -------------------------------------------------------------------------------- //
-// CIncremental.
-// -------------------------------------------------------------------------------- //
-
-CIncremental::CIncremental()
-{
- m_TotalMemory = 0;
- m_pIncrementalFilename = NULL;
- m_pBSPFilename = NULL;
- m_bSuccessfulRun = false;
-}
-
-
-CIncremental::~CIncremental()
-{
-}
-
-
-bool CIncremental::Init( char const *pBSPFilename, char const *pIncrementalFilename )
-{
- m_pBSPFilename = pBSPFilename;
- m_pIncrementalFilename = pIncrementalFilename;
- return true;
-}
-
-
-bool CIncremental::PrepareForLighting()
-{
- if( !m_pBSPFilename )
- return false;
-
- // Clear the touched faces list.
- m_FacesTouched.SetSize( numfaces );
- memset( m_FacesTouched.Base(), 0, numfaces );
-
- // If we haven't done a complete successful run yet, then we either haven't
- // loaded the lights, or a run was aborted and our lights are half-done so we
- // should reload them.
- if( !m_bSuccessfulRun )
- LoadIncrementalFile();
-
- // unmatched = a list of the lights we have
- CUtlLinkedList<int,int> unmatched;
- for( int i=m_Lights.Head(); i != m_Lights.InvalidIndex(); i = m_Lights.Next(i) )
- unmatched.AddToTail( i );
-
- // Match the light lists and get rid of lights that we already have all the data for.
- directlight_t *pNext;
- directlight_t **pPrev = &activelights;
- for( directlight_t *dl=activelights; dl != NULL; dl = pNext )
- {
- pNext = dl->next;
-
- //float flClosest = 3000000000;
- //CIncLight *pClosest = 0;
-
- // Look for this light in our light list.
- int iNextUnmatched, iUnmatched;
- for( iUnmatched=unmatched.Head(); iUnmatched != unmatched.InvalidIndex(); iUnmatched = iNextUnmatched )
- {
- iNextUnmatched = unmatched.Next( iUnmatched );
-
- CIncLight *pLight = m_Lights[ unmatched[iUnmatched] ];
-
- //float flTest = (pLight->m_Light.origin - dl->light.origin).Length();
- //if( flTest < flClosest )
- //{
- // flClosest = flTest;
- // pClosest = pLight;
- //}
-
- if( CompareLights( &dl->light, &pLight->m_Light ) )
- {
- unmatched.Remove( iUnmatched );
-
- // Ok, we have this light's data already, yay!
- // Get rid of it from the active light list.
- *pPrev = dl->next;
- free( dl );
- dl = 0;
- break;
- }
- }
-
- //bool bTest=false;
- //if(bTest)
- // CompareLights( &dl->light, &pClosest->m_Light );
-
- if( iUnmatched == unmatched.InvalidIndex() )
- pPrev = &dl->next;
- }
-
- // Remove any of our lights that were unmatched.
- for( int iUnmatched=unmatched.Head(); iUnmatched != unmatched.InvalidIndex(); iUnmatched = unmatched.Next( iUnmatched ) )
- {
- CIncLight *pLight = m_Lights[ unmatched[iUnmatched] ];
-
- // First tag faces that it touched so they get recomposited.
- for( unsigned short iFace=pLight->m_LightFaces.Head(); iFace != pLight->m_LightFaces.InvalidIndex(); iFace = pLight->m_LightFaces.Next( iFace ) )
- {
- m_FacesTouched[ pLight->m_LightFaces[iFace]->m_FaceIndex ] = 1;
- }
-
- delete pLight;
- m_Lights.Remove( unmatched[iUnmatched] );
- }
-
- // Now add a light structure for each new light.
- AddLightsForActiveLights();
-
- return true;
-}
-
-
-bool CIncremental::ReadIncrementalHeader( long fp, CIncrementalHeader *pHeader )
-{
- int version;
- FileRead( fp, version );
- if( version != INCREMENTALFILE_VERSION )
- return false;
-
- int nFaces;
- FileRead( fp, nFaces );
-
- pHeader->m_FaceLightmapSizes.SetSize( nFaces );
- FileRead( fp, pHeader->m_FaceLightmapSizes.Base(), sizeof(CIncrementalHeader::CLMSize) * nFaces );
-
- return !FileError();
-}
-
-
-bool CIncremental::WriteIncrementalHeader( long fp )
-{
- int version = INCREMENTALFILE_VERSION;
- FileWrite( fp, version );
-
- int nFaces = numfaces;
- FileWrite( fp, nFaces );
-
- CIncrementalHeader hdr;
- hdr.m_FaceLightmapSizes.SetSize( nFaces );
-
- for( int i=0; i < nFaces; i++ )
- {
- hdr.m_FaceLightmapSizes[i].m_Width = g_pFaces[i].m_LightmapTextureSizeInLuxels[0];
- hdr.m_FaceLightmapSizes[i].m_Height = g_pFaces[i].m_LightmapTextureSizeInLuxels[1];
- }
-
- FileWrite( fp, hdr.m_FaceLightmapSizes.Base(), sizeof(CIncrementalHeader::CLMSize) * nFaces );
-
- return !FileError();
-}
-
-
-bool CIncremental::IsIncrementalFileValid()
-{
- long fp = FileOpen( m_pIncrementalFilename, true );
- if( !fp )
- return false;
-
- bool bValid = false;
- CIncrementalHeader hdr;
- if( ReadIncrementalHeader( fp, &hdr ) )
- {
- // If the number of faces is the same and their lightmap sizes are the same,
- // then this file is considered a legitimate incremental file.
- if( hdr.m_FaceLightmapSizes.Count() == numfaces )
- {
- int i;
- for( i=0; i < numfaces; i++ )
- {
- if( hdr.m_FaceLightmapSizes[i].m_Width != g_pFaces[i].m_LightmapTextureSizeInLuxels[0] ||
- hdr.m_FaceLightmapSizes[i].m_Height != g_pFaces[i].m_LightmapTextureSizeInLuxels[1] )
- {
- break;
- }
- }
-
- // Were all faces valid?
- if( i == numfaces )
- bValid = true;
- }
- }
-
- FileClose( fp );
- return bValid && !FileError();
-}
-
-
-void CIncremental::AddLightToFace(
- IncrementalLightID lightID,
- int iFace,
- int iSample,
- int lmSize,
- float dot,
- int iThread )
-{
- // If we're not being used, don't do anything.
- if( !m_pIncrementalFilename )
- return;
-
- CIncLight *pLight = m_Lights[lightID];
-
- // Check for the 99.99% case in which the face already exists.
- CLightFace *pFace;
- if( pLight->m_pCachedFaces[iThread] &&
- pLight->m_pCachedFaces[iThread]->m_FaceIndex == iFace )
- {
- pFace = pLight->m_pCachedFaces[iThread];
- }
- else
- {
- bool bNew;
-
- EnterCriticalSection( &pLight->m_CS );
- pFace = pLight->FindOrCreateLightFace( iFace, lmSize, &bNew );
- LeaveCriticalSection( &pLight->m_CS );
-
- pLight->m_pCachedFaces[iThread] = pFace;
-
- if( bNew )
- m_TotalMemory += pFace->m_LightValues.Count() * sizeof( pFace->m_LightValues[0] );
- }
-
- // Add this into the light's data.
- pFace->m_LightValues[iSample].m_Dot = dot;
-}
-
-
-unsigned short DecodeCharOrShort( CUtlBuffer *pIn )
-{
- unsigned short val = pIn->GetUnsignedChar();
- if( val & 0x80 )
- {
- val = ((val & 0x7F) << 8) | pIn->GetUnsignedChar();
- }
-
- return val;
-}
-
-
-void EncodeCharOrShort( CUtlBuffer *pBuf, unsigned short val )
-{
- if( (val & 0xFF80) == 0 )
- {
- pBuf->PutUnsignedChar( (unsigned char)val );
- }
- else
- {
- if( val > 32767 )
- val = 32767;
-
- pBuf->PutUnsignedChar( (val >> 8) | 0x80 );
- pBuf->PutUnsignedChar( val & 0xFF );
- }
-}
-
-
-void DecompressLightData( CUtlBuffer *pIn, CUtlVector<CLightValue> *pOut )
-{
- int iOut = 0;
- while( pIn->TellGet() < pIn->TellPut() )
- {
- unsigned char runLength = pIn->GetUnsignedChar();
- unsigned short usVal = DecodeCharOrShort( pIn );
-
- while( runLength > 0 )
- {
- --runLength;
-
- pOut->Element(iOut).m_Dot = usVal;
- ++iOut;
- }
- }
-}
-
-#ifdef _WIN32
-#pragma warning (disable:4701)
-#endif
-
-void CompressLightData(
- CLightValue const *pValues,
- int nValues,
- CUtlBuffer *pBuf )
-{
- unsigned char runLength=0;
- unsigned short flLastValue;
-
- for( int i=0; i < nValues; i++ )
- {
- unsigned short flCurValue = (unsigned short)pValues[i].m_Dot;
-
- if( i == 0 )
- {
- flLastValue = flCurValue;
- runLength = 1;
- }
- else if( flCurValue == flLastValue && runLength < 255 )
- {
- ++runLength;
- }
- else
- {
- pBuf->PutUnsignedChar( runLength );
- EncodeCharOrShort( pBuf, flLastValue );
-
- flLastValue = flCurValue;
- runLength = 1;
- }
- }
-
- // Write the end..
- if( runLength )
- {
- pBuf->PutUnsignedChar( runLength );
- EncodeCharOrShort( pBuf, flLastValue );
- }
-}
-
-#ifdef _WIN32
-#pragma warning (default:4701)
-#endif
-
-void MultiplyValues( CUtlVector<CLightValue> &values, float scale )
-{
- for( int i=0; i < values.Count(); i++ )
- values[i].m_Dot *= scale;
-}
-
-
-void CIncremental::FinishFace(
- IncrementalLightID lightID,
- int iFace,
- int iThread )
-{
- CIncLight *pLight = m_Lights[lightID];
-
- // Check for the 99.99% case in which the face already exists.
- CLightFace *pFace;
- if( pLight->m_pCachedFaces[iThread] && pLight->m_pCachedFaces[iThread]->m_FaceIndex == iFace )
- {
- pFace = pLight->m_pCachedFaces[iThread];
-
- // Compress the data.
- MultiplyValues( pFace->m_LightValues, pLight->m_flMaxIntensity );
-
- pFace->m_CompressedData.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
- CompressLightData(
- pFace->m_LightValues.Base(),
- pFace->m_LightValues.Count(),
- &pFace->m_CompressedData );
-
-#if 0
- // test decompression
- CUtlVector<CLightValue> test;
- test.SetSize( 2048 );
- pFace->m_CompressedData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
- DecompressLightData( &pFace->m_CompressedData, &test );
-#endif
-
- if( pFace->m_CompressedData.TellPut() == 0 )
- {
- // No contribution.. delete this face from the light.
- EnterCriticalSection( &pLight->m_CS );
- pLight->m_LightFaces.Remove( pFace->m_LightFacesIndex );
- delete pFace;
- LeaveCriticalSection( &pLight->m_CS );
- }
- else
- {
- // Discard the uncompressed data.
- pFace->m_LightValues.Purge();
- m_FacesTouched[ pFace->m_FaceIndex ] = 1;
- }
- }
-}
-
-
-bool CIncremental::Finalize()
-{
- // If we're not being used, don't do anything.
- if( !m_pIncrementalFilename || !m_pBSPFilename )
- return false;
-
- CUtlVector<CFaceLightList> faceLights;
- LinkLightsToFaces( faceLights );
-
- Vector faceLight[(MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2) * (MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2)];
- CUtlVector<CLightValue> faceLightValues;
- faceLightValues.SetSize( (MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2) * (MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2) );
-
- // Only update the faces we've touched.
- for( int facenum = 0; facenum < numfaces; facenum++ )
- {
- if( !m_FacesTouched[facenum] || !faceLights[facenum].Count() )
- continue;
-
- int w = g_pFaces[facenum].m_LightmapTextureSizeInLuxels[0]+1;
- int h = g_pFaces[facenum].m_LightmapTextureSizeInLuxels[1]+1;
- int nLuxels = w * h;
- assert( nLuxels <= sizeof(faceLight) / sizeof(faceLight[0]) );
-
- // Clear the lighting for this face.
- memset( faceLight, 0, nLuxels * sizeof(Vector) );
-
- // Composite all the light contributions.
- for( int iFace=0; iFace < faceLights[facenum].Count(); iFace++ )
- {
- CLightFace *pFace = faceLights[facenum][iFace];
-
- pFace->m_CompressedData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
- DecompressLightData( &pFace->m_CompressedData, &faceLightValues );
-
- for( int iSample=0; iSample < nLuxels; iSample++ )
- {
- float flDot = faceLightValues[iSample].m_Dot;
- if( flDot )
- {
- VectorMA(
- faceLight[iSample],
- flDot / pFace->m_pLight->m_flMaxIntensity,
- pFace->m_pLight->m_Light.intensity,
- faceLight[iSample] );
- }
- }
- }
-
- // Convert to the floating-point representation in the BSP file.
- Vector *pSrc = faceLight;
- unsigned char *pDest = &(*pdlightdata)[ g_pFaces[facenum].lightofs ];
-
- for( int iSample=0; iSample < nLuxels; iSample++ )
- {
- VectorToColorRGBExp32( *pSrc, *( ColorRGBExp32 *)pDest );
- pDest += 4;
- pSrc++;
- }
- }
-
- m_bSuccessfulRun = true;
- return true;
-}
-
-
-void CIncremental::GetFacesTouched( CUtlVector<unsigned char> &touched )
-{
- touched.CopyArray( m_FacesTouched.Base(), m_FacesTouched.Count() );
-}
-
-
-bool CIncremental::Serialize()
-{
- if( !SaveIncrementalFile() )
- return false;
-
- WriteBSPFile( (char*)m_pBSPFilename );
- return true;
-}
-
-
-void CIncremental::Term()
-{
- m_Lights.PurgeAndDeleteElements();
- m_TotalMemory = 0;
-}
-
-
-void CIncremental::AddLightsForActiveLights()
-{
- // Create our lights.
- for( directlight_t *dl=activelights; dl != NULL; dl = dl->next )
- {
- CIncLight *pLight = new CIncLight;
- dl->m_IncrementalID = m_Lights.AddToTail( pLight );
-
- // Copy the light information.
- pLight->m_Light = dl->light;
- pLight->m_flMaxIntensity = max( dl->light.intensity[0], max( dl->light.intensity[1], dl->light.intensity[2] ) );
- }
-}
-
-
-bool CIncremental::LoadIncrementalFile()
-{
- Term();
-
- if( !IsIncrementalFileValid() )
- return false;
-
- long fp = FileOpen( m_pIncrementalFilename, true );
- if( !fp )
- return false;
-
- // Read the header.
- CIncrementalHeader hdr;
- if( !ReadIncrementalHeader( fp, &hdr ) )
- {
- FileClose( fp );
- return false;
- }
-
-
- // Read the lights.
- int nLights;
- FileRead( fp, nLights );
- for( int iLight=0; iLight < nLights; iLight++ )
- {
- CIncLight *pLight = new CIncLight;
- m_Lights.AddToTail( pLight );
-
- FileRead( fp, pLight->m_Light );
- pLight->m_flMaxIntensity =
- max( pLight->m_Light.intensity.x,
- max( pLight->m_Light.intensity.y, pLight->m_Light.intensity.z ) );
-
- int nFaces;
- FileRead( fp, nFaces );
- assert( nFaces < 70000 );
-
- for( int iFace=0; iFace < nFaces; iFace++ )
- {
- CLightFace *pFace = new CLightFace;
- pLight->m_LightFaces.AddToTail( pFace );
-
- pFace->m_pLight = pLight;
- FileRead( fp, pFace->m_FaceIndex );
-
- int dataSize;
- FileRead( fp, dataSize );
-
- pFace->m_CompressedData.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
- while( dataSize )
- {
- --dataSize;
-
- unsigned char ucData;
- FileRead( fp, ucData );
-
- pFace->m_CompressedData.PutUnsignedChar( ucData );
- }
- }
- }
-
-
- FileClose( fp );
- return !FileError();
-}
-
-
-bool CIncremental::SaveIncrementalFile()
-{
- long fp = FileOpen( m_pIncrementalFilename, false );
- if( !fp )
- return false;
-
- if( !WriteIncrementalHeader( fp ) )
- {
- FileClose( fp );
- return false;
- }
-
- // Write the lights.
- int nLights = m_Lights.Count();
- FileWrite( fp, nLights );
- for( int iLight=m_Lights.Head(); iLight != m_Lights.InvalidIndex(); iLight = m_Lights.Next( iLight ) )
- {
- CIncLight *pLight = m_Lights[iLight];
-
- FileWrite( fp, pLight->m_Light );
-
- int nFaces = pLight->m_LightFaces.Count();
- FileWrite( fp, nFaces );
- for( int iFace=pLight->m_LightFaces.Head(); iFace != pLight->m_LightFaces.InvalidIndex(); iFace = pLight->m_LightFaces.Next( iFace ) )
- {
- CLightFace *pFace = pLight->m_LightFaces[iFace];
-
- FileWrite( fp, pFace->m_FaceIndex );
-
- int dataSize = pFace->m_CompressedData.TellPut();
- FileWrite( fp, dataSize );
-
- pFace->m_CompressedData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
- while( dataSize )
- {
- --dataSize;
- FileWrite( fp, pFace->m_CompressedData.GetUnsignedChar() );
- }
- }
- }
-
-
- FileClose( fp );
- return !FileError();
-}
-
-
-void CIncremental::LinkLightsToFaces( CUtlVector<CFaceLightList> &faceLights )
-{
- faceLights.SetSize( numfaces );
-
- for( int iLight=m_Lights.Head(); iLight != m_Lights.InvalidIndex(); iLight = m_Lights.Next( iLight ) )
- {
- CIncLight *pLight = m_Lights[iLight];
-
- for( int iFace=pLight->m_LightFaces.Head(); iFace != pLight->m_LightFaces.InvalidIndex(); iFace = pLight->m_LightFaces.Next( iFace ) )
- {
- CLightFace *pFace = pLight->m_LightFaces[iFace];
-
- if( m_FacesTouched[pFace->m_FaceIndex] )
- faceLights[ pFace->m_FaceIndex ].AddToTail( pFace );
- }
- }
-}
-
-
-// ------------------------------------------------------------------ //
-// CIncLight
-// ------------------------------------------------------------------ //
-
-CIncLight::CIncLight()
-{
- memset( m_pCachedFaces, 0, sizeof(m_pCachedFaces) );
- InitializeCriticalSection( &m_CS );
-}
-
-
-CIncLight::~CIncLight()
-{
- m_LightFaces.PurgeAndDeleteElements();
- DeleteCriticalSection( &m_CS );
-}
-
-
-CLightFace* CIncLight::FindOrCreateLightFace( int iFace, int lmSize, bool *bNew )
-{
- if( bNew )
- *bNew = false;
-
-
- // Look for it.
- for( int i=m_LightFaces.Head(); i != m_LightFaces.InvalidIndex(); i=m_LightFaces.Next(i) )
- {
- CLightFace *pFace = m_LightFaces[i];
-
- if( pFace->m_FaceIndex == iFace )
- {
- assert( pFace->m_LightValues.Count() == lmSize );
- return pFace;
- }
- }
-
- // Ok, create one.
- CLightFace *pFace = new CLightFace;
- pFace->m_LightFacesIndex = m_LightFaces.AddToTail( pFace );
- pFace->m_pLight = this;
-
- pFace->m_FaceIndex = iFace;
- pFace->m_LightValues.SetSize( lmSize );
- memset( pFace->m_LightValues.Base(), 0, sizeof( CLightValue ) * lmSize );
-
- if( bNew )
- *bNew = true;
-
- return pFace;
-}
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "incremental.h"
+#include "lightmap.h"
+
+
+
+static bool g_bFileError = false;
+
+
+// -------------------------------------------------------------------------------- //
+// Static helpers.
+// -------------------------------------------------------------------------------- //
+
+static bool CompareLights( dworldlight_t *a, dworldlight_t *b )
+{
+ static float flEpsilon = 1e-7;
+
+ bool a1 = VectorsAreEqual( a->origin, b->origin, flEpsilon );
+ bool a2 = VectorsAreEqual( a->intensity, b->intensity, 1.1f ); // intensities are huge numbers
+ bool a3 = VectorsAreEqual( a->normal, b->normal, flEpsilon );
+ bool a4 = fabs( a->constant_attn - b->constant_attn ) < flEpsilon;
+ bool a5 = fabs( a->linear_attn - b->linear_attn ) < flEpsilon;
+ bool a6 = fabs( a->quadratic_attn - b->quadratic_attn ) < flEpsilon;
+ bool a7 = fabs( float( a->flags - b->flags ) ) < flEpsilon;
+ bool a8 = fabs( a->stopdot - b->stopdot ) < flEpsilon;
+ bool a9 = fabs( a->stopdot2 - b->stopdot2 ) < flEpsilon;
+ bool a10 = fabs( a->exponent - b->exponent ) < flEpsilon;
+ bool a11 = fabs( a->radius - b->radius ) < flEpsilon;
+
+ return a1 && a2 && a3 && a4 && a5 && a6 && a7 && a8 && a9 && a10 && a11;
+}
+
+
+long FileOpen( char const *pFilename, bool bRead )
+{
+ g_bFileError = false;
+ return (long)g_pFileSystem->Open( pFilename, bRead ? "rb" : "wb" );
+}
+
+
+void FileClose( long fp )
+{
+ if( fp )
+ g_pFileSystem->Close( (FILE*)fp );
+}
+
+
+// Returns true if there was an error reading from the file.
+bool FileError()
+{
+ return g_bFileError;
+}
+
+static inline void FileRead( long fp, void *pOut, int size )
+{
+ if( g_bFileError || g_pFileSystem->Read( pOut, size, (FileHandle_t)fp ) != size )
+ {
+ g_bFileError = true;
+ memset( pOut, 0, size );
+ }
+}
+
+
+template<class T>
+static inline void FileRead( long fp, T &out )
+{
+ FileRead( fp, &out, sizeof(out) );
+}
+
+
+static inline void FileWrite( long fp, void const *pData, int size )
+{
+ if( g_bFileError || g_pFileSystem->Write( pData, size, (FileHandle_t)fp ) != size )
+ {
+ g_bFileError = true;
+ }
+}
+
+
+template<class T>
+static inline void FileWrite( long fp, T out )
+{
+ FileWrite( fp, &out, sizeof(out) );
+}
+
+
+IIncremental* GetIncremental()
+{
+ static CIncremental inc;
+ return &inc;
+}
+
+
+// -------------------------------------------------------------------------------- //
+// CIncremental.
+// -------------------------------------------------------------------------------- //
+
+CIncremental::CIncremental()
+{
+ m_TotalMemory = 0;
+ m_pIncrementalFilename = NULL;
+ m_pBSPFilename = NULL;
+ m_bSuccessfulRun = false;
+}
+
+
+CIncremental::~CIncremental()
+{
+}
+
+
+bool CIncremental::Init( char const *pBSPFilename, char const *pIncrementalFilename )
+{
+ m_pBSPFilename = pBSPFilename;
+ m_pIncrementalFilename = pIncrementalFilename;
+ return true;
+}
+
+
+bool CIncremental::PrepareForLighting()
+{
+ if( !m_pBSPFilename )
+ return false;
+
+ // Clear the touched faces list.
+ m_FacesTouched.SetSize( numfaces );
+ memset( m_FacesTouched.Base(), 0, numfaces );
+
+ // If we haven't done a complete successful run yet, then we either haven't
+ // loaded the lights, or a run was aborted and our lights are half-done so we
+ // should reload them.
+ if( !m_bSuccessfulRun )
+ LoadIncrementalFile();
+
+ // unmatched = a list of the lights we have
+ CUtlLinkedList<int,int> unmatched;
+ for( int i=m_Lights.Head(); i != m_Lights.InvalidIndex(); i = m_Lights.Next(i) )
+ unmatched.AddToTail( i );
+
+ // Match the light lists and get rid of lights that we already have all the data for.
+ directlight_t *pNext;
+ directlight_t **pPrev = &activelights;
+ for( directlight_t *dl=activelights; dl != NULL; dl = pNext )
+ {
+ pNext = dl->next;
+
+ //float flClosest = 3000000000;
+ //CIncLight *pClosest = 0;
+
+ // Look for this light in our light list.
+ int iNextUnmatched, iUnmatched;
+ for( iUnmatched=unmatched.Head(); iUnmatched != unmatched.InvalidIndex(); iUnmatched = iNextUnmatched )
+ {
+ iNextUnmatched = unmatched.Next( iUnmatched );
+
+ CIncLight *pLight = m_Lights[ unmatched[iUnmatched] ];
+
+ //float flTest = (pLight->m_Light.origin - dl->light.origin).Length();
+ //if( flTest < flClosest )
+ //{
+ // flClosest = flTest;
+ // pClosest = pLight;
+ //}
+
+ if( CompareLights( &dl->light, &pLight->m_Light ) )
+ {
+ unmatched.Remove( iUnmatched );
+
+ // Ok, we have this light's data already, yay!
+ // Get rid of it from the active light list.
+ *pPrev = dl->next;
+ free( dl );
+ dl = 0;
+ break;
+ }
+ }
+
+ //bool bTest=false;
+ //if(bTest)
+ // CompareLights( &dl->light, &pClosest->m_Light );
+
+ if( iUnmatched == unmatched.InvalidIndex() )
+ pPrev = &dl->next;
+ }
+
+ // Remove any of our lights that were unmatched.
+ for( int iUnmatched=unmatched.Head(); iUnmatched != unmatched.InvalidIndex(); iUnmatched = unmatched.Next( iUnmatched ) )
+ {
+ CIncLight *pLight = m_Lights[ unmatched[iUnmatched] ];
+
+ // First tag faces that it touched so they get recomposited.
+ for( unsigned short iFace=pLight->m_LightFaces.Head(); iFace != pLight->m_LightFaces.InvalidIndex(); iFace = pLight->m_LightFaces.Next( iFace ) )
+ {
+ m_FacesTouched[ pLight->m_LightFaces[iFace]->m_FaceIndex ] = 1;
+ }
+
+ delete pLight;
+ m_Lights.Remove( unmatched[iUnmatched] );
+ }
+
+ // Now add a light structure for each new light.
+ AddLightsForActiveLights();
+
+ return true;
+}
+
+
+bool CIncremental::ReadIncrementalHeader( long fp, CIncrementalHeader *pHeader )
+{
+ int version;
+ FileRead( fp, version );
+ if( version != INCREMENTALFILE_VERSION )
+ return false;
+
+ int nFaces;
+ FileRead( fp, nFaces );
+
+ pHeader->m_FaceLightmapSizes.SetSize( nFaces );
+ FileRead( fp, pHeader->m_FaceLightmapSizes.Base(), sizeof(CIncrementalHeader::CLMSize) * nFaces );
+
+ return !FileError();
+}
+
+
+bool CIncremental::WriteIncrementalHeader( long fp )
+{
+ int version = INCREMENTALFILE_VERSION;
+ FileWrite( fp, version );
+
+ int nFaces = numfaces;
+ FileWrite( fp, nFaces );
+
+ CIncrementalHeader hdr;
+ hdr.m_FaceLightmapSizes.SetSize( nFaces );
+
+ for( int i=0; i < nFaces; i++ )
+ {
+ hdr.m_FaceLightmapSizes[i].m_Width = g_pFaces[i].m_LightmapTextureSizeInLuxels[0];
+ hdr.m_FaceLightmapSizes[i].m_Height = g_pFaces[i].m_LightmapTextureSizeInLuxels[1];
+ }
+
+ FileWrite( fp, hdr.m_FaceLightmapSizes.Base(), sizeof(CIncrementalHeader::CLMSize) * nFaces );
+
+ return !FileError();
+}
+
+
+bool CIncremental::IsIncrementalFileValid()
+{
+ long fp = FileOpen( m_pIncrementalFilename, true );
+ if( !fp )
+ return false;
+
+ bool bValid = false;
+ CIncrementalHeader hdr;
+ if( ReadIncrementalHeader( fp, &hdr ) )
+ {
+ // If the number of faces is the same and their lightmap sizes are the same,
+ // then this file is considered a legitimate incremental file.
+ if( hdr.m_FaceLightmapSizes.Count() == numfaces )
+ {
+ int i;
+ for( i=0; i < numfaces; i++ )
+ {
+ if( hdr.m_FaceLightmapSizes[i].m_Width != g_pFaces[i].m_LightmapTextureSizeInLuxels[0] ||
+ hdr.m_FaceLightmapSizes[i].m_Height != g_pFaces[i].m_LightmapTextureSizeInLuxels[1] )
+ {
+ break;
+ }
+ }
+
+ // Were all faces valid?
+ if( i == numfaces )
+ bValid = true;
+ }
+ }
+
+ FileClose( fp );
+ return bValid && !FileError();
+}
+
+
+void CIncremental::AddLightToFace(
+ IncrementalLightID lightID,
+ int iFace,
+ int iSample,
+ int lmSize,
+ float dot,
+ int iThread )
+{
+ // If we're not being used, don't do anything.
+ if( !m_pIncrementalFilename )
+ return;
+
+ CIncLight *pLight = m_Lights[lightID];
+
+ // Check for the 99.99% case in which the face already exists.
+ CLightFace *pFace;
+ if( pLight->m_pCachedFaces[iThread] &&
+ pLight->m_pCachedFaces[iThread]->m_FaceIndex == iFace )
+ {
+ pFace = pLight->m_pCachedFaces[iThread];
+ }
+ else
+ {
+ bool bNew;
+
+ EnterCriticalSection( &pLight->m_CS );
+ pFace = pLight->FindOrCreateLightFace( iFace, lmSize, &bNew );
+ LeaveCriticalSection( &pLight->m_CS );
+
+ pLight->m_pCachedFaces[iThread] = pFace;
+
+ if( bNew )
+ m_TotalMemory += pFace->m_LightValues.Count() * sizeof( pFace->m_LightValues[0] );
+ }
+
+ // Add this into the light's data.
+ pFace->m_LightValues[iSample].m_Dot = dot;
+}
+
+
+unsigned short DecodeCharOrShort( CUtlBuffer *pIn )
+{
+ unsigned short val = pIn->GetUnsignedChar();
+ if( val & 0x80 )
+ {
+ val = ((val & 0x7F) << 8) | pIn->GetUnsignedChar();
+ }
+
+ return val;
+}
+
+
+void EncodeCharOrShort( CUtlBuffer *pBuf, unsigned short val )
+{
+ if( (val & 0xFF80) == 0 )
+ {
+ pBuf->PutUnsignedChar( (unsigned char)val );
+ }
+ else
+ {
+ if( val > 32767 )
+ val = 32767;
+
+ pBuf->PutUnsignedChar( (val >> 8) | 0x80 );
+ pBuf->PutUnsignedChar( val & 0xFF );
+ }
+}
+
+
+void DecompressLightData( CUtlBuffer *pIn, CUtlVector<CLightValue> *pOut )
+{
+ int iOut = 0;
+ while( pIn->TellGet() < pIn->TellPut() )
+ {
+ unsigned char runLength = pIn->GetUnsignedChar();
+ unsigned short usVal = DecodeCharOrShort( pIn );
+
+ while( runLength > 0 )
+ {
+ --runLength;
+
+ pOut->Element(iOut).m_Dot = usVal;
+ ++iOut;
+ }
+ }
+}
+
+#ifdef _WIN32
+#pragma warning (disable:4701)
+#endif
+
+void CompressLightData(
+ CLightValue const *pValues,
+ int nValues,
+ CUtlBuffer *pBuf )
+{
+ unsigned char runLength=0;
+ unsigned short flLastValue;
+
+ for( int i=0; i < nValues; i++ )
+ {
+ unsigned short flCurValue = (unsigned short)pValues[i].m_Dot;
+
+ if( i == 0 )
+ {
+ flLastValue = flCurValue;
+ runLength = 1;
+ }
+ else if( flCurValue == flLastValue && runLength < 255 )
+ {
+ ++runLength;
+ }
+ else
+ {
+ pBuf->PutUnsignedChar( runLength );
+ EncodeCharOrShort( pBuf, flLastValue );
+
+ flLastValue = flCurValue;
+ runLength = 1;
+ }
+ }
+
+ // Write the end..
+ if( runLength )
+ {
+ pBuf->PutUnsignedChar( runLength );
+ EncodeCharOrShort( pBuf, flLastValue );
+ }
+}
+
+#ifdef _WIN32
+#pragma warning (default:4701)
+#endif
+
+void MultiplyValues( CUtlVector<CLightValue> &values, float scale )
+{
+ for( int i=0; i < values.Count(); i++ )
+ values[i].m_Dot *= scale;
+}
+
+
+void CIncremental::FinishFace(
+ IncrementalLightID lightID,
+ int iFace,
+ int iThread )
+{
+ CIncLight *pLight = m_Lights[lightID];
+
+ // Check for the 99.99% case in which the face already exists.
+ CLightFace *pFace;
+ if( pLight->m_pCachedFaces[iThread] && pLight->m_pCachedFaces[iThread]->m_FaceIndex == iFace )
+ {
+ pFace = pLight->m_pCachedFaces[iThread];
+
+ // Compress the data.
+ MultiplyValues( pFace->m_LightValues, pLight->m_flMaxIntensity );
+
+ pFace->m_CompressedData.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
+ CompressLightData(
+ pFace->m_LightValues.Base(),
+ pFace->m_LightValues.Count(),
+ &pFace->m_CompressedData );
+
+#if 0
+ // test decompression
+ CUtlVector<CLightValue> test;
+ test.SetSize( 2048 );
+ pFace->m_CompressedData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ DecompressLightData( &pFace->m_CompressedData, &test );
+#endif
+
+ if( pFace->m_CompressedData.TellPut() == 0 )
+ {
+ // No contribution.. delete this face from the light.
+ EnterCriticalSection( &pLight->m_CS );
+ pLight->m_LightFaces.Remove( pFace->m_LightFacesIndex );
+ delete pFace;
+ LeaveCriticalSection( &pLight->m_CS );
+ }
+ else
+ {
+ // Discard the uncompressed data.
+ pFace->m_LightValues.Purge();
+ m_FacesTouched[ pFace->m_FaceIndex ] = 1;
+ }
+ }
+}
+
+
+bool CIncremental::Finalize()
+{
+ // If we're not being used, don't do anything.
+ if( !m_pIncrementalFilename || !m_pBSPFilename )
+ return false;
+
+ CUtlVector<CFaceLightList> faceLights;
+ LinkLightsToFaces( faceLights );
+
+ Vector faceLight[(MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2) * (MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2)];
+ CUtlVector<CLightValue> faceLightValues;
+ faceLightValues.SetSize( (MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2) * (MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2) );
+
+ // Only update the faces we've touched.
+ for( int facenum = 0; facenum < numfaces; facenum++ )
+ {
+ if( !m_FacesTouched[facenum] || !faceLights[facenum].Count() )
+ continue;
+
+ int w = g_pFaces[facenum].m_LightmapTextureSizeInLuxels[0]+1;
+ int h = g_pFaces[facenum].m_LightmapTextureSizeInLuxels[1]+1;
+ int nLuxels = w * h;
+ assert( nLuxels <= sizeof(faceLight) / sizeof(faceLight[0]) );
+
+ // Clear the lighting for this face.
+ memset( faceLight, 0, nLuxels * sizeof(Vector) );
+
+ // Composite all the light contributions.
+ for( int iFace=0; iFace < faceLights[facenum].Count(); iFace++ )
+ {
+ CLightFace *pFace = faceLights[facenum][iFace];
+
+ pFace->m_CompressedData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ DecompressLightData( &pFace->m_CompressedData, &faceLightValues );
+
+ for( int iSample=0; iSample < nLuxels; iSample++ )
+ {
+ float flDot = faceLightValues[iSample].m_Dot;
+ if( flDot )
+ {
+ VectorMA(
+ faceLight[iSample],
+ flDot / pFace->m_pLight->m_flMaxIntensity,
+ pFace->m_pLight->m_Light.intensity,
+ faceLight[iSample] );
+ }
+ }
+ }
+
+ // Convert to the floating-point representation in the BSP file.
+ Vector *pSrc = faceLight;
+ unsigned char *pDest = &(*pdlightdata)[ g_pFaces[facenum].lightofs ];
+
+ for( int iSample=0; iSample < nLuxels; iSample++ )
+ {
+ VectorToColorRGBExp32( *pSrc, *( ColorRGBExp32 *)pDest );
+ pDest += 4;
+ pSrc++;
+ }
+ }
+
+ m_bSuccessfulRun = true;
+ return true;
+}
+
+
+void CIncremental::GetFacesTouched( CUtlVector<unsigned char> &touched )
+{
+ touched.CopyArray( m_FacesTouched.Base(), m_FacesTouched.Count() );
+}
+
+
+bool CIncremental::Serialize()
+{
+ if( !SaveIncrementalFile() )
+ return false;
+
+ WriteBSPFile( (char*)m_pBSPFilename );
+ return true;
+}
+
+
+void CIncremental::Term()
+{
+ m_Lights.PurgeAndDeleteElements();
+ m_TotalMemory = 0;
+}
+
+
+void CIncremental::AddLightsForActiveLights()
+{
+ // Create our lights.
+ for( directlight_t *dl=activelights; dl != NULL; dl = dl->next )
+ {
+ CIncLight *pLight = new CIncLight;
+ dl->m_IncrementalID = m_Lights.AddToTail( pLight );
+
+ // Copy the light information.
+ pLight->m_Light = dl->light;
+ pLight->m_flMaxIntensity = max( dl->light.intensity[0], max( dl->light.intensity[1], dl->light.intensity[2] ) );
+ }
+}
+
+
+bool CIncremental::LoadIncrementalFile()
+{
+ Term();
+
+ if( !IsIncrementalFileValid() )
+ return false;
+
+ long fp = FileOpen( m_pIncrementalFilename, true );
+ if( !fp )
+ return false;
+
+ // Read the header.
+ CIncrementalHeader hdr;
+ if( !ReadIncrementalHeader( fp, &hdr ) )
+ {
+ FileClose( fp );
+ return false;
+ }
+
+
+ // Read the lights.
+ int nLights;
+ FileRead( fp, nLights );
+ for( int iLight=0; iLight < nLights; iLight++ )
+ {
+ CIncLight *pLight = new CIncLight;
+ m_Lights.AddToTail( pLight );
+
+ FileRead( fp, pLight->m_Light );
+ pLight->m_flMaxIntensity =
+ max( pLight->m_Light.intensity.x,
+ max( pLight->m_Light.intensity.y, pLight->m_Light.intensity.z ) );
+
+ int nFaces;
+ FileRead( fp, nFaces );
+ assert( nFaces < 70000 );
+
+ for( int iFace=0; iFace < nFaces; iFace++ )
+ {
+ CLightFace *pFace = new CLightFace;
+ pLight->m_LightFaces.AddToTail( pFace );
+
+ pFace->m_pLight = pLight;
+ FileRead( fp, pFace->m_FaceIndex );
+
+ int dataSize;
+ FileRead( fp, dataSize );
+
+ pFace->m_CompressedData.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
+ while( dataSize )
+ {
+ --dataSize;
+
+ unsigned char ucData;
+ FileRead( fp, ucData );
+
+ pFace->m_CompressedData.PutUnsignedChar( ucData );
+ }
+ }
+ }
+
+
+ FileClose( fp );
+ return !FileError();
+}
+
+
+bool CIncremental::SaveIncrementalFile()
+{
+ long fp = FileOpen( m_pIncrementalFilename, false );
+ if( !fp )
+ return false;
+
+ if( !WriteIncrementalHeader( fp ) )
+ {
+ FileClose( fp );
+ return false;
+ }
+
+ // Write the lights.
+ int nLights = m_Lights.Count();
+ FileWrite( fp, nLights );
+ for( int iLight=m_Lights.Head(); iLight != m_Lights.InvalidIndex(); iLight = m_Lights.Next( iLight ) )
+ {
+ CIncLight *pLight = m_Lights[iLight];
+
+ FileWrite( fp, pLight->m_Light );
+
+ int nFaces = pLight->m_LightFaces.Count();
+ FileWrite( fp, nFaces );
+ for( int iFace=pLight->m_LightFaces.Head(); iFace != pLight->m_LightFaces.InvalidIndex(); iFace = pLight->m_LightFaces.Next( iFace ) )
+ {
+ CLightFace *pFace = pLight->m_LightFaces[iFace];
+
+ FileWrite( fp, pFace->m_FaceIndex );
+
+ int dataSize = pFace->m_CompressedData.TellPut();
+ FileWrite( fp, dataSize );
+
+ pFace->m_CompressedData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ while( dataSize )
+ {
+ --dataSize;
+ FileWrite( fp, pFace->m_CompressedData.GetUnsignedChar() );
+ }
+ }
+ }
+
+
+ FileClose( fp );
+ return !FileError();
+}
+
+
+void CIncremental::LinkLightsToFaces( CUtlVector<CFaceLightList> &faceLights )
+{
+ faceLights.SetSize( numfaces );
+
+ for( int iLight=m_Lights.Head(); iLight != m_Lights.InvalidIndex(); iLight = m_Lights.Next( iLight ) )
+ {
+ CIncLight *pLight = m_Lights[iLight];
+
+ for( int iFace=pLight->m_LightFaces.Head(); iFace != pLight->m_LightFaces.InvalidIndex(); iFace = pLight->m_LightFaces.Next( iFace ) )
+ {
+ CLightFace *pFace = pLight->m_LightFaces[iFace];
+
+ if( m_FacesTouched[pFace->m_FaceIndex] )
+ faceLights[ pFace->m_FaceIndex ].AddToTail( pFace );
+ }
+ }
+}
+
+
+// ------------------------------------------------------------------ //
+// CIncLight
+// ------------------------------------------------------------------ //
+
+CIncLight::CIncLight()
+{
+ memset( m_pCachedFaces, 0, sizeof(m_pCachedFaces) );
+ InitializeCriticalSection( &m_CS );
+}
+
+
+CIncLight::~CIncLight()
+{
+ m_LightFaces.PurgeAndDeleteElements();
+ DeleteCriticalSection( &m_CS );
+}
+
+
+CLightFace* CIncLight::FindOrCreateLightFace( int iFace, int lmSize, bool *bNew )
+{
+ if( bNew )
+ *bNew = false;
+
+
+ // Look for it.
+ for( int i=m_LightFaces.Head(); i != m_LightFaces.InvalidIndex(); i=m_LightFaces.Next(i) )
+ {
+ CLightFace *pFace = m_LightFaces[i];
+
+ if( pFace->m_FaceIndex == iFace )
+ {
+ assert( pFace->m_LightValues.Count() == lmSize );
+ return pFace;
+ }
+ }
+
+ // Ok, create one.
+ CLightFace *pFace = new CLightFace;
+ pFace->m_LightFacesIndex = m_LightFaces.AddToTail( pFace );
+ pFace->m_pLight = this;
+
+ pFace->m_FaceIndex = iFace;
+ pFace->m_LightValues.SetSize( lmSize );
+ memset( pFace->m_LightValues.Base(), 0, sizeof( CLightValue ) * lmSize );
+
+ if( bNew )
+ *bNew = true;
+
+ return pFace;
+}
+
+
diff --git a/mp/src/utils/vrad/incremental.h b/mp/src/utils/vrad/incremental.h
index fdac144e..9e98054b 100644
--- a/mp/src/utils/vrad/incremental.h
+++ b/mp/src/utils/vrad/incremental.h
@@ -1,175 +1,175 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef INCREMENTAL_H
-#define INCREMENTAL_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-
-#include "iincremental.h"
-#include "utllinkedlist.h"
-#include "utlvector.h"
-#include "utlbuffer.h"
-#include "vrad.h"
-
-
-#define INCREMENTALFILE_VERSION 31241
-
-
-class CIncLight;
-
-
-class CLightValue
-{
-public:
- float m_Dot;
-};
-
-
-class CLightFace
-{
-public:
- unsigned short m_FaceIndex; // global face index
- unsigned short m_LightFacesIndex; // index into CIncLight::m_LightFaces.
-
- // The lightmap grid for this face. Only used while building lighting data for a face.
- // Compressed into m_CompressedData immediately afterwards.
- CUtlVector<CLightValue> m_LightValues;
-
- CUtlBuffer m_CompressedData;
- CIncLight *m_pLight;
-};
-
-
-class CIncLight
-{
-public:
- CIncLight();
- ~CIncLight();
-
- CLightFace* FindOrCreateLightFace( int iFace, int lmSize, bool *bNew=NULL );
-
-
-public:
-
- CRITICAL_SECTION m_CS;
-
- // This is the light for which m_LightFaces was built.
- dworldlight_t m_Light;
-
- CLightFace *m_pCachedFaces[MAX_TOOL_THREADS+1];
-
- // The list of faces that this light contributes to.
- CUtlLinkedList<CLightFace*, unsigned short> m_LightFaces;
-
- // Largest value in intensity of light. Used to scale dot products up into a
- // range where their values make sense.
- float m_flMaxIntensity;
-};
-
-
-class CIncrementalHeader
-{
-public:
- class CLMSize
- {
- public:
- unsigned char m_Width;
- unsigned char m_Height;
- };
-
- CUtlVector<CLMSize> m_FaceLightmapSizes;
-};
-
-
-class CIncremental : public IIncremental
-{
-public:
-
- CIncremental();
- ~CIncremental();
-
-
-
-// IIncremental overrides.
-public:
-
- virtual bool Init( char const *pBSPFilename, char const *pIncrementalFilename );
-
- // Load the light definitions out of the incremental file.
- // Figure out which lights have changed.
- // Change 'activelights' to only consist of new or changed lights.
- virtual bool PrepareForLighting();
-
- virtual void AddLightToFace(
- IncrementalLightID lightID,
- int iFace,
- int iSample,
- int lmSize,
- float dot,
- int iThread );
-
- virtual void FinishFace(
- IncrementalLightID lightID,
- int iFace,
- int iThread );
-
- // For each face that was changed during the lighting process, save out
- // new data for it in the incremental file.
- virtual bool Finalize();
-
- virtual void GetFacesTouched( CUtlVector<unsigned char> &touched );
-
- virtual bool Serialize();
-
-
-private:
-
- // Read/write the header from the file.
- bool ReadIncrementalHeader( long fp, CIncrementalHeader *pHeader );
- bool WriteIncrementalHeader( long fp );
-
- // Returns true if the incremental file is valid and we can use InitUpdate.
- bool IsIncrementalFileValid();
-
- void Term();
-
- // For each light in 'activelights', add a light to m_Lights and link them together.
- void AddLightsForActiveLights();
-
- // Load and save the state.
- bool LoadIncrementalFile();
- bool SaveIncrementalFile();
-
- typedef CUtlVector<CLightFace*> CFaceLightList;
- void LinkLightsToFaces( CUtlVector<CFaceLightList> &faceLights );
-
-
-private:
-
- char const *m_pIncrementalFilename;
- char const *m_pBSPFilename;
-
- CUtlLinkedList<CIncLight*, IncrementalLightID>
- m_Lights;
-
- // The face index is set to 1 if a face has new lighting data applied to it.
- // This is used to optimize the set of lightmaps we recomposite.
- CUtlVector<unsigned char> m_FacesTouched;
-
- int m_TotalMemory;
-
- // Set to true when one or more runs were completed successfully.
- bool m_bSuccessfulRun;
-};
-
-
-
-#endif // INCREMENTAL_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef INCREMENTAL_H
+#define INCREMENTAL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+
+#include "iincremental.h"
+#include "utllinkedlist.h"
+#include "utlvector.h"
+#include "utlbuffer.h"
+#include "vrad.h"
+
+
+#define INCREMENTALFILE_VERSION 31241
+
+
+class CIncLight;
+
+
+class CLightValue
+{
+public:
+ float m_Dot;
+};
+
+
+class CLightFace
+{
+public:
+ unsigned short m_FaceIndex; // global face index
+ unsigned short m_LightFacesIndex; // index into CIncLight::m_LightFaces.
+
+ // The lightmap grid for this face. Only used while building lighting data for a face.
+ // Compressed into m_CompressedData immediately afterwards.
+ CUtlVector<CLightValue> m_LightValues;
+
+ CUtlBuffer m_CompressedData;
+ CIncLight *m_pLight;
+};
+
+
+class CIncLight
+{
+public:
+ CIncLight();
+ ~CIncLight();
+
+ CLightFace* FindOrCreateLightFace( int iFace, int lmSize, bool *bNew=NULL );
+
+
+public:
+
+ CRITICAL_SECTION m_CS;
+
+ // This is the light for which m_LightFaces was built.
+ dworldlight_t m_Light;
+
+ CLightFace *m_pCachedFaces[MAX_TOOL_THREADS+1];
+
+ // The list of faces that this light contributes to.
+ CUtlLinkedList<CLightFace*, unsigned short> m_LightFaces;
+
+ // Largest value in intensity of light. Used to scale dot products up into a
+ // range where their values make sense.
+ float m_flMaxIntensity;
+};
+
+
+class CIncrementalHeader
+{
+public:
+ class CLMSize
+ {
+ public:
+ unsigned char m_Width;
+ unsigned char m_Height;
+ };
+
+ CUtlVector<CLMSize> m_FaceLightmapSizes;
+};
+
+
+class CIncremental : public IIncremental
+{
+public:
+
+ CIncremental();
+ ~CIncremental();
+
+
+
+// IIncremental overrides.
+public:
+
+ virtual bool Init( char const *pBSPFilename, char const *pIncrementalFilename );
+
+ // Load the light definitions out of the incremental file.
+ // Figure out which lights have changed.
+ // Change 'activelights' to only consist of new or changed lights.
+ virtual bool PrepareForLighting();
+
+ virtual void AddLightToFace(
+ IncrementalLightID lightID,
+ int iFace,
+ int iSample,
+ int lmSize,
+ float dot,
+ int iThread );
+
+ virtual void FinishFace(
+ IncrementalLightID lightID,
+ int iFace,
+ int iThread );
+
+ // For each face that was changed during the lighting process, save out
+ // new data for it in the incremental file.
+ virtual bool Finalize();
+
+ virtual void GetFacesTouched( CUtlVector<unsigned char> &touched );
+
+ virtual bool Serialize();
+
+
+private:
+
+ // Read/write the header from the file.
+ bool ReadIncrementalHeader( long fp, CIncrementalHeader *pHeader );
+ bool WriteIncrementalHeader( long fp );
+
+ // Returns true if the incremental file is valid and we can use InitUpdate.
+ bool IsIncrementalFileValid();
+
+ void Term();
+
+ // For each light in 'activelights', add a light to m_Lights and link them together.
+ void AddLightsForActiveLights();
+
+ // Load and save the state.
+ bool LoadIncrementalFile();
+ bool SaveIncrementalFile();
+
+ typedef CUtlVector<CLightFace*> CFaceLightList;
+ void LinkLightsToFaces( CUtlVector<CFaceLightList> &faceLights );
+
+
+private:
+
+ char const *m_pIncrementalFilename;
+ char const *m_pBSPFilename;
+
+ CUtlLinkedList<CIncLight*, IncrementalLightID>
+ m_Lights;
+
+ // The face index is set to 1 if a face has new lighting data applied to it.
+ // This is used to optimize the set of lightmaps we recomposite.
+ CUtlVector<unsigned char> m_FacesTouched;
+
+ int m_TotalMemory;
+
+ // Set to true when one or more runs were completed successfully.
+ bool m_bSuccessfulRun;
+};
+
+
+
+#endif // INCREMENTAL_H
diff --git a/mp/src/utils/vrad/leaf_ambient_lighting.cpp b/mp/src/utils/vrad/leaf_ambient_lighting.cpp
index ea26c8c6..3836592e 100644
--- a/mp/src/utils/vrad/leaf_ambient_lighting.cpp
+++ b/mp/src/utils/vrad/leaf_ambient_lighting.cpp
@@ -1,708 +1,708 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "vrad.h"
-#include "leaf_ambient_lighting.h"
-#include "bsplib.h"
-#include "vraddetailprops.h"
-#include "mathlib/anorms.h"
-#include "pacifier.h"
-#include "coordsize.h"
-#include "vstdlib/random.h"
-#include "bsptreedata.h"
-#include "messbuf.h"
-#include "vmpi.h"
-#include "vmpi_distribute_work.h"
-
-static TableVector g_BoxDirections[6] =
-{
- { 1, 0, 0 },
- { -1, 0, 0 },
- { 0, 1, 0 },
- { 0, -1, 0 },
- { 0, 0, 1 },
- { 0, 0, -1 },
-};
-
-
-
-static void ComputeAmbientFromSurface( dface_t *surfID, dworldlight_t* pSkylight,
- Vector& radcolor )
-{
- if ( !surfID )
- return;
-
- texinfo_t *pTexInfo = &texinfo[surfID->texinfo];
-
- // If we hit the sky, use the sky ambient
- if ( pTexInfo->flags & SURF_SKY )
- {
- if ( pSkylight )
- {
- // add in sky ambient
- VectorCopy( pSkylight->intensity, radcolor );
- }
- }
- else
- {
- Vector reflectivity = dtexdata[pTexInfo->texdata].reflectivity;
- VectorMultiply( radcolor, reflectivity, radcolor );
- }
-}
-
-
-// TODO: it's CRAZY how much lighting code we share with the engine. It should all be shared code.
-float Engine_WorldLightAngle( const dworldlight_t *wl, const Vector& lnormal, const Vector& snormal, const Vector& delta )
-{
- float dot, dot2;
-
- Assert( wl->type == emit_surface );
-
- dot = DotProduct( snormal, delta );
- if (dot < 0)
- return 0;
-
- dot2 = -DotProduct (delta, lnormal);
- if (dot2 <= ON_EPSILON/10)
- return 0; // behind light surface
-
- return dot * dot2;
-}
-
-
-// TODO: it's CRAZY how much lighting code we share with the engine. It should all be shared code.
-float Engine_WorldLightDistanceFalloff( const dworldlight_t *wl, const Vector& delta )
-{
- Assert( wl->type == emit_surface );
-
- // Cull out stuff that's too far
- if (wl->radius != 0)
- {
- if ( DotProduct( delta, delta ) > (wl->radius * wl->radius))
- return 0.0f;
- }
-
- return InvRSquared(delta);
-}
-
-
-void AddEmitSurfaceLights( const Vector &vStart, Vector lightBoxColor[6] )
-{
- fltx4 fractionVisible;
-
- FourVectors vStart4, wlOrigin4;
- vStart4.DuplicateVector ( vStart );
-
- for ( int iLight=0; iLight < *pNumworldlights; iLight++ )
- {
- dworldlight_t *wl = &dworldlights[iLight];
-
- // Should this light even go in the ambient cubes?
- if ( !( wl->flags & DWL_FLAGS_INAMBIENTCUBE ) )
- continue;
-
- Assert( wl->type == emit_surface );
-
- // Can this light see the point?
- wlOrigin4.DuplicateVector ( wl->origin );
- TestLine ( vStart4, wlOrigin4, &fractionVisible );
- if ( !TestSignSIMD ( CmpGtSIMD ( fractionVisible, Four_Zeros ) ) )
- continue;
-
- // Add this light's contribution.
- Vector vDelta = wl->origin - vStart;
- float flDistanceScale = Engine_WorldLightDistanceFalloff( wl, vDelta );
-
- Vector vDeltaNorm = vDelta;
- VectorNormalize( vDeltaNorm );
- float flAngleScale = Engine_WorldLightAngle( wl, wl->normal, vDeltaNorm, vDeltaNorm );
-
- float ratio = flDistanceScale * flAngleScale * SubFloat ( fractionVisible, 0 );
- if ( ratio == 0 )
- continue;
-
- for ( int i=0; i < 6; i++ )
- {
- float t = DotProduct( g_BoxDirections[i], vDeltaNorm );
- if ( t > 0 )
- {
- lightBoxColor[i] += wl->intensity * (t * ratio);
- }
- }
- }
-}
-
-
-void ComputeAmbientFromSphericalSamples( int iThread, const Vector &vStart, Vector lightBoxColor[6] )
-{
- // Figure out the color that rays hit when shot out from this position.
- Vector radcolor[NUMVERTEXNORMALS];
- float tanTheta = tan(VERTEXNORMAL_CONE_INNER_ANGLE);
-
- for ( int i = 0; i < NUMVERTEXNORMALS; i++ )
- {
- Vector vEnd = vStart + g_anorms[i] * (COORD_EXTENT * 1.74);
-
- // Now that we've got a ray, see what surface we've hit
- Vector lightStyleColors[MAX_LIGHTSTYLES];
- lightStyleColors[0].Init(); // We only care about light style 0 here.
- CalcRayAmbientLighting( iThread, vStart, vEnd, tanTheta, lightStyleColors );
-
- radcolor[i] = lightStyleColors[0];
- }
-
- // accumulate samples into radiant box
- for ( int j = 6; --j >= 0; )
- {
- float t = 0;
-
- lightBoxColor[j].Init();
-
- for (int i = 0; i < NUMVERTEXNORMALS; i++)
- {
- float c = DotProduct( g_anorms[i], g_BoxDirections[j] );
- if (c > 0)
- {
- t += c;
- lightBoxColor[j] += radcolor[i] * c;
- }
- }
-
- lightBoxColor[j] *= 1/t;
- }
-
- // Now add direct light from the emit_surface lights. These go in the ambient cube because
- // there are a ton of them and they are often so dim that they get filtered out by r_worldlightmin.
- AddEmitSurfaceLights( vStart, lightBoxColor );
-}
-
-
-bool IsLeafAmbientSurfaceLight( dworldlight_t *wl )
-{
- static const float g_flWorldLightMinEmitSurface = 0.005f;
- static const float g_flWorldLightMinEmitSurfaceDistanceRatio = ( InvRSquared( Vector( 0, 0, 512 ) ) );
-
- if ( wl->type != emit_surface )
- return false;
-
- if ( wl->style != 0 )
- return false;
-
- float intensity = max( wl->intensity[0], wl->intensity[1] );
- intensity = max( intensity, wl->intensity[2] );
-
- return (intensity * g_flWorldLightMinEmitSurfaceDistanceRatio) < g_flWorldLightMinEmitSurface;
-}
-
-
-class CLeafSampler
-{
-public:
- CLeafSampler( int iThread ) : m_iThread(iThread) {}
-
- // Generate a random point in the leaf's bounding volume
- // reject any points that aren't actually in the leaf
- // do a couple of tracing heuristics to eliminate points that are inside detail brushes
- // or underneath displacement surfaces in the leaf
- // return once we have a valid point, use the center if one can't be computed quickly
- void GenerateLeafSamplePosition( int leafIndex, const CUtlVector<dplane_t> &leafPlanes, Vector &samplePosition )
- {
- dleaf_t *pLeaf = dleafs + leafIndex;
-
- float dx = pLeaf->maxs[0] - pLeaf->mins[0];
- float dy = pLeaf->maxs[1] - pLeaf->mins[1];
- float dz = pLeaf->maxs[2] - pLeaf->mins[2];
- bool bValid = false;
- for ( int i = 0; i < 1000 && !bValid; i++ )
- {
- samplePosition.x = pLeaf->mins[0] + m_random.RandomFloat(0, dx);
- samplePosition.y = pLeaf->mins[1] + m_random.RandomFloat(0, dy);
- samplePosition.z = pLeaf->mins[2] + m_random.RandomFloat(0, dz);
- bValid = true;
-
- for ( int j = leafPlanes.Count(); --j >= 0 && bValid; )
- {
- float d = DotProduct(leafPlanes[j].normal, samplePosition) - leafPlanes[j].dist;
- if ( d < DIST_EPSILON )
- {
- // not inside the leaf, try again
- bValid = false;
- break;
- }
- }
- if ( !bValid )
- continue;
-
- for ( int j = 0; j < 6; j++ )
- {
- Vector start = samplePosition;
- int axis = j%3;
- start[axis] = (j<3) ? pLeaf->mins[axis] : pLeaf->maxs[axis];
- float t;
- Vector normal;
- CastRayInLeaf( m_iThread, samplePosition, start, leafIndex, &t, &normal );
- if ( t == 0.0f )
- {
- // inside a func_detail, try again.
- bValid = false;
- break;
- }
- if ( t != 1.0f )
- {
- Vector delta = start - samplePosition;
- if ( DotProduct(delta, normal) > 0 )
- {
- // hit backside of displacement, try again.
- bValid = false;
- break;
- }
- }
- }
- }
- if ( !bValid )
- {
- // didn't generate a valid sample point, just use the center of the leaf bbox
- samplePosition = ( Vector( pLeaf->mins[0], pLeaf->mins[1], pLeaf->mins[2] ) + Vector( pLeaf->maxs[0], pLeaf->maxs[1], pLeaf->maxs[2] ) ) * 0.5f;
- }
- }
-
-private:
- int m_iThread;
- CUniformRandomStream m_random;
-};
-
-// gets a list of the planes pointing into a leaf
-void GetLeafBoundaryPlanes( CUtlVector<dplane_t> &list, int leafIndex )
-{
- list.RemoveAll();
- int nodeIndex = leafparents[leafIndex];
- int child = -(leafIndex + 1);
- while ( nodeIndex >= 0 )
- {
- dnode_t *pNode = dnodes + nodeIndex;
- dplane_t *pNodePlane = dplanes + pNode->planenum;
- if ( pNode->children[0] == child )
- {
- // front side
- list.AddToTail( *pNodePlane );
- }
- else
- {
- // back side
- int plane = list.AddToTail();
- list[plane].dist = -pNodePlane->dist;
- list[plane].normal = -pNodePlane->normal;
- list[plane].type = pNodePlane->type;
- }
- child = nodeIndex;
- nodeIndex = nodeparents[child];
- }
-}
-
-// this stores each sample of the ambient lighting
-struct ambientsample_t
-{
- Vector pos;
- Vector cube[6];
-};
-
-// add the sample to the list. If we exceed the maximum number of samples, the worst sample will
-// be discarded. This has the effect of converging on the best samples when enough are added.
-void AddSampleToList( CUtlVector<ambientsample_t> &list, const Vector &samplePosition, Vector *pCube )
-{
- const int MAX_SAMPLES = 16;
-
- int index = list.AddToTail();
- list[index].pos = samplePosition;
- for ( int i = 0; i < 6; i++ )
- {
- list[index].cube[i] = pCube[i];
- }
-
- if ( list.Count() <= MAX_SAMPLES )
- return;
-
- int nearestNeighborIndex = 0;
- float nearestNeighborDist = FLT_MAX;
- float nearestNeighborTotal = 0;
- for ( int i = 0; i < list.Count(); i++ )
- {
- int closestIndex = 0;
- float closestDist = FLT_MAX;
- float totalDC = 0;
- for ( int j = 0; j < list.Count(); j++ )
- {
- if ( j == i )
- continue;
- float dist = (list[i].pos - list[j].pos).Length();
- float maxDC = 0;
- for ( int k = 0; k < 6; k++ )
- {
- // color delta is computed per-component, per cube side
- for (int s = 0; s < 3; s++ )
- {
- float dc = fabs(list[i].cube[k][s] - list[j].cube[k][s]);
- maxDC = max(maxDC,dc);
- }
- totalDC += maxDC;
- }
- // need a measurable difference in color or we'll just rely on position
- if ( maxDC < 1e-4f )
- {
- maxDC = 0;
- }
- else if ( maxDC > 1.0f )
- {
- maxDC = 1.0f;
- }
- // selection criteria is 10% distance, 90% color difference
- // choose samples that fill the space (large distance from each other)
- // and have largest color variation
- float distanceFactor = 0.1f + (maxDC * 0.9f);
- dist *= distanceFactor;
-
- // find the "closest" sample to this one
- if ( dist < closestDist )
- {
- closestDist = dist;
- closestIndex = j;
- }
- }
- // the sample with the "closest" neighbor is rejected
- if ( closestDist < nearestNeighborDist || (closestDist == nearestNeighborDist && totalDC < nearestNeighborTotal) )
- {
- nearestNeighborDist = closestDist;
- nearestNeighborIndex = i;
- }
- }
- list.FastRemove( nearestNeighborIndex );
-}
-
-// max number of units in gamma space of per-side delta
-int CubeDeltaGammaSpace( Vector *pCube0, Vector *pCube1 )
-{
- int maxDelta = 0;
- // do this comparison in gamma space to try and get a perceptual basis for the compare
- for ( int i = 0; i < 6; i++ )
- {
- for ( int j = 0; j < 3; j++ )
- {
- int val0 = LinearToScreenGamma( pCube0[i][j] );
- int val1 = LinearToScreenGamma( pCube1[i][j] );
- int delta = abs(val0-val1);
- if ( delta > maxDelta )
- maxDelta = delta;
- }
- }
- return maxDelta;
-}
-// reconstruct the ambient lighting for a leaf at the given position in worldspace
-// optionally skip one of the entries in the list
-void Mod_LeafAmbientColorAtPos( Vector *pOut, const Vector &pos, const CUtlVector<ambientsample_t> &list, int skipIndex )
-{
- for ( int i = 0; i < 6; i++ )
- {
- pOut[i].Init();
- }
- float totalFactor = 0;
- for ( int i = 0; i < list.Count(); i++ )
- {
- if ( i == skipIndex )
- continue;
- // do an inverse squared distance weighted average of the samples to reconstruct
- // the original function
- float dist = (list[i].pos - pos).LengthSqr();
- float factor = 1.0f / (dist + 1.0f);
- totalFactor += factor;
- for ( int j = 0; j < 6; j++ )
- {
- pOut[j] += list[i].cube[j] * factor;
- }
- }
- for ( int i = 0; i < 6; i++ )
- {
- pOut[i] *= (1.0f / totalFactor);
- }
-}
-
-// this samples the lighting at each sample and removes any unnecessary samples
-void CompressAmbientSampleList( CUtlVector<ambientsample_t> &list )
-{
- Vector testCube[6];
- for ( int i = 0; i < list.Count(); i++ )
- {
- if ( list.Count() > 1 )
- {
- Mod_LeafAmbientColorAtPos( testCube, list[i].pos, list, i );
- if ( CubeDeltaGammaSpace(testCube, list[i].cube) < 3 )
- {
- list.FastRemove(i);
- i--;
- }
- }
- }
-}
-
-// basically this is an intersection routine that returns a distance between the boxes
-float AABBDistance( const Vector &mins0, const Vector &maxs0, const Vector &mins1, const Vector &maxs1 )
-{
- Vector delta;
- for ( int i = 0; i < 3; i++ )
- {
- float greatestMin = max(mins0[i], mins1[i]);
- float leastMax = min(maxs0[i], maxs1[i]);
- delta[i] = (greatestMin < leastMax) ? 0 : (leastMax - greatestMin);
- }
- return delta.Length();
-}
-
-// build a list of leaves from a query
-class CLeafList : public ISpatialLeafEnumerator
-{
-public:
- virtual bool EnumerateLeaf( int leaf, int context )
- {
- m_list.AddToTail(leaf);
- return true;
- }
-
- CUtlVector<int> m_list;
-};
-
-// conver short[3] to vector
-static void LeafBounds( int leafIndex, Vector &mins, Vector &maxs )
-{
- for ( int i = 0; i < 3; i++ )
- {
- mins[i] = dleafs[leafIndex].mins[i];
- maxs[i] = dleafs[leafIndex].maxs[i];
- }
-}
-
-// returns the index of the nearest leaf with ambient samples
-int NearestNeighborWithLight(int leafID)
-{
- Vector mins, maxs;
- LeafBounds( leafID, mins, maxs );
- Vector size = maxs - mins;
- CLeafList leafList;
- ToolBSPTree()->EnumerateLeavesInBox( mins-size, maxs+size, &leafList, 0 );
- float bestDist = FLT_MAX;
- int bestIndex = leafID;
- for ( int i = 0; i < leafList.m_list.Count(); i++ )
- {
- int testIndex = leafList.m_list[i];
- if ( !g_pLeafAmbientIndex->Element(testIndex).ambientSampleCount )
- continue;
-
- Vector testMins, testMaxs;
- LeafBounds( testIndex, testMins, testMaxs );
- float dist = AABBDistance( mins, maxs, testMins, testMaxs );
- if ( dist < bestDist )
- {
- bestDist = dist;
- bestIndex = testIndex;
- }
- }
- return bestIndex;
-}
-
-// maps a float to a byte fraction between min & max
-static byte Fixed8Fraction( float t, float tMin, float tMax )
-{
- if ( tMax <= tMin )
- return 0;
-
- float frac = RemapValClamped( t, tMin, tMax, 0.0f, 255.0f );
- return byte(frac+0.5f);
-}
-
-CUtlVector< CUtlVector<ambientsample_t> > g_LeafAmbientSamples;
-
-void ComputeAmbientForLeaf( int iThread, int leafID, CUtlVector<ambientsample_t> &list )
-{
- CUtlVector<dplane_t> leafPlanes;
- CLeafSampler sampler( iThread );
-
- GetLeafBoundaryPlanes( leafPlanes, leafID );
- list.RemoveAll();
- // this heuristic tries to generate at least one sample per volume (chosen to be similar to the size of a player) in the space
- int xSize = (dleafs[leafID].maxs[0] - dleafs[leafID].mins[0]) / 32;
- int ySize = (dleafs[leafID].maxs[1] - dleafs[leafID].mins[1]) / 32;
- int zSize = (dleafs[leafID].maxs[2] - dleafs[leafID].mins[2]) / 64;
- xSize = max(xSize,1);
- ySize = max(xSize,1);
- zSize = max(xSize,1);
- // generate update 128 candidate samples, always at least one sample
- int volumeCount = xSize * ySize * zSize;
- if ( g_bFastAmbient )
- {
- // save compute time, only do one sample
- volumeCount = 1;
- }
- int sampleCount = clamp( volumeCount, 1, 128 );
- if ( dleafs[leafID].contents & CONTENTS_SOLID )
- {
- // don't generate any samples in solid leaves
- // NOTE: We copy the nearest non-solid leaf sample pointers into this leaf at the end
- return;
- }
- Vector cube[6];
- for ( int i = 0; i < sampleCount; i++ )
- {
- // compute each candidate sample and add to the list
- Vector samplePosition;
- sampler.GenerateLeafSamplePosition( leafID, leafPlanes, samplePosition );
- ComputeAmbientFromSphericalSamples( iThread, samplePosition, cube );
- // note this will remove the least valuable sample once the limit is reached
- AddSampleToList( list, samplePosition, cube );
- }
-
- // remove any samples that can be reconstructed with the remaining data
- CompressAmbientSampleList( list );
-}
-
-static void ThreadComputeLeafAmbient( int iThread, void *pUserData )
-{
- CUtlVector<ambientsample_t> list;
- while (1)
- {
- int leafID = GetThreadWork ();
- if (leafID == -1)
- break;
- list.RemoveAll();
- ComputeAmbientForLeaf(iThread, leafID, list);
- // copy to the output array
- g_LeafAmbientSamples[leafID].SetCount( list.Count() );
- for ( int i = 0; i < list.Count(); i++ )
- {
- g_LeafAmbientSamples[leafID].Element(i) = list.Element(i);
- }
- }
-}
-
-void VMPI_ProcessLeafAmbient( int iThread, uint64 iLeaf, MessageBuffer *pBuf )
-{
- CUtlVector<ambientsample_t> list;
- ComputeAmbientForLeaf(iThread, (int)iLeaf, list);
-
- VMPI_SetCurrentStage( "EncodeLeafAmbientResults" );
-
- // Encode the results.
- int nSamples = list.Count();
- pBuf->write( &nSamples, sizeof( nSamples ) );
- if ( nSamples )
- {
- pBuf->write( list.Base(), list.Count() * sizeof( ambientsample_t ) );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Called on the master when a worker finishes processing a static prop.
-//-----------------------------------------------------------------------------
-void VMPI_ReceiveLeafAmbientResults( uint64 leafID, MessageBuffer *pBuf, int iWorker )
-{
- // Decode the results.
- int nSamples;
- pBuf->read( &nSamples, sizeof( nSamples ) );
-
- g_LeafAmbientSamples[leafID].SetCount( nSamples );
- if ( nSamples )
- {
- pBuf->read(g_LeafAmbientSamples[leafID].Base(), nSamples * sizeof(ambientsample_t) );
- }
-}
-
-
-void ComputePerLeafAmbientLighting()
-{
- // Figure out which lights should go in the per-leaf ambient cubes.
- int nInAmbientCube = 0;
- int nSurfaceLights = 0;
- for ( int i=0; i < *pNumworldlights; i++ )
- {
- dworldlight_t *wl = &dworldlights[i];
-
- if ( IsLeafAmbientSurfaceLight( wl ) )
- wl->flags |= DWL_FLAGS_INAMBIENTCUBE;
- else
- wl->flags &= ~DWL_FLAGS_INAMBIENTCUBE;
-
- if ( wl->type == emit_surface )
- ++nSurfaceLights;
-
- if ( wl->flags & DWL_FLAGS_INAMBIENTCUBE )
- ++nInAmbientCube;
- }
-
- Msg( "%d of %d (%d%% of) surface lights went in leaf ambient cubes.\n", nInAmbientCube, nSurfaceLights, nSurfaceLights ? ((nInAmbientCube*100) / nSurfaceLights) : 0 );
-
- g_LeafAmbientSamples.SetCount(numleafs);
-
- if ( g_bUseMPI )
- {
- // Distribute the work among the workers.
- VMPI_SetCurrentStage( "ComputeLeafAmbientLighting" );
- DistributeWork( numleafs, VMPI_DISTRIBUTEWORK_PACKETID, VMPI_ProcessLeafAmbient, VMPI_ReceiveLeafAmbientResults );
- }
- else
- {
- RunThreadsOn(numleafs, true, ThreadComputeLeafAmbient);
- }
-
- // now write out the data
- Msg("Writing leaf ambient...");
- g_pLeafAmbientIndex->RemoveAll();
- g_pLeafAmbientLighting->RemoveAll();
- g_pLeafAmbientIndex->SetCount( numleafs );
- g_pLeafAmbientLighting->EnsureCapacity( numleafs*4 );
- for ( int leafID = 0; leafID < numleafs; leafID++ )
- {
- const CUtlVector<ambientsample_t> &list = g_LeafAmbientSamples[leafID];
- g_pLeafAmbientIndex->Element(leafID).ambientSampleCount = list.Count();
- if ( !list.Count() )
- {
- g_pLeafAmbientIndex->Element(leafID).firstAmbientSample = 0;
- }
- else
- {
- g_pLeafAmbientIndex->Element(leafID).firstAmbientSample = g_pLeafAmbientLighting->Count();
- // compute the samples in disk format. Encode the positions in 8-bits using leaf bounds fractions
- for ( int i = 0; i < list.Count(); i++ )
- {
- int outIndex = g_pLeafAmbientLighting->AddToTail();
- dleafambientlighting_t &light = g_pLeafAmbientLighting->Element(outIndex);
-
- light.x = Fixed8Fraction( list[i].pos.x, dleafs[leafID].mins[0], dleafs[leafID].maxs[0] );
- light.y = Fixed8Fraction( list[i].pos.y, dleafs[leafID].mins[1], dleafs[leafID].maxs[1] );
- light.z = Fixed8Fraction( list[i].pos.z, dleafs[leafID].mins[2], dleafs[leafID].maxs[2] );
- light.pad = 0;
- for ( int side = 0; side < 6; side++ )
- {
- VectorToColorRGBExp32( list[i].cube[side], light.cube.m_Color[side] );
- }
- }
- }
- }
- for ( int i = 0; i < numleafs; i++ )
- {
- // UNDONE: Do this dynamically in the engine instead. This will allow us to sample across leaf
- // boundaries always which should improve the quality of lighting in general
- if ( g_pLeafAmbientIndex->Element(i).ambientSampleCount == 0 )
- {
- if ( !(dleafs[i].contents & CONTENTS_SOLID) )
- {
- Msg("Bad leaf ambient for leaf %d\n", i );
- }
-
- int refLeaf = NearestNeighborWithLight(i);
- g_pLeafAmbientIndex->Element(i).ambientSampleCount = 0;
- g_pLeafAmbientIndex->Element(i).firstAmbientSample = refLeaf;
- }
- }
- Msg("done\n");
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "vrad.h"
+#include "leaf_ambient_lighting.h"
+#include "bsplib.h"
+#include "vraddetailprops.h"
+#include "mathlib/anorms.h"
+#include "pacifier.h"
+#include "coordsize.h"
+#include "vstdlib/random.h"
+#include "bsptreedata.h"
+#include "messbuf.h"
+#include "vmpi.h"
+#include "vmpi_distribute_work.h"
+
+static TableVector g_BoxDirections[6] =
+{
+ { 1, 0, 0 },
+ { -1, 0, 0 },
+ { 0, 1, 0 },
+ { 0, -1, 0 },
+ { 0, 0, 1 },
+ { 0, 0, -1 },
+};
+
+
+
+static void ComputeAmbientFromSurface( dface_t *surfID, dworldlight_t* pSkylight,
+ Vector& radcolor )
+{
+ if ( !surfID )
+ return;
+
+ texinfo_t *pTexInfo = &texinfo[surfID->texinfo];
+
+ // If we hit the sky, use the sky ambient
+ if ( pTexInfo->flags & SURF_SKY )
+ {
+ if ( pSkylight )
+ {
+ // add in sky ambient
+ VectorCopy( pSkylight->intensity, radcolor );
+ }
+ }
+ else
+ {
+ Vector reflectivity = dtexdata[pTexInfo->texdata].reflectivity;
+ VectorMultiply( radcolor, reflectivity, radcolor );
+ }
+}
+
+
+// TODO: it's CRAZY how much lighting code we share with the engine. It should all be shared code.
+float Engine_WorldLightAngle( const dworldlight_t *wl, const Vector& lnormal, const Vector& snormal, const Vector& delta )
+{
+ float dot, dot2;
+
+ Assert( wl->type == emit_surface );
+
+ dot = DotProduct( snormal, delta );
+ if (dot < 0)
+ return 0;
+
+ dot2 = -DotProduct (delta, lnormal);
+ if (dot2 <= ON_EPSILON/10)
+ return 0; // behind light surface
+
+ return dot * dot2;
+}
+
+
+// TODO: it's CRAZY how much lighting code we share with the engine. It should all be shared code.
+float Engine_WorldLightDistanceFalloff( const dworldlight_t *wl, const Vector& delta )
+{
+ Assert( wl->type == emit_surface );
+
+ // Cull out stuff that's too far
+ if (wl->radius != 0)
+ {
+ if ( DotProduct( delta, delta ) > (wl->radius * wl->radius))
+ return 0.0f;
+ }
+
+ return InvRSquared(delta);
+}
+
+
+void AddEmitSurfaceLights( const Vector &vStart, Vector lightBoxColor[6] )
+{
+ fltx4 fractionVisible;
+
+ FourVectors vStart4, wlOrigin4;
+ vStart4.DuplicateVector ( vStart );
+
+ for ( int iLight=0; iLight < *pNumworldlights; iLight++ )
+ {
+ dworldlight_t *wl = &dworldlights[iLight];
+
+ // Should this light even go in the ambient cubes?
+ if ( !( wl->flags & DWL_FLAGS_INAMBIENTCUBE ) )
+ continue;
+
+ Assert( wl->type == emit_surface );
+
+ // Can this light see the point?
+ wlOrigin4.DuplicateVector ( wl->origin );
+ TestLine ( vStart4, wlOrigin4, &fractionVisible );
+ if ( !TestSignSIMD ( CmpGtSIMD ( fractionVisible, Four_Zeros ) ) )
+ continue;
+
+ // Add this light's contribution.
+ Vector vDelta = wl->origin - vStart;
+ float flDistanceScale = Engine_WorldLightDistanceFalloff( wl, vDelta );
+
+ Vector vDeltaNorm = vDelta;
+ VectorNormalize( vDeltaNorm );
+ float flAngleScale = Engine_WorldLightAngle( wl, wl->normal, vDeltaNorm, vDeltaNorm );
+
+ float ratio = flDistanceScale * flAngleScale * SubFloat ( fractionVisible, 0 );
+ if ( ratio == 0 )
+ continue;
+
+ for ( int i=0; i < 6; i++ )
+ {
+ float t = DotProduct( g_BoxDirections[i], vDeltaNorm );
+ if ( t > 0 )
+ {
+ lightBoxColor[i] += wl->intensity * (t * ratio);
+ }
+ }
+ }
+}
+
+
+void ComputeAmbientFromSphericalSamples( int iThread, const Vector &vStart, Vector lightBoxColor[6] )
+{
+ // Figure out the color that rays hit when shot out from this position.
+ Vector radcolor[NUMVERTEXNORMALS];
+ float tanTheta = tan(VERTEXNORMAL_CONE_INNER_ANGLE);
+
+ for ( int i = 0; i < NUMVERTEXNORMALS; i++ )
+ {
+ Vector vEnd = vStart + g_anorms[i] * (COORD_EXTENT * 1.74);
+
+ // Now that we've got a ray, see what surface we've hit
+ Vector lightStyleColors[MAX_LIGHTSTYLES];
+ lightStyleColors[0].Init(); // We only care about light style 0 here.
+ CalcRayAmbientLighting( iThread, vStart, vEnd, tanTheta, lightStyleColors );
+
+ radcolor[i] = lightStyleColors[0];
+ }
+
+ // accumulate samples into radiant box
+ for ( int j = 6; --j >= 0; )
+ {
+ float t = 0;
+
+ lightBoxColor[j].Init();
+
+ for (int i = 0; i < NUMVERTEXNORMALS; i++)
+ {
+ float c = DotProduct( g_anorms[i], g_BoxDirections[j] );
+ if (c > 0)
+ {
+ t += c;
+ lightBoxColor[j] += radcolor[i] * c;
+ }
+ }
+
+ lightBoxColor[j] *= 1/t;
+ }
+
+ // Now add direct light from the emit_surface lights. These go in the ambient cube because
+ // there are a ton of them and they are often so dim that they get filtered out by r_worldlightmin.
+ AddEmitSurfaceLights( vStart, lightBoxColor );
+}
+
+
+bool IsLeafAmbientSurfaceLight( dworldlight_t *wl )
+{
+ static const float g_flWorldLightMinEmitSurface = 0.005f;
+ static const float g_flWorldLightMinEmitSurfaceDistanceRatio = ( InvRSquared( Vector( 0, 0, 512 ) ) );
+
+ if ( wl->type != emit_surface )
+ return false;
+
+ if ( wl->style != 0 )
+ return false;
+
+ float intensity = max( wl->intensity[0], wl->intensity[1] );
+ intensity = max( intensity, wl->intensity[2] );
+
+ return (intensity * g_flWorldLightMinEmitSurfaceDistanceRatio) < g_flWorldLightMinEmitSurface;
+}
+
+
+class CLeafSampler
+{
+public:
+ CLeafSampler( int iThread ) : m_iThread(iThread) {}
+
+ // Generate a random point in the leaf's bounding volume
+ // reject any points that aren't actually in the leaf
+ // do a couple of tracing heuristics to eliminate points that are inside detail brushes
+ // or underneath displacement surfaces in the leaf
+ // return once we have a valid point, use the center if one can't be computed quickly
+ void GenerateLeafSamplePosition( int leafIndex, const CUtlVector<dplane_t> &leafPlanes, Vector &samplePosition )
+ {
+ dleaf_t *pLeaf = dleafs + leafIndex;
+
+ float dx = pLeaf->maxs[0] - pLeaf->mins[0];
+ float dy = pLeaf->maxs[1] - pLeaf->mins[1];
+ float dz = pLeaf->maxs[2] - pLeaf->mins[2];
+ bool bValid = false;
+ for ( int i = 0; i < 1000 && !bValid; i++ )
+ {
+ samplePosition.x = pLeaf->mins[0] + m_random.RandomFloat(0, dx);
+ samplePosition.y = pLeaf->mins[1] + m_random.RandomFloat(0, dy);
+ samplePosition.z = pLeaf->mins[2] + m_random.RandomFloat(0, dz);
+ bValid = true;
+
+ for ( int j = leafPlanes.Count(); --j >= 0 && bValid; )
+ {
+ float d = DotProduct(leafPlanes[j].normal, samplePosition) - leafPlanes[j].dist;
+ if ( d < DIST_EPSILON )
+ {
+ // not inside the leaf, try again
+ bValid = false;
+ break;
+ }
+ }
+ if ( !bValid )
+ continue;
+
+ for ( int j = 0; j < 6; j++ )
+ {
+ Vector start = samplePosition;
+ int axis = j%3;
+ start[axis] = (j<3) ? pLeaf->mins[axis] : pLeaf->maxs[axis];
+ float t;
+ Vector normal;
+ CastRayInLeaf( m_iThread, samplePosition, start, leafIndex, &t, &normal );
+ if ( t == 0.0f )
+ {
+ // inside a func_detail, try again.
+ bValid = false;
+ break;
+ }
+ if ( t != 1.0f )
+ {
+ Vector delta = start - samplePosition;
+ if ( DotProduct(delta, normal) > 0 )
+ {
+ // hit backside of displacement, try again.
+ bValid = false;
+ break;
+ }
+ }
+ }
+ }
+ if ( !bValid )
+ {
+ // didn't generate a valid sample point, just use the center of the leaf bbox
+ samplePosition = ( Vector( pLeaf->mins[0], pLeaf->mins[1], pLeaf->mins[2] ) + Vector( pLeaf->maxs[0], pLeaf->maxs[1], pLeaf->maxs[2] ) ) * 0.5f;
+ }
+ }
+
+private:
+ int m_iThread;
+ CUniformRandomStream m_random;
+};
+
+// gets a list of the planes pointing into a leaf
+void GetLeafBoundaryPlanes( CUtlVector<dplane_t> &list, int leafIndex )
+{
+ list.RemoveAll();
+ int nodeIndex = leafparents[leafIndex];
+ int child = -(leafIndex + 1);
+ while ( nodeIndex >= 0 )
+ {
+ dnode_t *pNode = dnodes + nodeIndex;
+ dplane_t *pNodePlane = dplanes + pNode->planenum;
+ if ( pNode->children[0] == child )
+ {
+ // front side
+ list.AddToTail( *pNodePlane );
+ }
+ else
+ {
+ // back side
+ int plane = list.AddToTail();
+ list[plane].dist = -pNodePlane->dist;
+ list[plane].normal = -pNodePlane->normal;
+ list[plane].type = pNodePlane->type;
+ }
+ child = nodeIndex;
+ nodeIndex = nodeparents[child];
+ }
+}
+
+// this stores each sample of the ambient lighting
+struct ambientsample_t
+{
+ Vector pos;
+ Vector cube[6];
+};
+
+// add the sample to the list. If we exceed the maximum number of samples, the worst sample will
+// be discarded. This has the effect of converging on the best samples when enough are added.
+void AddSampleToList( CUtlVector<ambientsample_t> &list, const Vector &samplePosition, Vector *pCube )
+{
+ const int MAX_SAMPLES = 16;
+
+ int index = list.AddToTail();
+ list[index].pos = samplePosition;
+ for ( int i = 0; i < 6; i++ )
+ {
+ list[index].cube[i] = pCube[i];
+ }
+
+ if ( list.Count() <= MAX_SAMPLES )
+ return;
+
+ int nearestNeighborIndex = 0;
+ float nearestNeighborDist = FLT_MAX;
+ float nearestNeighborTotal = 0;
+ for ( int i = 0; i < list.Count(); i++ )
+ {
+ int closestIndex = 0;
+ float closestDist = FLT_MAX;
+ float totalDC = 0;
+ for ( int j = 0; j < list.Count(); j++ )
+ {
+ if ( j == i )
+ continue;
+ float dist = (list[i].pos - list[j].pos).Length();
+ float maxDC = 0;
+ for ( int k = 0; k < 6; k++ )
+ {
+ // color delta is computed per-component, per cube side
+ for (int s = 0; s < 3; s++ )
+ {
+ float dc = fabs(list[i].cube[k][s] - list[j].cube[k][s]);
+ maxDC = max(maxDC,dc);
+ }
+ totalDC += maxDC;
+ }
+ // need a measurable difference in color or we'll just rely on position
+ if ( maxDC < 1e-4f )
+ {
+ maxDC = 0;
+ }
+ else if ( maxDC > 1.0f )
+ {
+ maxDC = 1.0f;
+ }
+ // selection criteria is 10% distance, 90% color difference
+ // choose samples that fill the space (large distance from each other)
+ // and have largest color variation
+ float distanceFactor = 0.1f + (maxDC * 0.9f);
+ dist *= distanceFactor;
+
+ // find the "closest" sample to this one
+ if ( dist < closestDist )
+ {
+ closestDist = dist;
+ closestIndex = j;
+ }
+ }
+ // the sample with the "closest" neighbor is rejected
+ if ( closestDist < nearestNeighborDist || (closestDist == nearestNeighborDist && totalDC < nearestNeighborTotal) )
+ {
+ nearestNeighborDist = closestDist;
+ nearestNeighborIndex = i;
+ }
+ }
+ list.FastRemove( nearestNeighborIndex );
+}
+
+// max number of units in gamma space of per-side delta
+int CubeDeltaGammaSpace( Vector *pCube0, Vector *pCube1 )
+{
+ int maxDelta = 0;
+ // do this comparison in gamma space to try and get a perceptual basis for the compare
+ for ( int i = 0; i < 6; i++ )
+ {
+ for ( int j = 0; j < 3; j++ )
+ {
+ int val0 = LinearToScreenGamma( pCube0[i][j] );
+ int val1 = LinearToScreenGamma( pCube1[i][j] );
+ int delta = abs(val0-val1);
+ if ( delta > maxDelta )
+ maxDelta = delta;
+ }
+ }
+ return maxDelta;
+}
+// reconstruct the ambient lighting for a leaf at the given position in worldspace
+// optionally skip one of the entries in the list
+void Mod_LeafAmbientColorAtPos( Vector *pOut, const Vector &pos, const CUtlVector<ambientsample_t> &list, int skipIndex )
+{
+ for ( int i = 0; i < 6; i++ )
+ {
+ pOut[i].Init();
+ }
+ float totalFactor = 0;
+ for ( int i = 0; i < list.Count(); i++ )
+ {
+ if ( i == skipIndex )
+ continue;
+ // do an inverse squared distance weighted average of the samples to reconstruct
+ // the original function
+ float dist = (list[i].pos - pos).LengthSqr();
+ float factor = 1.0f / (dist + 1.0f);
+ totalFactor += factor;
+ for ( int j = 0; j < 6; j++ )
+ {
+ pOut[j] += list[i].cube[j] * factor;
+ }
+ }
+ for ( int i = 0; i < 6; i++ )
+ {
+ pOut[i] *= (1.0f / totalFactor);
+ }
+}
+
+// this samples the lighting at each sample and removes any unnecessary samples
+void CompressAmbientSampleList( CUtlVector<ambientsample_t> &list )
+{
+ Vector testCube[6];
+ for ( int i = 0; i < list.Count(); i++ )
+ {
+ if ( list.Count() > 1 )
+ {
+ Mod_LeafAmbientColorAtPos( testCube, list[i].pos, list, i );
+ if ( CubeDeltaGammaSpace(testCube, list[i].cube) < 3 )
+ {
+ list.FastRemove(i);
+ i--;
+ }
+ }
+ }
+}
+
+// basically this is an intersection routine that returns a distance between the boxes
+float AABBDistance( const Vector &mins0, const Vector &maxs0, const Vector &mins1, const Vector &maxs1 )
+{
+ Vector delta;
+ for ( int i = 0; i < 3; i++ )
+ {
+ float greatestMin = max(mins0[i], mins1[i]);
+ float leastMax = min(maxs0[i], maxs1[i]);
+ delta[i] = (greatestMin < leastMax) ? 0 : (leastMax - greatestMin);
+ }
+ return delta.Length();
+}
+
+// build a list of leaves from a query
+class CLeafList : public ISpatialLeafEnumerator
+{
+public:
+ virtual bool EnumerateLeaf( int leaf, int context )
+ {
+ m_list.AddToTail(leaf);
+ return true;
+ }
+
+ CUtlVector<int> m_list;
+};
+
+// conver short[3] to vector
+static void LeafBounds( int leafIndex, Vector &mins, Vector &maxs )
+{
+ for ( int i = 0; i < 3; i++ )
+ {
+ mins[i] = dleafs[leafIndex].mins[i];
+ maxs[i] = dleafs[leafIndex].maxs[i];
+ }
+}
+
+// returns the index of the nearest leaf with ambient samples
+int NearestNeighborWithLight(int leafID)
+{
+ Vector mins, maxs;
+ LeafBounds( leafID, mins, maxs );
+ Vector size = maxs - mins;
+ CLeafList leafList;
+ ToolBSPTree()->EnumerateLeavesInBox( mins-size, maxs+size, &leafList, 0 );
+ float bestDist = FLT_MAX;
+ int bestIndex = leafID;
+ for ( int i = 0; i < leafList.m_list.Count(); i++ )
+ {
+ int testIndex = leafList.m_list[i];
+ if ( !g_pLeafAmbientIndex->Element(testIndex).ambientSampleCount )
+ continue;
+
+ Vector testMins, testMaxs;
+ LeafBounds( testIndex, testMins, testMaxs );
+ float dist = AABBDistance( mins, maxs, testMins, testMaxs );
+ if ( dist < bestDist )
+ {
+ bestDist = dist;
+ bestIndex = testIndex;
+ }
+ }
+ return bestIndex;
+}
+
+// maps a float to a byte fraction between min & max
+static byte Fixed8Fraction( float t, float tMin, float tMax )
+{
+ if ( tMax <= tMin )
+ return 0;
+
+ float frac = RemapValClamped( t, tMin, tMax, 0.0f, 255.0f );
+ return byte(frac+0.5f);
+}
+
+CUtlVector< CUtlVector<ambientsample_t> > g_LeafAmbientSamples;
+
+void ComputeAmbientForLeaf( int iThread, int leafID, CUtlVector<ambientsample_t> &list )
+{
+ CUtlVector<dplane_t> leafPlanes;
+ CLeafSampler sampler( iThread );
+
+ GetLeafBoundaryPlanes( leafPlanes, leafID );
+ list.RemoveAll();
+ // this heuristic tries to generate at least one sample per volume (chosen to be similar to the size of a player) in the space
+ int xSize = (dleafs[leafID].maxs[0] - dleafs[leafID].mins[0]) / 32;
+ int ySize = (dleafs[leafID].maxs[1] - dleafs[leafID].mins[1]) / 32;
+ int zSize = (dleafs[leafID].maxs[2] - dleafs[leafID].mins[2]) / 64;
+ xSize = max(xSize,1);
+ ySize = max(xSize,1);
+ zSize = max(xSize,1);
+ // generate update 128 candidate samples, always at least one sample
+ int volumeCount = xSize * ySize * zSize;
+ if ( g_bFastAmbient )
+ {
+ // save compute time, only do one sample
+ volumeCount = 1;
+ }
+ int sampleCount = clamp( volumeCount, 1, 128 );
+ if ( dleafs[leafID].contents & CONTENTS_SOLID )
+ {
+ // don't generate any samples in solid leaves
+ // NOTE: We copy the nearest non-solid leaf sample pointers into this leaf at the end
+ return;
+ }
+ Vector cube[6];
+ for ( int i = 0; i < sampleCount; i++ )
+ {
+ // compute each candidate sample and add to the list
+ Vector samplePosition;
+ sampler.GenerateLeafSamplePosition( leafID, leafPlanes, samplePosition );
+ ComputeAmbientFromSphericalSamples( iThread, samplePosition, cube );
+ // note this will remove the least valuable sample once the limit is reached
+ AddSampleToList( list, samplePosition, cube );
+ }
+
+ // remove any samples that can be reconstructed with the remaining data
+ CompressAmbientSampleList( list );
+}
+
+static void ThreadComputeLeafAmbient( int iThread, void *pUserData )
+{
+ CUtlVector<ambientsample_t> list;
+ while (1)
+ {
+ int leafID = GetThreadWork ();
+ if (leafID == -1)
+ break;
+ list.RemoveAll();
+ ComputeAmbientForLeaf(iThread, leafID, list);
+ // copy to the output array
+ g_LeafAmbientSamples[leafID].SetCount( list.Count() );
+ for ( int i = 0; i < list.Count(); i++ )
+ {
+ g_LeafAmbientSamples[leafID].Element(i) = list.Element(i);
+ }
+ }
+}
+
+void VMPI_ProcessLeafAmbient( int iThread, uint64 iLeaf, MessageBuffer *pBuf )
+{
+ CUtlVector<ambientsample_t> list;
+ ComputeAmbientForLeaf(iThread, (int)iLeaf, list);
+
+ VMPI_SetCurrentStage( "EncodeLeafAmbientResults" );
+
+ // Encode the results.
+ int nSamples = list.Count();
+ pBuf->write( &nSamples, sizeof( nSamples ) );
+ if ( nSamples )
+ {
+ pBuf->write( list.Base(), list.Count() * sizeof( ambientsample_t ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Called on the master when a worker finishes processing a static prop.
+//-----------------------------------------------------------------------------
+void VMPI_ReceiveLeafAmbientResults( uint64 leafID, MessageBuffer *pBuf, int iWorker )
+{
+ // Decode the results.
+ int nSamples;
+ pBuf->read( &nSamples, sizeof( nSamples ) );
+
+ g_LeafAmbientSamples[leafID].SetCount( nSamples );
+ if ( nSamples )
+ {
+ pBuf->read(g_LeafAmbientSamples[leafID].Base(), nSamples * sizeof(ambientsample_t) );
+ }
+}
+
+
+void ComputePerLeafAmbientLighting()
+{
+ // Figure out which lights should go in the per-leaf ambient cubes.
+ int nInAmbientCube = 0;
+ int nSurfaceLights = 0;
+ for ( int i=0; i < *pNumworldlights; i++ )
+ {
+ dworldlight_t *wl = &dworldlights[i];
+
+ if ( IsLeafAmbientSurfaceLight( wl ) )
+ wl->flags |= DWL_FLAGS_INAMBIENTCUBE;
+ else
+ wl->flags &= ~DWL_FLAGS_INAMBIENTCUBE;
+
+ if ( wl->type == emit_surface )
+ ++nSurfaceLights;
+
+ if ( wl->flags & DWL_FLAGS_INAMBIENTCUBE )
+ ++nInAmbientCube;
+ }
+
+ Msg( "%d of %d (%d%% of) surface lights went in leaf ambient cubes.\n", nInAmbientCube, nSurfaceLights, nSurfaceLights ? ((nInAmbientCube*100) / nSurfaceLights) : 0 );
+
+ g_LeafAmbientSamples.SetCount(numleafs);
+
+ if ( g_bUseMPI )
+ {
+ // Distribute the work among the workers.
+ VMPI_SetCurrentStage( "ComputeLeafAmbientLighting" );
+ DistributeWork( numleafs, VMPI_DISTRIBUTEWORK_PACKETID, VMPI_ProcessLeafAmbient, VMPI_ReceiveLeafAmbientResults );
+ }
+ else
+ {
+ RunThreadsOn(numleafs, true, ThreadComputeLeafAmbient);
+ }
+
+ // now write out the data
+ Msg("Writing leaf ambient...");
+ g_pLeafAmbientIndex->RemoveAll();
+ g_pLeafAmbientLighting->RemoveAll();
+ g_pLeafAmbientIndex->SetCount( numleafs );
+ g_pLeafAmbientLighting->EnsureCapacity( numleafs*4 );
+ for ( int leafID = 0; leafID < numleafs; leafID++ )
+ {
+ const CUtlVector<ambientsample_t> &list = g_LeafAmbientSamples[leafID];
+ g_pLeafAmbientIndex->Element(leafID).ambientSampleCount = list.Count();
+ if ( !list.Count() )
+ {
+ g_pLeafAmbientIndex->Element(leafID).firstAmbientSample = 0;
+ }
+ else
+ {
+ g_pLeafAmbientIndex->Element(leafID).firstAmbientSample = g_pLeafAmbientLighting->Count();
+ // compute the samples in disk format. Encode the positions in 8-bits using leaf bounds fractions
+ for ( int i = 0; i < list.Count(); i++ )
+ {
+ int outIndex = g_pLeafAmbientLighting->AddToTail();
+ dleafambientlighting_t &light = g_pLeafAmbientLighting->Element(outIndex);
+
+ light.x = Fixed8Fraction( list[i].pos.x, dleafs[leafID].mins[0], dleafs[leafID].maxs[0] );
+ light.y = Fixed8Fraction( list[i].pos.y, dleafs[leafID].mins[1], dleafs[leafID].maxs[1] );
+ light.z = Fixed8Fraction( list[i].pos.z, dleafs[leafID].mins[2], dleafs[leafID].maxs[2] );
+ light.pad = 0;
+ for ( int side = 0; side < 6; side++ )
+ {
+ VectorToColorRGBExp32( list[i].cube[side], light.cube.m_Color[side] );
+ }
+ }
+ }
+ }
+ for ( int i = 0; i < numleafs; i++ )
+ {
+ // UNDONE: Do this dynamically in the engine instead. This will allow us to sample across leaf
+ // boundaries always which should improve the quality of lighting in general
+ if ( g_pLeafAmbientIndex->Element(i).ambientSampleCount == 0 )
+ {
+ if ( !(dleafs[i].contents & CONTENTS_SOLID) )
+ {
+ Msg("Bad leaf ambient for leaf %d\n", i );
+ }
+
+ int refLeaf = NearestNeighborWithLight(i);
+ g_pLeafAmbientIndex->Element(i).ambientSampleCount = 0;
+ g_pLeafAmbientIndex->Element(i).firstAmbientSample = refLeaf;
+ }
+ }
+ Msg("done\n");
+}
+
diff --git a/mp/src/utils/vrad/leaf_ambient_lighting.h b/mp/src/utils/vrad/leaf_ambient_lighting.h
index b6bb50eb..6dd94106 100644
--- a/mp/src/utils/vrad/leaf_ambient_lighting.h
+++ b/mp/src/utils/vrad/leaf_ambient_lighting.h
@@ -1,17 +1,17 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#ifndef LEAF_AMBIENT_LIGHTING_H
-#define LEAF_AMBIENT_LIGHTING_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-void ComputePerLeafAmbientLighting();
-
-
-#endif // LEAF_AMBIENT_LIGHTING_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef LEAF_AMBIENT_LIGHTING_H
+#define LEAF_AMBIENT_LIGHTING_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+void ComputePerLeafAmbientLighting();
+
+
+#endif // LEAF_AMBIENT_LIGHTING_H
diff --git a/mp/src/utils/vrad/lightmap.cpp b/mp/src/utils/vrad/lightmap.cpp
index bc3d254e..bfa4bd03 100644
--- a/mp/src/utils/vrad/lightmap.cpp
+++ b/mp/src/utils/vrad/lightmap.cpp
@@ -1,3577 +1,3577 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#include "vrad.h"
-#include "lightmap.h"
-#include "radial.h"
-#include "mathlib/bumpvects.h"
-#include "tier1/utlvector.h"
-#include "vmpi.h"
-#include "mathlib/anorms.h"
-#include "map_utils.h"
-#include "mathlib/halton.h"
-#include "imagepacker.h"
-#include "tier1/utlrbtree.h"
-#include "tier1/utlbuffer.h"
-#include "bitmap/tgawriter.h"
-#include "mathlib/quantize.h"
-#include "bitmap/imageformat.h"
-#include "coordsize.h"
-
-enum
-{
- AMBIENT_ONLY = 0x1,
- NON_AMBIENT_ONLY = 0x2,
-};
-
-#define SMOOTHING_GROUP_HARD_EDGE 0xff000000
-
-//==========================================================================//
-// CNormalList.
-//==========================================================================//
-
-// This class keeps a list of unique normals and provides a fast
-class CNormalList
-{
-public:
- CNormalList();
-
- // Adds the normal if unique. Otherwise, returns the normal's index into m_Normals.
- int FindOrAddNormal( Vector const &vNormal );
-
-
-public:
-
- CUtlVector<Vector> m_Normals;
-
-
-private:
-
- // This represents a grid from (-1,-1,-1) to (1,1,1).
- enum {NUM_SUBDIVS = 8};
- CUtlVector<int> m_NormalGrid[NUM_SUBDIVS][NUM_SUBDIVS][NUM_SUBDIVS];
-};
-
-
-int g_iCurFace;
-edgeshare_t edgeshare[MAX_MAP_EDGES];
-
-Vector face_centroids[MAX_MAP_EDGES];
-
-int vertexref[MAX_MAP_VERTS];
-int *vertexface[MAX_MAP_VERTS];
-faceneighbor_t faceneighbor[MAX_MAP_FACES];
-
-static directlight_t *gSkyLight = NULL;
-static directlight_t *gAmbient = NULL;
-
-//==========================================================================//
-// CNormalList implementation.
-//==========================================================================//
-
-CNormalList::CNormalList() : m_Normals( 128 )
-{
- for( int i=0; i < sizeof(m_NormalGrid)/sizeof(m_NormalGrid[0][0][0]); i++ )
- {
- (&m_NormalGrid[0][0][0] + i)->SetGrowSize( 16 );
- }
-}
-
-int CNormalList::FindOrAddNormal( Vector const &vNormal )
-{
- int gi[3];
-
- // See which grid element it's in.
- for( int iDim=0; iDim < 3; iDim++ )
- {
- gi[iDim] = (int)( ((vNormal[iDim] + 1.0f) * 0.5f) * NUM_SUBDIVS - 0.000001f );
- gi[iDim] = min( gi[iDim], NUM_SUBDIVS );
- gi[iDim] = max( gi[iDim], 0 );
- }
-
- // Look for a matching vector in there.
- CUtlVector<int> *pGridElement = &m_NormalGrid[gi[0]][gi[1]][gi[2]];
- for( int i=0; i < pGridElement->Size(); i++ )
- {
- int iNormal = pGridElement->Element(i);
-
- Vector *pVec = &m_Normals[iNormal];
- //if( pVec->DistToSqr(vNormal) < 0.00001f )
- if( *pVec == vNormal )
- return iNormal;
- }
-
- // Ok, add a new one.
- pGridElement->AddToTail( m_Normals.Size() );
- return m_Normals.AddToTail( vNormal );
-}
-
-// FIXME: HACK until the plane normals are made more happy
-void GetBumpNormals( const float* sVect, const float* tVect, const Vector& flatNormal,
- const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] )
-{
- Vector stmp( sVect[0], sVect[1], sVect[2] );
- Vector ttmp( tVect[0], tVect[1], tVect[2] );
- GetBumpNormals( stmp, ttmp, flatNormal, phongNormal, bumpNormals );
-}
-
-int EdgeVertex( dface_t *f, int edge )
-{
- int k;
-
- if (edge < 0)
- edge += f->numedges;
- else if (edge >= f->numedges)
- edge = edge % f->numedges;
-
- k = dsurfedges[f->firstedge + edge];
- if (k < 0)
- {
- // Msg("(%d %d) ", dedges[-k].v[1], dedges[-k].v[0] );
- return dedges[-k].v[1];
- }
- else
- {
- // Msg("(%d %d) ", dedges[k].v[0], dedges[k].v[1] );
- return dedges[k].v[0];
- }
-}
-
-
-/*
- ============
- PairEdges
- ============
-*/
-void PairEdges (void)
-{
- int i, j, k, n, m;
- dface_t *f;
- int numneighbors;
- int tmpneighbor[64];
- faceneighbor_t *fn;
-
- // count number of faces that reference each vertex
- for (i=0, f = g_pFaces; i<numfaces ; i++, f++)
- {
- for (j=0 ; j<f->numedges ; j++)
- {
- // Store the count in vertexref
- vertexref[EdgeVertex(f,j)]++;
- }
- }
-
- // allocate room
- for (i = 0; i < numvertexes; i++)
- {
- // use the count from above to allocate a big enough array
- vertexface[i] = ( int* )calloc( vertexref[i], sizeof( vertexface[0] ) );
- // clear the temporary data
- vertexref[i] = 0;
- }
-
- // store a list of every face that uses a particular vertex
- for (i=0, f = g_pFaces ; i<numfaces ; i++, f++)
- {
- for (j=0 ; j<f->numedges ; j++)
- {
- n = EdgeVertex(f,j);
-
- for (k = 0; k < vertexref[n]; k++)
- {
- if (vertexface[n][k] == i)
- break;
- }
- if (k >= vertexref[n])
- {
- // add the face to the list
- vertexface[n][k] = i;
- vertexref[n]++;
- }
- }
- }
-
- // calc normals and set displacement surface flag
- for (i=0, f = g_pFaces; i<numfaces ; i++, f++)
- {
- fn = &faceneighbor[i];
-
- // get face normal
- VectorCopy( dplanes[f->planenum].normal, fn->facenormal );
-
- // set displacement surface flag
- fn->bHasDisp = false;
- if( ValidDispFace( f ) )
- {
- fn->bHasDisp = true;
- }
- }
-
- // find neighbors
- for (i=0, f = g_pFaces ; i<numfaces ; i++, f++)
- {
- numneighbors = 0;
- fn = &faceneighbor[i];
-
- // allocate room for vertex normals
- fn->normal = ( Vector* )calloc( f->numedges, sizeof( fn->normal[0] ) );
-
- // look up all faces sharing vertices and add them to the list
- for (j=0 ; j<f->numedges ; j++)
- {
- n = EdgeVertex(f,j);
-
- for (k = 0; k < vertexref[n]; k++)
- {
- double cos_normals_angle;
- Vector *pNeighbornormal;
-
- // skip self
- if (vertexface[n][k] == i)
- continue;
-
- // if this face doens't have a displacement -- don't consider displacement neighbors
- if( ( !fn->bHasDisp ) && ( faceneighbor[vertexface[n][k]].bHasDisp ) )
- continue;
-
- pNeighbornormal = &faceneighbor[vertexface[n][k]].facenormal;
- cos_normals_angle = DotProduct( *pNeighbornormal, fn->facenormal );
-
- // add normal if >= threshold or its a displacement surface (this is only if the original
- // face is a displacement)
- if ( fn->bHasDisp )
- {
- // Always smooth with and against a displacement surface.
- VectorAdd( fn->normal[j], *pNeighbornormal, fn->normal[j] );
- }
- else
- {
- // No smoothing - use of method (backwards compatibility).
- if ( ( f->smoothingGroups == 0 ) && ( g_pFaces[vertexface[n][k]].smoothingGroups == 0 ) )
- {
- if ( cos_normals_angle >= smoothing_threshold )
- {
- VectorAdd( fn->normal[j], *pNeighbornormal, fn->normal[j] );
- }
- else
- {
- // not considered a neighbor
- continue;
- }
- }
- else
- {
- unsigned int smoothingGroup = ( f->smoothingGroups & g_pFaces[vertexface[n][k]].smoothingGroups );
-
- // Hard edge.
- if ( ( smoothingGroup & SMOOTHING_GROUP_HARD_EDGE ) != 0 )
- continue;
-
- if ( smoothingGroup != 0 )
- {
- VectorAdd( fn->normal[j], *pNeighbornormal, fn->normal[j] );
- }
- else
- {
- // not considered a neighbor
- continue;
- }
- }
- }
-
- // look to see if we've already added this one
- for (m = 0; m < numneighbors; m++)
- {
- if (tmpneighbor[m] == vertexface[n][k])
- break;
- }
-
- if (m >= numneighbors)
- {
- // add to neighbor list
- tmpneighbor[m] = vertexface[n][k];
- numneighbors++;
- if ( numneighbors > ARRAYSIZE(tmpneighbor) )
- {
- Error("Stack overflow in neighbors\n");
- }
- }
- }
- }
-
- if (numneighbors)
- {
- // copy over neighbor list
- fn->numneighbors = numneighbors;
- fn->neighbor = ( int* )calloc( numneighbors, sizeof( fn->neighbor[0] ) );
- for (m = 0; m < numneighbors; m++)
- {
- fn->neighbor[m] = tmpneighbor[m];
- }
- }
-
- // fixup normals
- for (j = 0; j < f->numedges; j++)
- {
- VectorAdd( fn->normal[j], fn->facenormal, fn->normal[j] );
- VectorNormalize( fn->normal[j] );
- }
- }
-}
-
-
-void SaveVertexNormals( void )
-{
- faceneighbor_t *fn;
- int i, j;
- dface_t *f;
- CNormalList normalList;
-
- g_numvertnormalindices = 0;
-
- for( i = 0 ;i<numfaces ; i++ )
- {
- fn = &faceneighbor[i];
- f = &g_pFaces[i];
-
- for( j = 0; j < f->numedges; j++ )
- {
- Vector vNormal;
- if( fn->normal )
- {
- vNormal = fn->normal[j];
- }
- else
- {
- // original faces don't have normals
- vNormal.Init( 0, 0, 0 );
- }
-
- if( g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES )
- {
- Error( "g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES" );
- }
-
- g_vertnormalindices[g_numvertnormalindices] = (unsigned short)normalList.FindOrAddNormal( vNormal );
- g_numvertnormalindices++;
- }
- }
-
- if( normalList.m_Normals.Size() > MAX_MAP_VERTNORMALS )
- {
- Error( "g_numvertnormals > MAX_MAP_VERTNORMALS" );
- }
-
- // Copy the list of unique vert normals into g_vertnormals.
- g_numvertnormals = normalList.m_Normals.Size();
- memcpy( g_vertnormals, normalList.m_Normals.Base(), sizeof(g_vertnormals[0]) * normalList.m_Normals.Size() );
-}
-
-/*
- =================================================================
-
- LIGHTMAP SAMPLE GENERATION
-
- =================================================================
-*/
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Spits out an error message with information about a lightinfo_t.
-// Input : s - Error message string.
-// l - lightmap info struct.
-//-----------------------------------------------------------------------------
-void ErrorLightInfo(const char *s, lightinfo_t *l)
-{
- texinfo_t *tex = &texinfo[l->face->texinfo];
- winding_t *w = WindingFromFace(&g_pFaces[l->facenum], l->modelorg);
-
- //
- // Show the face center and material name if possible.
- //
- if (w != NULL)
- {
- // Don't exit, we'll try to recover...
- Vector vecCenter;
- WindingCenter(w, vecCenter);
-// FreeWinding(w);
-
- Warning("%s at (%g, %g, %g)\n\tmaterial=%s\n", s, (double)vecCenter.x, (double)vecCenter.y, (double)vecCenter.z, TexDataStringTable_GetString( dtexdata[tex->texdata].nameStringTableID ) );
- }
- //
- // If not, just show the material name.
- //
- else
- {
- Warning("%s at (degenerate face)\n\tmaterial=%s\n", s, TexDataStringTable_GetString( dtexdata[tex->texdata].nameStringTableID ));
- }
-}
-
-
-
-void CalcFaceVectors(lightinfo_t *l)
-{
- texinfo_t *tex;
- int i, j;
-
- tex = &texinfo[l->face->texinfo];
-
- // move into lightinfo_t
- for (i=0 ; i<2 ; i++)
- {
- for (j=0 ; j<3 ; j++)
- {
- l->worldToLuxelSpace[i][j] = tex->lightmapVecsLuxelsPerWorldUnits[i][j];
- }
- }
-
- //Solve[ { x * w00 + y * w01 + z * w02 - s == 0, x * w10 + y * w11 + z * w12 - t == 0, A * x + B * y + C * z + D == 0 }, { x, y, z } ]
- //Rule(x,( C*s*w11 - B*s*w12 + B*t*w02 - C*t*w01 + D*w02*w11 - D*w01*w12) / (+ A*w01*w12 - A*w02*w11 + B*w02*w10 - B*w00*w12 + C*w00*w11 - C*w01*w10 )),
- //Rule(y,( A*s*w12 - C*s*w10 + C*t*w00 - A*t*w02 + D*w00*w12 - D*w02*w10) / (+ A*w01*w12 - A*w02*w11 + B*w02*w10 - B*w00*w12 + C*w00*w11 - C*w01*w10 )),
- //Rule(z,( B*s*w10 - A*s*w11 + A*t*w01 - B*t*w00 + D*w01*w10 - D*w00*w11) / (+ A*w01*w12 - A*w02*w11 + B*w02*w10 - B*w00*w12 + C*w00*w11 - C*w01*w10 ))))
-
- Vector luxelSpaceCross;
-
- luxelSpaceCross[0] =
- tex->lightmapVecsLuxelsPerWorldUnits[1][1] * tex->lightmapVecsLuxelsPerWorldUnits[0][2] -
- tex->lightmapVecsLuxelsPerWorldUnits[1][2] * tex->lightmapVecsLuxelsPerWorldUnits[0][1];
- luxelSpaceCross[1] =
- tex->lightmapVecsLuxelsPerWorldUnits[1][2] * tex->lightmapVecsLuxelsPerWorldUnits[0][0] -
- tex->lightmapVecsLuxelsPerWorldUnits[1][0] * tex->lightmapVecsLuxelsPerWorldUnits[0][2];
- luxelSpaceCross[2] =
- tex->lightmapVecsLuxelsPerWorldUnits[1][0] * tex->lightmapVecsLuxelsPerWorldUnits[0][1] -
- tex->lightmapVecsLuxelsPerWorldUnits[1][1] * tex->lightmapVecsLuxelsPerWorldUnits[0][0];
-
- float det = -DotProduct( l->facenormal, luxelSpaceCross );
- if ( fabs( det ) < 1.0e-20 )
- {
- Warning(" warning - face vectors parallel to face normal. bad lighting will be produced\n" );
- l->luxelOrigin = vec3_origin;
- }
- else
- {
- // invert the matrix
- l->luxelToWorldSpace[0][0] = (l->facenormal[2] * l->worldToLuxelSpace[1][1] - l->facenormal[1] * l->worldToLuxelSpace[1][2]) / det;
- l->luxelToWorldSpace[1][0] = (l->facenormal[1] * l->worldToLuxelSpace[0][2] - l->facenormal[2] * l->worldToLuxelSpace[0][1]) / det;
- l->luxelOrigin[0] = -(l->facedist * luxelSpaceCross[0]) / det;
- l->luxelToWorldSpace[0][1] = (l->facenormal[0] * l->worldToLuxelSpace[1][2] - l->facenormal[2] * l->worldToLuxelSpace[1][0]) / det;
- l->luxelToWorldSpace[1][1] = (l->facenormal[2] * l->worldToLuxelSpace[0][0] - l->facenormal[0] * l->worldToLuxelSpace[0][2]) / det;
- l->luxelOrigin[1] = -(l->facedist * luxelSpaceCross[1]) / det;
- l->luxelToWorldSpace[0][2] = (l->facenormal[1] * l->worldToLuxelSpace[1][0] - l->facenormal[0] * l->worldToLuxelSpace[1][1]) / det;
- l->luxelToWorldSpace[1][2] = (l->facenormal[0] * l->worldToLuxelSpace[0][1] - l->facenormal[1] * l->worldToLuxelSpace[0][0]) / det;
- l->luxelOrigin[2] = -(l->facedist * luxelSpaceCross[2]) / det;
-
- // adjust for luxel offset
- VectorMA( l->luxelOrigin, -tex->lightmapVecsLuxelsPerWorldUnits[0][3], l->luxelToWorldSpace[0], l->luxelOrigin );
- VectorMA( l->luxelOrigin, -tex->lightmapVecsLuxelsPerWorldUnits[1][3], l->luxelToWorldSpace[1], l->luxelOrigin );
- }
- // compensate for org'd bmodels
- VectorAdd (l->luxelOrigin, l->modelorg, l->luxelOrigin);
-}
-
-
-
-winding_t *LightmapCoordWindingForFace( lightinfo_t *l )
-{
- int i;
- winding_t *w;
-
- w = WindingFromFace( l->face, l->modelorg );
-
- for (i = 0; i < w->numpoints; i++)
- {
- Vector2D coord;
- WorldToLuxelSpace( l, w->p[i], coord );
- w->p[i].x = coord.x;
- w->p[i].y = coord.y;
- w->p[i].z = 0;
- }
-
- return w;
-}
-
-
-void WriteCoordWinding (FILE *out, lightinfo_t *l, winding_t *w, Vector& color )
-{
- int i;
- Vector pos;
-
- fprintf (out, "%i\n", w->numpoints);
- for (i=0 ; i<w->numpoints ; i++)
- {
- LuxelSpaceToWorld( l, w->p[i][0], w->p[i][1], pos );
- fprintf (out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
- pos[0],
- pos[1],
- pos[2],
- color[ 0 ] / 256,
- color[ 1 ] / 256,
- color[ 2 ] / 256 );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void DumpFaces( lightinfo_t *pLightInfo, int ndxFace )
-{
- static FileHandle_t out;
-
- // get face data
- faceneighbor_t *fn = &faceneighbor[ndxFace];
- Vector &centroid = face_centroids[ndxFace];
-
- // disable threading (not a multi-threadable function!)
- ThreadLock();
-
- if( !out )
- {
- // open the file
- out = g_pFileSystem->Open( "face.txt", "w" );
- if( !out )
- return;
- }
-
- //
- // write out face
- //
- for( int ndxEdge = 0; ndxEdge < pLightInfo->face->numedges; ndxEdge++ )
- {
-// int edge = dsurfedges[pLightInfo->face->firstedge+ndxEdge];
-
- Vector p1, p2;
- VectorAdd( dvertexes[EdgeVertex( pLightInfo->face, ndxEdge )].point, pLightInfo->modelorg, p1 );
- VectorAdd( dvertexes[EdgeVertex( pLightInfo->face, ndxEdge+1 )].point, pLightInfo->modelorg, p2 );
-
- Vector &n1 = fn->normal[ndxEdge];
- Vector &n2 = fn->normal[(ndxEdge+1)%pLightInfo->face->numedges];
-
- CmdLib_FPrintf( out, "3\n");
-
- CmdLib_FPrintf(out, "%f %f %f %f %f %f\n", p1[0], p1[1], p1[2], n1[0] * 0.5 + 0.5, n1[1] * 0.5 + 0.5, n1[2] * 0.5 + 0.5 );
-
- CmdLib_FPrintf(out, "%f %f %f %f %f %f\n", p2[0], p2[1], p2[2], n2[0] * 0.5 + 0.5, n2[1] * 0.5 + 0.5, n2[2] * 0.5 + 0.5 );
-
- CmdLib_FPrintf(out, "%f %f %f %f %f %f\n", centroid[0] + pLightInfo->modelorg[0],
- centroid[1] + pLightInfo->modelorg[1],
- centroid[2] + pLightInfo->modelorg[2],
- fn->facenormal[0] * 0.5 + 0.5,
- fn->facenormal[1] * 0.5 + 0.5,
- fn->facenormal[2] * 0.5 + 0.5 );
-
- }
-
- // enable threading
- ThreadUnlock();
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool BuildFacesamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight )
-{
- // lightmap size
- int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
- int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
-
- // ratio of world area / lightmap area
- texinfo_t *pTex = &texinfo[pLightInfo->face->texinfo];
- pFaceLight->worldAreaPerLuxel = 1.0 / ( sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[0],
- pTex->lightmapVecsLuxelsPerWorldUnits[0] ) ) *
- sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[1],
- pTex->lightmapVecsLuxelsPerWorldUnits[1] ) ) );
-
- //
- // quickly create samples and luxels (copy over samples)
- //
- pFaceLight->numsamples = width * height;
- pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
- if( !pFaceLight->sample )
- return false;
-
- pFaceLight->numluxels = width * height;
- pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
- if( !pFaceLight->luxel )
- return false;
-
- sample_t *pSamples = pFaceLight->sample;
- Vector *pLuxels = pFaceLight->luxel;
-
- for( int t = 0; t < height; t++ )
- {
- for( int s = 0; s < width; s++ )
- {
- pSamples->s = s;
- pSamples->t = t;
- pSamples->coord[0] = s;
- pSamples->coord[1] = t;
- // unused but initialized anyway
- pSamples->mins[0] = s - 0.5;
- pSamples->mins[1] = t - 0.5;
- pSamples->maxs[0] = s + 0.5;
- pSamples->maxs[1] = t + 0.5;
- pSamples->area = pFaceLight->worldAreaPerLuxel;
- LuxelSpaceToWorld( pLightInfo, pSamples->coord[0], pSamples->coord[1], pSamples->pos );
- VectorCopy( pSamples->pos, *pLuxels );
-
- pSamples++;
- pLuxels++;
- }
- }
-
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool BuildSamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
-{
- // build samples for a "face"
- if( pLightInfo->face->dispinfo == -1 )
- {
- return BuildFacesamplesAndLuxels_DoFast( pLightInfo, pFaceLight );
- }
- // build samples for a "displacement"
- else
- {
- return StaticDispMgr()->BuildDispSamplesAndLuxels_DoFast( pLightInfo, pFaceLight, ndxFace );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool BuildFacesamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight )
-{
- // lightmap size
- int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
- int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
-
- // ratio of world area / lightmap area
- texinfo_t *pTex = &texinfo[pLightInfo->face->texinfo];
- pFaceLight->worldAreaPerLuxel = 1.0 / ( sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[0],
- pTex->lightmapVecsLuxelsPerWorldUnits[0] ) ) *
- sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[1],
- pTex->lightmapVecsLuxelsPerWorldUnits[1] ) ) );
-
- // allocate a large number of samples for creation -- get copied later!
- CUtlVector<sample_t> sampleData;
- sampleData.SetCount( SINGLE_BRUSH_MAP * 2 );
- sample_t *samples = sampleData.Base();
- sample_t *pSamples = samples;
-
- // lightmap space winding
- winding_t *pLightmapWinding = LightmapCoordWindingForFace( pLightInfo );
-
- //
- // build vector pointing along the lightmap cutting planes
- //
- Vector sNorm( 1.0f, 0.0f, 0.0f );
- Vector tNorm( 0.0f, 1.0f, 0.0f );
-
- // sample center offset
- float sampleOffset = ( do_centersamples ) ? 0.5 : 1.0;
-
- //
- // clip the lightmap "spaced" winding by the lightmap cutting planes
- //
- winding_t *pWindingT1, *pWindingT2;
- winding_t *pWindingS1, *pWindingS2;
- float dist;
-
- for( int t = 0; t < height && pLightmapWinding; t++ )
- {
- dist = t + sampleOffset;
-
- // lop off a sample in the t dimension
- // hack - need a separate epsilon for lightmap space since ON_EPSILON is for texture space
- ClipWindingEpsilon( pLightmapWinding, tNorm, dist, ON_EPSILON / 16.0f, &pWindingT1, &pWindingT2 );
-
- for( int s = 0; s < width && pWindingT2; s++ )
- {
- dist = s + sampleOffset;
-
- // lop off a sample in the s dimension, and put it in ws2
- // hack - need a separate epsilon for lightmap space since ON_EPSILON is for texture space
- ClipWindingEpsilon( pWindingT2, sNorm, dist, ON_EPSILON / 16.0f, &pWindingS1, &pWindingS2 );
-
- //
- // s2 winding is a single sample worth of winding
- //
- if( pWindingS2 )
- {
- // save the s, t positions
- pSamples->s = s;
- pSamples->t = t;
-
- // get the lightmap space area of ws2 and convert to world area
- // and find the center (then convert it to 2D)
- Vector center;
- pSamples->area = WindingAreaAndBalancePoint( pWindingS2, center ) * pFaceLight->worldAreaPerLuxel;
- pSamples->coord[0] = center.x;
- pSamples->coord[1] = center.y;
-
- // find winding bounds (then convert it to 2D)
- Vector minbounds, maxbounds;
- WindingBounds( pWindingS2, minbounds, maxbounds );
- pSamples->mins[0] = minbounds.x;
- pSamples->mins[1] = minbounds.y;
- pSamples->maxs[0] = maxbounds.x;
- pSamples->maxs[1] = maxbounds.y;
-
- // convert from lightmap space to world space
- LuxelSpaceToWorld( pLightInfo, pSamples->coord[0], pSamples->coord[1], pSamples->pos );
-
- if (g_bDumpPatches || (do_extra && pSamples->area < pFaceLight->worldAreaPerLuxel - EQUAL_EPSILON))
- {
- //
- // convert the winding from lightmaps space to world for debug rendering and sub-sampling
- //
- Vector worldPos;
- for( int ndxPt = 0; ndxPt < pWindingS2->numpoints; ndxPt++ )
- {
- LuxelSpaceToWorld( pLightInfo, pWindingS2->p[ndxPt].x, pWindingS2->p[ndxPt].y, worldPos );
- VectorCopy( worldPos, pWindingS2->p[ndxPt] );
- }
- pSamples->w = pWindingS2;
- }
- else
- {
- // winding isn't needed, free it.
- pSamples->w = NULL;
- FreeWinding( pWindingS2 );
- }
-
- pSamples++;
- }
-
- //
- // if winding T2 still exists free it and set it equal S1 (the rest of the row minus the sample just created)
- //
- if( pWindingT2 )
- {
- FreeWinding( pWindingT2 );
- }
-
- // clip the rest of "s"
- pWindingT2 = pWindingS1;
- }
-
- //
- // if the original lightmap winding exists free it and set it equal to T1 (the rest of the winding not cut into samples)
- //
- if( pLightmapWinding )
- {
- FreeWinding( pLightmapWinding );
- }
-
- if( pWindingT2 )
- {
- FreeWinding( pWindingT2 );
- }
-
- pLightmapWinding = pWindingT1;
- }
-
- //
- // copy over samples
- //
- pFaceLight->numsamples = pSamples - samples;
- pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
- if( !pFaceLight->sample )
- return false;
-
- memcpy( pFaceLight->sample, samples, pFaceLight->numsamples * sizeof( *pFaceLight->sample ) );
-
- // supply a default sample normal (face normal - assumed flat)
- for( int ndxSample = 0; ndxSample < pFaceLight->numsamples; ndxSample++ )
- {
- Assert ( VectorLength ( pLightInfo->facenormal ) > 1.0e-20);
- pFaceLight->sample[ndxSample].normal = pLightInfo->facenormal;
- }
-
- // statistics - warning?!
- if( pFaceLight->numsamples == 0 )
- {
- Msg( "no samples %d\n", pLightInfo->face - g_pFaces );
- }
-
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Free any windings used by this facelight. It's currently assumed they're not needed again
-//-----------------------------------------------------------------------------
-void FreeSampleWindings( facelight_t *fl )
-{
- int i;
- for (i = 0; i < fl->numsamples; i++)
- {
- if (fl->sample[i].w)
- {
- FreeWinding( fl->sample[i].w );
- fl->sample[i].w = NULL;
- }
- }
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: build the sample data for each lightmapped primitive type
-//-----------------------------------------------------------------------------
-bool BuildSamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
-{
- // build samples for a "face"
- if( pLightInfo->face->dispinfo == -1 )
- {
- return BuildFacesamples( pLightInfo, pFaceLight );
- }
- // build samples for a "displacement"
- else
- {
- return StaticDispMgr()->BuildDispSamples( pLightInfo, pFaceLight, ndxFace );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool BuildFaceLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight )
-{
- // lightmap size
- int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
- int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
-
- // calcuate actual luxel points
- pFaceLight->numluxels = width * height;
- pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
- if( !pFaceLight->luxel )
- return false;
-
- for( int t = 0; t < height; t++ )
- {
- for( int s = 0; s < width; s++ )
- {
- LuxelSpaceToWorld( pLightInfo, s, t, pFaceLight->luxel[s+t*width] );
- }
- }
-
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: build the luxels (find the luxel centers) for each lightmapped
-// primitive type
-//-----------------------------------------------------------------------------
-bool BuildLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
-{
- // build luxels for a "face"
- if( pLightInfo->face->dispinfo == -1 )
- {
- return BuildFaceLuxels( pLightInfo, pFaceLight );
- }
- // build luxels for a "displacement"
- else
- {
- return StaticDispMgr()->BuildDispLuxels( pLightInfo, pFaceLight, ndxFace );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: for each face, find the center of each luxel; for each texture
-// aligned grid point, back project onto the plane and get the world
-// xyz value of the sample point
-// NOTE: ndxFace = facenum
-//-----------------------------------------------------------------------------
-void CalcPoints( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
-{
- // debugging!
- if( g_bDumpPatches )
- {
- DumpFaces( pLightInfo, ndxFace );
- }
-
- // quick and dirty!
- if( do_fast )
- {
- if( !BuildSamplesAndLuxels_DoFast( pLightInfo, pFaceLight, ndxFace ) )
- {
- Msg( "Face %d: (Fast)Error Building Samples and Luxels\n", ndxFace );
- }
- return;
- }
-
- // build the samples
- if( !BuildSamples( pLightInfo, pFaceLight, ndxFace ) )
- {
- Msg( "Face %d: Error Building Samples\n", ndxFace );
- }
-
- // build the luxels
- if( !BuildLuxels( pLightInfo, pFaceLight, ndxFace ) )
- {
- Msg( "Face %d: Error Building Luxels\n", ndxFace );
- }
-}
-
-
-//==============================================================
-
-directlight_t *activelights;
-directlight_t *freelights;
-
-facelight_t facelight[MAX_MAP_FACES];
-int numdlights;
-
-/*
- ==================
- FindTargetEntity
- ==================
-*/
-entity_t *FindTargetEntity (char *target)
-{
- int i;
- char *n;
-
- for (i=0 ; i<num_entities ; i++)
- {
- n = ValueForKey (&entities[i], "targetname");
- if (!strcmp (n, target))
- return &entities[i];
- }
-
- return NULL;
-}
-
-
-
-/*
- =============
- AllocDLight
- =============
-*/
-
-int GetVisCache( int lastoffset, int cluster, byte *pvs );
-void SetDLightVis( directlight_t *dl, int cluster );
-void MergeDLightVis( directlight_t *dl, int cluster );
-
-directlight_t *AllocDLight( Vector& origin, bool bAddToList )
-{
- directlight_t *dl;
-
- dl = ( directlight_t* )calloc(1, sizeof(directlight_t));
- dl->index = numdlights++;
-
- VectorCopy( origin, dl->light.origin );
-
- dl->light.cluster = ClusterFromPoint(dl->light.origin);
- SetDLightVis( dl, dl->light.cluster );
-
- dl->facenum = -1;
-
- if ( bAddToList )
- {
- dl->next = activelights;
- activelights = dl;
- }
-
- return dl;
-}
-
-void AddDLightToActiveList( directlight_t *dl )
-{
- dl->next = activelights;
- activelights = dl;
-}
-
-void FreeDLights()
-{
- gSkyLight = NULL;
- gAmbient = NULL;
-
- directlight_t *pNext;
- for( directlight_t *pCur=activelights; pCur; pCur=pNext )
- {
- pNext = pCur->next;
- free( pCur );
- }
- activelights = 0;
-}
-
-
-void SetDLightVis( directlight_t *dl, int cluster )
-{
- if (dl->pvs == NULL)
- {
- dl->pvs = (byte *)calloc( 1, (dvis->numclusters / 8) + 1 );
- }
-
- GetVisCache( -1, cluster, dl->pvs );
-}
-
-void MergeDLightVis( directlight_t *dl, int cluster )
-{
- if (dl->pvs == NULL)
- {
- SetDLightVis( dl, cluster );
- }
- else
- {
- byte pvs[MAX_MAP_CLUSTERS/8];
- GetVisCache( -1, cluster, pvs );
-
- // merge both vis graphs
- for (int i = 0; i < (dvis->numclusters / 8) + 1; i++)
- {
- dl->pvs[i] |= pvs[i];
- }
- }
-}
-
-
-/*
- =============
- LightForKey
- =============
-*/
-int LightForKey (entity_t *ent, char *key, Vector& intensity )
-{
- char *pLight;
-
- pLight = ValueForKey( ent, key );
-
- return LightForString( pLight, intensity );
-}
-
-int LightForString( char *pLight, Vector& intensity )
-{
- double r, g, b, scaler;
- int argCnt;
-
- VectorFill( intensity, 0 );
-
- // scanf into doubles, then assign, so it is vec_t size independent
- r = g = b = scaler = 0;
- double r_hdr,g_hdr,b_hdr,scaler_hdr;
- argCnt = sscanf ( pLight, "%lf %lf %lf %lf %lf %lf %lf %lf",
- &r, &g, &b, &scaler, &r_hdr,&g_hdr,&b_hdr,&scaler_hdr );
-
- if (argCnt==8) // 2 4-tuples
- {
- if (g_bHDR)
- {
- r=r_hdr;
- g=g_hdr;
- b=b_hdr;
- scaler=scaler_hdr;
- }
- argCnt=4;
- }
-
- // make sure light is legal
- if( r < 0.0f || g < 0.0f || b < 0.0f || scaler < 0.0f )
- {
- intensity.Init( 0.0f, 0.0f, 0.0f );
- return false;
- }
-
- intensity[0] = pow( r / 255.0, 2.2 ) * 255; // convert to linear
-
- switch( argCnt)
- {
- case 1:
- // The R,G,B values are all equal.
- intensity[1] = intensity[2] = intensity[0];
- break;
-
- case 3:
- case 4:
- // Save the other two G,B values.
- intensity[1] = pow( g / 255.0, 2.2 ) * 255;
- intensity[2] = pow( b / 255.0, 2.2 ) * 255;
-
- // Did we also get an "intensity" scaler value too?
- if ( argCnt == 4 )
- {
- // Scale the normalized 0-255 R,G,B values by the intensity scaler
- VectorScale( intensity, scaler / 255.0, intensity );
- }
- break;
-
- default:
- printf("unknown light specifier type - %s\n",pLight);
- return false;
- }
- // scale up source lights by scaling factor
- VectorScale( intensity, lightscale, intensity );
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Various parsing methods
-//-----------------------------------------------------------------------------
-
-static void ParseLightGeneric( entity_t *e, directlight_t *dl )
-{
- entity_t *e2;
- char *target;
- Vector dest;
-
- dl->light.style = (int)FloatForKey (e, "style");
-
- // get intenfsity
- if( g_bHDR && LightForKey( e, "_lightHDR", dl->light.intensity ) )
- {
- }
- else
- {
- LightForKey( e, "_light", dl->light.intensity );
- }
-
- // check angle, targets
- target = ValueForKey (e, "target");
- if (target[0])
- { // point towards target
- e2 = FindTargetEntity (target);
- if (!e2)
- Warning("WARNING: light at (%i %i %i) has missing target\n",
- (int)dl->light.origin[0], (int)dl->light.origin[1], (int)dl->light.origin[2]);
- else
- {
- GetVectorForKey (e2, "origin", dest);
- VectorSubtract (dest, dl->light.origin, dl->light.normal);
- VectorNormalize (dl->light.normal);
- }
- }
- else
- {
- // point down angle
- Vector angles;
- GetVectorForKey( e, "angles", angles );
- float pitch = FloatForKey (e, "pitch");
- float angle = FloatForKey (e, "angle");
- SetupLightNormalFromProps( QAngle( angles.x, angles.y, angles.z ), angle, pitch, dl->light.normal );
- }
- if ( g_bHDR )
- VectorScale( dl->light.intensity,
- FloatForKeyWithDefault( e, "_lightscaleHDR", 1.0 ),
- dl->light.intensity );
-}
-
-static void SetLightFalloffParams( entity_t * e, directlight_t * dl )
-{
- float d50=FloatForKey( e, "_fifty_percent_distance" );
- dl->m_flStartFadeDistance = 0;
- dl->m_flEndFadeDistance = - 1;
- dl->m_flCapDist = 1.0e22;
- if ( d50 )
- {
- float d0 = FloatForKey( e, "_zero_percent_distance" );
- if ( d0 < d50 )
- {
- Warning( "light has _fifty_percent_distance of %f but _zero_percent_distance of %f\n", d50, d0);
- d0 = 2.0 * d50;
- }
- float a = 0, b = 1, c = 0;
- if ( ! SolveInverseQuadraticMonotonic( 0, 1.0, d50, 2.0, d0, 256.0, a, b, c ))
- {
- Warning( "can't solve quadratic for light %f %f\n", d50, d0 );
- }
- // it it possible that the parameters couldn't be used because of enforing monoticity. If so, rescale so at
- // least the 50 percent value is right
-// printf("50 percent=%f 0 percent=%f\n",d50,d0);
-// printf("a=%f b=%f c=%f\n",a,b,c);
- float v50 = c + d50 * ( b + d50 * a );
- float scale = 2.0 / v50;
- a *= scale;
- b *= scale;
- c *= scale;
-// printf("scaled=%f a=%f b=%f c=%f\n",scale,a,b,c);
-// for(float d=0;d<1000;d+=20)
-// printf("at %f, %f\n",d,1.0/(c+d*(b+d*a)));
- dl->light.quadratic_attn = a;
- dl->light.linear_attn = b;
- dl->light.constant_attn = c;
-
-
-
- if ( IntForKey(e, "_hardfalloff" ) )
- {
- dl->m_flEndFadeDistance = d0;
- dl->m_flStartFadeDistance = 0.75 * d0 + 0.25 * d50; // start fading 3/4 way between 50 and 0. could allow adjust
- }
- else
- {
- // now, we will find the point at which the 1/x term reaches its maximum value, and
- // prevent the light from going past there. If a user specifes an extreme falloff, the
- // quadratic will start making the light brighter at some distance. We handle this by
- // fading it from the minimum brightess point down to zero at 10x the minimum distance
- if ( fabs( a ) > 0. )
- {
- float flMax = b / ( - 2.0 * a ); // where f' = 0
- if ( flMax > 0.0 )
- {
- dl->m_flCapDist = flMax;
- dl->m_flStartFadeDistance = flMax;
- dl->m_flEndFadeDistance = 10.0 * flMax;
- }
- }
- }
- }
- else
- {
- dl->light.constant_attn = FloatForKey (e, "_constant_attn" );
- dl->light.linear_attn = FloatForKey (e, "_linear_attn" );
- dl->light.quadratic_attn = FloatForKey (e, "_quadratic_attn" );
-
- dl->light.radius = FloatForKey (e, "_distance");
-
- // clamp values to >= 0
- if ( dl->light.constant_attn < EQUAL_EPSILON )
- dl->light.constant_attn = 0;
-
- if ( dl->light.linear_attn < EQUAL_EPSILON )
- dl->light.linear_attn = 0;
-
- if ( dl->light.quadratic_attn < EQUAL_EPSILON )
- dl->light.quadratic_attn = 0;
-
- if ( dl->light.constant_attn < EQUAL_EPSILON && dl->light.linear_attn < EQUAL_EPSILON && dl->light.quadratic_attn < EQUAL_EPSILON )
- dl->light.constant_attn = 1;
-
- // scale intensity for unit 100 distance
- float ratio = ( dl->light.constant_attn + 100 * dl->light.linear_attn + 100 * 100 * dl->light.quadratic_attn );
- if ( ratio > 0 )
- {
- VectorScale( dl->light.intensity, ratio, dl->light.intensity );
- }
- }
-}
-
-static void ParseLightSpot( entity_t* e, directlight_t* dl )
-{
- Vector dest;
- GetVectorForKey (e, "origin", dest );
- dl = AllocDLight( dest, true );
-
- ParseLightGeneric( e, dl );
-
- dl->light.type = emit_spotlight;
-
- dl->light.stopdot = FloatForKey (e, "_inner_cone");
- if (!dl->light.stopdot)
- dl->light.stopdot = 10;
-
- dl->light.stopdot2 = FloatForKey (e, "_cone");
- if (!dl->light.stopdot2)
- dl->light.stopdot2 = dl->light.stopdot;
- if (dl->light.stopdot2 < dl->light.stopdot)
- dl->light.stopdot2 = dl->light.stopdot;
-
- // This is a point light if stop dots are 180...
- if ((dl->light.stopdot == 180) && (dl->light.stopdot2 == 180))
- {
- dl->light.stopdot = dl->light.stopdot2 = 0;
- dl->light.type = emit_point;
- dl->light.exponent = 0;
- }
- else
- {
- // Clamp to 90, that's all DX8 can handle!
- if (dl->light.stopdot > 90)
- {
- Warning("WARNING: light_spot at (%i %i %i) has inner angle larger than 90 degrees! Clamping to 90...\n",
- (int)dl->light.origin[0], (int)dl->light.origin[1], (int)dl->light.origin[2]);
- dl->light.stopdot = 90;
- }
-
- if (dl->light.stopdot2 > 90)
- {
- Warning("WARNING: light_spot at (%i %i %i) has outer angle larger than 90 degrees! Clamping to 90...\n",
- (int)dl->light.origin[0], (int)dl->light.origin[1], (int)dl->light.origin[2]);
- dl->light.stopdot2 = 90;
- }
-
- dl->light.stopdot2 = (float)cos(dl->light.stopdot2/180*M_PI);
- dl->light.stopdot = (float)cos(dl->light.stopdot/180*M_PI);
- dl->light.exponent = FloatForKey (e, "_exponent");
- }
-
- SetLightFalloffParams(e,dl);
-}
-
-// NOTE: This is just a heuristic. It traces a finite number of rays to find sky
-// NOTE: Full vis is necessary to make this 100% correct.
-bool CanLeafTraceToSky( int iLeaf )
-{
- // UNDONE: Really want a point inside the leaf here. Center is a guess, may not be in the leaf
- // UNDONE: Clip this to each plane bounding the leaf to guarantee
- Vector center = vec3_origin;
- for ( int i = 0; i < 3; i++ )
- {
- center[i] = ( (float)(dleafs[iLeaf].mins[i] + dleafs[iLeaf].maxs[i]) ) * 0.5f;
- }
-
- FourVectors center4, delta;
- fltx4 fractionVisible;
- for ( int j = 0; j < NUMVERTEXNORMALS; j+=4 )
- {
- // search back to see if we can hit a sky brush
- delta.LoadAndSwizzle( g_anorms[j], g_anorms[min( j+1, NUMVERTEXNORMALS-1 )],
- g_anorms[min( j+2, NUMVERTEXNORMALS-1 )], g_anorms[min( j+3, NUMVERTEXNORMALS-1 )] );
- delta *= -MAX_TRACE_LENGTH;
- delta += center4;
-
- // return true if any hits sky
- TestLine_DoesHitSky ( center4, delta, &fractionVisible );
- if ( TestSignSIMD ( CmpGtSIMD ( fractionVisible, Four_Zeros ) ) )
- return true;
- }
-
- return false;
-}
-
-void BuildVisForLightEnvironment( void )
-{
- // Create the vis.
- for ( int iLeaf = 0; iLeaf < numleafs; ++iLeaf )
- {
- dleafs[iLeaf].flags &= ~( LEAF_FLAGS_SKY | LEAF_FLAGS_SKY2D );
- unsigned int iFirstFace = dleafs[iLeaf].firstleafface;
- for ( int iLeafFace = 0; iLeafFace < dleafs[iLeaf].numleaffaces; ++iLeafFace )
- {
- unsigned int iFace = dleaffaces[iFirstFace+iLeafFace];
-
- texinfo_t &tex = texinfo[g_pFaces[iFace].texinfo];
- if ( tex.flags & SURF_SKY )
- {
- if ( tex.flags & SURF_SKY2D )
- {
- dleafs[iLeaf].flags |= LEAF_FLAGS_SKY2D;
- }
- else
- {
- dleafs[iLeaf].flags |= LEAF_FLAGS_SKY;
- }
- MergeDLightVis( gSkyLight, dleafs[iLeaf].cluster );
- MergeDLightVis( gAmbient, dleafs[iLeaf].cluster );
- break;
- }
- }
- }
-
- // Second pass to set flags on leaves that don't contain sky, but touch leaves that
- // contain sky.
- byte pvs[MAX_MAP_CLUSTERS / 8];
-
- int nLeafBytes = (numleafs >> 3) + 1;
- unsigned char *pLeafBits = (unsigned char *)stackalloc( nLeafBytes * sizeof(unsigned char) );
- unsigned char *pLeaf2DBits = (unsigned char *)stackalloc( nLeafBytes * sizeof(unsigned char) );
- memset( pLeafBits, 0, nLeafBytes );
- memset( pLeaf2DBits, 0, nLeafBytes );
-
- for ( int iLeaf = 0; iLeaf < numleafs; ++iLeaf )
- {
- // If this leaf has light (3d skybox) in it, then don't bother
- if ( dleafs[iLeaf].flags & LEAF_FLAGS_SKY )
- continue;
-
- // Don't bother with this leaf if it's solid
- if ( dleafs[iLeaf].contents & CONTENTS_SOLID )
- continue;
-
- // See what other leaves are visible from this leaf
- GetVisCache( -1, dleafs[iLeaf].cluster, pvs );
-
- // Now check out all other leaves
- int nByte = iLeaf >> 3;
- int nBit = 1 << ( iLeaf & 0x7 );
- for ( int iLeaf2 = 0; iLeaf2 < numleafs; ++iLeaf2 )
- {
- if ( iLeaf2 == iLeaf )
- continue;
-
- if ( !(dleafs[iLeaf2].flags & ( LEAF_FLAGS_SKY | LEAF_FLAGS_SKY2D ) ) )
- continue;
-
- // Can this leaf see into the leaf with the sky in it?
- if ( !PVSCheck( pvs, dleafs[iLeaf2].cluster ) )
- continue;
-
- if ( dleafs[iLeaf2].flags & LEAF_FLAGS_SKY2D )
- {
- pLeaf2DBits[ nByte ] |= nBit;
- }
- if ( dleafs[iLeaf2].flags & LEAF_FLAGS_SKY )
- {
- pLeafBits[ nByte ] |= nBit;
-
- // As soon as we know this leaf needs to draw the 3d skybox, we're done
- break;
- }
- }
- }
-
- // Must set the bits in a separate pass so as to not flood-fill LEAF_FLAGS_SKY everywhere
- // pLeafbits is a bit array of all leaves that need to be marked as seeing sky
- for ( int iLeaf = 0; iLeaf < numleafs; ++iLeaf )
- {
- // If this leaf has light (3d skybox) in it, then don't bother
- if ( dleafs[iLeaf].flags & LEAF_FLAGS_SKY )
- continue;
-
- // Don't bother with this leaf if it's solid
- if ( dleafs[iLeaf].contents & CONTENTS_SOLID )
- continue;
-
- // Check to see if this is a 2D skybox leaf
- if ( pLeaf2DBits[ iLeaf >> 3 ] & (1 << ( iLeaf & 0x7 )) )
- {
- dleafs[iLeaf].flags |= LEAF_FLAGS_SKY2D;
- }
-
- // If this is a 3D skybox leaf, then we don't care if it was previously a 2D skybox leaf
- if ( pLeafBits[ iLeaf >> 3 ] & (1 << ( iLeaf & 0x7 )) )
- {
- dleafs[iLeaf].flags |= LEAF_FLAGS_SKY;
- dleafs[iLeaf].flags &= ~LEAF_FLAGS_SKY2D;
- }
- else
- {
- // if radial vis was used on this leaf some of the portals leading
- // to sky may have been culled. Try tracing to find sky.
- if ( dleafs[iLeaf].flags & LEAF_FLAGS_RADIAL )
- {
- if ( CanLeafTraceToSky(iLeaf) )
- {
- // FIXME: Should make a version that checks if we hit 2D skyboxes.. oh well.
- dleafs[iLeaf].flags |= LEAF_FLAGS_SKY;
- }
- }
- }
- }
-}
-
-static char *ValueForKeyWithDefault (entity_t *ent, char *key, char *default_value = NULL)
-{
- epair_t *ep;
-
- for (ep=ent->epairs ; ep ; ep=ep->next)
- if (!strcmp (ep->key, key) )
- return ep->value;
- return default_value;
-}
-
-static void ParseLightEnvironment( entity_t* e, directlight_t* dl )
-{
- Vector dest;
- GetVectorForKey (e, "origin", dest );
- dl = AllocDLight( dest, false );
-
- ParseLightGeneric( e, dl );
-
- char *angle_str=ValueForKeyWithDefault( e, "SunSpreadAngle" );
- if (angle_str)
- {
- g_SunAngularExtent=atof(angle_str);
- g_SunAngularExtent=sin((M_PI/180.0)*g_SunAngularExtent);
- printf("sun extent from map=%f\n",g_SunAngularExtent);
- }
- if ( !gSkyLight )
- {
- // Sky light.
- gSkyLight = dl;
- dl->light.type = emit_skylight;
-
- // Sky ambient light.
- gAmbient = AllocDLight( dl->light.origin, false );
- gAmbient->light.type = emit_skyambient;
- if( g_bHDR && LightForKey( e, "_ambientHDR", gAmbient->light.intensity ) )
- {
- // we have a valid HDR ambient light value
- }
- else if ( !LightForKey( e, "_ambient", gAmbient->light.intensity ) )
- {
- VectorScale( dl->light.intensity, 0.5, gAmbient->light.intensity );
- }
- if ( g_bHDR )
- {
- VectorScale( gAmbient->light.intensity,
- FloatForKeyWithDefault( e, "_AmbientScaleHDR", 1.0 ),
- gAmbient->light.intensity );
- }
-
- BuildVisForLightEnvironment();
-
- // Add sky and sky ambient lights to the list.
- AddDLightToActiveList( gSkyLight );
- AddDLightToActiveList( gAmbient );
- }
-}
-
-static void ParseLightPoint( entity_t* e, directlight_t* dl )
-{
- Vector dest;
- GetVectorForKey (e, "origin", dest );
- dl = AllocDLight( dest, true );
-
- ParseLightGeneric( e, dl );
-
- dl->light.type = emit_point;
-
- SetLightFalloffParams(e,dl);
-}
-
-/*
- =============
- CreateDirectLights
- =============
-*/
-#define DIRECT_SCALE (100.0*100.0)
-void CreateDirectLights (void)
-{
- unsigned i;
- CPatch *p = NULL;
- directlight_t *dl = NULL;
- entity_t *e = NULL;
- char *name;
- Vector dest;
-
- numdlights = 0;
-
- FreeDLights();
-
- //
- // surfaces
- //
- unsigned int uiPatchCount = g_Patches.Count();
- for (i=0; i< uiPatchCount; i++)
- {
- p = &g_Patches.Element( i );
-
- // skip parent patches
- if (p->child1 != g_Patches.InvalidIndex() )
- continue;
-
- if (p->basearea < 1e-6)
- continue;
-
- if( VectorAvg( p->baselight ) >= dlight_threshold )
- {
- dl = AllocDLight( p->origin, true );
-
- dl->light.type = emit_surface;
- VectorCopy (p->normal, dl->light.normal);
- Assert( VectorLength( p->normal ) > 1.0e-20 );
- // scale intensity by number of texture instances
- VectorScale( p->baselight, lightscale * p->area * p->scale[0] * p->scale[1] / p->basearea, dl->light.intensity );
-
- // scale to a range that results in actual light
- VectorScale( dl->light.intensity, DIRECT_SCALE, dl->light.intensity );
- }
- }
-
- //
- // entities
- //
- for (i=0 ; i<(unsigned)num_entities ; i++)
- {
- e = &entities[i];
- name = ValueForKey (e, "classname");
- if (strncmp (name, "light", 5))
- continue;
-
- // Light_dynamic is actually a real entity; not to be included here...
- if (!strcmp (name, "light_dynamic"))
- continue;
-
- if (!strcmp (name, "light_spot"))
- {
- ParseLightSpot( e, dl );
- }
- else if (!strcmp(name, "light_environment"))
- {
- ParseLightEnvironment( e, dl );
- }
- else if (!strcmp(name, "light"))
- {
- ParseLightPoint( e, dl );
- }
- else
- {
- qprintf( "unsupported light entity: \"%s\"\n", name );
- }
- }
-
- qprintf ("%i direct lights\n", numdlights);
- // exit(1);
-}
-
-/*
- =============
- ExportDirectLightsToWorldLights
- =============
-*/
-
-void ExportDirectLightsToWorldLights()
-{
- directlight_t *dl;
-
- // In case the level has already been VRADed.
- *pNumworldlights = 0;
-
- for (dl = activelights; dl != NULL; dl = dl->next )
- {
- dworldlight_t *wl = &dworldlights[(*pNumworldlights)++];
-
- if (*pNumworldlights > MAX_MAP_WORLDLIGHTS)
- {
- Error("too many lights %d / %d\n", *pNumworldlights, MAX_MAP_WORLDLIGHTS );
- }
-
- wl->cluster = dl->light.cluster;
- wl->type = dl->light.type;
- wl->style = dl->light.style;
- VectorCopy( dl->light.origin, wl->origin );
- // FIXME: why does vrad want 0 to 255 and not 0 to 1??
- VectorScale( dl->light.intensity, (1.0 / 255.0), wl->intensity );
- VectorCopy( dl->light.normal, wl->normal );
- wl->stopdot = dl->light.stopdot;
- wl->stopdot2 = dl->light.stopdot2;
- wl->exponent = dl->light.exponent;
- wl->radius = dl->light.radius;
- wl->constant_attn = dl->light.constant_attn;
- wl->linear_attn = dl->light.linear_attn;
- wl->quadratic_attn = dl->light.quadratic_attn;
- wl->flags = 0;
- }
-}
-
-/*
- =============
- GatherSampleLight
- =============
-*/
-#define NORMALFORMFACTOR 40.156979 // accumuated dot products for hemisphere
-
-#define CONSTANT_DOT (.7/2)
-
-#define NSAMPLES_SUN_AREA_LIGHT 30 // number of samples to take for an
- // non-point sun light
-
-// Helper function - gathers light from sun (emit_skylight)
-void GatherSampleSkyLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
- FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
- int nLFlags, int static_prop_index_to_ignore,
- float flEpsilon )
-{
- bool bIgnoreNormals = ( nLFlags & GATHERLFLAGS_IGNORE_NORMALS ) != 0;
- bool force_fast = ( nLFlags & GATHERLFLAGS_FORCE_FAST ) != 0;
-
- fltx4 dot;
-
- if ( bIgnoreNormals )
- dot = ReplicateX4( CONSTANT_DOT );
- else
- dot = NegSIMD( pNormals[0] * dl->light.normal );
-
- dot = MaxSIMD( dot, Four_Zeros );
- int zeroMask = TestSignSIMD ( CmpEqSIMD( dot, Four_Zeros ) );
- if (zeroMask == 0xF)
- return;
-
- int nsamples = 1;
- if ( g_SunAngularExtent > 0.0f )
- {
- nsamples = NSAMPLES_SUN_AREA_LIGHT;
- if ( do_fast || force_fast )
- nsamples /= 4;
- }
-
- fltx4 totalFractionVisible = Four_Zeros;
- fltx4 fractionVisible = Four_Zeros;
-
- DirectionalSampler_t sampler;
-
- for ( int d = 0; d < nsamples; d++ )
- {
- // determine visibility of skylight
- // serach back to see if we can hit a sky brush
- Vector delta;
- VectorScale( dl->light.normal, -MAX_TRACE_LENGTH, delta );
- if ( d )
- {
- // jitter light source location
- Vector ofs = sampler.NextValue();
- ofs *= MAX_TRACE_LENGTH * g_SunAngularExtent;
- delta += ofs;
- }
- FourVectors delta4;
- delta4.DuplicateVector ( delta );
- delta4 += pos;
-
- TestLine_DoesHitSky ( pos, delta4, &fractionVisible, true, static_prop_index_to_ignore );
-
- totalFractionVisible = AddSIMD ( totalFractionVisible, fractionVisible );
- }
-
- fltx4 seeAmount = MulSIMD ( totalFractionVisible, ReplicateX4 ( 1.0f / nsamples ) );
- out.m_flDot[0] = MulSIMD ( dot, seeAmount );
- out.m_flFalloff = Four_Ones;
- out.m_flSunAmount = MulSIMD ( seeAmount, ReplicateX4( 10000.0f ) );
- for ( int i = 1; i < normalCount; i++ )
- {
- if ( bIgnoreNormals )
- out.m_flDot[i] = ReplicateX4 ( CONSTANT_DOT );
- else
- {
- out.m_flDot[i] = NegSIMD( pNormals[i] * dl->light.normal );
- out.m_flDot[i] = MulSIMD( out.m_flDot[i], seeAmount );
- }
- }
-}
-
-// Helper function - gathers light from ambient sky light
-void GatherSampleAmbientSkySSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
- FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
- int nLFlags, int static_prop_index_to_ignore,
- float flEpsilon )
-{
-
- bool bIgnoreNormals = ( nLFlags & GATHERLFLAGS_IGNORE_NORMALS ) != 0;
- bool force_fast = ( nLFlags & GATHERLFLAGS_FORCE_FAST ) != 0;
-
- fltx4 sumdot = Four_Zeros;
- fltx4 ambient_intensity[NUM_BUMP_VECTS+1];
- fltx4 possibleHitCount[NUM_BUMP_VECTS+1];
- fltx4 dots[NUM_BUMP_VECTS+1];
-
- for ( int i = 0; i < normalCount; i++ )
- {
- ambient_intensity[i] = Four_Zeros;
- possibleHitCount[i] = Four_Zeros;
- }
-
- DirectionalSampler_t sampler;
- int nsky_samples = NUMVERTEXNORMALS;
- if (do_fast || force_fast )
- nsky_samples /= 4;
- else
- nsky_samples *= g_flSkySampleScale;
-
- for (int j = 0; j < nsky_samples; j++)
- {
- FourVectors anorm;
- anorm.DuplicateVector( sampler.NextValue() );
-
- if ( bIgnoreNormals )
- dots[0] = ReplicateX4( CONSTANT_DOT );
- else
- dots[0] = NegSIMD( pNormals[0] * anorm );
-
- fltx4 validity = CmpGtSIMD( dots[0], ReplicateX4( EQUAL_EPSILON ) );
-
- // No possibility of anybody getting lit
- if ( !TestSignSIMD( validity ) )
- continue;
-
- dots[0] = AndSIMD( validity, dots[0] );
- sumdot = AddSIMD( dots[0], sumdot );
- possibleHitCount[0] = AddSIMD( AndSIMD( validity, Four_Ones ), possibleHitCount[0] );
-
- for ( int i = 1; i < normalCount; i++ )
- {
- if ( bIgnoreNormals )
- dots[i] = ReplicateX4( CONSTANT_DOT );
- else
- dots[i] = NegSIMD( pNormals[i] * anorm );
- fltx4 validity2 = CmpGtSIMD( dots[i], ReplicateX4 ( EQUAL_EPSILON ) );
- dots[i] = AndSIMD( validity2, dots[i] );
- possibleHitCount[i] = AddSIMD( AndSIMD( AndSIMD( validity, validity2 ), Four_Ones ), possibleHitCount[i] );
- }
-
- // search back to see if we can hit a sky brush
- FourVectors delta = anorm;
- delta *= -MAX_TRACE_LENGTH;
- delta += pos;
- FourVectors surfacePos = pos;
- FourVectors offset = anorm;
- offset *= -flEpsilon;
- surfacePos -= offset;
-
- fltx4 fractionVisible = Four_Ones;
- TestLine_DoesHitSky( surfacePos, delta, &fractionVisible, true, static_prop_index_to_ignore );
- for ( int i = 0; i < normalCount; i++ )
- {
- fltx4 addedAmount = MulSIMD( fractionVisible, dots[i] );
- ambient_intensity[i] = AddSIMD( ambient_intensity[i], addedAmount );
- }
-
- }
-
- out.m_flFalloff = Four_Ones;
- for ( int i = 0; i < normalCount; i++ )
- {
- // now scale out the missing parts of the hemisphere of this bump basis vector
- fltx4 factor = ReciprocalSIMD( possibleHitCount[0] );
- factor = MulSIMD( factor, possibleHitCount[i] );
- out.m_flDot[i] = MulSIMD( factor, sumdot );
- out.m_flDot[i] = ReciprocalSIMD( out.m_flDot[i] );
- out.m_flDot[i] = MulSIMD( ambient_intensity[i], out.m_flDot[i] );
- }
-
-}
-
-// Helper function - gathers light from area lights, spot lights, and point lights
-void GatherSampleStandardLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
- FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
- int nLFlags, int static_prop_index_to_ignore,
- float flEpsilon )
-{
- bool bIgnoreNormals = ( nLFlags & GATHERLFLAGS_IGNORE_NORMALS ) != 0;
-
- FourVectors src;
- src.DuplicateVector( vec3_origin );
-
- if (dl->facenum == -1)
- {
- src.DuplicateVector( dl->light.origin );
- }
-
- // Find light vector
- FourVectors delta;
- delta = src;
- delta -= pos;
- fltx4 dist2 = delta.length2();
- fltx4 rpcDist = ReciprocalSqrtSIMD( dist2 );
- delta *= rpcDist;
- fltx4 dist = SqrtEstSIMD( dist2 );//delta.VectorNormalize();
-
- // Compute dot
- fltx4 dot = ReplicateX4( (float) CONSTANT_DOT );
- if ( !bIgnoreNormals )
- dot = delta * pNormals[0];
- dot = MaxSIMD( Four_Zeros, dot );
-
- // Affix dot to zero if past fade distz
- bool bHasHardFalloff = ( dl->m_flEndFadeDistance > dl->m_flStartFadeDistance );
- if ( bHasHardFalloff )
- {
- fltx4 notPastFadeDist = CmpLeSIMD ( dist, ReplicateX4 ( dl->m_flEndFadeDistance ) );
- dot = AndSIMD( dot, notPastFadeDist ); // dot = 0 if past fade distance
- if ( !TestSignSIMD ( notPastFadeDist ) )
- return;
- }
-
- dist = MaxSIMD( dist, Four_Ones );
- fltx4 falloffEvalDist = MinSIMD( dist, ReplicateX4( dl->m_flCapDist ) );
-
- fltx4 constant, linear, quadratic;
- fltx4 dot2, inCone, inFringe, mult;
- FourVectors offset;
-
- switch (dl->light.type)
- {
- case emit_point:
- constant = ReplicateX4( dl->light.constant_attn );
- linear = ReplicateX4( dl->light.linear_attn );
- quadratic = ReplicateX4( dl->light.quadratic_attn );
-
- out.m_flFalloff = MulSIMD( falloffEvalDist, falloffEvalDist );
- out.m_flFalloff = MulSIMD( out.m_flFalloff, quadratic );
- out.m_flFalloff = AddSIMD( out.m_flFalloff, MulSIMD( linear, falloffEvalDist ) );
- out.m_flFalloff = AddSIMD( out.m_flFalloff, constant );
- out.m_flFalloff = ReciprocalSIMD( out.m_flFalloff );
- break;
-
- case emit_surface:
- dot2 = delta * dl->light.normal;
- dot2 = NegSIMD( dot2 );
-
- // Light behind surface yields zero dot
- dot2 = MaxSIMD( Four_Zeros, dot2 );
- if ( TestSignSIMD( CmpEqSIMD( Four_Zeros, dot ) ) == 0xF )
- return;
-
- out.m_flFalloff = ReciprocalSIMD ( dist2 );
- out.m_flFalloff = MulSIMD( out.m_flFalloff, dot2 );
-
- // move the endpoint away from the surface by epsilon to prevent hitting the surface with the trace
- offset.DuplicateVector ( dl->light.normal );
- offset *= DIST_EPSILON;
- src += offset;
- break;
-
- case emit_spotlight:
- dot2 = delta * dl->light.normal;
- dot2 = NegSIMD( dot2 );
-
- // Affix dot2 to zero if outside light cone
- inCone = CmpGtSIMD( dot2, ReplicateX4( dl->light.stopdot2 ) );
- if ( !TestSignSIMD ( inCone ) )
- return;
- dot = AndSIMD( inCone, dot );
-
- constant = ReplicateX4( dl->light.constant_attn );
- linear = ReplicateX4( dl->light.linear_attn );
- quadratic = ReplicateX4( dl->light.quadratic_attn );
-
- out.m_flFalloff = MulSIMD( falloffEvalDist, falloffEvalDist );
- out.m_flFalloff = MulSIMD( out.m_flFalloff, quadratic );
- out.m_flFalloff = AddSIMD( out.m_flFalloff, MulSIMD( linear, falloffEvalDist ) );
- out.m_flFalloff = AddSIMD( out.m_flFalloff, constant );
- out.m_flFalloff = ReciprocalSIMD( out.m_flFalloff );
- out.m_flFalloff = MulSIMD( out.m_flFalloff, dot2 );
-
- // outside the inner cone
- inFringe = CmpLeSIMD( dot2, ReplicateX4( dl->light.stopdot ) );
- mult = ReplicateX4( dl->light.stopdot - dl->light.stopdot2 );
- mult = ReciprocalSIMD( mult );
- mult = MulSIMD( mult, SubSIMD( dot2, ReplicateX4( dl->light.stopdot2 ) ) );
- mult = MinSIMD( mult, Four_Ones );
- mult = MaxSIMD( mult, Four_Zeros );
-
- // pow is fixed point, so this isn't the most accurate, but it doesn't need to be
- if ( (dl->light.exponent != 0.0f ) && ( dl->light.exponent != 1.0f ) )
- mult = PowSIMD( mult, dl->light.exponent );
-
- // if not in between inner and outer cones, mult by 1
- mult = AndSIMD( inFringe, mult );
- mult = AddSIMD( mult, AndNotSIMD( inFringe, Four_Ones ) );
- out.m_flFalloff = MulSIMD( mult, out.m_flFalloff );
- break;
-
- }
-
- // we may be in the fade region - modulate lighting by the fade curve
- //float t = ( dist - dl->m_flStartFadeDistance ) /
- // ( dl->m_flEndFadeDistance - dl->m_flStartFadeDistance );
- if ( bHasHardFalloff )
- {
- fltx4 t = ReplicateX4( dl->m_flEndFadeDistance - dl->m_flStartFadeDistance );
- t = ReciprocalSIMD( t );
- t = MulSIMD( t, SubSIMD( dist, ReplicateX4( dl->m_flStartFadeDistance ) ) );
-
- // clamp t to [0...1]
- t = MinSIMD( t, Four_Ones );
- t = MaxSIMD( t, Four_Zeros );
- t = SubSIMD( Four_Ones, t );
-
- // Using QuinticInterpolatingPolynomial, SSE-ified
- // t * t * t *( t * ( t* 6.0 - 15.0 ) + 10.0 )
- mult = SubSIMD( MulSIMD( ReplicateX4( 6.0f ), t ), ReplicateX4( 15.0f ) );
- mult = AddSIMD( MulSIMD( mult, t ), ReplicateX4( 10.0f ) );
- mult = MulSIMD( MulSIMD( t, t), mult );
- mult = MulSIMD( t, mult );
- out.m_flFalloff = MulSIMD( mult, out.m_flFalloff );
- }
-
- // Raytrace for visibility function
- fltx4 fractionVisible = Four_Ones;
- TestLine( pos, src, &fractionVisible, static_prop_index_to_ignore);
- dot = MulSIMD( fractionVisible, dot );
- out.m_flDot[0] = dot;
-
- for ( int i = 1; i < normalCount; i++ )
- {
- if ( bIgnoreNormals )
- out.m_flDot[i] = ReplicateX4( (float) CONSTANT_DOT );
- else
- {
- out.m_flDot[i] = pNormals[i] * delta;
- out.m_flDot[i] = MaxSIMD( Four_Zeros, out.m_flDot[i] );
- }
- }
-}
-
-// returns dot product with normal and delta
-// dl - light
-// pos - position of sample
-// normal - surface normal of sample
-// out.m_flDot[] - returned dot products with light vector and each normal
-// out.m_flFalloff - amount of light falloff
-void GatherSampleLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
- FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
- int nLFlags,
- int static_prop_index_to_ignore,
- float flEpsilon )
-{
- for ( int b = 0; b < normalCount; b++ )
- out.m_flDot[b] = Four_Zeros;
- out.m_flFalloff = Four_Zeros;
- out.m_flSunAmount = Four_Zeros;
- Assert( normalCount <= (NUM_BUMP_VECTS+1) );
-
- // skylights work fundamentally differently than normal lights
- switch( dl->light.type )
- {
- case emit_skylight:
- GatherSampleSkyLightSSE( out, dl, facenum, pos, pNormals, normalCount,
- iThread, nLFlags, static_prop_index_to_ignore, flEpsilon );
- break;
- case emit_skyambient:
- GatherSampleAmbientSkySSE( out, dl, facenum, pos, pNormals, normalCount,
- iThread, nLFlags, static_prop_index_to_ignore, flEpsilon );
- break;
- case emit_point:
- case emit_surface:
- case emit_spotlight:
- GatherSampleStandardLightSSE( out, dl, facenum, pos, pNormals, normalCount,
- iThread, nLFlags, static_prop_index_to_ignore, flEpsilon );
- break;
- default:
- Error ("Bad dl->light.type");
- return;
- }
-
- // NOTE: Notice here that if the light is on the back side of the face
- // (tested by checking the dot product of the face normal and the light position)
- // we don't want it to contribute to *any* of the bumped lightmaps. It glows
- // in disturbing ways if we don't do this.
- out.m_flDot[0] = MaxSIMD ( out.m_flDot[0], Four_Zeros );
- fltx4 notZero = CmpGtSIMD( out.m_flDot[0], Four_Zeros );
- for ( int n = 1; n < normalCount; n++ )
- {
- out.m_flDot[n] = MaxSIMD( out.m_flDot[n], Four_Zeros );
- out.m_flDot[n] = AndSIMD( out.m_flDot[n], notZero );
- }
-
-}
-
-/*
- =============
- AddSampleToPatch
-
- Take the sample's collected light and
- add it back into the apropriate patch
- for the radiosity pass.
- =============
-*/
-void AddSampleToPatch (sample_t *s, LightingValue_t& light, int facenum)
-{
- CPatch *patch;
- Vector mins, maxs;
- int i;
-
- if (numbounce == 0)
- return;
- if( VectorAvg( light.m_vecLighting ) < 1)
- return;
-
- //
- // fixed the sample position and normal -- need to find the equiv pos, etc to set up
- // patches
- //
- if( g_FacePatches.Element( facenum ) == g_FacePatches.InvalidIndex() )
- return;
-
- float radius = sqrt( s->area ) / 2.0;
-
- CPatch *pNextPatch = NULL;
- for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
- {
- // next patch
- pNextPatch = NULL;
- if( patch->ndxNext != g_Patches.InvalidIndex() )
- {
- pNextPatch = &g_Patches.Element( patch->ndxNext );
- }
-
- if (patch->sky)
- continue;
-
- // skip patches with children
- if ( patch->child1 != g_Patches.InvalidIndex() )
- continue;
-
- // see if the point is in this patch (roughly)
- WindingBounds (patch->winding, mins, maxs);
-
- for (i=0 ; i<3 ; i++)
- {
- if (mins[i] > s->pos[i] + radius)
- goto nextpatch;
- if (maxs[i] < s->pos[i] - radius)
- goto nextpatch;
- }
-
- // add the sample to the patch
- patch->samplearea += s->area;
- VectorMA( patch->samplelight, s->area, light.m_vecLighting, patch->samplelight );
-
- nextpatch:;
- }
- // don't worry if some samples don't find a patch
-}
-
-
-void GetPhongNormal( int facenum, Vector const& spot, Vector& phongnormal )
-{
- int j;
- dface_t *f = &g_pFaces[facenum];
-// dplane_t *p = &dplanes[f->planenum];
- Vector facenormal, vspot;
-
- VectorCopy( dplanes[f->planenum].normal, facenormal );
- VectorCopy( facenormal, phongnormal );
-
- if ( smoothing_threshold != 1 )
- {
- faceneighbor_t *fn = &faceneighbor[facenum];
-
- // Calculate modified point normal for surface
- // Use the edge normals iff they are defined. Bend the surface towards the edge normal(s)
- // Crude first attempt: find nearest edge normal and do a simple interpolation with facenormal.
- // Second attempt: find edge points+center that bound the point and do a three-point triangulation(baricentric)
- // Better third attempt: generate the point normals for all vertices and do baricentric triangulation.
-
- for (j=0 ; j<f->numedges ; j++)
- {
- Vector v1, v2;
- //int e = dsurfedges[f->firstedge + j];
- //int e1 = dsurfedges[f->firstedge + ((j+f->numedges-1)%f->numedges)];
- //int e2 = dsurfedges[f->firstedge + ((j+1)%f->numedges)];
-
- //edgeshare_t *es = &edgeshare[abs(e)];
- //edgeshare_t *es1 = &edgeshare[abs(e1)];
- //edgeshare_t *es2 = &edgeshare[abs(e2)];
- // dface_t *f2;
- float a1, a2, aa, bb, ab;
- int vert1, vert2;
-
- Vector& n1 = fn->normal[j];
- Vector& n2 = fn->normal[(j+1)%f->numedges];
-
- /*
- if (VectorCompare( n1, fn->facenormal )
- && VectorCompare( n2, fn->facenormal) )
- continue;
- */
-
- vert1 = EdgeVertex( f, j );
- vert2 = EdgeVertex( f, j+1 );
-
- Vector& p1 = dvertexes[vert1].point;
- Vector& p2 = dvertexes[vert2].point;
-
- // Build vectors from the middle of the face to the edge vertexes and the sample pos.
- VectorSubtract( p1, face_centroids[facenum], v1 );
- VectorSubtract( p2, face_centroids[facenum], v2 );
- VectorSubtract( spot, face_centroids[facenum], vspot );
- aa = DotProduct( v1, v1 );
- bb = DotProduct( v2, v2 );
- ab = DotProduct( v1, v2 );
- a1 = (bb * DotProduct( v1, vspot ) - ab * DotProduct( vspot, v2 )) / (aa * bb - ab * ab);
- a2 = (DotProduct( vspot, v2 ) - a1 * ab) / bb;
-
- // Test center to sample vector for inclusion between center to vertex vectors (Use dot product of vectors)
- if ( a1 >= 0.0 && a2 >= 0.0)
- {
- // calculate distance from edge to pos
- Vector temp;
- float scale;
-
- // Interpolate between the center and edge normals based on sample position
- scale = 1.0 - a1 - a2;
- VectorScale( fn->facenormal, scale, phongnormal );
- VectorScale( n1, a1, temp );
- VectorAdd( phongnormal, temp, phongnormal );
- VectorScale( n2, a2, temp );
- VectorAdd( phongnormal, temp, phongnormal );
- Assert( VectorLength( phongnormal ) > 1.0e-20 );
- VectorNormalize( phongnormal );
-
- /*
- if (a1 > 1 || a2 > 1 || a1 + a2 > 1)
- {
- Msg("\n%.2f %.2f\n", a1, a2 );
- Msg("%.2f %.2f %.2f\n", v1[0], v1[1], v1[2] );
- Msg("%.2f %.2f %.2f\n", v2[0], v2[1], v2[2] );
- Msg("%.2f %.2f %.2f\n", vspot[0], vspot[1], vspot[2] );
- exit(1);
-
- a1 = 0;
- }
- */
- /*
- phongnormal[0] = (((j + 1) & 4) != 0) * 255;
- phongnormal[1] = (((j + 1) & 2) != 0) * 255;
- phongnormal[2] = (((j + 1) & 1) != 0) * 255;
- */
- return;
- }
- }
- }
-}
-
-void GetPhongNormal( int facenum, FourVectors const& spot, FourVectors& phongnormal )
-{
- int j;
- dface_t *f = &g_pFaces[facenum];
- // dplane_t *p = &dplanes[f->planenum];
- Vector facenormal;
- FourVectors vspot;
-
- VectorCopy( dplanes[f->planenum].normal, facenormal );
- phongnormal.DuplicateVector( facenormal );
-
- FourVectors faceCentroid;
- faceCentroid.DuplicateVector( face_centroids[facenum] );
-
- if ( smoothing_threshold != 1 )
- {
- faceneighbor_t *fn = &faceneighbor[facenum];
-
- // Calculate modified point normal for surface
- // Use the edge normals iff they are defined. Bend the surface towards the edge normal(s)
- // Crude first attempt: find nearest edge normal and do a simple interpolation with facenormal.
- // Second attempt: find edge points+center that bound the point and do a three-point triangulation(baricentric)
- // Better third attempt: generate the point normals for all vertices and do baricentric triangulation.
-
- for ( j = 0; j < f->numedges; ++j )
- {
- Vector v1, v2;
- fltx4 a1, a2;
- float aa, bb, ab;
- int vert1, vert2;
-
- Vector& n1 = fn->normal[j];
- Vector& n2 = fn->normal[(j+1)%f->numedges];
-
- vert1 = EdgeVertex( f, j );
- vert2 = EdgeVertex( f, j+1 );
-
- Vector& p1 = dvertexes[vert1].point;
- Vector& p2 = dvertexes[vert2].point;
-
- // Build vectors from the middle of the face to the edge vertexes and the sample pos.
- VectorSubtract( p1, face_centroids[facenum], v1 );
- VectorSubtract( p2, face_centroids[facenum], v2 );
- //VectorSubtract( spot, face_centroids[facenum], vspot );
- vspot = spot;
- vspot -= faceCentroid;
- aa = DotProduct( v1, v1 );
- bb = DotProduct( v2, v2 );
- ab = DotProduct( v1, v2 );
- //a1 = (bb * DotProduct( v1, vspot ) - ab * DotProduct( vspot, v2 )) / (aa * bb - ab * ab);
- a1 = ReciprocalSIMD( ReplicateX4( aa * bb - ab * ab ) );
- a1 = MulSIMD( a1, SubSIMD( MulSIMD( ReplicateX4( bb ), vspot * v1 ), MulSIMD( ReplicateX4( ab ), vspot * v2 ) ) );
- //a2 = (DotProduct( vspot, v2 ) - a1 * ab) / bb;
- a2 = ReciprocalSIMD( ReplicateX4( bb ) );
- a2 = MulSIMD( a2, SubSIMD( vspot * v2, MulSIMD( a1, ReplicateX4( ab ) ) ) );
-
- fltx4 resultMask = AndSIMD( CmpGeSIMD( a1, Four_Zeros ), CmpGeSIMD( a2, Four_Zeros ) );
-
- if ( !TestSignSIMD( resultMask ) )
- continue;
-
- // Store the old phong normal to avoid overwriting already computed phong normals
- FourVectors oldPhongNormal = phongnormal;
-
- // calculate distance from edge to pos
- FourVectors temp;
- fltx4 scale;
-
- // Interpolate between the center and edge normals based on sample position
- scale = SubSIMD( SubSIMD( Four_Ones, a1 ), a2 );
- phongnormal.DuplicateVector( fn->facenormal );
- phongnormal *= scale;
- temp.DuplicateVector( n1 );
- temp *= a1;
- phongnormal += temp;
- temp.DuplicateVector( n2 );
- temp *= a2;
- phongnormal += temp;
-
- // restore the old phong normals
- phongnormal.x = AddSIMD( AndSIMD( resultMask, phongnormal.x ), AndNotSIMD( resultMask, oldPhongNormal.x ) );
- phongnormal.y = AddSIMD( AndSIMD( resultMask, phongnormal.y ), AndNotSIMD( resultMask, oldPhongNormal.y ) );
- phongnormal.z = AddSIMD( AndSIMD( resultMask, phongnormal.z ), AndNotSIMD( resultMask, oldPhongNormal.z ) );
- }
-
- phongnormal.VectorNormalize();
- }
-}
-
-
-
-int GetVisCache( int lastoffset, int cluster, byte *pvs )
-{
- // get the PVS for the pos to limit the number of checks
- if ( !visdatasize )
- {
- memset (pvs, 255, (dvis->numclusters+7)/8 );
- lastoffset = -1;
- }
- else
- {
- if (cluster < 0)
- {
- // Error, point embedded in wall
- // sampled[0][1] = 255;
- memset (pvs, 255, (dvis->numclusters+7)/8 );
- lastoffset = -1;
- }
- else
- {
- int thisoffset = dvis->bitofs[ cluster ][DVIS_PVS];
- if ( thisoffset != lastoffset )
- {
- if ( thisoffset == -1 )
- {
- Error ("visofs == -1");
- }
-
- DecompressVis (&dvisdata[thisoffset], pvs);
- }
- lastoffset = thisoffset;
- }
- }
- return lastoffset;
-}
-
-
-void BuildPatchLights( int facenum );
-
-void DumpSamples( int ndxFace, facelight_t *pFaceLight )
-{
- ThreadLock();
-
- dface_t *pFace = &g_pFaces[ndxFace];
- if( pFace )
- {
- bool bBumpped = ( ( texinfo[pFace->texinfo].flags & SURF_BUMPLIGHT ) != 0 );
-
- for( int iStyle = 0; iStyle < 4; ++iStyle )
- {
- if( pFace->styles[iStyle] != 255 )
- {
- for ( int iBump = 0; iBump < 4; ++iBump )
- {
- if ( iBump == 0 || ( iBump > 0 && bBumpped ) )
- {
- for( int iSample = 0; iSample < pFaceLight->numsamples; ++iSample )
- {
- sample_t *pSample = &pFaceLight->sample[iSample];
- WriteWinding( pFileSamples[iStyle][iBump], pSample->w, pFaceLight->light[iStyle][iBump][iSample].m_vecLighting );
- if( bDumpNormals )
- {
- WriteNormal( pFileSamples[iStyle][iBump], pSample->pos, pSample->normal, 15.0f, pSample->normal * 255.0f );
- }
- }
- }
- }
- }
- }
- }
-
- ThreadUnlock();
-}
-
-
-//-----------------------------------------------------------------------------
-// Allocates light sample data
-//-----------------------------------------------------------------------------
-static inline void AllocateLightstyleSamples( facelight_t* fl, int styleIndex, int numnormals )
-{
- for (int n = 0; n < numnormals; ++n)
- {
- fl->light[styleIndex][n] = ( LightingValue_t* )calloc( fl->numsamples, sizeof(LightingValue_t ) );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Used to find an existing lightstyle on a face
-//-----------------------------------------------------------------------------
-static inline int FindLightstyle( dface_t* f, int lightstyle )
-{
- for (int k = 0; k < MAXLIGHTMAPS; k++)
- {
- if (f->styles[k] == lightstyle)
- return k;
- }
-
- return -1;
-}
-
-static int FindOrAllocateLightstyleSamples( dface_t* f, facelight_t *fl, int lightstyle, int numnormals )
-{
- // Search the lightstyles associated with the face for a match
- int k;
- for (k = 0; k < MAXLIGHTMAPS; k++)
- {
- if (f->styles[k] == lightstyle)
- break;
-
- // Found an empty entry, we can use it for a new lightstyle
- if (f->styles[k] == 255)
- {
- AllocateLightstyleSamples( fl, k, numnormals );
- f->styles[k] = lightstyle;
- break;
- }
- }
-
- // Check for overflow
- if (k >= MAXLIGHTMAPS)
- return -1;
-
- return k;
-}
-
-
-//-----------------------------------------------------------------------------
-// Compute the illumination point + normal for the sample
-//-----------------------------------------------------------------------------
-static void ComputeIlluminationPointAndNormalsSSE( lightinfo_t const& l, FourVectors const &pos, FourVectors const &norm, SSE_SampleInfo_t* pInfo, int numSamples )
-{
-
- Vector v[4];
-
- pInfo->m_Points = pos;
- bool computeNormals = ( pInfo->m_NormalCount > 1 && ( pInfo->m_IsDispFace || !l.isflat ) );
-
- // FIXME: move sample point off the surface a bit, this is done so that
- // light sampling will not be affected by a bug where raycasts will
- // intersect with the face being lit. We really should just have that
- // logic in GatherSampleLight
- FourVectors faceNormal;
- faceNormal.DuplicateVector( l.facenormal );
- pInfo->m_Points += faceNormal;
-
- if ( pInfo->m_IsDispFace )
- {
- pInfo->m_PointNormals[0] = norm;
- }
- else if ( !l.isflat )
- {
- // If the face isn't flat, use a phong-based normal instead
- FourVectors modelorg;
- modelorg.DuplicateVector( l.modelorg );
- FourVectors vecSample = pos;
- vecSample -= modelorg;
- GetPhongNormal( pInfo->m_FaceNum, vecSample, pInfo->m_PointNormals[0] );
- }
-
- if ( computeNormals )
- {
- Vector bv[4][NUM_BUMP_VECTS];
- for ( int i = 0; i < 4; ++i )
- {
- // TODO: using Vec may slow things down a bit
- GetBumpNormals( pInfo->m_pTexInfo->textureVecsTexelsPerWorldUnits[0],
- pInfo->m_pTexInfo->textureVecsTexelsPerWorldUnits[1],
- l.facenormal, pInfo->m_PointNormals[0].Vec( i ), bv[i] );
- }
- for ( int b = 0; b < NUM_BUMP_VECTS; ++b )
- {
- pInfo->m_PointNormals[b+1].LoadAndSwizzle ( bv[0][b], bv[1][b], bv[2][b], bv[3][b] );
- }
- }
-
- // TODO: this may slow things down a bit ( using Vec )
- for ( int i = 0; i < 4; ++i )
- pInfo->m_Clusters[i] = ClusterFromPoint( pos.Vec( i ) );
-}
-
-//-----------------------------------------------------------------------------
-// Iterates over all lights and computes lighting at up to 4 sample points
-//-----------------------------------------------------------------------------
-static void GatherSampleLightAt4Points( SSE_SampleInfo_t& info, int sampleIdx, int numSamples )
-{
- SSE_sampleLightOutput_t out;
-
- // Iterate over all direct lights and add them to the particular sample
- for (directlight_t *dl = activelights; dl != NULL; dl = dl->next)
- {
- // is this lights cluster visible?
- fltx4 dotMask = Four_Zeros;
- bool skipLight = true;
- for( int s = 0; s < numSamples; s++ )
- {
- if( PVSCheck( dl->pvs, info.m_Clusters[s] ) )
- {
- dotMask = SetComponentSIMD( dotMask, s, 1.0f );
- skipLight = false;
- }
- }
- if ( skipLight )
- continue;
-
- GatherSampleLightSSE( out, dl, info.m_FaceNum, info.m_Points, info.m_PointNormals, info.m_NormalCount, info.m_iThread );
-
- // Apply the PVS check filter and compute falloff x dot
- fltx4 fxdot[NUM_BUMP_VECTS + 1];
- skipLight = true;
- for ( int b = 0; b < info.m_NormalCount; b++ )
- {
- fxdot[b] = MulSIMD( out.m_flDot[b], dotMask );
- fxdot[b] = MulSIMD( fxdot[b], out.m_flFalloff );
- if ( !IsAllZeros( fxdot[b] ) )
- {
- skipLight = false;
- }
- }
- if ( skipLight )
- continue;
-
- // Figure out the lightstyle for this particular sample
- int lightStyleIndex = FindOrAllocateLightstyleSamples( info.m_pFace, info.m_pFaceLight,
- dl->light.style, info.m_NormalCount );
- if (lightStyleIndex < 0)
- {
- if (info.m_WarnFace != info.m_FaceNum)
- {
- Warning ("\nWARNING: Too many light styles on a face at (%f, %f, %f)\n",
- info.m_Points.x.m128_f32[0], info.m_Points.y.m128_f32[0], info.m_Points.z.m128_f32[0] );
- info.m_WarnFace = info.m_FaceNum;
- }
- continue;
- }
-
- // pLightmaps is an array of the lightmaps for each normal direction,
- // here's where the result of the sample gathering goes
- LightingValue_t** pLightmaps = info.m_pFaceLight->light[lightStyleIndex];
-
- // Incremental lighting only cares about lightstyle zero
- if( g_pIncremental && (dl->light.style == 0) )
- {
- for ( int i = 0; i < numSamples; i++ )
- {
- g_pIncremental->AddLightToFace( dl->m_IncrementalID, info.m_FaceNum, sampleIdx + i,
- info.m_LightmapSize, SubFloat( fxdot[0], i ), info.m_iThread );
- }
- }
-
- for( int n = 0; n < info.m_NormalCount; ++n )
- {
- for ( int i = 0; i < numSamples; i++ )
- {
- pLightmaps[n][sampleIdx + i].AddLight( SubFloat( fxdot[n], i ), dl->light.intensity, SubFloat( out.m_flSunAmount, i ) );
- }
- }
- }
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Iterates over all lights and computes lighting at a sample point
-//-----------------------------------------------------------------------------
-static void ResampleLightAt4Points( SSE_SampleInfo_t& info, int lightStyleIndex, int flags, LightingValue_t pLightmap[4][NUM_BUMP_VECTS+1] )
-{
- SSE_sampleLightOutput_t out;
-
- // Clear result
- for ( int i = 0; i < 4; ++i )
- {
- for ( int n = 0; n < info.m_NormalCount; ++n )
- {
- pLightmap[i][n].Zero();
- }
- }
-
- // Iterate over all direct lights and add them to the particular sample
- for (directlight_t *dl = activelights; dl != NULL; dl = dl->next)
- {
- if ((flags & AMBIENT_ONLY) && (dl->light.type != emit_skyambient))
- continue;
-
- if ((flags & NON_AMBIENT_ONLY) && (dl->light.type == emit_skyambient))
- continue;
-
- // Only add contributions that match the lightstyle
- Assert( lightStyleIndex <= MAXLIGHTMAPS );
- Assert( info.m_pFace->styles[lightStyleIndex] != 255 );
- if (dl->light.style != info.m_pFace->styles[lightStyleIndex])
- continue;
-
- // is this lights cluster visible?
- fltx4 dotMask = Four_Zeros;
- bool skipLight = true;
- for( int s = 0; s < 4; s++ )
- {
- if( PVSCheck( dl->pvs, info.m_Clusters[s] ) )
- {
- dotMask = SetComponentSIMD( dotMask, s, 1.0f );
- skipLight = false;
- }
- }
- if ( skipLight )
- continue;
-
- // NOTE: Notice here that if the light is on the back side of the face
- // (tested by checking the dot product of the face normal and the light position)
- // we don't want it to contribute to *any* of the bumped lightmaps. It glows
- // in disturbing ways if we don't do this.
- GatherSampleLightSSE( out, dl, info.m_FaceNum, info.m_Points, info.m_PointNormals, info.m_NormalCount, info.m_iThread );
-
- // Apply the PVS check filter and compute falloff x dot
- fltx4 fxdot[NUM_BUMP_VECTS + 1];
- for ( int b = 0; b < info.m_NormalCount; b++ )
- {
- fxdot[b] = MulSIMD( out.m_flFalloff, out.m_flDot[b] );
- fxdot[b] = MulSIMD( fxdot[b], dotMask );
- }
-
- // Compute the contributions to each of the bumped lightmaps
- // The first sample is for non-bumped lighting.
- // The other sample are for bumpmapping.
- for( int i = 0; i < 4; ++i )
- {
- for( int n = 0; n < info.m_NormalCount; ++n )
- {
- pLightmap[i][n].AddLight( SubFloat( fxdot[n], i ), dl->light.intensity, SubFloat( out.m_flSunAmount, i ) );
- }
- }
- }
-}
-
-bool PointsInWinding ( FourVectors const & point, winding_t *w, int &invalidBits )
-{
- FourVectors edge, toPt, cross, testCross, p0, p1;
- fltx4 invalidMask;
-
- //
- // get the first normal to test
- //
- p0.DuplicateVector( w->p[0] );
- p1.DuplicateVector( w->p[1] );
- toPt = point;
- toPt -= p0;
- edge = p1;
- edge -= p0;
- testCross = edge ^ toPt;
- testCross.VectorNormalizeFast();
-
- for( int ndxPt = 1; ndxPt < w->numpoints; ndxPt++ )
- {
- p0.DuplicateVector( w->p[ndxPt] );
- p1.DuplicateVector( w->p[(ndxPt+1)%w->numpoints] );
- toPt = point;
- toPt -= p0;
- edge = p1;
- edge -= p0;
- cross = edge ^ toPt;
- cross.VectorNormalizeFast();
-
- fltx4 dot = cross * testCross;
- invalidMask = OrSIMD( invalidMask, CmpLtSIMD( dot, Four_Zeros ) );
-
- invalidBits = TestSignSIMD ( invalidMask );
- if ( invalidBits == 0xF )
- return false;
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Perform supersampling at a particular point
-//-----------------------------------------------------------------------------
-static int SupersampleLightAtPoint( lightinfo_t& l, SSE_SampleInfo_t& info,
- int sampleIndex, int lightStyleIndex, LightingValue_t *pLight, int flags )
-{
- sample_t& sample = info.m_pFaceLight->sample[sampleIndex];
-
- // Get the position of the original sample in lightmapspace
- Vector2D temp;
- WorldToLuxelSpace( &l, sample.pos, temp );
- Vector sampleLightOrigin( temp[0], temp[1], 0.0f );
-
- // Some parameters related to supersampling
- float sampleWidth = ( flags & NON_AMBIENT_ONLY ) ? 4 : 2;
- float cscale = 1.0f / sampleWidth;
- float csshift = -((sampleWidth - 1) * cscale) / 2.0;
-
- // Clear out the light values
- for (int i = 0; i < info.m_NormalCount; ++i )
- pLight[i].Zero();
-
- int subsampleCount = 0;
-
- FourVectors superSampleNormal;
- superSampleNormal.DuplicateVector( sample.normal );
-
- FourVectors superSampleLightCoord;
- FourVectors superSamplePosition;
-
- if ( flags & NON_AMBIENT_ONLY )
- {
- float aRow[4];
- for ( int coord = 0; coord < 4; ++coord )
- aRow[coord] = csshift + coord * cscale;
- fltx4 sseRow = LoadUnalignedSIMD( aRow );
-
- for (int s = 0; s < 4; ++s)
- {
- // make sure the coordinate is inside of the sample's winding and when normalizing
- // below use the number of samples used, not just numsamples and some of them
- // will be skipped if they are not inside of the winding
- superSampleLightCoord.DuplicateVector( sampleLightOrigin );
- superSampleLightCoord.x = AddSIMD( superSampleLightCoord.x, ReplicateX4( aRow[s] ) );
- superSampleLightCoord.y = AddSIMD( superSampleLightCoord.y, sseRow );
-
- // Figure out where the supersample exists in the world, and make sure
- // it lies within the sample winding
- LuxelSpaceToWorld( &l, superSampleLightCoord[0], superSampleLightCoord[1], superSamplePosition );
-
- // A winding should exist only if the sample wasn't a uniform luxel, or if g_bDumpPatches is true.
- int invalidBits = 0;
- if ( sample.w && !PointsInWinding( superSamplePosition, sample.w, invalidBits ) )
- continue;
-
- // Compute the super-sample illumination point and normal
- // We're assuming the flat normal is the same for all supersamples
- ComputeIlluminationPointAndNormalsSSE( l, superSamplePosition, superSampleNormal, &info, 4 );
-
- // Resample the non-ambient light at this point...
- LightingValue_t result[4][NUM_BUMP_VECTS+1];
- ResampleLightAt4Points( info, lightStyleIndex, NON_AMBIENT_ONLY, result );
-
- // Got more subsamples
- for ( int i = 0; i < 4; i++ )
- {
- if ( !( ( invalidBits >> i ) & 0x1 ) )
- {
- for ( int n = 0; n < info.m_NormalCount; ++n )
- {
- pLight[n].AddLight( result[i][n] );
- }
- ++subsampleCount;
- }
- }
- }
- }
- else
- {
- FourVectors superSampleOffsets;
- superSampleOffsets.LoadAndSwizzle( Vector( csshift, csshift, 0 ), Vector( csshift, csshift + cscale, 0),
- Vector( csshift + cscale, csshift, 0 ), Vector( csshift + cscale, csshift + cscale, 0 ) );
- superSampleLightCoord.DuplicateVector( sampleLightOrigin );
- superSampleLightCoord += superSampleOffsets;
-
- LuxelSpaceToWorld( &l, superSampleLightCoord[0], superSampleLightCoord[1], superSamplePosition );
-
- int invalidBits = 0;
- if ( sample.w && !PointsInWinding( superSamplePosition, sample.w, invalidBits ) )
- return 0;
-
- ComputeIlluminationPointAndNormalsSSE( l, superSamplePosition, superSampleNormal, &info, 4 );
-
- LightingValue_t result[4][NUM_BUMP_VECTS+1];
- ResampleLightAt4Points( info, lightStyleIndex, AMBIENT_ONLY, result );
-
- // Got more subsamples
- for ( int i = 0; i < 4; i++ )
- {
- if ( !( ( invalidBits >> i ) & 0x1 ) )
- {
- for ( int n = 0; n < info.m_NormalCount; ++n )
- {
- pLight[n].AddLight( result[i][n] );
- }
- ++subsampleCount;
- }
- }
- }
-
- return subsampleCount;
-}
-
-
-//-----------------------------------------------------------------------------
-// Compute gradients of a lightmap
-//-----------------------------------------------------------------------------
-static void ComputeLightmapGradients( SSE_SampleInfo_t& info, bool const* pHasProcessedSample,
- float* pIntensity, float* gradient )
-{
- int w = info.m_LightmapWidth;
- int h = info.m_LightmapHeight;
- facelight_t* fl = info.m_pFaceLight;
-
- for (int i=0 ; i<fl->numsamples ; i++)
- {
- // Don't supersample the same sample twice
- if (pHasProcessedSample[i])
- continue;
-
- gradient[i] = 0.0f;
- sample_t& sample = fl->sample[i];
-
- // Choose the maximum gradient of all bumped lightmap intensities
- for ( int n = 0; n < info.m_NormalCount; ++n )
- {
- int j = n * info.m_LightmapSize + sample.s + sample.t * w;
-
- if (sample.t > 0)
- {
- if (sample.s > 0) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-1-w] ) );
- gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-w] ) );
- if (sample.s < w-1) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+1-w] ) );
- }
- if (sample.t < h-1)
- {
- if (sample.s > 0) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-1+w] ) );
- gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+w] ) );
- if (sample.s < w-1) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+1+w] ) );
- }
- if (sample.s > 0) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-1] ) );
- if (sample.s < w-1) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+1] ) );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// ComputeLuxelIntensity...
-//-----------------------------------------------------------------------------
-static inline void ComputeLuxelIntensity( SSE_SampleInfo_t& info, int sampleIdx,
- LightingValue_t **ppLightSamples, float* pSampleIntensity )
-{
- // Compute a separate intensity for each
- sample_t& sample = info.m_pFaceLight->sample[sampleIdx];
- int destIdx = sample.s + sample.t * info.m_LightmapWidth;
- for (int n = 0; n < info.m_NormalCount; ++n)
- {
- float intensity = ppLightSamples[n][sampleIdx].Intensity();
-
- // convert to a linear perception space
- pSampleIntensity[n * info.m_LightmapSize + destIdx] = pow( intensity / 256.0, 1.0 / 2.2 );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Compute the maximum intensity based on all bumped lighting
-//-----------------------------------------------------------------------------
-static void ComputeSampleIntensities( SSE_SampleInfo_t& info, LightingValue_t **ppLightSamples, float* pSampleIntensity )
-{
- for (int i=0; i<info.m_pFaceLight->numsamples; i++)
- {
- ComputeLuxelIntensity( info, i, ppLightSamples, pSampleIntensity );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Perform supersampling on a particular lightstyle
-//-----------------------------------------------------------------------------
-static void BuildSupersampleFaceLights( lightinfo_t& l, SSE_SampleInfo_t& info, int lightstyleIndex )
-{
- LightingValue_t pAmbientLight[NUM_BUMP_VECTS+1];
- LightingValue_t pDirectLight[NUM_BUMP_VECTS+1];
-
- // This is used to make sure we don't supersample a light sample more than once
- int processedSampleSize = info.m_LightmapSize * sizeof(bool);
- bool* pHasProcessedSample = (bool*)stackalloc( processedSampleSize );
- memset( pHasProcessedSample, 0, processedSampleSize );
-
- // This is used to compute a simple gradient computation of the light samples
- // We're going to store the maximum intensity of all bumped samples at each sample location
- float* pGradient = (float*)stackalloc( info.m_pFaceLight->numsamples * sizeof(float) );
- float* pSampleIntensity = (float*)stackalloc( info.m_NormalCount * info.m_LightmapSize * sizeof(float) );
-
- // Compute the maximum intensity of all lighting associated with this lightstyle
- // for all bumped lighting
- LightingValue_t **ppLightSamples = info.m_pFaceLight->light[lightstyleIndex];
- ComputeSampleIntensities( info, ppLightSamples, pSampleIntensity );
-
- Vector *pVisualizePass = NULL;
- if (debug_extra)
- {
- int visualizationSize = info.m_pFaceLight->numsamples * sizeof(Vector);
- pVisualizePass = (Vector*)stackalloc( visualizationSize );
- memset( pVisualizePass, 0, visualizationSize );
- }
-
- // What's going on here is that we're looking for large lighting discontinuities
- // (large light intensity gradients) as a clue that we should probably be supersampling
- // in that area. Because the supersampling operation will cause lighting changes,
- // we've found that it's good to re-check the gradients again and see if any other
- // areas should be supersampled as a result of the previous pass. Keep going
- // until all the gradients are reasonable or until we hit a max number of passes
- bool do_anotherpass = true;
- int pass = 1;
- while (do_anotherpass && pass <= extrapasses)
- {
- // Look for lighting discontinuities to see what we should be supersampling
- ComputeLightmapGradients( info, pHasProcessedSample, pSampleIntensity, pGradient );
-
- do_anotherpass = false;
-
- // Now check all of the samples and supersample those which we have
- // marked as having high gradients
- for (int i=0 ; i<info.m_pFaceLight->numsamples; ++i)
- {
- // Don't supersample the same sample twice
- if (pHasProcessedSample[i])
- continue;
-
- // Don't supersample if the lighting is pretty uniform near the sample
- if (pGradient[i] < 0.0625)
- continue;
-
- // Joy! We're supersampling now, and we therefore must do another pass
- // Also, we need never bother with this sample again
- pHasProcessedSample[i] = true;
- do_anotherpass = true;
-
- if (debug_extra)
- {
- // Mark the little visualization bitmap with a color indicating
- // which pass it was updated on.
- pVisualizePass[i][0] = (pass & 1) * 255;
- pVisualizePass[i][1] = (pass & 2) * 128;
- pVisualizePass[i][2] = (pass & 4) * 64;
- }
-
- // Supersample the ambient light for each bump direction vector
- int ambientSupersampleCount = SupersampleLightAtPoint( l, info, i, lightstyleIndex, pAmbientLight, AMBIENT_ONLY );
-
- // Supersample the non-ambient light for each bump direction vector
- int directSupersampleCount = SupersampleLightAtPoint( l, info, i, lightstyleIndex, pDirectLight, NON_AMBIENT_ONLY );
-
- // Because of sampling problems, small area triangles may have no samples.
- // In this case, just use what we already have
- if ( ambientSupersampleCount > 0 && directSupersampleCount > 0 )
- {
- // Add the ambient + directional terms together, stick it back into the lightmap
- for (int n = 0; n < info.m_NormalCount; ++n)
- {
- ppLightSamples[n][i].Zero();
- ppLightSamples[n][i].AddWeighted( pDirectLight[n],1.0f / directSupersampleCount );
- ppLightSamples[n][i].AddWeighted( pAmbientLight[n], 1.0f / ambientSupersampleCount );
- }
-
- // Recompute the luxel intensity based on the supersampling
- ComputeLuxelIntensity( info, i, ppLightSamples, pSampleIntensity );
- }
-
- }
-
- // We've finished another pass
- pass++;
- }
-
- if (debug_extra)
- {
- // Copy colors representing which supersample pass the sample was messed with
- // into the actual lighting values so we can visualize it
- for (int i=0 ; i<info.m_pFaceLight->numsamples ; ++i)
- {
- for (int j = 0; j <info.m_NormalCount; ++j)
- {
- VectorCopy( pVisualizePass[i], ppLightSamples[j][i].m_vecLighting );
- }
- }
- }
-}
-
-void InitLightinfo( lightinfo_t *pl, int facenum )
-{
- dface_t *f;
-
- f = &g_pFaces[facenum];
-
- memset (pl, 0, sizeof(*pl));
- pl->facenum = facenum;
-
- pl->face = f;
-
- //
- // rotate plane
- //
- VectorCopy (dplanes[f->planenum].normal, pl->facenormal);
- pl->facedist = dplanes[f->planenum].dist;
-
- // get the origin offset for rotating bmodels
- VectorCopy (face_offset[facenum], pl->modelorg);
-
- CalcFaceVectors( pl );
-
- // figure out if the surface is flat
- pl->isflat = true;
- if (smoothing_threshold != 1)
- {
- faceneighbor_t *fn = &faceneighbor[facenum];
-
- for (int j=0 ; j<f->numedges ; j++)
- {
- float dot = DotProduct( pl->facenormal, fn->normal[j] );
- if (dot < 1.0 - EQUAL_EPSILON)
- {
- pl->isflat = false;
- break;
- }
- }
- }
-}
-
-static void InitSampleInfo( lightinfo_t const& l, int iThread, SSE_SampleInfo_t& info )
-{
- info.m_LightmapWidth = l.face->m_LightmapTextureSizeInLuxels[0]+1;
- info.m_LightmapHeight = l.face->m_LightmapTextureSizeInLuxels[1]+1;
- info.m_LightmapSize = info.m_LightmapWidth * info.m_LightmapHeight;
-
- // How many lightmaps are we going to need?
- info.m_pTexInfo = &texinfo[l.face->texinfo];
- info.m_NormalCount = (info.m_pTexInfo->flags & SURF_BUMPLIGHT) ? NUM_BUMP_VECTS + 1 : 1;
- info.m_FaceNum = l.facenum;
- info.m_pFace = l.face;
- info.m_pFaceLight = &facelight[info.m_FaceNum];
- info.m_IsDispFace = ValidDispFace( info.m_pFace );
- info.m_iThread = iThread;
- info.m_WarnFace = -1;
-
- info.m_NumSamples = info.m_pFaceLight->numsamples;
- info.m_NumSampleGroups = ( info.m_NumSamples & 0x3) ? ( info.m_NumSamples / 4 ) + 1 : ( info.m_NumSamples / 4 );
-
- // initialize normals if the surface is flat
- if (l.isflat)
- {
- info.m_PointNormals[0].DuplicateVector( l.facenormal );
-
- // use facenormal along with the smooth normal to build the three bump map vectors
- if( info.m_NormalCount > 1 )
- {
- Vector bumpVects[NUM_BUMP_VECTS];
- GetBumpNormals( info.m_pTexInfo->textureVecsTexelsPerWorldUnits[0],
- info.m_pTexInfo->textureVecsTexelsPerWorldUnits[1], l.facenormal,
- l.facenormal, bumpVects );//&info.m_PointNormal[1] );
-
- for ( int b = 0; b < NUM_BUMP_VECTS; ++b )
- {
- info.m_PointNormals[b + 1].DuplicateVector( bumpVects[b] );
- }
- }
- }
-}
-
-void BuildFacelights (int iThread, int facenum)
-{
- int i, j;
-
- lightinfo_t l;
- dface_t *f;
- facelight_t *fl;
- SSE_SampleInfo_t sampleInfo;
- directlight_t *dl;
- Vector spot;
- Vector v[4], n[4];
-
- if( g_bInterrupt )
- return;
-
- // FIXME: Is there a better way to do this? Like, in RunThreadsOn, for instance?
- // Don't pay this cost unless we have to; this is super perf-critical code.
- if (g_pIncremental)
- {
- // Both threads will be accessing this so it needs to be protected or else thread A
- // will load it in and thread B will increment it but its increment will be
- // overwritten by thread A when thread A writes it back.
- ThreadLock();
- ++g_iCurFace;
- ThreadUnlock();
- }
-
- // some surfaces don't need lightmaps
- f = &g_pFaces[facenum];
- f->lightofs = -1;
- for (j=0 ; j<MAXLIGHTMAPS ; j++)
- f->styles[j] = 255;
-
- // Trivial-reject the whole face?
- if( !( g_FacesVisibleToLights[facenum>>3] & (1 << (facenum & 7)) ) )
- return;
-
- if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
- return; // non-lit texture
-
- // check for patches for this face. If none it must be degenerate. Ignore.
- if( g_FacePatches.Element( facenum ) == g_FacePatches.InvalidIndex() )
- return;
-
- fl = &facelight[facenum];
-
- InitLightinfo( &l, facenum );
- CalcPoints( &l, fl, facenum );
- InitSampleInfo( l, iThread, sampleInfo );
-
- // Allocate sample positions/normals to SSE
- int numGroups = ( fl->numsamples & 0x3) ? ( fl->numsamples / 4 ) + 1 : ( fl->numsamples / 4 );
-
- // always allocate style 0 lightmap
- f->styles[0] = 0;
- AllocateLightstyleSamples( fl, 0, sampleInfo.m_NormalCount );
-
- // sample the lights at each sample location
- for ( int grp = 0; grp < numGroups; ++grp )
- {
- int nSample = 4 * grp;
-
- sample_t *sample = sampleInfo.m_pFaceLight->sample + nSample;
- int numSamples = min ( 4, sampleInfo.m_pFaceLight->numsamples - nSample );
-
- FourVectors positions;
- FourVectors normals;
-
- for ( int i = 0; i < 4; i++ )
- {
- v[i] = ( i < numSamples ) ? sample[i].pos : sample[numSamples - 1].pos;
- n[i] = ( i < numSamples ) ? sample[i].normal : sample[numSamples - 1].normal;
- }
- positions.LoadAndSwizzle( v[0], v[1], v[2], v[3] );
- normals.LoadAndSwizzle( n[0], n[1], n[2], n[3] );
-
- ComputeIlluminationPointAndNormalsSSE( l, positions, normals, &sampleInfo, numSamples );
-
- // Fixup sample normals in case of smooth faces
- if ( !l.isflat )
- {
- for ( int i = 0; i < numSamples; i++ )
- sample[i].normal = sampleInfo.m_PointNormals[0].Vec( i );
- }
-
- // Iterate over all the lights and add their contribution to this group of spots
- GatherSampleLightAt4Points( sampleInfo, nSample, numSamples );
- }
-
- // Tell the incremental light manager that we're done with this face.
- if( g_pIncremental )
- {
- for (dl = activelights; dl != NULL; dl = dl->next)
- {
- // Only deal with lightstyle 0 for incremental lighting
- if (dl->light.style == 0)
- g_pIncremental->FinishFace( dl->m_IncrementalID, facenum, iThread );
- }
-
- // Don't have to deal with patch lights (only direct lighting is used)
- // or supersampling
- return;
- }
-
- // get rid of the -extra functionality on displacement surfaces
- if (do_extra && !sampleInfo.m_IsDispFace)
- {
- // For each lightstyle, perform a supersampling pass
- for ( i = 0; i < MAXLIGHTMAPS; ++i )
- {
- // Stop when we run out of lightstyles
- if (f->styles[i] == 255)
- break;
-
- BuildSupersampleFaceLights( l, sampleInfo, i );
- }
- }
-
- if (!g_bUseMPI)
- {
- //
- // This is done on the master node when MPI is used
- //
- BuildPatchLights( facenum );
- }
-
- if( g_bDumpPatches )
- {
- DumpSamples( facenum, fl );
- }
- else
- {
- FreeSampleWindings( fl );
- }
-
-}
-
-void BuildPatchLights( int facenum )
-{
- int i, k;
-
- CPatch *patch;
-
- dface_t *f = &g_pFaces[facenum];
- facelight_t *fl = &facelight[facenum];
-
- for( k = 0; k < MAXLIGHTMAPS; k++ )
- {
- if (f->styles[k] == 0)
- break;
- }
-
- if (k >= MAXLIGHTMAPS)
- return;
-
- for (i = 0; i < fl->numsamples; i++)
- {
- AddSampleToPatch( &fl->sample[i], fl->light[k][0][i], facenum);
- }
-
- // check for a valid face
- if( g_FacePatches.Element( facenum ) == g_FacePatches.InvalidIndex() )
- return;
-
- // push up sampled light to parents (children always exist first in the list)
- CPatch *pNextPatch;
- for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
- {
- // next patch
- pNextPatch = NULL;
- if( patch->ndxNext != g_Patches.InvalidIndex() )
- {
- pNextPatch = &g_Patches.Element( patch->ndxNext );
- }
-
- // skip patches without parents
- if( patch->parent == g_Patches.InvalidIndex() )
-// if (patch->parent == -1)
- continue;
-
- CPatch *parent = &g_Patches.Element( patch->parent );
-
- parent->samplearea += patch->samplearea;
- VectorAdd( parent->samplelight, patch->samplelight, parent->samplelight );
- }
-
- // average up the direct light on each patch for radiosity
- if (numbounce > 0)
- {
- for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
- {
- // next patch
- pNextPatch = NULL;
- if( patch->ndxNext != g_Patches.InvalidIndex() )
- {
- pNextPatch = &g_Patches.Element( patch->ndxNext );
- }
-
- if (patch->samplearea)
- {
- float scale;
- Vector v;
- scale = 1.0 / patch->samplearea;
-
- VectorScale( patch->samplelight, scale, v );
- VectorAdd( patch->totallight.light[0], v, patch->totallight.light[0] );
- VectorAdd( patch->directlight, v, patch->directlight );
- }
- }
- }
-
- // pull totallight from children (children always exist first in the list)
- for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
- {
- // next patch
- pNextPatch = NULL;
- if( patch->ndxNext != g_Patches.InvalidIndex() )
- {
- pNextPatch = &g_Patches.Element( patch->ndxNext );
- }
-
- if ( patch->child1 != g_Patches.InvalidIndex() )
- {
- float s1, s2;
- CPatch *child1;
- CPatch *child2;
-
- child1 = &g_Patches.Element( patch->child1 );
- child2 = &g_Patches.Element( patch->child2 );
-
- s1 = child1->area / (child1->area + child2->area);
- s2 = child2->area / (child1->area + child2->area);
-
- VectorScale( child1->totallight.light[0], s1, patch->totallight.light[0] );
- VectorMA( patch->totallight.light[0], s2, child2->totallight.light[0], patch->totallight.light[0] );
-
- VectorCopy( patch->totallight.light[0], patch->directlight );
- }
- }
-
- bool needsBumpmap = false;
- if( texinfo[f->texinfo].flags & SURF_BUMPLIGHT )
- {
- needsBumpmap = true;
- }
-
- // add an ambient term if desired
- if (ambient[0] || ambient[1] || ambient[2])
- {
- for( int j=0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++ )
- {
- if ( f->styles[j] == 0 )
- {
- for (i = 0; i < fl->numsamples; i++)
- {
- fl->light[j][0][i].m_vecLighting += ambient;
- if( needsBumpmap )
- {
- fl->light[j][1][i].m_vecLighting += ambient;
- fl->light[j][2][i].m_vecLighting += ambient;
- fl->light[j][3][i].m_vecLighting += ambient;
- }
- }
- break;
- }
- }
- }
-
- // light from dlight_threshold and above is sent out, but the
- // texture itself should still be full bright
-
-#if 0
- // if( VectorAvg( g_FacePatches[facenum]->baselight ) >= dlight_threshold) // Now all lighted surfaces glow
- {
- for( j=0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++ )
- {
- if ( f->styles[j] == 0 )
- {
- // BUG: shouldn't this be done for all patches on the face?
- for (i=0 ; i<fl->numsamples ; i++)
- {
- // garymctchange
- VectorAdd( fl->light[j][0][i], g_FacePatches[facenum]->baselight, fl->light[j][0][i] );
- if( needsBumpmap )
- {
- for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
- {
- VectorAdd( fl->light[j][bumpSample][i], g_FacePatches[facenum]->baselight, fl->light[j][bumpSample][i] );
- }
- }
- }
- break;
- }
- }
- }
-#endif
-}
-
-
-/*
- =============
- PrecompLightmapOffsets
- =============
-*/
-
-void PrecompLightmapOffsets()
-{
- int facenum;
- dface_t *f;
- int lightstyles;
- int lightdatasize = 0;
-
- // NOTE: We store avg face light data in this lump *before* the lightmap data itself
- // in *reverse order* of the way the lightstyles appear in the styles array.
- for( facenum = 0; facenum < numfaces; facenum++ )
- {
- f = &g_pFaces[facenum];
-
- if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
- continue; // non-lit texture
-
- if ( dlight_map != 0 )
- f->styles[1] = 0;
-
- for (lightstyles=0; lightstyles < MAXLIGHTMAPS; lightstyles++ )
- {
- if ( f->styles[lightstyles] == 255 )
- break;
- }
-
- if ( !lightstyles )
- continue;
-
- // Reserve room for the avg light color data
- lightdatasize += lightstyles * 4;
-
- f->lightofs = lightdatasize;
-
- bool needsBumpmap = false;
- if( texinfo[f->texinfo].flags & SURF_BUMPLIGHT )
- {
- needsBumpmap = true;
- }
-
- int nLuxels = (f->m_LightmapTextureSizeInLuxels[0]+1) * (f->m_LightmapTextureSizeInLuxels[1]+1);
- if( needsBumpmap )
- {
- lightdatasize += nLuxels * 4 * lightstyles * ( NUM_BUMP_VECTS + 1 );
- }
- else
- {
- lightdatasize += nLuxels * 4 * lightstyles;
- }
- }
-
- // The incremental lighting code needs us to preserve the contents of dlightdata
- // since it only recomposites lighting for faces that have lights that touch them.
- if( g_pIncremental && pdlightdata->Count() )
- return;
-
- pdlightdata->SetSize( lightdatasize );
-}
-
-// Clamp the three values for bumped lighting such that we trade off directionality for brightness.
-static void ColorClampBumped( Vector& color1, Vector& color2, Vector& color3 )
-{
- Vector maxs;
- Vector *colors[3] = { &color1, &color2, &color3 };
- maxs[0] = VectorMaximum( color1 );
- maxs[1] = VectorMaximum( color2 );
- maxs[2] = VectorMaximum( color3 );
-
- // HACK! Clean this up, and add some else statements
-#define CONDITION(a,b,c) do { if( maxs[a] >= maxs[b] && maxs[b] >= maxs[c] ) { order[0] = a; order[1] = b; order[2] = c; } } while( 0 )
-
- int order[3];
- CONDITION(0,1,2);
- CONDITION(0,2,1);
- CONDITION(1,0,2);
- CONDITION(1,2,0);
- CONDITION(2,0,1);
- CONDITION(2,1,0);
-
- int i;
- for( i = 0; i < 3; i++ )
- {
- float max = VectorMaximum( *colors[order[i]] );
- if( max <= 1.0f )
- {
- continue;
- }
- // This channel is too bright. . take half of the amount that we are over and
- // add it to the other two channel.
- float factorToRedist = ( max - 1.0f ) / max;
- Vector colorToRedist = factorToRedist * *colors[order[i]];
- *colors[order[i]] -= colorToRedist;
- colorToRedist *= 0.5f;
- *colors[order[(i+1)%3]] += colorToRedist;
- *colors[order[(i+2)%3]] += colorToRedist;
- }
-
- ColorClamp( color1 );
- ColorClamp( color2 );
- ColorClamp( color3 );
-
- if( color1[0] < 0.f ) color1[0] = 0.f;
- if( color1[1] < 0.f ) color1[1] = 0.f;
- if( color1[2] < 0.f ) color1[2] = 0.f;
- if( color2[0] < 0.f ) color2[0] = 0.f;
- if( color2[1] < 0.f ) color2[1] = 0.f;
- if( color2[2] < 0.f ) color2[2] = 0.f;
- if( color3[0] < 0.f ) color3[0] = 0.f;
- if( color3[1] < 0.f ) color3[1] = 0.f;
- if( color3[2] < 0.f ) color3[2] = 0.f;
-}
-
-static void LinearToBumpedLightmap(
- const float *linearColor,
- const float *linearBumpColor1,
- const float *linearBumpColor2,
- const float *linearBumpColor3,
- unsigned char *ret,
- unsigned char *retBump1,
- unsigned char *retBump2,
- unsigned char *retBump3 )
-{
- const Vector &linearBump1 = *( ( const Vector * )linearBumpColor1 );
- const Vector &linearBump2 = *( ( const Vector * )linearBumpColor2 );
- const Vector &linearBump3 = *( ( const Vector * )linearBumpColor3 );
-
- Vector gammaGoal;
- // gammaGoal is premultiplied by 1/overbright, which we want
- gammaGoal[0] = LinearToVertexLight( linearColor[0] );
- gammaGoal[1] = LinearToVertexLight( linearColor[1] );
- gammaGoal[2] = LinearToVertexLight( linearColor[2] );
- Vector bumpAverage = linearBump1;
- bumpAverage += linearBump2;
- bumpAverage += linearBump3;
- bumpAverage *= ( 1.0f / 3.0f );
-
- Vector correctionScale;
- if( *( int * )&bumpAverage[0] != 0 && *( int * )&bumpAverage[1] != 0 && *( int * )&bumpAverage[2] != 0 )
- {
- // fast path when we know that we don't have to worry about divide by zero.
- VectorDivide( gammaGoal, bumpAverage, correctionScale );
-// correctionScale = gammaGoal / bumpSum;
- }
- else
- {
- correctionScale.Init( 0.0f, 0.0f, 0.0f );
- if( bumpAverage[0] != 0.0f )
- {
- correctionScale[0] = gammaGoal[0] / bumpAverage[0];
- }
- if( bumpAverage[1] != 0.0f )
- {
- correctionScale[1] = gammaGoal[1] / bumpAverage[1];
- }
- if( bumpAverage[2] != 0.0f )
- {
- correctionScale[2] = gammaGoal[2] / bumpAverage[2];
- }
- }
- Vector correctedBumpColor1;
- Vector correctedBumpColor2;
- Vector correctedBumpColor3;
- VectorMultiply( linearBump1, correctionScale, correctedBumpColor1 );
- VectorMultiply( linearBump2, correctionScale, correctedBumpColor2 );
- VectorMultiply( linearBump3, correctionScale, correctedBumpColor3 );
-
- Vector check = ( correctedBumpColor1 + correctedBumpColor2 + correctedBumpColor3 ) / 3.0f;
-
- ColorClampBumped( correctedBumpColor1, correctedBumpColor2, correctedBumpColor3 );
-
- ret[0] = RoundFloatToByte( gammaGoal[0] * 255.0f );
- ret[1] = RoundFloatToByte( gammaGoal[1] * 255.0f );
- ret[2] = RoundFloatToByte( gammaGoal[2] * 255.0f );
- retBump1[0] = RoundFloatToByte( correctedBumpColor1[0] * 255.0f );
- retBump1[1] = RoundFloatToByte( correctedBumpColor1[1] * 255.0f );
- retBump1[2] = RoundFloatToByte( correctedBumpColor1[2] * 255.0f );
- retBump2[0] = RoundFloatToByte( correctedBumpColor2[0] * 255.0f );
- retBump2[1] = RoundFloatToByte( correctedBumpColor2[1] * 255.0f );
- retBump2[2] = RoundFloatToByte( correctedBumpColor2[2] * 255.0f );
- retBump3[0] = RoundFloatToByte( correctedBumpColor3[0] * 255.0f );
- retBump3[1] = RoundFloatToByte( correctedBumpColor3[1] * 255.0f );
- retBump3[2] = RoundFloatToByte( correctedBumpColor3[2] * 255.0f );
-}
-
-//-----------------------------------------------------------------------------
-// Convert a RGBExp32 to a RGBA8888
-// This matches the engine's conversion, so the lighting result is consistent.
-//-----------------------------------------------------------------------------
-void ConvertRGBExp32ToRGBA8888( const ColorRGBExp32 *pSrc, unsigned char *pDst )
-{
- Vector linearColor;
- Vector vertexColor;
-
- // convert from ColorRGBExp32 to linear space
- linearColor[0] = TexLightToLinear( ((ColorRGBExp32 *)pSrc)->r, ((ColorRGBExp32 *)pSrc)->exponent );
- linearColor[1] = TexLightToLinear( ((ColorRGBExp32 *)pSrc)->g, ((ColorRGBExp32 *)pSrc)->exponent );
- linearColor[2] = TexLightToLinear( ((ColorRGBExp32 *)pSrc)->b, ((ColorRGBExp32 *)pSrc)->exponent );
-
- // convert from linear space to lightmap space
- // cannot use mathlib routine directly because it doesn't match
- // the colorspace version found in the engine, which *is* the same sequence here
- vertexColor[0] = LinearToVertexLight( linearColor[0] );
- vertexColor[1] = LinearToVertexLight( linearColor[1] );
- vertexColor[2] = LinearToVertexLight( linearColor[2] );
-
- // this is really a color normalization with a floor
- ColorClamp( vertexColor );
-
- // final [0..255] scale
- pDst[0] = RoundFloatToByte( vertexColor[0] * 255.0f );
- pDst[1] = RoundFloatToByte( vertexColor[1] * 255.0f );
- pDst[2] = RoundFloatToByte( vertexColor[2] * 255.0f );
- pDst[3] = 255;
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vrad.h"
+#include "lightmap.h"
+#include "radial.h"
+#include "mathlib/bumpvects.h"
+#include "tier1/utlvector.h"
+#include "vmpi.h"
+#include "mathlib/anorms.h"
+#include "map_utils.h"
+#include "mathlib/halton.h"
+#include "imagepacker.h"
+#include "tier1/utlrbtree.h"
+#include "tier1/utlbuffer.h"
+#include "bitmap/tgawriter.h"
+#include "mathlib/quantize.h"
+#include "bitmap/imageformat.h"
+#include "coordsize.h"
+
+enum
+{
+ AMBIENT_ONLY = 0x1,
+ NON_AMBIENT_ONLY = 0x2,
+};
+
+#define SMOOTHING_GROUP_HARD_EDGE 0xff000000
+
+//==========================================================================//
+// CNormalList.
+//==========================================================================//
+
+// This class keeps a list of unique normals and provides a fast
+class CNormalList
+{
+public:
+ CNormalList();
+
+ // Adds the normal if unique. Otherwise, returns the normal's index into m_Normals.
+ int FindOrAddNormal( Vector const &vNormal );
+
+
+public:
+
+ CUtlVector<Vector> m_Normals;
+
+
+private:
+
+ // This represents a grid from (-1,-1,-1) to (1,1,1).
+ enum {NUM_SUBDIVS = 8};
+ CUtlVector<int> m_NormalGrid[NUM_SUBDIVS][NUM_SUBDIVS][NUM_SUBDIVS];
+};
+
+
+int g_iCurFace;
+edgeshare_t edgeshare[MAX_MAP_EDGES];
+
+Vector face_centroids[MAX_MAP_EDGES];
+
+int vertexref[MAX_MAP_VERTS];
+int *vertexface[MAX_MAP_VERTS];
+faceneighbor_t faceneighbor[MAX_MAP_FACES];
+
+static directlight_t *gSkyLight = NULL;
+static directlight_t *gAmbient = NULL;
+
+//==========================================================================//
+// CNormalList implementation.
+//==========================================================================//
+
+CNormalList::CNormalList() : m_Normals( 128 )
+{
+ for( int i=0; i < sizeof(m_NormalGrid)/sizeof(m_NormalGrid[0][0][0]); i++ )
+ {
+ (&m_NormalGrid[0][0][0] + i)->SetGrowSize( 16 );
+ }
+}
+
+int CNormalList::FindOrAddNormal( Vector const &vNormal )
+{
+ int gi[3];
+
+ // See which grid element it's in.
+ for( int iDim=0; iDim < 3; iDim++ )
+ {
+ gi[iDim] = (int)( ((vNormal[iDim] + 1.0f) * 0.5f) * NUM_SUBDIVS - 0.000001f );
+ gi[iDim] = min( gi[iDim], NUM_SUBDIVS );
+ gi[iDim] = max( gi[iDim], 0 );
+ }
+
+ // Look for a matching vector in there.
+ CUtlVector<int> *pGridElement = &m_NormalGrid[gi[0]][gi[1]][gi[2]];
+ for( int i=0; i < pGridElement->Size(); i++ )
+ {
+ int iNormal = pGridElement->Element(i);
+
+ Vector *pVec = &m_Normals[iNormal];
+ //if( pVec->DistToSqr(vNormal) < 0.00001f )
+ if( *pVec == vNormal )
+ return iNormal;
+ }
+
+ // Ok, add a new one.
+ pGridElement->AddToTail( m_Normals.Size() );
+ return m_Normals.AddToTail( vNormal );
+}
+
+// FIXME: HACK until the plane normals are made more happy
+void GetBumpNormals( const float* sVect, const float* tVect, const Vector& flatNormal,
+ const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] )
+{
+ Vector stmp( sVect[0], sVect[1], sVect[2] );
+ Vector ttmp( tVect[0], tVect[1], tVect[2] );
+ GetBumpNormals( stmp, ttmp, flatNormal, phongNormal, bumpNormals );
+}
+
+int EdgeVertex( dface_t *f, int edge )
+{
+ int k;
+
+ if (edge < 0)
+ edge += f->numedges;
+ else if (edge >= f->numedges)
+ edge = edge % f->numedges;
+
+ k = dsurfedges[f->firstedge + edge];
+ if (k < 0)
+ {
+ // Msg("(%d %d) ", dedges[-k].v[1], dedges[-k].v[0] );
+ return dedges[-k].v[1];
+ }
+ else
+ {
+ // Msg("(%d %d) ", dedges[k].v[0], dedges[k].v[1] );
+ return dedges[k].v[0];
+ }
+}
+
+
+/*
+ ============
+ PairEdges
+ ============
+*/
+void PairEdges (void)
+{
+ int i, j, k, n, m;
+ dface_t *f;
+ int numneighbors;
+ int tmpneighbor[64];
+ faceneighbor_t *fn;
+
+ // count number of faces that reference each vertex
+ for (i=0, f = g_pFaces; i<numfaces ; i++, f++)
+ {
+ for (j=0 ; j<f->numedges ; j++)
+ {
+ // Store the count in vertexref
+ vertexref[EdgeVertex(f,j)]++;
+ }
+ }
+
+ // allocate room
+ for (i = 0; i < numvertexes; i++)
+ {
+ // use the count from above to allocate a big enough array
+ vertexface[i] = ( int* )calloc( vertexref[i], sizeof( vertexface[0] ) );
+ // clear the temporary data
+ vertexref[i] = 0;
+ }
+
+ // store a list of every face that uses a particular vertex
+ for (i=0, f = g_pFaces ; i<numfaces ; i++, f++)
+ {
+ for (j=0 ; j<f->numedges ; j++)
+ {
+ n = EdgeVertex(f,j);
+
+ for (k = 0; k < vertexref[n]; k++)
+ {
+ if (vertexface[n][k] == i)
+ break;
+ }
+ if (k >= vertexref[n])
+ {
+ // add the face to the list
+ vertexface[n][k] = i;
+ vertexref[n]++;
+ }
+ }
+ }
+
+ // calc normals and set displacement surface flag
+ for (i=0, f = g_pFaces; i<numfaces ; i++, f++)
+ {
+ fn = &faceneighbor[i];
+
+ // get face normal
+ VectorCopy( dplanes[f->planenum].normal, fn->facenormal );
+
+ // set displacement surface flag
+ fn->bHasDisp = false;
+ if( ValidDispFace( f ) )
+ {
+ fn->bHasDisp = true;
+ }
+ }
+
+ // find neighbors
+ for (i=0, f = g_pFaces ; i<numfaces ; i++, f++)
+ {
+ numneighbors = 0;
+ fn = &faceneighbor[i];
+
+ // allocate room for vertex normals
+ fn->normal = ( Vector* )calloc( f->numedges, sizeof( fn->normal[0] ) );
+
+ // look up all faces sharing vertices and add them to the list
+ for (j=0 ; j<f->numedges ; j++)
+ {
+ n = EdgeVertex(f,j);
+
+ for (k = 0; k < vertexref[n]; k++)
+ {
+ double cos_normals_angle;
+ Vector *pNeighbornormal;
+
+ // skip self
+ if (vertexface[n][k] == i)
+ continue;
+
+ // if this face doens't have a displacement -- don't consider displacement neighbors
+ if( ( !fn->bHasDisp ) && ( faceneighbor[vertexface[n][k]].bHasDisp ) )
+ continue;
+
+ pNeighbornormal = &faceneighbor[vertexface[n][k]].facenormal;
+ cos_normals_angle = DotProduct( *pNeighbornormal, fn->facenormal );
+
+ // add normal if >= threshold or its a displacement surface (this is only if the original
+ // face is a displacement)
+ if ( fn->bHasDisp )
+ {
+ // Always smooth with and against a displacement surface.
+ VectorAdd( fn->normal[j], *pNeighbornormal, fn->normal[j] );
+ }
+ else
+ {
+ // No smoothing - use of method (backwards compatibility).
+ if ( ( f->smoothingGroups == 0 ) && ( g_pFaces[vertexface[n][k]].smoothingGroups == 0 ) )
+ {
+ if ( cos_normals_angle >= smoothing_threshold )
+ {
+ VectorAdd( fn->normal[j], *pNeighbornormal, fn->normal[j] );
+ }
+ else
+ {
+ // not considered a neighbor
+ continue;
+ }
+ }
+ else
+ {
+ unsigned int smoothingGroup = ( f->smoothingGroups & g_pFaces[vertexface[n][k]].smoothingGroups );
+
+ // Hard edge.
+ if ( ( smoothingGroup & SMOOTHING_GROUP_HARD_EDGE ) != 0 )
+ continue;
+
+ if ( smoothingGroup != 0 )
+ {
+ VectorAdd( fn->normal[j], *pNeighbornormal, fn->normal[j] );
+ }
+ else
+ {
+ // not considered a neighbor
+ continue;
+ }
+ }
+ }
+
+ // look to see if we've already added this one
+ for (m = 0; m < numneighbors; m++)
+ {
+ if (tmpneighbor[m] == vertexface[n][k])
+ break;
+ }
+
+ if (m >= numneighbors)
+ {
+ // add to neighbor list
+ tmpneighbor[m] = vertexface[n][k];
+ numneighbors++;
+ if ( numneighbors > ARRAYSIZE(tmpneighbor) )
+ {
+ Error("Stack overflow in neighbors\n");
+ }
+ }
+ }
+ }
+
+ if (numneighbors)
+ {
+ // copy over neighbor list
+ fn->numneighbors = numneighbors;
+ fn->neighbor = ( int* )calloc( numneighbors, sizeof( fn->neighbor[0] ) );
+ for (m = 0; m < numneighbors; m++)
+ {
+ fn->neighbor[m] = tmpneighbor[m];
+ }
+ }
+
+ // fixup normals
+ for (j = 0; j < f->numedges; j++)
+ {
+ VectorAdd( fn->normal[j], fn->facenormal, fn->normal[j] );
+ VectorNormalize( fn->normal[j] );
+ }
+ }
+}
+
+
+void SaveVertexNormals( void )
+{
+ faceneighbor_t *fn;
+ int i, j;
+ dface_t *f;
+ CNormalList normalList;
+
+ g_numvertnormalindices = 0;
+
+ for( i = 0 ;i<numfaces ; i++ )
+ {
+ fn = &faceneighbor[i];
+ f = &g_pFaces[i];
+
+ for( j = 0; j < f->numedges; j++ )
+ {
+ Vector vNormal;
+ if( fn->normal )
+ {
+ vNormal = fn->normal[j];
+ }
+ else
+ {
+ // original faces don't have normals
+ vNormal.Init( 0, 0, 0 );
+ }
+
+ if( g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES )
+ {
+ Error( "g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES" );
+ }
+
+ g_vertnormalindices[g_numvertnormalindices] = (unsigned short)normalList.FindOrAddNormal( vNormal );
+ g_numvertnormalindices++;
+ }
+ }
+
+ if( normalList.m_Normals.Size() > MAX_MAP_VERTNORMALS )
+ {
+ Error( "g_numvertnormals > MAX_MAP_VERTNORMALS" );
+ }
+
+ // Copy the list of unique vert normals into g_vertnormals.
+ g_numvertnormals = normalList.m_Normals.Size();
+ memcpy( g_vertnormals, normalList.m_Normals.Base(), sizeof(g_vertnormals[0]) * normalList.m_Normals.Size() );
+}
+
+/*
+ =================================================================
+
+ LIGHTMAP SAMPLE GENERATION
+
+ =================================================================
+*/
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Spits out an error message with information about a lightinfo_t.
+// Input : s - Error message string.
+// l - lightmap info struct.
+//-----------------------------------------------------------------------------
+void ErrorLightInfo(const char *s, lightinfo_t *l)
+{
+ texinfo_t *tex = &texinfo[l->face->texinfo];
+ winding_t *w = WindingFromFace(&g_pFaces[l->facenum], l->modelorg);
+
+ //
+ // Show the face center and material name if possible.
+ //
+ if (w != NULL)
+ {
+ // Don't exit, we'll try to recover...
+ Vector vecCenter;
+ WindingCenter(w, vecCenter);
+// FreeWinding(w);
+
+ Warning("%s at (%g, %g, %g)\n\tmaterial=%s\n", s, (double)vecCenter.x, (double)vecCenter.y, (double)vecCenter.z, TexDataStringTable_GetString( dtexdata[tex->texdata].nameStringTableID ) );
+ }
+ //
+ // If not, just show the material name.
+ //
+ else
+ {
+ Warning("%s at (degenerate face)\n\tmaterial=%s\n", s, TexDataStringTable_GetString( dtexdata[tex->texdata].nameStringTableID ));
+ }
+}
+
+
+
+void CalcFaceVectors(lightinfo_t *l)
+{
+ texinfo_t *tex;
+ int i, j;
+
+ tex = &texinfo[l->face->texinfo];
+
+ // move into lightinfo_t
+ for (i=0 ; i<2 ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ l->worldToLuxelSpace[i][j] = tex->lightmapVecsLuxelsPerWorldUnits[i][j];
+ }
+ }
+
+ //Solve[ { x * w00 + y * w01 + z * w02 - s == 0, x * w10 + y * w11 + z * w12 - t == 0, A * x + B * y + C * z + D == 0 }, { x, y, z } ]
+ //Rule(x,( C*s*w11 - B*s*w12 + B*t*w02 - C*t*w01 + D*w02*w11 - D*w01*w12) / (+ A*w01*w12 - A*w02*w11 + B*w02*w10 - B*w00*w12 + C*w00*w11 - C*w01*w10 )),
+ //Rule(y,( A*s*w12 - C*s*w10 + C*t*w00 - A*t*w02 + D*w00*w12 - D*w02*w10) / (+ A*w01*w12 - A*w02*w11 + B*w02*w10 - B*w00*w12 + C*w00*w11 - C*w01*w10 )),
+ //Rule(z,( B*s*w10 - A*s*w11 + A*t*w01 - B*t*w00 + D*w01*w10 - D*w00*w11) / (+ A*w01*w12 - A*w02*w11 + B*w02*w10 - B*w00*w12 + C*w00*w11 - C*w01*w10 ))))
+
+ Vector luxelSpaceCross;
+
+ luxelSpaceCross[0] =
+ tex->lightmapVecsLuxelsPerWorldUnits[1][1] * tex->lightmapVecsLuxelsPerWorldUnits[0][2] -
+ tex->lightmapVecsLuxelsPerWorldUnits[1][2] * tex->lightmapVecsLuxelsPerWorldUnits[0][1];
+ luxelSpaceCross[1] =
+ tex->lightmapVecsLuxelsPerWorldUnits[1][2] * tex->lightmapVecsLuxelsPerWorldUnits[0][0] -
+ tex->lightmapVecsLuxelsPerWorldUnits[1][0] * tex->lightmapVecsLuxelsPerWorldUnits[0][2];
+ luxelSpaceCross[2] =
+ tex->lightmapVecsLuxelsPerWorldUnits[1][0] * tex->lightmapVecsLuxelsPerWorldUnits[0][1] -
+ tex->lightmapVecsLuxelsPerWorldUnits[1][1] * tex->lightmapVecsLuxelsPerWorldUnits[0][0];
+
+ float det = -DotProduct( l->facenormal, luxelSpaceCross );
+ if ( fabs( det ) < 1.0e-20 )
+ {
+ Warning(" warning - face vectors parallel to face normal. bad lighting will be produced\n" );
+ l->luxelOrigin = vec3_origin;
+ }
+ else
+ {
+ // invert the matrix
+ l->luxelToWorldSpace[0][0] = (l->facenormal[2] * l->worldToLuxelSpace[1][1] - l->facenormal[1] * l->worldToLuxelSpace[1][2]) / det;
+ l->luxelToWorldSpace[1][0] = (l->facenormal[1] * l->worldToLuxelSpace[0][2] - l->facenormal[2] * l->worldToLuxelSpace[0][1]) / det;
+ l->luxelOrigin[0] = -(l->facedist * luxelSpaceCross[0]) / det;
+ l->luxelToWorldSpace[0][1] = (l->facenormal[0] * l->worldToLuxelSpace[1][2] - l->facenormal[2] * l->worldToLuxelSpace[1][0]) / det;
+ l->luxelToWorldSpace[1][1] = (l->facenormal[2] * l->worldToLuxelSpace[0][0] - l->facenormal[0] * l->worldToLuxelSpace[0][2]) / det;
+ l->luxelOrigin[1] = -(l->facedist * luxelSpaceCross[1]) / det;
+ l->luxelToWorldSpace[0][2] = (l->facenormal[1] * l->worldToLuxelSpace[1][0] - l->facenormal[0] * l->worldToLuxelSpace[1][1]) / det;
+ l->luxelToWorldSpace[1][2] = (l->facenormal[0] * l->worldToLuxelSpace[0][1] - l->facenormal[1] * l->worldToLuxelSpace[0][0]) / det;
+ l->luxelOrigin[2] = -(l->facedist * luxelSpaceCross[2]) / det;
+
+ // adjust for luxel offset
+ VectorMA( l->luxelOrigin, -tex->lightmapVecsLuxelsPerWorldUnits[0][3], l->luxelToWorldSpace[0], l->luxelOrigin );
+ VectorMA( l->luxelOrigin, -tex->lightmapVecsLuxelsPerWorldUnits[1][3], l->luxelToWorldSpace[1], l->luxelOrigin );
+ }
+ // compensate for org'd bmodels
+ VectorAdd (l->luxelOrigin, l->modelorg, l->luxelOrigin);
+}
+
+
+
+winding_t *LightmapCoordWindingForFace( lightinfo_t *l )
+{
+ int i;
+ winding_t *w;
+
+ w = WindingFromFace( l->face, l->modelorg );
+
+ for (i = 0; i < w->numpoints; i++)
+ {
+ Vector2D coord;
+ WorldToLuxelSpace( l, w->p[i], coord );
+ w->p[i].x = coord.x;
+ w->p[i].y = coord.y;
+ w->p[i].z = 0;
+ }
+
+ return w;
+}
+
+
+void WriteCoordWinding (FILE *out, lightinfo_t *l, winding_t *w, Vector& color )
+{
+ int i;
+ Vector pos;
+
+ fprintf (out, "%i\n", w->numpoints);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ LuxelSpaceToWorld( l, w->p[i][0], w->p[i][1], pos );
+ fprintf (out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+ pos[0],
+ pos[1],
+ pos[2],
+ color[ 0 ] / 256,
+ color[ 1 ] / 256,
+ color[ 2 ] / 256 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void DumpFaces( lightinfo_t *pLightInfo, int ndxFace )
+{
+ static FileHandle_t out;
+
+ // get face data
+ faceneighbor_t *fn = &faceneighbor[ndxFace];
+ Vector &centroid = face_centroids[ndxFace];
+
+ // disable threading (not a multi-threadable function!)
+ ThreadLock();
+
+ if( !out )
+ {
+ // open the file
+ out = g_pFileSystem->Open( "face.txt", "w" );
+ if( !out )
+ return;
+ }
+
+ //
+ // write out face
+ //
+ for( int ndxEdge = 0; ndxEdge < pLightInfo->face->numedges; ndxEdge++ )
+ {
+// int edge = dsurfedges[pLightInfo->face->firstedge+ndxEdge];
+
+ Vector p1, p2;
+ VectorAdd( dvertexes[EdgeVertex( pLightInfo->face, ndxEdge )].point, pLightInfo->modelorg, p1 );
+ VectorAdd( dvertexes[EdgeVertex( pLightInfo->face, ndxEdge+1 )].point, pLightInfo->modelorg, p2 );
+
+ Vector &n1 = fn->normal[ndxEdge];
+ Vector &n2 = fn->normal[(ndxEdge+1)%pLightInfo->face->numedges];
+
+ CmdLib_FPrintf( out, "3\n");
+
+ CmdLib_FPrintf(out, "%f %f %f %f %f %f\n", p1[0], p1[1], p1[2], n1[0] * 0.5 + 0.5, n1[1] * 0.5 + 0.5, n1[2] * 0.5 + 0.5 );
+
+ CmdLib_FPrintf(out, "%f %f %f %f %f %f\n", p2[0], p2[1], p2[2], n2[0] * 0.5 + 0.5, n2[1] * 0.5 + 0.5, n2[2] * 0.5 + 0.5 );
+
+ CmdLib_FPrintf(out, "%f %f %f %f %f %f\n", centroid[0] + pLightInfo->modelorg[0],
+ centroid[1] + pLightInfo->modelorg[1],
+ centroid[2] + pLightInfo->modelorg[2],
+ fn->facenormal[0] * 0.5 + 0.5,
+ fn->facenormal[1] * 0.5 + 0.5,
+ fn->facenormal[2] * 0.5 + 0.5 );
+
+ }
+
+ // enable threading
+ ThreadUnlock();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool BuildFacesamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight )
+{
+ // lightmap size
+ int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
+ int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
+
+ // ratio of world area / lightmap area
+ texinfo_t *pTex = &texinfo[pLightInfo->face->texinfo];
+ pFaceLight->worldAreaPerLuxel = 1.0 / ( sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[0],
+ pTex->lightmapVecsLuxelsPerWorldUnits[0] ) ) *
+ sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[1],
+ pTex->lightmapVecsLuxelsPerWorldUnits[1] ) ) );
+
+ //
+ // quickly create samples and luxels (copy over samples)
+ //
+ pFaceLight->numsamples = width * height;
+ pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
+ if( !pFaceLight->sample )
+ return false;
+
+ pFaceLight->numluxels = width * height;
+ pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
+ if( !pFaceLight->luxel )
+ return false;
+
+ sample_t *pSamples = pFaceLight->sample;
+ Vector *pLuxels = pFaceLight->luxel;
+
+ for( int t = 0; t < height; t++ )
+ {
+ for( int s = 0; s < width; s++ )
+ {
+ pSamples->s = s;
+ pSamples->t = t;
+ pSamples->coord[0] = s;
+ pSamples->coord[1] = t;
+ // unused but initialized anyway
+ pSamples->mins[0] = s - 0.5;
+ pSamples->mins[1] = t - 0.5;
+ pSamples->maxs[0] = s + 0.5;
+ pSamples->maxs[1] = t + 0.5;
+ pSamples->area = pFaceLight->worldAreaPerLuxel;
+ LuxelSpaceToWorld( pLightInfo, pSamples->coord[0], pSamples->coord[1], pSamples->pos );
+ VectorCopy( pSamples->pos, *pLuxels );
+
+ pSamples++;
+ pLuxels++;
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool BuildSamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
+{
+ // build samples for a "face"
+ if( pLightInfo->face->dispinfo == -1 )
+ {
+ return BuildFacesamplesAndLuxels_DoFast( pLightInfo, pFaceLight );
+ }
+ // build samples for a "displacement"
+ else
+ {
+ return StaticDispMgr()->BuildDispSamplesAndLuxels_DoFast( pLightInfo, pFaceLight, ndxFace );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool BuildFacesamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight )
+{
+ // lightmap size
+ int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
+ int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
+
+ // ratio of world area / lightmap area
+ texinfo_t *pTex = &texinfo[pLightInfo->face->texinfo];
+ pFaceLight->worldAreaPerLuxel = 1.0 / ( sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[0],
+ pTex->lightmapVecsLuxelsPerWorldUnits[0] ) ) *
+ sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[1],
+ pTex->lightmapVecsLuxelsPerWorldUnits[1] ) ) );
+
+ // allocate a large number of samples for creation -- get copied later!
+ CUtlVector<sample_t> sampleData;
+ sampleData.SetCount( SINGLE_BRUSH_MAP * 2 );
+ sample_t *samples = sampleData.Base();
+ sample_t *pSamples = samples;
+
+ // lightmap space winding
+ winding_t *pLightmapWinding = LightmapCoordWindingForFace( pLightInfo );
+
+ //
+ // build vector pointing along the lightmap cutting planes
+ //
+ Vector sNorm( 1.0f, 0.0f, 0.0f );
+ Vector tNorm( 0.0f, 1.0f, 0.0f );
+
+ // sample center offset
+ float sampleOffset = ( do_centersamples ) ? 0.5 : 1.0;
+
+ //
+ // clip the lightmap "spaced" winding by the lightmap cutting planes
+ //
+ winding_t *pWindingT1, *pWindingT2;
+ winding_t *pWindingS1, *pWindingS2;
+ float dist;
+
+ for( int t = 0; t < height && pLightmapWinding; t++ )
+ {
+ dist = t + sampleOffset;
+
+ // lop off a sample in the t dimension
+ // hack - need a separate epsilon for lightmap space since ON_EPSILON is for texture space
+ ClipWindingEpsilon( pLightmapWinding, tNorm, dist, ON_EPSILON / 16.0f, &pWindingT1, &pWindingT2 );
+
+ for( int s = 0; s < width && pWindingT2; s++ )
+ {
+ dist = s + sampleOffset;
+
+ // lop off a sample in the s dimension, and put it in ws2
+ // hack - need a separate epsilon for lightmap space since ON_EPSILON is for texture space
+ ClipWindingEpsilon( pWindingT2, sNorm, dist, ON_EPSILON / 16.0f, &pWindingS1, &pWindingS2 );
+
+ //
+ // s2 winding is a single sample worth of winding
+ //
+ if( pWindingS2 )
+ {
+ // save the s, t positions
+ pSamples->s = s;
+ pSamples->t = t;
+
+ // get the lightmap space area of ws2 and convert to world area
+ // and find the center (then convert it to 2D)
+ Vector center;
+ pSamples->area = WindingAreaAndBalancePoint( pWindingS2, center ) * pFaceLight->worldAreaPerLuxel;
+ pSamples->coord[0] = center.x;
+ pSamples->coord[1] = center.y;
+
+ // find winding bounds (then convert it to 2D)
+ Vector minbounds, maxbounds;
+ WindingBounds( pWindingS2, minbounds, maxbounds );
+ pSamples->mins[0] = minbounds.x;
+ pSamples->mins[1] = minbounds.y;
+ pSamples->maxs[0] = maxbounds.x;
+ pSamples->maxs[1] = maxbounds.y;
+
+ // convert from lightmap space to world space
+ LuxelSpaceToWorld( pLightInfo, pSamples->coord[0], pSamples->coord[1], pSamples->pos );
+
+ if (g_bDumpPatches || (do_extra && pSamples->area < pFaceLight->worldAreaPerLuxel - EQUAL_EPSILON))
+ {
+ //
+ // convert the winding from lightmaps space to world for debug rendering and sub-sampling
+ //
+ Vector worldPos;
+ for( int ndxPt = 0; ndxPt < pWindingS2->numpoints; ndxPt++ )
+ {
+ LuxelSpaceToWorld( pLightInfo, pWindingS2->p[ndxPt].x, pWindingS2->p[ndxPt].y, worldPos );
+ VectorCopy( worldPos, pWindingS2->p[ndxPt] );
+ }
+ pSamples->w = pWindingS2;
+ }
+ else
+ {
+ // winding isn't needed, free it.
+ pSamples->w = NULL;
+ FreeWinding( pWindingS2 );
+ }
+
+ pSamples++;
+ }
+
+ //
+ // if winding T2 still exists free it and set it equal S1 (the rest of the row minus the sample just created)
+ //
+ if( pWindingT2 )
+ {
+ FreeWinding( pWindingT2 );
+ }
+
+ // clip the rest of "s"
+ pWindingT2 = pWindingS1;
+ }
+
+ //
+ // if the original lightmap winding exists free it and set it equal to T1 (the rest of the winding not cut into samples)
+ //
+ if( pLightmapWinding )
+ {
+ FreeWinding( pLightmapWinding );
+ }
+
+ if( pWindingT2 )
+ {
+ FreeWinding( pWindingT2 );
+ }
+
+ pLightmapWinding = pWindingT1;
+ }
+
+ //
+ // copy over samples
+ //
+ pFaceLight->numsamples = pSamples - samples;
+ pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
+ if( !pFaceLight->sample )
+ return false;
+
+ memcpy( pFaceLight->sample, samples, pFaceLight->numsamples * sizeof( *pFaceLight->sample ) );
+
+ // supply a default sample normal (face normal - assumed flat)
+ for( int ndxSample = 0; ndxSample < pFaceLight->numsamples; ndxSample++ )
+ {
+ Assert ( VectorLength ( pLightInfo->facenormal ) > 1.0e-20);
+ pFaceLight->sample[ndxSample].normal = pLightInfo->facenormal;
+ }
+
+ // statistics - warning?!
+ if( pFaceLight->numsamples == 0 )
+ {
+ Msg( "no samples %d\n", pLightInfo->face - g_pFaces );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Free any windings used by this facelight. It's currently assumed they're not needed again
+//-----------------------------------------------------------------------------
+void FreeSampleWindings( facelight_t *fl )
+{
+ int i;
+ for (i = 0; i < fl->numsamples; i++)
+ {
+ if (fl->sample[i].w)
+ {
+ FreeWinding( fl->sample[i].w );
+ fl->sample[i].w = NULL;
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: build the sample data for each lightmapped primitive type
+//-----------------------------------------------------------------------------
+bool BuildSamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
+{
+ // build samples for a "face"
+ if( pLightInfo->face->dispinfo == -1 )
+ {
+ return BuildFacesamples( pLightInfo, pFaceLight );
+ }
+ // build samples for a "displacement"
+ else
+ {
+ return StaticDispMgr()->BuildDispSamples( pLightInfo, pFaceLight, ndxFace );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool BuildFaceLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight )
+{
+ // lightmap size
+ int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
+ int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
+
+ // calcuate actual luxel points
+ pFaceLight->numluxels = width * height;
+ pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
+ if( !pFaceLight->luxel )
+ return false;
+
+ for( int t = 0; t < height; t++ )
+ {
+ for( int s = 0; s < width; s++ )
+ {
+ LuxelSpaceToWorld( pLightInfo, s, t, pFaceLight->luxel[s+t*width] );
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: build the luxels (find the luxel centers) for each lightmapped
+// primitive type
+//-----------------------------------------------------------------------------
+bool BuildLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
+{
+ // build luxels for a "face"
+ if( pLightInfo->face->dispinfo == -1 )
+ {
+ return BuildFaceLuxels( pLightInfo, pFaceLight );
+ }
+ // build luxels for a "displacement"
+ else
+ {
+ return StaticDispMgr()->BuildDispLuxels( pLightInfo, pFaceLight, ndxFace );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: for each face, find the center of each luxel; for each texture
+// aligned grid point, back project onto the plane and get the world
+// xyz value of the sample point
+// NOTE: ndxFace = facenum
+//-----------------------------------------------------------------------------
+void CalcPoints( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
+{
+ // debugging!
+ if( g_bDumpPatches )
+ {
+ DumpFaces( pLightInfo, ndxFace );
+ }
+
+ // quick and dirty!
+ if( do_fast )
+ {
+ if( !BuildSamplesAndLuxels_DoFast( pLightInfo, pFaceLight, ndxFace ) )
+ {
+ Msg( "Face %d: (Fast)Error Building Samples and Luxels\n", ndxFace );
+ }
+ return;
+ }
+
+ // build the samples
+ if( !BuildSamples( pLightInfo, pFaceLight, ndxFace ) )
+ {
+ Msg( "Face %d: Error Building Samples\n", ndxFace );
+ }
+
+ // build the luxels
+ if( !BuildLuxels( pLightInfo, pFaceLight, ndxFace ) )
+ {
+ Msg( "Face %d: Error Building Luxels\n", ndxFace );
+ }
+}
+
+
+//==============================================================
+
+directlight_t *activelights;
+directlight_t *freelights;
+
+facelight_t facelight[MAX_MAP_FACES];
+int numdlights;
+
+/*
+ ==================
+ FindTargetEntity
+ ==================
+*/
+entity_t *FindTargetEntity (char *target)
+{
+ int i;
+ char *n;
+
+ for (i=0 ; i<num_entities ; i++)
+ {
+ n = ValueForKey (&entities[i], "targetname");
+ if (!strcmp (n, target))
+ return &entities[i];
+ }
+
+ return NULL;
+}
+
+
+
+/*
+ =============
+ AllocDLight
+ =============
+*/
+
+int GetVisCache( int lastoffset, int cluster, byte *pvs );
+void SetDLightVis( directlight_t *dl, int cluster );
+void MergeDLightVis( directlight_t *dl, int cluster );
+
+directlight_t *AllocDLight( Vector& origin, bool bAddToList )
+{
+ directlight_t *dl;
+
+ dl = ( directlight_t* )calloc(1, sizeof(directlight_t));
+ dl->index = numdlights++;
+
+ VectorCopy( origin, dl->light.origin );
+
+ dl->light.cluster = ClusterFromPoint(dl->light.origin);
+ SetDLightVis( dl, dl->light.cluster );
+
+ dl->facenum = -1;
+
+ if ( bAddToList )
+ {
+ dl->next = activelights;
+ activelights = dl;
+ }
+
+ return dl;
+}
+
+void AddDLightToActiveList( directlight_t *dl )
+{
+ dl->next = activelights;
+ activelights = dl;
+}
+
+void FreeDLights()
+{
+ gSkyLight = NULL;
+ gAmbient = NULL;
+
+ directlight_t *pNext;
+ for( directlight_t *pCur=activelights; pCur; pCur=pNext )
+ {
+ pNext = pCur->next;
+ free( pCur );
+ }
+ activelights = 0;
+}
+
+
+void SetDLightVis( directlight_t *dl, int cluster )
+{
+ if (dl->pvs == NULL)
+ {
+ dl->pvs = (byte *)calloc( 1, (dvis->numclusters / 8) + 1 );
+ }
+
+ GetVisCache( -1, cluster, dl->pvs );
+}
+
+void MergeDLightVis( directlight_t *dl, int cluster )
+{
+ if (dl->pvs == NULL)
+ {
+ SetDLightVis( dl, cluster );
+ }
+ else
+ {
+ byte pvs[MAX_MAP_CLUSTERS/8];
+ GetVisCache( -1, cluster, pvs );
+
+ // merge both vis graphs
+ for (int i = 0; i < (dvis->numclusters / 8) + 1; i++)
+ {
+ dl->pvs[i] |= pvs[i];
+ }
+ }
+}
+
+
+/*
+ =============
+ LightForKey
+ =============
+*/
+int LightForKey (entity_t *ent, char *key, Vector& intensity )
+{
+ char *pLight;
+
+ pLight = ValueForKey( ent, key );
+
+ return LightForString( pLight, intensity );
+}
+
+int LightForString( char *pLight, Vector& intensity )
+{
+ double r, g, b, scaler;
+ int argCnt;
+
+ VectorFill( intensity, 0 );
+
+ // scanf into doubles, then assign, so it is vec_t size independent
+ r = g = b = scaler = 0;
+ double r_hdr,g_hdr,b_hdr,scaler_hdr;
+ argCnt = sscanf ( pLight, "%lf %lf %lf %lf %lf %lf %lf %lf",
+ &r, &g, &b, &scaler, &r_hdr,&g_hdr,&b_hdr,&scaler_hdr );
+
+ if (argCnt==8) // 2 4-tuples
+ {
+ if (g_bHDR)
+ {
+ r=r_hdr;
+ g=g_hdr;
+ b=b_hdr;
+ scaler=scaler_hdr;
+ }
+ argCnt=4;
+ }
+
+ // make sure light is legal
+ if( r < 0.0f || g < 0.0f || b < 0.0f || scaler < 0.0f )
+ {
+ intensity.Init( 0.0f, 0.0f, 0.0f );
+ return false;
+ }
+
+ intensity[0] = pow( r / 255.0, 2.2 ) * 255; // convert to linear
+
+ switch( argCnt)
+ {
+ case 1:
+ // The R,G,B values are all equal.
+ intensity[1] = intensity[2] = intensity[0];
+ break;
+
+ case 3:
+ case 4:
+ // Save the other two G,B values.
+ intensity[1] = pow( g / 255.0, 2.2 ) * 255;
+ intensity[2] = pow( b / 255.0, 2.2 ) * 255;
+
+ // Did we also get an "intensity" scaler value too?
+ if ( argCnt == 4 )
+ {
+ // Scale the normalized 0-255 R,G,B values by the intensity scaler
+ VectorScale( intensity, scaler / 255.0, intensity );
+ }
+ break;
+
+ default:
+ printf("unknown light specifier type - %s\n",pLight);
+ return false;
+ }
+ // scale up source lights by scaling factor
+ VectorScale( intensity, lightscale, intensity );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Various parsing methods
+//-----------------------------------------------------------------------------
+
+static void ParseLightGeneric( entity_t *e, directlight_t *dl )
+{
+ entity_t *e2;
+ char *target;
+ Vector dest;
+
+ dl->light.style = (int)FloatForKey (e, "style");
+
+ // get intenfsity
+ if( g_bHDR && LightForKey( e, "_lightHDR", dl->light.intensity ) )
+ {
+ }
+ else
+ {
+ LightForKey( e, "_light", dl->light.intensity );
+ }
+
+ // check angle, targets
+ target = ValueForKey (e, "target");
+ if (target[0])
+ { // point towards target
+ e2 = FindTargetEntity (target);
+ if (!e2)
+ Warning("WARNING: light at (%i %i %i) has missing target\n",
+ (int)dl->light.origin[0], (int)dl->light.origin[1], (int)dl->light.origin[2]);
+ else
+ {
+ GetVectorForKey (e2, "origin", dest);
+ VectorSubtract (dest, dl->light.origin, dl->light.normal);
+ VectorNormalize (dl->light.normal);
+ }
+ }
+ else
+ {
+ // point down angle
+ Vector angles;
+ GetVectorForKey( e, "angles", angles );
+ float pitch = FloatForKey (e, "pitch");
+ float angle = FloatForKey (e, "angle");
+ SetupLightNormalFromProps( QAngle( angles.x, angles.y, angles.z ), angle, pitch, dl->light.normal );
+ }
+ if ( g_bHDR )
+ VectorScale( dl->light.intensity,
+ FloatForKeyWithDefault( e, "_lightscaleHDR", 1.0 ),
+ dl->light.intensity );
+}
+
+static void SetLightFalloffParams( entity_t * e, directlight_t * dl )
+{
+ float d50=FloatForKey( e, "_fifty_percent_distance" );
+ dl->m_flStartFadeDistance = 0;
+ dl->m_flEndFadeDistance = - 1;
+ dl->m_flCapDist = 1.0e22;
+ if ( d50 )
+ {
+ float d0 = FloatForKey( e, "_zero_percent_distance" );
+ if ( d0 < d50 )
+ {
+ Warning( "light has _fifty_percent_distance of %f but _zero_percent_distance of %f\n", d50, d0);
+ d0 = 2.0 * d50;
+ }
+ float a = 0, b = 1, c = 0;
+ if ( ! SolveInverseQuadraticMonotonic( 0, 1.0, d50, 2.0, d0, 256.0, a, b, c ))
+ {
+ Warning( "can't solve quadratic for light %f %f\n", d50, d0 );
+ }
+ // it it possible that the parameters couldn't be used because of enforing monoticity. If so, rescale so at
+ // least the 50 percent value is right
+// printf("50 percent=%f 0 percent=%f\n",d50,d0);
+// printf("a=%f b=%f c=%f\n",a,b,c);
+ float v50 = c + d50 * ( b + d50 * a );
+ float scale = 2.0 / v50;
+ a *= scale;
+ b *= scale;
+ c *= scale;
+// printf("scaled=%f a=%f b=%f c=%f\n",scale,a,b,c);
+// for(float d=0;d<1000;d+=20)
+// printf("at %f, %f\n",d,1.0/(c+d*(b+d*a)));
+ dl->light.quadratic_attn = a;
+ dl->light.linear_attn = b;
+ dl->light.constant_attn = c;
+
+
+
+ if ( IntForKey(e, "_hardfalloff" ) )
+ {
+ dl->m_flEndFadeDistance = d0;
+ dl->m_flStartFadeDistance = 0.75 * d0 + 0.25 * d50; // start fading 3/4 way between 50 and 0. could allow adjust
+ }
+ else
+ {
+ // now, we will find the point at which the 1/x term reaches its maximum value, and
+ // prevent the light from going past there. If a user specifes an extreme falloff, the
+ // quadratic will start making the light brighter at some distance. We handle this by
+ // fading it from the minimum brightess point down to zero at 10x the minimum distance
+ if ( fabs( a ) > 0. )
+ {
+ float flMax = b / ( - 2.0 * a ); // where f' = 0
+ if ( flMax > 0.0 )
+ {
+ dl->m_flCapDist = flMax;
+ dl->m_flStartFadeDistance = flMax;
+ dl->m_flEndFadeDistance = 10.0 * flMax;
+ }
+ }
+ }
+ }
+ else
+ {
+ dl->light.constant_attn = FloatForKey (e, "_constant_attn" );
+ dl->light.linear_attn = FloatForKey (e, "_linear_attn" );
+ dl->light.quadratic_attn = FloatForKey (e, "_quadratic_attn" );
+
+ dl->light.radius = FloatForKey (e, "_distance");
+
+ // clamp values to >= 0
+ if ( dl->light.constant_attn < EQUAL_EPSILON )
+ dl->light.constant_attn = 0;
+
+ if ( dl->light.linear_attn < EQUAL_EPSILON )
+ dl->light.linear_attn = 0;
+
+ if ( dl->light.quadratic_attn < EQUAL_EPSILON )
+ dl->light.quadratic_attn = 0;
+
+ if ( dl->light.constant_attn < EQUAL_EPSILON && dl->light.linear_attn < EQUAL_EPSILON && dl->light.quadratic_attn < EQUAL_EPSILON )
+ dl->light.constant_attn = 1;
+
+ // scale intensity for unit 100 distance
+ float ratio = ( dl->light.constant_attn + 100 * dl->light.linear_attn + 100 * 100 * dl->light.quadratic_attn );
+ if ( ratio > 0 )
+ {
+ VectorScale( dl->light.intensity, ratio, dl->light.intensity );
+ }
+ }
+}
+
+static void ParseLightSpot( entity_t* e, directlight_t* dl )
+{
+ Vector dest;
+ GetVectorForKey (e, "origin", dest );
+ dl = AllocDLight( dest, true );
+
+ ParseLightGeneric( e, dl );
+
+ dl->light.type = emit_spotlight;
+
+ dl->light.stopdot = FloatForKey (e, "_inner_cone");
+ if (!dl->light.stopdot)
+ dl->light.stopdot = 10;
+
+ dl->light.stopdot2 = FloatForKey (e, "_cone");
+ if (!dl->light.stopdot2)
+ dl->light.stopdot2 = dl->light.stopdot;
+ if (dl->light.stopdot2 < dl->light.stopdot)
+ dl->light.stopdot2 = dl->light.stopdot;
+
+ // This is a point light if stop dots are 180...
+ if ((dl->light.stopdot == 180) && (dl->light.stopdot2 == 180))
+ {
+ dl->light.stopdot = dl->light.stopdot2 = 0;
+ dl->light.type = emit_point;
+ dl->light.exponent = 0;
+ }
+ else
+ {
+ // Clamp to 90, that's all DX8 can handle!
+ if (dl->light.stopdot > 90)
+ {
+ Warning("WARNING: light_spot at (%i %i %i) has inner angle larger than 90 degrees! Clamping to 90...\n",
+ (int)dl->light.origin[0], (int)dl->light.origin[1], (int)dl->light.origin[2]);
+ dl->light.stopdot = 90;
+ }
+
+ if (dl->light.stopdot2 > 90)
+ {
+ Warning("WARNING: light_spot at (%i %i %i) has outer angle larger than 90 degrees! Clamping to 90...\n",
+ (int)dl->light.origin[0], (int)dl->light.origin[1], (int)dl->light.origin[2]);
+ dl->light.stopdot2 = 90;
+ }
+
+ dl->light.stopdot2 = (float)cos(dl->light.stopdot2/180*M_PI);
+ dl->light.stopdot = (float)cos(dl->light.stopdot/180*M_PI);
+ dl->light.exponent = FloatForKey (e, "_exponent");
+ }
+
+ SetLightFalloffParams(e,dl);
+}
+
+// NOTE: This is just a heuristic. It traces a finite number of rays to find sky
+// NOTE: Full vis is necessary to make this 100% correct.
+bool CanLeafTraceToSky( int iLeaf )
+{
+ // UNDONE: Really want a point inside the leaf here. Center is a guess, may not be in the leaf
+ // UNDONE: Clip this to each plane bounding the leaf to guarantee
+ Vector center = vec3_origin;
+ for ( int i = 0; i < 3; i++ )
+ {
+ center[i] = ( (float)(dleafs[iLeaf].mins[i] + dleafs[iLeaf].maxs[i]) ) * 0.5f;
+ }
+
+ FourVectors center4, delta;
+ fltx4 fractionVisible;
+ for ( int j = 0; j < NUMVERTEXNORMALS; j+=4 )
+ {
+ // search back to see if we can hit a sky brush
+ delta.LoadAndSwizzle( g_anorms[j], g_anorms[min( j+1, NUMVERTEXNORMALS-1 )],
+ g_anorms[min( j+2, NUMVERTEXNORMALS-1 )], g_anorms[min( j+3, NUMVERTEXNORMALS-1 )] );
+ delta *= -MAX_TRACE_LENGTH;
+ delta += center4;
+
+ // return true if any hits sky
+ TestLine_DoesHitSky ( center4, delta, &fractionVisible );
+ if ( TestSignSIMD ( CmpGtSIMD ( fractionVisible, Four_Zeros ) ) )
+ return true;
+ }
+
+ return false;
+}
+
+void BuildVisForLightEnvironment( void )
+{
+ // Create the vis.
+ for ( int iLeaf = 0; iLeaf < numleafs; ++iLeaf )
+ {
+ dleafs[iLeaf].flags &= ~( LEAF_FLAGS_SKY | LEAF_FLAGS_SKY2D );
+ unsigned int iFirstFace = dleafs[iLeaf].firstleafface;
+ for ( int iLeafFace = 0; iLeafFace < dleafs[iLeaf].numleaffaces; ++iLeafFace )
+ {
+ unsigned int iFace = dleaffaces[iFirstFace+iLeafFace];
+
+ texinfo_t &tex = texinfo[g_pFaces[iFace].texinfo];
+ if ( tex.flags & SURF_SKY )
+ {
+ if ( tex.flags & SURF_SKY2D )
+ {
+ dleafs[iLeaf].flags |= LEAF_FLAGS_SKY2D;
+ }
+ else
+ {
+ dleafs[iLeaf].flags |= LEAF_FLAGS_SKY;
+ }
+ MergeDLightVis( gSkyLight, dleafs[iLeaf].cluster );
+ MergeDLightVis( gAmbient, dleafs[iLeaf].cluster );
+ break;
+ }
+ }
+ }
+
+ // Second pass to set flags on leaves that don't contain sky, but touch leaves that
+ // contain sky.
+ byte pvs[MAX_MAP_CLUSTERS / 8];
+
+ int nLeafBytes = (numleafs >> 3) + 1;
+ unsigned char *pLeafBits = (unsigned char *)stackalloc( nLeafBytes * sizeof(unsigned char) );
+ unsigned char *pLeaf2DBits = (unsigned char *)stackalloc( nLeafBytes * sizeof(unsigned char) );
+ memset( pLeafBits, 0, nLeafBytes );
+ memset( pLeaf2DBits, 0, nLeafBytes );
+
+ for ( int iLeaf = 0; iLeaf < numleafs; ++iLeaf )
+ {
+ // If this leaf has light (3d skybox) in it, then don't bother
+ if ( dleafs[iLeaf].flags & LEAF_FLAGS_SKY )
+ continue;
+
+ // Don't bother with this leaf if it's solid
+ if ( dleafs[iLeaf].contents & CONTENTS_SOLID )
+ continue;
+
+ // See what other leaves are visible from this leaf
+ GetVisCache( -1, dleafs[iLeaf].cluster, pvs );
+
+ // Now check out all other leaves
+ int nByte = iLeaf >> 3;
+ int nBit = 1 << ( iLeaf & 0x7 );
+ for ( int iLeaf2 = 0; iLeaf2 < numleafs; ++iLeaf2 )
+ {
+ if ( iLeaf2 == iLeaf )
+ continue;
+
+ if ( !(dleafs[iLeaf2].flags & ( LEAF_FLAGS_SKY | LEAF_FLAGS_SKY2D ) ) )
+ continue;
+
+ // Can this leaf see into the leaf with the sky in it?
+ if ( !PVSCheck( pvs, dleafs[iLeaf2].cluster ) )
+ continue;
+
+ if ( dleafs[iLeaf2].flags & LEAF_FLAGS_SKY2D )
+ {
+ pLeaf2DBits[ nByte ] |= nBit;
+ }
+ if ( dleafs[iLeaf2].flags & LEAF_FLAGS_SKY )
+ {
+ pLeafBits[ nByte ] |= nBit;
+
+ // As soon as we know this leaf needs to draw the 3d skybox, we're done
+ break;
+ }
+ }
+ }
+
+ // Must set the bits in a separate pass so as to not flood-fill LEAF_FLAGS_SKY everywhere
+ // pLeafbits is a bit array of all leaves that need to be marked as seeing sky
+ for ( int iLeaf = 0; iLeaf < numleafs; ++iLeaf )
+ {
+ // If this leaf has light (3d skybox) in it, then don't bother
+ if ( dleafs[iLeaf].flags & LEAF_FLAGS_SKY )
+ continue;
+
+ // Don't bother with this leaf if it's solid
+ if ( dleafs[iLeaf].contents & CONTENTS_SOLID )
+ continue;
+
+ // Check to see if this is a 2D skybox leaf
+ if ( pLeaf2DBits[ iLeaf >> 3 ] & (1 << ( iLeaf & 0x7 )) )
+ {
+ dleafs[iLeaf].flags |= LEAF_FLAGS_SKY2D;
+ }
+
+ // If this is a 3D skybox leaf, then we don't care if it was previously a 2D skybox leaf
+ if ( pLeafBits[ iLeaf >> 3 ] & (1 << ( iLeaf & 0x7 )) )
+ {
+ dleafs[iLeaf].flags |= LEAF_FLAGS_SKY;
+ dleafs[iLeaf].flags &= ~LEAF_FLAGS_SKY2D;
+ }
+ else
+ {
+ // if radial vis was used on this leaf some of the portals leading
+ // to sky may have been culled. Try tracing to find sky.
+ if ( dleafs[iLeaf].flags & LEAF_FLAGS_RADIAL )
+ {
+ if ( CanLeafTraceToSky(iLeaf) )
+ {
+ // FIXME: Should make a version that checks if we hit 2D skyboxes.. oh well.
+ dleafs[iLeaf].flags |= LEAF_FLAGS_SKY;
+ }
+ }
+ }
+ }
+}
+
+static char *ValueForKeyWithDefault (entity_t *ent, char *key, char *default_value = NULL)
+{
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ if (!strcmp (ep->key, key) )
+ return ep->value;
+ return default_value;
+}
+
+static void ParseLightEnvironment( entity_t* e, directlight_t* dl )
+{
+ Vector dest;
+ GetVectorForKey (e, "origin", dest );
+ dl = AllocDLight( dest, false );
+
+ ParseLightGeneric( e, dl );
+
+ char *angle_str=ValueForKeyWithDefault( e, "SunSpreadAngle" );
+ if (angle_str)
+ {
+ g_SunAngularExtent=atof(angle_str);
+ g_SunAngularExtent=sin((M_PI/180.0)*g_SunAngularExtent);
+ printf("sun extent from map=%f\n",g_SunAngularExtent);
+ }
+ if ( !gSkyLight )
+ {
+ // Sky light.
+ gSkyLight = dl;
+ dl->light.type = emit_skylight;
+
+ // Sky ambient light.
+ gAmbient = AllocDLight( dl->light.origin, false );
+ gAmbient->light.type = emit_skyambient;
+ if( g_bHDR && LightForKey( e, "_ambientHDR", gAmbient->light.intensity ) )
+ {
+ // we have a valid HDR ambient light value
+ }
+ else if ( !LightForKey( e, "_ambient", gAmbient->light.intensity ) )
+ {
+ VectorScale( dl->light.intensity, 0.5, gAmbient->light.intensity );
+ }
+ if ( g_bHDR )
+ {
+ VectorScale( gAmbient->light.intensity,
+ FloatForKeyWithDefault( e, "_AmbientScaleHDR", 1.0 ),
+ gAmbient->light.intensity );
+ }
+
+ BuildVisForLightEnvironment();
+
+ // Add sky and sky ambient lights to the list.
+ AddDLightToActiveList( gSkyLight );
+ AddDLightToActiveList( gAmbient );
+ }
+}
+
+static void ParseLightPoint( entity_t* e, directlight_t* dl )
+{
+ Vector dest;
+ GetVectorForKey (e, "origin", dest );
+ dl = AllocDLight( dest, true );
+
+ ParseLightGeneric( e, dl );
+
+ dl->light.type = emit_point;
+
+ SetLightFalloffParams(e,dl);
+}
+
+/*
+ =============
+ CreateDirectLights
+ =============
+*/
+#define DIRECT_SCALE (100.0*100.0)
+void CreateDirectLights (void)
+{
+ unsigned i;
+ CPatch *p = NULL;
+ directlight_t *dl = NULL;
+ entity_t *e = NULL;
+ char *name;
+ Vector dest;
+
+ numdlights = 0;
+
+ FreeDLights();
+
+ //
+ // surfaces
+ //
+ unsigned int uiPatchCount = g_Patches.Count();
+ for (i=0; i< uiPatchCount; i++)
+ {
+ p = &g_Patches.Element( i );
+
+ // skip parent patches
+ if (p->child1 != g_Patches.InvalidIndex() )
+ continue;
+
+ if (p->basearea < 1e-6)
+ continue;
+
+ if( VectorAvg( p->baselight ) >= dlight_threshold )
+ {
+ dl = AllocDLight( p->origin, true );
+
+ dl->light.type = emit_surface;
+ VectorCopy (p->normal, dl->light.normal);
+ Assert( VectorLength( p->normal ) > 1.0e-20 );
+ // scale intensity by number of texture instances
+ VectorScale( p->baselight, lightscale * p->area * p->scale[0] * p->scale[1] / p->basearea, dl->light.intensity );
+
+ // scale to a range that results in actual light
+ VectorScale( dl->light.intensity, DIRECT_SCALE, dl->light.intensity );
+ }
+ }
+
+ //
+ // entities
+ //
+ for (i=0 ; i<(unsigned)num_entities ; i++)
+ {
+ e = &entities[i];
+ name = ValueForKey (e, "classname");
+ if (strncmp (name, "light", 5))
+ continue;
+
+ // Light_dynamic is actually a real entity; not to be included here...
+ if (!strcmp (name, "light_dynamic"))
+ continue;
+
+ if (!strcmp (name, "light_spot"))
+ {
+ ParseLightSpot( e, dl );
+ }
+ else if (!strcmp(name, "light_environment"))
+ {
+ ParseLightEnvironment( e, dl );
+ }
+ else if (!strcmp(name, "light"))
+ {
+ ParseLightPoint( e, dl );
+ }
+ else
+ {
+ qprintf( "unsupported light entity: \"%s\"\n", name );
+ }
+ }
+
+ qprintf ("%i direct lights\n", numdlights);
+ // exit(1);
+}
+
+/*
+ =============
+ ExportDirectLightsToWorldLights
+ =============
+*/
+
+void ExportDirectLightsToWorldLights()
+{
+ directlight_t *dl;
+
+ // In case the level has already been VRADed.
+ *pNumworldlights = 0;
+
+ for (dl = activelights; dl != NULL; dl = dl->next )
+ {
+ dworldlight_t *wl = &dworldlights[(*pNumworldlights)++];
+
+ if (*pNumworldlights > MAX_MAP_WORLDLIGHTS)
+ {
+ Error("too many lights %d / %d\n", *pNumworldlights, MAX_MAP_WORLDLIGHTS );
+ }
+
+ wl->cluster = dl->light.cluster;
+ wl->type = dl->light.type;
+ wl->style = dl->light.style;
+ VectorCopy( dl->light.origin, wl->origin );
+ // FIXME: why does vrad want 0 to 255 and not 0 to 1??
+ VectorScale( dl->light.intensity, (1.0 / 255.0), wl->intensity );
+ VectorCopy( dl->light.normal, wl->normal );
+ wl->stopdot = dl->light.stopdot;
+ wl->stopdot2 = dl->light.stopdot2;
+ wl->exponent = dl->light.exponent;
+ wl->radius = dl->light.radius;
+ wl->constant_attn = dl->light.constant_attn;
+ wl->linear_attn = dl->light.linear_attn;
+ wl->quadratic_attn = dl->light.quadratic_attn;
+ wl->flags = 0;
+ }
+}
+
+/*
+ =============
+ GatherSampleLight
+ =============
+*/
+#define NORMALFORMFACTOR 40.156979 // accumuated dot products for hemisphere
+
+#define CONSTANT_DOT (.7/2)
+
+#define NSAMPLES_SUN_AREA_LIGHT 30 // number of samples to take for an
+ // non-point sun light
+
+// Helper function - gathers light from sun (emit_skylight)
+void GatherSampleSkyLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+ FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+ int nLFlags, int static_prop_index_to_ignore,
+ float flEpsilon )
+{
+ bool bIgnoreNormals = ( nLFlags & GATHERLFLAGS_IGNORE_NORMALS ) != 0;
+ bool force_fast = ( nLFlags & GATHERLFLAGS_FORCE_FAST ) != 0;
+
+ fltx4 dot;
+
+ if ( bIgnoreNormals )
+ dot = ReplicateX4( CONSTANT_DOT );
+ else
+ dot = NegSIMD( pNormals[0] * dl->light.normal );
+
+ dot = MaxSIMD( dot, Four_Zeros );
+ int zeroMask = TestSignSIMD ( CmpEqSIMD( dot, Four_Zeros ) );
+ if (zeroMask == 0xF)
+ return;
+
+ int nsamples = 1;
+ if ( g_SunAngularExtent > 0.0f )
+ {
+ nsamples = NSAMPLES_SUN_AREA_LIGHT;
+ if ( do_fast || force_fast )
+ nsamples /= 4;
+ }
+
+ fltx4 totalFractionVisible = Four_Zeros;
+ fltx4 fractionVisible = Four_Zeros;
+
+ DirectionalSampler_t sampler;
+
+ for ( int d = 0; d < nsamples; d++ )
+ {
+ // determine visibility of skylight
+ // serach back to see if we can hit a sky brush
+ Vector delta;
+ VectorScale( dl->light.normal, -MAX_TRACE_LENGTH, delta );
+ if ( d )
+ {
+ // jitter light source location
+ Vector ofs = sampler.NextValue();
+ ofs *= MAX_TRACE_LENGTH * g_SunAngularExtent;
+ delta += ofs;
+ }
+ FourVectors delta4;
+ delta4.DuplicateVector ( delta );
+ delta4 += pos;
+
+ TestLine_DoesHitSky ( pos, delta4, &fractionVisible, true, static_prop_index_to_ignore );
+
+ totalFractionVisible = AddSIMD ( totalFractionVisible, fractionVisible );
+ }
+
+ fltx4 seeAmount = MulSIMD ( totalFractionVisible, ReplicateX4 ( 1.0f / nsamples ) );
+ out.m_flDot[0] = MulSIMD ( dot, seeAmount );
+ out.m_flFalloff = Four_Ones;
+ out.m_flSunAmount = MulSIMD ( seeAmount, ReplicateX4( 10000.0f ) );
+ for ( int i = 1; i < normalCount; i++ )
+ {
+ if ( bIgnoreNormals )
+ out.m_flDot[i] = ReplicateX4 ( CONSTANT_DOT );
+ else
+ {
+ out.m_flDot[i] = NegSIMD( pNormals[i] * dl->light.normal );
+ out.m_flDot[i] = MulSIMD( out.m_flDot[i], seeAmount );
+ }
+ }
+}
+
+// Helper function - gathers light from ambient sky light
+void GatherSampleAmbientSkySSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+ FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+ int nLFlags, int static_prop_index_to_ignore,
+ float flEpsilon )
+{
+
+ bool bIgnoreNormals = ( nLFlags & GATHERLFLAGS_IGNORE_NORMALS ) != 0;
+ bool force_fast = ( nLFlags & GATHERLFLAGS_FORCE_FAST ) != 0;
+
+ fltx4 sumdot = Four_Zeros;
+ fltx4 ambient_intensity[NUM_BUMP_VECTS+1];
+ fltx4 possibleHitCount[NUM_BUMP_VECTS+1];
+ fltx4 dots[NUM_BUMP_VECTS+1];
+
+ for ( int i = 0; i < normalCount; i++ )
+ {
+ ambient_intensity[i] = Four_Zeros;
+ possibleHitCount[i] = Four_Zeros;
+ }
+
+ DirectionalSampler_t sampler;
+ int nsky_samples = NUMVERTEXNORMALS;
+ if (do_fast || force_fast )
+ nsky_samples /= 4;
+ else
+ nsky_samples *= g_flSkySampleScale;
+
+ for (int j = 0; j < nsky_samples; j++)
+ {
+ FourVectors anorm;
+ anorm.DuplicateVector( sampler.NextValue() );
+
+ if ( bIgnoreNormals )
+ dots[0] = ReplicateX4( CONSTANT_DOT );
+ else
+ dots[0] = NegSIMD( pNormals[0] * anorm );
+
+ fltx4 validity = CmpGtSIMD( dots[0], ReplicateX4( EQUAL_EPSILON ) );
+
+ // No possibility of anybody getting lit
+ if ( !TestSignSIMD( validity ) )
+ continue;
+
+ dots[0] = AndSIMD( validity, dots[0] );
+ sumdot = AddSIMD( dots[0], sumdot );
+ possibleHitCount[0] = AddSIMD( AndSIMD( validity, Four_Ones ), possibleHitCount[0] );
+
+ for ( int i = 1; i < normalCount; i++ )
+ {
+ if ( bIgnoreNormals )
+ dots[i] = ReplicateX4( CONSTANT_DOT );
+ else
+ dots[i] = NegSIMD( pNormals[i] * anorm );
+ fltx4 validity2 = CmpGtSIMD( dots[i], ReplicateX4 ( EQUAL_EPSILON ) );
+ dots[i] = AndSIMD( validity2, dots[i] );
+ possibleHitCount[i] = AddSIMD( AndSIMD( AndSIMD( validity, validity2 ), Four_Ones ), possibleHitCount[i] );
+ }
+
+ // search back to see if we can hit a sky brush
+ FourVectors delta = anorm;
+ delta *= -MAX_TRACE_LENGTH;
+ delta += pos;
+ FourVectors surfacePos = pos;
+ FourVectors offset = anorm;
+ offset *= -flEpsilon;
+ surfacePos -= offset;
+
+ fltx4 fractionVisible = Four_Ones;
+ TestLine_DoesHitSky( surfacePos, delta, &fractionVisible, true, static_prop_index_to_ignore );
+ for ( int i = 0; i < normalCount; i++ )
+ {
+ fltx4 addedAmount = MulSIMD( fractionVisible, dots[i] );
+ ambient_intensity[i] = AddSIMD( ambient_intensity[i], addedAmount );
+ }
+
+ }
+
+ out.m_flFalloff = Four_Ones;
+ for ( int i = 0; i < normalCount; i++ )
+ {
+ // now scale out the missing parts of the hemisphere of this bump basis vector
+ fltx4 factor = ReciprocalSIMD( possibleHitCount[0] );
+ factor = MulSIMD( factor, possibleHitCount[i] );
+ out.m_flDot[i] = MulSIMD( factor, sumdot );
+ out.m_flDot[i] = ReciprocalSIMD( out.m_flDot[i] );
+ out.m_flDot[i] = MulSIMD( ambient_intensity[i], out.m_flDot[i] );
+ }
+
+}
+
+// Helper function - gathers light from area lights, spot lights, and point lights
+void GatherSampleStandardLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+ FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+ int nLFlags, int static_prop_index_to_ignore,
+ float flEpsilon )
+{
+ bool bIgnoreNormals = ( nLFlags & GATHERLFLAGS_IGNORE_NORMALS ) != 0;
+
+ FourVectors src;
+ src.DuplicateVector( vec3_origin );
+
+ if (dl->facenum == -1)
+ {
+ src.DuplicateVector( dl->light.origin );
+ }
+
+ // Find light vector
+ FourVectors delta;
+ delta = src;
+ delta -= pos;
+ fltx4 dist2 = delta.length2();
+ fltx4 rpcDist = ReciprocalSqrtSIMD( dist2 );
+ delta *= rpcDist;
+ fltx4 dist = SqrtEstSIMD( dist2 );//delta.VectorNormalize();
+
+ // Compute dot
+ fltx4 dot = ReplicateX4( (float) CONSTANT_DOT );
+ if ( !bIgnoreNormals )
+ dot = delta * pNormals[0];
+ dot = MaxSIMD( Four_Zeros, dot );
+
+ // Affix dot to zero if past fade distz
+ bool bHasHardFalloff = ( dl->m_flEndFadeDistance > dl->m_flStartFadeDistance );
+ if ( bHasHardFalloff )
+ {
+ fltx4 notPastFadeDist = CmpLeSIMD ( dist, ReplicateX4 ( dl->m_flEndFadeDistance ) );
+ dot = AndSIMD( dot, notPastFadeDist ); // dot = 0 if past fade distance
+ if ( !TestSignSIMD ( notPastFadeDist ) )
+ return;
+ }
+
+ dist = MaxSIMD( dist, Four_Ones );
+ fltx4 falloffEvalDist = MinSIMD( dist, ReplicateX4( dl->m_flCapDist ) );
+
+ fltx4 constant, linear, quadratic;
+ fltx4 dot2, inCone, inFringe, mult;
+ FourVectors offset;
+
+ switch (dl->light.type)
+ {
+ case emit_point:
+ constant = ReplicateX4( dl->light.constant_attn );
+ linear = ReplicateX4( dl->light.linear_attn );
+ quadratic = ReplicateX4( dl->light.quadratic_attn );
+
+ out.m_flFalloff = MulSIMD( falloffEvalDist, falloffEvalDist );
+ out.m_flFalloff = MulSIMD( out.m_flFalloff, quadratic );
+ out.m_flFalloff = AddSIMD( out.m_flFalloff, MulSIMD( linear, falloffEvalDist ) );
+ out.m_flFalloff = AddSIMD( out.m_flFalloff, constant );
+ out.m_flFalloff = ReciprocalSIMD( out.m_flFalloff );
+ break;
+
+ case emit_surface:
+ dot2 = delta * dl->light.normal;
+ dot2 = NegSIMD( dot2 );
+
+ // Light behind surface yields zero dot
+ dot2 = MaxSIMD( Four_Zeros, dot2 );
+ if ( TestSignSIMD( CmpEqSIMD( Four_Zeros, dot ) ) == 0xF )
+ return;
+
+ out.m_flFalloff = ReciprocalSIMD ( dist2 );
+ out.m_flFalloff = MulSIMD( out.m_flFalloff, dot2 );
+
+ // move the endpoint away from the surface by epsilon to prevent hitting the surface with the trace
+ offset.DuplicateVector ( dl->light.normal );
+ offset *= DIST_EPSILON;
+ src += offset;
+ break;
+
+ case emit_spotlight:
+ dot2 = delta * dl->light.normal;
+ dot2 = NegSIMD( dot2 );
+
+ // Affix dot2 to zero if outside light cone
+ inCone = CmpGtSIMD( dot2, ReplicateX4( dl->light.stopdot2 ) );
+ if ( !TestSignSIMD ( inCone ) )
+ return;
+ dot = AndSIMD( inCone, dot );
+
+ constant = ReplicateX4( dl->light.constant_attn );
+ linear = ReplicateX4( dl->light.linear_attn );
+ quadratic = ReplicateX4( dl->light.quadratic_attn );
+
+ out.m_flFalloff = MulSIMD( falloffEvalDist, falloffEvalDist );
+ out.m_flFalloff = MulSIMD( out.m_flFalloff, quadratic );
+ out.m_flFalloff = AddSIMD( out.m_flFalloff, MulSIMD( linear, falloffEvalDist ) );
+ out.m_flFalloff = AddSIMD( out.m_flFalloff, constant );
+ out.m_flFalloff = ReciprocalSIMD( out.m_flFalloff );
+ out.m_flFalloff = MulSIMD( out.m_flFalloff, dot2 );
+
+ // outside the inner cone
+ inFringe = CmpLeSIMD( dot2, ReplicateX4( dl->light.stopdot ) );
+ mult = ReplicateX4( dl->light.stopdot - dl->light.stopdot2 );
+ mult = ReciprocalSIMD( mult );
+ mult = MulSIMD( mult, SubSIMD( dot2, ReplicateX4( dl->light.stopdot2 ) ) );
+ mult = MinSIMD( mult, Four_Ones );
+ mult = MaxSIMD( mult, Four_Zeros );
+
+ // pow is fixed point, so this isn't the most accurate, but it doesn't need to be
+ if ( (dl->light.exponent != 0.0f ) && ( dl->light.exponent != 1.0f ) )
+ mult = PowSIMD( mult, dl->light.exponent );
+
+ // if not in between inner and outer cones, mult by 1
+ mult = AndSIMD( inFringe, mult );
+ mult = AddSIMD( mult, AndNotSIMD( inFringe, Four_Ones ) );
+ out.m_flFalloff = MulSIMD( mult, out.m_flFalloff );
+ break;
+
+ }
+
+ // we may be in the fade region - modulate lighting by the fade curve
+ //float t = ( dist - dl->m_flStartFadeDistance ) /
+ // ( dl->m_flEndFadeDistance - dl->m_flStartFadeDistance );
+ if ( bHasHardFalloff )
+ {
+ fltx4 t = ReplicateX4( dl->m_flEndFadeDistance - dl->m_flStartFadeDistance );
+ t = ReciprocalSIMD( t );
+ t = MulSIMD( t, SubSIMD( dist, ReplicateX4( dl->m_flStartFadeDistance ) ) );
+
+ // clamp t to [0...1]
+ t = MinSIMD( t, Four_Ones );
+ t = MaxSIMD( t, Four_Zeros );
+ t = SubSIMD( Four_Ones, t );
+
+ // Using QuinticInterpolatingPolynomial, SSE-ified
+ // t * t * t *( t * ( t* 6.0 - 15.0 ) + 10.0 )
+ mult = SubSIMD( MulSIMD( ReplicateX4( 6.0f ), t ), ReplicateX4( 15.0f ) );
+ mult = AddSIMD( MulSIMD( mult, t ), ReplicateX4( 10.0f ) );
+ mult = MulSIMD( MulSIMD( t, t), mult );
+ mult = MulSIMD( t, mult );
+ out.m_flFalloff = MulSIMD( mult, out.m_flFalloff );
+ }
+
+ // Raytrace for visibility function
+ fltx4 fractionVisible = Four_Ones;
+ TestLine( pos, src, &fractionVisible, static_prop_index_to_ignore);
+ dot = MulSIMD( fractionVisible, dot );
+ out.m_flDot[0] = dot;
+
+ for ( int i = 1; i < normalCount; i++ )
+ {
+ if ( bIgnoreNormals )
+ out.m_flDot[i] = ReplicateX4( (float) CONSTANT_DOT );
+ else
+ {
+ out.m_flDot[i] = pNormals[i] * delta;
+ out.m_flDot[i] = MaxSIMD( Four_Zeros, out.m_flDot[i] );
+ }
+ }
+}
+
+// returns dot product with normal and delta
+// dl - light
+// pos - position of sample
+// normal - surface normal of sample
+// out.m_flDot[] - returned dot products with light vector and each normal
+// out.m_flFalloff - amount of light falloff
+void GatherSampleLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+ FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+ int nLFlags,
+ int static_prop_index_to_ignore,
+ float flEpsilon )
+{
+ for ( int b = 0; b < normalCount; b++ )
+ out.m_flDot[b] = Four_Zeros;
+ out.m_flFalloff = Four_Zeros;
+ out.m_flSunAmount = Four_Zeros;
+ Assert( normalCount <= (NUM_BUMP_VECTS+1) );
+
+ // skylights work fundamentally differently than normal lights
+ switch( dl->light.type )
+ {
+ case emit_skylight:
+ GatherSampleSkyLightSSE( out, dl, facenum, pos, pNormals, normalCount,
+ iThread, nLFlags, static_prop_index_to_ignore, flEpsilon );
+ break;
+ case emit_skyambient:
+ GatherSampleAmbientSkySSE( out, dl, facenum, pos, pNormals, normalCount,
+ iThread, nLFlags, static_prop_index_to_ignore, flEpsilon );
+ break;
+ case emit_point:
+ case emit_surface:
+ case emit_spotlight:
+ GatherSampleStandardLightSSE( out, dl, facenum, pos, pNormals, normalCount,
+ iThread, nLFlags, static_prop_index_to_ignore, flEpsilon );
+ break;
+ default:
+ Error ("Bad dl->light.type");
+ return;
+ }
+
+ // NOTE: Notice here that if the light is on the back side of the face
+ // (tested by checking the dot product of the face normal and the light position)
+ // we don't want it to contribute to *any* of the bumped lightmaps. It glows
+ // in disturbing ways if we don't do this.
+ out.m_flDot[0] = MaxSIMD ( out.m_flDot[0], Four_Zeros );
+ fltx4 notZero = CmpGtSIMD( out.m_flDot[0], Four_Zeros );
+ for ( int n = 1; n < normalCount; n++ )
+ {
+ out.m_flDot[n] = MaxSIMD( out.m_flDot[n], Four_Zeros );
+ out.m_flDot[n] = AndSIMD( out.m_flDot[n], notZero );
+ }
+
+}
+
+/*
+ =============
+ AddSampleToPatch
+
+ Take the sample's collected light and
+ add it back into the apropriate patch
+ for the radiosity pass.
+ =============
+*/
+void AddSampleToPatch (sample_t *s, LightingValue_t& light, int facenum)
+{
+ CPatch *patch;
+ Vector mins, maxs;
+ int i;
+
+ if (numbounce == 0)
+ return;
+ if( VectorAvg( light.m_vecLighting ) < 1)
+ return;
+
+ //
+ // fixed the sample position and normal -- need to find the equiv pos, etc to set up
+ // patches
+ //
+ if( g_FacePatches.Element( facenum ) == g_FacePatches.InvalidIndex() )
+ return;
+
+ float radius = sqrt( s->area ) / 2.0;
+
+ CPatch *pNextPatch = NULL;
+ for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNext );
+ }
+
+ if (patch->sky)
+ continue;
+
+ // skip patches with children
+ if ( patch->child1 != g_Patches.InvalidIndex() )
+ continue;
+
+ // see if the point is in this patch (roughly)
+ WindingBounds (patch->winding, mins, maxs);
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (mins[i] > s->pos[i] + radius)
+ goto nextpatch;
+ if (maxs[i] < s->pos[i] - radius)
+ goto nextpatch;
+ }
+
+ // add the sample to the patch
+ patch->samplearea += s->area;
+ VectorMA( patch->samplelight, s->area, light.m_vecLighting, patch->samplelight );
+
+ nextpatch:;
+ }
+ // don't worry if some samples don't find a patch
+}
+
+
+void GetPhongNormal( int facenum, Vector const& spot, Vector& phongnormal )
+{
+ int j;
+ dface_t *f = &g_pFaces[facenum];
+// dplane_t *p = &dplanes[f->planenum];
+ Vector facenormal, vspot;
+
+ VectorCopy( dplanes[f->planenum].normal, facenormal );
+ VectorCopy( facenormal, phongnormal );
+
+ if ( smoothing_threshold != 1 )
+ {
+ faceneighbor_t *fn = &faceneighbor[facenum];
+
+ // Calculate modified point normal for surface
+ // Use the edge normals iff they are defined. Bend the surface towards the edge normal(s)
+ // Crude first attempt: find nearest edge normal and do a simple interpolation with facenormal.
+ // Second attempt: find edge points+center that bound the point and do a three-point triangulation(baricentric)
+ // Better third attempt: generate the point normals for all vertices and do baricentric triangulation.
+
+ for (j=0 ; j<f->numedges ; j++)
+ {
+ Vector v1, v2;
+ //int e = dsurfedges[f->firstedge + j];
+ //int e1 = dsurfedges[f->firstedge + ((j+f->numedges-1)%f->numedges)];
+ //int e2 = dsurfedges[f->firstedge + ((j+1)%f->numedges)];
+
+ //edgeshare_t *es = &edgeshare[abs(e)];
+ //edgeshare_t *es1 = &edgeshare[abs(e1)];
+ //edgeshare_t *es2 = &edgeshare[abs(e2)];
+ // dface_t *f2;
+ float a1, a2, aa, bb, ab;
+ int vert1, vert2;
+
+ Vector& n1 = fn->normal[j];
+ Vector& n2 = fn->normal[(j+1)%f->numedges];
+
+ /*
+ if (VectorCompare( n1, fn->facenormal )
+ && VectorCompare( n2, fn->facenormal) )
+ continue;
+ */
+
+ vert1 = EdgeVertex( f, j );
+ vert2 = EdgeVertex( f, j+1 );
+
+ Vector& p1 = dvertexes[vert1].point;
+ Vector& p2 = dvertexes[vert2].point;
+
+ // Build vectors from the middle of the face to the edge vertexes and the sample pos.
+ VectorSubtract( p1, face_centroids[facenum], v1 );
+ VectorSubtract( p2, face_centroids[facenum], v2 );
+ VectorSubtract( spot, face_centroids[facenum], vspot );
+ aa = DotProduct( v1, v1 );
+ bb = DotProduct( v2, v2 );
+ ab = DotProduct( v1, v2 );
+ a1 = (bb * DotProduct( v1, vspot ) - ab * DotProduct( vspot, v2 )) / (aa * bb - ab * ab);
+ a2 = (DotProduct( vspot, v2 ) - a1 * ab) / bb;
+
+ // Test center to sample vector for inclusion between center to vertex vectors (Use dot product of vectors)
+ if ( a1 >= 0.0 && a2 >= 0.0)
+ {
+ // calculate distance from edge to pos
+ Vector temp;
+ float scale;
+
+ // Interpolate between the center and edge normals based on sample position
+ scale = 1.0 - a1 - a2;
+ VectorScale( fn->facenormal, scale, phongnormal );
+ VectorScale( n1, a1, temp );
+ VectorAdd( phongnormal, temp, phongnormal );
+ VectorScale( n2, a2, temp );
+ VectorAdd( phongnormal, temp, phongnormal );
+ Assert( VectorLength( phongnormal ) > 1.0e-20 );
+ VectorNormalize( phongnormal );
+
+ /*
+ if (a1 > 1 || a2 > 1 || a1 + a2 > 1)
+ {
+ Msg("\n%.2f %.2f\n", a1, a2 );
+ Msg("%.2f %.2f %.2f\n", v1[0], v1[1], v1[2] );
+ Msg("%.2f %.2f %.2f\n", v2[0], v2[1], v2[2] );
+ Msg("%.2f %.2f %.2f\n", vspot[0], vspot[1], vspot[2] );
+ exit(1);
+
+ a1 = 0;
+ }
+ */
+ /*
+ phongnormal[0] = (((j + 1) & 4) != 0) * 255;
+ phongnormal[1] = (((j + 1) & 2) != 0) * 255;
+ phongnormal[2] = (((j + 1) & 1) != 0) * 255;
+ */
+ return;
+ }
+ }
+ }
+}
+
+void GetPhongNormal( int facenum, FourVectors const& spot, FourVectors& phongnormal )
+{
+ int j;
+ dface_t *f = &g_pFaces[facenum];
+ // dplane_t *p = &dplanes[f->planenum];
+ Vector facenormal;
+ FourVectors vspot;
+
+ VectorCopy( dplanes[f->planenum].normal, facenormal );
+ phongnormal.DuplicateVector( facenormal );
+
+ FourVectors faceCentroid;
+ faceCentroid.DuplicateVector( face_centroids[facenum] );
+
+ if ( smoothing_threshold != 1 )
+ {
+ faceneighbor_t *fn = &faceneighbor[facenum];
+
+ // Calculate modified point normal for surface
+ // Use the edge normals iff they are defined. Bend the surface towards the edge normal(s)
+ // Crude first attempt: find nearest edge normal and do a simple interpolation with facenormal.
+ // Second attempt: find edge points+center that bound the point and do a three-point triangulation(baricentric)
+ // Better third attempt: generate the point normals for all vertices and do baricentric triangulation.
+
+ for ( j = 0; j < f->numedges; ++j )
+ {
+ Vector v1, v2;
+ fltx4 a1, a2;
+ float aa, bb, ab;
+ int vert1, vert2;
+
+ Vector& n1 = fn->normal[j];
+ Vector& n2 = fn->normal[(j+1)%f->numedges];
+
+ vert1 = EdgeVertex( f, j );
+ vert2 = EdgeVertex( f, j+1 );
+
+ Vector& p1 = dvertexes[vert1].point;
+ Vector& p2 = dvertexes[vert2].point;
+
+ // Build vectors from the middle of the face to the edge vertexes and the sample pos.
+ VectorSubtract( p1, face_centroids[facenum], v1 );
+ VectorSubtract( p2, face_centroids[facenum], v2 );
+ //VectorSubtract( spot, face_centroids[facenum], vspot );
+ vspot = spot;
+ vspot -= faceCentroid;
+ aa = DotProduct( v1, v1 );
+ bb = DotProduct( v2, v2 );
+ ab = DotProduct( v1, v2 );
+ //a1 = (bb * DotProduct( v1, vspot ) - ab * DotProduct( vspot, v2 )) / (aa * bb - ab * ab);
+ a1 = ReciprocalSIMD( ReplicateX4( aa * bb - ab * ab ) );
+ a1 = MulSIMD( a1, SubSIMD( MulSIMD( ReplicateX4( bb ), vspot * v1 ), MulSIMD( ReplicateX4( ab ), vspot * v2 ) ) );
+ //a2 = (DotProduct( vspot, v2 ) - a1 * ab) / bb;
+ a2 = ReciprocalSIMD( ReplicateX4( bb ) );
+ a2 = MulSIMD( a2, SubSIMD( vspot * v2, MulSIMD( a1, ReplicateX4( ab ) ) ) );
+
+ fltx4 resultMask = AndSIMD( CmpGeSIMD( a1, Four_Zeros ), CmpGeSIMD( a2, Four_Zeros ) );
+
+ if ( !TestSignSIMD( resultMask ) )
+ continue;
+
+ // Store the old phong normal to avoid overwriting already computed phong normals
+ FourVectors oldPhongNormal = phongnormal;
+
+ // calculate distance from edge to pos
+ FourVectors temp;
+ fltx4 scale;
+
+ // Interpolate between the center and edge normals based on sample position
+ scale = SubSIMD( SubSIMD( Four_Ones, a1 ), a2 );
+ phongnormal.DuplicateVector( fn->facenormal );
+ phongnormal *= scale;
+ temp.DuplicateVector( n1 );
+ temp *= a1;
+ phongnormal += temp;
+ temp.DuplicateVector( n2 );
+ temp *= a2;
+ phongnormal += temp;
+
+ // restore the old phong normals
+ phongnormal.x = AddSIMD( AndSIMD( resultMask, phongnormal.x ), AndNotSIMD( resultMask, oldPhongNormal.x ) );
+ phongnormal.y = AddSIMD( AndSIMD( resultMask, phongnormal.y ), AndNotSIMD( resultMask, oldPhongNormal.y ) );
+ phongnormal.z = AddSIMD( AndSIMD( resultMask, phongnormal.z ), AndNotSIMD( resultMask, oldPhongNormal.z ) );
+ }
+
+ phongnormal.VectorNormalize();
+ }
+}
+
+
+
+int GetVisCache( int lastoffset, int cluster, byte *pvs )
+{
+ // get the PVS for the pos to limit the number of checks
+ if ( !visdatasize )
+ {
+ memset (pvs, 255, (dvis->numclusters+7)/8 );
+ lastoffset = -1;
+ }
+ else
+ {
+ if (cluster < 0)
+ {
+ // Error, point embedded in wall
+ // sampled[0][1] = 255;
+ memset (pvs, 255, (dvis->numclusters+7)/8 );
+ lastoffset = -1;
+ }
+ else
+ {
+ int thisoffset = dvis->bitofs[ cluster ][DVIS_PVS];
+ if ( thisoffset != lastoffset )
+ {
+ if ( thisoffset == -1 )
+ {
+ Error ("visofs == -1");
+ }
+
+ DecompressVis (&dvisdata[thisoffset], pvs);
+ }
+ lastoffset = thisoffset;
+ }
+ }
+ return lastoffset;
+}
+
+
+void BuildPatchLights( int facenum );
+
+void DumpSamples( int ndxFace, facelight_t *pFaceLight )
+{
+ ThreadLock();
+
+ dface_t *pFace = &g_pFaces[ndxFace];
+ if( pFace )
+ {
+ bool bBumpped = ( ( texinfo[pFace->texinfo].flags & SURF_BUMPLIGHT ) != 0 );
+
+ for( int iStyle = 0; iStyle < 4; ++iStyle )
+ {
+ if( pFace->styles[iStyle] != 255 )
+ {
+ for ( int iBump = 0; iBump < 4; ++iBump )
+ {
+ if ( iBump == 0 || ( iBump > 0 && bBumpped ) )
+ {
+ for( int iSample = 0; iSample < pFaceLight->numsamples; ++iSample )
+ {
+ sample_t *pSample = &pFaceLight->sample[iSample];
+ WriteWinding( pFileSamples[iStyle][iBump], pSample->w, pFaceLight->light[iStyle][iBump][iSample].m_vecLighting );
+ if( bDumpNormals )
+ {
+ WriteNormal( pFileSamples[iStyle][iBump], pSample->pos, pSample->normal, 15.0f, pSample->normal * 255.0f );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ThreadUnlock();
+}
+
+
+//-----------------------------------------------------------------------------
+// Allocates light sample data
+//-----------------------------------------------------------------------------
+static inline void AllocateLightstyleSamples( facelight_t* fl, int styleIndex, int numnormals )
+{
+ for (int n = 0; n < numnormals; ++n)
+ {
+ fl->light[styleIndex][n] = ( LightingValue_t* )calloc( fl->numsamples, sizeof(LightingValue_t ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to find an existing lightstyle on a face
+//-----------------------------------------------------------------------------
+static inline int FindLightstyle( dface_t* f, int lightstyle )
+{
+ for (int k = 0; k < MAXLIGHTMAPS; k++)
+ {
+ if (f->styles[k] == lightstyle)
+ return k;
+ }
+
+ return -1;
+}
+
+static int FindOrAllocateLightstyleSamples( dface_t* f, facelight_t *fl, int lightstyle, int numnormals )
+{
+ // Search the lightstyles associated with the face for a match
+ int k;
+ for (k = 0; k < MAXLIGHTMAPS; k++)
+ {
+ if (f->styles[k] == lightstyle)
+ break;
+
+ // Found an empty entry, we can use it for a new lightstyle
+ if (f->styles[k] == 255)
+ {
+ AllocateLightstyleSamples( fl, k, numnormals );
+ f->styles[k] = lightstyle;
+ break;
+ }
+ }
+
+ // Check for overflow
+ if (k >= MAXLIGHTMAPS)
+ return -1;
+
+ return k;
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute the illumination point + normal for the sample
+//-----------------------------------------------------------------------------
+static void ComputeIlluminationPointAndNormalsSSE( lightinfo_t const& l, FourVectors const &pos, FourVectors const &norm, SSE_SampleInfo_t* pInfo, int numSamples )
+{
+
+ Vector v[4];
+
+ pInfo->m_Points = pos;
+ bool computeNormals = ( pInfo->m_NormalCount > 1 && ( pInfo->m_IsDispFace || !l.isflat ) );
+
+ // FIXME: move sample point off the surface a bit, this is done so that
+ // light sampling will not be affected by a bug where raycasts will
+ // intersect with the face being lit. We really should just have that
+ // logic in GatherSampleLight
+ FourVectors faceNormal;
+ faceNormal.DuplicateVector( l.facenormal );
+ pInfo->m_Points += faceNormal;
+
+ if ( pInfo->m_IsDispFace )
+ {
+ pInfo->m_PointNormals[0] = norm;
+ }
+ else if ( !l.isflat )
+ {
+ // If the face isn't flat, use a phong-based normal instead
+ FourVectors modelorg;
+ modelorg.DuplicateVector( l.modelorg );
+ FourVectors vecSample = pos;
+ vecSample -= modelorg;
+ GetPhongNormal( pInfo->m_FaceNum, vecSample, pInfo->m_PointNormals[0] );
+ }
+
+ if ( computeNormals )
+ {
+ Vector bv[4][NUM_BUMP_VECTS];
+ for ( int i = 0; i < 4; ++i )
+ {
+ // TODO: using Vec may slow things down a bit
+ GetBumpNormals( pInfo->m_pTexInfo->textureVecsTexelsPerWorldUnits[0],
+ pInfo->m_pTexInfo->textureVecsTexelsPerWorldUnits[1],
+ l.facenormal, pInfo->m_PointNormals[0].Vec( i ), bv[i] );
+ }
+ for ( int b = 0; b < NUM_BUMP_VECTS; ++b )
+ {
+ pInfo->m_PointNormals[b+1].LoadAndSwizzle ( bv[0][b], bv[1][b], bv[2][b], bv[3][b] );
+ }
+ }
+
+ // TODO: this may slow things down a bit ( using Vec )
+ for ( int i = 0; i < 4; ++i )
+ pInfo->m_Clusters[i] = ClusterFromPoint( pos.Vec( i ) );
+}
+
+//-----------------------------------------------------------------------------
+// Iterates over all lights and computes lighting at up to 4 sample points
+//-----------------------------------------------------------------------------
+static void GatherSampleLightAt4Points( SSE_SampleInfo_t& info, int sampleIdx, int numSamples )
+{
+ SSE_sampleLightOutput_t out;
+
+ // Iterate over all direct lights and add them to the particular sample
+ for (directlight_t *dl = activelights; dl != NULL; dl = dl->next)
+ {
+ // is this lights cluster visible?
+ fltx4 dotMask = Four_Zeros;
+ bool skipLight = true;
+ for( int s = 0; s < numSamples; s++ )
+ {
+ if( PVSCheck( dl->pvs, info.m_Clusters[s] ) )
+ {
+ dotMask = SetComponentSIMD( dotMask, s, 1.0f );
+ skipLight = false;
+ }
+ }
+ if ( skipLight )
+ continue;
+
+ GatherSampleLightSSE( out, dl, info.m_FaceNum, info.m_Points, info.m_PointNormals, info.m_NormalCount, info.m_iThread );
+
+ // Apply the PVS check filter and compute falloff x dot
+ fltx4 fxdot[NUM_BUMP_VECTS + 1];
+ skipLight = true;
+ for ( int b = 0; b < info.m_NormalCount; b++ )
+ {
+ fxdot[b] = MulSIMD( out.m_flDot[b], dotMask );
+ fxdot[b] = MulSIMD( fxdot[b], out.m_flFalloff );
+ if ( !IsAllZeros( fxdot[b] ) )
+ {
+ skipLight = false;
+ }
+ }
+ if ( skipLight )
+ continue;
+
+ // Figure out the lightstyle for this particular sample
+ int lightStyleIndex = FindOrAllocateLightstyleSamples( info.m_pFace, info.m_pFaceLight,
+ dl->light.style, info.m_NormalCount );
+ if (lightStyleIndex < 0)
+ {
+ if (info.m_WarnFace != info.m_FaceNum)
+ {
+ Warning ("\nWARNING: Too many light styles on a face at (%f, %f, %f)\n",
+ info.m_Points.x.m128_f32[0], info.m_Points.y.m128_f32[0], info.m_Points.z.m128_f32[0] );
+ info.m_WarnFace = info.m_FaceNum;
+ }
+ continue;
+ }
+
+ // pLightmaps is an array of the lightmaps for each normal direction,
+ // here's where the result of the sample gathering goes
+ LightingValue_t** pLightmaps = info.m_pFaceLight->light[lightStyleIndex];
+
+ // Incremental lighting only cares about lightstyle zero
+ if( g_pIncremental && (dl->light.style == 0) )
+ {
+ for ( int i = 0; i < numSamples; i++ )
+ {
+ g_pIncremental->AddLightToFace( dl->m_IncrementalID, info.m_FaceNum, sampleIdx + i,
+ info.m_LightmapSize, SubFloat( fxdot[0], i ), info.m_iThread );
+ }
+ }
+
+ for( int n = 0; n < info.m_NormalCount; ++n )
+ {
+ for ( int i = 0; i < numSamples; i++ )
+ {
+ pLightmaps[n][sampleIdx + i].AddLight( SubFloat( fxdot[n], i ), dl->light.intensity, SubFloat( out.m_flSunAmount, i ) );
+ }
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Iterates over all lights and computes lighting at a sample point
+//-----------------------------------------------------------------------------
+static void ResampleLightAt4Points( SSE_SampleInfo_t& info, int lightStyleIndex, int flags, LightingValue_t pLightmap[4][NUM_BUMP_VECTS+1] )
+{
+ SSE_sampleLightOutput_t out;
+
+ // Clear result
+ for ( int i = 0; i < 4; ++i )
+ {
+ for ( int n = 0; n < info.m_NormalCount; ++n )
+ {
+ pLightmap[i][n].Zero();
+ }
+ }
+
+ // Iterate over all direct lights and add them to the particular sample
+ for (directlight_t *dl = activelights; dl != NULL; dl = dl->next)
+ {
+ if ((flags & AMBIENT_ONLY) && (dl->light.type != emit_skyambient))
+ continue;
+
+ if ((flags & NON_AMBIENT_ONLY) && (dl->light.type == emit_skyambient))
+ continue;
+
+ // Only add contributions that match the lightstyle
+ Assert( lightStyleIndex <= MAXLIGHTMAPS );
+ Assert( info.m_pFace->styles[lightStyleIndex] != 255 );
+ if (dl->light.style != info.m_pFace->styles[lightStyleIndex])
+ continue;
+
+ // is this lights cluster visible?
+ fltx4 dotMask = Four_Zeros;
+ bool skipLight = true;
+ for( int s = 0; s < 4; s++ )
+ {
+ if( PVSCheck( dl->pvs, info.m_Clusters[s] ) )
+ {
+ dotMask = SetComponentSIMD( dotMask, s, 1.0f );
+ skipLight = false;
+ }
+ }
+ if ( skipLight )
+ continue;
+
+ // NOTE: Notice here that if the light is on the back side of the face
+ // (tested by checking the dot product of the face normal and the light position)
+ // we don't want it to contribute to *any* of the bumped lightmaps. It glows
+ // in disturbing ways if we don't do this.
+ GatherSampleLightSSE( out, dl, info.m_FaceNum, info.m_Points, info.m_PointNormals, info.m_NormalCount, info.m_iThread );
+
+ // Apply the PVS check filter and compute falloff x dot
+ fltx4 fxdot[NUM_BUMP_VECTS + 1];
+ for ( int b = 0; b < info.m_NormalCount; b++ )
+ {
+ fxdot[b] = MulSIMD( out.m_flFalloff, out.m_flDot[b] );
+ fxdot[b] = MulSIMD( fxdot[b], dotMask );
+ }
+
+ // Compute the contributions to each of the bumped lightmaps
+ // The first sample is for non-bumped lighting.
+ // The other sample are for bumpmapping.
+ for( int i = 0; i < 4; ++i )
+ {
+ for( int n = 0; n < info.m_NormalCount; ++n )
+ {
+ pLightmap[i][n].AddLight( SubFloat( fxdot[n], i ), dl->light.intensity, SubFloat( out.m_flSunAmount, i ) );
+ }
+ }
+ }
+}
+
+bool PointsInWinding ( FourVectors const & point, winding_t *w, int &invalidBits )
+{
+ FourVectors edge, toPt, cross, testCross, p0, p1;
+ fltx4 invalidMask;
+
+ //
+ // get the first normal to test
+ //
+ p0.DuplicateVector( w->p[0] );
+ p1.DuplicateVector( w->p[1] );
+ toPt = point;
+ toPt -= p0;
+ edge = p1;
+ edge -= p0;
+ testCross = edge ^ toPt;
+ testCross.VectorNormalizeFast();
+
+ for( int ndxPt = 1; ndxPt < w->numpoints; ndxPt++ )
+ {
+ p0.DuplicateVector( w->p[ndxPt] );
+ p1.DuplicateVector( w->p[(ndxPt+1)%w->numpoints] );
+ toPt = point;
+ toPt -= p0;
+ edge = p1;
+ edge -= p0;
+ cross = edge ^ toPt;
+ cross.VectorNormalizeFast();
+
+ fltx4 dot = cross * testCross;
+ invalidMask = OrSIMD( invalidMask, CmpLtSIMD( dot, Four_Zeros ) );
+
+ invalidBits = TestSignSIMD ( invalidMask );
+ if ( invalidBits == 0xF )
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Perform supersampling at a particular point
+//-----------------------------------------------------------------------------
+static int SupersampleLightAtPoint( lightinfo_t& l, SSE_SampleInfo_t& info,
+ int sampleIndex, int lightStyleIndex, LightingValue_t *pLight, int flags )
+{
+ sample_t& sample = info.m_pFaceLight->sample[sampleIndex];
+
+ // Get the position of the original sample in lightmapspace
+ Vector2D temp;
+ WorldToLuxelSpace( &l, sample.pos, temp );
+ Vector sampleLightOrigin( temp[0], temp[1], 0.0f );
+
+ // Some parameters related to supersampling
+ float sampleWidth = ( flags & NON_AMBIENT_ONLY ) ? 4 : 2;
+ float cscale = 1.0f / sampleWidth;
+ float csshift = -((sampleWidth - 1) * cscale) / 2.0;
+
+ // Clear out the light values
+ for (int i = 0; i < info.m_NormalCount; ++i )
+ pLight[i].Zero();
+
+ int subsampleCount = 0;
+
+ FourVectors superSampleNormal;
+ superSampleNormal.DuplicateVector( sample.normal );
+
+ FourVectors superSampleLightCoord;
+ FourVectors superSamplePosition;
+
+ if ( flags & NON_AMBIENT_ONLY )
+ {
+ float aRow[4];
+ for ( int coord = 0; coord < 4; ++coord )
+ aRow[coord] = csshift + coord * cscale;
+ fltx4 sseRow = LoadUnalignedSIMD( aRow );
+
+ for (int s = 0; s < 4; ++s)
+ {
+ // make sure the coordinate is inside of the sample's winding and when normalizing
+ // below use the number of samples used, not just numsamples and some of them
+ // will be skipped if they are not inside of the winding
+ superSampleLightCoord.DuplicateVector( sampleLightOrigin );
+ superSampleLightCoord.x = AddSIMD( superSampleLightCoord.x, ReplicateX4( aRow[s] ) );
+ superSampleLightCoord.y = AddSIMD( superSampleLightCoord.y, sseRow );
+
+ // Figure out where the supersample exists in the world, and make sure
+ // it lies within the sample winding
+ LuxelSpaceToWorld( &l, superSampleLightCoord[0], superSampleLightCoord[1], superSamplePosition );
+
+ // A winding should exist only if the sample wasn't a uniform luxel, or if g_bDumpPatches is true.
+ int invalidBits = 0;
+ if ( sample.w && !PointsInWinding( superSamplePosition, sample.w, invalidBits ) )
+ continue;
+
+ // Compute the super-sample illumination point and normal
+ // We're assuming the flat normal is the same for all supersamples
+ ComputeIlluminationPointAndNormalsSSE( l, superSamplePosition, superSampleNormal, &info, 4 );
+
+ // Resample the non-ambient light at this point...
+ LightingValue_t result[4][NUM_BUMP_VECTS+1];
+ ResampleLightAt4Points( info, lightStyleIndex, NON_AMBIENT_ONLY, result );
+
+ // Got more subsamples
+ for ( int i = 0; i < 4; i++ )
+ {
+ if ( !( ( invalidBits >> i ) & 0x1 ) )
+ {
+ for ( int n = 0; n < info.m_NormalCount; ++n )
+ {
+ pLight[n].AddLight( result[i][n] );
+ }
+ ++subsampleCount;
+ }
+ }
+ }
+ }
+ else
+ {
+ FourVectors superSampleOffsets;
+ superSampleOffsets.LoadAndSwizzle( Vector( csshift, csshift, 0 ), Vector( csshift, csshift + cscale, 0),
+ Vector( csshift + cscale, csshift, 0 ), Vector( csshift + cscale, csshift + cscale, 0 ) );
+ superSampleLightCoord.DuplicateVector( sampleLightOrigin );
+ superSampleLightCoord += superSampleOffsets;
+
+ LuxelSpaceToWorld( &l, superSampleLightCoord[0], superSampleLightCoord[1], superSamplePosition );
+
+ int invalidBits = 0;
+ if ( sample.w && !PointsInWinding( superSamplePosition, sample.w, invalidBits ) )
+ return 0;
+
+ ComputeIlluminationPointAndNormalsSSE( l, superSamplePosition, superSampleNormal, &info, 4 );
+
+ LightingValue_t result[4][NUM_BUMP_VECTS+1];
+ ResampleLightAt4Points( info, lightStyleIndex, AMBIENT_ONLY, result );
+
+ // Got more subsamples
+ for ( int i = 0; i < 4; i++ )
+ {
+ if ( !( ( invalidBits >> i ) & 0x1 ) )
+ {
+ for ( int n = 0; n < info.m_NormalCount; ++n )
+ {
+ pLight[n].AddLight( result[i][n] );
+ }
+ ++subsampleCount;
+ }
+ }
+ }
+
+ return subsampleCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute gradients of a lightmap
+//-----------------------------------------------------------------------------
+static void ComputeLightmapGradients( SSE_SampleInfo_t& info, bool const* pHasProcessedSample,
+ float* pIntensity, float* gradient )
+{
+ int w = info.m_LightmapWidth;
+ int h = info.m_LightmapHeight;
+ facelight_t* fl = info.m_pFaceLight;
+
+ for (int i=0 ; i<fl->numsamples ; i++)
+ {
+ // Don't supersample the same sample twice
+ if (pHasProcessedSample[i])
+ continue;
+
+ gradient[i] = 0.0f;
+ sample_t& sample = fl->sample[i];
+
+ // Choose the maximum gradient of all bumped lightmap intensities
+ for ( int n = 0; n < info.m_NormalCount; ++n )
+ {
+ int j = n * info.m_LightmapSize + sample.s + sample.t * w;
+
+ if (sample.t > 0)
+ {
+ if (sample.s > 0) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-1-w] ) );
+ gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-w] ) );
+ if (sample.s < w-1) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+1-w] ) );
+ }
+ if (sample.t < h-1)
+ {
+ if (sample.s > 0) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-1+w] ) );
+ gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+w] ) );
+ if (sample.s < w-1) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+1+w] ) );
+ }
+ if (sample.s > 0) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-1] ) );
+ if (sample.s < w-1) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+1] ) );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ComputeLuxelIntensity...
+//-----------------------------------------------------------------------------
+static inline void ComputeLuxelIntensity( SSE_SampleInfo_t& info, int sampleIdx,
+ LightingValue_t **ppLightSamples, float* pSampleIntensity )
+{
+ // Compute a separate intensity for each
+ sample_t& sample = info.m_pFaceLight->sample[sampleIdx];
+ int destIdx = sample.s + sample.t * info.m_LightmapWidth;
+ for (int n = 0; n < info.m_NormalCount; ++n)
+ {
+ float intensity = ppLightSamples[n][sampleIdx].Intensity();
+
+ // convert to a linear perception space
+ pSampleIntensity[n * info.m_LightmapSize + destIdx] = pow( intensity / 256.0, 1.0 / 2.2 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Compute the maximum intensity based on all bumped lighting
+//-----------------------------------------------------------------------------
+static void ComputeSampleIntensities( SSE_SampleInfo_t& info, LightingValue_t **ppLightSamples, float* pSampleIntensity )
+{
+ for (int i=0; i<info.m_pFaceLight->numsamples; i++)
+ {
+ ComputeLuxelIntensity( info, i, ppLightSamples, pSampleIntensity );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Perform supersampling on a particular lightstyle
+//-----------------------------------------------------------------------------
+static void BuildSupersampleFaceLights( lightinfo_t& l, SSE_SampleInfo_t& info, int lightstyleIndex )
+{
+ LightingValue_t pAmbientLight[NUM_BUMP_VECTS+1];
+ LightingValue_t pDirectLight[NUM_BUMP_VECTS+1];
+
+ // This is used to make sure we don't supersample a light sample more than once
+ int processedSampleSize = info.m_LightmapSize * sizeof(bool);
+ bool* pHasProcessedSample = (bool*)stackalloc( processedSampleSize );
+ memset( pHasProcessedSample, 0, processedSampleSize );
+
+ // This is used to compute a simple gradient computation of the light samples
+ // We're going to store the maximum intensity of all bumped samples at each sample location
+ float* pGradient = (float*)stackalloc( info.m_pFaceLight->numsamples * sizeof(float) );
+ float* pSampleIntensity = (float*)stackalloc( info.m_NormalCount * info.m_LightmapSize * sizeof(float) );
+
+ // Compute the maximum intensity of all lighting associated with this lightstyle
+ // for all bumped lighting
+ LightingValue_t **ppLightSamples = info.m_pFaceLight->light[lightstyleIndex];
+ ComputeSampleIntensities( info, ppLightSamples, pSampleIntensity );
+
+ Vector *pVisualizePass = NULL;
+ if (debug_extra)
+ {
+ int visualizationSize = info.m_pFaceLight->numsamples * sizeof(Vector);
+ pVisualizePass = (Vector*)stackalloc( visualizationSize );
+ memset( pVisualizePass, 0, visualizationSize );
+ }
+
+ // What's going on here is that we're looking for large lighting discontinuities
+ // (large light intensity gradients) as a clue that we should probably be supersampling
+ // in that area. Because the supersampling operation will cause lighting changes,
+ // we've found that it's good to re-check the gradients again and see if any other
+ // areas should be supersampled as a result of the previous pass. Keep going
+ // until all the gradients are reasonable or until we hit a max number of passes
+ bool do_anotherpass = true;
+ int pass = 1;
+ while (do_anotherpass && pass <= extrapasses)
+ {
+ // Look for lighting discontinuities to see what we should be supersampling
+ ComputeLightmapGradients( info, pHasProcessedSample, pSampleIntensity, pGradient );
+
+ do_anotherpass = false;
+
+ // Now check all of the samples and supersample those which we have
+ // marked as having high gradients
+ for (int i=0 ; i<info.m_pFaceLight->numsamples; ++i)
+ {
+ // Don't supersample the same sample twice
+ if (pHasProcessedSample[i])
+ continue;
+
+ // Don't supersample if the lighting is pretty uniform near the sample
+ if (pGradient[i] < 0.0625)
+ continue;
+
+ // Joy! We're supersampling now, and we therefore must do another pass
+ // Also, we need never bother with this sample again
+ pHasProcessedSample[i] = true;
+ do_anotherpass = true;
+
+ if (debug_extra)
+ {
+ // Mark the little visualization bitmap with a color indicating
+ // which pass it was updated on.
+ pVisualizePass[i][0] = (pass & 1) * 255;
+ pVisualizePass[i][1] = (pass & 2) * 128;
+ pVisualizePass[i][2] = (pass & 4) * 64;
+ }
+
+ // Supersample the ambient light for each bump direction vector
+ int ambientSupersampleCount = SupersampleLightAtPoint( l, info, i, lightstyleIndex, pAmbientLight, AMBIENT_ONLY );
+
+ // Supersample the non-ambient light for each bump direction vector
+ int directSupersampleCount = SupersampleLightAtPoint( l, info, i, lightstyleIndex, pDirectLight, NON_AMBIENT_ONLY );
+
+ // Because of sampling problems, small area triangles may have no samples.
+ // In this case, just use what we already have
+ if ( ambientSupersampleCount > 0 && directSupersampleCount > 0 )
+ {
+ // Add the ambient + directional terms together, stick it back into the lightmap
+ for (int n = 0; n < info.m_NormalCount; ++n)
+ {
+ ppLightSamples[n][i].Zero();
+ ppLightSamples[n][i].AddWeighted( pDirectLight[n],1.0f / directSupersampleCount );
+ ppLightSamples[n][i].AddWeighted( pAmbientLight[n], 1.0f / ambientSupersampleCount );
+ }
+
+ // Recompute the luxel intensity based on the supersampling
+ ComputeLuxelIntensity( info, i, ppLightSamples, pSampleIntensity );
+ }
+
+ }
+
+ // We've finished another pass
+ pass++;
+ }
+
+ if (debug_extra)
+ {
+ // Copy colors representing which supersample pass the sample was messed with
+ // into the actual lighting values so we can visualize it
+ for (int i=0 ; i<info.m_pFaceLight->numsamples ; ++i)
+ {
+ for (int j = 0; j <info.m_NormalCount; ++j)
+ {
+ VectorCopy( pVisualizePass[i], ppLightSamples[j][i].m_vecLighting );
+ }
+ }
+ }
+}
+
+void InitLightinfo( lightinfo_t *pl, int facenum )
+{
+ dface_t *f;
+
+ f = &g_pFaces[facenum];
+
+ memset (pl, 0, sizeof(*pl));
+ pl->facenum = facenum;
+
+ pl->face = f;
+
+ //
+ // rotate plane
+ //
+ VectorCopy (dplanes[f->planenum].normal, pl->facenormal);
+ pl->facedist = dplanes[f->planenum].dist;
+
+ // get the origin offset for rotating bmodels
+ VectorCopy (face_offset[facenum], pl->modelorg);
+
+ CalcFaceVectors( pl );
+
+ // figure out if the surface is flat
+ pl->isflat = true;
+ if (smoothing_threshold != 1)
+ {
+ faceneighbor_t *fn = &faceneighbor[facenum];
+
+ for (int j=0 ; j<f->numedges ; j++)
+ {
+ float dot = DotProduct( pl->facenormal, fn->normal[j] );
+ if (dot < 1.0 - EQUAL_EPSILON)
+ {
+ pl->isflat = false;
+ break;
+ }
+ }
+ }
+}
+
+static void InitSampleInfo( lightinfo_t const& l, int iThread, SSE_SampleInfo_t& info )
+{
+ info.m_LightmapWidth = l.face->m_LightmapTextureSizeInLuxels[0]+1;
+ info.m_LightmapHeight = l.face->m_LightmapTextureSizeInLuxels[1]+1;
+ info.m_LightmapSize = info.m_LightmapWidth * info.m_LightmapHeight;
+
+ // How many lightmaps are we going to need?
+ info.m_pTexInfo = &texinfo[l.face->texinfo];
+ info.m_NormalCount = (info.m_pTexInfo->flags & SURF_BUMPLIGHT) ? NUM_BUMP_VECTS + 1 : 1;
+ info.m_FaceNum = l.facenum;
+ info.m_pFace = l.face;
+ info.m_pFaceLight = &facelight[info.m_FaceNum];
+ info.m_IsDispFace = ValidDispFace( info.m_pFace );
+ info.m_iThread = iThread;
+ info.m_WarnFace = -1;
+
+ info.m_NumSamples = info.m_pFaceLight->numsamples;
+ info.m_NumSampleGroups = ( info.m_NumSamples & 0x3) ? ( info.m_NumSamples / 4 ) + 1 : ( info.m_NumSamples / 4 );
+
+ // initialize normals if the surface is flat
+ if (l.isflat)
+ {
+ info.m_PointNormals[0].DuplicateVector( l.facenormal );
+
+ // use facenormal along with the smooth normal to build the three bump map vectors
+ if( info.m_NormalCount > 1 )
+ {
+ Vector bumpVects[NUM_BUMP_VECTS];
+ GetBumpNormals( info.m_pTexInfo->textureVecsTexelsPerWorldUnits[0],
+ info.m_pTexInfo->textureVecsTexelsPerWorldUnits[1], l.facenormal,
+ l.facenormal, bumpVects );//&info.m_PointNormal[1] );
+
+ for ( int b = 0; b < NUM_BUMP_VECTS; ++b )
+ {
+ info.m_PointNormals[b + 1].DuplicateVector( bumpVects[b] );
+ }
+ }
+ }
+}
+
+void BuildFacelights (int iThread, int facenum)
+{
+ int i, j;
+
+ lightinfo_t l;
+ dface_t *f;
+ facelight_t *fl;
+ SSE_SampleInfo_t sampleInfo;
+ directlight_t *dl;
+ Vector spot;
+ Vector v[4], n[4];
+
+ if( g_bInterrupt )
+ return;
+
+ // FIXME: Is there a better way to do this? Like, in RunThreadsOn, for instance?
+ // Don't pay this cost unless we have to; this is super perf-critical code.
+ if (g_pIncremental)
+ {
+ // Both threads will be accessing this so it needs to be protected or else thread A
+ // will load it in and thread B will increment it but its increment will be
+ // overwritten by thread A when thread A writes it back.
+ ThreadLock();
+ ++g_iCurFace;
+ ThreadUnlock();
+ }
+
+ // some surfaces don't need lightmaps
+ f = &g_pFaces[facenum];
+ f->lightofs = -1;
+ for (j=0 ; j<MAXLIGHTMAPS ; j++)
+ f->styles[j] = 255;
+
+ // Trivial-reject the whole face?
+ if( !( g_FacesVisibleToLights[facenum>>3] & (1 << (facenum & 7)) ) )
+ return;
+
+ if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
+ return; // non-lit texture
+
+ // check for patches for this face. If none it must be degenerate. Ignore.
+ if( g_FacePatches.Element( facenum ) == g_FacePatches.InvalidIndex() )
+ return;
+
+ fl = &facelight[facenum];
+
+ InitLightinfo( &l, facenum );
+ CalcPoints( &l, fl, facenum );
+ InitSampleInfo( l, iThread, sampleInfo );
+
+ // Allocate sample positions/normals to SSE
+ int numGroups = ( fl->numsamples & 0x3) ? ( fl->numsamples / 4 ) + 1 : ( fl->numsamples / 4 );
+
+ // always allocate style 0 lightmap
+ f->styles[0] = 0;
+ AllocateLightstyleSamples( fl, 0, sampleInfo.m_NormalCount );
+
+ // sample the lights at each sample location
+ for ( int grp = 0; grp < numGroups; ++grp )
+ {
+ int nSample = 4 * grp;
+
+ sample_t *sample = sampleInfo.m_pFaceLight->sample + nSample;
+ int numSamples = min ( 4, sampleInfo.m_pFaceLight->numsamples - nSample );
+
+ FourVectors positions;
+ FourVectors normals;
+
+ for ( int i = 0; i < 4; i++ )
+ {
+ v[i] = ( i < numSamples ) ? sample[i].pos : sample[numSamples - 1].pos;
+ n[i] = ( i < numSamples ) ? sample[i].normal : sample[numSamples - 1].normal;
+ }
+ positions.LoadAndSwizzle( v[0], v[1], v[2], v[3] );
+ normals.LoadAndSwizzle( n[0], n[1], n[2], n[3] );
+
+ ComputeIlluminationPointAndNormalsSSE( l, positions, normals, &sampleInfo, numSamples );
+
+ // Fixup sample normals in case of smooth faces
+ if ( !l.isflat )
+ {
+ for ( int i = 0; i < numSamples; i++ )
+ sample[i].normal = sampleInfo.m_PointNormals[0].Vec( i );
+ }
+
+ // Iterate over all the lights and add their contribution to this group of spots
+ GatherSampleLightAt4Points( sampleInfo, nSample, numSamples );
+ }
+
+ // Tell the incremental light manager that we're done with this face.
+ if( g_pIncremental )
+ {
+ for (dl = activelights; dl != NULL; dl = dl->next)
+ {
+ // Only deal with lightstyle 0 for incremental lighting
+ if (dl->light.style == 0)
+ g_pIncremental->FinishFace( dl->m_IncrementalID, facenum, iThread );
+ }
+
+ // Don't have to deal with patch lights (only direct lighting is used)
+ // or supersampling
+ return;
+ }
+
+ // get rid of the -extra functionality on displacement surfaces
+ if (do_extra && !sampleInfo.m_IsDispFace)
+ {
+ // For each lightstyle, perform a supersampling pass
+ for ( i = 0; i < MAXLIGHTMAPS; ++i )
+ {
+ // Stop when we run out of lightstyles
+ if (f->styles[i] == 255)
+ break;
+
+ BuildSupersampleFaceLights( l, sampleInfo, i );
+ }
+ }
+
+ if (!g_bUseMPI)
+ {
+ //
+ // This is done on the master node when MPI is used
+ //
+ BuildPatchLights( facenum );
+ }
+
+ if( g_bDumpPatches )
+ {
+ DumpSamples( facenum, fl );
+ }
+ else
+ {
+ FreeSampleWindings( fl );
+ }
+
+}
+
+void BuildPatchLights( int facenum )
+{
+ int i, k;
+
+ CPatch *patch;
+
+ dface_t *f = &g_pFaces[facenum];
+ facelight_t *fl = &facelight[facenum];
+
+ for( k = 0; k < MAXLIGHTMAPS; k++ )
+ {
+ if (f->styles[k] == 0)
+ break;
+ }
+
+ if (k >= MAXLIGHTMAPS)
+ return;
+
+ for (i = 0; i < fl->numsamples; i++)
+ {
+ AddSampleToPatch( &fl->sample[i], fl->light[k][0][i], facenum);
+ }
+
+ // check for a valid face
+ if( g_FacePatches.Element( facenum ) == g_FacePatches.InvalidIndex() )
+ return;
+
+ // push up sampled light to parents (children always exist first in the list)
+ CPatch *pNextPatch;
+ for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNext );
+ }
+
+ // skip patches without parents
+ if( patch->parent == g_Patches.InvalidIndex() )
+// if (patch->parent == -1)
+ continue;
+
+ CPatch *parent = &g_Patches.Element( patch->parent );
+
+ parent->samplearea += patch->samplearea;
+ VectorAdd( parent->samplelight, patch->samplelight, parent->samplelight );
+ }
+
+ // average up the direct light on each patch for radiosity
+ if (numbounce > 0)
+ {
+ for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNext );
+ }
+
+ if (patch->samplearea)
+ {
+ float scale;
+ Vector v;
+ scale = 1.0 / patch->samplearea;
+
+ VectorScale( patch->samplelight, scale, v );
+ VectorAdd( patch->totallight.light[0], v, patch->totallight.light[0] );
+ VectorAdd( patch->directlight, v, patch->directlight );
+ }
+ }
+ }
+
+ // pull totallight from children (children always exist first in the list)
+ for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNext );
+ }
+
+ if ( patch->child1 != g_Patches.InvalidIndex() )
+ {
+ float s1, s2;
+ CPatch *child1;
+ CPatch *child2;
+
+ child1 = &g_Patches.Element( patch->child1 );
+ child2 = &g_Patches.Element( patch->child2 );
+
+ s1 = child1->area / (child1->area + child2->area);
+ s2 = child2->area / (child1->area + child2->area);
+
+ VectorScale( child1->totallight.light[0], s1, patch->totallight.light[0] );
+ VectorMA( patch->totallight.light[0], s2, child2->totallight.light[0], patch->totallight.light[0] );
+
+ VectorCopy( patch->totallight.light[0], patch->directlight );
+ }
+ }
+
+ bool needsBumpmap = false;
+ if( texinfo[f->texinfo].flags & SURF_BUMPLIGHT )
+ {
+ needsBumpmap = true;
+ }
+
+ // add an ambient term if desired
+ if (ambient[0] || ambient[1] || ambient[2])
+ {
+ for( int j=0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++ )
+ {
+ if ( f->styles[j] == 0 )
+ {
+ for (i = 0; i < fl->numsamples; i++)
+ {
+ fl->light[j][0][i].m_vecLighting += ambient;
+ if( needsBumpmap )
+ {
+ fl->light[j][1][i].m_vecLighting += ambient;
+ fl->light[j][2][i].m_vecLighting += ambient;
+ fl->light[j][3][i].m_vecLighting += ambient;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // light from dlight_threshold and above is sent out, but the
+ // texture itself should still be full bright
+
+#if 0
+ // if( VectorAvg( g_FacePatches[facenum]->baselight ) >= dlight_threshold) // Now all lighted surfaces glow
+ {
+ for( j=0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++ )
+ {
+ if ( f->styles[j] == 0 )
+ {
+ // BUG: shouldn't this be done for all patches on the face?
+ for (i=0 ; i<fl->numsamples ; i++)
+ {
+ // garymctchange
+ VectorAdd( fl->light[j][0][i], g_FacePatches[facenum]->baselight, fl->light[j][0][i] );
+ if( needsBumpmap )
+ {
+ for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ VectorAdd( fl->light[j][bumpSample][i], g_FacePatches[facenum]->baselight, fl->light[j][bumpSample][i] );
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+#endif
+}
+
+
+/*
+ =============
+ PrecompLightmapOffsets
+ =============
+*/
+
+void PrecompLightmapOffsets()
+{
+ int facenum;
+ dface_t *f;
+ int lightstyles;
+ int lightdatasize = 0;
+
+ // NOTE: We store avg face light data in this lump *before* the lightmap data itself
+ // in *reverse order* of the way the lightstyles appear in the styles array.
+ for( facenum = 0; facenum < numfaces; facenum++ )
+ {
+ f = &g_pFaces[facenum];
+
+ if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
+ continue; // non-lit texture
+
+ if ( dlight_map != 0 )
+ f->styles[1] = 0;
+
+ for (lightstyles=0; lightstyles < MAXLIGHTMAPS; lightstyles++ )
+ {
+ if ( f->styles[lightstyles] == 255 )
+ break;
+ }
+
+ if ( !lightstyles )
+ continue;
+
+ // Reserve room for the avg light color data
+ lightdatasize += lightstyles * 4;
+
+ f->lightofs = lightdatasize;
+
+ bool needsBumpmap = false;
+ if( texinfo[f->texinfo].flags & SURF_BUMPLIGHT )
+ {
+ needsBumpmap = true;
+ }
+
+ int nLuxels = (f->m_LightmapTextureSizeInLuxels[0]+1) * (f->m_LightmapTextureSizeInLuxels[1]+1);
+ if( needsBumpmap )
+ {
+ lightdatasize += nLuxels * 4 * lightstyles * ( NUM_BUMP_VECTS + 1 );
+ }
+ else
+ {
+ lightdatasize += nLuxels * 4 * lightstyles;
+ }
+ }
+
+ // The incremental lighting code needs us to preserve the contents of dlightdata
+ // since it only recomposites lighting for faces that have lights that touch them.
+ if( g_pIncremental && pdlightdata->Count() )
+ return;
+
+ pdlightdata->SetSize( lightdatasize );
+}
+
+// Clamp the three values for bumped lighting such that we trade off directionality for brightness.
+static void ColorClampBumped( Vector& color1, Vector& color2, Vector& color3 )
+{
+ Vector maxs;
+ Vector *colors[3] = { &color1, &color2, &color3 };
+ maxs[0] = VectorMaximum( color1 );
+ maxs[1] = VectorMaximum( color2 );
+ maxs[2] = VectorMaximum( color3 );
+
+ // HACK! Clean this up, and add some else statements
+#define CONDITION(a,b,c) do { if( maxs[a] >= maxs[b] && maxs[b] >= maxs[c] ) { order[0] = a; order[1] = b; order[2] = c; } } while( 0 )
+
+ int order[3];
+ CONDITION(0,1,2);
+ CONDITION(0,2,1);
+ CONDITION(1,0,2);
+ CONDITION(1,2,0);
+ CONDITION(2,0,1);
+ CONDITION(2,1,0);
+
+ int i;
+ for( i = 0; i < 3; i++ )
+ {
+ float max = VectorMaximum( *colors[order[i]] );
+ if( max <= 1.0f )
+ {
+ continue;
+ }
+ // This channel is too bright. . take half of the amount that we are over and
+ // add it to the other two channel.
+ float factorToRedist = ( max - 1.0f ) / max;
+ Vector colorToRedist = factorToRedist * *colors[order[i]];
+ *colors[order[i]] -= colorToRedist;
+ colorToRedist *= 0.5f;
+ *colors[order[(i+1)%3]] += colorToRedist;
+ *colors[order[(i+2)%3]] += colorToRedist;
+ }
+
+ ColorClamp( color1 );
+ ColorClamp( color2 );
+ ColorClamp( color3 );
+
+ if( color1[0] < 0.f ) color1[0] = 0.f;
+ if( color1[1] < 0.f ) color1[1] = 0.f;
+ if( color1[2] < 0.f ) color1[2] = 0.f;
+ if( color2[0] < 0.f ) color2[0] = 0.f;
+ if( color2[1] < 0.f ) color2[1] = 0.f;
+ if( color2[2] < 0.f ) color2[2] = 0.f;
+ if( color3[0] < 0.f ) color3[0] = 0.f;
+ if( color3[1] < 0.f ) color3[1] = 0.f;
+ if( color3[2] < 0.f ) color3[2] = 0.f;
+}
+
+static void LinearToBumpedLightmap(
+ const float *linearColor,
+ const float *linearBumpColor1,
+ const float *linearBumpColor2,
+ const float *linearBumpColor3,
+ unsigned char *ret,
+ unsigned char *retBump1,
+ unsigned char *retBump2,
+ unsigned char *retBump3 )
+{
+ const Vector &linearBump1 = *( ( const Vector * )linearBumpColor1 );
+ const Vector &linearBump2 = *( ( const Vector * )linearBumpColor2 );
+ const Vector &linearBump3 = *( ( const Vector * )linearBumpColor3 );
+
+ Vector gammaGoal;
+ // gammaGoal is premultiplied by 1/overbright, which we want
+ gammaGoal[0] = LinearToVertexLight( linearColor[0] );
+ gammaGoal[1] = LinearToVertexLight( linearColor[1] );
+ gammaGoal[2] = LinearToVertexLight( linearColor[2] );
+ Vector bumpAverage = linearBump1;
+ bumpAverage += linearBump2;
+ bumpAverage += linearBump3;
+ bumpAverage *= ( 1.0f / 3.0f );
+
+ Vector correctionScale;
+ if( *( int * )&bumpAverage[0] != 0 && *( int * )&bumpAverage[1] != 0 && *( int * )&bumpAverage[2] != 0 )
+ {
+ // fast path when we know that we don't have to worry about divide by zero.
+ VectorDivide( gammaGoal, bumpAverage, correctionScale );
+// correctionScale = gammaGoal / bumpSum;
+ }
+ else
+ {
+ correctionScale.Init( 0.0f, 0.0f, 0.0f );
+ if( bumpAverage[0] != 0.0f )
+ {
+ correctionScale[0] = gammaGoal[0] / bumpAverage[0];
+ }
+ if( bumpAverage[1] != 0.0f )
+ {
+ correctionScale[1] = gammaGoal[1] / bumpAverage[1];
+ }
+ if( bumpAverage[2] != 0.0f )
+ {
+ correctionScale[2] = gammaGoal[2] / bumpAverage[2];
+ }
+ }
+ Vector correctedBumpColor1;
+ Vector correctedBumpColor2;
+ Vector correctedBumpColor3;
+ VectorMultiply( linearBump1, correctionScale, correctedBumpColor1 );
+ VectorMultiply( linearBump2, correctionScale, correctedBumpColor2 );
+ VectorMultiply( linearBump3, correctionScale, correctedBumpColor3 );
+
+ Vector check = ( correctedBumpColor1 + correctedBumpColor2 + correctedBumpColor3 ) / 3.0f;
+
+ ColorClampBumped( correctedBumpColor1, correctedBumpColor2, correctedBumpColor3 );
+
+ ret[0] = RoundFloatToByte( gammaGoal[0] * 255.0f );
+ ret[1] = RoundFloatToByte( gammaGoal[1] * 255.0f );
+ ret[2] = RoundFloatToByte( gammaGoal[2] * 255.0f );
+ retBump1[0] = RoundFloatToByte( correctedBumpColor1[0] * 255.0f );
+ retBump1[1] = RoundFloatToByte( correctedBumpColor1[1] * 255.0f );
+ retBump1[2] = RoundFloatToByte( correctedBumpColor1[2] * 255.0f );
+ retBump2[0] = RoundFloatToByte( correctedBumpColor2[0] * 255.0f );
+ retBump2[1] = RoundFloatToByte( correctedBumpColor2[1] * 255.0f );
+ retBump2[2] = RoundFloatToByte( correctedBumpColor2[2] * 255.0f );
+ retBump3[0] = RoundFloatToByte( correctedBumpColor3[0] * 255.0f );
+ retBump3[1] = RoundFloatToByte( correctedBumpColor3[1] * 255.0f );
+ retBump3[2] = RoundFloatToByte( correctedBumpColor3[2] * 255.0f );
+}
+
+//-----------------------------------------------------------------------------
+// Convert a RGBExp32 to a RGBA8888
+// This matches the engine's conversion, so the lighting result is consistent.
+//-----------------------------------------------------------------------------
+void ConvertRGBExp32ToRGBA8888( const ColorRGBExp32 *pSrc, unsigned char *pDst )
+{
+ Vector linearColor;
+ Vector vertexColor;
+
+ // convert from ColorRGBExp32 to linear space
+ linearColor[0] = TexLightToLinear( ((ColorRGBExp32 *)pSrc)->r, ((ColorRGBExp32 *)pSrc)->exponent );
+ linearColor[1] = TexLightToLinear( ((ColorRGBExp32 *)pSrc)->g, ((ColorRGBExp32 *)pSrc)->exponent );
+ linearColor[2] = TexLightToLinear( ((ColorRGBExp32 *)pSrc)->b, ((ColorRGBExp32 *)pSrc)->exponent );
+
+ // convert from linear space to lightmap space
+ // cannot use mathlib routine directly because it doesn't match
+ // the colorspace version found in the engine, which *is* the same sequence here
+ vertexColor[0] = LinearToVertexLight( linearColor[0] );
+ vertexColor[1] = LinearToVertexLight( linearColor[1] );
+ vertexColor[2] = LinearToVertexLight( linearColor[2] );
+
+ // this is really a color normalization with a floor
+ ColorClamp( vertexColor );
+
+ // final [0..255] scale
+ pDst[0] = RoundFloatToByte( vertexColor[0] * 255.0f );
+ pDst[1] = RoundFloatToByte( vertexColor[1] * 255.0f );
+ pDst[2] = RoundFloatToByte( vertexColor[2] * 255.0f );
+ pDst[3] = 255;
+}
+
diff --git a/mp/src/utils/vrad/lightmap.h b/mp/src/utils/vrad/lightmap.h
index 0912c43f..a4c698da 100644
--- a/mp/src/utils/vrad/lightmap.h
+++ b/mp/src/utils/vrad/lightmap.h
@@ -1,141 +1,141 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $Workfile: $
-// $Date: $
-//
-//-----------------------------------------------------------------------------
-// $Log: $
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef LIGHTMAP_H
-#define LIGHTMAP_H
-#pragma once
-
-#include "mathlib/bumpvects.h"
-#include "bsplib.h"
-
-typedef struct
-{
- dface_t *faces[2];
- Vector interface_normal;
- qboolean coplanar;
-} edgeshare_t;
-
-extern edgeshare_t edgeshare[MAX_MAP_EDGES];
-
-
-//==============================================
-
-// This is incremented each time BuildFaceLights and FinalLightFace
-// are called. It's used for a status bar in WorldCraft.
-extern int g_iCurFace;
-
-extern int vertexref[MAX_MAP_VERTS];
-extern int *vertexface[MAX_MAP_VERTS];
-
-struct faceneighbor_t
-{
- int numneighbors; // neighboring faces that share vertices
- int *neighbor; // neighboring face list (max of 64)
-
- Vector *normal; // adjusted normal per vertex
- Vector facenormal; // face normal
-
- bool bHasDisp; // is this surface a displacement surface???
-};
-
-extern faceneighbor_t faceneighbor[MAX_MAP_FACES];
-
-//==============================================
-
-
-struct sample_t
-{
- // in local luxel space
- winding_t *w;
- int s, t;
- Vector2D coord;
- Vector2D mins;
- Vector2D maxs;
- // in world units
- Vector pos;
- Vector normal;
- float area;
-};
-
-struct facelight_t
-{
- // irregularly shaped light sample data, clipped by face and luxel grid
- int numsamples;
- sample_t *sample;
- LightingValue_t *light[MAXLIGHTMAPS][NUM_BUMP_VECTS+1]; // result of direct illumination, indexed by sample
-
- // regularly spaced lightmap grid
- int numluxels;
- Vector *luxel; // world space position of luxel
- Vector *luxelNormals; // world space normal of luxel
- float worldAreaPerLuxel;
-};
-
-extern directlight_t *activelights;
-extern directlight_t *freelights;
-
-extern facelight_t facelight[MAX_MAP_FACES];
-extern int numdlights;
-
-
-//==============================================
-
-struct lightinfo_t
-{
- vec_t facedist;
- Vector facenormal;
-
- Vector facemid; // world coordinates of center
-
- Vector modelorg; // for origined bmodels
-
- Vector luxelOrigin;
- Vector worldToLuxelSpace[2]; // s = (world - luxelOrigin) . worldToLuxelSpace[0], t = (world - luxelOrigin) . worldToLuxelSpace[1]
- Vector luxelToWorldSpace[2]; // world = luxelOrigin + s * luxelToWorldSpace[0] + t * luxelToWorldSpace[1]
-
- int facenum;
- dface_t *face;
-
- int isflat;
- int hasbumpmap;
-};
-
-struct SSE_SampleInfo_t
-{
- int m_FaceNum;
- int m_WarnFace;
- dface_t *m_pFace;
- facelight_t *m_pFaceLight;
- int m_LightmapWidth;
- int m_LightmapHeight;
- int m_LightmapSize;
- int m_NormalCount;
- int m_iThread;
- texinfo_t *m_pTexInfo;
- bool m_IsDispFace;
-
- int m_NumSamples;
- int m_NumSampleGroups;
- int m_Clusters[4];
- FourVectors m_Points;
- FourVectors m_PointNormals[ NUM_BUMP_VECTS + 1 ];
-};
-
-extern void InitLightinfo( lightinfo_t *l, int facenum );
-
-void FreeDLights();
-
-void ExportDirectLightsToWorldLights();
-
-
-#endif // LIGHTMAP_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef LIGHTMAP_H
+#define LIGHTMAP_H
+#pragma once
+
+#include "mathlib/bumpvects.h"
+#include "bsplib.h"
+
+typedef struct
+{
+ dface_t *faces[2];
+ Vector interface_normal;
+ qboolean coplanar;
+} edgeshare_t;
+
+extern edgeshare_t edgeshare[MAX_MAP_EDGES];
+
+
+//==============================================
+
+// This is incremented each time BuildFaceLights and FinalLightFace
+// are called. It's used for a status bar in WorldCraft.
+extern int g_iCurFace;
+
+extern int vertexref[MAX_MAP_VERTS];
+extern int *vertexface[MAX_MAP_VERTS];
+
+struct faceneighbor_t
+{
+ int numneighbors; // neighboring faces that share vertices
+ int *neighbor; // neighboring face list (max of 64)
+
+ Vector *normal; // adjusted normal per vertex
+ Vector facenormal; // face normal
+
+ bool bHasDisp; // is this surface a displacement surface???
+};
+
+extern faceneighbor_t faceneighbor[MAX_MAP_FACES];
+
+//==============================================
+
+
+struct sample_t
+{
+ // in local luxel space
+ winding_t *w;
+ int s, t;
+ Vector2D coord;
+ Vector2D mins;
+ Vector2D maxs;
+ // in world units
+ Vector pos;
+ Vector normal;
+ float area;
+};
+
+struct facelight_t
+{
+ // irregularly shaped light sample data, clipped by face and luxel grid
+ int numsamples;
+ sample_t *sample;
+ LightingValue_t *light[MAXLIGHTMAPS][NUM_BUMP_VECTS+1]; // result of direct illumination, indexed by sample
+
+ // regularly spaced lightmap grid
+ int numluxels;
+ Vector *luxel; // world space position of luxel
+ Vector *luxelNormals; // world space normal of luxel
+ float worldAreaPerLuxel;
+};
+
+extern directlight_t *activelights;
+extern directlight_t *freelights;
+
+extern facelight_t facelight[MAX_MAP_FACES];
+extern int numdlights;
+
+
+//==============================================
+
+struct lightinfo_t
+{
+ vec_t facedist;
+ Vector facenormal;
+
+ Vector facemid; // world coordinates of center
+
+ Vector modelorg; // for origined bmodels
+
+ Vector luxelOrigin;
+ Vector worldToLuxelSpace[2]; // s = (world - luxelOrigin) . worldToLuxelSpace[0], t = (world - luxelOrigin) . worldToLuxelSpace[1]
+ Vector luxelToWorldSpace[2]; // world = luxelOrigin + s * luxelToWorldSpace[0] + t * luxelToWorldSpace[1]
+
+ int facenum;
+ dface_t *face;
+
+ int isflat;
+ int hasbumpmap;
+};
+
+struct SSE_SampleInfo_t
+{
+ int m_FaceNum;
+ int m_WarnFace;
+ dface_t *m_pFace;
+ facelight_t *m_pFaceLight;
+ int m_LightmapWidth;
+ int m_LightmapHeight;
+ int m_LightmapSize;
+ int m_NormalCount;
+ int m_iThread;
+ texinfo_t *m_pTexInfo;
+ bool m_IsDispFace;
+
+ int m_NumSamples;
+ int m_NumSampleGroups;
+ int m_Clusters[4];
+ FourVectors m_Points;
+ FourVectors m_PointNormals[ NUM_BUMP_VECTS + 1 ];
+};
+
+extern void InitLightinfo( lightinfo_t *l, int facenum );
+
+void FreeDLights();
+
+void ExportDirectLightsToWorldLights();
+
+
+#endif // LIGHTMAP_H
diff --git a/mp/src/utils/vrad/macro_texture.cpp b/mp/src/utils/vrad/macro_texture.cpp
index 8511d979..cd0eac12 100644
--- a/mp/src/utils/vrad/macro_texture.cpp
+++ b/mp/src/utils/vrad/macro_texture.cpp
@@ -1,166 +1,166 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "tier1/strtools.h"
-#include "macro_texture.h"
-#include "bsplib.h"
-#include "cmdlib.h"
-#include "vtf/vtf.h"
-#include "tier1/utldict.h"
-#include "tier1/utlbuffer.h"
-#include "bitmap/imageformat.h"
-
-
-class CMacroTextureData
-{
-public:
- int m_Width, m_Height;
- CUtlMemory<unsigned char> m_ImageData;
-};
-
-
-CMacroTextureData *g_pGlobalMacroTextureData = NULL;
-
-// Which macro texture each map face uses.
-static CUtlDict<CMacroTextureData*, int> g_MacroTextureLookup; // Stores a list of unique macro textures.
-static CUtlVector<CMacroTextureData*> g_FaceMacroTextures; // Which macro texture each face wants to use.
-static Vector g_MacroWorldMins, g_MacroWorldMaxs;
-
-
-CMacroTextureData* FindMacroTexture( const char *pFilename )
-{
- int index = g_MacroTextureLookup.Find( pFilename );
- if ( g_MacroTextureLookup.IsValidIndex( index ) )
- return g_MacroTextureLookup[index];
- else
- return NULL;
-}
-
-
-CMacroTextureData* LoadMacroTextureFile( const char *pFilename )
-{
- FileHandle_t hFile = g_pFileSystem->Open( pFilename, "rb" );
- if ( hFile == FILESYSTEM_INVALID_HANDLE )
- return NULL;
-
- // Read the file in.
- CUtlVector<char> tempData;
- tempData.SetSize( g_pFileSystem->Size( hFile ) );
- g_pFileSystem->Read( tempData.Base(), tempData.Count(), hFile );
- g_pFileSystem->Close( hFile );
-
-
- // Now feed the data into a CUtlBuffer (great...)
- CUtlBuffer buf;
- buf.Put( tempData.Base(), tempData.Count() );
-
-
- // Now make a texture out of it.
- IVTFTexture *pTex = CreateVTFTexture();
- if ( !pTex->Unserialize( buf ) )
- Error( "IVTFTexture::Unserialize( %s ) failed.", pFilename );
-
- pTex->ConvertImageFormat( IMAGE_FORMAT_RGBA8888, false ); // Get it in a format we like.
-
-
- // Now convert to a CMacroTextureData.
- CMacroTextureData *pData = new CMacroTextureData;
- pData->m_Width = pTex->Width();
- pData->m_Height = pTex->Height();
- pData->m_ImageData.EnsureCapacity( pData->m_Width * pData->m_Height * 4 );
- memcpy( pData->m_ImageData.Base(), pTex->ImageData(), pData->m_Width * pData->m_Height * 4 );
-
- DestroyVTFTexture( pTex );
-
- Msg( "-- LoadMacroTextureFile: %s\n", pFilename );
- return pData;
-}
-
-
-void InitMacroTexture( const char *pBSPFilename )
-{
- // Get the world bounds (same ones used by minimaps and level designers know how to use).
- int i = 0;
- for (i; i < num_entities; ++i)
- {
- char* pEntity = ValueForKey(&entities[i], "classname");
- if( !strcmp(pEntity, "worldspawn") )
- {
- GetVectorForKey( &entities[i], "world_mins", g_MacroWorldMins );
- GetVectorForKey( &entities[i], "world_maxs", g_MacroWorldMaxs );
- break;
- }
- }
-
- if ( i == num_entities )
- {
- Warning( "MaskOnMacroTexture: can't find worldspawn" );
- return;
- }
-
-
- // Load the macro texture that is mapped onto everything.
- char mapName[512], vtfFilename[512];
- Q_FileBase( pBSPFilename, mapName, sizeof( mapName ) );
- Q_snprintf( vtfFilename, sizeof( vtfFilename ), "materials/macro/%s/base.vtf", mapName );
- g_pGlobalMacroTextureData = LoadMacroTextureFile( vtfFilename );
-
-
- // Now load the macro texture for each face.
- g_FaceMacroTextures.SetSize( numfaces );
- for ( int iFace=0; iFace < numfaces; iFace++ )
- {
- g_FaceMacroTextures[iFace] = NULL;
-
- if ( iFace < g_FaceMacroTextureInfos.Count() )
- {
- unsigned short stringID = g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID;
- if ( stringID != 0xFFFF )
- {
- const char *pMacroTextureName = &g_TexDataStringData[ g_TexDataStringTable[stringID] ];
- Q_snprintf( vtfFilename, sizeof( vtfFilename ), "%smaterials/%s.vtf", gamedir, pMacroTextureName );
-
- g_FaceMacroTextures[iFace] = FindMacroTexture( vtfFilename );
- if ( !g_FaceMacroTextures[iFace] )
- {
- g_FaceMacroTextures[iFace] = LoadMacroTextureFile( vtfFilename );
- if ( g_FaceMacroTextures[iFace] )
- {
- g_MacroTextureLookup.Insert( vtfFilename, g_FaceMacroTextures[iFace] );
- }
- }
- }
- }
- }
-}
-
-
-inline Vector SampleMacroTexture( const CMacroTextureData *t, const Vector &vWorldPos )
-{
- int ix = (int)RemapVal( vWorldPos.x, g_MacroWorldMins.x, g_MacroWorldMaxs.x, 0, t->m_Width-0.00001 );
- int iy = (int)RemapVal( vWorldPos.y, g_MacroWorldMins.y, g_MacroWorldMaxs.y, 0, t->m_Height-0.00001 );
- ix = clamp( ix, 0, t->m_Width-1 );
- iy = t->m_Height - 1 - clamp( iy, 0, t->m_Height-1 );
-
- const unsigned char *pInputColor = &t->m_ImageData[(iy*t->m_Width + ix) * 4];
- return Vector( pInputColor[0] / 255.0, pInputColor[1] / 255.0, pInputColor[2] / 255.0 );
-}
-
-
-void ApplyMacroTextures( int iFace, const Vector &vWorldPos, Vector &outLuxel )
-{
- // Add the global macro texture.
- Vector vGlobal;
- if ( g_pGlobalMacroTextureData )
- outLuxel *= SampleMacroTexture( g_pGlobalMacroTextureData, vWorldPos );
-
- // Now add the per-material macro texture.
- if ( g_FaceMacroTextures[iFace] )
- outLuxel *= SampleMacroTexture( g_FaceMacroTextures[iFace], vWorldPos );
-}
-
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "tier1/strtools.h"
+#include "macro_texture.h"
+#include "bsplib.h"
+#include "cmdlib.h"
+#include "vtf/vtf.h"
+#include "tier1/utldict.h"
+#include "tier1/utlbuffer.h"
+#include "bitmap/imageformat.h"
+
+
+class CMacroTextureData
+{
+public:
+ int m_Width, m_Height;
+ CUtlMemory<unsigned char> m_ImageData;
+};
+
+
+CMacroTextureData *g_pGlobalMacroTextureData = NULL;
+
+// Which macro texture each map face uses.
+static CUtlDict<CMacroTextureData*, int> g_MacroTextureLookup; // Stores a list of unique macro textures.
+static CUtlVector<CMacroTextureData*> g_FaceMacroTextures; // Which macro texture each face wants to use.
+static Vector g_MacroWorldMins, g_MacroWorldMaxs;
+
+
+CMacroTextureData* FindMacroTexture( const char *pFilename )
+{
+ int index = g_MacroTextureLookup.Find( pFilename );
+ if ( g_MacroTextureLookup.IsValidIndex( index ) )
+ return g_MacroTextureLookup[index];
+ else
+ return NULL;
+}
+
+
+CMacroTextureData* LoadMacroTextureFile( const char *pFilename )
+{
+ FileHandle_t hFile = g_pFileSystem->Open( pFilename, "rb" );
+ if ( hFile == FILESYSTEM_INVALID_HANDLE )
+ return NULL;
+
+ // Read the file in.
+ CUtlVector<char> tempData;
+ tempData.SetSize( g_pFileSystem->Size( hFile ) );
+ g_pFileSystem->Read( tempData.Base(), tempData.Count(), hFile );
+ g_pFileSystem->Close( hFile );
+
+
+ // Now feed the data into a CUtlBuffer (great...)
+ CUtlBuffer buf;
+ buf.Put( tempData.Base(), tempData.Count() );
+
+
+ // Now make a texture out of it.
+ IVTFTexture *pTex = CreateVTFTexture();
+ if ( !pTex->Unserialize( buf ) )
+ Error( "IVTFTexture::Unserialize( %s ) failed.", pFilename );
+
+ pTex->ConvertImageFormat( IMAGE_FORMAT_RGBA8888, false ); // Get it in a format we like.
+
+
+ // Now convert to a CMacroTextureData.
+ CMacroTextureData *pData = new CMacroTextureData;
+ pData->m_Width = pTex->Width();
+ pData->m_Height = pTex->Height();
+ pData->m_ImageData.EnsureCapacity( pData->m_Width * pData->m_Height * 4 );
+ memcpy( pData->m_ImageData.Base(), pTex->ImageData(), pData->m_Width * pData->m_Height * 4 );
+
+ DestroyVTFTexture( pTex );
+
+ Msg( "-- LoadMacroTextureFile: %s\n", pFilename );
+ return pData;
+}
+
+
+void InitMacroTexture( const char *pBSPFilename )
+{
+ // Get the world bounds (same ones used by minimaps and level designers know how to use).
+ int i = 0;
+ for (i; i < num_entities; ++i)
+ {
+ char* pEntity = ValueForKey(&entities[i], "classname");
+ if( !strcmp(pEntity, "worldspawn") )
+ {
+ GetVectorForKey( &entities[i], "world_mins", g_MacroWorldMins );
+ GetVectorForKey( &entities[i], "world_maxs", g_MacroWorldMaxs );
+ break;
+ }
+ }
+
+ if ( i == num_entities )
+ {
+ Warning( "MaskOnMacroTexture: can't find worldspawn" );
+ return;
+ }
+
+
+ // Load the macro texture that is mapped onto everything.
+ char mapName[512], vtfFilename[512];
+ Q_FileBase( pBSPFilename, mapName, sizeof( mapName ) );
+ Q_snprintf( vtfFilename, sizeof( vtfFilename ), "materials/macro/%s/base.vtf", mapName );
+ g_pGlobalMacroTextureData = LoadMacroTextureFile( vtfFilename );
+
+
+ // Now load the macro texture for each face.
+ g_FaceMacroTextures.SetSize( numfaces );
+ for ( int iFace=0; iFace < numfaces; iFace++ )
+ {
+ g_FaceMacroTextures[iFace] = NULL;
+
+ if ( iFace < g_FaceMacroTextureInfos.Count() )
+ {
+ unsigned short stringID = g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID;
+ if ( stringID != 0xFFFF )
+ {
+ const char *pMacroTextureName = &g_TexDataStringData[ g_TexDataStringTable[stringID] ];
+ Q_snprintf( vtfFilename, sizeof( vtfFilename ), "%smaterials/%s.vtf", gamedir, pMacroTextureName );
+
+ g_FaceMacroTextures[iFace] = FindMacroTexture( vtfFilename );
+ if ( !g_FaceMacroTextures[iFace] )
+ {
+ g_FaceMacroTextures[iFace] = LoadMacroTextureFile( vtfFilename );
+ if ( g_FaceMacroTextures[iFace] )
+ {
+ g_MacroTextureLookup.Insert( vtfFilename, g_FaceMacroTextures[iFace] );
+ }
+ }
+ }
+ }
+ }
+}
+
+
+inline Vector SampleMacroTexture( const CMacroTextureData *t, const Vector &vWorldPos )
+{
+ int ix = (int)RemapVal( vWorldPos.x, g_MacroWorldMins.x, g_MacroWorldMaxs.x, 0, t->m_Width-0.00001 );
+ int iy = (int)RemapVal( vWorldPos.y, g_MacroWorldMins.y, g_MacroWorldMaxs.y, 0, t->m_Height-0.00001 );
+ ix = clamp( ix, 0, t->m_Width-1 );
+ iy = t->m_Height - 1 - clamp( iy, 0, t->m_Height-1 );
+
+ const unsigned char *pInputColor = &t->m_ImageData[(iy*t->m_Width + ix) * 4];
+ return Vector( pInputColor[0] / 255.0, pInputColor[1] / 255.0, pInputColor[2] / 255.0 );
+}
+
+
+void ApplyMacroTextures( int iFace, const Vector &vWorldPos, Vector &outLuxel )
+{
+ // Add the global macro texture.
+ Vector vGlobal;
+ if ( g_pGlobalMacroTextureData )
+ outLuxel *= SampleMacroTexture( g_pGlobalMacroTextureData, vWorldPos );
+
+ // Now add the per-material macro texture.
+ if ( g_FaceMacroTextures[iFace] )
+ outLuxel *= SampleMacroTexture( g_FaceMacroTextures[iFace], vWorldPos );
+}
+
+
+
diff --git a/mp/src/utils/vrad/macro_texture.h b/mp/src/utils/vrad/macro_texture.h
index 249c2474..bfae258a 100644
--- a/mp/src/utils/vrad/macro_texture.h
+++ b/mp/src/utils/vrad/macro_texture.h
@@ -1,24 +1,24 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#ifndef MACRO_TEXTURE_H
-#define MACRO_TEXTURE_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-#include "mathlib/vector.h"
-
-
-// The macro texture looks for a TGA file with the same name as the BSP file and in
-// the same directory. If it finds one, it maps this texture onto the world dimensions
-// (in the worldspawn entity) and masks all lightmaps with it.
-void InitMacroTexture( const char *pBSPFilename );
-void ApplyMacroTextures( int iFace, const Vector &vWorldPos, Vector &outLuxel );
-
-
-#endif // MACRO_TEXTURE_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef MACRO_TEXTURE_H
+#define MACRO_TEXTURE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "mathlib/vector.h"
+
+
+// The macro texture looks for a TGA file with the same name as the BSP file and in
+// the same directory. If it finds one, it maps this texture onto the world dimensions
+// (in the worldspawn entity) and masks all lightmaps with it.
+void InitMacroTexture( const char *pBSPFilename );
+void ApplyMacroTextures( int iFace, const Vector &vWorldPos, Vector &outLuxel );
+
+
+#endif // MACRO_TEXTURE_H
diff --git a/mp/src/utils/vrad/mpivrad.cpp b/mp/src/utils/vrad/mpivrad.cpp
index d54dfaeb..5b7bfc03 100644
--- a/mp/src/utils/vrad/mpivrad.cpp
+++ b/mp/src/utils/vrad/mpivrad.cpp
@@ -1,496 +1,496 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-//
-// mpivrad.cpp
-//
-
-#include <windows.h>
-#include <conio.h>
-#include "vrad.h"
-#include "physdll.h"
-#include "lightmap.h"
-#include "tier1/strtools.h"
-#include "radial.h"
-#include "utlbuffer.h"
-#include "pacifier.h"
-#include "messbuf.h"
-#include "bsplib.h"
-#include "consolewnd.h"
-#include "vismat.h"
-#include "vmpi_filesystem.h"
-#include "vmpi_dispatch.h"
-#include "utllinkedlist.h"
-#include "vmpi.h"
-#include "mpi_stats.h"
-#include "vmpi_distribute_work.h"
-#include "vmpi_tools_shared.h"
-
-
-
-
-CUtlVector<char> g_LightResultsFilename;
-
-
-extern int total_transfer;
-extern int max_transfer;
-
-extern void BuildVisLeafs(int);
-extern void BuildPatchLights( int facenum );
-
-
-// Handle VRAD packets.
-bool VRAD_DispatchFn( MessageBuffer *pBuf, int iSource, int iPacketID )
-{
- switch( pBuf->data[1] )
- {
- case VMPI_SUBPACKETID_PLIGHTDATA_RESULTS:
- {
- const char *pFilename = &pBuf->data[2];
- g_LightResultsFilename.CopyArray( pFilename, strlen( pFilename ) + 1 );
- return true;
- }
-
- default:
- return false;
- }
-}
-CDispatchReg g_VRADDispatchReg( VMPI_VRAD_PACKET_ID, VRAD_DispatchFn ); // register to handle the messages we want
-CDispatchReg g_DistributeWorkReg( VMPI_DISTRIBUTEWORK_PACKETID, DistributeWorkDispatch );
-
-
-
-void VRAD_SetupMPI( int &argc, char **&argv )
-{
- CmdLib_AtCleanup( VMPI_Stats_Term );
-
- //
- // Preliminary check -mpi flag
- //
- if ( !VMPI_FindArg( argc, argv, "-mpi", "" ) && !VMPI_FindArg( argc, argv, VMPI_GetParamString( mpi_Worker ), "" ) )
- return;
-
- // Force local mode?
- VMPIRunMode mode;
- if ( VMPI_FindArg( argc, argv, VMPI_GetParamString( mpi_Local ), "" ) )
- mode = VMPI_RUN_LOCAL;
- else
- mode = VMPI_RUN_NETWORKED;
-
- VMPI_Stats_InstallSpewHook();
-
- //
- // Extract mpi specific arguments
- //
- Msg( "Initializing VMPI...\n" );
- if ( !VMPI_Init(
- argc,
- argv,
- "dependency_info_vrad.txt",
- HandleMPIDisconnect,
- mode
- ) )
- {
- Error( "MPI_Init failed." );
- }
-
- StatsDB_InitStatsDatabase( argc, argv, "dbinfo_vrad.txt" );
-}
-
-
-//-----------------------------------------
-//
-// Run BuildFaceLights across all available processing nodes
-// and collect the results.
-//
-
-CCycleCount g_CPUTime;
-
-
-template<class T> void WriteValues( MessageBuffer *pmb, T const *pSrc, int nNumValues)
-{
- pmb->write(pSrc, sizeof( pSrc[0]) * nNumValues );
-}
-
-template<class T> int ReadValues( MessageBuffer *pmb, T *pDest, int nNumValues)
-{
- return pmb->read( pDest, sizeof( pDest[0]) * nNumValues );
-}
-
-
-//--------------------------------------------------
-// Serialize face data
-void SerializeFace( MessageBuffer * pmb, int facenum )
-{
- int i, n;
-
- dface_t * f = &g_pFaces[facenum];
- facelight_t * fl = &facelight[facenum];
-
- pmb->write(f, sizeof(dface_t));
- pmb->write(fl, sizeof(facelight_t));
-
- WriteValues( pmb, fl->sample, fl->numsamples);
-
- //
- // Write the light information
- //
- for (i=0; i<MAXLIGHTMAPS; ++i) {
- for (n=0; n<NUM_BUMP_VECTS+1; ++n) {
- if (fl->light[i][n])
- {
- WriteValues( pmb, fl->light[i][n], fl->numsamples);
- }
- }
- }
-
- if (fl->luxel)
- WriteValues( pmb, fl->luxel, fl->numluxels);
-
- if (fl->luxelNormals)
- WriteValues( pmb, fl->luxelNormals, fl->numluxels);
-}
-
-//--------------------------------------------------
-// UnSerialize face data
-//
-void UnSerializeFace( MessageBuffer * pmb, int facenum, int iSource )
-{
- int i, n;
-
- dface_t * f = &g_pFaces[facenum];
- facelight_t * fl = &facelight[facenum];
-
- if (pmb->read(f, sizeof(dface_t)) < 0)
- Error("UnSerializeFace - invalid dface_t from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() );
-
- if (pmb->read(fl, sizeof(facelight_t)) < 0)
- Error("UnSerializeFace - invalid facelight_t from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() );
-
- fl->sample = (sample_t *) calloc(fl->numsamples, sizeof(sample_t));
- if (pmb->read(fl->sample, sizeof(sample_t) * fl->numsamples) < 0)
- Error("UnSerializeFace - invalid sample_t from %s (mb len: %d, offset: %d, fl->numsamples: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset(), fl->numsamples );
-
- //
- // Read the light information
- //
- for (i=0; i<MAXLIGHTMAPS; ++i) {
- for (n=0; n<NUM_BUMP_VECTS+1; ++n) {
- if (fl->light[i][n])
- {
- fl->light[i][n] = (LightingValue_t *) calloc( fl->numsamples, sizeof(LightingValue_t ) );
- if ( ReadValues( pmb, fl->light[i][n], fl->numsamples) < 0)
- Error("UnSerializeFace - invalid fl->light from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() );
- }
- }
- }
-
- if (fl->luxel) {
- fl->luxel = (Vector *) calloc(fl->numluxels, sizeof(Vector));
- if (ReadValues( pmb, fl->luxel, fl->numluxels) < 0)
- Error("UnSerializeFace - invalid fl->luxel from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() );
- }
-
- if (fl->luxelNormals) {
- fl->luxelNormals = (Vector *) calloc(fl->numluxels, sizeof( Vector ));
- if ( ReadValues( pmb, fl->luxelNormals, fl->numluxels) < 0)
- Error("UnSerializeFace - invalid fl->luxelNormals from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() );
- }
-
-}
-
-
-void MPI_ReceiveFaceResults( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker )
-{
- UnSerializeFace( pBuf, iWorkUnit, iWorker );
-}
-
-
-void MPI_ProcessFaces( int iThread, uint64 iWorkUnit, MessageBuffer *pBuf )
-{
- // Do BuildFacelights on the face.
- CTimeAdder adder( &g_CPUTime );
-
- BuildFacelights( iThread, iWorkUnit );
-
- // Send the results.
- if ( pBuf )
- {
- SerializeFace( pBuf, iWorkUnit );
- }
-}
-
-
-void RunMPIBuildFacelights()
-{
- g_CPUTime.Init();
-
- Msg( "%-20s ", "BuildFaceLights:" );
- if ( g_bMPIMaster )
- {
- StartPacifier("");
- }
-
- VMPI_SetCurrentStage( "RunMPIBuildFaceLights" );
- double elapsed = DistributeWork(
- numfaces,
- VMPI_DISTRIBUTEWORK_PACKETID,
- MPI_ProcessFaces,
- MPI_ReceiveFaceResults );
-
- if ( g_bMPIMaster )
- {
- EndPacifier(false);
- Msg( " (%d)\n", (int)elapsed );
- }
-
- if ( g_bMPIMaster )
- {
- //
- // BuildPatchLights is normally called from BuildFacelights(),
- // but in MPI mode we have the master do the calculation
- // We might be able to speed this up by doing while the master
- // is idling in the above loop. Wouldn't want to slow down the
- // handing out of work - maybe another thread?
- //
- for ( int i=0; i < numfaces; ++i )
- {
- BuildPatchLights(i);
- }
- }
- else
- {
- if ( g_iVMPIVerboseLevel >= 1 )
- Msg( "\n\n%.1f%% CPU utilization during BuildFaceLights\n\n", ( g_CPUTime.GetSeconds() * 100 / elapsed ) );
- }
-}
-
-
-//-----------------------------------------
-//
-// Run BuildVisLeafs across all available processing nodes
-// and collect the results.
-//
-
-// This function is called when the master receives results back from a worker.
-void MPI_ReceiveVisLeafsResults( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker )
-{
- int patchesInCluster = 0;
-
- pBuf->read(&patchesInCluster, sizeof(patchesInCluster));
-
- for ( int k=0; k < patchesInCluster; ++k )
- {
- int patchnum = 0;
- pBuf->read(&patchnum, sizeof(patchnum));
-
- CPatch * patch = &g_Patches[patchnum];
- int numtransfers;
- pBuf->read( &numtransfers, sizeof(numtransfers) );
- patch->numtransfers = numtransfers;
- if (numtransfers)
- {
- patch->transfers = new transfer_t[numtransfers];
- pBuf->read(patch->transfers, numtransfers * sizeof(transfer_t));
- }
-
- total_transfer += numtransfers;
- if (max_transfer < numtransfers)
- max_transfer = numtransfers;
- }
-}
-
-
-// Temporary variables used during callbacks. If we're going to be threadsafe, these
-// should go in a structure and get passed around.
-class CVMPIVisLeafsData
-{
-public:
- MessageBuffer *m_pVisLeafsMB;
- int m_nPatchesInCluster;
- transfer_t *m_pBuildVisLeafsTransfers;
-};
-
-CVMPIVisLeafsData g_VMPIVisLeafsData[MAX_TOOL_THREADS+1];
-
-
-
-// This is called by BuildVisLeafs_Cluster every time it finishes a patch.
-// The results are appended to g_VisLeafsMB and sent back to the master when all clusters are done.
-void MPI_AddPatchData( int iThread, int patchnum, CPatch *patch )
-{
- CVMPIVisLeafsData *pData = &g_VMPIVisLeafsData[iThread];
- if ( pData->m_pVisLeafsMB )
- {
- // Add in results for this patch
- ++pData->m_nPatchesInCluster;
- pData->m_pVisLeafsMB->write(&patchnum, sizeof(patchnum));
- pData->m_pVisLeafsMB->write(&patch->numtransfers, sizeof(patch->numtransfers));
- pData->m_pVisLeafsMB->write( patch->transfers, patch->numtransfers * sizeof(transfer_t) );
- }
-}
-
-
-// This handles a work unit sent by the master. Each work unit here is a
-// list of clusters.
-void MPI_ProcessVisLeafs( int iThread, uint64 iWorkUnit, MessageBuffer *pBuf )
-{
- CTimeAdder adder( &g_CPUTime );
-
- CVMPIVisLeafsData *pData = &g_VMPIVisLeafsData[iThread];
- int iCluster = iWorkUnit;
-
- // Start this cluster.
- pData->m_nPatchesInCluster = 0;
- pData->m_pVisLeafsMB = pBuf;
-
- // Write a temp value in there. We overwrite it later.
- int iSavePos = 0;
- if ( pBuf )
- {
- iSavePos = pBuf->getLen();
- pBuf->write( &pData->m_nPatchesInCluster, sizeof(pData->m_nPatchesInCluster) );
- }
-
- // Collect the results in MPI_AddPatchData.
- BuildVisLeafs_Cluster( iThread, pData->m_pBuildVisLeafsTransfers, iCluster, MPI_AddPatchData );
-
- // Now send the results back..
- if ( pBuf )
- {
- pBuf->update( iSavePos, &pData->m_nPatchesInCluster, sizeof(pData->m_nPatchesInCluster) );
- pData->m_pVisLeafsMB = NULL;
- }
-}
-
-
-void RunMPIBuildVisLeafs()
-{
- g_CPUTime.Init();
-
- Msg( "%-20s ", "BuildVisLeafs :" );
- if ( g_bMPIMaster )
- {
- StartPacifier("");
- }
-
- memset( g_VMPIVisLeafsData, 0, sizeof( g_VMPIVisLeafsData ) );
- if ( !g_bMPIMaster || VMPI_GetActiveWorkUnitDistributor() == k_eWorkUnitDistributor_SDK )
- {
- // Allocate space for the transfers for each thread.
- for ( int i=0; i < numthreads; i++ )
- {
- g_VMPIVisLeafsData[i].m_pBuildVisLeafsTransfers = BuildVisLeafs_Start();
- }
- }
-
- //
- // Slaves ask for work via GetMPIBuildVisLeafWork()
- // Results are returned in BuildVisRow()
- //
- VMPI_SetCurrentStage( "RunMPIBuildVisLeafs" );
-
- double elapsed = DistributeWork(
- dvis->numclusters,
- VMPI_DISTRIBUTEWORK_PACKETID,
- MPI_ProcessVisLeafs,
- MPI_ReceiveVisLeafsResults );
-
- // Free the transfers from each thread.
- for ( int i=0; i < numthreads; i++ )
- {
- if ( g_VMPIVisLeafsData[i].m_pBuildVisLeafsTransfers )
- BuildVisLeafs_End( g_VMPIVisLeafsData[i].m_pBuildVisLeafsTransfers );
- }
-
- if ( g_bMPIMaster )
- {
- EndPacifier(false);
- Msg( " (%d)\n", (int)elapsed );
- }
- else
- {
- if ( g_iVMPIVerboseLevel >= 1 )
- Msg( "%.1f%% CPU utilization during PortalFlow\n", (g_CPUTime.GetSeconds() * 100.0f / elapsed) / numthreads );
- }
-}
-
-void VMPI_DistributeLightData()
-{
- if ( !g_bUseMPI )
- return;
-
- if ( g_bMPIMaster )
- {
- const char *pVirtualFilename = "--plightdata--";
-
- CUtlBuffer lightFaceData;
-
- // write out the light data
- lightFaceData.EnsureCapacity( pdlightdata->Count() + (numfaces * (MAXLIGHTMAPS+sizeof(int))) );
- Q_memcpy( lightFaceData.PeekPut(), pdlightdata->Base(), pdlightdata->Count() );
- lightFaceData.SeekPut( CUtlBuffer::SEEK_HEAD, pdlightdata->Count() );
-
- // write out the relevant face info into the stream
- for ( int i = 0; i < numfaces; i++ )
- {
- for ( int j = 0; j < MAXLIGHTMAPS; j++ )
- {
- lightFaceData.PutChar(g_pFaces[i].styles[j]);
- }
- lightFaceData.PutInt(g_pFaces[i].lightofs);
- }
- VMPI_FileSystem_CreateVirtualFile( pVirtualFilename, lightFaceData.Base(), lightFaceData.TellMaxPut() );
-
- char cPacketID[2] = { VMPI_VRAD_PACKET_ID, VMPI_SUBPACKETID_PLIGHTDATA_RESULTS };
- VMPI_Send2Chunks( cPacketID, sizeof( cPacketID ), pVirtualFilename, strlen( pVirtualFilename ) + 1, VMPI_PERSISTENT );
- }
- else
- {
- VMPI_SetCurrentStage( "VMPI_DistributeLightData" );
-
- // Wait until we've received the filename from the master.
- while ( g_LightResultsFilename.Count() == 0 )
- {
- VMPI_DispatchNextMessage();
- }
-
- // Open
- FileHandle_t fp = g_pFileSystem->Open( g_LightResultsFilename.Base(), "rb", VMPI_VIRTUAL_FILES_PATH_ID );
- if ( !fp )
- Error( "Can't open '%s' to read lighting info.", g_LightResultsFilename.Base() );
-
- int size = g_pFileSystem->Size( fp );
- int faceSize = (numfaces*(MAXLIGHTMAPS+sizeof(int)));
-
- if ( size > faceSize )
- {
- int lightSize = size - faceSize;
- CUtlBuffer faceData;
- pdlightdata->EnsureCount( lightSize );
- faceData.EnsureCapacity( faceSize );
-
- g_pFileSystem->Read( pdlightdata->Base(), lightSize, fp );
- g_pFileSystem->Read( faceData.Base(), faceSize, fp );
- g_pFileSystem->Close( fp );
-
- faceData.SeekPut( CUtlBuffer::SEEK_HEAD, faceSize );
-
- // write out the face data
- for ( int i = 0; i < numfaces; i++ )
- {
- for ( int j = 0; j < MAXLIGHTMAPS; j++ )
- {
- g_pFaces[i].styles[j] = faceData.GetChar();
- }
- g_pFaces[i].lightofs = faceData.GetInt();
- }
- }
- }
-}
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//
+// mpivrad.cpp
+//
+
+#include <windows.h>
+#include <conio.h>
+#include "vrad.h"
+#include "physdll.h"
+#include "lightmap.h"
+#include "tier1/strtools.h"
+#include "radial.h"
+#include "utlbuffer.h"
+#include "pacifier.h"
+#include "messbuf.h"
+#include "bsplib.h"
+#include "consolewnd.h"
+#include "vismat.h"
+#include "vmpi_filesystem.h"
+#include "vmpi_dispatch.h"
+#include "utllinkedlist.h"
+#include "vmpi.h"
+#include "mpi_stats.h"
+#include "vmpi_distribute_work.h"
+#include "vmpi_tools_shared.h"
+
+
+
+
+CUtlVector<char> g_LightResultsFilename;
+
+
+extern int total_transfer;
+extern int max_transfer;
+
+extern void BuildVisLeafs(int);
+extern void BuildPatchLights( int facenum );
+
+
+// Handle VRAD packets.
+bool VRAD_DispatchFn( MessageBuffer *pBuf, int iSource, int iPacketID )
+{
+ switch( pBuf->data[1] )
+ {
+ case VMPI_SUBPACKETID_PLIGHTDATA_RESULTS:
+ {
+ const char *pFilename = &pBuf->data[2];
+ g_LightResultsFilename.CopyArray( pFilename, strlen( pFilename ) + 1 );
+ return true;
+ }
+
+ default:
+ return false;
+ }
+}
+CDispatchReg g_VRADDispatchReg( VMPI_VRAD_PACKET_ID, VRAD_DispatchFn ); // register to handle the messages we want
+CDispatchReg g_DistributeWorkReg( VMPI_DISTRIBUTEWORK_PACKETID, DistributeWorkDispatch );
+
+
+
+void VRAD_SetupMPI( int &argc, char **&argv )
+{
+ CmdLib_AtCleanup( VMPI_Stats_Term );
+
+ //
+ // Preliminary check -mpi flag
+ //
+ if ( !VMPI_FindArg( argc, argv, "-mpi", "" ) && !VMPI_FindArg( argc, argv, VMPI_GetParamString( mpi_Worker ), "" ) )
+ return;
+
+ // Force local mode?
+ VMPIRunMode mode;
+ if ( VMPI_FindArg( argc, argv, VMPI_GetParamString( mpi_Local ), "" ) )
+ mode = VMPI_RUN_LOCAL;
+ else
+ mode = VMPI_RUN_NETWORKED;
+
+ VMPI_Stats_InstallSpewHook();
+
+ //
+ // Extract mpi specific arguments
+ //
+ Msg( "Initializing VMPI...\n" );
+ if ( !VMPI_Init(
+ argc,
+ argv,
+ "dependency_info_vrad.txt",
+ HandleMPIDisconnect,
+ mode
+ ) )
+ {
+ Error( "MPI_Init failed." );
+ }
+
+ StatsDB_InitStatsDatabase( argc, argv, "dbinfo_vrad.txt" );
+}
+
+
+//-----------------------------------------
+//
+// Run BuildFaceLights across all available processing nodes
+// and collect the results.
+//
+
+CCycleCount g_CPUTime;
+
+
+template<class T> void WriteValues( MessageBuffer *pmb, T const *pSrc, int nNumValues)
+{
+ pmb->write(pSrc, sizeof( pSrc[0]) * nNumValues );
+}
+
+template<class T> int ReadValues( MessageBuffer *pmb, T *pDest, int nNumValues)
+{
+ return pmb->read( pDest, sizeof( pDest[0]) * nNumValues );
+}
+
+
+//--------------------------------------------------
+// Serialize face data
+void SerializeFace( MessageBuffer * pmb, int facenum )
+{
+ int i, n;
+
+ dface_t * f = &g_pFaces[facenum];
+ facelight_t * fl = &facelight[facenum];
+
+ pmb->write(f, sizeof(dface_t));
+ pmb->write(fl, sizeof(facelight_t));
+
+ WriteValues( pmb, fl->sample, fl->numsamples);
+
+ //
+ // Write the light information
+ //
+ for (i=0; i<MAXLIGHTMAPS; ++i) {
+ for (n=0; n<NUM_BUMP_VECTS+1; ++n) {
+ if (fl->light[i][n])
+ {
+ WriteValues( pmb, fl->light[i][n], fl->numsamples);
+ }
+ }
+ }
+
+ if (fl->luxel)
+ WriteValues( pmb, fl->luxel, fl->numluxels);
+
+ if (fl->luxelNormals)
+ WriteValues( pmb, fl->luxelNormals, fl->numluxels);
+}
+
+//--------------------------------------------------
+// UnSerialize face data
+//
+void UnSerializeFace( MessageBuffer * pmb, int facenum, int iSource )
+{
+ int i, n;
+
+ dface_t * f = &g_pFaces[facenum];
+ facelight_t * fl = &facelight[facenum];
+
+ if (pmb->read(f, sizeof(dface_t)) < 0)
+ Error("UnSerializeFace - invalid dface_t from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() );
+
+ if (pmb->read(fl, sizeof(facelight_t)) < 0)
+ Error("UnSerializeFace - invalid facelight_t from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() );
+
+ fl->sample = (sample_t *) calloc(fl->numsamples, sizeof(sample_t));
+ if (pmb->read(fl->sample, sizeof(sample_t) * fl->numsamples) < 0)
+ Error("UnSerializeFace - invalid sample_t from %s (mb len: %d, offset: %d, fl->numsamples: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset(), fl->numsamples );
+
+ //
+ // Read the light information
+ //
+ for (i=0; i<MAXLIGHTMAPS; ++i) {
+ for (n=0; n<NUM_BUMP_VECTS+1; ++n) {
+ if (fl->light[i][n])
+ {
+ fl->light[i][n] = (LightingValue_t *) calloc( fl->numsamples, sizeof(LightingValue_t ) );
+ if ( ReadValues( pmb, fl->light[i][n], fl->numsamples) < 0)
+ Error("UnSerializeFace - invalid fl->light from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() );
+ }
+ }
+ }
+
+ if (fl->luxel) {
+ fl->luxel = (Vector *) calloc(fl->numluxels, sizeof(Vector));
+ if (ReadValues( pmb, fl->luxel, fl->numluxels) < 0)
+ Error("UnSerializeFace - invalid fl->luxel from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() );
+ }
+
+ if (fl->luxelNormals) {
+ fl->luxelNormals = (Vector *) calloc(fl->numluxels, sizeof( Vector ));
+ if ( ReadValues( pmb, fl->luxelNormals, fl->numluxels) < 0)
+ Error("UnSerializeFace - invalid fl->luxelNormals from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() );
+ }
+
+}
+
+
+void MPI_ReceiveFaceResults( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker )
+{
+ UnSerializeFace( pBuf, iWorkUnit, iWorker );
+}
+
+
+void MPI_ProcessFaces( int iThread, uint64 iWorkUnit, MessageBuffer *pBuf )
+{
+ // Do BuildFacelights on the face.
+ CTimeAdder adder( &g_CPUTime );
+
+ BuildFacelights( iThread, iWorkUnit );
+
+ // Send the results.
+ if ( pBuf )
+ {
+ SerializeFace( pBuf, iWorkUnit );
+ }
+}
+
+
+void RunMPIBuildFacelights()
+{
+ g_CPUTime.Init();
+
+ Msg( "%-20s ", "BuildFaceLights:" );
+ if ( g_bMPIMaster )
+ {
+ StartPacifier("");
+ }
+
+ VMPI_SetCurrentStage( "RunMPIBuildFaceLights" );
+ double elapsed = DistributeWork(
+ numfaces,
+ VMPI_DISTRIBUTEWORK_PACKETID,
+ MPI_ProcessFaces,
+ MPI_ReceiveFaceResults );
+
+ if ( g_bMPIMaster )
+ {
+ EndPacifier(false);
+ Msg( " (%d)\n", (int)elapsed );
+ }
+
+ if ( g_bMPIMaster )
+ {
+ //
+ // BuildPatchLights is normally called from BuildFacelights(),
+ // but in MPI mode we have the master do the calculation
+ // We might be able to speed this up by doing while the master
+ // is idling in the above loop. Wouldn't want to slow down the
+ // handing out of work - maybe another thread?
+ //
+ for ( int i=0; i < numfaces; ++i )
+ {
+ BuildPatchLights(i);
+ }
+ }
+ else
+ {
+ if ( g_iVMPIVerboseLevel >= 1 )
+ Msg( "\n\n%.1f%% CPU utilization during BuildFaceLights\n\n", ( g_CPUTime.GetSeconds() * 100 / elapsed ) );
+ }
+}
+
+
+//-----------------------------------------
+//
+// Run BuildVisLeafs across all available processing nodes
+// and collect the results.
+//
+
+// This function is called when the master receives results back from a worker.
+void MPI_ReceiveVisLeafsResults( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker )
+{
+ int patchesInCluster = 0;
+
+ pBuf->read(&patchesInCluster, sizeof(patchesInCluster));
+
+ for ( int k=0; k < patchesInCluster; ++k )
+ {
+ int patchnum = 0;
+ pBuf->read(&patchnum, sizeof(patchnum));
+
+ CPatch * patch = &g_Patches[patchnum];
+ int numtransfers;
+ pBuf->read( &numtransfers, sizeof(numtransfers) );
+ patch->numtransfers = numtransfers;
+ if (numtransfers)
+ {
+ patch->transfers = new transfer_t[numtransfers];
+ pBuf->read(patch->transfers, numtransfers * sizeof(transfer_t));
+ }
+
+ total_transfer += numtransfers;
+ if (max_transfer < numtransfers)
+ max_transfer = numtransfers;
+ }
+}
+
+
+// Temporary variables used during callbacks. If we're going to be threadsafe, these
+// should go in a structure and get passed around.
+class CVMPIVisLeafsData
+{
+public:
+ MessageBuffer *m_pVisLeafsMB;
+ int m_nPatchesInCluster;
+ transfer_t *m_pBuildVisLeafsTransfers;
+};
+
+CVMPIVisLeafsData g_VMPIVisLeafsData[MAX_TOOL_THREADS+1];
+
+
+
+// This is called by BuildVisLeafs_Cluster every time it finishes a patch.
+// The results are appended to g_VisLeafsMB and sent back to the master when all clusters are done.
+void MPI_AddPatchData( int iThread, int patchnum, CPatch *patch )
+{
+ CVMPIVisLeafsData *pData = &g_VMPIVisLeafsData[iThread];
+ if ( pData->m_pVisLeafsMB )
+ {
+ // Add in results for this patch
+ ++pData->m_nPatchesInCluster;
+ pData->m_pVisLeafsMB->write(&patchnum, sizeof(patchnum));
+ pData->m_pVisLeafsMB->write(&patch->numtransfers, sizeof(patch->numtransfers));
+ pData->m_pVisLeafsMB->write( patch->transfers, patch->numtransfers * sizeof(transfer_t) );
+ }
+}
+
+
+// This handles a work unit sent by the master. Each work unit here is a
+// list of clusters.
+void MPI_ProcessVisLeafs( int iThread, uint64 iWorkUnit, MessageBuffer *pBuf )
+{
+ CTimeAdder adder( &g_CPUTime );
+
+ CVMPIVisLeafsData *pData = &g_VMPIVisLeafsData[iThread];
+ int iCluster = iWorkUnit;
+
+ // Start this cluster.
+ pData->m_nPatchesInCluster = 0;
+ pData->m_pVisLeafsMB = pBuf;
+
+ // Write a temp value in there. We overwrite it later.
+ int iSavePos = 0;
+ if ( pBuf )
+ {
+ iSavePos = pBuf->getLen();
+ pBuf->write( &pData->m_nPatchesInCluster, sizeof(pData->m_nPatchesInCluster) );
+ }
+
+ // Collect the results in MPI_AddPatchData.
+ BuildVisLeafs_Cluster( iThread, pData->m_pBuildVisLeafsTransfers, iCluster, MPI_AddPatchData );
+
+ // Now send the results back..
+ if ( pBuf )
+ {
+ pBuf->update( iSavePos, &pData->m_nPatchesInCluster, sizeof(pData->m_nPatchesInCluster) );
+ pData->m_pVisLeafsMB = NULL;
+ }
+}
+
+
+void RunMPIBuildVisLeafs()
+{
+ g_CPUTime.Init();
+
+ Msg( "%-20s ", "BuildVisLeafs :" );
+ if ( g_bMPIMaster )
+ {
+ StartPacifier("");
+ }
+
+ memset( g_VMPIVisLeafsData, 0, sizeof( g_VMPIVisLeafsData ) );
+ if ( !g_bMPIMaster || VMPI_GetActiveWorkUnitDistributor() == k_eWorkUnitDistributor_SDK )
+ {
+ // Allocate space for the transfers for each thread.
+ for ( int i=0; i < numthreads; i++ )
+ {
+ g_VMPIVisLeafsData[i].m_pBuildVisLeafsTransfers = BuildVisLeafs_Start();
+ }
+ }
+
+ //
+ // Slaves ask for work via GetMPIBuildVisLeafWork()
+ // Results are returned in BuildVisRow()
+ //
+ VMPI_SetCurrentStage( "RunMPIBuildVisLeafs" );
+
+ double elapsed = DistributeWork(
+ dvis->numclusters,
+ VMPI_DISTRIBUTEWORK_PACKETID,
+ MPI_ProcessVisLeafs,
+ MPI_ReceiveVisLeafsResults );
+
+ // Free the transfers from each thread.
+ for ( int i=0; i < numthreads; i++ )
+ {
+ if ( g_VMPIVisLeafsData[i].m_pBuildVisLeafsTransfers )
+ BuildVisLeafs_End( g_VMPIVisLeafsData[i].m_pBuildVisLeafsTransfers );
+ }
+
+ if ( g_bMPIMaster )
+ {
+ EndPacifier(false);
+ Msg( " (%d)\n", (int)elapsed );
+ }
+ else
+ {
+ if ( g_iVMPIVerboseLevel >= 1 )
+ Msg( "%.1f%% CPU utilization during PortalFlow\n", (g_CPUTime.GetSeconds() * 100.0f / elapsed) / numthreads );
+ }
+}
+
+void VMPI_DistributeLightData()
+{
+ if ( !g_bUseMPI )
+ return;
+
+ if ( g_bMPIMaster )
+ {
+ const char *pVirtualFilename = "--plightdata--";
+
+ CUtlBuffer lightFaceData;
+
+ // write out the light data
+ lightFaceData.EnsureCapacity( pdlightdata->Count() + (numfaces * (MAXLIGHTMAPS+sizeof(int))) );
+ Q_memcpy( lightFaceData.PeekPut(), pdlightdata->Base(), pdlightdata->Count() );
+ lightFaceData.SeekPut( CUtlBuffer::SEEK_HEAD, pdlightdata->Count() );
+
+ // write out the relevant face info into the stream
+ for ( int i = 0; i < numfaces; i++ )
+ {
+ for ( int j = 0; j < MAXLIGHTMAPS; j++ )
+ {
+ lightFaceData.PutChar(g_pFaces[i].styles[j]);
+ }
+ lightFaceData.PutInt(g_pFaces[i].lightofs);
+ }
+ VMPI_FileSystem_CreateVirtualFile( pVirtualFilename, lightFaceData.Base(), lightFaceData.TellMaxPut() );
+
+ char cPacketID[2] = { VMPI_VRAD_PACKET_ID, VMPI_SUBPACKETID_PLIGHTDATA_RESULTS };
+ VMPI_Send2Chunks( cPacketID, sizeof( cPacketID ), pVirtualFilename, strlen( pVirtualFilename ) + 1, VMPI_PERSISTENT );
+ }
+ else
+ {
+ VMPI_SetCurrentStage( "VMPI_DistributeLightData" );
+
+ // Wait until we've received the filename from the master.
+ while ( g_LightResultsFilename.Count() == 0 )
+ {
+ VMPI_DispatchNextMessage();
+ }
+
+ // Open
+ FileHandle_t fp = g_pFileSystem->Open( g_LightResultsFilename.Base(), "rb", VMPI_VIRTUAL_FILES_PATH_ID );
+ if ( !fp )
+ Error( "Can't open '%s' to read lighting info.", g_LightResultsFilename.Base() );
+
+ int size = g_pFileSystem->Size( fp );
+ int faceSize = (numfaces*(MAXLIGHTMAPS+sizeof(int)));
+
+ if ( size > faceSize )
+ {
+ int lightSize = size - faceSize;
+ CUtlBuffer faceData;
+ pdlightdata->EnsureCount( lightSize );
+ faceData.EnsureCapacity( faceSize );
+
+ g_pFileSystem->Read( pdlightdata->Base(), lightSize, fp );
+ g_pFileSystem->Read( faceData.Base(), faceSize, fp );
+ g_pFileSystem->Close( fp );
+
+ faceData.SeekPut( CUtlBuffer::SEEK_HEAD, faceSize );
+
+ // write out the face data
+ for ( int i = 0; i < numfaces; i++ )
+ {
+ for ( int j = 0; j < MAXLIGHTMAPS; j++ )
+ {
+ g_pFaces[i].styles[j] = faceData.GetChar();
+ }
+ g_pFaces[i].lightofs = faceData.GetInt();
+ }
+ }
+ }
+}
+
+
diff --git a/mp/src/utils/vrad/mpivrad.h b/mp/src/utils/vrad/mpivrad.h
index 990b3df8..01c841b3 100644
--- a/mp/src/utils/vrad/mpivrad.h
+++ b/mp/src/utils/vrad/mpivrad.h
@@ -1,36 +1,36 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef MPIVRAD_H
-#define MPIVRAD_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-#define VMPI_VRAD_PACKET_ID 1
- // Sub packet IDs.
- #define VMPI_SUBPACKETID_VIS_LEAFS 0
- #define VMPI_SUBPACKETID_BUILDFACELIGHTS 1
- #define VMPI_SUBPACKETID_PLIGHTDATA_RESULTS 2
-
-// DistributeWork owns this packet ID.
-#define VMPI_DISTRIBUTEWORK_PACKETID 2
-
-
-// Called first thing in the exe.
-void VRAD_SetupMPI( int &argc, char **&argv );
-
-void RunMPIBuildFacelights(void);
-void RunMPIBuildVisLeafs(void);
-void VMPI_DistributeLightData();
-
-// This handles disconnections. They're usually not fatal for the master.
-void HandleMPIDisconnect( int procID );
-
-
-#endif // MPIVRAD_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef MPIVRAD_H
+#define MPIVRAD_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#define VMPI_VRAD_PACKET_ID 1
+ // Sub packet IDs.
+ #define VMPI_SUBPACKETID_VIS_LEAFS 0
+ #define VMPI_SUBPACKETID_BUILDFACELIGHTS 1
+ #define VMPI_SUBPACKETID_PLIGHTDATA_RESULTS 2
+
+// DistributeWork owns this packet ID.
+#define VMPI_DISTRIBUTEWORK_PACKETID 2
+
+
+// Called first thing in the exe.
+void VRAD_SetupMPI( int &argc, char **&argv );
+
+void RunMPIBuildFacelights(void);
+void RunMPIBuildVisLeafs(void);
+void VMPI_DistributeLightData();
+
+// This handles disconnections. They're usually not fatal for the master.
+void HandleMPIDisconnect( int procID );
+
+
+#endif // MPIVRAD_H
diff --git a/mp/src/utils/vrad/origface.cpp b/mp/src/utils/vrad/origface.cpp
index 43a19577..fbbe0e87 100644
--- a/mp/src/utils/vrad/origface.cpp
+++ b/mp/src/utils/vrad/origface.cpp
@@ -1,51 +1,51 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $Workfile: $
-// $Date: $
-//
-//-----------------------------------------------------------------------------
-// $Log: $
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "vrad.h"
-
-bool bOrigFacesTouched[MAX_MAP_FACES];
-
-
-//-----------------------------------------------------------------------------
-// Pupose: clear (reset) the bOrigFacesTouched list -- parellels the original
-// face list allowing an original face to only be processed once in
-// pairing edges!
-//-----------------------------------------------------------------------------
-void ResetOrigFacesTouched( void )
-{
- for( int i = 0; i < MAX_MAP_FACES; i++ )
- {
- bOrigFacesTouched[i] = false;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: mark an original faces as touched (dirty)
-// Input: index - index of the original face touched
-//-----------------------------------------------------------------------------
-void SetOrigFaceTouched( int index )
-{
- bOrigFacesTouched[index] = true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: return whether or not an original face has been touched
-// Input: index - index of the original face touched
-// Output: true/false
-//-----------------------------------------------------------------------------
-bool IsOrigFaceTouched( int index )
-{
- return bOrigFacesTouched[index];
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vrad.h"
+
+bool bOrigFacesTouched[MAX_MAP_FACES];
+
+
+//-----------------------------------------------------------------------------
+// Pupose: clear (reset) the bOrigFacesTouched list -- parellels the original
+// face list allowing an original face to only be processed once in
+// pairing edges!
+//-----------------------------------------------------------------------------
+void ResetOrigFacesTouched( void )
+{
+ for( int i = 0; i < MAX_MAP_FACES; i++ )
+ {
+ bOrigFacesTouched[i] = false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: mark an original faces as touched (dirty)
+// Input: index - index of the original face touched
+//-----------------------------------------------------------------------------
+void SetOrigFaceTouched( int index )
+{
+ bOrigFacesTouched[index] = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: return whether or not an original face has been touched
+// Input: index - index of the original face touched
+// Output: true/false
+//-----------------------------------------------------------------------------
+bool IsOrigFaceTouched( int index )
+{
+ return bOrigFacesTouched[index];
+}
diff --git a/mp/src/utils/vrad/radial.cpp b/mp/src/utils/vrad/radial.cpp
index 46e3b925..5dc99b50 100644
--- a/mp/src/utils/vrad/radial.cpp
+++ b/mp/src/utils/vrad/radial.cpp
@@ -1,882 +1,882 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#include "vrad.h"
-#include "lightmap.h"
-#include "radial.h"
-#include "mathlib/bumpvects.h"
-#include "utlrbtree.h"
-#include "mathlib/VMatrix.h"
-#include "macro_texture.h"
-
-
-void WorldToLuxelSpace( lightinfo_t const *l, Vector const &world, Vector2D &coord )
-{
- Vector pos;
-
- VectorSubtract( world, l->luxelOrigin, pos );
- coord[0] = DotProduct( pos, l->worldToLuxelSpace[0] ) - l->face->m_LightmapTextureMinsInLuxels[0];
- coord[1] = DotProduct( pos, l->worldToLuxelSpace[1] ) - l->face->m_LightmapTextureMinsInLuxels[1];
-}
-
-void LuxelSpaceToWorld( lightinfo_t const *l, float s, float t, Vector &world )
-{
- Vector pos;
-
- s += l->face->m_LightmapTextureMinsInLuxels[0];
- t += l->face->m_LightmapTextureMinsInLuxels[1];
- VectorMA( l->luxelOrigin, s, l->luxelToWorldSpace[0], pos );
- VectorMA( pos, t, l->luxelToWorldSpace[1], world );
-}
-
-void WorldToLuxelSpace( lightinfo_t const *l, FourVectors const &world, FourVectors &coord )
-{
- FourVectors luxelOrigin;
- luxelOrigin.DuplicateVector ( l->luxelOrigin );
-
- FourVectors pos = world;
- pos -= luxelOrigin;
-
- coord.x = pos * l->worldToLuxelSpace[0];
- coord.x = SubSIMD ( coord.x, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[0] ) );
- coord.y = pos * l->worldToLuxelSpace[1];
- coord.y = SubSIMD ( coord.y, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[1] ) );
- coord.z = Four_Zeros;
-}
-
-void LuxelSpaceToWorld( lightinfo_t const *l, fltx4 s, fltx4 t, FourVectors &world )
-{
- world.DuplicateVector ( l->luxelOrigin );
- FourVectors st;
-
- s = AddSIMD ( s, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[0] ) );
- st.DuplicateVector ( l->luxelToWorldSpace[0] );
- st *= s;
- world += st;
-
- t = AddSIMD ( t, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[1] ) );
- st.DuplicateVector ( l->luxelToWorldSpace[1] );
- st *= t;
- world += st;
-}
-
-
-
-void AddDirectToRadial( radial_t *rad,
- Vector const &pnt,
- Vector2D const &coordmins, Vector2D const &coordmaxs,
- LightingValue_t const light[NUM_BUMP_VECTS+1],
- bool hasBumpmap, bool neighborHasBumpmap )
-{
- int s_min, s_max, t_min, t_max;
- Vector2D coord;
- int s, t;
- float ds, dt;
- float r;
- float area;
- int bumpSample;
-
- // convert world pos into local lightmap texture coord
- WorldToLuxelSpace( &rad->l, pnt, coord );
-
- s_min = ( int )( coordmins[0] );
- t_min = ( int )( coordmins[1] );
- s_max = ( int )( coordmaxs[0] + 0.9999f ) + 1; // ????
- t_max = ( int )( coordmaxs[1] + 0.9999f ) + 1;
-
- s_min = max( s_min, 0 );
- t_min = max( t_min, 0 );
- s_max = min( s_max, rad->w );
- t_max = min( t_max, rad->h );
-
- for( s = s_min; s < s_max; s++ )
- {
- for( t = t_min; t < t_max; t++ )
- {
- float s0 = max( coordmins[0] - s, -1.0 );
- float t0 = max( coordmins[1] - t, -1.0 );
- float s1 = min( coordmaxs[0] - s, 1.0 );
- float t1 = min( coordmaxs[1] - t, 1.0 );
-
- area = (s1 - s0) * (t1 - t0);
-
- if (area > EQUAL_EPSILON)
- {
- ds = fabs( coord[0] - s );
- dt = fabs( coord[1] - t );
-
- r = max( ds, dt );
-
- if (r < 0.1)
- {
- r = area / 0.1;
- }
- else
- {
- r = area / r;
- }
-
- int i = s+t*rad->w;
-
- if( hasBumpmap )
- {
- if( neighborHasBumpmap )
- {
- for( bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
- {
- rad->light[bumpSample][i].AddWeighted( light[bumpSample], r );
- }
- }
- else
- {
- rad->light[0][i].AddWeighted(light[0],r );
- for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
- {
- rad->light[bumpSample][i].AddWeighted( light[0], r * OO_SQRT_3 );
- }
- }
- }
- else
- {
- rad->light[0][i].AddWeighted( light[0], r );
- }
-
- rad->weight[i] += r;
- }
- }
- }
-}
-
-
-
-void AddBouncedToRadial( radial_t *rad,
- Vector const &pnt,
- Vector2D const &coordmins, Vector2D const &coordmaxs,
- Vector const light[NUM_BUMP_VECTS+1],
- bool hasBumpmap, bool neighborHasBumpmap )
-{
- int s_min, s_max, t_min, t_max;
- Vector2D coord;
- int s, t;
- float ds, dt;
- float r;
- int bumpSample;
-
- // convert world pos into local lightmap texture coord
- WorldToLuxelSpace( &rad->l, pnt, coord );
-
- float dists, distt;
-
- dists = (coordmaxs[0] - coordmins[0]);
- distt = (coordmaxs[1] - coordmins[1]);
-
- // patches less than a luxel in size could be mistakeningly filtered, so clamp.
- dists = max( 1.0, dists );
- distt = max( 1.0, distt );
-
- // find possible domain of patch influence
- s_min = ( int )( coord[0] - dists * RADIALDIST );
- t_min = ( int )( coord[1] - distt * RADIALDIST );
- s_max = ( int )( coord[0] + dists * RADIALDIST + 1.0f );
- t_max = ( int )( coord[1] + distt * RADIALDIST + 1.0f );
-
- // clamp to valid luxel
- s_min = max( s_min, 0 );
- t_min = max( t_min, 0 );
- s_max = min( s_max, rad->w );
- t_max = min( t_max, rad->h );
-
- for( s = s_min; s < s_max; s++ )
- {
- for( t = t_min; t < t_max; t++ )
- {
- // patch influence is based on patch size
- ds = ( coord[0] - s ) / dists;
- dt = ( coord[1] - t ) / distt;
-
- r = RADIALDIST2 - (ds * ds + dt * dt);
-
- int i = s+t*rad->w;
-
- if (r > 0)
- {
- if( hasBumpmap )
- {
- if( neighborHasBumpmap )
- {
- for( bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
- {
- rad->light[bumpSample][i].AddWeighted( light[bumpSample], r );
- }
- }
- else
- {
- rad->light[0][i].AddWeighted( light[0], r );
- for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
- {
- rad->light[bumpSample][i].AddWeighted( light[0], r * OO_SQRT_3 );
- }
- }
- }
- else
- {
- rad->light[0][i].AddWeighted( light[0], r );
- }
-
- rad->weight[i] += r;
- }
- }
- }
-}
-
-void PatchLightmapCoordRange( radial_t *rad, int ndxPatch, Vector2D &mins, Vector2D &maxs )
-{
- winding_t *w;
- int i;
- Vector2D coord;
-
- mins.Init( 1E30, 1E30 );
- maxs.Init( -1E30, -1E30 );
-
- CPatch *patch = &g_Patches.Element( ndxPatch );
- w = patch->winding;
-
- for (i = 0; i < w->numpoints; i++)
- {
- WorldToLuxelSpace( &rad->l, w->p[i], coord );
- mins[0] = min( mins[0], coord[0] );
- maxs[0] = max( maxs[0], coord[0] );
- mins[1] = min( mins[1], coord[1] );
- maxs[1] = max( maxs[1], coord[1] );
- }
-}
-
-radial_t *AllocateRadial( int facenum )
-{
- radial_t *rad;
-
- rad = ( radial_t* )calloc( 1, sizeof( *rad ) );
-
- rad->facenum = facenum;
- InitLightinfo( &rad->l, facenum );
-
- rad->w = rad->l.face->m_LightmapTextureSizeInLuxels[0]+1;
- rad->h = rad->l.face->m_LightmapTextureSizeInLuxels[1]+1;
-
- return rad;
-}
-
-void FreeRadial( radial_t *rad )
-{
- if (rad)
- free( rad );
-}
-
-
-radial_t *BuildPatchRadial( int facenum )
-{
- int j;
- radial_t *rad;
- CPatch *patch;
- faceneighbor_t *fn;
- Vector2D mins, maxs;
- bool needsBumpmap, neighborNeedsBumpmap;
-
- needsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
-
- rad = AllocateRadial( facenum );
-
- fn = &faceneighbor[ rad->facenum ];
-
- CPatch *pNextPatch;
-
- if( g_FacePatches.Element( rad->facenum ) != g_FacePatches.InvalidIndex() )
- {
- for( patch = &g_Patches.Element( g_FacePatches.Element( rad->facenum ) ); patch; patch = pNextPatch )
- {
- // next patch
- pNextPatch = NULL;
- if( patch->ndxNext != g_Patches.InvalidIndex() )
- {
- pNextPatch = &g_Patches.Element( patch->ndxNext );
- }
-
- // skip patches with children
- if (patch->child1 != g_Patches.InvalidIndex() )
- continue;
-
- // get the range of patch lightmap texture coords
- int ndxPatch = patch - g_Patches.Base();
- PatchLightmapCoordRange( rad, ndxPatch, mins, maxs );
-
- if (patch->numtransfers == 0)
- {
- // Error, using patch that was never evaluated or has no samples
- // patch->totallight[1] = 255;
- }
-
- //
- // displacement surface patch origin position and normal vectors have been changed to
- // represent the displacement surface position and normal -- for radial "blending"
- // we need to get the base surface patch origin!
- //
- if( ValidDispFace( &g_pFaces[facenum] ) )
- {
- Vector patchOrigin;
- WindingCenter (patch->winding, patchOrigin );
- AddBouncedToRadial( rad, patchOrigin, mins, maxs, patch->totallight.light,
- needsBumpmap, needsBumpmap );
- }
- else
- {
- AddBouncedToRadial( rad, patch->origin, mins, maxs, patch->totallight.light,
- needsBumpmap, needsBumpmap );
- }
- }
- }
-
- for (j=0 ; j<fn->numneighbors; j++)
- {
- if( g_FacePatches.Element( fn->neighbor[j] ) != g_FacePatches.InvalidIndex() )
- {
- for( patch = &g_Patches.Element( g_FacePatches.Element( fn->neighbor[j] ) ); patch; patch = pNextPatch )
- {
- // next patch
- pNextPatch = NULL;
- if( patch->ndxNext != g_Patches.InvalidIndex() )
- {
- pNextPatch = &g_Patches.Element( patch->ndxNext );
- }
-
- // skip patches with children
- if (patch->child1 != g_Patches.InvalidIndex() )
- continue;
-
- // get the range of patch lightmap texture coords
- int ndxPatch = patch - g_Patches.Base();
- PatchLightmapCoordRange( rad, ndxPatch, mins, maxs );
-
- neighborNeedsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
-
- //
- // displacement surface patch origin position and normal vectors have been changed to
- // represent the displacement surface position and normal -- for radial "blending"
- // we need to get the base surface patch origin!
- //
- if( ValidDispFace( &g_pFaces[fn->neighbor[j]] ) )
- {
- Vector patchOrigin;
- WindingCenter (patch->winding, patchOrigin );
- AddBouncedToRadial( rad, patchOrigin, mins, maxs, patch->totallight.light,
- needsBumpmap, needsBumpmap );
- }
- else
- {
- AddBouncedToRadial( rad, patch->origin, mins, maxs, patch->totallight.light,
- needsBumpmap, needsBumpmap );
- }
- }
- }
- }
-
- return rad;
-}
-
-
-radial_t *BuildLuxelRadial( int facenum, int style )
-{
- LightingValue_t light[NUM_BUMP_VECTS + 1];
-
- facelight_t *fl = &facelight[facenum];
- faceneighbor_t *fn = &faceneighbor[facenum];
-
- radial_t *rad = AllocateRadial( facenum );
-
- bool needsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
-
- for (int k=0 ; k<fl->numsamples ; k++)
- {
- if( needsBumpmap )
- {
- for( int bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
- {
- light[bumpSample] = fl->light[style][bumpSample][k];
- }
- }
- else
- {
- light[0] = fl->light[style][0][k];
- }
-
- AddDirectToRadial( rad, fl->sample[k].pos, fl->sample[k].mins, fl->sample[k].maxs, light, needsBumpmap, needsBumpmap );
- }
-
- for (int j = 0; j < fn->numneighbors; j++)
- {
- fl = &facelight[fn->neighbor[j]];
-
- bool neighborHasBumpmap = false;
-
- if( texinfo[g_pFaces[fn->neighbor[j]].texinfo].flags & SURF_BUMPLIGHT )
- {
- neighborHasBumpmap = true;
- }
-
- int nstyle = 0;
-
- // look for style that matches
- if (g_pFaces[fn->neighbor[j]].styles[nstyle] != g_pFaces[facenum].styles[style])
- {
- for (nstyle = 1; nstyle < MAXLIGHTMAPS; nstyle++ )
- if ( g_pFaces[fn->neighbor[j]].styles[nstyle] == g_pFaces[facenum].styles[style] )
- break;
-
- // if not found, skip this neighbor
- if (nstyle >= MAXLIGHTMAPS)
- continue;
- }
-
- lightinfo_t l;
-
- InitLightinfo( &l, fn->neighbor[j] );
-
- for (int k=0 ; k<fl->numsamples ; k++)
- {
- if( neighborHasBumpmap )
- {
- for( int bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
- {
- light[bumpSample] = fl->light[nstyle][bumpSample][k];
- }
- }
- else
- {
- light[0]=fl->light[nstyle][0][k];
- }
-
- Vector tmp;
- Vector2D mins, maxs;
-
- LuxelSpaceToWorld( &l, fl->sample[k].mins[0], fl->sample[k].mins[1], tmp );
- WorldToLuxelSpace( &rad->l, tmp, mins );
- LuxelSpaceToWorld( &l, fl->sample[k].maxs[0], fl->sample[k].maxs[1], tmp );
- WorldToLuxelSpace( &rad->l, tmp, maxs );
-
- AddDirectToRadial( rad, fl->sample[k].pos, mins, maxs, light,
- needsBumpmap, neighborHasBumpmap );
- }
- }
-
- return rad;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: returns the closest light value for a given point on the surface
-// this is normally a 1:1 mapping
-//-----------------------------------------------------------------------------
-bool SampleRadial( radial_t *rad, Vector& pnt, LightingValue_t light[NUM_BUMP_VECTS + 1], int bumpSampleCount )
-{
- int bumpSample;
- Vector2D coord;
-
- WorldToLuxelSpace( &rad->l, pnt, coord );
- int u = ( int )( coord[0] + 0.5f );
- int v = ( int )( coord[1] + 0.5f );
- int i = u + v * rad->w;
-
- if (u < 0 || u > rad->w || v < 0 || v > rad->h)
- {
- static bool warning = false;
- if ( !warning )
- {
- // punting over to KenB
- // 2d coord indexes off of lightmap, generation of pnt seems suspect
- Warning( "SampleRadial: Punting, Waiting for fix\n" );
- warning = true;
- }
- for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
- {
- light[bumpSample].m_vecLighting.Init( 2550, 0, 0 );
- }
- return false;
- }
-
- bool baseSampleOk = true;
- for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
- {
- light[bumpSample].Zero();
-
- if (rad->weight[i] > WEIGHT_EPS)
- {
- light[bumpSample]= rad->light[bumpSample][i];
- light[bumpSample].Scale( 1.0 / rad->weight[i] );
- }
- else
- {
- if ( bRed2Black )
- {
- // Error, luxel has no samples
- light[bumpSample].m_vecLighting.Init( 0, 0, 0 );
- }
- else
- {
- // Error, luxel has no samples
- // Yes, it actually should be 2550
- light[bumpSample].m_vecLighting.Init( 2550, 0, 0 );
- }
-
- if (bumpSample == 0)
- baseSampleOk = false;
- }
- }
-
- return baseSampleOk;
-}
-
-bool FloatLess( float const& src1, float const& src2 )
-{
- return src1 < src2;
-}
-
-
-//-----------------------------------------------------------------------------
-// Debugging!
-//-----------------------------------------------------------------------------
-void GetRandomColor( unsigned char *color )
-{
- static bool firstTime = true;
-
- if( firstTime )
- {
- firstTime = false;
- srand( 0 );
- }
-
- color[0] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
- color[1] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
- color[2] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
-}
-
-
-#if 0
-// debugging! -- not accurate!
-void DumpLuxels( facelight_t *pFaceLight, Vector *luxelColors, int ndxFace )
-{
- static FileHandle_t pFpLuxels = NULL;
-
- ThreadLock();
-
- if( !pFpLuxels )
- {
- pFpLuxels = g_pFileSystem->Open( "luxels.txt", "w" );
- }
-
- dface_t *pFace = &g_pFaces[ndxFace];
- bool bDisp = ( pFace->dispinfo != -1 );
-
- for( int ndx = 0; ndx < pFaceLight->numluxels; ndx++ )
- {
- WriteWinding( pFpLuxels, pFaceLight->sample[ndx].w, luxelColors[ndx] );
- if( bDumpNormals && bDisp )
- {
- WriteNormal( pFpLuxels, pFaceLight->luxel[ndx], pFaceLight->luxelNormals[ndx], 15.0f, Vector( 255, 255, 0 ) );
- }
- }
-
- ThreadUnlock();
-}
-#endif
-
-
-static FileHandle_t pFileLuxels[4] = { NULL, NULL, NULL, NULL };
-
-void DumpDispLuxels( int iFace, Vector &color, int iLuxel, int nBump )
-{
- // Lock the thread and dump the luxel data.
- ThreadLock();
-
- // Get the face and facelight data.
- facelight_t *pFaceLight = &facelight[iFace];
-
- // Open the luxel files.
- char szFileName[512];
- for ( int iBump = 0; iBump < ( NUM_BUMP_VECTS+1 ); ++iBump )
- {
- if ( pFileLuxels[iBump] == NULL )
- {
- sprintf( szFileName, "luxels_bump%d.txt", iBump );
- pFileLuxels[iBump] = g_pFileSystem->Open( szFileName, "w" );
- }
- }
-
- WriteWinding( pFileLuxels[nBump], pFaceLight->sample[iLuxel].w, color );
-
- ThreadUnlock();
-}
-
-void CloseDispLuxels()
-{
- for ( int iBump = 0; iBump < ( NUM_BUMP_VECTS+1 ); ++iBump )
- {
- if ( pFileLuxels[iBump] )
- {
- g_pFileSystem->Close( pFileLuxels[iBump] );
- }
- }
-}
-
-/*
-=============
-FinalLightFace
-
-Add the indirect lighting on top of the direct
-lighting and save into final map format
-=============
-*/
-void FinalLightFace( int iThread, int facenum )
-{
- dface_t *f;
- int i, j, k;
- facelight_t *fl;
- float minlight;
- int lightstyles;
- LightingValue_t lb[NUM_BUMP_VECTS + 1], v[NUM_BUMP_VECTS + 1];
- unsigned char *pdata[NUM_BUMP_VECTS + 1];
- int bumpSample;
- radial_t *rad = NULL;
- radial_t *prad = NULL;
-
- f = &g_pFaces[facenum];
-
- // test for non-lit texture
- if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
- return;
-
- fl = &facelight[facenum];
-
-
- for (lightstyles=0; lightstyles < MAXLIGHTMAPS; lightstyles++ )
- {
- if ( f->styles[lightstyles] == 255 )
- break;
- }
- if ( !lightstyles )
- return;
-
-
- //
- // sample the triangulation
- //
- minlight = FloatForKey (face_entity[facenum], "_minlight") * 128;
-
- bool needsBumpmap = ( texinfo[f->texinfo].flags & SURF_BUMPLIGHT ) ? true : false;
- int bumpSampleCount = needsBumpmap ? NUM_BUMP_VECTS + 1 : 1;
-
- bool bDisp = ( f->dispinfo != -1 );
-
-//#define RANDOM_COLOR
-
-#ifdef RANDOM_COLOR
- unsigned char randomColor[3];
- GetRandomColor( randomColor );
-#endif
-
-
- // NOTE: I'm using these RB trees to sort all the illumination values
- // to compute median colors. Turns out that this is a somewhat better
- // method that using the average; usually if there are surfaces
- // with a large light intensity variation, the extremely bright regions
- // have a very small area and tend to influence the average too much.
- CUtlRBTree< float, int > m_Red( 0, 256, FloatLess );
- CUtlRBTree< float, int > m_Green( 0, 256, FloatLess );
- CUtlRBTree< float, int > m_Blue( 0, 256, FloatLess );
-
- for (k=0 ; k < lightstyles; k++ )
- {
- m_Red.RemoveAll();
- m_Green.RemoveAll();
- m_Blue.RemoveAll();
-
- if (!do_fast)
- {
- if( !bDisp )
- {
- rad = BuildLuxelRadial( facenum, k );
- }
- else
- {
- rad = StaticDispMgr()->BuildLuxelRadial( facenum, k, needsBumpmap );
- }
- }
-
- if (numbounce > 0 && k == 0)
- {
- // currently only radiosity light non-displacement surfaces!
- if( !bDisp )
- {
- prad = BuildPatchRadial( facenum );
- }
- else
- {
- prad = StaticDispMgr()->BuildPatchRadial( facenum, needsBumpmap );
- }
- }
-
- // pack the nonbump texture and the three bump texture for the given
- // lightstyle right next to each other.
- // NOTE: Even though it's building positions for all bump-mapped data,
- // it isn't going to use those positions (see loop over bumpSample below)
- // The file offset is correctly computed to only store space for 1 set
- // of light data if we don't have bumped lighting.
- for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
- {
- pdata[bumpSample] = &(*pdlightdata)[f->lightofs + (k * bumpSampleCount + bumpSample) * fl->numluxels*4];
- }
-
- // Compute the average luxel color, but not for the bump samples
- Vector avg( 0.0f, 0.0f, 0.0f );
- int avgCount = 0;
-
- for (j=0 ; j<fl->numluxels; j++)
- {
- // garymct - direct lighting
- bool baseSampleOk = true;
-
- if (!do_fast)
- {
- if( !bDisp )
- {
- baseSampleOk = SampleRadial( rad, fl->luxel[j], lb, bumpSampleCount );
- }
- else
- {
- baseSampleOk = StaticDispMgr()->SampleRadial( facenum, rad, fl->luxel[j], j, lb, bumpSampleCount, false );
- }
- }
- else
- {
- for ( int iBump = 0 ; iBump < bumpSampleCount; iBump++ )
- {
- lb[iBump] = fl->light[0][iBump][j];
- }
- }
-
- if (prad)
- {
- // garymct - bounced light
- // v is indirect light that is received on the luxel.
- if( !bDisp )
- {
- SampleRadial( prad, fl->luxel[j], v, bumpSampleCount );
- }
- else
- {
- StaticDispMgr()->SampleRadial( facenum, prad, fl->luxel[j], j, v, bumpSampleCount, true );
- }
-
- for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
- {
- lb[bumpSample].AddLight( v[bumpSample] );
- }
- }
-
- if ( bDisp && g_bDumpPatches )
- {
- for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
- {
- DumpDispLuxels( facenum, lb[bumpSample].m_vecLighting, j, bumpSample );
- }
- }
-
- if (fl->numsamples == 0)
- {
- for( i = 0; i < bumpSampleCount; i++ )
- {
- lb[i].Init( 255, 0, 0 );
- }
- baseSampleOk = false;
- }
-
- int bumpSample;
- for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
- {
- // clip from the bottom first
- // garymct: minlight is a per entity minimum light value?
- for( i=0; i<3; i++ )
- {
- lb[bumpSample].m_vecLighting[i] = max( lb[bumpSample].m_vecLighting[i], minlight );
- }
-
- // Do the average light computation, I'm assuming (perhaps incorrectly?)
- // that all luxels in a particular lightmap have the same area here.
- // Also, don't bother doing averages for the bump samples. Doing it here
- // because of the minlight clamp above + the random color testy thingy.
- // Also have to do it before Vec3toColorRGBExp32 because it
- // destructively modifies lb[bumpSample] (Feh!)
- if ((bumpSample == 0) && baseSampleOk)
- {
- ++avgCount;
-
- ApplyMacroTextures( facenum, fl->luxel[j], lb[0].m_vecLighting );
-
- // For median computation
- m_Red.Insert( lb[bumpSample].m_vecLighting[0] );
- m_Green.Insert( lb[bumpSample].m_vecLighting[1] );
- m_Blue.Insert( lb[bumpSample].m_vecLighting[2] );
- }
-
-#ifdef RANDOM_COLOR
- pdata[bumpSample][0] = randomColor[0] / ( bumpSample + 1 );
- pdata[bumpSample][1] = randomColor[1] / ( bumpSample + 1 );
- pdata[bumpSample][2] = randomColor[2] / ( bumpSample + 1 );
- pdata[bumpSample][3] = 0;
-#else
- // convert to a 4 byte r,g,b,signed exponent format
- VectorToColorRGBExp32( Vector( lb[bumpSample].m_vecLighting.x, lb[bumpSample].m_vecLighting.y,
- lb[bumpSample].m_vecLighting.z ), *( ColorRGBExp32 *)pdata[bumpSample] );
-#endif
-
- pdata[bumpSample] += 4;
- }
- }
- FreeRadial( rad );
- if (prad)
- {
- FreeRadial( prad );
- prad = NULL;
- }
-
- // Compute the median color for this lightstyle
- // Remember, the data goes *before* the specified light_ofs, in *reverse order*
- ColorRGBExp32 *pAvgColor = dface_AvgLightColor( f, k );
- if (avgCount == 0)
- {
- Vector median( 0, 0, 0 );
- VectorToColorRGBExp32( median, *pAvgColor );
- }
- else
- {
- unsigned int r, g, b;
- r = m_Red.FirstInorder();
- g = m_Green.FirstInorder();
- b = m_Blue.FirstInorder();
- avgCount >>= 1;
- while (avgCount > 0)
- {
- r = m_Red.NextInorder(r);
- g = m_Green.NextInorder(g);
- b = m_Blue.NextInorder(b);
- --avgCount;
- }
-
- Vector median( m_Red[r], m_Green[g], m_Blue[b] );
- VectorToColorRGBExp32( median, *pAvgColor );
- }
- }
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vrad.h"
+#include "lightmap.h"
+#include "radial.h"
+#include "mathlib/bumpvects.h"
+#include "utlrbtree.h"
+#include "mathlib/VMatrix.h"
+#include "macro_texture.h"
+
+
+void WorldToLuxelSpace( lightinfo_t const *l, Vector const &world, Vector2D &coord )
+{
+ Vector pos;
+
+ VectorSubtract( world, l->luxelOrigin, pos );
+ coord[0] = DotProduct( pos, l->worldToLuxelSpace[0] ) - l->face->m_LightmapTextureMinsInLuxels[0];
+ coord[1] = DotProduct( pos, l->worldToLuxelSpace[1] ) - l->face->m_LightmapTextureMinsInLuxels[1];
+}
+
+void LuxelSpaceToWorld( lightinfo_t const *l, float s, float t, Vector &world )
+{
+ Vector pos;
+
+ s += l->face->m_LightmapTextureMinsInLuxels[0];
+ t += l->face->m_LightmapTextureMinsInLuxels[1];
+ VectorMA( l->luxelOrigin, s, l->luxelToWorldSpace[0], pos );
+ VectorMA( pos, t, l->luxelToWorldSpace[1], world );
+}
+
+void WorldToLuxelSpace( lightinfo_t const *l, FourVectors const &world, FourVectors &coord )
+{
+ FourVectors luxelOrigin;
+ luxelOrigin.DuplicateVector ( l->luxelOrigin );
+
+ FourVectors pos = world;
+ pos -= luxelOrigin;
+
+ coord.x = pos * l->worldToLuxelSpace[0];
+ coord.x = SubSIMD ( coord.x, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[0] ) );
+ coord.y = pos * l->worldToLuxelSpace[1];
+ coord.y = SubSIMD ( coord.y, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[1] ) );
+ coord.z = Four_Zeros;
+}
+
+void LuxelSpaceToWorld( lightinfo_t const *l, fltx4 s, fltx4 t, FourVectors &world )
+{
+ world.DuplicateVector ( l->luxelOrigin );
+ FourVectors st;
+
+ s = AddSIMD ( s, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[0] ) );
+ st.DuplicateVector ( l->luxelToWorldSpace[0] );
+ st *= s;
+ world += st;
+
+ t = AddSIMD ( t, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[1] ) );
+ st.DuplicateVector ( l->luxelToWorldSpace[1] );
+ st *= t;
+ world += st;
+}
+
+
+
+void AddDirectToRadial( radial_t *rad,
+ Vector const &pnt,
+ Vector2D const &coordmins, Vector2D const &coordmaxs,
+ LightingValue_t const light[NUM_BUMP_VECTS+1],
+ bool hasBumpmap, bool neighborHasBumpmap )
+{
+ int s_min, s_max, t_min, t_max;
+ Vector2D coord;
+ int s, t;
+ float ds, dt;
+ float r;
+ float area;
+ int bumpSample;
+
+ // convert world pos into local lightmap texture coord
+ WorldToLuxelSpace( &rad->l, pnt, coord );
+
+ s_min = ( int )( coordmins[0] );
+ t_min = ( int )( coordmins[1] );
+ s_max = ( int )( coordmaxs[0] + 0.9999f ) + 1; // ????
+ t_max = ( int )( coordmaxs[1] + 0.9999f ) + 1;
+
+ s_min = max( s_min, 0 );
+ t_min = max( t_min, 0 );
+ s_max = min( s_max, rad->w );
+ t_max = min( t_max, rad->h );
+
+ for( s = s_min; s < s_max; s++ )
+ {
+ for( t = t_min; t < t_max; t++ )
+ {
+ float s0 = max( coordmins[0] - s, -1.0 );
+ float t0 = max( coordmins[1] - t, -1.0 );
+ float s1 = min( coordmaxs[0] - s, 1.0 );
+ float t1 = min( coordmaxs[1] - t, 1.0 );
+
+ area = (s1 - s0) * (t1 - t0);
+
+ if (area > EQUAL_EPSILON)
+ {
+ ds = fabs( coord[0] - s );
+ dt = fabs( coord[1] - t );
+
+ r = max( ds, dt );
+
+ if (r < 0.1)
+ {
+ r = area / 0.1;
+ }
+ else
+ {
+ r = area / r;
+ }
+
+ int i = s+t*rad->w;
+
+ if( hasBumpmap )
+ {
+ if( neighborHasBumpmap )
+ {
+ for( bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ rad->light[bumpSample][i].AddWeighted( light[bumpSample], r );
+ }
+ }
+ else
+ {
+ rad->light[0][i].AddWeighted(light[0],r );
+ for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ rad->light[bumpSample][i].AddWeighted( light[0], r * OO_SQRT_3 );
+ }
+ }
+ }
+ else
+ {
+ rad->light[0][i].AddWeighted( light[0], r );
+ }
+
+ rad->weight[i] += r;
+ }
+ }
+ }
+}
+
+
+
+void AddBouncedToRadial( radial_t *rad,
+ Vector const &pnt,
+ Vector2D const &coordmins, Vector2D const &coordmaxs,
+ Vector const light[NUM_BUMP_VECTS+1],
+ bool hasBumpmap, bool neighborHasBumpmap )
+{
+ int s_min, s_max, t_min, t_max;
+ Vector2D coord;
+ int s, t;
+ float ds, dt;
+ float r;
+ int bumpSample;
+
+ // convert world pos into local lightmap texture coord
+ WorldToLuxelSpace( &rad->l, pnt, coord );
+
+ float dists, distt;
+
+ dists = (coordmaxs[0] - coordmins[0]);
+ distt = (coordmaxs[1] - coordmins[1]);
+
+ // patches less than a luxel in size could be mistakeningly filtered, so clamp.
+ dists = max( 1.0, dists );
+ distt = max( 1.0, distt );
+
+ // find possible domain of patch influence
+ s_min = ( int )( coord[0] - dists * RADIALDIST );
+ t_min = ( int )( coord[1] - distt * RADIALDIST );
+ s_max = ( int )( coord[0] + dists * RADIALDIST + 1.0f );
+ t_max = ( int )( coord[1] + distt * RADIALDIST + 1.0f );
+
+ // clamp to valid luxel
+ s_min = max( s_min, 0 );
+ t_min = max( t_min, 0 );
+ s_max = min( s_max, rad->w );
+ t_max = min( t_max, rad->h );
+
+ for( s = s_min; s < s_max; s++ )
+ {
+ for( t = t_min; t < t_max; t++ )
+ {
+ // patch influence is based on patch size
+ ds = ( coord[0] - s ) / dists;
+ dt = ( coord[1] - t ) / distt;
+
+ r = RADIALDIST2 - (ds * ds + dt * dt);
+
+ int i = s+t*rad->w;
+
+ if (r > 0)
+ {
+ if( hasBumpmap )
+ {
+ if( neighborHasBumpmap )
+ {
+ for( bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ rad->light[bumpSample][i].AddWeighted( light[bumpSample], r );
+ }
+ }
+ else
+ {
+ rad->light[0][i].AddWeighted( light[0], r );
+ for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ rad->light[bumpSample][i].AddWeighted( light[0], r * OO_SQRT_3 );
+ }
+ }
+ }
+ else
+ {
+ rad->light[0][i].AddWeighted( light[0], r );
+ }
+
+ rad->weight[i] += r;
+ }
+ }
+ }
+}
+
+void PatchLightmapCoordRange( radial_t *rad, int ndxPatch, Vector2D &mins, Vector2D &maxs )
+{
+ winding_t *w;
+ int i;
+ Vector2D coord;
+
+ mins.Init( 1E30, 1E30 );
+ maxs.Init( -1E30, -1E30 );
+
+ CPatch *patch = &g_Patches.Element( ndxPatch );
+ w = patch->winding;
+
+ for (i = 0; i < w->numpoints; i++)
+ {
+ WorldToLuxelSpace( &rad->l, w->p[i], coord );
+ mins[0] = min( mins[0], coord[0] );
+ maxs[0] = max( maxs[0], coord[0] );
+ mins[1] = min( mins[1], coord[1] );
+ maxs[1] = max( maxs[1], coord[1] );
+ }
+}
+
+radial_t *AllocateRadial( int facenum )
+{
+ radial_t *rad;
+
+ rad = ( radial_t* )calloc( 1, sizeof( *rad ) );
+
+ rad->facenum = facenum;
+ InitLightinfo( &rad->l, facenum );
+
+ rad->w = rad->l.face->m_LightmapTextureSizeInLuxels[0]+1;
+ rad->h = rad->l.face->m_LightmapTextureSizeInLuxels[1]+1;
+
+ return rad;
+}
+
+void FreeRadial( radial_t *rad )
+{
+ if (rad)
+ free( rad );
+}
+
+
+radial_t *BuildPatchRadial( int facenum )
+{
+ int j;
+ radial_t *rad;
+ CPatch *patch;
+ faceneighbor_t *fn;
+ Vector2D mins, maxs;
+ bool needsBumpmap, neighborNeedsBumpmap;
+
+ needsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
+
+ rad = AllocateRadial( facenum );
+
+ fn = &faceneighbor[ rad->facenum ];
+
+ CPatch *pNextPatch;
+
+ if( g_FacePatches.Element( rad->facenum ) != g_FacePatches.InvalidIndex() )
+ {
+ for( patch = &g_Patches.Element( g_FacePatches.Element( rad->facenum ) ); patch; patch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNext );
+ }
+
+ // skip patches with children
+ if (patch->child1 != g_Patches.InvalidIndex() )
+ continue;
+
+ // get the range of patch lightmap texture coords
+ int ndxPatch = patch - g_Patches.Base();
+ PatchLightmapCoordRange( rad, ndxPatch, mins, maxs );
+
+ if (patch->numtransfers == 0)
+ {
+ // Error, using patch that was never evaluated or has no samples
+ // patch->totallight[1] = 255;
+ }
+
+ //
+ // displacement surface patch origin position and normal vectors have been changed to
+ // represent the displacement surface position and normal -- for radial "blending"
+ // we need to get the base surface patch origin!
+ //
+ if( ValidDispFace( &g_pFaces[facenum] ) )
+ {
+ Vector patchOrigin;
+ WindingCenter (patch->winding, patchOrigin );
+ AddBouncedToRadial( rad, patchOrigin, mins, maxs, patch->totallight.light,
+ needsBumpmap, needsBumpmap );
+ }
+ else
+ {
+ AddBouncedToRadial( rad, patch->origin, mins, maxs, patch->totallight.light,
+ needsBumpmap, needsBumpmap );
+ }
+ }
+ }
+
+ for (j=0 ; j<fn->numneighbors; j++)
+ {
+ if( g_FacePatches.Element( fn->neighbor[j] ) != g_FacePatches.InvalidIndex() )
+ {
+ for( patch = &g_Patches.Element( g_FacePatches.Element( fn->neighbor[j] ) ); patch; patch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNext );
+ }
+
+ // skip patches with children
+ if (patch->child1 != g_Patches.InvalidIndex() )
+ continue;
+
+ // get the range of patch lightmap texture coords
+ int ndxPatch = patch - g_Patches.Base();
+ PatchLightmapCoordRange( rad, ndxPatch, mins, maxs );
+
+ neighborNeedsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
+
+ //
+ // displacement surface patch origin position and normal vectors have been changed to
+ // represent the displacement surface position and normal -- for radial "blending"
+ // we need to get the base surface patch origin!
+ //
+ if( ValidDispFace( &g_pFaces[fn->neighbor[j]] ) )
+ {
+ Vector patchOrigin;
+ WindingCenter (patch->winding, patchOrigin );
+ AddBouncedToRadial( rad, patchOrigin, mins, maxs, patch->totallight.light,
+ needsBumpmap, needsBumpmap );
+ }
+ else
+ {
+ AddBouncedToRadial( rad, patch->origin, mins, maxs, patch->totallight.light,
+ needsBumpmap, needsBumpmap );
+ }
+ }
+ }
+ }
+
+ return rad;
+}
+
+
+radial_t *BuildLuxelRadial( int facenum, int style )
+{
+ LightingValue_t light[NUM_BUMP_VECTS + 1];
+
+ facelight_t *fl = &facelight[facenum];
+ faceneighbor_t *fn = &faceneighbor[facenum];
+
+ radial_t *rad = AllocateRadial( facenum );
+
+ bool needsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
+
+ for (int k=0 ; k<fl->numsamples ; k++)
+ {
+ if( needsBumpmap )
+ {
+ for( int bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ light[bumpSample] = fl->light[style][bumpSample][k];
+ }
+ }
+ else
+ {
+ light[0] = fl->light[style][0][k];
+ }
+
+ AddDirectToRadial( rad, fl->sample[k].pos, fl->sample[k].mins, fl->sample[k].maxs, light, needsBumpmap, needsBumpmap );
+ }
+
+ for (int j = 0; j < fn->numneighbors; j++)
+ {
+ fl = &facelight[fn->neighbor[j]];
+
+ bool neighborHasBumpmap = false;
+
+ if( texinfo[g_pFaces[fn->neighbor[j]].texinfo].flags & SURF_BUMPLIGHT )
+ {
+ neighborHasBumpmap = true;
+ }
+
+ int nstyle = 0;
+
+ // look for style that matches
+ if (g_pFaces[fn->neighbor[j]].styles[nstyle] != g_pFaces[facenum].styles[style])
+ {
+ for (nstyle = 1; nstyle < MAXLIGHTMAPS; nstyle++ )
+ if ( g_pFaces[fn->neighbor[j]].styles[nstyle] == g_pFaces[facenum].styles[style] )
+ break;
+
+ // if not found, skip this neighbor
+ if (nstyle >= MAXLIGHTMAPS)
+ continue;
+ }
+
+ lightinfo_t l;
+
+ InitLightinfo( &l, fn->neighbor[j] );
+
+ for (int k=0 ; k<fl->numsamples ; k++)
+ {
+ if( neighborHasBumpmap )
+ {
+ for( int bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ light[bumpSample] = fl->light[nstyle][bumpSample][k];
+ }
+ }
+ else
+ {
+ light[0]=fl->light[nstyle][0][k];
+ }
+
+ Vector tmp;
+ Vector2D mins, maxs;
+
+ LuxelSpaceToWorld( &l, fl->sample[k].mins[0], fl->sample[k].mins[1], tmp );
+ WorldToLuxelSpace( &rad->l, tmp, mins );
+ LuxelSpaceToWorld( &l, fl->sample[k].maxs[0], fl->sample[k].maxs[1], tmp );
+ WorldToLuxelSpace( &rad->l, tmp, maxs );
+
+ AddDirectToRadial( rad, fl->sample[k].pos, mins, maxs, light,
+ needsBumpmap, neighborHasBumpmap );
+ }
+ }
+
+ return rad;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the closest light value for a given point on the surface
+// this is normally a 1:1 mapping
+//-----------------------------------------------------------------------------
+bool SampleRadial( radial_t *rad, Vector& pnt, LightingValue_t light[NUM_BUMP_VECTS + 1], int bumpSampleCount )
+{
+ int bumpSample;
+ Vector2D coord;
+
+ WorldToLuxelSpace( &rad->l, pnt, coord );
+ int u = ( int )( coord[0] + 0.5f );
+ int v = ( int )( coord[1] + 0.5f );
+ int i = u + v * rad->w;
+
+ if (u < 0 || u > rad->w || v < 0 || v > rad->h)
+ {
+ static bool warning = false;
+ if ( !warning )
+ {
+ // punting over to KenB
+ // 2d coord indexes off of lightmap, generation of pnt seems suspect
+ Warning( "SampleRadial: Punting, Waiting for fix\n" );
+ warning = true;
+ }
+ for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
+ {
+ light[bumpSample].m_vecLighting.Init( 2550, 0, 0 );
+ }
+ return false;
+ }
+
+ bool baseSampleOk = true;
+ for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
+ {
+ light[bumpSample].Zero();
+
+ if (rad->weight[i] > WEIGHT_EPS)
+ {
+ light[bumpSample]= rad->light[bumpSample][i];
+ light[bumpSample].Scale( 1.0 / rad->weight[i] );
+ }
+ else
+ {
+ if ( bRed2Black )
+ {
+ // Error, luxel has no samples
+ light[bumpSample].m_vecLighting.Init( 0, 0, 0 );
+ }
+ else
+ {
+ // Error, luxel has no samples
+ // Yes, it actually should be 2550
+ light[bumpSample].m_vecLighting.Init( 2550, 0, 0 );
+ }
+
+ if (bumpSample == 0)
+ baseSampleOk = false;
+ }
+ }
+
+ return baseSampleOk;
+}
+
+bool FloatLess( float const& src1, float const& src2 )
+{
+ return src1 < src2;
+}
+
+
+//-----------------------------------------------------------------------------
+// Debugging!
+//-----------------------------------------------------------------------------
+void GetRandomColor( unsigned char *color )
+{
+ static bool firstTime = true;
+
+ if( firstTime )
+ {
+ firstTime = false;
+ srand( 0 );
+ }
+
+ color[0] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
+ color[1] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
+ color[2] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
+}
+
+
+#if 0
+// debugging! -- not accurate!
+void DumpLuxels( facelight_t *pFaceLight, Vector *luxelColors, int ndxFace )
+{
+ static FileHandle_t pFpLuxels = NULL;
+
+ ThreadLock();
+
+ if( !pFpLuxels )
+ {
+ pFpLuxels = g_pFileSystem->Open( "luxels.txt", "w" );
+ }
+
+ dface_t *pFace = &g_pFaces[ndxFace];
+ bool bDisp = ( pFace->dispinfo != -1 );
+
+ for( int ndx = 0; ndx < pFaceLight->numluxels; ndx++ )
+ {
+ WriteWinding( pFpLuxels, pFaceLight->sample[ndx].w, luxelColors[ndx] );
+ if( bDumpNormals && bDisp )
+ {
+ WriteNormal( pFpLuxels, pFaceLight->luxel[ndx], pFaceLight->luxelNormals[ndx], 15.0f, Vector( 255, 255, 0 ) );
+ }
+ }
+
+ ThreadUnlock();
+}
+#endif
+
+
+static FileHandle_t pFileLuxels[4] = { NULL, NULL, NULL, NULL };
+
+void DumpDispLuxels( int iFace, Vector &color, int iLuxel, int nBump )
+{
+ // Lock the thread and dump the luxel data.
+ ThreadLock();
+
+ // Get the face and facelight data.
+ facelight_t *pFaceLight = &facelight[iFace];
+
+ // Open the luxel files.
+ char szFileName[512];
+ for ( int iBump = 0; iBump < ( NUM_BUMP_VECTS+1 ); ++iBump )
+ {
+ if ( pFileLuxels[iBump] == NULL )
+ {
+ sprintf( szFileName, "luxels_bump%d.txt", iBump );
+ pFileLuxels[iBump] = g_pFileSystem->Open( szFileName, "w" );
+ }
+ }
+
+ WriteWinding( pFileLuxels[nBump], pFaceLight->sample[iLuxel].w, color );
+
+ ThreadUnlock();
+}
+
+void CloseDispLuxels()
+{
+ for ( int iBump = 0; iBump < ( NUM_BUMP_VECTS+1 ); ++iBump )
+ {
+ if ( pFileLuxels[iBump] )
+ {
+ g_pFileSystem->Close( pFileLuxels[iBump] );
+ }
+ }
+}
+
+/*
+=============
+FinalLightFace
+
+Add the indirect lighting on top of the direct
+lighting and save into final map format
+=============
+*/
+void FinalLightFace( int iThread, int facenum )
+{
+ dface_t *f;
+ int i, j, k;
+ facelight_t *fl;
+ float minlight;
+ int lightstyles;
+ LightingValue_t lb[NUM_BUMP_VECTS + 1], v[NUM_BUMP_VECTS + 1];
+ unsigned char *pdata[NUM_BUMP_VECTS + 1];
+ int bumpSample;
+ radial_t *rad = NULL;
+ radial_t *prad = NULL;
+
+ f = &g_pFaces[facenum];
+
+ // test for non-lit texture
+ if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
+ return;
+
+ fl = &facelight[facenum];
+
+
+ for (lightstyles=0; lightstyles < MAXLIGHTMAPS; lightstyles++ )
+ {
+ if ( f->styles[lightstyles] == 255 )
+ break;
+ }
+ if ( !lightstyles )
+ return;
+
+
+ //
+ // sample the triangulation
+ //
+ minlight = FloatForKey (face_entity[facenum], "_minlight") * 128;
+
+ bool needsBumpmap = ( texinfo[f->texinfo].flags & SURF_BUMPLIGHT ) ? true : false;
+ int bumpSampleCount = needsBumpmap ? NUM_BUMP_VECTS + 1 : 1;
+
+ bool bDisp = ( f->dispinfo != -1 );
+
+//#define RANDOM_COLOR
+
+#ifdef RANDOM_COLOR
+ unsigned char randomColor[3];
+ GetRandomColor( randomColor );
+#endif
+
+
+ // NOTE: I'm using these RB trees to sort all the illumination values
+ // to compute median colors. Turns out that this is a somewhat better
+ // method that using the average; usually if there are surfaces
+ // with a large light intensity variation, the extremely bright regions
+ // have a very small area and tend to influence the average too much.
+ CUtlRBTree< float, int > m_Red( 0, 256, FloatLess );
+ CUtlRBTree< float, int > m_Green( 0, 256, FloatLess );
+ CUtlRBTree< float, int > m_Blue( 0, 256, FloatLess );
+
+ for (k=0 ; k < lightstyles; k++ )
+ {
+ m_Red.RemoveAll();
+ m_Green.RemoveAll();
+ m_Blue.RemoveAll();
+
+ if (!do_fast)
+ {
+ if( !bDisp )
+ {
+ rad = BuildLuxelRadial( facenum, k );
+ }
+ else
+ {
+ rad = StaticDispMgr()->BuildLuxelRadial( facenum, k, needsBumpmap );
+ }
+ }
+
+ if (numbounce > 0 && k == 0)
+ {
+ // currently only radiosity light non-displacement surfaces!
+ if( !bDisp )
+ {
+ prad = BuildPatchRadial( facenum );
+ }
+ else
+ {
+ prad = StaticDispMgr()->BuildPatchRadial( facenum, needsBumpmap );
+ }
+ }
+
+ // pack the nonbump texture and the three bump texture for the given
+ // lightstyle right next to each other.
+ // NOTE: Even though it's building positions for all bump-mapped data,
+ // it isn't going to use those positions (see loop over bumpSample below)
+ // The file offset is correctly computed to only store space for 1 set
+ // of light data if we don't have bumped lighting.
+ for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
+ {
+ pdata[bumpSample] = &(*pdlightdata)[f->lightofs + (k * bumpSampleCount + bumpSample) * fl->numluxels*4];
+ }
+
+ // Compute the average luxel color, but not for the bump samples
+ Vector avg( 0.0f, 0.0f, 0.0f );
+ int avgCount = 0;
+
+ for (j=0 ; j<fl->numluxels; j++)
+ {
+ // garymct - direct lighting
+ bool baseSampleOk = true;
+
+ if (!do_fast)
+ {
+ if( !bDisp )
+ {
+ baseSampleOk = SampleRadial( rad, fl->luxel[j], lb, bumpSampleCount );
+ }
+ else
+ {
+ baseSampleOk = StaticDispMgr()->SampleRadial( facenum, rad, fl->luxel[j], j, lb, bumpSampleCount, false );
+ }
+ }
+ else
+ {
+ for ( int iBump = 0 ; iBump < bumpSampleCount; iBump++ )
+ {
+ lb[iBump] = fl->light[0][iBump][j];
+ }
+ }
+
+ if (prad)
+ {
+ // garymct - bounced light
+ // v is indirect light that is received on the luxel.
+ if( !bDisp )
+ {
+ SampleRadial( prad, fl->luxel[j], v, bumpSampleCount );
+ }
+ else
+ {
+ StaticDispMgr()->SampleRadial( facenum, prad, fl->luxel[j], j, v, bumpSampleCount, true );
+ }
+
+ for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
+ {
+ lb[bumpSample].AddLight( v[bumpSample] );
+ }
+ }
+
+ if ( bDisp && g_bDumpPatches )
+ {
+ for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
+ {
+ DumpDispLuxels( facenum, lb[bumpSample].m_vecLighting, j, bumpSample );
+ }
+ }
+
+ if (fl->numsamples == 0)
+ {
+ for( i = 0; i < bumpSampleCount; i++ )
+ {
+ lb[i].Init( 255, 0, 0 );
+ }
+ baseSampleOk = false;
+ }
+
+ int bumpSample;
+ for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
+ {
+ // clip from the bottom first
+ // garymct: minlight is a per entity minimum light value?
+ for( i=0; i<3; i++ )
+ {
+ lb[bumpSample].m_vecLighting[i] = max( lb[bumpSample].m_vecLighting[i], minlight );
+ }
+
+ // Do the average light computation, I'm assuming (perhaps incorrectly?)
+ // that all luxels in a particular lightmap have the same area here.
+ // Also, don't bother doing averages for the bump samples. Doing it here
+ // because of the minlight clamp above + the random color testy thingy.
+ // Also have to do it before Vec3toColorRGBExp32 because it
+ // destructively modifies lb[bumpSample] (Feh!)
+ if ((bumpSample == 0) && baseSampleOk)
+ {
+ ++avgCount;
+
+ ApplyMacroTextures( facenum, fl->luxel[j], lb[0].m_vecLighting );
+
+ // For median computation
+ m_Red.Insert( lb[bumpSample].m_vecLighting[0] );
+ m_Green.Insert( lb[bumpSample].m_vecLighting[1] );
+ m_Blue.Insert( lb[bumpSample].m_vecLighting[2] );
+ }
+
+#ifdef RANDOM_COLOR
+ pdata[bumpSample][0] = randomColor[0] / ( bumpSample + 1 );
+ pdata[bumpSample][1] = randomColor[1] / ( bumpSample + 1 );
+ pdata[bumpSample][2] = randomColor[2] / ( bumpSample + 1 );
+ pdata[bumpSample][3] = 0;
+#else
+ // convert to a 4 byte r,g,b,signed exponent format
+ VectorToColorRGBExp32( Vector( lb[bumpSample].m_vecLighting.x, lb[bumpSample].m_vecLighting.y,
+ lb[bumpSample].m_vecLighting.z ), *( ColorRGBExp32 *)pdata[bumpSample] );
+#endif
+
+ pdata[bumpSample] += 4;
+ }
+ }
+ FreeRadial( rad );
+ if (prad)
+ {
+ FreeRadial( prad );
+ prad = NULL;
+ }
+
+ // Compute the median color for this lightstyle
+ // Remember, the data goes *before* the specified light_ofs, in *reverse order*
+ ColorRGBExp32 *pAvgColor = dface_AvgLightColor( f, k );
+ if (avgCount == 0)
+ {
+ Vector median( 0, 0, 0 );
+ VectorToColorRGBExp32( median, *pAvgColor );
+ }
+ else
+ {
+ unsigned int r, g, b;
+ r = m_Red.FirstInorder();
+ g = m_Green.FirstInorder();
+ b = m_Blue.FirstInorder();
+ avgCount >>= 1;
+ while (avgCount > 0)
+ {
+ r = m_Red.NextInorder(r);
+ g = m_Green.NextInorder(g);
+ b = m_Blue.NextInorder(b);
+ --avgCount;
+ }
+
+ Vector median( m_Red[r], m_Green[g], m_Blue[b] );
+ VectorToColorRGBExp32( median, *pAvgColor );
+ }
+ }
+}
diff --git a/mp/src/utils/vrad/radial.h b/mp/src/utils/vrad/radial.h
index 862167cb..ebf82890 100644
--- a/mp/src/utils/vrad/radial.h
+++ b/mp/src/utils/vrad/radial.h
@@ -1,76 +1,76 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $Workfile: $
-// $Date: $
-//
-//-----------------------------------------------------------------------------
-// $Log: $
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef RADIAL_H
-#define RADIAL_H
-#pragma once
-
-#include "mathlib/bumpvects.h"
-#include "mathlib/ssemath.h"
-#include "lightmap.h"
-
-#define RADIALDIST2 2 // (1.25*1.25+1.25*1.25)
-#define RADIALDIST 1.42 // 1.77 // sqrt( RADIALDIST2 )
-
-#define WEIGHT_EPS 0.00001f
-
-//-----------------------------------------------------------------------------
-// The radial_t data structure is used to accumulate irregularly spaced and irregularly
-// shaped direct and indirect lighting samples into a uniformly spaced and shaped luxel grid.
-//
-// The name "radial" is more historical than discriptive; it stems from the filtering method,
-// one of several methods initially tried. Since all the other methods have since been deleted,
-// it would probably be more accurate to rename it something like "LuxelAccumulationBucket" or
-// something similar, but since "radial" is fairly meaningless it's not like it's actually confusing
-// the issue.
-//-----------------------------------------------------------------------------
-typedef struct radial_s
-{
- int facenum;
- lightinfo_t l;
- int w, h;
- float weight[SINGLEMAP];
- LightingValue_t light[NUM_BUMP_VECTS + 1][SINGLEMAP];
-} radial_t;
-
-
-void WorldToLuxelSpace( lightinfo_t const *l, Vector const &world, Vector2D &coord );
-void LuxelSpaceToWorld( lightinfo_t const *l, float s, float t, Vector &world );
-
-void WorldToLuxelSpace( lightinfo_t const *l, FourVectors const &world, FourVectors &coord );
-void LuxelSpaceToWorld( lightinfo_t const *l, fltx4 s, fltx4 t, FourVectors &world );
-
-void AddDirectToRadial( radial_t *rad,
- Vector const &pnt,
- Vector2D const &coordmins, Vector2D const &coordmaxs,
- Vector const light[NUM_BUMP_VECTS+1],
- bool hasBumpmap, bool neighborHasBumpmap );
-
-void AddBounceToRadial( radial_t *rad,
- Vector const &pnt,
- Vector2D const &coordmins, Vector2D const &coordmaxs,
- Vector const light[NUM_BUMP_VECTS+1],
- bool hasBumpmap, bool neighborHasBumpmap );
-
-bool SampleRadial( radial_t *rad, Vector& pnt, Vector light[NUM_BUMP_VECTS+1], int bumpSampleCount );
-
-radial_t *AllocateRadial( int facenum );
-void FreeRadial( radial_t *rad );
-
-bool SampleRadial( radial_t *rad, Vector& pnt, Vector light[NUM_BUMP_VECTS + 1], int bumpSampleCount );
-radial_t *BuildPatchRadial( int facenum );
-
-// utilities
-bool FloatLess( float const& src1, float const& src2 );
-
-#endif
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef RADIAL_H
+#define RADIAL_H
+#pragma once
+
+#include "mathlib/bumpvects.h"
+#include "mathlib/ssemath.h"
+#include "lightmap.h"
+
+#define RADIALDIST2 2 // (1.25*1.25+1.25*1.25)
+#define RADIALDIST 1.42 // 1.77 // sqrt( RADIALDIST2 )
+
+#define WEIGHT_EPS 0.00001f
+
+//-----------------------------------------------------------------------------
+// The radial_t data structure is used to accumulate irregularly spaced and irregularly
+// shaped direct and indirect lighting samples into a uniformly spaced and shaped luxel grid.
+//
+// The name "radial" is more historical than discriptive; it stems from the filtering method,
+// one of several methods initially tried. Since all the other methods have since been deleted,
+// it would probably be more accurate to rename it something like "LuxelAccumulationBucket" or
+// something similar, but since "radial" is fairly meaningless it's not like it's actually confusing
+// the issue.
+//-----------------------------------------------------------------------------
+typedef struct radial_s
+{
+ int facenum;
+ lightinfo_t l;
+ int w, h;
+ float weight[SINGLEMAP];
+ LightingValue_t light[NUM_BUMP_VECTS + 1][SINGLEMAP];
+} radial_t;
+
+
+void WorldToLuxelSpace( lightinfo_t const *l, Vector const &world, Vector2D &coord );
+void LuxelSpaceToWorld( lightinfo_t const *l, float s, float t, Vector &world );
+
+void WorldToLuxelSpace( lightinfo_t const *l, FourVectors const &world, FourVectors &coord );
+void LuxelSpaceToWorld( lightinfo_t const *l, fltx4 s, fltx4 t, FourVectors &world );
+
+void AddDirectToRadial( radial_t *rad,
+ Vector const &pnt,
+ Vector2D const &coordmins, Vector2D const &coordmaxs,
+ Vector const light[NUM_BUMP_VECTS+1],
+ bool hasBumpmap, bool neighborHasBumpmap );
+
+void AddBounceToRadial( radial_t *rad,
+ Vector const &pnt,
+ Vector2D const &coordmins, Vector2D const &coordmaxs,
+ Vector const light[NUM_BUMP_VECTS+1],
+ bool hasBumpmap, bool neighborHasBumpmap );
+
+bool SampleRadial( radial_t *rad, Vector& pnt, Vector light[NUM_BUMP_VECTS+1], int bumpSampleCount );
+
+radial_t *AllocateRadial( int facenum );
+void FreeRadial( radial_t *rad );
+
+bool SampleRadial( radial_t *rad, Vector& pnt, Vector light[NUM_BUMP_VECTS + 1], int bumpSampleCount );
+radial_t *BuildPatchRadial( int facenum );
+
+// utilities
+bool FloatLess( float const& src1, float const& src2 );
+
+#endif
diff --git a/mp/src/utils/vrad/samplehash.cpp b/mp/src/utils/vrad/samplehash.cpp
index fb35d351..09bc94ec 100644
--- a/mp/src/utils/vrad/samplehash.cpp
+++ b/mp/src/utils/vrad/samplehash.cpp
@@ -1,230 +1,230 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#include "vrad.h"
-#include "lightmap.h"
-
-#define SAMPLEHASH_NUM_BUCKETS 65536
-#define SAMPLEHASH_GROW_SIZE 0
-#define SAMPLEHASH_INIT_SIZE 0
-
-int samplesAdded = 0;
-int patchSamplesAdded = 0;
-static unsigned short g_PatchIterationKey = 0;
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool SampleData_CompareFunc( SampleData_t const &src1, SampleData_t const &src2 )
-{
- return ( ( src1.x == src2.x ) &&
- ( src1.y == src2.y ) &&
- ( src1.z == src2.z ) );
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-unsigned int SampleData_KeyFunc( SampleData_t const &src )
-{
- return ( src.x + src.y + src.z );
-}
-
-
-CUtlHash<SampleData_t> g_SampleHashTable( SAMPLEHASH_NUM_BUCKETS,
- SAMPLEHASH_GROW_SIZE,
- SAMPLEHASH_INIT_SIZE,
- SampleData_CompareFunc, SampleData_KeyFunc );
-
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-UtlHashHandle_t SampleData_Find( sample_t *pSample )
-{
- SampleData_t sampleData;
- sampleData.x = ( int )( pSample->pos.x / SAMPLEHASH_VOXEL_SIZE ) * 100;
- sampleData.y = ( int )( pSample->pos.y / SAMPLEHASH_VOXEL_SIZE ) * 10;
- sampleData.z = ( int )( pSample->pos.z / SAMPLEHASH_VOXEL_SIZE );
-
- return g_SampleHashTable.Find( sampleData );
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-UtlHashHandle_t SampleData_InsertIntoHashTable( sample_t *pSample, SampleHandle_t sampleHandle )
-{
- SampleData_t sampleData;
- sampleData.x = ( int )( pSample->pos.x / SAMPLEHASH_VOXEL_SIZE ) * 100;
- sampleData.y = ( int )( pSample->pos.y / SAMPLEHASH_VOXEL_SIZE ) * 10;
- sampleData.z = ( int )( pSample->pos.z / SAMPLEHASH_VOXEL_SIZE );
-
- UtlHashHandle_t handle = g_SampleHashTable.AllocEntryFromKey( sampleData );
-
- SampleData_t *pSampleData = &g_SampleHashTable.Element( handle );
- pSampleData->x = sampleData.x;
- pSampleData->y = sampleData.y;
- pSampleData->z = sampleData.z;
- pSampleData->m_Samples.AddToTail( sampleHandle );
-
- samplesAdded++;
-
- return handle;
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-UtlHashHandle_t SampleData_AddSample( sample_t *pSample, SampleHandle_t sampleHandle )
-{
-
- // find the key -- if it doesn't exist add new sample data to the
- // hash table
- UtlHashHandle_t handle = SampleData_Find( pSample );
- if( handle == g_SampleHashTable.InvalidHandle() )
- {
- handle = SampleData_InsertIntoHashTable( pSample, sampleHandle );
- }
- else
- {
- SampleData_t *pSampleData = &g_SampleHashTable.Element( handle );
- pSampleData->m_Samples.AddToTail( sampleHandle );
-
- samplesAdded++;
- }
-
- return handle;
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void SampleData_Log( void )
-{
- if( g_bLogHashData )
- {
- g_SampleHashTable.Log( "samplehash.txt" );
- }
-}
-
-
-//=============================================================================
-//=============================================================================
-//
-// PatchSample Functions
-//
-//=============================================================================
-//=============================================================================
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool PatchSampleData_CompareFunc( PatchSampleData_t const &src1, PatchSampleData_t const &src2 )
-{
- return ( ( src1.x == src2.x ) &&
- ( src1.y == src2.y ) &&
- ( src1.z == src2.z ) );
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-unsigned int PatchSampleData_KeyFunc( PatchSampleData_t const &src )
-{
- return ( src.x + src.y + src.z );
-}
-
-
-CUtlHash<PatchSampleData_t> g_PatchSampleHashTable( SAMPLEHASH_NUM_BUCKETS,
- SAMPLEHASH_GROW_SIZE,
- SAMPLEHASH_INIT_SIZE,
- PatchSampleData_CompareFunc, PatchSampleData_KeyFunc );
-
-void GetPatchSampleHashXYZ( const Vector &vOrigin, int &x, int &y, int &z )
-{
- x = ( int )( vOrigin.x / SAMPLEHASH_VOXEL_SIZE );
- y = ( int )( vOrigin.y / SAMPLEHASH_VOXEL_SIZE );
- z = ( int )( vOrigin.z / SAMPLEHASH_VOXEL_SIZE );
-}
-
-
-unsigned short IncrementPatchIterationKey()
-{
- if ( g_PatchIterationKey == 0xFFFF )
- {
- g_PatchIterationKey = 1;
- for ( int i=0; i < g_Patches.Count(); i++ )
- g_Patches[i].m_IterationKey = 0;
- }
- else
- {
- g_PatchIterationKey++;
- }
- return g_PatchIterationKey;
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void PatchSampleData_AddSample( CPatch *pPatch, int ndxPatch )
-{
- int patchSampleMins[3], patchSampleMaxs[3];
-
-#if defined( SAMPLEHASH_USE_AREA_PATCHES )
- GetPatchSampleHashXYZ( pPatch->mins, patchSampleMins[0], patchSampleMins[1], patchSampleMins[2] );
- GetPatchSampleHashXYZ( pPatch->maxs, patchSampleMaxs[0], patchSampleMaxs[1], patchSampleMaxs[2] );
-#else
- // If not using area patches, just use the patch's origin to add it to the voxels.
- GetPatchSampleHashXYZ( pPatch->origin, patchSampleMins[0], patchSampleMins[1], patchSampleMins[2] );
- memcpy( patchSampleMaxs, patchSampleMins, sizeof( patchSampleMaxs ) );
-#endif
-
- // Make sure mins are smaller than maxs so we don't iterate for 4 bil.
- Assert( patchSampleMins[0] <= patchSampleMaxs[0] && patchSampleMins[1] <= patchSampleMaxs[1] && patchSampleMins[2] <= patchSampleMaxs[2] );
- patchSampleMins[0] = min( patchSampleMins[0], patchSampleMaxs[0] );
- patchSampleMins[1] = min( patchSampleMins[1], patchSampleMaxs[1] );
- patchSampleMins[2] = min( patchSampleMins[2], patchSampleMaxs[2] );
-
- int iterateCoords[3];
- for ( iterateCoords[0]=patchSampleMins[0]; iterateCoords[0] <= patchSampleMaxs[0]; iterateCoords[0]++ )
- {
- for ( iterateCoords[1]=patchSampleMins[1]; iterateCoords[1] <= patchSampleMaxs[1]; iterateCoords[1]++ )
- {
- for ( iterateCoords[2]=patchSampleMins[2]; iterateCoords[2] <= patchSampleMaxs[2]; iterateCoords[2]++ )
- {
- // find the key -- if it doesn't exist add new sample data to the
- // hash table
- PatchSampleData_t iteratePatch;
- iteratePatch.x = iterateCoords[0] * 100;
- iteratePatch.y = iterateCoords[1] * 10;
- iteratePatch.z = iterateCoords[2];
-
- UtlHashHandle_t handle = g_PatchSampleHashTable.Find( iteratePatch );
- if( handle == g_PatchSampleHashTable.InvalidHandle() )
- {
- UtlHashHandle_t handle = g_PatchSampleHashTable.AllocEntryFromKey( iteratePatch );
-
- PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element( handle );
- pPatchData->x = iteratePatch.x;
- pPatchData->y = iteratePatch.y;
- pPatchData->z = iteratePatch.z;
- pPatchData->m_ndxPatches.AddToTail( ndxPatch );
-
- patchSamplesAdded++;
- }
- else
- {
- PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element( handle );
- pPatchData->m_ndxPatches.AddToTail( ndxPatch );
-
- patchSamplesAdded++;
- }
- }
- }
- }
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vrad.h"
+#include "lightmap.h"
+
+#define SAMPLEHASH_NUM_BUCKETS 65536
+#define SAMPLEHASH_GROW_SIZE 0
+#define SAMPLEHASH_INIT_SIZE 0
+
+int samplesAdded = 0;
+int patchSamplesAdded = 0;
+static unsigned short g_PatchIterationKey = 0;
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool SampleData_CompareFunc( SampleData_t const &src1, SampleData_t const &src2 )
+{
+ return ( ( src1.x == src2.x ) &&
+ ( src1.y == src2.y ) &&
+ ( src1.z == src2.z ) );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+unsigned int SampleData_KeyFunc( SampleData_t const &src )
+{
+ return ( src.x + src.y + src.z );
+}
+
+
+CUtlHash<SampleData_t> g_SampleHashTable( SAMPLEHASH_NUM_BUCKETS,
+ SAMPLEHASH_GROW_SIZE,
+ SAMPLEHASH_INIT_SIZE,
+ SampleData_CompareFunc, SampleData_KeyFunc );
+
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+UtlHashHandle_t SampleData_Find( sample_t *pSample )
+{
+ SampleData_t sampleData;
+ sampleData.x = ( int )( pSample->pos.x / SAMPLEHASH_VOXEL_SIZE ) * 100;
+ sampleData.y = ( int )( pSample->pos.y / SAMPLEHASH_VOXEL_SIZE ) * 10;
+ sampleData.z = ( int )( pSample->pos.z / SAMPLEHASH_VOXEL_SIZE );
+
+ return g_SampleHashTable.Find( sampleData );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+UtlHashHandle_t SampleData_InsertIntoHashTable( sample_t *pSample, SampleHandle_t sampleHandle )
+{
+ SampleData_t sampleData;
+ sampleData.x = ( int )( pSample->pos.x / SAMPLEHASH_VOXEL_SIZE ) * 100;
+ sampleData.y = ( int )( pSample->pos.y / SAMPLEHASH_VOXEL_SIZE ) * 10;
+ sampleData.z = ( int )( pSample->pos.z / SAMPLEHASH_VOXEL_SIZE );
+
+ UtlHashHandle_t handle = g_SampleHashTable.AllocEntryFromKey( sampleData );
+
+ SampleData_t *pSampleData = &g_SampleHashTable.Element( handle );
+ pSampleData->x = sampleData.x;
+ pSampleData->y = sampleData.y;
+ pSampleData->z = sampleData.z;
+ pSampleData->m_Samples.AddToTail( sampleHandle );
+
+ samplesAdded++;
+
+ return handle;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+UtlHashHandle_t SampleData_AddSample( sample_t *pSample, SampleHandle_t sampleHandle )
+{
+
+ // find the key -- if it doesn't exist add new sample data to the
+ // hash table
+ UtlHashHandle_t handle = SampleData_Find( pSample );
+ if( handle == g_SampleHashTable.InvalidHandle() )
+ {
+ handle = SampleData_InsertIntoHashTable( pSample, sampleHandle );
+ }
+ else
+ {
+ SampleData_t *pSampleData = &g_SampleHashTable.Element( handle );
+ pSampleData->m_Samples.AddToTail( sampleHandle );
+
+ samplesAdded++;
+ }
+
+ return handle;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void SampleData_Log( void )
+{
+ if( g_bLogHashData )
+ {
+ g_SampleHashTable.Log( "samplehash.txt" );
+ }
+}
+
+
+//=============================================================================
+//=============================================================================
+//
+// PatchSample Functions
+//
+//=============================================================================
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool PatchSampleData_CompareFunc( PatchSampleData_t const &src1, PatchSampleData_t const &src2 )
+{
+ return ( ( src1.x == src2.x ) &&
+ ( src1.y == src2.y ) &&
+ ( src1.z == src2.z ) );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+unsigned int PatchSampleData_KeyFunc( PatchSampleData_t const &src )
+{
+ return ( src.x + src.y + src.z );
+}
+
+
+CUtlHash<PatchSampleData_t> g_PatchSampleHashTable( SAMPLEHASH_NUM_BUCKETS,
+ SAMPLEHASH_GROW_SIZE,
+ SAMPLEHASH_INIT_SIZE,
+ PatchSampleData_CompareFunc, PatchSampleData_KeyFunc );
+
+void GetPatchSampleHashXYZ( const Vector &vOrigin, int &x, int &y, int &z )
+{
+ x = ( int )( vOrigin.x / SAMPLEHASH_VOXEL_SIZE );
+ y = ( int )( vOrigin.y / SAMPLEHASH_VOXEL_SIZE );
+ z = ( int )( vOrigin.z / SAMPLEHASH_VOXEL_SIZE );
+}
+
+
+unsigned short IncrementPatchIterationKey()
+{
+ if ( g_PatchIterationKey == 0xFFFF )
+ {
+ g_PatchIterationKey = 1;
+ for ( int i=0; i < g_Patches.Count(); i++ )
+ g_Patches[i].m_IterationKey = 0;
+ }
+ else
+ {
+ g_PatchIterationKey++;
+ }
+ return g_PatchIterationKey;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void PatchSampleData_AddSample( CPatch *pPatch, int ndxPatch )
+{
+ int patchSampleMins[3], patchSampleMaxs[3];
+
+#if defined( SAMPLEHASH_USE_AREA_PATCHES )
+ GetPatchSampleHashXYZ( pPatch->mins, patchSampleMins[0], patchSampleMins[1], patchSampleMins[2] );
+ GetPatchSampleHashXYZ( pPatch->maxs, patchSampleMaxs[0], patchSampleMaxs[1], patchSampleMaxs[2] );
+#else
+ // If not using area patches, just use the patch's origin to add it to the voxels.
+ GetPatchSampleHashXYZ( pPatch->origin, patchSampleMins[0], patchSampleMins[1], patchSampleMins[2] );
+ memcpy( patchSampleMaxs, patchSampleMins, sizeof( patchSampleMaxs ) );
+#endif
+
+ // Make sure mins are smaller than maxs so we don't iterate for 4 bil.
+ Assert( patchSampleMins[0] <= patchSampleMaxs[0] && patchSampleMins[1] <= patchSampleMaxs[1] && patchSampleMins[2] <= patchSampleMaxs[2] );
+ patchSampleMins[0] = min( patchSampleMins[0], patchSampleMaxs[0] );
+ patchSampleMins[1] = min( patchSampleMins[1], patchSampleMaxs[1] );
+ patchSampleMins[2] = min( patchSampleMins[2], patchSampleMaxs[2] );
+
+ int iterateCoords[3];
+ for ( iterateCoords[0]=patchSampleMins[0]; iterateCoords[0] <= patchSampleMaxs[0]; iterateCoords[0]++ )
+ {
+ for ( iterateCoords[1]=patchSampleMins[1]; iterateCoords[1] <= patchSampleMaxs[1]; iterateCoords[1]++ )
+ {
+ for ( iterateCoords[2]=patchSampleMins[2]; iterateCoords[2] <= patchSampleMaxs[2]; iterateCoords[2]++ )
+ {
+ // find the key -- if it doesn't exist add new sample data to the
+ // hash table
+ PatchSampleData_t iteratePatch;
+ iteratePatch.x = iterateCoords[0] * 100;
+ iteratePatch.y = iterateCoords[1] * 10;
+ iteratePatch.z = iterateCoords[2];
+
+ UtlHashHandle_t handle = g_PatchSampleHashTable.Find( iteratePatch );
+ if( handle == g_PatchSampleHashTable.InvalidHandle() )
+ {
+ UtlHashHandle_t handle = g_PatchSampleHashTable.AllocEntryFromKey( iteratePatch );
+
+ PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element( handle );
+ pPatchData->x = iteratePatch.x;
+ pPatchData->y = iteratePatch.y;
+ pPatchData->z = iteratePatch.z;
+ pPatchData->m_ndxPatches.AddToTail( ndxPatch );
+
+ patchSamplesAdded++;
+ }
+ else
+ {
+ PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element( handle );
+ pPatchData->m_ndxPatches.AddToTail( ndxPatch );
+
+ patchSamplesAdded++;
+ }
+ }
+ }
+ }
+}
+
diff --git a/mp/src/utils/vrad/trace.cpp b/mp/src/utils/vrad/trace.cpp
index 8069dbe7..e0926e29 100644
--- a/mp/src/utils/vrad/trace.cpp
+++ b/mp/src/utils/vrad/trace.cpp
@@ -1,653 +1,653 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//===========================================================================//
-// trace.c
-
-//=============================================================================
-
-#include "vrad.h"
-#include "trace.h"
-#include "Cmodel.h"
-#include "mathlib/vmatrix.h"
-
-
-//=============================================================================
-
-class CToolTrace : public CBaseTrace
-{
-public:
- CToolTrace() {}
-
- Vector mins;
- Vector maxs;
- Vector extents;
-
- texinfo_t *surface;
-
- qboolean ispoint;
-
-private:
- CToolTrace( const CToolTrace& );
-};
-
-
-// 1/32 epsilon to keep floating point happy
-#define DIST_EPSILON (0.03125)
-
-// JAYHL2: This used to be -1, but that caused lots of epsilon issues
-// around slow sloping planes. Perhaps Quake2 limited maps to a certain
-// slope / angle on walkable ground. It has to be a negative number
-// so that the tests work out.
-#define NEVER_UPDATED -9999
-
-//=============================================================================
-
-bool DM_RayDispIntersectTest( CVRADDispColl *pTree, Vector& rayStart, Vector& rayEnd, CToolTrace *pTrace );
-void DM_ClipBoxToBrush( CToolTrace *trace, const Vector & mins, const Vector & maxs, const Vector& p1, const Vector& p2, dbrush_t *brush );
-
-//=============================================================================
-
-float TraceLeafBrushes( int leafIndex, const Vector &start, const Vector &end, CBaseTrace &traceOut )
-{
- dleaf_t *pLeaf = dleafs + leafIndex;
- CToolTrace trace;
- memset( &trace, 0, sizeof(trace) );
- trace.ispoint = true;
- trace.startsolid = false;
- trace.fraction = 1.0;
-
- for ( int i = 0; i < pLeaf->numleafbrushes; i++ )
- {
- int brushnum = dleafbrushes[pLeaf->firstleafbrush+i];
- dbrush_t *b = &dbrushes[brushnum];
- if ( !(b->contents & MASK_OPAQUE))
- continue;
-
- Vector zeroExtents = vec3_origin;
- DM_ClipBoxToBrush( &trace, zeroExtents, zeroExtents, start, end, b);
- if ( trace.fraction != 1.0 || trace.startsolid )
- {
- if ( trace.startsolid )
- trace.fraction = 0.0f;
- traceOut = trace;
- return trace.fraction;
- }
- }
- traceOut = trace;
- return 1.0f;
-}
-
-DispTested_t s_DispTested[MAX_TOOL_THREADS+1];
-
-// this just uses the average coverage for the triangle
-class CCoverageCount : public ITransparentTriangleCallback
-{
-public:
- CCoverageCount()
- {
- m_coverage = Four_Zeros;
- }
-
- virtual bool VisitTriangle_ShouldContinue( const TriIntersectData_t &triangle, const FourRays &rays, fltx4 *pHitMask, fltx4 *b0, fltx4 *b1, fltx4 *b2, int32 hitID )
- {
- float color = g_RtEnv.GetTriangleColor( hitID ).x;
- m_coverage = AddSIMD( m_coverage, AndSIMD ( *pHitMask, ReplicateX4 ( color ) ) );
- m_coverage = MinSIMD( m_coverage, Four_Ones );
-
- fltx4 onesMask = CmpEqSIMD( m_coverage, Four_Ones );
-
- // we should continue if the ones that hit the triangle have onesMask set to zero
- // so hitMask & onesMask != hitMask
- // so hitMask & onesMask == hitMask means we're done
- // so ts(hitMask & onesMask == hitMask) != 0xF says go on
- return 0xF != TestSignSIMD ( CmpEqSIMD ( AndSIMD( *pHitMask, onesMask ), *pHitMask ) );
- }
-
- fltx4 GetCoverage()
- {
- return m_coverage;
- }
-
- fltx4 GetFractionVisible()
- {
- return SubSIMD ( Four_Ones, m_coverage );
- }
-
- fltx4 m_coverage;
-};
-
-// this will sample the texture to get a coverage at the ray intersection point
-class CCoverageCountTexture : public CCoverageCount
-{
-public:
- virtual bool VisitTriangle_ShouldContinue( const TriIntersectData_t &triangle, const FourRays &rays, fltx4 *pHitMask, fltx4 *b0, fltx4 *b1, fltx4 *b2, int32 hitID )
- {
- int sign = TestSignSIMD( *pHitMask );
- float addedCoverage[4];
- for ( int s = 0; s < 4; s++)
- {
- addedCoverage[s] = 0.0f;
- if ( ( sign >> s) & 0x1 )
- {
- addedCoverage[s] = ComputeCoverageFromTexture( b0->m128_f32[s], b1->m128_f32[s], b2->m128_f32[s], hitID );
- }
- }
- m_coverage = AddSIMD( m_coverage, LoadUnalignedSIMD( addedCoverage ) );
- m_coverage = MinSIMD( m_coverage, Four_Ones );
- fltx4 onesMask = CmpEqSIMD( m_coverage, Four_Ones );
-
- // we should continue if the ones that hit the triangle have onesMask set to zero
- // so hitMask & onesMask != hitMask
- // so hitMask & onesMask == hitMask means we're done
- // so ts(hitMask & onesMask == hitMask) != 0xF says go on
- return 0xF != TestSignSIMD ( CmpEqSIMD ( AndSIMD( *pHitMask, onesMask ), *pHitMask ) );
- }
-};
-
-void TestLine( const FourVectors& start, const FourVectors& stop,
- fltx4 *pFractionVisible, int static_prop_index_to_ignore )
-{
- FourRays myrays;
- myrays.origin = start;
- myrays.direction = stop;
- myrays.direction -= myrays.origin;
- fltx4 len = myrays.direction.length();
- myrays.direction *= ReciprocalSIMD( len );
-
- RayTracingResult rt_result;
- CCoverageCountTexture coverageCallback;
-
- g_RtEnv.Trace4Rays(myrays, Four_Zeros, len, &rt_result, TRACE_ID_STATICPROP | static_prop_index_to_ignore, g_bTextureShadows ? &coverageCallback : 0 );
-
- // Assume we can see the targets unless we get hits
- float visibility[4];
- for ( int i = 0; i < 4; i++ )
- {
- visibility[i] = 1.0f;
- if ( ( rt_result.HitIds[i] != -1 ) &&
- ( rt_result.HitDistance.m128_f32[i] < len.m128_f32[i] ) )
- {
- visibility[i] = 0.0f;
- }
- }
- *pFractionVisible = LoadUnalignedSIMD( visibility );
- if ( g_bTextureShadows )
- *pFractionVisible = MinSIMD( *pFractionVisible, coverageCallback.GetFractionVisible() );
-}
-
-
-
-/*
-================
-DM_ClipBoxToBrush
-================
-*/
-void DM_ClipBoxToBrush( CToolTrace *trace, const Vector& mins, const Vector& maxs, const Vector& p1, const Vector& p2,
- dbrush_t *brush)
-{
- dplane_t *plane, *clipplane;
- float dist;
- Vector ofs;
- float d1, d2;
- float f;
- dbrushside_t *side, *leadside;
-
- if (!brush->numsides)
- return;
-
- float enterfrac = NEVER_UPDATED;
- float leavefrac = 1.f;
- clipplane = NULL;
-
- bool getout = false;
- bool startout = false;
- leadside = NULL;
-
- // Loop interchanged, so we don't have to check trace->ispoint every side.
- if ( !trace->ispoint )
- {
- for (int i=0 ; i<brush->numsides ; ++i)
- {
- side = &dbrushsides[brush->firstside+i];
- plane = dplanes + side->planenum;
-
- // FIXME: special case for axial
-
- // general box case
- // push the plane out apropriately for mins/maxs
-
- // FIXME: use signbits into 8 way lookup for each mins/maxs
- ofs.x = (plane->normal.x < 0) ? maxs.x : mins.x;
- ofs.y = (plane->normal.y < 0) ? maxs.y : mins.y;
- ofs.z = (plane->normal.z < 0) ? maxs.z : mins.z;
-// for (j=0 ; j<3 ; j++)
-// {
- // Set signmask to either 0 if the sign is negative, or 0xFFFFFFFF is the sign is positive:
- //int signmask = (((*(int *)&(plane->normal[j]))&0x80000000) >> 31) - 1;
-
- //float temp = maxs[j];
- //*(int *)&(ofs[j]) = (~signmask) & (*(int *)&temp);
- //float temp1 = mins[j];
- //*(int *)&(ofs[j]) |= (signmask) & (*(int *)&temp1);
-// }
- dist = DotProduct (ofs, plane->normal);
- dist = plane->dist - dist;
-
- d1 = DotProduct (p1, plane->normal) - dist;
- d2 = DotProduct (p2, plane->normal) - dist;
-
- // if completely in front of face, no intersection
- if (d1 > 0 && d2 > 0)
- return;
-
- if (d2 > 0)
- getout = true; // endpoint is not in solid
- if (d1 > 0)
- startout = true;
-
- if (d1 <= 0 && d2 <= 0)
- continue;
-
- // crosses face
- if (d1 > d2)
- { // enter
- f = (d1-DIST_EPSILON) / (d1-d2);
- if (f > enterfrac)
- {
- enterfrac = f;
- clipplane = plane;
- leadside = side;
- }
- }
- else
- { // leave
- f = (d1+DIST_EPSILON) / (d1-d2);
- if (f < leavefrac)
- leavefrac = f;
- }
- }
- }
- else
- {
- for (int i=0 ; i<brush->numsides ; ++i)
- {
- side = &dbrushsides[brush->firstside+i];
- plane = dplanes + side->planenum;
-
- // FIXME: special case for axial
-
- // special point case
- // don't ray trace against bevel planes
- if( side->bevel == 1 )
- continue;
-
- dist = plane->dist;
- d1 = DotProduct (p1, plane->normal) - dist;
- d2 = DotProduct (p2, plane->normal) - dist;
-
- // if completely in front of face, no intersection
- if (d1 > 0 && d2 > 0)
- return;
-
- if (d2 > 0)
- getout = true; // endpoint is not in solid
- if (d1 > 0)
- startout = true;
-
- if (d1 <= 0 && d2 <= 0)
- continue;
-
- // crosses face
- if (d1 > d2)
- { // enter
- f = (d1-DIST_EPSILON) / (d1-d2);
- if (f > enterfrac)
- {
- enterfrac = f;
- clipplane = plane;
- leadside = side;
- }
- }
- else
- { // leave
- f = (d1+DIST_EPSILON) / (d1-d2);
- if (f < leavefrac)
- leavefrac = f;
- }
- }
- }
-
-
-
- if (!startout)
- { // original point was inside brush
- trace->startsolid = true;
- if (!getout)
- trace->allsolid = true;
- return;
- }
- if (enterfrac < leavefrac)
- {
- if (enterfrac > NEVER_UPDATED && enterfrac < trace->fraction)
- {
- if (enterfrac < 0)
- enterfrac = 0;
- trace->fraction = enterfrac;
- trace->plane.dist = clipplane->dist;
- trace->plane.normal = clipplane->normal;
- trace->plane.type = clipplane->type;
- if (leadside->texinfo!=-1)
- trace->surface = &texinfo[leadside->texinfo];
- else
- trace->surface = 0;
- trace->contents = brush->contents;
- }
- }
-}
-
-void TestLine_DoesHitSky( FourVectors const& start, FourVectors const& stop,
- fltx4 *pFractionVisible, bool canRecurse, int static_prop_to_skip, bool bDoDebug )
-{
- FourRays myrays;
- myrays.origin = start;
- myrays.direction = stop;
- myrays.direction -= myrays.origin;
- fltx4 len = myrays.direction.length();
- myrays.direction *= ReciprocalSIMD( len );
- RayTracingResult rt_result;
- CCoverageCountTexture coverageCallback;
-
- g_RtEnv.Trace4Rays(myrays, Four_Zeros, len, &rt_result, TRACE_ID_STATICPROP | static_prop_to_skip, g_bTextureShadows? &coverageCallback : 0);
-
- if ( bDoDebug )
- {
- WriteTrace( "trace.txt", myrays, rt_result );
- }
-
- float aOcclusion[4];
- for ( int i = 0; i < 4; i++ )
- {
- aOcclusion[i] = 0.0f;
- if ( ( rt_result.HitIds[i] != -1 ) &&
- ( rt_result.HitDistance.m128_f32[i] < len.m128_f32[i] ) )
- {
- int id = g_RtEnv.OptimizedTriangleList[rt_result.HitIds[i]].m_Data.m_IntersectData.m_nTriangleID;
- if ( !( id & TRACE_ID_SKY ) )
- aOcclusion[i] = 1.0f;
- }
- }
- fltx4 occlusion = LoadUnalignedSIMD( aOcclusion );
- if (g_bTextureShadows)
- occlusion = MaxSIMD ( occlusion, coverageCallback.GetCoverage() );
-
- bool fullyOccluded = ( TestSignSIMD( CmpGeSIMD( occlusion, Four_Ones ) ) == 0xF );
-
- // if we hit sky, and we're not in a sky camera's area, try clipping into the 3D sky boxes
- if ( (! fullyOccluded) && canRecurse && (! g_bNoSkyRecurse ) )
- {
- FourVectors dir = stop;
- dir -= start;
- dir.VectorNormalize();
-
- int leafIndex = -1;
- leafIndex = PointLeafnum( start.Vec( 0 ) );
- if ( leafIndex >= 0 )
- {
- int area = dleafs[leafIndex].area;
- if (area >= 0 && area < numareas)
- {
- if (area_sky_cameras[area] < 0)
- {
- int cam;
- for (cam = 0; cam < num_sky_cameras; ++cam)
- {
- FourVectors skystart, skytrans, skystop;
- skystart.DuplicateVector( sky_cameras[cam].origin );
- skystop = start;
- skystop *= sky_cameras[cam].world_to_sky;
- skystart += skystop;
-
- skystop = dir;
- skystop *= MAX_TRACE_LENGTH;
- skystop += skystart;
- TestLine_DoesHitSky ( skystart, skystop, pFractionVisible, false, static_prop_to_skip, bDoDebug );
- occlusion = AddSIMD ( occlusion, Four_Ones );
- occlusion = SubSIMD ( occlusion, *pFractionVisible );
- }
- }
- }
- }
- }
-
- occlusion = MaxSIMD( occlusion, Four_Zeros );
- occlusion = MinSIMD( occlusion, Four_Ones );
- *pFractionVisible = SubSIMD( Four_Ones, occlusion );
-}
-
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-int PointLeafnum_r( const Vector &point, int ndxNode )
-{
- // while loop here is to avoid recursion overhead
- while( ndxNode >= 0 )
- {
- dnode_t *pNode = dnodes + ndxNode;
- dplane_t *pPlane = dplanes + pNode->planenum;
-
- float dist;
- if( pPlane->type < 3 )
- {
- dist = point[pPlane->type] - pPlane->dist;
- }
- else
- {
- dist = DotProduct( pPlane->normal, point ) - pPlane->dist;
- }
-
- if( dist < 0.0f )
- {
- ndxNode = pNode->children[1];
- }
- else
- {
- ndxNode = pNode->children[0];
- }
- }
-
- return ( -1 - ndxNode );
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-int PointLeafnum( const Vector &point )
-{
- return PointLeafnum_r( point, 0 );
-}
-
-// this iterates the list of entities looking for _vradshadows 1
-// each brush entity containing this key is added to the raytracing environment
-// as a triangle soup model.
-
-dmodel_t *BrushmodelForEntity( entity_t *pEntity )
-{
- const char *pModelname = ValueForKey( pEntity, "model" );
- if ( Q_strlen(pModelname) > 1 )
- {
- int modelIndex = atol( pModelname + 1 );
- if ( modelIndex > 0 && modelIndex < nummodels )
- {
- return &dmodels[modelIndex];
- }
- }
- return NULL;
-}
-
-void AddBrushToRaytraceEnvironment( dbrush_t *pBrush, const VMatrix &xform )
-{
- if ( !( pBrush->contents & MASK_OPAQUE ) )
- return;
-
- Vector v0, v1, v2;
- for (int i = 0; i < pBrush->numsides; i++ )
- {
- dbrushside_t *side = &dbrushsides[pBrush->firstside + i];
- dplane_t *plane = &dplanes[side->planenum];
- texinfo_t *tx = &texinfo[side->texinfo];
- winding_t *w = BaseWindingForPlane (plane->normal, plane->dist);
-
- if ( tx->flags & SURF_SKY || side->dispinfo )
- continue;
-
- for (int j=0 ; j<pBrush->numsides && w; j++)
- {
- if (i == j)
- continue;
- dbrushside_t *pOtherSide = &dbrushsides[pBrush->firstside + j];
- if (pOtherSide->bevel)
- continue;
- plane = &dplanes[pOtherSide->planenum^1];
- ChopWindingInPlace (&w, plane->normal, plane->dist, 0);
- }
- if ( w )
- {
- for ( int j = 2; j < w->numpoints; j++ )
- {
- v0 = xform.VMul4x3(w->p[0]);
- v1 = xform.VMul4x3(w->p[j-1]);
- v2 = xform.VMul4x3(w->p[j]);
- Vector fullCoverage;
- fullCoverage.x = 1.0f;
- g_RtEnv.AddTriangle(TRACE_ID_OPAQUE, v0, v1, v2, fullCoverage);
- }
- FreeWinding( w );
- }
- }
-}
-
-
-// recurse the bsp and build a list of brushes at the leaves under this node
-void GetBrushes_r( int node, CUtlVector<int> &list )
-{
- if ( node < 0 )
- {
- int leafIndex = -1 - node;
- // Add the solids in the leaf
- for ( int i = 0; i < dleafs[leafIndex].numleafbrushes; i++ )
- {
- int brushIndex = dleafbrushes[dleafs[leafIndex].firstleafbrush + i];
- if ( list.Find(brushIndex) < 0 )
- {
- list.AddToTail( brushIndex );
- }
- }
- }
- else
- {
- // recurse
- dnode_t *pnode = dnodes + node;
-
- GetBrushes_r( pnode->children[0], list );
- GetBrushes_r( pnode->children[1], list );
- }
-}
-
-
-void AddBrushes( dmodel_t *pModel, const VMatrix &xform )
-{
- if ( pModel )
- {
- CUtlVector<int> brushList;
- GetBrushes_r( pModel->headnode, brushList );
- for ( int i = 0; i < brushList.Count(); i++ )
- {
- int ndxBrush = brushList[i];
- AddBrushToRaytraceEnvironment( &dbrushes[ndxBrush], xform );
- }
- }
-}
-
-
-// Adds the brush entities that cast shadows to the raytrace environment
-void ExtractBrushEntityShadowCasters()
-{
- for ( int i = 0; i < num_entities; i++ )
- {
- if ( IntForKey( &entities[i], "vrad_brush_cast_shadows" ) != 0 )
- {
- Vector origin;
- QAngle angles;
- GetVectorForKey( &entities[i], "origin", origin );
- GetAnglesForKey( &entities[i], "angles", angles );
- VMatrix xform;
- xform.SetupMatrixOrgAngles( origin, angles );
- AddBrushes( BrushmodelForEntity( &entities[i] ), xform );
- }
- }
-}
-
-void AddBrushesForRayTrace( void )
-{
- if ( !nummodels )
- return;
-
- VMatrix identity;
- identity.Identity();
-
- CUtlVector<int> brushList;
- GetBrushes_r ( dmodels[0].headnode, brushList );
-
- for ( int i = 0; i < brushList.Size(); i++ )
- {
- dbrush_t *brush = &dbrushes[brushList[i]];
- AddBrushToRaytraceEnvironment ( brush, identity );
- }
-
- for ( int i = 0; i < dmodels[0].numfaces; i++ )
- {
- int ndxFace = dmodels[0].firstface + i;
- dface_t *face = &g_pFaces[ndxFace];
-
- texinfo_t *tx = &texinfo[face->texinfo];
- if ( !( tx->flags & SURF_SKY ) )
- continue;
-
- Vector points[MAX_POINTS_ON_WINDING];
-
- for ( int j = 0; j < face->numedges; j++ )
- {
- if ( j >= MAX_POINTS_ON_WINDING )
- Error( "***** ERROR! MAX_POINTS_ON_WINDING reached!" );
-
- if ( face->firstedge + j >= ARRAYSIZE( dsurfedges ) )
- Error( "***** ERROR! face->firstedge + j >= ARRAYSIZE( dsurfedges )!" );
-
- int surfEdge = dsurfedges[face->firstedge + j];
- unsigned short v;
-
- if (surfEdge < 0)
- v = dedges[-surfEdge].v[1];
- else
- v = dedges[surfEdge].v[0];
-
- if ( v >= ARRAYSIZE( dvertexes ) )
- Error( "***** ERROR! v(%u) >= ARRAYSIZE( dvertexes(%d) )!", ( unsigned int )v, ARRAYSIZE( dvertexes ) );
-
- dvertex_t *dv = &dvertexes[v];
- points[j] = dv->point;
- }
-
- for ( int j = 2; j < face->numedges; j++ )
- {
- Vector fullCoverage;
- fullCoverage.x = 1.0f;
- g_RtEnv.AddTriangle ( TRACE_ID_SKY, points[0], points[j - 1], points[j], fullCoverage );
- }
- }
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+// trace.c
+
+//=============================================================================
+
+#include "vrad.h"
+#include "trace.h"
+#include "Cmodel.h"
+#include "mathlib/vmatrix.h"
+
+
+//=============================================================================
+
+class CToolTrace : public CBaseTrace
+{
+public:
+ CToolTrace() {}
+
+ Vector mins;
+ Vector maxs;
+ Vector extents;
+
+ texinfo_t *surface;
+
+ qboolean ispoint;
+
+private:
+ CToolTrace( const CToolTrace& );
+};
+
+
+// 1/32 epsilon to keep floating point happy
+#define DIST_EPSILON (0.03125)
+
+// JAYHL2: This used to be -1, but that caused lots of epsilon issues
+// around slow sloping planes. Perhaps Quake2 limited maps to a certain
+// slope / angle on walkable ground. It has to be a negative number
+// so that the tests work out.
+#define NEVER_UPDATED -9999
+
+//=============================================================================
+
+bool DM_RayDispIntersectTest( CVRADDispColl *pTree, Vector& rayStart, Vector& rayEnd, CToolTrace *pTrace );
+void DM_ClipBoxToBrush( CToolTrace *trace, const Vector & mins, const Vector & maxs, const Vector& p1, const Vector& p2, dbrush_t *brush );
+
+//=============================================================================
+
+float TraceLeafBrushes( int leafIndex, const Vector &start, const Vector &end, CBaseTrace &traceOut )
+{
+ dleaf_t *pLeaf = dleafs + leafIndex;
+ CToolTrace trace;
+ memset( &trace, 0, sizeof(trace) );
+ trace.ispoint = true;
+ trace.startsolid = false;
+ trace.fraction = 1.0;
+
+ for ( int i = 0; i < pLeaf->numleafbrushes; i++ )
+ {
+ int brushnum = dleafbrushes[pLeaf->firstleafbrush+i];
+ dbrush_t *b = &dbrushes[brushnum];
+ if ( !(b->contents & MASK_OPAQUE))
+ continue;
+
+ Vector zeroExtents = vec3_origin;
+ DM_ClipBoxToBrush( &trace, zeroExtents, zeroExtents, start, end, b);
+ if ( trace.fraction != 1.0 || trace.startsolid )
+ {
+ if ( trace.startsolid )
+ trace.fraction = 0.0f;
+ traceOut = trace;
+ return trace.fraction;
+ }
+ }
+ traceOut = trace;
+ return 1.0f;
+}
+
+DispTested_t s_DispTested[MAX_TOOL_THREADS+1];
+
+// this just uses the average coverage for the triangle
+class CCoverageCount : public ITransparentTriangleCallback
+{
+public:
+ CCoverageCount()
+ {
+ m_coverage = Four_Zeros;
+ }
+
+ virtual bool VisitTriangle_ShouldContinue( const TriIntersectData_t &triangle, const FourRays &rays, fltx4 *pHitMask, fltx4 *b0, fltx4 *b1, fltx4 *b2, int32 hitID )
+ {
+ float color = g_RtEnv.GetTriangleColor( hitID ).x;
+ m_coverage = AddSIMD( m_coverage, AndSIMD ( *pHitMask, ReplicateX4 ( color ) ) );
+ m_coverage = MinSIMD( m_coverage, Four_Ones );
+
+ fltx4 onesMask = CmpEqSIMD( m_coverage, Four_Ones );
+
+ // we should continue if the ones that hit the triangle have onesMask set to zero
+ // so hitMask & onesMask != hitMask
+ // so hitMask & onesMask == hitMask means we're done
+ // so ts(hitMask & onesMask == hitMask) != 0xF says go on
+ return 0xF != TestSignSIMD ( CmpEqSIMD ( AndSIMD( *pHitMask, onesMask ), *pHitMask ) );
+ }
+
+ fltx4 GetCoverage()
+ {
+ return m_coverage;
+ }
+
+ fltx4 GetFractionVisible()
+ {
+ return SubSIMD ( Four_Ones, m_coverage );
+ }
+
+ fltx4 m_coverage;
+};
+
+// this will sample the texture to get a coverage at the ray intersection point
+class CCoverageCountTexture : public CCoverageCount
+{
+public:
+ virtual bool VisitTriangle_ShouldContinue( const TriIntersectData_t &triangle, const FourRays &rays, fltx4 *pHitMask, fltx4 *b0, fltx4 *b1, fltx4 *b2, int32 hitID )
+ {
+ int sign = TestSignSIMD( *pHitMask );
+ float addedCoverage[4];
+ for ( int s = 0; s < 4; s++)
+ {
+ addedCoverage[s] = 0.0f;
+ if ( ( sign >> s) & 0x1 )
+ {
+ addedCoverage[s] = ComputeCoverageFromTexture( b0->m128_f32[s], b1->m128_f32[s], b2->m128_f32[s], hitID );
+ }
+ }
+ m_coverage = AddSIMD( m_coverage, LoadUnalignedSIMD( addedCoverage ) );
+ m_coverage = MinSIMD( m_coverage, Four_Ones );
+ fltx4 onesMask = CmpEqSIMD( m_coverage, Four_Ones );
+
+ // we should continue if the ones that hit the triangle have onesMask set to zero
+ // so hitMask & onesMask != hitMask
+ // so hitMask & onesMask == hitMask means we're done
+ // so ts(hitMask & onesMask == hitMask) != 0xF says go on
+ return 0xF != TestSignSIMD ( CmpEqSIMD ( AndSIMD( *pHitMask, onesMask ), *pHitMask ) );
+ }
+};
+
+void TestLine( const FourVectors& start, const FourVectors& stop,
+ fltx4 *pFractionVisible, int static_prop_index_to_ignore )
+{
+ FourRays myrays;
+ myrays.origin = start;
+ myrays.direction = stop;
+ myrays.direction -= myrays.origin;
+ fltx4 len = myrays.direction.length();
+ myrays.direction *= ReciprocalSIMD( len );
+
+ RayTracingResult rt_result;
+ CCoverageCountTexture coverageCallback;
+
+ g_RtEnv.Trace4Rays(myrays, Four_Zeros, len, &rt_result, TRACE_ID_STATICPROP | static_prop_index_to_ignore, g_bTextureShadows ? &coverageCallback : 0 );
+
+ // Assume we can see the targets unless we get hits
+ float visibility[4];
+ for ( int i = 0; i < 4; i++ )
+ {
+ visibility[i] = 1.0f;
+ if ( ( rt_result.HitIds[i] != -1 ) &&
+ ( rt_result.HitDistance.m128_f32[i] < len.m128_f32[i] ) )
+ {
+ visibility[i] = 0.0f;
+ }
+ }
+ *pFractionVisible = LoadUnalignedSIMD( visibility );
+ if ( g_bTextureShadows )
+ *pFractionVisible = MinSIMD( *pFractionVisible, coverageCallback.GetFractionVisible() );
+}
+
+
+
+/*
+================
+DM_ClipBoxToBrush
+================
+*/
+void DM_ClipBoxToBrush( CToolTrace *trace, const Vector& mins, const Vector& maxs, const Vector& p1, const Vector& p2,
+ dbrush_t *brush)
+{
+ dplane_t *plane, *clipplane;
+ float dist;
+ Vector ofs;
+ float d1, d2;
+ float f;
+ dbrushside_t *side, *leadside;
+
+ if (!brush->numsides)
+ return;
+
+ float enterfrac = NEVER_UPDATED;
+ float leavefrac = 1.f;
+ clipplane = NULL;
+
+ bool getout = false;
+ bool startout = false;
+ leadside = NULL;
+
+ // Loop interchanged, so we don't have to check trace->ispoint every side.
+ if ( !trace->ispoint )
+ {
+ for (int i=0 ; i<brush->numsides ; ++i)
+ {
+ side = &dbrushsides[brush->firstside+i];
+ plane = dplanes + side->planenum;
+
+ // FIXME: special case for axial
+
+ // general box case
+ // push the plane out apropriately for mins/maxs
+
+ // FIXME: use signbits into 8 way lookup for each mins/maxs
+ ofs.x = (plane->normal.x < 0) ? maxs.x : mins.x;
+ ofs.y = (plane->normal.y < 0) ? maxs.y : mins.y;
+ ofs.z = (plane->normal.z < 0) ? maxs.z : mins.z;
+// for (j=0 ; j<3 ; j++)
+// {
+ // Set signmask to either 0 if the sign is negative, or 0xFFFFFFFF is the sign is positive:
+ //int signmask = (((*(int *)&(plane->normal[j]))&0x80000000) >> 31) - 1;
+
+ //float temp = maxs[j];
+ //*(int *)&(ofs[j]) = (~signmask) & (*(int *)&temp);
+ //float temp1 = mins[j];
+ //*(int *)&(ofs[j]) |= (signmask) & (*(int *)&temp1);
+// }
+ dist = DotProduct (ofs, plane->normal);
+ dist = plane->dist - dist;
+
+ d1 = DotProduct (p1, plane->normal) - dist;
+ d2 = DotProduct (p2, plane->normal) - dist;
+
+ // if completely in front of face, no intersection
+ if (d1 > 0 && d2 > 0)
+ return;
+
+ if (d2 > 0)
+ getout = true; // endpoint is not in solid
+ if (d1 > 0)
+ startout = true;
+
+ if (d1 <= 0 && d2 <= 0)
+ continue;
+
+ // crosses face
+ if (d1 > d2)
+ { // enter
+ f = (d1-DIST_EPSILON) / (d1-d2);
+ if (f > enterfrac)
+ {
+ enterfrac = f;
+ clipplane = plane;
+ leadside = side;
+ }
+ }
+ else
+ { // leave
+ f = (d1+DIST_EPSILON) / (d1-d2);
+ if (f < leavefrac)
+ leavefrac = f;
+ }
+ }
+ }
+ else
+ {
+ for (int i=0 ; i<brush->numsides ; ++i)
+ {
+ side = &dbrushsides[brush->firstside+i];
+ plane = dplanes + side->planenum;
+
+ // FIXME: special case for axial
+
+ // special point case
+ // don't ray trace against bevel planes
+ if( side->bevel == 1 )
+ continue;
+
+ dist = plane->dist;
+ d1 = DotProduct (p1, plane->normal) - dist;
+ d2 = DotProduct (p2, plane->normal) - dist;
+
+ // if completely in front of face, no intersection
+ if (d1 > 0 && d2 > 0)
+ return;
+
+ if (d2 > 0)
+ getout = true; // endpoint is not in solid
+ if (d1 > 0)
+ startout = true;
+
+ if (d1 <= 0 && d2 <= 0)
+ continue;
+
+ // crosses face
+ if (d1 > d2)
+ { // enter
+ f = (d1-DIST_EPSILON) / (d1-d2);
+ if (f > enterfrac)
+ {
+ enterfrac = f;
+ clipplane = plane;
+ leadside = side;
+ }
+ }
+ else
+ { // leave
+ f = (d1+DIST_EPSILON) / (d1-d2);
+ if (f < leavefrac)
+ leavefrac = f;
+ }
+ }
+ }
+
+
+
+ if (!startout)
+ { // original point was inside brush
+ trace->startsolid = true;
+ if (!getout)
+ trace->allsolid = true;
+ return;
+ }
+ if (enterfrac < leavefrac)
+ {
+ if (enterfrac > NEVER_UPDATED && enterfrac < trace->fraction)
+ {
+ if (enterfrac < 0)
+ enterfrac = 0;
+ trace->fraction = enterfrac;
+ trace->plane.dist = clipplane->dist;
+ trace->plane.normal = clipplane->normal;
+ trace->plane.type = clipplane->type;
+ if (leadside->texinfo!=-1)
+ trace->surface = &texinfo[leadside->texinfo];
+ else
+ trace->surface = 0;
+ trace->contents = brush->contents;
+ }
+ }
+}
+
+void TestLine_DoesHitSky( FourVectors const& start, FourVectors const& stop,
+ fltx4 *pFractionVisible, bool canRecurse, int static_prop_to_skip, bool bDoDebug )
+{
+ FourRays myrays;
+ myrays.origin = start;
+ myrays.direction = stop;
+ myrays.direction -= myrays.origin;
+ fltx4 len = myrays.direction.length();
+ myrays.direction *= ReciprocalSIMD( len );
+ RayTracingResult rt_result;
+ CCoverageCountTexture coverageCallback;
+
+ g_RtEnv.Trace4Rays(myrays, Four_Zeros, len, &rt_result, TRACE_ID_STATICPROP | static_prop_to_skip, g_bTextureShadows? &coverageCallback : 0);
+
+ if ( bDoDebug )
+ {
+ WriteTrace( "trace.txt", myrays, rt_result );
+ }
+
+ float aOcclusion[4];
+ for ( int i = 0; i < 4; i++ )
+ {
+ aOcclusion[i] = 0.0f;
+ if ( ( rt_result.HitIds[i] != -1 ) &&
+ ( rt_result.HitDistance.m128_f32[i] < len.m128_f32[i] ) )
+ {
+ int id = g_RtEnv.OptimizedTriangleList[rt_result.HitIds[i]].m_Data.m_IntersectData.m_nTriangleID;
+ if ( !( id & TRACE_ID_SKY ) )
+ aOcclusion[i] = 1.0f;
+ }
+ }
+ fltx4 occlusion = LoadUnalignedSIMD( aOcclusion );
+ if (g_bTextureShadows)
+ occlusion = MaxSIMD ( occlusion, coverageCallback.GetCoverage() );
+
+ bool fullyOccluded = ( TestSignSIMD( CmpGeSIMD( occlusion, Four_Ones ) ) == 0xF );
+
+ // if we hit sky, and we're not in a sky camera's area, try clipping into the 3D sky boxes
+ if ( (! fullyOccluded) && canRecurse && (! g_bNoSkyRecurse ) )
+ {
+ FourVectors dir = stop;
+ dir -= start;
+ dir.VectorNormalize();
+
+ int leafIndex = -1;
+ leafIndex = PointLeafnum( start.Vec( 0 ) );
+ if ( leafIndex >= 0 )
+ {
+ int area = dleafs[leafIndex].area;
+ if (area >= 0 && area < numareas)
+ {
+ if (area_sky_cameras[area] < 0)
+ {
+ int cam;
+ for (cam = 0; cam < num_sky_cameras; ++cam)
+ {
+ FourVectors skystart, skytrans, skystop;
+ skystart.DuplicateVector( sky_cameras[cam].origin );
+ skystop = start;
+ skystop *= sky_cameras[cam].world_to_sky;
+ skystart += skystop;
+
+ skystop = dir;
+ skystop *= MAX_TRACE_LENGTH;
+ skystop += skystart;
+ TestLine_DoesHitSky ( skystart, skystop, pFractionVisible, false, static_prop_to_skip, bDoDebug );
+ occlusion = AddSIMD ( occlusion, Four_Ones );
+ occlusion = SubSIMD ( occlusion, *pFractionVisible );
+ }
+ }
+ }
+ }
+ }
+
+ occlusion = MaxSIMD( occlusion, Four_Zeros );
+ occlusion = MinSIMD( occlusion, Four_Ones );
+ *pFractionVisible = SubSIMD( Four_Ones, occlusion );
+}
+
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int PointLeafnum_r( const Vector &point, int ndxNode )
+{
+ // while loop here is to avoid recursion overhead
+ while( ndxNode >= 0 )
+ {
+ dnode_t *pNode = dnodes + ndxNode;
+ dplane_t *pPlane = dplanes + pNode->planenum;
+
+ float dist;
+ if( pPlane->type < 3 )
+ {
+ dist = point[pPlane->type] - pPlane->dist;
+ }
+ else
+ {
+ dist = DotProduct( pPlane->normal, point ) - pPlane->dist;
+ }
+
+ if( dist < 0.0f )
+ {
+ ndxNode = pNode->children[1];
+ }
+ else
+ {
+ ndxNode = pNode->children[0];
+ }
+ }
+
+ return ( -1 - ndxNode );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int PointLeafnum( const Vector &point )
+{
+ return PointLeafnum_r( point, 0 );
+}
+
+// this iterates the list of entities looking for _vradshadows 1
+// each brush entity containing this key is added to the raytracing environment
+// as a triangle soup model.
+
+dmodel_t *BrushmodelForEntity( entity_t *pEntity )
+{
+ const char *pModelname = ValueForKey( pEntity, "model" );
+ if ( Q_strlen(pModelname) > 1 )
+ {
+ int modelIndex = atol( pModelname + 1 );
+ if ( modelIndex > 0 && modelIndex < nummodels )
+ {
+ return &dmodels[modelIndex];
+ }
+ }
+ return NULL;
+}
+
+void AddBrushToRaytraceEnvironment( dbrush_t *pBrush, const VMatrix &xform )
+{
+ if ( !( pBrush->contents & MASK_OPAQUE ) )
+ return;
+
+ Vector v0, v1, v2;
+ for (int i = 0; i < pBrush->numsides; i++ )
+ {
+ dbrushside_t *side = &dbrushsides[pBrush->firstside + i];
+ dplane_t *plane = &dplanes[side->planenum];
+ texinfo_t *tx = &texinfo[side->texinfo];
+ winding_t *w = BaseWindingForPlane (plane->normal, plane->dist);
+
+ if ( tx->flags & SURF_SKY || side->dispinfo )
+ continue;
+
+ for (int j=0 ; j<pBrush->numsides && w; j++)
+ {
+ if (i == j)
+ continue;
+ dbrushside_t *pOtherSide = &dbrushsides[pBrush->firstside + j];
+ if (pOtherSide->bevel)
+ continue;
+ plane = &dplanes[pOtherSide->planenum^1];
+ ChopWindingInPlace (&w, plane->normal, plane->dist, 0);
+ }
+ if ( w )
+ {
+ for ( int j = 2; j < w->numpoints; j++ )
+ {
+ v0 = xform.VMul4x3(w->p[0]);
+ v1 = xform.VMul4x3(w->p[j-1]);
+ v2 = xform.VMul4x3(w->p[j]);
+ Vector fullCoverage;
+ fullCoverage.x = 1.0f;
+ g_RtEnv.AddTriangle(TRACE_ID_OPAQUE, v0, v1, v2, fullCoverage);
+ }
+ FreeWinding( w );
+ }
+ }
+}
+
+
+// recurse the bsp and build a list of brushes at the leaves under this node
+void GetBrushes_r( int node, CUtlVector<int> &list )
+{
+ if ( node < 0 )
+ {
+ int leafIndex = -1 - node;
+ // Add the solids in the leaf
+ for ( int i = 0; i < dleafs[leafIndex].numleafbrushes; i++ )
+ {
+ int brushIndex = dleafbrushes[dleafs[leafIndex].firstleafbrush + i];
+ if ( list.Find(brushIndex) < 0 )
+ {
+ list.AddToTail( brushIndex );
+ }
+ }
+ }
+ else
+ {
+ // recurse
+ dnode_t *pnode = dnodes + node;
+
+ GetBrushes_r( pnode->children[0], list );
+ GetBrushes_r( pnode->children[1], list );
+ }
+}
+
+
+void AddBrushes( dmodel_t *pModel, const VMatrix &xform )
+{
+ if ( pModel )
+ {
+ CUtlVector<int> brushList;
+ GetBrushes_r( pModel->headnode, brushList );
+ for ( int i = 0; i < brushList.Count(); i++ )
+ {
+ int ndxBrush = brushList[i];
+ AddBrushToRaytraceEnvironment( &dbrushes[ndxBrush], xform );
+ }
+ }
+}
+
+
+// Adds the brush entities that cast shadows to the raytrace environment
+void ExtractBrushEntityShadowCasters()
+{
+ for ( int i = 0; i < num_entities; i++ )
+ {
+ if ( IntForKey( &entities[i], "vrad_brush_cast_shadows" ) != 0 )
+ {
+ Vector origin;
+ QAngle angles;
+ GetVectorForKey( &entities[i], "origin", origin );
+ GetAnglesForKey( &entities[i], "angles", angles );
+ VMatrix xform;
+ xform.SetupMatrixOrgAngles( origin, angles );
+ AddBrushes( BrushmodelForEntity( &entities[i] ), xform );
+ }
+ }
+}
+
+void AddBrushesForRayTrace( void )
+{
+ if ( !nummodels )
+ return;
+
+ VMatrix identity;
+ identity.Identity();
+
+ CUtlVector<int> brushList;
+ GetBrushes_r ( dmodels[0].headnode, brushList );
+
+ for ( int i = 0; i < brushList.Size(); i++ )
+ {
+ dbrush_t *brush = &dbrushes[brushList[i]];
+ AddBrushToRaytraceEnvironment ( brush, identity );
+ }
+
+ for ( int i = 0; i < dmodels[0].numfaces; i++ )
+ {
+ int ndxFace = dmodels[0].firstface + i;
+ dface_t *face = &g_pFaces[ndxFace];
+
+ texinfo_t *tx = &texinfo[face->texinfo];
+ if ( !( tx->flags & SURF_SKY ) )
+ continue;
+
+ Vector points[MAX_POINTS_ON_WINDING];
+
+ for ( int j = 0; j < face->numedges; j++ )
+ {
+ if ( j >= MAX_POINTS_ON_WINDING )
+ Error( "***** ERROR! MAX_POINTS_ON_WINDING reached!" );
+
+ if ( face->firstedge + j >= ARRAYSIZE( dsurfedges ) )
+ Error( "***** ERROR! face->firstedge + j >= ARRAYSIZE( dsurfedges )!" );
+
+ int surfEdge = dsurfedges[face->firstedge + j];
+ unsigned short v;
+
+ if (surfEdge < 0)
+ v = dedges[-surfEdge].v[1];
+ else
+ v = dedges[surfEdge].v[0];
+
+ if ( v >= ARRAYSIZE( dvertexes ) )
+ Error( "***** ERROR! v(%u) >= ARRAYSIZE( dvertexes(%d) )!", ( unsigned int )v, ARRAYSIZE( dvertexes ) );
+
+ dvertex_t *dv = &dvertexes[v];
+ points[j] = dv->point;
+ }
+
+ for ( int j = 2; j < face->numedges; j++ )
+ {
+ Vector fullCoverage;
+ fullCoverage.x = 1.0f;
+ g_RtEnv.AddTriangle ( TRACE_ID_SKY, points[0], points[j - 1], points[j], fullCoverage );
+ }
+ }
+}
diff --git a/mp/src/utils/vrad/vismat.cpp b/mp/src/utils/vrad/vismat.cpp
index 7491f69f..ca9398ec 100644
--- a/mp/src/utils/vrad/vismat.cpp
+++ b/mp/src/utils/vrad/vismat.cpp
@@ -1,483 +1,483 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#include "vrad.h"
-#include "vmpi.h"
-#ifdef MPI
-#include "messbuf.h"
-static MessageBuffer mb;
-#endif
-
-#define HALFBIT
-
-extern char source[MAX_PATH];
-extern char vismatfile[_MAX_PATH];
-extern char incrementfile[_MAX_PATH];
-extern qboolean incremental;
-
-/*
-===================================================================
-
-VISIBILITY MATRIX
-
-Determine which patches can see each other
-Use the PVS to accelerate if available
-===================================================================
-*/
-
-#define TEST_EPSILON 0.1
-#define PLANE_TEST_EPSILON 0.01 // patch must be this much in front of the plane to be considered "in front"
-#define PATCH_FACE_OFFSET 0.1 // push patch origins off from the face by this amount to avoid self collisions
-
-#define STREAM_SIZE 512
-
-class CTransferMaker
-{
-public:
-
- CTransferMaker( transfer_t *all_transfers );
- ~CTransferMaker();
-
- FORCEINLINE void TestMakeTransfer( Vector start, Vector stop, int ndxShooter, int ndxReciever )
- {
- g_RtEnv.AddToRayStream( m_RayStream, start, stop, &m_pResults[m_nTests] );
- m_pShooterPatches[m_nTests] = ndxShooter;
- m_pRecieverPatches[m_nTests] = ndxReciever;
- ++m_nTests;
- }
-
- void Finish();
-
-private:
-
- int m_nTests;
- RayTracingSingleResult *m_pResults;
- int *m_pShooterPatches;
- int *m_pRecieverPatches;
- RayStream m_RayStream;
- transfer_t *m_AllTransfers;
-};
-
-CTransferMaker::CTransferMaker( transfer_t *all_transfers ) :
- m_AllTransfers( all_transfers ), m_nTests( 0 )
-{
- m_pResults = (RayTracingSingleResult *)calloc( 1, MAX_PATCHES * sizeof ( RayTracingSingleResult ) );
- m_pShooterPatches = (int *)calloc( 1, MAX_PATCHES * sizeof( int ) );
- m_pRecieverPatches = (int *)calloc( 1, MAX_PATCHES * sizeof( int ) );
-}
-
-CTransferMaker::~CTransferMaker()
-{
- free ( m_pResults );
- free ( m_pShooterPatches );
- free (m_pRecieverPatches );
-}
-
-void CTransferMaker::Finish()
-{
- g_RtEnv.FinishRayStream( m_RayStream );
- for ( int i = 0; i < m_nTests; ++i )
- {
- if ( m_pResults[i].HitID == -1 || m_pResults[i].HitDistance >= m_pResults[i].ray_length )
- {
- MakeTransfer( m_pShooterPatches[i], m_pRecieverPatches[i], m_AllTransfers );
- }
- }
- m_nTests = 0;
-}
-
-
-dleaf_t* PointInLeaf (int iNode, Vector const& point)
-{
- if ( iNode < 0 )
- return &dleafs[ (-1-iNode) ];
-
- dnode_t *node = &dnodes[iNode];
- dplane_t *plane = &dplanes[ node->planenum ];
-
- float dist = DotProduct (point, plane->normal) - plane->dist;
- if ( dist > TEST_EPSILON )
- {
- return PointInLeaf( node->children[0], point );
- }
- else if ( dist < -TEST_EPSILON )
- {
- return PointInLeaf( node->children[1], point );
- }
- else
- {
- dleaf_t *pTest = PointInLeaf( node->children[0], point );
- if ( pTest->cluster != -1 )
- return pTest;
-
- return PointInLeaf( node->children[1], point );
- }
-}
-
-
-int ClusterFromPoint( Vector const& point )
-{
- dleaf_t *leaf = PointInLeaf( 0, point );
-
- return leaf->cluster;
-}
-
-void PvsForOrigin (Vector& org, byte *pvs)
-{
- int visofs;
- int cluster;
-
- if (!visdatasize)
- {
- memset (pvs, 255, (dvis->numclusters+7)/8 );
- return;
- }
-
- cluster = ClusterFromPoint( org );
- if ( cluster < 0 )
- {
- visofs = -1;
- }
- else
- {
- visofs = dvis->bitofs[ cluster ][DVIS_PVS];
- }
-
- if (visofs == -1)
- Error ("visofs == -1");
-
- DecompressVis (&dvisdata[visofs], pvs);
-}
-
-
-void TestPatchToPatch( int ndxPatch1, int ndxPatch2, int head, transfer_t *transfers, CTransferMaker &transferMaker, int iThread )
-{
- Vector tmp;
-
- //
- // get patches
- //
- if( ndxPatch1 == g_Patches.InvalidIndex() || ndxPatch2 == g_Patches.InvalidIndex() )
- return;
-
- CPatch *patch = &g_Patches.Element( ndxPatch1 );
- CPatch *patch2 = &g_Patches.Element( ndxPatch2 );
-
- if (patch2->child1 != g_Patches.InvalidIndex() )
- {
- // check to see if we should use a child node instead
-
- VectorSubtract( patch->origin, patch2->origin, tmp );
- // SQRT( 1/4 )
- // FIXME: should be based on form-factor (ie. include visible angle, etc)
- if ( DotProduct(tmp, tmp) * 0.0625 < patch2->area )
- {
- TestPatchToPatch( ndxPatch1, patch2->child1, head, transfers, transferMaker, iThread );
- TestPatchToPatch( ndxPatch1, patch2->child2, head, transfers, transferMaker, iThread );
- return;
- }
- }
-
- // check vis between patch and patch2
- // if bit has not already been set
- // && v2 is not behind light plane
- // && v2 is visible from v1
- if ( DotProduct( patch2->origin, patch->normal ) > patch->planeDist + PLANE_TEST_EPSILON )
- {
- // push out origins from face so that don't intersect their owners
- Vector p1, p2;
- VectorAdd( patch->origin, patch->normal, p1 );
- VectorAdd( patch2->origin, patch2->normal, p2 );
- transferMaker.TestMakeTransfer( p1, p2, ndxPatch1, ndxPatch2 );
- }
-}
-
-
-/*
-==============
-TestPatchToFace
-
-Sets vis bits for all patches in the face
-==============
-*/
-void TestPatchToFace (unsigned patchnum, int facenum, int head, transfer_t *transfers, CTransferMaker &transferMaker, int iThread )
-{
- if( faceParents.Element( facenum ) == g_Patches.InvalidIndex() || patchnum == g_Patches.InvalidIndex() )
- return;
-
- CPatch *patch = &g_Patches.Element( patchnum );
- CPatch *patch2 = &g_Patches.Element( faceParents.Element( facenum ) );
-
- // if emitter is behind that face plane, skip all patches
-
- CPatch *pNextPatch;
-
- if ( patch2 && DotProduct(patch->origin, patch2->normal) > patch2->planeDist + PLANE_TEST_EPSILON )
- {
- // we need to do a real test
- for( ; patch2; patch2 = pNextPatch )
- {
- // next patch
- pNextPatch = NULL;
- if( patch2->ndxNextParent != g_Patches.InvalidIndex() )
- {
- pNextPatch = &g_Patches.Element( patch2->ndxNextParent );
- }
-
- /*
- // skip patches too far away
- VectorSubtract( patch->origin, patch2->origin, tmp );
- if (DotProduct( tmp, tmp ) > 512 * 512)
- continue;
- */
-
- int ndxPatch2 = patch2 - g_Patches.Base();
- TestPatchToPatch( patchnum, ndxPatch2, head, transfers, transferMaker, iThread );
- }
- }
-}
-
-
-struct ClusterDispList_t
-{
- CUtlVector<int> dispFaces;
-};
-
-static CUtlVector<ClusterDispList_t> g_ClusterDispFaces;
-
-//-----------------------------------------------------------------------------
-// Helps us find all displacements associated with a particular cluster
-//-----------------------------------------------------------------------------
-void AddDispsToClusterTable( void )
-{
- g_ClusterDispFaces.SetCount( g_ClusterLeaves.Count() );
-
- //
- // add displacement faces to the cluster table
- //
- for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
- {
- // search for displacement faces
- if( g_pFaces[ndxFace].dispinfo == -1 )
- continue;
-
- //
- // get the clusters associated with the face
- //
- if( g_FacePatches.Element( ndxFace ) != g_FacePatches.InvalidIndex() )
- {
- CPatch *pNextPatch = NULL;
- for( CPatch *pPatch = &g_Patches.Element( g_FacePatches.Element( ndxFace ) ); pPatch; pPatch = pNextPatch )
- {
- // next patch
- pNextPatch = NULL;
- if( pPatch->ndxNext != g_Patches.InvalidIndex() )
- {
- pNextPatch = &g_Patches.Element( pPatch->ndxNext );
- }
-
- if( pPatch->clusterNumber != g_Patches.InvalidIndex() )
- {
- int ndxDisp = g_ClusterDispFaces[pPatch->clusterNumber].dispFaces.Find( ndxFace );
- if( ndxDisp == -1 )
- {
- ndxDisp = g_ClusterDispFaces[pPatch->clusterNumber].dispFaces.AddToTail();
- g_ClusterDispFaces[pPatch->clusterNumber].dispFaces[ndxDisp] = ndxFace;
- }
- }
- }
- }
- }
-}
-
-
-/*
-==============
-BuildVisRow
-
-Calc vis bits from a single patch
-==============
-*/
-void BuildVisRow (int patchnum, byte *pvs, int head, transfer_t *transfers, CTransferMaker &transferMaker, int iThread )
-{
- int j, k, l, leafIndex;
- CPatch *patch;
- dleaf_t *leaf;
- byte face_tested[MAX_MAP_FACES];
- byte disp_tested[MAX_MAP_FACES];
-
- patch = &g_Patches.Element( patchnum );
-
- memset( face_tested, 0, numfaces ) ;
- memset( disp_tested, 0, numfaces );
-
- for (j=0; j<dvis->numclusters; j++)
- {
- if ( ! ( pvs[(j)>>3] & (1<<((j)&7)) ) )
- {
- continue; // not in pvs
- }
-
- for ( leafIndex = 0; leafIndex < g_ClusterLeaves[j].leafCount; leafIndex++ )
- {
- leaf = dleafs + g_ClusterLeaves[j].leafs[leafIndex];
-
- for (k=0 ; k<leaf->numleaffaces; k++)
- {
- l = dleaffaces[leaf->firstleafface + k];
- // faces can be marksurfed by multiple leaves, but
- // don't bother testing again
- if (face_tested[l])
- {
- continue;
- }
- face_tested[l] = 1;
-
- // don't check patches on the same face
- if (patch->faceNumber == l)
- continue;
- TestPatchToFace (patchnum, l, head, transfers, transferMaker, iThread );
- }
- }
-
- int dispCount = g_ClusterDispFaces[j].dispFaces.Size();
- for( int ndxDisp = 0; ndxDisp < dispCount; ndxDisp++ )
- {
- int ndxFace = g_ClusterDispFaces[j].dispFaces[ndxDisp];
- if( disp_tested[ndxFace] )
- continue;
-
- disp_tested[ndxFace] = 1;
-
- // don't check patches on the same face
- if( patch->faceNumber == ndxFace )
- continue;
-
- TestPatchToFace( patchnum, ndxFace, head, transfers, transferMaker, iThread );
- }
- }
-
-
- // Msg("%d) Transfers: %5d\n", patchnum, patch->numtransfers);
-}
-
-
-
-/*
-===========
-BuildVisLeafs
-
- This is run by multiple threads
-===========
-*/
-
-transfer_t* BuildVisLeafs_Start()
-{
- return (transfer_t *)calloc( 1, MAX_PATCHES * sizeof( transfer_t ) );
-}
-
-
-// If PatchCB is non-null, it is called after each row is generated (used by MPI).
-void BuildVisLeafs_Cluster(
- int threadnum,
- transfer_t *transfers,
- int iCluster,
- void (*PatchCB)(int iThread, int patchnum, CPatch *patch)
- )
-{
- byte pvs[(MAX_MAP_CLUSTERS+7)/8];
- CPatch *patch;
- int head;
- unsigned patchnum;
-
- DecompressVis( &dvisdata[ dvis->bitofs[ iCluster ][DVIS_PVS] ], pvs);
- head = 0;
-
- CTransferMaker transferMaker( transfers );
-
- // light every patch in the cluster
- if( clusterChildren.Element( iCluster ) != clusterChildren.InvalidIndex() )
- {
- CPatch *pNextPatch;
- for( patch = &g_Patches.Element( clusterChildren.Element( iCluster ) ); patch; patch = pNextPatch )
- {
- //
- // next patch
- //
- pNextPatch = NULL;
- if( patch->ndxNextClusterChild != g_Patches.InvalidIndex() )
- {
- pNextPatch = &g_Patches.Element( patch->ndxNextClusterChild );
- }
-
- patchnum = patch - g_Patches.Base();
-
- // build to all other world clusters
- BuildVisRow (patchnum, pvs, head, transfers, transferMaker, threadnum );
- transferMaker.Finish();
-
- // do the transfers
- MakeScales( patchnum, transfers );
-
- // Let MPI aggregate the data if it's being used.
- if ( PatchCB )
- PatchCB( threadnum, patchnum, patch );
- }
- }
-}
-
-
-void BuildVisLeafs_End( transfer_t *transfers )
-{
- free( transfers );
-}
-
-
-void BuildVisLeafs( int threadnum, void *pUserData )
-{
- transfer_t *transfers = BuildVisLeafs_Start();
-
- while ( 1 )
- {
- //
- // build a minimal BSP tree that only
- // covers areas relevent to the PVS
- //
- // JAY: Now this returns a cluster index
- int iCluster = GetThreadWork();
- if ( iCluster == -1 )
- break;
-
- BuildVisLeafs_Cluster( threadnum, transfers, iCluster, NULL );
- }
-
- BuildVisLeafs_End( transfers );
-}
-
-
-/*
-==============
-BuildVisMatrix
-==============
-*/
-void BuildVisMatrix (void)
-{
- if ( g_bUseMPI )
- {
- RunMPIBuildVisLeafs();
- }
- else
- {
- RunThreadsOn (dvis->numclusters, true, BuildVisLeafs);
- }
-}
-
-void FreeVisMatrix (void)
-{
-
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vrad.h"
+#include "vmpi.h"
+#ifdef MPI
+#include "messbuf.h"
+static MessageBuffer mb;
+#endif
+
+#define HALFBIT
+
+extern char source[MAX_PATH];
+extern char vismatfile[_MAX_PATH];
+extern char incrementfile[_MAX_PATH];
+extern qboolean incremental;
+
+/*
+===================================================================
+
+VISIBILITY MATRIX
+
+Determine which patches can see each other
+Use the PVS to accelerate if available
+===================================================================
+*/
+
+#define TEST_EPSILON 0.1
+#define PLANE_TEST_EPSILON 0.01 // patch must be this much in front of the plane to be considered "in front"
+#define PATCH_FACE_OFFSET 0.1 // push patch origins off from the face by this amount to avoid self collisions
+
+#define STREAM_SIZE 512
+
+class CTransferMaker
+{
+public:
+
+ CTransferMaker( transfer_t *all_transfers );
+ ~CTransferMaker();
+
+ FORCEINLINE void TestMakeTransfer( Vector start, Vector stop, int ndxShooter, int ndxReciever )
+ {
+ g_RtEnv.AddToRayStream( m_RayStream, start, stop, &m_pResults[m_nTests] );
+ m_pShooterPatches[m_nTests] = ndxShooter;
+ m_pRecieverPatches[m_nTests] = ndxReciever;
+ ++m_nTests;
+ }
+
+ void Finish();
+
+private:
+
+ int m_nTests;
+ RayTracingSingleResult *m_pResults;
+ int *m_pShooterPatches;
+ int *m_pRecieverPatches;
+ RayStream m_RayStream;
+ transfer_t *m_AllTransfers;
+};
+
+CTransferMaker::CTransferMaker( transfer_t *all_transfers ) :
+ m_AllTransfers( all_transfers ), m_nTests( 0 )
+{
+ m_pResults = (RayTracingSingleResult *)calloc( 1, MAX_PATCHES * sizeof ( RayTracingSingleResult ) );
+ m_pShooterPatches = (int *)calloc( 1, MAX_PATCHES * sizeof( int ) );
+ m_pRecieverPatches = (int *)calloc( 1, MAX_PATCHES * sizeof( int ) );
+}
+
+CTransferMaker::~CTransferMaker()
+{
+ free ( m_pResults );
+ free ( m_pShooterPatches );
+ free (m_pRecieverPatches );
+}
+
+void CTransferMaker::Finish()
+{
+ g_RtEnv.FinishRayStream( m_RayStream );
+ for ( int i = 0; i < m_nTests; ++i )
+ {
+ if ( m_pResults[i].HitID == -1 || m_pResults[i].HitDistance >= m_pResults[i].ray_length )
+ {
+ MakeTransfer( m_pShooterPatches[i], m_pRecieverPatches[i], m_AllTransfers );
+ }
+ }
+ m_nTests = 0;
+}
+
+
+dleaf_t* PointInLeaf (int iNode, Vector const& point)
+{
+ if ( iNode < 0 )
+ return &dleafs[ (-1-iNode) ];
+
+ dnode_t *node = &dnodes[iNode];
+ dplane_t *plane = &dplanes[ node->planenum ];
+
+ float dist = DotProduct (point, plane->normal) - plane->dist;
+ if ( dist > TEST_EPSILON )
+ {
+ return PointInLeaf( node->children[0], point );
+ }
+ else if ( dist < -TEST_EPSILON )
+ {
+ return PointInLeaf( node->children[1], point );
+ }
+ else
+ {
+ dleaf_t *pTest = PointInLeaf( node->children[0], point );
+ if ( pTest->cluster != -1 )
+ return pTest;
+
+ return PointInLeaf( node->children[1], point );
+ }
+}
+
+
+int ClusterFromPoint( Vector const& point )
+{
+ dleaf_t *leaf = PointInLeaf( 0, point );
+
+ return leaf->cluster;
+}
+
+void PvsForOrigin (Vector& org, byte *pvs)
+{
+ int visofs;
+ int cluster;
+
+ if (!visdatasize)
+ {
+ memset (pvs, 255, (dvis->numclusters+7)/8 );
+ return;
+ }
+
+ cluster = ClusterFromPoint( org );
+ if ( cluster < 0 )
+ {
+ visofs = -1;
+ }
+ else
+ {
+ visofs = dvis->bitofs[ cluster ][DVIS_PVS];
+ }
+
+ if (visofs == -1)
+ Error ("visofs == -1");
+
+ DecompressVis (&dvisdata[visofs], pvs);
+}
+
+
+void TestPatchToPatch( int ndxPatch1, int ndxPatch2, int head, transfer_t *transfers, CTransferMaker &transferMaker, int iThread )
+{
+ Vector tmp;
+
+ //
+ // get patches
+ //
+ if( ndxPatch1 == g_Patches.InvalidIndex() || ndxPatch2 == g_Patches.InvalidIndex() )
+ return;
+
+ CPatch *patch = &g_Patches.Element( ndxPatch1 );
+ CPatch *patch2 = &g_Patches.Element( ndxPatch2 );
+
+ if (patch2->child1 != g_Patches.InvalidIndex() )
+ {
+ // check to see if we should use a child node instead
+
+ VectorSubtract( patch->origin, patch2->origin, tmp );
+ // SQRT( 1/4 )
+ // FIXME: should be based on form-factor (ie. include visible angle, etc)
+ if ( DotProduct(tmp, tmp) * 0.0625 < patch2->area )
+ {
+ TestPatchToPatch( ndxPatch1, patch2->child1, head, transfers, transferMaker, iThread );
+ TestPatchToPatch( ndxPatch1, patch2->child2, head, transfers, transferMaker, iThread );
+ return;
+ }
+ }
+
+ // check vis between patch and patch2
+ // if bit has not already been set
+ // && v2 is not behind light plane
+ // && v2 is visible from v1
+ if ( DotProduct( patch2->origin, patch->normal ) > patch->planeDist + PLANE_TEST_EPSILON )
+ {
+ // push out origins from face so that don't intersect their owners
+ Vector p1, p2;
+ VectorAdd( patch->origin, patch->normal, p1 );
+ VectorAdd( patch2->origin, patch2->normal, p2 );
+ transferMaker.TestMakeTransfer( p1, p2, ndxPatch1, ndxPatch2 );
+ }
+}
+
+
+/*
+==============
+TestPatchToFace
+
+Sets vis bits for all patches in the face
+==============
+*/
+void TestPatchToFace (unsigned patchnum, int facenum, int head, transfer_t *transfers, CTransferMaker &transferMaker, int iThread )
+{
+ if( faceParents.Element( facenum ) == g_Patches.InvalidIndex() || patchnum == g_Patches.InvalidIndex() )
+ return;
+
+ CPatch *patch = &g_Patches.Element( patchnum );
+ CPatch *patch2 = &g_Patches.Element( faceParents.Element( facenum ) );
+
+ // if emitter is behind that face plane, skip all patches
+
+ CPatch *pNextPatch;
+
+ if ( patch2 && DotProduct(patch->origin, patch2->normal) > patch2->planeDist + PLANE_TEST_EPSILON )
+ {
+ // we need to do a real test
+ for( ; patch2; patch2 = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch2->ndxNextParent != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch2->ndxNextParent );
+ }
+
+ /*
+ // skip patches too far away
+ VectorSubtract( patch->origin, patch2->origin, tmp );
+ if (DotProduct( tmp, tmp ) > 512 * 512)
+ continue;
+ */
+
+ int ndxPatch2 = patch2 - g_Patches.Base();
+ TestPatchToPatch( patchnum, ndxPatch2, head, transfers, transferMaker, iThread );
+ }
+ }
+}
+
+
+struct ClusterDispList_t
+{
+ CUtlVector<int> dispFaces;
+};
+
+static CUtlVector<ClusterDispList_t> g_ClusterDispFaces;
+
+//-----------------------------------------------------------------------------
+// Helps us find all displacements associated with a particular cluster
+//-----------------------------------------------------------------------------
+void AddDispsToClusterTable( void )
+{
+ g_ClusterDispFaces.SetCount( g_ClusterLeaves.Count() );
+
+ //
+ // add displacement faces to the cluster table
+ //
+ for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
+ {
+ // search for displacement faces
+ if( g_pFaces[ndxFace].dispinfo == -1 )
+ continue;
+
+ //
+ // get the clusters associated with the face
+ //
+ if( g_FacePatches.Element( ndxFace ) != g_FacePatches.InvalidIndex() )
+ {
+ CPatch *pNextPatch = NULL;
+ for( CPatch *pPatch = &g_Patches.Element( g_FacePatches.Element( ndxFace ) ); pPatch; pPatch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( pPatch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( pPatch->ndxNext );
+ }
+
+ if( pPatch->clusterNumber != g_Patches.InvalidIndex() )
+ {
+ int ndxDisp = g_ClusterDispFaces[pPatch->clusterNumber].dispFaces.Find( ndxFace );
+ if( ndxDisp == -1 )
+ {
+ ndxDisp = g_ClusterDispFaces[pPatch->clusterNumber].dispFaces.AddToTail();
+ g_ClusterDispFaces[pPatch->clusterNumber].dispFaces[ndxDisp] = ndxFace;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/*
+==============
+BuildVisRow
+
+Calc vis bits from a single patch
+==============
+*/
+void BuildVisRow (int patchnum, byte *pvs, int head, transfer_t *transfers, CTransferMaker &transferMaker, int iThread )
+{
+ int j, k, l, leafIndex;
+ CPatch *patch;
+ dleaf_t *leaf;
+ byte face_tested[MAX_MAP_FACES];
+ byte disp_tested[MAX_MAP_FACES];
+
+ patch = &g_Patches.Element( patchnum );
+
+ memset( face_tested, 0, numfaces ) ;
+ memset( disp_tested, 0, numfaces );
+
+ for (j=0; j<dvis->numclusters; j++)
+ {
+ if ( ! ( pvs[(j)>>3] & (1<<((j)&7)) ) )
+ {
+ continue; // not in pvs
+ }
+
+ for ( leafIndex = 0; leafIndex < g_ClusterLeaves[j].leafCount; leafIndex++ )
+ {
+ leaf = dleafs + g_ClusterLeaves[j].leafs[leafIndex];
+
+ for (k=0 ; k<leaf->numleaffaces; k++)
+ {
+ l = dleaffaces[leaf->firstleafface + k];
+ // faces can be marksurfed by multiple leaves, but
+ // don't bother testing again
+ if (face_tested[l])
+ {
+ continue;
+ }
+ face_tested[l] = 1;
+
+ // don't check patches on the same face
+ if (patch->faceNumber == l)
+ continue;
+ TestPatchToFace (patchnum, l, head, transfers, transferMaker, iThread );
+ }
+ }
+
+ int dispCount = g_ClusterDispFaces[j].dispFaces.Size();
+ for( int ndxDisp = 0; ndxDisp < dispCount; ndxDisp++ )
+ {
+ int ndxFace = g_ClusterDispFaces[j].dispFaces[ndxDisp];
+ if( disp_tested[ndxFace] )
+ continue;
+
+ disp_tested[ndxFace] = 1;
+
+ // don't check patches on the same face
+ if( patch->faceNumber == ndxFace )
+ continue;
+
+ TestPatchToFace( patchnum, ndxFace, head, transfers, transferMaker, iThread );
+ }
+ }
+
+
+ // Msg("%d) Transfers: %5d\n", patchnum, patch->numtransfers);
+}
+
+
+
+/*
+===========
+BuildVisLeafs
+
+ This is run by multiple threads
+===========
+*/
+
+transfer_t* BuildVisLeafs_Start()
+{
+ return (transfer_t *)calloc( 1, MAX_PATCHES * sizeof( transfer_t ) );
+}
+
+
+// If PatchCB is non-null, it is called after each row is generated (used by MPI).
+void BuildVisLeafs_Cluster(
+ int threadnum,
+ transfer_t *transfers,
+ int iCluster,
+ void (*PatchCB)(int iThread, int patchnum, CPatch *patch)
+ )
+{
+ byte pvs[(MAX_MAP_CLUSTERS+7)/8];
+ CPatch *patch;
+ int head;
+ unsigned patchnum;
+
+ DecompressVis( &dvisdata[ dvis->bitofs[ iCluster ][DVIS_PVS] ], pvs);
+ head = 0;
+
+ CTransferMaker transferMaker( transfers );
+
+ // light every patch in the cluster
+ if( clusterChildren.Element( iCluster ) != clusterChildren.InvalidIndex() )
+ {
+ CPatch *pNextPatch;
+ for( patch = &g_Patches.Element( clusterChildren.Element( iCluster ) ); patch; patch = pNextPatch )
+ {
+ //
+ // next patch
+ //
+ pNextPatch = NULL;
+ if( patch->ndxNextClusterChild != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNextClusterChild );
+ }
+
+ patchnum = patch - g_Patches.Base();
+
+ // build to all other world clusters
+ BuildVisRow (patchnum, pvs, head, transfers, transferMaker, threadnum );
+ transferMaker.Finish();
+
+ // do the transfers
+ MakeScales( patchnum, transfers );
+
+ // Let MPI aggregate the data if it's being used.
+ if ( PatchCB )
+ PatchCB( threadnum, patchnum, patch );
+ }
+ }
+}
+
+
+void BuildVisLeafs_End( transfer_t *transfers )
+{
+ free( transfers );
+}
+
+
+void BuildVisLeafs( int threadnum, void *pUserData )
+{
+ transfer_t *transfers = BuildVisLeafs_Start();
+
+ while ( 1 )
+ {
+ //
+ // build a minimal BSP tree that only
+ // covers areas relevent to the PVS
+ //
+ // JAY: Now this returns a cluster index
+ int iCluster = GetThreadWork();
+ if ( iCluster == -1 )
+ break;
+
+ BuildVisLeafs_Cluster( threadnum, transfers, iCluster, NULL );
+ }
+
+ BuildVisLeafs_End( transfers );
+}
+
+
+/*
+==============
+BuildVisMatrix
+==============
+*/
+void BuildVisMatrix (void)
+{
+ if ( g_bUseMPI )
+ {
+ RunMPIBuildVisLeafs();
+ }
+ else
+ {
+ RunThreadsOn (dvis->numclusters, true, BuildVisLeafs);
+ }
+}
+
+void FreeVisMatrix (void)
+{
+
+}
diff --git a/mp/src/utils/vrad/vismat.h b/mp/src/utils/vrad/vismat.h
index 74e0ba4c..927314a6 100644
--- a/mp/src/utils/vrad/vismat.h
+++ b/mp/src/utils/vrad/vismat.h
@@ -1,34 +1,34 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef VISMAT_H
-#define VISMAT_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-
-void BuildVisLeafs( int threadnum );
-
-
-// MPI uses these.
-struct transfer_t;
-transfer_t* BuildVisLeafs_Start();
-
-// If PatchCB is non-null, it is called after each row is generated (used by MPI).
-void BuildVisLeafs_Cluster(
- int threadnum,
- transfer_t *transfers,
- int iCluster,
- void (*PatchCB)(int iThread, int patchnum, CPatch *patch) );
-
-void BuildVisLeafs_End( transfer_t *transfers );
-
-
-
-#endif // VISMAT_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef VISMAT_H
+#define VISMAT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+
+void BuildVisLeafs( int threadnum );
+
+
+// MPI uses these.
+struct transfer_t;
+transfer_t* BuildVisLeafs_Start();
+
+// If PatchCB is non-null, it is called after each row is generated (used by MPI).
+void BuildVisLeafs_Cluster(
+ int threadnum,
+ transfer_t *transfers,
+ int iCluster,
+ void (*PatchCB)(int iThread, int patchnum, CPatch *patch) );
+
+void BuildVisLeafs_End( transfer_t *transfers );
+
+
+
+#endif // VISMAT_H
diff --git a/mp/src/utils/vrad/vrad.cpp b/mp/src/utils/vrad/vrad.cpp
index 8100dc3f..70e481fb 100644
--- a/mp/src/utils/vrad/vrad.cpp
+++ b/mp/src/utils/vrad/vrad.cpp
@@ -1,2936 +1,2936 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-// vrad.c
-
-#include "vrad.h"
-#include "physdll.h"
-#include "lightmap.h"
-#include "tier1/strtools.h"
-#include "vmpi.h"
-#include "macro_texture.h"
-#include "vmpi_tools_shared.h"
-#include "leaf_ambient_lighting.h"
-#include "tools_minidump.h"
-#include "loadcmdline.h"
-#include "byteswap.h"
-
-#define ALLOWDEBUGOPTIONS (0 || _DEBUG)
-
-static FileHandle_t pFpTrans = NULL;
-
-/*
-
-NOTES
------
-
-every surface must be divided into at least two patches each axis
-
-*/
-
-CUtlVector<CPatch> g_Patches;
-CUtlVector<int> g_FacePatches; // constains all patches, children first
-CUtlVector<int> faceParents; // contains only root patches, use next parent to iterate
-CUtlVector<int> clusterChildren;
-CUtlVector<Vector> emitlight;
-CUtlVector<bumplights_t> addlight;
-
-int num_sky_cameras;
-sky_camera_t sky_cameras[MAX_MAP_AREAS];
-int area_sky_cameras[MAX_MAP_AREAS];
-
-entity_t *face_entity[MAX_MAP_FACES];
-Vector face_offset[MAX_MAP_FACES]; // for rotating bmodels
-int fakeplanes;
-
-unsigned numbounce = 100; // 25; /* Originally this was 8 */
-
-float maxchop = 4; // coarsest allowed number of luxel widths for a patch
-float minchop = 4; // "-chop" tightest number of luxel widths for a patch, used on edges
-float dispchop = 8.0f; // number of luxel widths for a patch
-float g_MaxDispPatchRadius = 1500.0f; // Maximum radius allowed for displacement patches
-qboolean g_bDumpPatches;
-bool bDumpNormals = false;
-bool g_bDumpRtEnv = false;
-bool bRed2Black = true;
-bool g_bFastAmbient = false;
-bool g_bNoSkyRecurse = false;
-
-int junk;
-
-Vector ambient( 0, 0, 0 );
-
-float lightscale = 1.0;
-float dlight_threshold = 0.1; // was DIRECT_LIGHT constant
-
-char source[MAX_PATH] = "";
-char platformPath[MAX_PATH] = "";
-
-char level_name[MAX_PATH] = ""; // map filename, without extension or path info
-
-char global_lights[MAX_PATH] = "";
-char designer_lights[MAX_PATH] = "";
-char level_lights[MAX_PATH] = "";
-
-char vismatfile[_MAX_PATH] = "";
-char incrementfile[_MAX_PATH] = "";
-
-IIncremental *g_pIncremental = 0;
-bool g_bInterrupt = false; // Wsed with background lighting in WC. Tells VRAD
- // to stop lighting.
-float g_SunAngularExtent=0.0;
-
-float g_flSkySampleScale = 1.0;
-
-bool g_bLargeDispSampleRadius = false;
-
-bool g_bOnlyStaticProps = false;
-bool g_bShowStaticPropNormals = false;
-
-
-float gamma_value = 0.5;
-float indirect_sun = 1.0;
-float reflectivityScale = 1.0;
-qboolean do_extra = true;
-bool debug_extra = false;
-qboolean do_fast = false;
-qboolean do_centersamples = false;
-int extrapasses = 4;
-float smoothing_threshold = 0.7071067; // cos(45.0*(M_PI/180))
-// Cosine of smoothing angle(in radians)
-float coring = 1.0; // Light threshold to force to blackness(minimizes lightmaps)
-qboolean texscale = true;
-int dlight_map = 0; // Setting to 1 forces direct lighting into different lightmap than radiosity
-
-float luxeldensity = 1.0;
-unsigned num_degenerate_faces;
-
-qboolean g_bLowPriority = false;
-qboolean g_bLogHashData = false;
-bool g_bNoDetailLighting = false;
-double g_flStartTime;
-bool g_bStaticPropLighting = false;
-bool g_bStaticPropPolys = false;
-bool g_bTextureShadows = false;
-bool g_bDisablePropSelfShadowing = false;
-
-
-CUtlVector<byte> g_FacesVisibleToLights;
-
-RayTracingEnvironment g_RtEnv;
-
-dface_t *g_pFaces=0;
-
-// this is a list of material names used on static props which shouldn't cast shadows. a
-// sequential search is used since we allow substring matches. its not time critical, and this
-// functionality is a stopgap until vrad starts reading .vmt files.
-CUtlVector<char const *> g_NonShadowCastingMaterialStrings;
-/*
-===================================================================
-
-MISC
-
-===================================================================
-*/
-
-
-int leafparents[MAX_MAP_LEAFS];
-int nodeparents[MAX_MAP_NODES];
-
-void MakeParents (int nodenum, int parent)
-{
- int i, j;
- dnode_t *node;
-
- nodeparents[nodenum] = parent;
- node = &dnodes[nodenum];
-
- for (i=0 ; i<2 ; i++)
- {
- j = node->children[i];
- if (j < 0)
- leafparents[-j - 1] = nodenum;
- else
- MakeParents (j, nodenum);
- }
-}
-
-
-/*
-===================================================================
-
- TEXTURE LIGHT VALUES
-
-===================================================================
-*/
-
-typedef struct
-{
- char name[256];
- Vector value;
- char *filename;
-} texlight_t;
-
-#define MAX_TEXLIGHTS 128
-
-texlight_t texlights[MAX_TEXLIGHTS];
-int num_texlights;
-
-/*
-============
-ReadLightFile
-============
-*/
-void ReadLightFile (char *filename)
-{
- char buf[1024];
- int file_texlights = 0;
-
- FileHandle_t f = g_pFileSystem->Open( filename, "r" );
- if (!f)
- {
- Warning("Warning: Couldn't open texlight file %s.\n", filename);
- return;
- }
-
- Msg("[Reading texlights from '%s']\n", filename);
- while ( CmdLib_FGets( buf, sizeof( buf ), f ) )
- {
- // check ldr/hdr
- char * scan = buf;
- if ( !strnicmp( "hdr:", scan, 4) )
- {
- scan += 4;
- if ( ! g_bHDR )
- {
- continue;
- }
- }
- if ( !strnicmp( "ldr:", scan, 4) )
- {
- scan += 4;
- if ( g_bHDR )
- {
- continue;
- }
- }
-
- scan += strspn( scan, " \t" );
- char NoShadName[1024];
- if ( sscanf(scan,"noshadow %s",NoShadName)==1)
- {
- char * dot = strchr( NoShadName, '.' );
- if ( dot ) // if they specify .vmt, kill it
- * dot = 0;
- //printf("add %s as a non shadow casting material\n",NoShadName);
- g_NonShadowCastingMaterialStrings.AddToTail( strdup( NoShadName ));
- }
- else if ( sscanf( scan, "forcetextureshadow %s", NoShadName ) == 1 )
- {
- //printf("add %s as a non shadow casting material\n",NoShadName);
- ForceTextureShadowsOnModel( NoShadName );
- }
- else
- {
- char szTexlight[256];
- Vector value;
- if ( num_texlights == MAX_TEXLIGHTS )
- Error ("Too many texlights, max = %d", MAX_TEXLIGHTS);
-
- int argCnt = sscanf (scan, "%s ",szTexlight );
-
- if( argCnt != 1 )
- {
- if ( strlen( scan ) > 4 )
- Msg( "ignoring bad texlight '%s' in %s", scan, filename );
- continue;
- }
-
- LightForString( scan + strlen( szTexlight ) + 1, value );
-
- int j = 0;
- for( j; j < num_texlights; j ++ )
- {
- if ( strcmp( texlights[j].name, szTexlight ) == 0 )
- {
- if ( strcmp( texlights[j].filename, filename ) == 0 )
- {
- Msg( "ERROR\a: Duplication of '%s' in file '%s'!\n",
- texlights[j].name, texlights[j].filename );
- }
- else if ( texlights[j].value[0] != value[0]
- || texlights[j].value[1] != value[1]
- || texlights[j].value[2] != value[2] )
- {
- Warning( "Warning: Overriding '%s' from '%s' with '%s'!\n",
- texlights[j].name, texlights[j].filename, filename );
- }
- else
- {
- Warning( "Warning: Redundant '%s' def in '%s' AND '%s'!\n",
- texlights[j].name, texlights[j].filename, filename );
- }
- break;
- }
- }
- strcpy( texlights[j].name, szTexlight );
- VectorCopy( value, texlights[j].value );
- texlights[j].filename = filename;
- file_texlights ++;
-
- num_texlights = max( num_texlights, j + 1 );
- }
- }
- qprintf ( "[%i texlights parsed from '%s']\n\n", file_texlights, filename);
- g_pFileSystem->Close( f );
-}
-
-
-/*
-============
-LightForTexture
-============
-*/
-void LightForTexture( const char *name, Vector& result )
-{
- int i;
-
- result[ 0 ] = result[ 1 ] = result[ 2 ] = 0;
-
- char baseFilename[ MAX_PATH ];
-
- if ( Q_strncmp( "maps/", name, 5 ) == 0 )
- {
- // this might be a patch texture for cubemaps. try to parse out the original filename.
- if ( Q_strncmp( level_name, name + 5, Q_strlen( level_name ) ) == 0 )
- {
- const char *base = name + 5 + Q_strlen( level_name );
- if ( *base == '/' )
- {
- ++base; // step past the path separator
-
- // now we've gotten rid of the 'maps/level_name/' part, so we're left with
- // 'originalName_%d_%d_%d'.
- strcpy( baseFilename, base );
- bool foundSeparators = true;
- for ( int i=0; i<3; ++i )
- {
- char *underscore = Q_strrchr( baseFilename, '_' );
- if ( underscore && *underscore )
- {
- *underscore = '\0';
- }
- else
- {
- foundSeparators = false;
- }
- }
-
- if ( foundSeparators )
- {
- name = baseFilename;
- }
- }
- }
- }
-
- for (i=0 ; i<num_texlights ; i++)
- {
- if (!Q_strcasecmp (name, texlights[i].name))
- {
- VectorCopy( texlights[i].value, result );
- return;
- }
- }
-}
-
-/*
-=======================================================================
-
-MAKE FACES
-
-=======================================================================
-*/
-
-/*
-=============
-WindingFromFace
-=============
-*/
-winding_t *WindingFromFace (dface_t *f, Vector& origin )
-{
- int i;
- int se;
- dvertex_t *dv;
- int v;
- winding_t *w;
-
- w = AllocWinding (f->numedges);
- w->numpoints = f->numedges;
-
- for (i=0 ; i<f->numedges ; i++)
- {
- se = dsurfedges[f->firstedge + i];
- if (se < 0)
- v = dedges[-se].v[1];
- else
- v = dedges[se].v[0];
-
- dv = &dvertexes[v];
- VectorAdd (dv->point, origin, w->p[i]);
- }
-
- RemoveColinearPoints (w);
-
- return w;
-}
-
-/*
-=============
-BaseLightForFace
-=============
-*/
-void BaseLightForFace( dface_t *f, Vector& light, float *parea, Vector& reflectivity )
-{
- texinfo_t *tx;
- dtexdata_t *texdata;
-
- //
- // check for light emited by texture
- //
- tx = &texinfo[f->texinfo];
- texdata = &dtexdata[tx->texdata];
-
- LightForTexture (TexDataStringTable_GetString( texdata->nameStringTableID ), light);
-
-
- *parea = texdata->height * texdata->width;
-
- VectorScale( texdata->reflectivity, reflectivityScale, reflectivity );
-
- // always keep this less than 1 or the solution will not converge
- for ( int i = 0; i < 3; i++ )
- {
- if ( reflectivity[i] > 0.99 )
- reflectivity[i] = 0.99;
- }
-}
-
-qboolean IsSky (dface_t *f)
-{
- texinfo_t *tx;
-
- tx = &texinfo[f->texinfo];
- if (tx->flags & SURF_SKY)
- return true;
- return false;
-}
-
-#ifdef STATIC_FOG
-/*=============
-IsFog
-=============*/
-qboolean IsFog( dface_t *f )
-{
- texinfo_t *tx;
-
- tx = &texinfo[f->texinfo];
-
- // % denotes a fog texture
- if( tx->texture[0] == '%' )
- return true;
-
- return false;
-}
-#endif
-
-
-void ProcessSkyCameras()
-{
- int i;
- num_sky_cameras = 0;
- for (i = 0; i < numareas; ++i)
- {
- area_sky_cameras[i] = -1;
- }
-
- for (i = 0; i < num_entities; ++i)
- {
- entity_t *e = &entities[i];
- const char *name = ValueForKey (e, "classname");
- if (stricmp (name, "sky_camera"))
- continue;
-
- Vector origin;
- GetVectorForKey( e, "origin", origin );
- int node = PointLeafnum( origin );
- int area = -1;
- if (node >= 0 && node < numleafs) area = dleafs[node].area;
- float scale = FloatForKey( e, "scale" );
-
- if (scale > 0.0f)
- {
- sky_cameras[num_sky_cameras].origin = origin;
- sky_cameras[num_sky_cameras].sky_to_world = scale;
- sky_cameras[num_sky_cameras].world_to_sky = 1.0f / scale;
- sky_cameras[num_sky_cameras].area = area;
-
- if (area >= 0 && area < numareas)
- {
- area_sky_cameras[area] = num_sky_cameras;
- }
-
- ++num_sky_cameras;
- }
- }
-
-}
-
-
-/*
-=============
-MakePatchForFace
-=============
-*/
-float totalarea;
-void MakePatchForFace (int fn, winding_t *w)
-{
- dface_t *f = g_pFaces + fn;
- float area;
- CPatch *patch;
- Vector centroid(0,0,0);
- int i, j;
- texinfo_t *tx;
-
- // get texture info
- tx = &texinfo[f->texinfo];
-
- // No patches at all for fog!
-#ifdef STATIC_FOG
- if ( IsFog( f ) )
- return;
-#endif
-
- // the sky needs patches or the form factors don't work out correctly
- // if (IsSky( f ) )
- // return;
-
- area = WindingArea (w);
- if (area <= 0)
- {
- num_degenerate_faces++;
- // Msg("degenerate face\n");
- return;
- }
-
- totalarea += area;
-
- // get a patch
- int ndxPatch = g_Patches.AddToTail();
- patch = &g_Patches[ndxPatch];
- memset( patch, 0, sizeof( CPatch ) );
- patch->ndxNext = g_Patches.InvalidIndex();
- patch->ndxNextParent = g_Patches.InvalidIndex();
- patch->ndxNextClusterChild = g_Patches.InvalidIndex();
- patch->child1 = g_Patches.InvalidIndex();
- patch->child2 = g_Patches.InvalidIndex();
- patch->parent = g_Patches.InvalidIndex();
- patch->needsBumpmap = tx->flags & SURF_BUMPLIGHT ? true : false;
-
- // link and save patch data
- patch->ndxNext = g_FacePatches.Element( fn );
- g_FacePatches[fn] = ndxPatch;
-// patch->next = face_g_Patches[fn];
-// face_g_Patches[fn] = patch;
-
- // compute a separate scale for chop - since the patch "scale" is the texture scale
- // we want textures with higher resolution lighting to be chopped up more
- float chopscale[2];
- chopscale[0] = chopscale[1] = 16.0f;
- if ( texscale )
- {
- // Compute the texture "scale" in s,t
- for( i=0; i<2; i++ )
- {
- patch->scale[i] = 0.0f;
- chopscale[i] = 0.0f;
- for( j=0; j<3; j++ )
- {
- patch->scale[i] +=
- tx->textureVecsTexelsPerWorldUnits[i][j] *
- tx->textureVecsTexelsPerWorldUnits[i][j];
- chopscale[i] +=
- tx->lightmapVecsLuxelsPerWorldUnits[i][j] *
- tx->lightmapVecsLuxelsPerWorldUnits[i][j];
- }
- patch->scale[i] = sqrt( patch->scale[i] );
- chopscale[i] = sqrt( chopscale[i] );
- }
- }
- else
- {
- patch->scale[0] = patch->scale[1] = 1.0f;
- }
-
- patch->area = area;
-
- patch->sky = IsSky( f );
-
- // chop scaled up lightmaps coarser
- patch->luxscale = ((chopscale[0]+chopscale[1])/2);
- patch->chop = maxchop;
-
-
-#ifdef STATIC_FOG
- patch->fog = FALSE;
-#endif
-
- patch->winding = w;
-
- patch->plane = &dplanes[f->planenum];
-
- // make a new plane to adjust for origined bmodels
- if (face_offset[fn][0] || face_offset[fn][1] || face_offset[fn][2] )
- {
- dplane_t *pl;
-
- // origin offset faces must create new planes
- if (numplanes + fakeplanes >= MAX_MAP_PLANES)
- {
- Error ("numplanes + fakeplanes >= MAX_MAP_PLANES");
- }
- pl = &dplanes[numplanes + fakeplanes];
- fakeplanes++;
-
- *pl = *(patch->plane);
- pl->dist += DotProduct (face_offset[fn], pl->normal);
- patch->plane = pl;
- }
-
- patch->faceNumber = fn;
- WindingCenter (w, patch->origin);
-
- // Save "center" for generating the face normals later.
- VectorSubtract( patch->origin, face_offset[fn], face_centroids[fn] );
-
- VectorCopy( patch->plane->normal, patch->normal );
-
- WindingBounds (w, patch->face_mins, patch->face_maxs);
- VectorCopy( patch->face_mins, patch->mins );
- VectorCopy( patch->face_maxs, patch->maxs );
-
- BaseLightForFace( f, patch->baselight, &patch->basearea, patch->reflectivity );
-
- // Chop all texlights very fine.
- if ( !VectorCompare( patch->baselight, vec3_origin ) )
- {
- // patch->chop = do_extra ? maxchop / 2 : maxchop;
- tx->flags |= SURF_LIGHT;
- }
-
- // get rid of do extra functionality on displacement surfaces
- if( ValidDispFace( f ) )
- {
- patch->chop = maxchop;
- }
-
- // FIXME: If we wanted to add a dependency from vrad to the material system,
- // we could do this. It would add a bunch of file accesses, though:
-
- /*
- // Check for a material var which would override the patch chop
- bool bFound;
- const char *pMaterialName = TexDataStringTable_GetString( dtexdata[ tx->texdata ].nameStringTableID );
- MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, &bFound, false );
- if ( bFound )
- {
- const char *pChopValue = GetMaterialVar( hMaterial, "%chop" );
- if ( pChopValue )
- {
- float flChopValue;
- if ( sscanf( pChopValue, "%f", &flChopValue ) > 0 )
- {
- patch->chop = flChopValue;
- }
- }
- }
- */
-}
-
-
-entity_t *EntityForModel (int modnum)
-{
- int i;
- char *s;
- char name[16];
-
- sprintf (name, "*%i", modnum);
- // search the entities for one using modnum
- for (i=0 ; i<num_entities ; i++)
- {
- s = ValueForKey (&entities[i], "model");
- if (!strcmp (s, name))
- return &entities[i];
- }
-
- return &entities[0];
-}
-
-/*
-=============
-MakePatches
-=============
-*/
-void MakePatches (void)
-{
- int i, j;
- dface_t *f;
- int fn;
- winding_t *w;
- dmodel_t *mod;
- Vector origin;
- entity_t *ent;
-
- ParseEntities ();
- qprintf ("%i faces\n", numfaces);
-
- for (i=0 ; i<nummodels ; i++)
- {
- mod = dmodels+i;
- ent = EntityForModel (i);
- VectorCopy (vec3_origin, origin);
-
- // bmodels with origin brushes need to be offset into their
- // in-use position
- GetVectorForKey (ent, "origin", origin);
-
- for (j=0 ; j<mod->numfaces ; j++)
- {
- fn = mod->firstface + j;
- face_entity[fn] = ent;
- VectorCopy (origin, face_offset[fn]);
- f = &g_pFaces[fn];
- if( f->dispinfo == -1 )
- {
- w = WindingFromFace (f, origin );
- MakePatchForFace( fn, w );
- }
- }
- }
-
- if (num_degenerate_faces > 0)
- {
- qprintf("%d degenerate faces\n", num_degenerate_faces );
- }
-
- qprintf ("%i square feet [%.2f square inches]\n", (int)(totalarea/144), totalarea );
-
- // make the displacement surface patches
- StaticDispMgr()->MakePatches();
-}
-
-/*
-=======================================================================
-
-SUBDIVIDE
-
-=======================================================================
-*/
-
-
-//-----------------------------------------------------------------------------
-// Purpose: does this surface take/emit light
-//-----------------------------------------------------------------------------
-bool PreventSubdivision( CPatch *patch )
-{
- dface_t *f = g_pFaces + patch->faceNumber;
- texinfo_t *tx = &texinfo[f->texinfo];
-
- if (tx->flags & SURF_NOCHOP)
- return true;
-
- if (tx->flags & SURF_NOLIGHT && !(tx->flags & SURF_LIGHT))
- return true;
-
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: subdivide the "parent" patch
-//-----------------------------------------------------------------------------
-int CreateChildPatch( int nParentIndex, winding_t *pWinding, float flArea, const Vector &vecCenter )
-{
- int nChildIndex = g_Patches.AddToTail();
-
- CPatch *child = &g_Patches[nChildIndex];
- CPatch *parent = &g_Patches[nParentIndex];
-
- // copy all elements of parent patch to children
- *child = *parent;
-
- // Set up links
- child->ndxNext = g_Patches.InvalidIndex();
- child->ndxNextParent = g_Patches.InvalidIndex();
- child->ndxNextClusterChild = g_Patches.InvalidIndex();
- child->child1 = g_Patches.InvalidIndex();
- child->child2 = g_Patches.InvalidIndex();
- child->parent = nParentIndex;
- child->m_IterationKey = 0;
-
- child->winding = pWinding;
- child->area = flArea;
-
- VectorCopy( vecCenter, child->origin );
- if ( ValidDispFace( g_pFaces + child->faceNumber ) )
- {
- // shouldn't get here anymore!!
- Msg( "SubdividePatch: Error - Should not be here!\n" );
- StaticDispMgr()->GetDispSurfNormal( child->faceNumber, child->origin, child->normal, true );
- }
- else
- {
- GetPhongNormal( child->faceNumber, child->origin, child->normal );
- }
-
- child->planeDist = child->plane->dist;
- WindingBounds(child->winding, child->mins, child->maxs);
-
- if ( !VectorCompare( child->baselight, vec3_origin ) )
- {
- // don't check edges on surf lights
- return nChildIndex;
- }
-
- // Subdivide patch towards minchop if on the edge of the face
- Vector total;
- VectorSubtract( child->maxs, child->mins, total );
- VectorScale( total, child->luxscale, total );
- if ( child->chop > minchop && (total[0] < child->chop) && (total[1] < child->chop) && (total[2] < child->chop) )
- {
- for ( int i=0; i<3; ++i )
- {
- if ( (child->face_maxs[i] == child->maxs[i] || child->face_mins[i] == child->mins[i] )
- && total[i] > minchop )
- {
- child->chop = max( minchop, child->chop / 2 );
- break;
- }
- }
- }
-
- return nChildIndex;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: subdivide the "parent" patch
-//-----------------------------------------------------------------------------
-void SubdividePatch( int ndxPatch )
-{
- winding_t *w, *o1, *o2;
- Vector total;
- Vector split;
- vec_t dist;
- vec_t widest = -1;
- int i, widest_axis = -1;
- bool bSubdivide = false;
-
- // get the current patch
- CPatch *patch = &g_Patches.Element( ndxPatch );
- if ( !patch )
- return;
-
- // never subdivide sky patches
- if ( patch->sky )
- return;
-
- // get the patch winding
- w = patch->winding;
-
- // subdivide along the widest axis
- VectorSubtract (patch->maxs, patch->mins, total);
- VectorScale( total, patch->luxscale, total );
- for (i=0 ; i<3 ; i++)
- {
- if ( total[i] > widest )
- {
- widest_axis = i;
- widest = total[i];
- }
-
- if ( (total[i] >= patch->chop) && (total[i] >= minchop) )
- {
- bSubdivide = true;
- }
- }
-
- if ((!bSubdivide) && widest_axis != -1)
- {
- // make more square
- if (total[widest_axis] > total[(widest_axis + 1) % 3] * 2 && total[widest_axis] > total[(widest_axis + 2) % 3] * 2)
- {
- if (patch->chop > minchop)
- {
- bSubdivide = true;
- patch->chop = max( minchop, patch->chop / 2 );
- }
- }
- }
-
- if ( !bSubdivide )
- return;
-
- // split the winding
- VectorCopy (vec3_origin, split);
- split[widest_axis] = 1;
- dist = (patch->mins[widest_axis] + patch->maxs[widest_axis])*0.5f;
- ClipWindingEpsilon (w, split, dist, ON_EPSILON, &o1, &o2);
-
- // calculate the area of the patches to see if they are "significant"
- Vector center1, center2;
- float area1 = WindingAreaAndBalancePoint( o1, center1 );
- float area2 = WindingAreaAndBalancePoint( o2, center2 );
-
- if( area1 == 0 || area2 == 0 )
- {
- Msg( "zero area child patch\n" );
- return;
- }
-
- // create new child patches
- int ndxChild1Patch = CreateChildPatch( ndxPatch, o1, area1, center1 );
- int ndxChild2Patch = CreateChildPatch( ndxPatch, o2, area2, center2 );
-
- // FIXME: This could go into CreateChildPatch if child1, child2 were stored in the patch as child[0], child[1]
- patch = &g_Patches.Element( ndxPatch );
- patch->child1 = ndxChild1Patch;
- patch->child2 = ndxChild2Patch;
-
- SubdividePatch( ndxChild1Patch );
- SubdividePatch( ndxChild2Patch );
-}
-
-
-/*
-=============
-SubdividePatches
-=============
-*/
-void SubdividePatches (void)
-{
- unsigned i, num;
-
- if (numbounce == 0)
- return;
-
- unsigned int uiPatchCount = g_Patches.Size();
- qprintf ("%i patches before subdivision\n", uiPatchCount);
-
- for (i = 0; i < uiPatchCount; i++)
- {
- CPatch *pCur = &g_Patches.Element( i );
- pCur->planeDist = pCur->plane->dist;
-
- pCur->ndxNextParent = faceParents.Element( pCur->faceNumber );
- faceParents[pCur->faceNumber] = pCur - g_Patches.Base();
- }
-
- for (i=0 ; i< uiPatchCount; i++)
- {
- CPatch *patch = &g_Patches.Element( i );
- patch->parent = -1;
- if ( PreventSubdivision(patch) )
- continue;
-
- if (!do_fast)
- {
- if( g_pFaces[patch->faceNumber].dispinfo == -1 )
- {
- SubdividePatch( i );
- }
- else
- {
- StaticDispMgr()->SubdividePatch( i );
- }
- }
- }
-
- // fixup next pointers
- for (i = 0; i < (unsigned)numfaces; i++)
- {
- g_FacePatches[i] = g_FacePatches.InvalidIndex();
- }
-
- uiPatchCount = g_Patches.Size();
- for (i = 0; i < uiPatchCount; i++)
- {
- CPatch *pCur = &g_Patches.Element( i );
- pCur->ndxNext = g_FacePatches.Element( pCur->faceNumber );
- g_FacePatches[pCur->faceNumber] = pCur - g_Patches.Base();
-
-#if 0
- CPatch *prev;
- prev = face_g_Patches[g_Patches[i].faceNumber];
- g_Patches[i].next = prev;
- face_g_Patches[g_Patches[i].faceNumber] = &g_Patches[i];
-#endif
- }
-
- // Cache off the leaf number:
- // We have to do this after subdivision because some patches span leaves.
- // (only the faces for model #0 are split by it's BSP which is what governs the PVS, and the leaves we're interested in)
- // Sub models (1-255) are only split for the BSP that their model forms.
- // When those patches are subdivided their origins can end up in a different leaf.
- // The engine will split (clip) those faces at run time to the world BSP because the models
- // are dynamic and can be moved. In the software renderer, they must be split exactly in order
- // to sort per polygon.
- for ( i = 0; i < uiPatchCount; i++ )
- {
- g_Patches[i].clusterNumber = ClusterFromPoint( g_Patches[i].origin );
-
- //
- // test for point in solid space (can happen with detail and displacement surfaces)
- //
- if( g_Patches[i].clusterNumber == -1 )
- {
- for( int j = 0; j < g_Patches[i].winding->numpoints; j++ )
- {
- int clusterNumber = ClusterFromPoint( g_Patches[i].winding->p[j] );
- if( clusterNumber != -1 )
- {
- g_Patches[i].clusterNumber = clusterNumber;
- break;
- }
- }
- }
- }
-
- // build the list of patches that need to be lit
- for ( num = 0; num < uiPatchCount; num++ )
- {
- // do them in reverse order
- i = uiPatchCount - num - 1;
-
- // skip patches with children
- CPatch *pCur = &g_Patches.Element( i );
- if( pCur->child1 == g_Patches.InvalidIndex() )
- {
- if( pCur->clusterNumber != - 1 )
- {
- pCur->ndxNextClusterChild = clusterChildren.Element( pCur->clusterNumber );
- clusterChildren[pCur->clusterNumber] = pCur - g_Patches.Base();
- }
- }
-
-#if 0
- if (g_Patches[i].child1 == g_Patches.InvalidIndex() )
- {
- if( g_Patches[i].clusterNumber != -1 )
- {
- g_Patches[i].nextclusterchild = cluster_children[g_Patches[i].clusterNumber];
- cluster_children[g_Patches[i].clusterNumber] = &g_Patches[i];
- }
- }
-#endif
- }
-
- qprintf ("%i patches after subdivision\n", uiPatchCount);
-}
-
-
-//=====================================================================
-
-/*
-=============
-MakeScales
-
- This is the primary time sink.
- It can be run multi threaded.
-=============
-*/
-int total_transfer;
-int max_transfer;
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Computes the form factor from a polygon patch to a differential patch
-// using formula 81 of Philip Dutre's Global Illumination Compendium,
-// [email protected], http://www.graphics.cornell.edu/~phil/GI/
-//-----------------------------------------------------------------------------
-float FormFactorPolyToDiff ( CPatch *pPolygon, CPatch* pDifferential )
-{
- winding_t *pWinding = pPolygon->winding;
-
- float flFormFactor = 0.0f;
-
- for ( int iPoint = 0; iPoint < pWinding->numpoints; iPoint++ )
- {
- int iNextPoint = ( iPoint < pWinding->numpoints - 1 ) ? iPoint + 1 : 0;
-
- Vector vGammaVector, vVector1, vVector2;
- VectorSubtract( pWinding->p[ iPoint ], pDifferential->origin, vVector1 );
- VectorSubtract( pWinding->p[ iNextPoint ], pDifferential->origin, vVector2 );
- VectorNormalize( vVector1 );
- VectorNormalize( vVector2 );
- CrossProduct( vVector1, vVector2, vGammaVector );
- float flSinAlpha = VectorNormalize( vGammaVector );
- if (flSinAlpha < -1.0f || flSinAlpha > 1.0f)
- return 0.0f;
- vGammaVector *= asin( flSinAlpha );
-
- flFormFactor += DotProduct( vGammaVector, pDifferential->normal );
- }
-
- flFormFactor *= ( 0.5f / pPolygon->area ); // divide by pi later, multiply by area later
-
- return flFormFactor;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Computes the form factor from a differential element to a differential
-// element. This is okay when the distance between patches is 5 times
-// greater than patch size. Lecture slides by Pat Hanrahan,
-// http://graphics.stanford.edu/courses/cs348b-00/lectures/lecture17/radiosity.2.pdf
-//-----------------------------------------------------------------------------
-float FormFactorDiffToDiff ( CPatch *pDiff1, CPatch* pDiff2 )
-{
- Vector vDelta;
- VectorSubtract( pDiff1->origin, pDiff2->origin, vDelta );
- float flLength = VectorNormalize( vDelta );
-
- return -DotProduct( vDelta, pDiff1->normal ) * DotProduct( vDelta, pDiff2->normal ) / ( flLength * flLength );
-}
-
-
-
-void MakeTransfer( int ndxPatch1, int ndxPatch2, transfer_t *all_transfers )
-//void MakeTransfer (CPatch *patch, CPatch *patch2, transfer_t *all_transfers )
-{
- Vector delta;
- vec_t scale;
- float trans;
- transfer_t *transfer;
-
- //
- // get patches
- //
- if( ndxPatch1 == g_Patches.InvalidIndex() || ndxPatch2 == g_Patches.InvalidIndex() )
- return;
-
- CPatch *pPatch1 = &g_Patches.Element( ndxPatch1 );
- CPatch *pPatch2 = &g_Patches.Element( ndxPatch2 );
-
- if (IsSky( &g_pFaces[ pPatch2->faceNumber ] ) )
- return;
-
- // overflow check!
- if ( pPatch1->numtransfers >= MAX_PATCHES)
- {
- return;
- }
-
- // hack for patch areas that area <= 0 (degenerate)
- if ( pPatch2->area <= 0)
- {
- return;
- }
-
- transfer = &all_transfers[pPatch1->numtransfers];
-
- scale = FormFactorDiffToDiff( pPatch2, pPatch1 );
-
- // patch normals may be > 90 due to smoothing groups
- if (scale <= 0)
- {
- //Msg("scale <= 0\n");
- return;
- }
-
- // Test 5 times rule
- Vector vDelta;
- VectorSubtract( pPatch1->origin, pPatch2->origin, vDelta );
- float flThreshold = ( M_PI * 0.04 ) * DotProduct( vDelta, vDelta );
-
- if (flThreshold < pPatch2->area)
- {
- scale = FormFactorPolyToDiff( pPatch2, pPatch1 );
- if (scale <= 0.0)
- return;
- }
-
- trans = (pPatch2->area*scale);
-
- if (trans <= TRANSFER_EPSILON)
- {
- return;
- }
-
- transfer->patch = pPatch2 - g_Patches.Base();
-
- // FIXME: why is this not trans?
- transfer->transfer = trans;
-
-#if 0
- // DEBUG! Dump patches and transfer connection for displacements. This creates a lot of data, so only
- // use it when you really want it - that is why it is #if-ed out.
- if ( g_bDumpPatches )
- {
- if ( !pFpTrans )
- {
- pFpTrans = g_pFileSystem->Open( "trans.txt", "w" );
- }
- Vector light = pPatch1->totallight.light[0] + pPatch1->directlight;
- WriteWinding( pFpTrans, pPatch1->winding, light );
- light = pPatch2->totallight.light[0] + pPatch2->directlight;
- WriteWinding( pFpTrans, pPatch2->winding, light );
- WriteLine( pFpTrans, pPatch1->origin, pPatch2->origin, Vector( 255, 0, 255 ) );
- }
-#endif
-
- pPatch1->numtransfers++;
-}
-
-
-void MakeScales ( int ndxPatch, transfer_t *all_transfers )
-{
- int j;
- float total;
- transfer_t *t, *t2;
- total = 0;
-
- if( ndxPatch == g_Patches.InvalidIndex() )
- return;
- CPatch *patch = &g_Patches.Element( ndxPatch );
-
- // copy the transfers out
- if (patch->numtransfers)
- {
- if (patch->numtransfers > max_transfer)
- {
- max_transfer = patch->numtransfers;
- }
-
-
- patch->transfers = ( transfer_t* )calloc (1, patch->numtransfers * sizeof(transfer_t));
- if (!patch->transfers)
- Error ("Memory allocation failure");
-
- // get total transfer energy
- t2 = all_transfers;
-
- // overflow check!
- for (j=0 ; j<patch->numtransfers ; j++, t2++)
- {
- total += t2->transfer;
- }
-
- // the total transfer should be PI, but we need to correct errors due to overlaping surfaces
- if (total > M_PI)
- total = 1.0f/total;
- else
- total = 1.0f/M_PI;
-
- t = patch->transfers;
- t2 = all_transfers;
- for (j=0 ; j<patch->numtransfers ; j++, t++, t2++)
- {
- t->transfer = t2->transfer*total;
- t->patch = t2->patch;
- }
- if (patch->numtransfers > max_transfer)
- {
- max_transfer = patch->numtransfers;
- }
- }
- else
- {
- // Error - patch has no transfers
- // patch->totallight[2] = 255;
- }
-
- ThreadLock ();
- total_transfer += patch->numtransfers;
- ThreadUnlock ();
-}
-
-/*
-=============
-WriteWorld
-=============
-*/
-void WriteWorld (char *name, int iBump)
-{
- unsigned j;
- FileHandle_t out;
- CPatch *patch;
-
- out = g_pFileSystem->Open( name, "w" );
- if (!out)
- Error ("Couldn't open %s", name);
-
- unsigned int uiPatchCount = g_Patches.Size();
- for (j=0; j<uiPatchCount; j++)
- {
- patch = &g_Patches.Element( j );
-
- // skip parent patches
- if (patch->child1 != g_Patches.InvalidIndex() )
- continue;
-
- if( patch->clusterNumber == -1 )
- {
- Vector vGreen;
- VectorClear( vGreen );
- vGreen[1] = 256.0f;
- WriteWinding( out, patch->winding, vGreen );
- }
- else
- {
- Vector light = patch->totallight.light[iBump] + patch->directlight;
- WriteWinding( out, patch->winding, light );
- if( bDumpNormals )
- {
- WriteNormal( out, patch->origin, patch->plane->normal, 15.0f, patch->plane->normal * 255.0f );
- }
- }
- }
-
- g_pFileSystem->Close( out );
-}
-
-void WriteRTEnv (char *name)
-{
- FileHandle_t out;
-
- out = g_pFileSystem->Open( name, "w" );
- if (!out)
- Error ("Couldn't open %s", name);
-
- winding_t *triw = AllocWinding( 3 );
- triw->numpoints = 3;
-
- for( int i = 0; i < g_RtEnv.OptimizedTriangleList.Size(); i++ )
- {
- triw->p[0] = g_RtEnv.OptimizedTriangleList[i].Vertex( 0);
- triw->p[1] = g_RtEnv.OptimizedTriangleList[i].Vertex( 1);
- triw->p[2] = g_RtEnv.OptimizedTriangleList[i].Vertex( 2);
- int id = g_RtEnv.OptimizedTriangleList[i].m_Data.m_GeometryData.m_nTriangleID;
- Vector color(0, 0, 0);
- if (id & TRACE_ID_OPAQUE) color.Init(0, 255, 0);
- if (id & TRACE_ID_SKY) color.Init(0, 0, 255);
- if (id & TRACE_ID_STATICPROP) color.Init(255, 0, 0);
- WriteWinding(out, triw, color);
- }
- FreeWinding(triw);
-
- g_pFileSystem->Close( out );
-}
-
-void WriteWinding (FileHandle_t out, winding_t *w, Vector& color )
-{
- int i;
-
- CmdLib_FPrintf (out, "%i\n", w->numpoints);
- for (i=0 ; i<w->numpoints ; i++)
- {
- CmdLib_FPrintf (out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
- w->p[i][0],
- w->p[i][1],
- w->p[i][2],
- color[ 0 ] / 256,
- color[ 1 ] / 256,
- color[ 2 ] / 256 );
- }
-}
-
-
-void WriteNormal( FileHandle_t out, Vector const &nPos, Vector const &nDir,
- float length, Vector const &color )
-{
- CmdLib_FPrintf( out, "2\n" );
- CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
- nPos.x, nPos.y, nPos.z,
- color.x / 256, color.y / 256, color.z / 256 );
- CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
- nPos.x + ( nDir.x * length ),
- nPos.y + ( nDir.y * length ),
- nPos.z + ( nDir.z * length ),
- color.x / 256, color.y / 256, color.z / 256 );
-}
-
-void WriteLine( FileHandle_t out, const Vector &vecPos1, const Vector &vecPos2, const Vector &color )
-{
- CmdLib_FPrintf( out, "2\n" );
- CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
- vecPos1.x, vecPos1.y, vecPos1.z,
- color.x / 256, color.y / 256, color.z / 256 );
- CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
- vecPos2.x, vecPos2.y, vecPos2.z,
- color.x / 256, color.y / 256, color.z / 256 );
-}
-
-void WriteTrace( const char *pFileName, const FourRays &rays, const RayTracingResult& result )
-{
- FileHandle_t out;
-
- out = g_pFileSystem->Open( pFileName, "a" );
- if (!out)
- Error ("Couldn't open %s", pFileName);
-
- // Draws rays
- for ( int i = 0; i < 4; ++i )
- {
- Vector vecOrigin = rays.origin.Vec(i);
- Vector vecEnd = rays.direction.Vec(i);
- VectorNormalize( vecEnd );
- vecEnd *= SubFloat( result.HitDistance, i );
- vecEnd += vecOrigin;
- WriteLine( out, vecOrigin, vecEnd, Vector( 256, 0, 0 ) );
- WriteNormal( out, vecEnd, result.surface_normal.Vec(i), 10.0f, Vector( 256, 265, 0 ) );
- }
-
- g_pFileSystem->Close( out );
-}
-
-
-/*
-=============
-CollectLight
-=============
-*/
-// patch's totallight += new light received to each patch
-// patch's emitlight = addlight (newly received light from GatherLight)
-// patch's addlight = 0
-// pull received light from children.
-void CollectLight( Vector& total )
-{
- int i, j;
- CPatch *patch;
-
- VectorFill( total, 0 );
-
- // process patches in reverse order so that children are processed before their parents
- unsigned int uiPatchCount = g_Patches.Size();
- for( i = uiPatchCount - 1; i >= 0; i-- )
- {
- patch = &g_Patches.Element( i );
- int normalCount = patch->needsBumpmap ? NUM_BUMP_VECTS+1 : 1;
- // sky's never collect light, it is just dropped
- if (patch->sky)
- {
- VectorFill( emitlight[ i ], 0 );
- }
- else if ( patch->child1 == g_Patches.InvalidIndex() )
- {
- // This is a leaf node.
- for ( j = 0; j < normalCount; j++ )
- {
- VectorAdd( patch->totallight.light[j], addlight[i].light[j], patch->totallight.light[j] );
- }
- VectorCopy( addlight[i].light[0], emitlight[i] );
- VectorAdd( total, emitlight[i], total );
- }
- else
- {
- // This is an interior node.
- // Pull received light from children.
- float s1, s2;
- CPatch *child1;
- CPatch *child2;
-
- child1 = &g_Patches[patch->child1];
- child2 = &g_Patches[patch->child2];
-
- // BUG: This doesn't do anything?
- if ((int)patch->area != (int)(child1->area + child2->area))
- s1 = 0;
-
- s1 = child1->area / (child1->area + child2->area);
- s2 = child2->area / (child1->area + child2->area);
-
- // patch->totallight = s1 * child1->totallight + s2 * child2->totallight
- for ( j = 0; j < normalCount; j++ )
- {
- VectorScale( child1->totallight.light[j], s1, patch->totallight.light[j] );
- VectorMA( patch->totallight.light[j], s2, child2->totallight.light[j], patch->totallight.light[j] );
- }
-
- // patch->emitlight = s1 * child1->emitlight + s2 * child2->emitlight
- VectorScale( emitlight[patch->child1], s1, emitlight[i] );
- VectorMA( emitlight[i], s2, emitlight[patch->child2], emitlight[i] );
- }
- for ( j = 0; j < NUM_BUMP_VECTS+1; j++ )
- {
- VectorFill( addlight[ i ].light[j], 0 );
- }
- }
-}
-
-/*
-=============
-GatherLight
-
-Get light from other patches
- Run multi-threaded
-=============
-*/
-
-#ifdef _WIN32
-#pragma warning (disable:4701)
-#endif
-
-extern void GetBumpNormals( const float* sVect, const float* tVect, const Vector& flatNormal,
- const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] );
-
-
-void PreGetBumpNormalsForDisp( texinfo_t *pTexinfo, Vector &vecU, Vector &vecV, Vector &vecNormal )
-{
- Vector vecTexU( pTexinfo->textureVecsTexelsPerWorldUnits[0][0], pTexinfo->textureVecsTexelsPerWorldUnits[0][1], pTexinfo->textureVecsTexelsPerWorldUnits[0][2] );
- Vector vecTexV( pTexinfo->textureVecsTexelsPerWorldUnits[1][0], pTexinfo->textureVecsTexelsPerWorldUnits[1][1], pTexinfo->textureVecsTexelsPerWorldUnits[1][2] );
- Vector vecLightU( pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][0], pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][1], pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][2] );
- Vector vecLightV( pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][0], pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][1], pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][2] );
-
- VectorNormalize( vecTexU );
- VectorNormalize( vecTexV );
- VectorNormalize( vecLightU );
- VectorNormalize( vecLightV );
-
- bool bDoConversion = false;
- if ( fabs( vecTexU.Dot( vecLightU ) ) < 0.999f )
- {
- bDoConversion = true;
- }
-
- if ( fabs( vecTexV.Dot( vecLightV ) ) < 0.999f )
- {
- bDoConversion = true;
- }
-
- if ( bDoConversion )
- {
- matrix3x4_t matTex( vecTexU, vecTexV, vecNormal, vec3_origin );
- matrix3x4_t matLight( vecLightU, vecLightV, vecNormal, vec3_origin );
- matrix3x4_t matTmp;
- ConcatTransforms ( matLight, matTex, matTmp );
- MatrixGetColumn( matTmp, 0, vecU );
- MatrixGetColumn( matTmp, 1, vecV );
- MatrixGetColumn( matTmp, 2, vecNormal );
-
- Assert( fabs( vecTexU.Dot( vecTexV ) ) <= 0.001f );
- return;
- }
-
- vecU = vecTexU;
- vecV = vecTexV;
-}
-
-void GatherLight (int threadnum, void *pUserData)
-{
- int i, j, k;
- transfer_t *trans;
- int num;
- CPatch *patch;
- Vector sum, v;
-
- while (1)
- {
- j = GetThreadWork ();
- if (j == -1)
- break;
-
- patch = &g_Patches[j];
-
- trans = patch->transfers;
- num = patch->numtransfers;
- if ( patch->needsBumpmap )
- {
- Vector delta;
- Vector bumpSum[NUM_BUMP_VECTS+1];
- Vector normals[NUM_BUMP_VECTS+1];
-
- // Disps
- bool bDisp = ( g_pFaces[patch->faceNumber].dispinfo != -1 );
- if ( bDisp )
- {
- normals[0] = patch->normal;
- texinfo_t *pTexinfo = &texinfo[g_pFaces[patch->faceNumber].texinfo];
- Vector vecTexU, vecTexV;
- PreGetBumpNormalsForDisp( pTexinfo, vecTexU, vecTexV, normals[0] );
-
- // use facenormal along with the smooth normal to build the three bump map vectors
- GetBumpNormals( vecTexU, vecTexV, normals[0], normals[0], &normals[1] );
- }
- else
- {
- GetPhongNormal( patch->faceNumber, patch->origin, normals[0] );
-
- texinfo_t *pTexinfo = &texinfo[g_pFaces[patch->faceNumber].texinfo];
- // use facenormal along with the smooth normal to build the three bump map vectors
- GetBumpNormals( pTexinfo->textureVecsTexelsPerWorldUnits[0],
- pTexinfo->textureVecsTexelsPerWorldUnits[1], patch->normal,
- normals[0], &normals[1] );
- }
-
- // force the base lightmap to use the flat normal instead of the phong normal
- // FIXME: why does the patch not use the phong normal?
- normals[0] = patch->normal;
-
- for ( i = 0; i < NUM_BUMP_VECTS+1; i++ )
- {
- VectorFill( bumpSum[i], 0 );
- }
-
- float dot;
- for (k=0 ; k<num ; k++, trans++)
- {
- CPatch *patch2 = &g_Patches[trans->patch];
-
- // get vector to other patch
- VectorSubtract (patch2->origin, patch->origin, delta);
- VectorNormalize (delta);
- // find light emitted from other patch
- for(i=0; i<3; i++)
- {
- v[i] = emitlight[trans->patch][i] * patch2->reflectivity[i];
- }
- // remove normal already factored into transfer steradian
- float scale = 1.0f / DotProduct (delta, patch->normal);
- VectorScale( v, trans->transfer * scale, v );
-
- Vector bumpTransfer;
- for ( i = 0; i < NUM_BUMP_VECTS+1; i++ )
- {
- dot = DotProduct( delta, normals[i] );
- if ( dot <= 0 )
- {
-// Assert( i > 0 ); // if this hits, then the transfer shouldn't be here. It doesn't face the flat normal of this face!
- continue;
- }
- bumpTransfer = v * dot;
- VectorAdd( bumpSum[i], bumpTransfer, bumpSum[i] );
- }
- }
- for ( i = 0; i < NUM_BUMP_VECTS+1; i++ )
- {
- VectorCopy( bumpSum[i], addlight[j].light[i] );
- }
- }
- else
- {
- VectorFill( sum, 0 );
- for (k=0 ; k<num ; k++, trans++)
- {
- for(i=0; i<3; i++)
- {
- v[i] = emitlight[trans->patch][i] * g_Patches[trans->patch].reflectivity[i];
- }
- VectorScale( v, trans->transfer, v );
- VectorAdd( sum, v, sum );
- }
- VectorCopy( sum, addlight[j].light[0] );
- }
- }
-}
-
-#ifdef _WIN32
-#pragma warning (default:4701)
-#endif
-
-
-/*
-=============
-BounceLight
-=============
-*/
-void BounceLight (void)
-{
- unsigned i;
- Vector added;
- char name[64];
- qboolean bouncing = numbounce > 0;
-
- unsigned int uiPatchCount = g_Patches.Size();
- for (i=0 ; i<uiPatchCount; i++)
- {
- // totallight has a copy of the direct lighting. Move it to the emitted light and zero it out (to integrate bounces only)
- VectorCopy( g_Patches[i].totallight.light[0], emitlight[i] );
-
- // NOTE: This means that only the bounced light is integrated into totallight!
- VectorFill( g_Patches[i].totallight.light[0], 0 );
- }
-
-#if 0
- FileHandle_t dFp = g_pFileSystem->Open( "lightemit.txt", "w" );
-
- unsigned int uiPatchCount = g_Patches.Size();
- for (i=0 ; i<uiPatchCount; i++)
- {
- CmdLib_FPrintf( dFp, "Emit %d: %f %f %f\n", i, emitlight[i].x, emitlight[i].y, emitlight[i].z );
- }
-
- g_pFileSystem->Close( dFp );
-
- for (i=0; i<num_patches ; i++)
- {
- Vector total;
-
- VectorSubtract (g_Patches[i].maxs, g_Patches[i].mins, total);
- Msg("%4d %4d %4d %4d (%d) %.0f", i, g_Patches[i].parent, g_Patches[i].child1, g_Patches[i].child2, g_Patches[i].samples, g_Patches[i].area );
- Msg(" [%.0f %.0f %.0f]", total[0], total[1], total[2] );
- if (g_Patches[i].child1 != g_Patches.InvalidIndex() )
- {
- Vector tmp;
- VectorScale( g_Patches[i].totallight.light[0], g_Patches[i].area, tmp );
-
- VectorMA( tmp, -g_Patches[g_Patches[i].child1].area, g_Patches[g_Patches[i].child1].totallight.light[0], tmp );
- VectorMA( tmp, -g_Patches[g_Patches[i].child2].area, g_Patches[g_Patches[i].child2].totallight.light[0], tmp );
- // Msg("%.0f ", VectorLength( tmp ) );
- // Msg("%d ", g_Patches[i].samples - g_Patches[g_Patches[i].child1].samples - g_Patches[g_Patches[i].child2].samples );
- // Msg("%d ", g_Patches[i].samples );
- }
- Msg("\n");
- }
-#endif
-
- i = 0;
- while ( bouncing )
- {
- // transfer light from to the leaf patches from other patches via transfers
- // this moves shooter->emitlight to receiver->addlight
- unsigned int uiPatchCount = g_Patches.Size();
- RunThreadsOn (uiPatchCount, true, GatherLight);
- // move newly received light (addlight) to light to be sent out (emitlight)
- // start at children and pull light up to parents
- // light is always received to leaf patches
- CollectLight( added );
-
- qprintf ("\tBounce #%i added RGB(%.0f, %.0f, %.0f)\n", i+1, added[0], added[1], added[2] );
-
- if ( i+1 == numbounce || (added[0] < 1.0 && added[1] < 1.0 && added[2] < 1.0) )
- bouncing = false;
-
- i++;
- if ( g_bDumpPatches && !bouncing && i != 1)
- {
- sprintf (name, "bounce%i.txt", i);
- WriteWorld (name, 0);
- }
- }
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Counts the number of clusters in a map with no visibility
-// Output : int
-//-----------------------------------------------------------------------------
-int CountClusters( void )
-{
- int clusterCount = 0;
-
- for ( int i = 0; i < numleafs; i++ )
- {
- if ( dleafs[i].cluster > clusterCount )
- clusterCount = dleafs[i].cluster;
- }
-
- return clusterCount + 1;
-}
-
-
-/*
-=============
-RadWorld
-=============
-*/
-void RadWorld_Start()
-{
- unsigned i;
-
- if (luxeldensity < 1.0)
- {
- // Remember the old lightmap vectors.
- float oldLightmapVecs[MAX_MAP_TEXINFO][2][4];
- for (i = 0; i < texinfo.Count(); i++)
- {
- for( int j=0; j < 2; j++ )
- {
- for( int k=0; k < 3; k++ )
- {
- oldLightmapVecs[i][j][k] = texinfo[i].lightmapVecsLuxelsPerWorldUnits[j][k];
- }
- }
- }
-
- // rescale luxels to be no denser than "luxeldensity"
- for (i = 0; i < texinfo.Count(); i++)
- {
- texinfo_t *tx = &texinfo[i];
-
- for (int j = 0; j < 2; j++ )
- {
- Vector tmp( tx->lightmapVecsLuxelsPerWorldUnits[j][0], tx->lightmapVecsLuxelsPerWorldUnits[j][1], tx->lightmapVecsLuxelsPerWorldUnits[j][2] );
- float scale = VectorNormalize( tmp );
- // only rescale them if the current scale is "tighter" than the desired scale
- // FIXME: since this writes out to the BSP file every run, once it's set high it can't be reset
- // to a lower value.
- if (fabs( scale ) > luxeldensity)
- {
- if (scale < 0)
- {
- scale = -luxeldensity;
- }
- else
- {
- scale = luxeldensity;
- }
- VectorScale( tmp, scale, tmp );
- tx->lightmapVecsLuxelsPerWorldUnits[j][0] = tmp.x;
- tx->lightmapVecsLuxelsPerWorldUnits[j][1] = tmp.y;
- tx->lightmapVecsLuxelsPerWorldUnits[j][2] = tmp.z;
- }
- }
- }
-
- UpdateAllFaceLightmapExtents();
- }
-
- MakeParents (0, -1);
-
- BuildClusterTable();
-
- // turn each face into a single patch
- MakePatches ();
- PairEdges ();
-
- // store the vertex normals calculated in PairEdges
- // so that the can be written to the bsp file for
- // use in the engine
- SaveVertexNormals();
-
- // subdivide patches to a maximum dimension
- SubdividePatches ();
-
- // add displacement faces to cluster table
- AddDispsToClusterTable();
-
- // create directlights out of patches and lights
- CreateDirectLights ();
-
- // set up sky cameras
- ProcessSkyCameras();
-}
-
-
-// This function should fill in the indices into g_pFaces[] for the faces
-// with displacements that touch the specified leaf.
-void STUB_GetDisplacementsTouchingLeaf( int iLeaf, CUtlVector<int> &dispFaces )
-{
-}
-
-
-void BuildFacesVisibleToLights( bool bAllVisible )
-{
- g_FacesVisibleToLights.SetSize( numfaces/8 + 1 );
-
- if( bAllVisible )
- {
- memset( g_FacesVisibleToLights.Base(), 0xFF, g_FacesVisibleToLights.Count() );
- return;
- }
-
- // First merge all the light PVSes.
- CUtlVector<byte> aggregate;
- aggregate.SetSize( (dvis->numclusters/8) + 1 );
- memset( aggregate.Base(), 0, aggregate.Count() );
-
- int nDWords = aggregate.Count() / 4;
- int nBytes = aggregate.Count() - nDWords*4;
-
- for( directlight_t *dl = activelights; dl != NULL; dl = dl->next )
- {
- byte *pIn = dl->pvs;
- byte *pOut = aggregate.Base();
- for( int iDWord=0; iDWord < nDWords; iDWord++ )
- {
- *((unsigned long*)pOut) |= *((unsigned long*)pIn);
- pIn += 4;
- pOut += 4;
- }
-
- for( int iByte=0; iByte < nBytes; iByte++ )
- {
- *pOut |= *pIn;
- ++pOut;
- ++pIn;
- }
- }
-
-
- // Now tag any faces that are visible to this monster PVS.
- for( int iCluster=0; iCluster < dvis->numclusters; iCluster++ )
- {
- if( g_ClusterLeaves[iCluster].leafCount )
- {
- if( aggregate[iCluster>>3] & (1 << (iCluster & 7)) )
- {
- for ( int i = 0; i < g_ClusterLeaves[iCluster].leafCount; i++ )
- {
- int iLeaf = g_ClusterLeaves[iCluster].leafs[i];
-
- // Tag all the faces.
- int iFace;
- for( iFace=0; iFace < dleafs[iLeaf].numleaffaces; iFace++ )
- {
- int index = dleafs[iLeaf].firstleafface + iFace;
- index = dleaffaces[index];
-
- assert( index < numfaces );
- g_FacesVisibleToLights[index >> 3] |= (1 << (index & 7));
- }
-
- // Fill in STUB_GetDisplacementsTouchingLeaf when it's available
- // so displacements get relit.
- CUtlVector<int> dispFaces;
- STUB_GetDisplacementsTouchingLeaf( iLeaf, dispFaces );
- for( iFace=0; iFace < dispFaces.Count(); iFace++ )
- {
- int index = dispFaces[iFace];
- g_FacesVisibleToLights[index >> 3] |= (1 << (index & 7));
- }
- }
- }
- }
- }
-
- // For stats.. figure out how many faces it's going to touch.
- int nFacesToProcess = 0;
- for( int i=0; i < numfaces; i++ )
- {
- if( g_FacesVisibleToLights[i>>3] & (1 << (i & 7)) )
- ++nFacesToProcess;
- }
-}
-
-
-
-void MakeAllScales (void)
-{
- // determine visibility between patches
- BuildVisMatrix ();
-
- // release visibility matrix
- FreeVisMatrix ();
-
- Msg("transfers %d, max %d\n", total_transfer, max_transfer );
-
- qprintf ("transfer lists: %5.1f megs\n"
- , (float)total_transfer * sizeof(transfer_t) / (1024*1024));
-}
-
-
-// Helper function. This can be useful to visualize the world and faces and see which face
-// corresponds to which dface.
-#if 0
- #include "iscratchpad3d.h"
- void ScratchPad_DrawWorld()
- {
- IScratchPad3D *pPad = ScratchPad3D_Create();
- pPad->SetAutoFlush( false );
-
- for ( int i=0; i < numfaces; i++ )
- {
- dface_t *f = &g_pFaces[i];
-
- // Draw the face's outline, then put text for its face index on it too.
- CUtlVector<Vector> points;
- for ( int iEdge = 0; iEdge < f->numedges; iEdge++ )
- {
- int v;
- int se = dsurfedges[f->firstedge + iEdge];
- if ( se < 0 )
- v = dedges[-se].v[1];
- else
- v = dedges[se].v[0];
-
- dvertex_t *dv = &dvertexes[v];
- points.AddToTail( dv->point );
- }
-
- // Draw the outline.
- Vector vCenter( 0, 0, 0 );
- for ( iEdge=0; iEdge < points.Count(); iEdge++ )
- {
- pPad->DrawLine( CSPVert( points[iEdge] ), CSPVert( points[(iEdge+1)%points.Count()] ) );
- vCenter += points[iEdge];
- }
- vCenter /= points.Count();
-
- // Draw the text.
- char str[512];
- Q_snprintf( str, sizeof( str ), "%d", i );
-
- CTextParams params;
-
- params.m_bCentered = true;
- params.m_bOutline = true;
- params.m_flLetterWidth = 2;
- params.m_vColor.Init( 1, 0, 0 );
-
- VectorAngles( dplanes[f->planenum].normal, params.m_vAngles );
- params.m_bTwoSided = true;
-
- params.m_vPos = vCenter;
-
- pPad->DrawText( str, params );
- }
-
- pPad->Release();
- }
-#endif
-
-
-bool RadWorld_Go()
-{
- g_iCurFace = 0;
-
- InitMacroTexture( source );
-
- if( g_pIncremental )
- {
- g_pIncremental->PrepareForLighting();
-
- // Cull out faces that aren't visible to any of the lights that we're updating with.
- BuildFacesVisibleToLights( false );
- }
- else
- {
- // Mark all faces visible.. when not doing incremental lighting, it's highly
- // likely that all faces are going to be touched by at least one light so don't
- // waste time here.
- BuildFacesVisibleToLights( true );
- }
-
- // build initial facelights
- if (g_bUseMPI)
- {
- // RunThreadsOnIndividual (numfaces, true, BuildFacelights);
- RunMPIBuildFacelights();
- }
- else
- {
- RunThreadsOnIndividual (numfaces, true, BuildFacelights);
- }
-
- // Was the process interrupted?
- if( g_pIncremental && (g_iCurFace != numfaces) )
- return false;
-
- // Figure out the offset into lightmap data for each face.
- PrecompLightmapOffsets();
-
- // If we're doing incremental lighting, stop here.
- if( g_pIncremental )
- {
- g_pIncremental->Finalize();
- }
- else
- {
- // free up the direct lights now that we have facelights
- ExportDirectLightsToWorldLights();
-
- if ( g_bDumpPatches )
- {
- for( int iBump = 0; iBump < 4; ++iBump )
- {
- char szName[64];
- sprintf ( szName, "bounce0_%d.txt", iBump );
- WriteWorld( szName, iBump );
- }
- }
-
- if (numbounce > 0)
- {
- // allocate memory for emitlight/addlight
- emitlight.SetSize( g_Patches.Size() );
- memset( emitlight.Base(), 0, g_Patches.Size() * sizeof( Vector ) );
- addlight.SetSize( g_Patches.Size() );
- memset( addlight.Base(), 0, g_Patches.Size() * sizeof( bumplights_t ) );
-
- MakeAllScales ();
-
- // spread light around
- BounceLight ();
- }
-
- //
- // displacement surface luxel accumulation (make threaded!!!)
- //
- StaticDispMgr()->StartTimer( "Build Patch/Sample Hash Table(s)....." );
- StaticDispMgr()->InsertSamplesDataIntoHashTable();
- StaticDispMgr()->InsertPatchSampleDataIntoHashTable();
- StaticDispMgr()->EndTimer();
-
- // blend bounced light into direct light and save
- VMPI_SetCurrentStage( "FinalLightFace" );
- if ( !g_bUseMPI || g_bMPIMaster )
- RunThreadsOnIndividual (numfaces, true, FinalLightFace);
-
- // Distribute the lighting data to workers.
- VMPI_DistributeLightData();
-
- Msg("FinalLightFace Done\n"); fflush(stdout);
- }
-
- return true;
-}
-
-// declare the sample file pointer -- the whole debug print system should
-// be reworked at some point!!
-FileHandle_t pFileSamples[4][4];
-
-void LoadPhysicsDLL( void )
-{
- PhysicsDLLPath( "VPHYSICS.DLL" );
-}
-
-
-void InitDumpPatchesFiles()
-{
- for( int iStyle = 0; iStyle < 4; ++iStyle )
- {
- for ( int iBump = 0; iBump < 4; ++iBump )
- {
- char szFilename[MAX_PATH];
- sprintf( szFilename, "samples_style%d_bump%d.txt", iStyle, iBump );
- pFileSamples[iStyle][iBump] = g_pFileSystem->Open( szFilename, "w" );
- if( !pFileSamples[iStyle][iBump] )
- {
- Error( "Can't open %s for -dump.\n", szFilename );
- }
- }
- }
-}
-
-
-void VRAD_LoadBSP( char const *pFilename )
-{
- ThreadSetDefault ();
-
- g_flStartTime = Plat_FloatTime();
-
- if( g_bLowPriority )
- {
- SetLowPriority();
- }
-
- strcpy( level_name, source );
-
- // This must come after InitFileSystem because the file system pointer might change.
- if ( g_bDumpPatches )
- InitDumpPatchesFiles();
-
- // This part is just for VMPI. VMPI's file system needs the basedir in front of all filenames,
- // so we prepend qdir here.
- strcpy( source, ExpandPath( source ) );
-
- if ( !g_bUseMPI )
- {
- // Setup the logfile.
- char logFile[512];
- _snprintf( logFile, sizeof(logFile), "%s.log", source );
- SetSpewFunctionLogFile( logFile );
- }
-
- LoadPhysicsDLL();
-
- // Set the required global lights filename and try looking in qproject
- strcpy( global_lights, "lights.rad" );
- if ( !g_pFileSystem->FileExists( global_lights ) )
- {
- // Otherwise, try looking in the BIN directory from which we were run from
- Msg( "Could not find lights.rad in %s.\nTrying VRAD BIN directory instead...\n",
- global_lights );
- GetModuleFileName( NULL, global_lights, sizeof( global_lights ) );
- Q_ExtractFilePath( global_lights, global_lights, sizeof( global_lights ) );
- strcat( global_lights, "lights.rad" );
- }
-
- // Set the optional level specific lights filename
- strcpy( level_lights, source );
-
- Q_DefaultExtension( level_lights, ".rad", sizeof( level_lights ) );
- if ( !g_pFileSystem->FileExists( level_lights ) )
- *level_lights = 0;
-
- ReadLightFile(global_lights); // Required
- if ( *designer_lights ) ReadLightFile(designer_lights); // Command-line
- if ( *level_lights ) ReadLightFile(level_lights); // Optional & implied
-
- strcpy(incrementfile, source);
- Q_DefaultExtension(incrementfile, ".r0", sizeof(incrementfile));
- Q_DefaultExtension(source, ".bsp", sizeof( source ));
-
- GetPlatformMapPath( source, platformPath, 0, MAX_PATH );
-
- Msg( "Loading %s\n", platformPath );
- VMPI_SetCurrentStage( "LoadBSPFile" );
- LoadBSPFile (platformPath);
-
- // now, set whether or not static prop lighting is present
- if (g_bStaticPropLighting)
- g_LevelFlags |= g_bHDR? LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR : LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR;
- else
- {
- g_LevelFlags &= ~( LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR | LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR );
- }
-
- // now, we need to set our face ptr depending upon hdr, and if hdr, init it
- if (g_bHDR)
- {
- g_pFaces = dfaces_hdr;
- if (numfaces_hdr==0)
- {
- numfaces_hdr = numfaces;
- memcpy( dfaces_hdr, dfaces, numfaces*sizeof(dfaces[0]) );
- }
- }
- else
- {
- g_pFaces = dfaces;
- }
-
-
- ParseEntities ();
- ExtractBrushEntityShadowCasters();
-
- StaticPropMgr()->Init();
- StaticDispMgr()->Init();
-
- if (!visdatasize)
- {
- Msg("No vis information, direct lighting only.\n");
- numbounce = 0;
- ambient[0] = ambient[1] = ambient[2] = 0.1f;
- dvis->numclusters = CountClusters();
- }
-
- //
- // patches and referencing data (ensure capacity)
- //
- // TODO: change the maxes to the amount from the bsp!!
- //
-// g_Patches.EnsureCapacity( MAX_PATCHES );
-
- g_FacePatches.SetSize( MAX_MAP_FACES );
- faceParents.SetSize( MAX_MAP_FACES );
- clusterChildren.SetSize( MAX_MAP_CLUSTERS );
-
- int ndx;
- for ( ndx = 0; ndx < MAX_MAP_FACES; ndx++ )
- {
- g_FacePatches[ndx] = g_FacePatches.InvalidIndex();
- faceParents[ndx] = faceParents.InvalidIndex();
- }
-
- for ( ndx = 0; ndx < MAX_MAP_CLUSTERS; ndx++ )
- {
- clusterChildren[ndx] = clusterChildren.InvalidIndex();
- }
-
- // Setup ray tracer
- AddBrushesForRayTrace();
- StaticDispMgr()->AddPolysForRayTrace();
- StaticPropMgr()->AddPolysForRayTrace();
-
- // Dump raytracer for glview
- if ( g_bDumpRtEnv )
- WriteRTEnv("trace.txt");
-
- // Build acceleration structure
- printf ( "Setting up ray-trace acceleration structure... ");
- float start = Plat_FloatTime();
- g_RtEnv.SetupAccelerationStructure();
- float end = Plat_FloatTime();
- printf ( "Done (%.2f seconds)\n", end-start );
-
-#if 0 // To test only k-d build
- exit(0);
-#endif
-
- RadWorld_Start();
-
- // Setup incremental lighting.
- if( g_pIncremental )
- {
- if( !g_pIncremental->Init( source, incrementfile ) )
- {
- Error( "Unable to load incremental lighting file in %s.\n", incrementfile );
- return;
- }
- }
-}
-
-
-void VRAD_ComputeOtherLighting()
-{
- // Compute lighting for the bsp file
- if ( !g_bNoDetailLighting )
- {
- ComputeDetailPropLighting( THREADINDEX_MAIN );
- }
-
- ComputePerLeafAmbientLighting();
-
- // bake the static props high quality vertex lighting into the bsp
- if ( !do_fast && g_bStaticPropLighting )
- {
- StaticPropMgr()->ComputeLighting( THREADINDEX_MAIN );
- }
-}
-
-extern void CloseDispLuxels();
-
-void VRAD_Finish()
-{
- Msg( "Ready to Finish\n" );
- fflush( stdout );
-
- if ( verbose )
- {
- PrintBSPFileSizes();
- }
-
- Msg( "Writing %s\n", platformPath );
- VMPI_SetCurrentStage( "WriteBSPFile" );
- WriteBSPFile(platformPath);
-
- if ( g_bDumpPatches )
- {
- for ( int iStyle = 0; iStyle < 4; ++iStyle )
- {
- for ( int iBump = 0; iBump < 4; ++iBump )
- {
- g_pFileSystem->Close( pFileSamples[iStyle][iBump] );
- }
- }
- }
-
- CloseDispLuxels();
-
- StaticPropMgr()->Shutdown();
-
- double end = Plat_FloatTime();
-
- char str[512];
- GetHourMinuteSecondsString( (int)( end - g_flStartTime ), str, sizeof( str ) );
- Msg( "%s elapsed\n", str );
-
- ReleasePakFileLumps();
-}
-
-
-// Run startup code like initialize mathlib (called from main() and from the
-// WorldCraft interface into vrad).
-void VRAD_Init()
-{
- MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
- InstallAllocationFunctions();
- InstallSpewFunction();
-}
-
-
-int ParseCommandLine( int argc, char **argv, bool *onlydetail )
-{
- *onlydetail = false;
-
- // default to LDR
- SetHDRMode( false );
- int i;
- for( i=1 ; i<argc ; i++ )
- {
- if ( !Q_stricmp( argv[i], "-StaticPropLighting" ) )
- {
- g_bStaticPropLighting = true;
- }
- else if ( !stricmp( argv[i], "-StaticPropNormals" ) )
- {
- g_bShowStaticPropNormals = true;
- }
- else if ( !stricmp( argv[i], "-OnlyStaticProps" ) )
- {
- g_bOnlyStaticProps = true;
- }
- else if ( !Q_stricmp( argv[i], "-StaticPropPolys" ) )
- {
- g_bStaticPropPolys = true;
- }
- else if ( !Q_stricmp( argv[i], "-nossprops" ) )
- {
- g_bDisablePropSelfShadowing = true;
- }
- else if ( !Q_stricmp( argv[i], "-textureshadows" ) )
- {
- g_bTextureShadows = true;
- }
- else if ( !strcmp(argv[i], "-dump") )
- {
- g_bDumpPatches = true;
- }
- else if ( !Q_stricmp( argv[i], "-nodetaillight" ) )
- {
- g_bNoDetailLighting = true;
- }
- else if ( !Q_stricmp( argv[i], "-rederrors" ) )
- {
- bRed2Black = false;
- }
- else if ( !Q_stricmp( argv[i], "-dumpnormals" ) )
- {
- bDumpNormals = true;
- }
- else if ( !Q_stricmp( argv[i], "-dumptrace" ) )
- {
- g_bDumpRtEnv = true;
- }
- else if ( !Q_stricmp( argv[i], "-LargeDispSampleRadius" ) )
- {
- g_bLargeDispSampleRadius = true;
- }
- else if (!Q_stricmp(argv[i],"-bounce"))
- {
- if ( ++i < argc )
- {
- numbounce = atoi (argv[i]);
- if ( numbounce < 0 )
- {
- Warning("Error: expected non-negative value after '-bounce'\n" );
- return 1;
- }
- }
- else
- {
- Warning("Error: expected a value after '-bounce'\n" );
- return 1;
- }
- }
- else if (!Q_stricmp(argv[i],"-verbose") || !Q_stricmp(argv[i],"-v"))
- {
- verbose = true;
- }
- else if (!Q_stricmp(argv[i],"-threads"))
- {
- if ( ++i < argc )
- {
- numthreads = atoi (argv[i]);
- if ( numthreads <= 0 )
- {
- Warning("Error: expected positive value after '-threads'\n" );
- return 1;
- }
- }
- else
- {
- Warning("Error: expected a value after '-threads'\n" );
- return 1;
- }
- }
- else if ( !Q_stricmp(argv[i], "-lights" ) )
- {
- if ( ++i < argc && *argv[i] )
- {
- strcpy( designer_lights, argv[i] );
- }
- else
- {
- Warning("Error: expected a filepath after '-lights'\n" );
- return 1;
- }
- }
- else if (!Q_stricmp(argv[i],"-noextra"))
- {
- do_extra = false;
- }
- else if (!Q_stricmp(argv[i],"-debugextra"))
- {
- debug_extra = true;
- }
- else if ( !Q_stricmp(argv[i], "-fastambient") )
- {
- g_bFastAmbient = true;
- }
- else if (!Q_stricmp(argv[i],"-fast"))
- {
- do_fast = true;
- }
- else if (!Q_stricmp(argv[i],"-noskyboxrecurse"))
- {
- g_bNoSkyRecurse = true;
- }
- else if (!Q_stricmp(argv[i],"-final"))
- {
- g_flSkySampleScale = 16.0;
- }
- else if (!Q_stricmp(argv[i],"-extrasky"))
- {
- if ( ++i < argc && *argv[i] )
- {
- g_flSkySampleScale = atof( argv[i] );
- }
- else
- {
- Warning("Error: expected a scale factor after '-extrasky'\n" );
- return 1;
- }
- }
- else if (!Q_stricmp(argv[i],"-centersamples"))
- {
- do_centersamples = true;
- }
- else if (!Q_stricmp(argv[i],"-smooth"))
- {
- if ( ++i < argc )
- {
- smoothing_threshold = (float)cos(atof(argv[i])*(M_PI/180.0));
- }
- else
- {
- Warning("Error: expected an angle after '-smooth'\n" );
- return 1;
- }
- }
- else if (!Q_stricmp(argv[i],"-dlightmap"))
- {
- dlight_map = 1;
- }
- else if (!Q_stricmp(argv[i],"-luxeldensity"))
- {
- if ( ++i < argc )
- {
- luxeldensity = (float)atof (argv[i]);
- if (luxeldensity > 1.0)
- luxeldensity = 1.0 / luxeldensity;
- }
- else
- {
- Warning("Error: expected a value after '-luxeldensity'\n" );
- return 1;
- }
- }
- else if( !Q_stricmp( argv[i], "-low" ) )
- {
- g_bLowPriority = true;
- }
- else if( !Q_stricmp( argv[i], "-loghash" ) )
- {
- g_bLogHashData = true;
- }
- else if( !Q_stricmp( argv[i], "-onlydetail" ) )
- {
- *onlydetail = true;
- }
- else if (!Q_stricmp(argv[i],"-softsun"))
- {
- if ( ++i < argc )
- {
- g_SunAngularExtent=atof(argv[i]);
- g_SunAngularExtent=sin((M_PI/180.0)*g_SunAngularExtent);
- printf("sun extent=%f\n",g_SunAngularExtent);
- }
- else
- {
- Warning("Error: expected an angular extent value (0..180) '-softsun'\n" );
- return 1;
- }
- }
- else if ( !Q_stricmp( argv[i], "-maxdispsamplesize" ) )
- {
- if ( ++i < argc )
- {
- g_flMaxDispSampleSize = ( float )atof( argv[i] );
- }
- else
- {
- Warning( "Error: expected a sample size after '-maxdispsamplesize'\n" );
- return 1;
- }
- }
- else if ( stricmp( argv[i], "-StopOnExit" ) == 0 )
- {
- g_bStopOnExit = true;
- }
- else if ( stricmp( argv[i], "-steam" ) == 0 )
- {
- }
- else if ( stricmp( argv[i], "-allowdebug" ) == 0 )
- {
- // Don't need to do anything, just don't error out.
- }
- else if ( !Q_stricmp( argv[i], CMDLINEOPTION_NOVCONFIG ) )
- {
- }
- else if ( !Q_stricmp( argv[i], "-vproject" ) || !Q_stricmp( argv[i], "-game" ) )
- {
- ++i;
- }
- else if ( !Q_stricmp( argv[i], "-FullMinidumps" ) )
- {
- EnableFullMinidumps( true );
- }
- else if ( !Q_stricmp( argv[i], "-hdr" ) )
- {
- SetHDRMode( true );
- }
- else if ( !Q_stricmp( argv[i], "-ldr" ) )
- {
- SetHDRMode( false );
- }
- else if (!Q_stricmp(argv[i],"-maxchop"))
- {
- if ( ++i < argc )
- {
- maxchop = (float)atof (argv[i]);
- if ( maxchop < 1 )
- {
- Warning("Error: expected positive value after '-maxchop'\n" );
- return 1;
- }
- }
- else
- {
- Warning("Error: expected a value after '-maxchop'\n" );
- return 1;
- }
- }
- else if (!Q_stricmp(argv[i],"-chop"))
- {
- if ( ++i < argc )
- {
- minchop = (float)atof (argv[i]);
- if ( minchop < 1 )
- {
- Warning("Error: expected positive value after '-chop'\n" );
- return 1;
- }
- minchop = min( minchop, maxchop );
- }
- else
- {
- Warning("Error: expected a value after '-chop'\n" );
- return 1;
- }
- }
- else if ( !Q_stricmp( argv[i], "-dispchop" ) )
- {
- if ( ++i < argc )
- {
- dispchop = ( float )atof( argv[i] );
- if ( dispchop < 1.0f )
- {
- Warning( "Error: expected positive value after '-dipschop'\n" );
- return 1;
- }
- }
- else
- {
- Warning( "Error: expected a value after '-dispchop'\n" );
- return 1;
- }
- }
- else if ( !Q_stricmp( argv[i], "-disppatchradius" ) )
- {
- if ( ++i < argc )
- {
- g_MaxDispPatchRadius = ( float )atof( argv[i] );
- if ( g_MaxDispPatchRadius < 10.0f )
- {
- Warning( "Error: g_MaxDispPatchRadius < 10.0\n" );
- return 1;
- }
- }
- else
- {
- Warning( "Error: expected a value after '-disppatchradius'\n" );
- return 1;
- }
- }
-
-#if ALLOWDEBUGOPTIONS
- else if (!Q_stricmp(argv[i],"-scale"))
- {
- if ( ++i < argc )
- {
- lightscale = (float)atof (argv[i]);
- }
- else
- {
- Warning("Error: expected a value after '-scale'\n" );
- return 1;
- }
- }
- else if (!Q_stricmp(argv[i],"-ambient"))
- {
- if ( i+3 < argc )
- {
- ambient[0] = (float)atof (argv[++i]) * 128;
- ambient[1] = (float)atof (argv[++i]) * 128;
- ambient[2] = (float)atof (argv[++i]) * 128;
- }
- else
- {
- Warning("Error: expected three color values after '-ambient'\n" );
- return 1;
- }
- }
- else if (!Q_stricmp(argv[i],"-dlight"))
- {
- if ( ++i < argc )
- {
- dlight_threshold = (float)atof (argv[i]);
- }
- else
- {
- Warning("Error: expected a value after '-dlight'\n" );
- return 1;
- }
- }
- else if (!Q_stricmp(argv[i],"-sky"))
- {
- if ( ++i < argc )
- {
- indirect_sun = (float)atof (argv[i]);
- }
- else
- {
- Warning("Error: expected a value after '-sky'\n" );
- return 1;
- }
- }
- else if (!Q_stricmp(argv[i],"-notexscale"))
- {
- texscale = false;
- }
- else if (!Q_stricmp(argv[i],"-coring"))
- {
- if ( ++i < argc )
- {
- coring = (float)atof( argv[i] );
- }
- else
- {
- Warning("Error: expected a light threshold after '-coring'\n" );
- return 1;
- }
- }
-#endif
- // NOTE: the -mpi checks must come last here because they allow the previous argument
- // to be -mpi as well. If it game before something else like -game, then if the previous
- // argument was -mpi and the current argument was something valid like -game, it would skip it.
- else if ( !Q_strncasecmp( argv[i], "-mpi", 4 ) || !Q_strncasecmp( argv[i-1], "-mpi", 4 ) )
- {
- if ( stricmp( argv[i], "-mpi" ) == 0 )
- g_bUseMPI = true;
-
- // Any other args that start with -mpi are ok too.
- if ( i == argc - 1 && V_stricmp( argv[i], "-mpi_ListParams" ) != 0 )
- break;
- }
- else
- {
- break;
- }
- }
-
- return i;
-}
-
-
-void PrintCommandLine( int argc, char **argv )
-{
- Warning( "Command line: " );
- for ( int z=0; z < argc; z++ )
- {
- Warning( "\"%s\" ", argv[z] );
- }
- Warning( "\n\n" );
-}
-
-
-void PrintUsage( int argc, char **argv )
-{
- PrintCommandLine( argc, argv );
-
- Warning(
- "usage : vrad [options...] bspfile\n"
- "example: vrad c:\\hl2\\hl2\\maps\\test\n"
- "\n"
- "Common options:\n"
- "\n"
- " -v (or -verbose): Turn on verbose output (also shows more command\n"
- " -bounce # : Set max number of bounces (default: 100).\n"
- " -fast : Quick and dirty lighting.\n"
- " -fastambient : Per-leaf ambient sampling is lower quality to save compute time.\n"
- " -final : High quality processing. equivalent to -extrasky 16.\n"
- " -extrasky n : trace N times as many rays for indirect light and sky ambient.\n"
- " -low : Run as an idle-priority process.\n"
- " -mpi : Use VMPI to distribute computations.\n"
- " -rederror : Show errors in red.\n"
- "\n"
- " -vproject <directory> : Override the VPROJECT environment variable.\n"
- " -game <directory> : Same as -vproject.\n"
- "\n"
- "Other options:\n"
- " -novconfig : Don't bring up graphical UI on vproject errors.\n"
- " -dump : Write debugging .txt files.\n"
- " -dumpnormals : Write normals to debug files.\n"
- " -dumptrace : Write ray-tracing environment to debug files.\n"
- " -threads : Control the number of threads vbsp uses (defaults to the #\n"
- " or processors on your machine).\n"
- " -lights <file> : Load a lights file in addition to lights.rad and the\n"
- " level lights file.\n"
- " -noextra : Disable supersampling.\n"
- " -debugextra : Places debugging data in lightmaps to visualize\n"
- " supersampling.\n"
- " -smooth # : Set the threshold for smoothing groups, in degrees\n"
- " (default 45).\n"
- " -dlightmap : Force direct lighting into different lightmap than\n"
- " radiosity.\n"
- " -stoponexit : Wait for a keypress on exit.\n"
- " -mpi_pw <pw> : Use a password to choose a specific set of VMPI workers.\n"
- " -nodetaillight : Don't light detail props.\n"
- " -centersamples : Move sample centers.\n"
- " -luxeldensity # : Rescale all luxels by the specified amount (default: 1.0).\n"
- " The number specified must be less than 1.0 or it will be\n"
- " ignored.\n"
- " -loghash : Log the sample hash table to samplehash.txt.\n"
- " -onlydetail : Only light detail props and per-leaf lighting.\n"
- " -maxdispsamplesize #: Set max displacement sample size (default: 512).\n"
- " -softsun <n> : Treat the sun as an area light source of size <n> degrees."
- " Produces soft shadows.\n"
- " Recommended values are between 0 and 5. Default is 0.\n"
- " -FullMinidumps : Write large minidumps on crash.\n"
- " -chop : Smallest number of luxel widths for a bounce patch, used on edges\n"
- " -maxchop : Coarsest allowed number of luxel widths for a patch, used in face interiors\n"
- "\n"
- " -LargeDispSampleRadius: This can be used if there are splotches of bounced light\n"
- " on terrain. The compile will take longer, but it will gather\n"
- " light across a wider area.\n"
- " -StaticPropLighting : generate backed static prop vertex lighting\n"
- " -StaticPropPolys : Perform shadow tests of static props at polygon precision\n"
- " -OnlyStaticProps : Only perform direct static prop lighting (vrad debug option)\n"
- " -StaticPropNormals : when lighting static props, just show their normal vector\n"
- " -textureshadows : Allows texture alpha channels to block light - rays intersecting alpha surfaces will sample the texture\n"
- " -noskyboxrecurse : Turn off recursion into 3d skybox (skybox shadows on world)\n"
- " -nossprops : Globally disable self-shadowing on static props\n"
- "\n"
-#if 1 // Disabled for the initial SDK release with VMPI so we can get feedback from selected users.
- );
-#else
- " -mpi_ListParams : Show a list of VMPI parameters.\n"
- "\n"
- );
-
- // Show VMPI parameters?
- for ( int i=1; i < argc; i++ )
- {
- if ( V_stricmp( argv[i], "-mpi_ListParams" ) == 0 )
- {
- Warning( "VMPI-specific options:\n\n" );
-
- bool bIsSDKMode = VMPI_IsSDKMode();
- for ( int i=k_eVMPICmdLineParam_FirstParam+1; i < k_eVMPICmdLineParam_LastParam; i++ )
- {
- if ( (VMPI_GetParamFlags( (EVMPICmdLineParam)i ) & VMPI_PARAM_SDK_HIDDEN) && bIsSDKMode )
- continue;
-
- Warning( "[%s]\n", VMPI_GetParamString( (EVMPICmdLineParam)i ) );
- Warning( VMPI_GetParamHelpString( (EVMPICmdLineParam)i ) );
- Warning( "\n\n" );
- }
- break;
- }
- }
-#endif
-}
-
-
-int RunVRAD( int argc, char **argv )
-{
-#if defined(_MSC_VER) && ( _MSC_VER >= 1310 )
- Msg("Valve Software - vrad.exe SSE (" __DATE__ ")\n" );
-#else
- Msg("Valve Software - vrad.exe (" __DATE__ ")\n" );
-#endif
-
- Msg("\n Valve Radiosity Simulator \n");
-
- verbose = true; // Originally FALSE
-
- bool onlydetail;
- int i = ParseCommandLine( argc, argv, &onlydetail );
- if (i != argc - 1)
- {
- PrintUsage( argc, argv );
- DeleteCmdLine( argc, argv );
- CmdLib_Exit( 1 );
- }
-
- VRAD_LoadBSP( argv[i] );
-
- if ( (! onlydetail) && (! g_bOnlyStaticProps ) )
- {
- RadWorld_Go();
- }
-
- VRAD_ComputeOtherLighting();
-
- VRAD_Finish();
-
- VMPI_SetCurrentStage( "master done" );
-
- DeleteCmdLine( argc, argv );
- CmdLib_Cleanup();
- return 0;
-}
-
-
-int VRAD_Main(int argc, char **argv)
-{
- g_pFileSystem = NULL; // Safeguard against using it before it's properly initialized.
-
- VRAD_Init();
-
- // This must come first.
- VRAD_SetupMPI( argc, argv );
-
- // Initialize the filesystem, so additional commandline options can be loaded
- Q_StripExtension( argv[ argc - 1 ], source, sizeof( source ) );
- CmdLib_InitFileSystem( argv[ argc - 1 ] );
- Q_FileBase( source, source, sizeof( source ) );
-
-#if !defined( _DEBUG )
- if ( g_bUseMPI && !g_bMPIMaster )
- {
- SetupToolsMinidumpHandler( VMPI_ExceptionFilter );
- }
- else
-#endif
- {
- LoadCmdLineFromFile( argc, argv, source, "vrad" ); // Don't do this if we're a VMPI worker..
- SetupDefaultToolsMinidumpHandler();
- }
-
- return RunVRAD( argc, argv );
-}
-
-
-
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+// vrad.c
+
+#include "vrad.h"
+#include "physdll.h"
+#include "lightmap.h"
+#include "tier1/strtools.h"
+#include "vmpi.h"
+#include "macro_texture.h"
+#include "vmpi_tools_shared.h"
+#include "leaf_ambient_lighting.h"
+#include "tools_minidump.h"
+#include "loadcmdline.h"
+#include "byteswap.h"
+
+#define ALLOWDEBUGOPTIONS (0 || _DEBUG)
+
+static FileHandle_t pFpTrans = NULL;
+
+/*
+
+NOTES
+-----
+
+every surface must be divided into at least two patches each axis
+
+*/
+
+CUtlVector<CPatch> g_Patches;
+CUtlVector<int> g_FacePatches; // constains all patches, children first
+CUtlVector<int> faceParents; // contains only root patches, use next parent to iterate
+CUtlVector<int> clusterChildren;
+CUtlVector<Vector> emitlight;
+CUtlVector<bumplights_t> addlight;
+
+int num_sky_cameras;
+sky_camera_t sky_cameras[MAX_MAP_AREAS];
+int area_sky_cameras[MAX_MAP_AREAS];
+
+entity_t *face_entity[MAX_MAP_FACES];
+Vector face_offset[MAX_MAP_FACES]; // for rotating bmodels
+int fakeplanes;
+
+unsigned numbounce = 100; // 25; /* Originally this was 8 */
+
+float maxchop = 4; // coarsest allowed number of luxel widths for a patch
+float minchop = 4; // "-chop" tightest number of luxel widths for a patch, used on edges
+float dispchop = 8.0f; // number of luxel widths for a patch
+float g_MaxDispPatchRadius = 1500.0f; // Maximum radius allowed for displacement patches
+qboolean g_bDumpPatches;
+bool bDumpNormals = false;
+bool g_bDumpRtEnv = false;
+bool bRed2Black = true;
+bool g_bFastAmbient = false;
+bool g_bNoSkyRecurse = false;
+
+int junk;
+
+Vector ambient( 0, 0, 0 );
+
+float lightscale = 1.0;
+float dlight_threshold = 0.1; // was DIRECT_LIGHT constant
+
+char source[MAX_PATH] = "";
+char platformPath[MAX_PATH] = "";
+
+char level_name[MAX_PATH] = ""; // map filename, without extension or path info
+
+char global_lights[MAX_PATH] = "";
+char designer_lights[MAX_PATH] = "";
+char level_lights[MAX_PATH] = "";
+
+char vismatfile[_MAX_PATH] = "";
+char incrementfile[_MAX_PATH] = "";
+
+IIncremental *g_pIncremental = 0;
+bool g_bInterrupt = false; // Wsed with background lighting in WC. Tells VRAD
+ // to stop lighting.
+float g_SunAngularExtent=0.0;
+
+float g_flSkySampleScale = 1.0;
+
+bool g_bLargeDispSampleRadius = false;
+
+bool g_bOnlyStaticProps = false;
+bool g_bShowStaticPropNormals = false;
+
+
+float gamma_value = 0.5;
+float indirect_sun = 1.0;
+float reflectivityScale = 1.0;
+qboolean do_extra = true;
+bool debug_extra = false;
+qboolean do_fast = false;
+qboolean do_centersamples = false;
+int extrapasses = 4;
+float smoothing_threshold = 0.7071067; // cos(45.0*(M_PI/180))
+// Cosine of smoothing angle(in radians)
+float coring = 1.0; // Light threshold to force to blackness(minimizes lightmaps)
+qboolean texscale = true;
+int dlight_map = 0; // Setting to 1 forces direct lighting into different lightmap than radiosity
+
+float luxeldensity = 1.0;
+unsigned num_degenerate_faces;
+
+qboolean g_bLowPriority = false;
+qboolean g_bLogHashData = false;
+bool g_bNoDetailLighting = false;
+double g_flStartTime;
+bool g_bStaticPropLighting = false;
+bool g_bStaticPropPolys = false;
+bool g_bTextureShadows = false;
+bool g_bDisablePropSelfShadowing = false;
+
+
+CUtlVector<byte> g_FacesVisibleToLights;
+
+RayTracingEnvironment g_RtEnv;
+
+dface_t *g_pFaces=0;
+
+// this is a list of material names used on static props which shouldn't cast shadows. a
+// sequential search is used since we allow substring matches. its not time critical, and this
+// functionality is a stopgap until vrad starts reading .vmt files.
+CUtlVector<char const *> g_NonShadowCastingMaterialStrings;
+/*
+===================================================================
+
+MISC
+
+===================================================================
+*/
+
+
+int leafparents[MAX_MAP_LEAFS];
+int nodeparents[MAX_MAP_NODES];
+
+void MakeParents (int nodenum, int parent)
+{
+ int i, j;
+ dnode_t *node;
+
+ nodeparents[nodenum] = parent;
+ node = &dnodes[nodenum];
+
+ for (i=0 ; i<2 ; i++)
+ {
+ j = node->children[i];
+ if (j < 0)
+ leafparents[-j - 1] = nodenum;
+ else
+ MakeParents (j, nodenum);
+ }
+}
+
+
+/*
+===================================================================
+
+ TEXTURE LIGHT VALUES
+
+===================================================================
+*/
+
+typedef struct
+{
+ char name[256];
+ Vector value;
+ char *filename;
+} texlight_t;
+
+#define MAX_TEXLIGHTS 128
+
+texlight_t texlights[MAX_TEXLIGHTS];
+int num_texlights;
+
+/*
+============
+ReadLightFile
+============
+*/
+void ReadLightFile (char *filename)
+{
+ char buf[1024];
+ int file_texlights = 0;
+
+ FileHandle_t f = g_pFileSystem->Open( filename, "r" );
+ if (!f)
+ {
+ Warning("Warning: Couldn't open texlight file %s.\n", filename);
+ return;
+ }
+
+ Msg("[Reading texlights from '%s']\n", filename);
+ while ( CmdLib_FGets( buf, sizeof( buf ), f ) )
+ {
+ // check ldr/hdr
+ char * scan = buf;
+ if ( !strnicmp( "hdr:", scan, 4) )
+ {
+ scan += 4;
+ if ( ! g_bHDR )
+ {
+ continue;
+ }
+ }
+ if ( !strnicmp( "ldr:", scan, 4) )
+ {
+ scan += 4;
+ if ( g_bHDR )
+ {
+ continue;
+ }
+ }
+
+ scan += strspn( scan, " \t" );
+ char NoShadName[1024];
+ if ( sscanf(scan,"noshadow %s",NoShadName)==1)
+ {
+ char * dot = strchr( NoShadName, '.' );
+ if ( dot ) // if they specify .vmt, kill it
+ * dot = 0;
+ //printf("add %s as a non shadow casting material\n",NoShadName);
+ g_NonShadowCastingMaterialStrings.AddToTail( strdup( NoShadName ));
+ }
+ else if ( sscanf( scan, "forcetextureshadow %s", NoShadName ) == 1 )
+ {
+ //printf("add %s as a non shadow casting material\n",NoShadName);
+ ForceTextureShadowsOnModel( NoShadName );
+ }
+ else
+ {
+ char szTexlight[256];
+ Vector value;
+ if ( num_texlights == MAX_TEXLIGHTS )
+ Error ("Too many texlights, max = %d", MAX_TEXLIGHTS);
+
+ int argCnt = sscanf (scan, "%s ",szTexlight );
+
+ if( argCnt != 1 )
+ {
+ if ( strlen( scan ) > 4 )
+ Msg( "ignoring bad texlight '%s' in %s", scan, filename );
+ continue;
+ }
+
+ LightForString( scan + strlen( szTexlight ) + 1, value );
+
+ int j = 0;
+ for( j; j < num_texlights; j ++ )
+ {
+ if ( strcmp( texlights[j].name, szTexlight ) == 0 )
+ {
+ if ( strcmp( texlights[j].filename, filename ) == 0 )
+ {
+ Msg( "ERROR\a: Duplication of '%s' in file '%s'!\n",
+ texlights[j].name, texlights[j].filename );
+ }
+ else if ( texlights[j].value[0] != value[0]
+ || texlights[j].value[1] != value[1]
+ || texlights[j].value[2] != value[2] )
+ {
+ Warning( "Warning: Overriding '%s' from '%s' with '%s'!\n",
+ texlights[j].name, texlights[j].filename, filename );
+ }
+ else
+ {
+ Warning( "Warning: Redundant '%s' def in '%s' AND '%s'!\n",
+ texlights[j].name, texlights[j].filename, filename );
+ }
+ break;
+ }
+ }
+ strcpy( texlights[j].name, szTexlight );
+ VectorCopy( value, texlights[j].value );
+ texlights[j].filename = filename;
+ file_texlights ++;
+
+ num_texlights = max( num_texlights, j + 1 );
+ }
+ }
+ qprintf ( "[%i texlights parsed from '%s']\n\n", file_texlights, filename);
+ g_pFileSystem->Close( f );
+}
+
+
+/*
+============
+LightForTexture
+============
+*/
+void LightForTexture( const char *name, Vector& result )
+{
+ int i;
+
+ result[ 0 ] = result[ 1 ] = result[ 2 ] = 0;
+
+ char baseFilename[ MAX_PATH ];
+
+ if ( Q_strncmp( "maps/", name, 5 ) == 0 )
+ {
+ // this might be a patch texture for cubemaps. try to parse out the original filename.
+ if ( Q_strncmp( level_name, name + 5, Q_strlen( level_name ) ) == 0 )
+ {
+ const char *base = name + 5 + Q_strlen( level_name );
+ if ( *base == '/' )
+ {
+ ++base; // step past the path separator
+
+ // now we've gotten rid of the 'maps/level_name/' part, so we're left with
+ // 'originalName_%d_%d_%d'.
+ strcpy( baseFilename, base );
+ bool foundSeparators = true;
+ for ( int i=0; i<3; ++i )
+ {
+ char *underscore = Q_strrchr( baseFilename, '_' );
+ if ( underscore && *underscore )
+ {
+ *underscore = '\0';
+ }
+ else
+ {
+ foundSeparators = false;
+ }
+ }
+
+ if ( foundSeparators )
+ {
+ name = baseFilename;
+ }
+ }
+ }
+ }
+
+ for (i=0 ; i<num_texlights ; i++)
+ {
+ if (!Q_strcasecmp (name, texlights[i].name))
+ {
+ VectorCopy( texlights[i].value, result );
+ return;
+ }
+ }
+}
+
+/*
+=======================================================================
+
+MAKE FACES
+
+=======================================================================
+*/
+
+/*
+=============
+WindingFromFace
+=============
+*/
+winding_t *WindingFromFace (dface_t *f, Vector& origin )
+{
+ int i;
+ int se;
+ dvertex_t *dv;
+ int v;
+ winding_t *w;
+
+ w = AllocWinding (f->numedges);
+ w->numpoints = f->numedges;
+
+ for (i=0 ; i<f->numedges ; i++)
+ {
+ se = dsurfedges[f->firstedge + i];
+ if (se < 0)
+ v = dedges[-se].v[1];
+ else
+ v = dedges[se].v[0];
+
+ dv = &dvertexes[v];
+ VectorAdd (dv->point, origin, w->p[i]);
+ }
+
+ RemoveColinearPoints (w);
+
+ return w;
+}
+
+/*
+=============
+BaseLightForFace
+=============
+*/
+void BaseLightForFace( dface_t *f, Vector& light, float *parea, Vector& reflectivity )
+{
+ texinfo_t *tx;
+ dtexdata_t *texdata;
+
+ //
+ // check for light emited by texture
+ //
+ tx = &texinfo[f->texinfo];
+ texdata = &dtexdata[tx->texdata];
+
+ LightForTexture (TexDataStringTable_GetString( texdata->nameStringTableID ), light);
+
+
+ *parea = texdata->height * texdata->width;
+
+ VectorScale( texdata->reflectivity, reflectivityScale, reflectivity );
+
+ // always keep this less than 1 or the solution will not converge
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( reflectivity[i] > 0.99 )
+ reflectivity[i] = 0.99;
+ }
+}
+
+qboolean IsSky (dface_t *f)
+{
+ texinfo_t *tx;
+
+ tx = &texinfo[f->texinfo];
+ if (tx->flags & SURF_SKY)
+ return true;
+ return false;
+}
+
+#ifdef STATIC_FOG
+/*=============
+IsFog
+=============*/
+qboolean IsFog( dface_t *f )
+{
+ texinfo_t *tx;
+
+ tx = &texinfo[f->texinfo];
+
+ // % denotes a fog texture
+ if( tx->texture[0] == '%' )
+ return true;
+
+ return false;
+}
+#endif
+
+
+void ProcessSkyCameras()
+{
+ int i;
+ num_sky_cameras = 0;
+ for (i = 0; i < numareas; ++i)
+ {
+ area_sky_cameras[i] = -1;
+ }
+
+ for (i = 0; i < num_entities; ++i)
+ {
+ entity_t *e = &entities[i];
+ const char *name = ValueForKey (e, "classname");
+ if (stricmp (name, "sky_camera"))
+ continue;
+
+ Vector origin;
+ GetVectorForKey( e, "origin", origin );
+ int node = PointLeafnum( origin );
+ int area = -1;
+ if (node >= 0 && node < numleafs) area = dleafs[node].area;
+ float scale = FloatForKey( e, "scale" );
+
+ if (scale > 0.0f)
+ {
+ sky_cameras[num_sky_cameras].origin = origin;
+ sky_cameras[num_sky_cameras].sky_to_world = scale;
+ sky_cameras[num_sky_cameras].world_to_sky = 1.0f / scale;
+ sky_cameras[num_sky_cameras].area = area;
+
+ if (area >= 0 && area < numareas)
+ {
+ area_sky_cameras[area] = num_sky_cameras;
+ }
+
+ ++num_sky_cameras;
+ }
+ }
+
+}
+
+
+/*
+=============
+MakePatchForFace
+=============
+*/
+float totalarea;
+void MakePatchForFace (int fn, winding_t *w)
+{
+ dface_t *f = g_pFaces + fn;
+ float area;
+ CPatch *patch;
+ Vector centroid(0,0,0);
+ int i, j;
+ texinfo_t *tx;
+
+ // get texture info
+ tx = &texinfo[f->texinfo];
+
+ // No patches at all for fog!
+#ifdef STATIC_FOG
+ if ( IsFog( f ) )
+ return;
+#endif
+
+ // the sky needs patches or the form factors don't work out correctly
+ // if (IsSky( f ) )
+ // return;
+
+ area = WindingArea (w);
+ if (area <= 0)
+ {
+ num_degenerate_faces++;
+ // Msg("degenerate face\n");
+ return;
+ }
+
+ totalarea += area;
+
+ // get a patch
+ int ndxPatch = g_Patches.AddToTail();
+ patch = &g_Patches[ndxPatch];
+ memset( patch, 0, sizeof( CPatch ) );
+ patch->ndxNext = g_Patches.InvalidIndex();
+ patch->ndxNextParent = g_Patches.InvalidIndex();
+ patch->ndxNextClusterChild = g_Patches.InvalidIndex();
+ patch->child1 = g_Patches.InvalidIndex();
+ patch->child2 = g_Patches.InvalidIndex();
+ patch->parent = g_Patches.InvalidIndex();
+ patch->needsBumpmap = tx->flags & SURF_BUMPLIGHT ? true : false;
+
+ // link and save patch data
+ patch->ndxNext = g_FacePatches.Element( fn );
+ g_FacePatches[fn] = ndxPatch;
+// patch->next = face_g_Patches[fn];
+// face_g_Patches[fn] = patch;
+
+ // compute a separate scale for chop - since the patch "scale" is the texture scale
+ // we want textures with higher resolution lighting to be chopped up more
+ float chopscale[2];
+ chopscale[0] = chopscale[1] = 16.0f;
+ if ( texscale )
+ {
+ // Compute the texture "scale" in s,t
+ for( i=0; i<2; i++ )
+ {
+ patch->scale[i] = 0.0f;
+ chopscale[i] = 0.0f;
+ for( j=0; j<3; j++ )
+ {
+ patch->scale[i] +=
+ tx->textureVecsTexelsPerWorldUnits[i][j] *
+ tx->textureVecsTexelsPerWorldUnits[i][j];
+ chopscale[i] +=
+ tx->lightmapVecsLuxelsPerWorldUnits[i][j] *
+ tx->lightmapVecsLuxelsPerWorldUnits[i][j];
+ }
+ patch->scale[i] = sqrt( patch->scale[i] );
+ chopscale[i] = sqrt( chopscale[i] );
+ }
+ }
+ else
+ {
+ patch->scale[0] = patch->scale[1] = 1.0f;
+ }
+
+ patch->area = area;
+
+ patch->sky = IsSky( f );
+
+ // chop scaled up lightmaps coarser
+ patch->luxscale = ((chopscale[0]+chopscale[1])/2);
+ patch->chop = maxchop;
+
+
+#ifdef STATIC_FOG
+ patch->fog = FALSE;
+#endif
+
+ patch->winding = w;
+
+ patch->plane = &dplanes[f->planenum];
+
+ // make a new plane to adjust for origined bmodels
+ if (face_offset[fn][0] || face_offset[fn][1] || face_offset[fn][2] )
+ {
+ dplane_t *pl;
+
+ // origin offset faces must create new planes
+ if (numplanes + fakeplanes >= MAX_MAP_PLANES)
+ {
+ Error ("numplanes + fakeplanes >= MAX_MAP_PLANES");
+ }
+ pl = &dplanes[numplanes + fakeplanes];
+ fakeplanes++;
+
+ *pl = *(patch->plane);
+ pl->dist += DotProduct (face_offset[fn], pl->normal);
+ patch->plane = pl;
+ }
+
+ patch->faceNumber = fn;
+ WindingCenter (w, patch->origin);
+
+ // Save "center" for generating the face normals later.
+ VectorSubtract( patch->origin, face_offset[fn], face_centroids[fn] );
+
+ VectorCopy( patch->plane->normal, patch->normal );
+
+ WindingBounds (w, patch->face_mins, patch->face_maxs);
+ VectorCopy( patch->face_mins, patch->mins );
+ VectorCopy( patch->face_maxs, patch->maxs );
+
+ BaseLightForFace( f, patch->baselight, &patch->basearea, patch->reflectivity );
+
+ // Chop all texlights very fine.
+ if ( !VectorCompare( patch->baselight, vec3_origin ) )
+ {
+ // patch->chop = do_extra ? maxchop / 2 : maxchop;
+ tx->flags |= SURF_LIGHT;
+ }
+
+ // get rid of do extra functionality on displacement surfaces
+ if( ValidDispFace( f ) )
+ {
+ patch->chop = maxchop;
+ }
+
+ // FIXME: If we wanted to add a dependency from vrad to the material system,
+ // we could do this. It would add a bunch of file accesses, though:
+
+ /*
+ // Check for a material var which would override the patch chop
+ bool bFound;
+ const char *pMaterialName = TexDataStringTable_GetString( dtexdata[ tx->texdata ].nameStringTableID );
+ MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, &bFound, false );
+ if ( bFound )
+ {
+ const char *pChopValue = GetMaterialVar( hMaterial, "%chop" );
+ if ( pChopValue )
+ {
+ float flChopValue;
+ if ( sscanf( pChopValue, "%f", &flChopValue ) > 0 )
+ {
+ patch->chop = flChopValue;
+ }
+ }
+ }
+ */
+}
+
+
+entity_t *EntityForModel (int modnum)
+{
+ int i;
+ char *s;
+ char name[16];
+
+ sprintf (name, "*%i", modnum);
+ // search the entities for one using modnum
+ for (i=0 ; i<num_entities ; i++)
+ {
+ s = ValueForKey (&entities[i], "model");
+ if (!strcmp (s, name))
+ return &entities[i];
+ }
+
+ return &entities[0];
+}
+
+/*
+=============
+MakePatches
+=============
+*/
+void MakePatches (void)
+{
+ int i, j;
+ dface_t *f;
+ int fn;
+ winding_t *w;
+ dmodel_t *mod;
+ Vector origin;
+ entity_t *ent;
+
+ ParseEntities ();
+ qprintf ("%i faces\n", numfaces);
+
+ for (i=0 ; i<nummodels ; i++)
+ {
+ mod = dmodels+i;
+ ent = EntityForModel (i);
+ VectorCopy (vec3_origin, origin);
+
+ // bmodels with origin brushes need to be offset into their
+ // in-use position
+ GetVectorForKey (ent, "origin", origin);
+
+ for (j=0 ; j<mod->numfaces ; j++)
+ {
+ fn = mod->firstface + j;
+ face_entity[fn] = ent;
+ VectorCopy (origin, face_offset[fn]);
+ f = &g_pFaces[fn];
+ if( f->dispinfo == -1 )
+ {
+ w = WindingFromFace (f, origin );
+ MakePatchForFace( fn, w );
+ }
+ }
+ }
+
+ if (num_degenerate_faces > 0)
+ {
+ qprintf("%d degenerate faces\n", num_degenerate_faces );
+ }
+
+ qprintf ("%i square feet [%.2f square inches]\n", (int)(totalarea/144), totalarea );
+
+ // make the displacement surface patches
+ StaticDispMgr()->MakePatches();
+}
+
+/*
+=======================================================================
+
+SUBDIVIDE
+
+=======================================================================
+*/
+
+
+//-----------------------------------------------------------------------------
+// Purpose: does this surface take/emit light
+//-----------------------------------------------------------------------------
+bool PreventSubdivision( CPatch *patch )
+{
+ dface_t *f = g_pFaces + patch->faceNumber;
+ texinfo_t *tx = &texinfo[f->texinfo];
+
+ if (tx->flags & SURF_NOCHOP)
+ return true;
+
+ if (tx->flags & SURF_NOLIGHT && !(tx->flags & SURF_LIGHT))
+ return true;
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: subdivide the "parent" patch
+//-----------------------------------------------------------------------------
+int CreateChildPatch( int nParentIndex, winding_t *pWinding, float flArea, const Vector &vecCenter )
+{
+ int nChildIndex = g_Patches.AddToTail();
+
+ CPatch *child = &g_Patches[nChildIndex];
+ CPatch *parent = &g_Patches[nParentIndex];
+
+ // copy all elements of parent patch to children
+ *child = *parent;
+
+ // Set up links
+ child->ndxNext = g_Patches.InvalidIndex();
+ child->ndxNextParent = g_Patches.InvalidIndex();
+ child->ndxNextClusterChild = g_Patches.InvalidIndex();
+ child->child1 = g_Patches.InvalidIndex();
+ child->child2 = g_Patches.InvalidIndex();
+ child->parent = nParentIndex;
+ child->m_IterationKey = 0;
+
+ child->winding = pWinding;
+ child->area = flArea;
+
+ VectorCopy( vecCenter, child->origin );
+ if ( ValidDispFace( g_pFaces + child->faceNumber ) )
+ {
+ // shouldn't get here anymore!!
+ Msg( "SubdividePatch: Error - Should not be here!\n" );
+ StaticDispMgr()->GetDispSurfNormal( child->faceNumber, child->origin, child->normal, true );
+ }
+ else
+ {
+ GetPhongNormal( child->faceNumber, child->origin, child->normal );
+ }
+
+ child->planeDist = child->plane->dist;
+ WindingBounds(child->winding, child->mins, child->maxs);
+
+ if ( !VectorCompare( child->baselight, vec3_origin ) )
+ {
+ // don't check edges on surf lights
+ return nChildIndex;
+ }
+
+ // Subdivide patch towards minchop if on the edge of the face
+ Vector total;
+ VectorSubtract( child->maxs, child->mins, total );
+ VectorScale( total, child->luxscale, total );
+ if ( child->chop > minchop && (total[0] < child->chop) && (total[1] < child->chop) && (total[2] < child->chop) )
+ {
+ for ( int i=0; i<3; ++i )
+ {
+ if ( (child->face_maxs[i] == child->maxs[i] || child->face_mins[i] == child->mins[i] )
+ && total[i] > minchop )
+ {
+ child->chop = max( minchop, child->chop / 2 );
+ break;
+ }
+ }
+ }
+
+ return nChildIndex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: subdivide the "parent" patch
+//-----------------------------------------------------------------------------
+void SubdividePatch( int ndxPatch )
+{
+ winding_t *w, *o1, *o2;
+ Vector total;
+ Vector split;
+ vec_t dist;
+ vec_t widest = -1;
+ int i, widest_axis = -1;
+ bool bSubdivide = false;
+
+ // get the current patch
+ CPatch *patch = &g_Patches.Element( ndxPatch );
+ if ( !patch )
+ return;
+
+ // never subdivide sky patches
+ if ( patch->sky )
+ return;
+
+ // get the patch winding
+ w = patch->winding;
+
+ // subdivide along the widest axis
+ VectorSubtract (patch->maxs, patch->mins, total);
+ VectorScale( total, patch->luxscale, total );
+ for (i=0 ; i<3 ; i++)
+ {
+ if ( total[i] > widest )
+ {
+ widest_axis = i;
+ widest = total[i];
+ }
+
+ if ( (total[i] >= patch->chop) && (total[i] >= minchop) )
+ {
+ bSubdivide = true;
+ }
+ }
+
+ if ((!bSubdivide) && widest_axis != -1)
+ {
+ // make more square
+ if (total[widest_axis] > total[(widest_axis + 1) % 3] * 2 && total[widest_axis] > total[(widest_axis + 2) % 3] * 2)
+ {
+ if (patch->chop > minchop)
+ {
+ bSubdivide = true;
+ patch->chop = max( minchop, patch->chop / 2 );
+ }
+ }
+ }
+
+ if ( !bSubdivide )
+ return;
+
+ // split the winding
+ VectorCopy (vec3_origin, split);
+ split[widest_axis] = 1;
+ dist = (patch->mins[widest_axis] + patch->maxs[widest_axis])*0.5f;
+ ClipWindingEpsilon (w, split, dist, ON_EPSILON, &o1, &o2);
+
+ // calculate the area of the patches to see if they are "significant"
+ Vector center1, center2;
+ float area1 = WindingAreaAndBalancePoint( o1, center1 );
+ float area2 = WindingAreaAndBalancePoint( o2, center2 );
+
+ if( area1 == 0 || area2 == 0 )
+ {
+ Msg( "zero area child patch\n" );
+ return;
+ }
+
+ // create new child patches
+ int ndxChild1Patch = CreateChildPatch( ndxPatch, o1, area1, center1 );
+ int ndxChild2Patch = CreateChildPatch( ndxPatch, o2, area2, center2 );
+
+ // FIXME: This could go into CreateChildPatch if child1, child2 were stored in the patch as child[0], child[1]
+ patch = &g_Patches.Element( ndxPatch );
+ patch->child1 = ndxChild1Patch;
+ patch->child2 = ndxChild2Patch;
+
+ SubdividePatch( ndxChild1Patch );
+ SubdividePatch( ndxChild2Patch );
+}
+
+
+/*
+=============
+SubdividePatches
+=============
+*/
+void SubdividePatches (void)
+{
+ unsigned i, num;
+
+ if (numbounce == 0)
+ return;
+
+ unsigned int uiPatchCount = g_Patches.Size();
+ qprintf ("%i patches before subdivision\n", uiPatchCount);
+
+ for (i = 0; i < uiPatchCount; i++)
+ {
+ CPatch *pCur = &g_Patches.Element( i );
+ pCur->planeDist = pCur->plane->dist;
+
+ pCur->ndxNextParent = faceParents.Element( pCur->faceNumber );
+ faceParents[pCur->faceNumber] = pCur - g_Patches.Base();
+ }
+
+ for (i=0 ; i< uiPatchCount; i++)
+ {
+ CPatch *patch = &g_Patches.Element( i );
+ patch->parent = -1;
+ if ( PreventSubdivision(patch) )
+ continue;
+
+ if (!do_fast)
+ {
+ if( g_pFaces[patch->faceNumber].dispinfo == -1 )
+ {
+ SubdividePatch( i );
+ }
+ else
+ {
+ StaticDispMgr()->SubdividePatch( i );
+ }
+ }
+ }
+
+ // fixup next pointers
+ for (i = 0; i < (unsigned)numfaces; i++)
+ {
+ g_FacePatches[i] = g_FacePatches.InvalidIndex();
+ }
+
+ uiPatchCount = g_Patches.Size();
+ for (i = 0; i < uiPatchCount; i++)
+ {
+ CPatch *pCur = &g_Patches.Element( i );
+ pCur->ndxNext = g_FacePatches.Element( pCur->faceNumber );
+ g_FacePatches[pCur->faceNumber] = pCur - g_Patches.Base();
+
+#if 0
+ CPatch *prev;
+ prev = face_g_Patches[g_Patches[i].faceNumber];
+ g_Patches[i].next = prev;
+ face_g_Patches[g_Patches[i].faceNumber] = &g_Patches[i];
+#endif
+ }
+
+ // Cache off the leaf number:
+ // We have to do this after subdivision because some patches span leaves.
+ // (only the faces for model #0 are split by it's BSP which is what governs the PVS, and the leaves we're interested in)
+ // Sub models (1-255) are only split for the BSP that their model forms.
+ // When those patches are subdivided their origins can end up in a different leaf.
+ // The engine will split (clip) those faces at run time to the world BSP because the models
+ // are dynamic and can be moved. In the software renderer, they must be split exactly in order
+ // to sort per polygon.
+ for ( i = 0; i < uiPatchCount; i++ )
+ {
+ g_Patches[i].clusterNumber = ClusterFromPoint( g_Patches[i].origin );
+
+ //
+ // test for point in solid space (can happen with detail and displacement surfaces)
+ //
+ if( g_Patches[i].clusterNumber == -1 )
+ {
+ for( int j = 0; j < g_Patches[i].winding->numpoints; j++ )
+ {
+ int clusterNumber = ClusterFromPoint( g_Patches[i].winding->p[j] );
+ if( clusterNumber != -1 )
+ {
+ g_Patches[i].clusterNumber = clusterNumber;
+ break;
+ }
+ }
+ }
+ }
+
+ // build the list of patches that need to be lit
+ for ( num = 0; num < uiPatchCount; num++ )
+ {
+ // do them in reverse order
+ i = uiPatchCount - num - 1;
+
+ // skip patches with children
+ CPatch *pCur = &g_Patches.Element( i );
+ if( pCur->child1 == g_Patches.InvalidIndex() )
+ {
+ if( pCur->clusterNumber != - 1 )
+ {
+ pCur->ndxNextClusterChild = clusterChildren.Element( pCur->clusterNumber );
+ clusterChildren[pCur->clusterNumber] = pCur - g_Patches.Base();
+ }
+ }
+
+#if 0
+ if (g_Patches[i].child1 == g_Patches.InvalidIndex() )
+ {
+ if( g_Patches[i].clusterNumber != -1 )
+ {
+ g_Patches[i].nextclusterchild = cluster_children[g_Patches[i].clusterNumber];
+ cluster_children[g_Patches[i].clusterNumber] = &g_Patches[i];
+ }
+ }
+#endif
+ }
+
+ qprintf ("%i patches after subdivision\n", uiPatchCount);
+}
+
+
+//=====================================================================
+
+/*
+=============
+MakeScales
+
+ This is the primary time sink.
+ It can be run multi threaded.
+=============
+*/
+int total_transfer;
+int max_transfer;
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Computes the form factor from a polygon patch to a differential patch
+// using formula 81 of Philip Dutre's Global Illumination Compendium,
+// [email protected], http://www.graphics.cornell.edu/~phil/GI/
+//-----------------------------------------------------------------------------
+float FormFactorPolyToDiff ( CPatch *pPolygon, CPatch* pDifferential )
+{
+ winding_t *pWinding = pPolygon->winding;
+
+ float flFormFactor = 0.0f;
+
+ for ( int iPoint = 0; iPoint < pWinding->numpoints; iPoint++ )
+ {
+ int iNextPoint = ( iPoint < pWinding->numpoints - 1 ) ? iPoint + 1 : 0;
+
+ Vector vGammaVector, vVector1, vVector2;
+ VectorSubtract( pWinding->p[ iPoint ], pDifferential->origin, vVector1 );
+ VectorSubtract( pWinding->p[ iNextPoint ], pDifferential->origin, vVector2 );
+ VectorNormalize( vVector1 );
+ VectorNormalize( vVector2 );
+ CrossProduct( vVector1, vVector2, vGammaVector );
+ float flSinAlpha = VectorNormalize( vGammaVector );
+ if (flSinAlpha < -1.0f || flSinAlpha > 1.0f)
+ return 0.0f;
+ vGammaVector *= asin( flSinAlpha );
+
+ flFormFactor += DotProduct( vGammaVector, pDifferential->normal );
+ }
+
+ flFormFactor *= ( 0.5f / pPolygon->area ); // divide by pi later, multiply by area later
+
+ return flFormFactor;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Computes the form factor from a differential element to a differential
+// element. This is okay when the distance between patches is 5 times
+// greater than patch size. Lecture slides by Pat Hanrahan,
+// http://graphics.stanford.edu/courses/cs348b-00/lectures/lecture17/radiosity.2.pdf
+//-----------------------------------------------------------------------------
+float FormFactorDiffToDiff ( CPatch *pDiff1, CPatch* pDiff2 )
+{
+ Vector vDelta;
+ VectorSubtract( pDiff1->origin, pDiff2->origin, vDelta );
+ float flLength = VectorNormalize( vDelta );
+
+ return -DotProduct( vDelta, pDiff1->normal ) * DotProduct( vDelta, pDiff2->normal ) / ( flLength * flLength );
+}
+
+
+
+void MakeTransfer( int ndxPatch1, int ndxPatch2, transfer_t *all_transfers )
+//void MakeTransfer (CPatch *patch, CPatch *patch2, transfer_t *all_transfers )
+{
+ Vector delta;
+ vec_t scale;
+ float trans;
+ transfer_t *transfer;
+
+ //
+ // get patches
+ //
+ if( ndxPatch1 == g_Patches.InvalidIndex() || ndxPatch2 == g_Patches.InvalidIndex() )
+ return;
+
+ CPatch *pPatch1 = &g_Patches.Element( ndxPatch1 );
+ CPatch *pPatch2 = &g_Patches.Element( ndxPatch2 );
+
+ if (IsSky( &g_pFaces[ pPatch2->faceNumber ] ) )
+ return;
+
+ // overflow check!
+ if ( pPatch1->numtransfers >= MAX_PATCHES)
+ {
+ return;
+ }
+
+ // hack for patch areas that area <= 0 (degenerate)
+ if ( pPatch2->area <= 0)
+ {
+ return;
+ }
+
+ transfer = &all_transfers[pPatch1->numtransfers];
+
+ scale = FormFactorDiffToDiff( pPatch2, pPatch1 );
+
+ // patch normals may be > 90 due to smoothing groups
+ if (scale <= 0)
+ {
+ //Msg("scale <= 0\n");
+ return;
+ }
+
+ // Test 5 times rule
+ Vector vDelta;
+ VectorSubtract( pPatch1->origin, pPatch2->origin, vDelta );
+ float flThreshold = ( M_PI * 0.04 ) * DotProduct( vDelta, vDelta );
+
+ if (flThreshold < pPatch2->area)
+ {
+ scale = FormFactorPolyToDiff( pPatch2, pPatch1 );
+ if (scale <= 0.0)
+ return;
+ }
+
+ trans = (pPatch2->area*scale);
+
+ if (trans <= TRANSFER_EPSILON)
+ {
+ return;
+ }
+
+ transfer->patch = pPatch2 - g_Patches.Base();
+
+ // FIXME: why is this not trans?
+ transfer->transfer = trans;
+
+#if 0
+ // DEBUG! Dump patches and transfer connection for displacements. This creates a lot of data, so only
+ // use it when you really want it - that is why it is #if-ed out.
+ if ( g_bDumpPatches )
+ {
+ if ( !pFpTrans )
+ {
+ pFpTrans = g_pFileSystem->Open( "trans.txt", "w" );
+ }
+ Vector light = pPatch1->totallight.light[0] + pPatch1->directlight;
+ WriteWinding( pFpTrans, pPatch1->winding, light );
+ light = pPatch2->totallight.light[0] + pPatch2->directlight;
+ WriteWinding( pFpTrans, pPatch2->winding, light );
+ WriteLine( pFpTrans, pPatch1->origin, pPatch2->origin, Vector( 255, 0, 255 ) );
+ }
+#endif
+
+ pPatch1->numtransfers++;
+}
+
+
+void MakeScales ( int ndxPatch, transfer_t *all_transfers )
+{
+ int j;
+ float total;
+ transfer_t *t, *t2;
+ total = 0;
+
+ if( ndxPatch == g_Patches.InvalidIndex() )
+ return;
+ CPatch *patch = &g_Patches.Element( ndxPatch );
+
+ // copy the transfers out
+ if (patch->numtransfers)
+ {
+ if (patch->numtransfers > max_transfer)
+ {
+ max_transfer = patch->numtransfers;
+ }
+
+
+ patch->transfers = ( transfer_t* )calloc (1, patch->numtransfers * sizeof(transfer_t));
+ if (!patch->transfers)
+ Error ("Memory allocation failure");
+
+ // get total transfer energy
+ t2 = all_transfers;
+
+ // overflow check!
+ for (j=0 ; j<patch->numtransfers ; j++, t2++)
+ {
+ total += t2->transfer;
+ }
+
+ // the total transfer should be PI, but we need to correct errors due to overlaping surfaces
+ if (total > M_PI)
+ total = 1.0f/total;
+ else
+ total = 1.0f/M_PI;
+
+ t = patch->transfers;
+ t2 = all_transfers;
+ for (j=0 ; j<patch->numtransfers ; j++, t++, t2++)
+ {
+ t->transfer = t2->transfer*total;
+ t->patch = t2->patch;
+ }
+ if (patch->numtransfers > max_transfer)
+ {
+ max_transfer = patch->numtransfers;
+ }
+ }
+ else
+ {
+ // Error - patch has no transfers
+ // patch->totallight[2] = 255;
+ }
+
+ ThreadLock ();
+ total_transfer += patch->numtransfers;
+ ThreadUnlock ();
+}
+
+/*
+=============
+WriteWorld
+=============
+*/
+void WriteWorld (char *name, int iBump)
+{
+ unsigned j;
+ FileHandle_t out;
+ CPatch *patch;
+
+ out = g_pFileSystem->Open( name, "w" );
+ if (!out)
+ Error ("Couldn't open %s", name);
+
+ unsigned int uiPatchCount = g_Patches.Size();
+ for (j=0; j<uiPatchCount; j++)
+ {
+ patch = &g_Patches.Element( j );
+
+ // skip parent patches
+ if (patch->child1 != g_Patches.InvalidIndex() )
+ continue;
+
+ if( patch->clusterNumber == -1 )
+ {
+ Vector vGreen;
+ VectorClear( vGreen );
+ vGreen[1] = 256.0f;
+ WriteWinding( out, patch->winding, vGreen );
+ }
+ else
+ {
+ Vector light = patch->totallight.light[iBump] + patch->directlight;
+ WriteWinding( out, patch->winding, light );
+ if( bDumpNormals )
+ {
+ WriteNormal( out, patch->origin, patch->plane->normal, 15.0f, patch->plane->normal * 255.0f );
+ }
+ }
+ }
+
+ g_pFileSystem->Close( out );
+}
+
+void WriteRTEnv (char *name)
+{
+ FileHandle_t out;
+
+ out = g_pFileSystem->Open( name, "w" );
+ if (!out)
+ Error ("Couldn't open %s", name);
+
+ winding_t *triw = AllocWinding( 3 );
+ triw->numpoints = 3;
+
+ for( int i = 0; i < g_RtEnv.OptimizedTriangleList.Size(); i++ )
+ {
+ triw->p[0] = g_RtEnv.OptimizedTriangleList[i].Vertex( 0);
+ triw->p[1] = g_RtEnv.OptimizedTriangleList[i].Vertex( 1);
+ triw->p[2] = g_RtEnv.OptimizedTriangleList[i].Vertex( 2);
+ int id = g_RtEnv.OptimizedTriangleList[i].m_Data.m_GeometryData.m_nTriangleID;
+ Vector color(0, 0, 0);
+ if (id & TRACE_ID_OPAQUE) color.Init(0, 255, 0);
+ if (id & TRACE_ID_SKY) color.Init(0, 0, 255);
+ if (id & TRACE_ID_STATICPROP) color.Init(255, 0, 0);
+ WriteWinding(out, triw, color);
+ }
+ FreeWinding(triw);
+
+ g_pFileSystem->Close( out );
+}
+
+void WriteWinding (FileHandle_t out, winding_t *w, Vector& color )
+{
+ int i;
+
+ CmdLib_FPrintf (out, "%i\n", w->numpoints);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ CmdLib_FPrintf (out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+ w->p[i][0],
+ w->p[i][1],
+ w->p[i][2],
+ color[ 0 ] / 256,
+ color[ 1 ] / 256,
+ color[ 2 ] / 256 );
+ }
+}
+
+
+void WriteNormal( FileHandle_t out, Vector const &nPos, Vector const &nDir,
+ float length, Vector const &color )
+{
+ CmdLib_FPrintf( out, "2\n" );
+ CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+ nPos.x, nPos.y, nPos.z,
+ color.x / 256, color.y / 256, color.z / 256 );
+ CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+ nPos.x + ( nDir.x * length ),
+ nPos.y + ( nDir.y * length ),
+ nPos.z + ( nDir.z * length ),
+ color.x / 256, color.y / 256, color.z / 256 );
+}
+
+void WriteLine( FileHandle_t out, const Vector &vecPos1, const Vector &vecPos2, const Vector &color )
+{
+ CmdLib_FPrintf( out, "2\n" );
+ CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+ vecPos1.x, vecPos1.y, vecPos1.z,
+ color.x / 256, color.y / 256, color.z / 256 );
+ CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+ vecPos2.x, vecPos2.y, vecPos2.z,
+ color.x / 256, color.y / 256, color.z / 256 );
+}
+
+void WriteTrace( const char *pFileName, const FourRays &rays, const RayTracingResult& result )
+{
+ FileHandle_t out;
+
+ out = g_pFileSystem->Open( pFileName, "a" );
+ if (!out)
+ Error ("Couldn't open %s", pFileName);
+
+ // Draws rays
+ for ( int i = 0; i < 4; ++i )
+ {
+ Vector vecOrigin = rays.origin.Vec(i);
+ Vector vecEnd = rays.direction.Vec(i);
+ VectorNormalize( vecEnd );
+ vecEnd *= SubFloat( result.HitDistance, i );
+ vecEnd += vecOrigin;
+ WriteLine( out, vecOrigin, vecEnd, Vector( 256, 0, 0 ) );
+ WriteNormal( out, vecEnd, result.surface_normal.Vec(i), 10.0f, Vector( 256, 265, 0 ) );
+ }
+
+ g_pFileSystem->Close( out );
+}
+
+
+/*
+=============
+CollectLight
+=============
+*/
+// patch's totallight += new light received to each patch
+// patch's emitlight = addlight (newly received light from GatherLight)
+// patch's addlight = 0
+// pull received light from children.
+void CollectLight( Vector& total )
+{
+ int i, j;
+ CPatch *patch;
+
+ VectorFill( total, 0 );
+
+ // process patches in reverse order so that children are processed before their parents
+ unsigned int uiPatchCount = g_Patches.Size();
+ for( i = uiPatchCount - 1; i >= 0; i-- )
+ {
+ patch = &g_Patches.Element( i );
+ int normalCount = patch->needsBumpmap ? NUM_BUMP_VECTS+1 : 1;
+ // sky's never collect light, it is just dropped
+ if (patch->sky)
+ {
+ VectorFill( emitlight[ i ], 0 );
+ }
+ else if ( patch->child1 == g_Patches.InvalidIndex() )
+ {
+ // This is a leaf node.
+ for ( j = 0; j < normalCount; j++ )
+ {
+ VectorAdd( patch->totallight.light[j], addlight[i].light[j], patch->totallight.light[j] );
+ }
+ VectorCopy( addlight[i].light[0], emitlight[i] );
+ VectorAdd( total, emitlight[i], total );
+ }
+ else
+ {
+ // This is an interior node.
+ // Pull received light from children.
+ float s1, s2;
+ CPatch *child1;
+ CPatch *child2;
+
+ child1 = &g_Patches[patch->child1];
+ child2 = &g_Patches[patch->child2];
+
+ // BUG: This doesn't do anything?
+ if ((int)patch->area != (int)(child1->area + child2->area))
+ s1 = 0;
+
+ s1 = child1->area / (child1->area + child2->area);
+ s2 = child2->area / (child1->area + child2->area);
+
+ // patch->totallight = s1 * child1->totallight + s2 * child2->totallight
+ for ( j = 0; j < normalCount; j++ )
+ {
+ VectorScale( child1->totallight.light[j], s1, patch->totallight.light[j] );
+ VectorMA( patch->totallight.light[j], s2, child2->totallight.light[j], patch->totallight.light[j] );
+ }
+
+ // patch->emitlight = s1 * child1->emitlight + s2 * child2->emitlight
+ VectorScale( emitlight[patch->child1], s1, emitlight[i] );
+ VectorMA( emitlight[i], s2, emitlight[patch->child2], emitlight[i] );
+ }
+ for ( j = 0; j < NUM_BUMP_VECTS+1; j++ )
+ {
+ VectorFill( addlight[ i ].light[j], 0 );
+ }
+ }
+}
+
+/*
+=============
+GatherLight
+
+Get light from other patches
+ Run multi-threaded
+=============
+*/
+
+#ifdef _WIN32
+#pragma warning (disable:4701)
+#endif
+
+extern void GetBumpNormals( const float* sVect, const float* tVect, const Vector& flatNormal,
+ const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] );
+
+
+void PreGetBumpNormalsForDisp( texinfo_t *pTexinfo, Vector &vecU, Vector &vecV, Vector &vecNormal )
+{
+ Vector vecTexU( pTexinfo->textureVecsTexelsPerWorldUnits[0][0], pTexinfo->textureVecsTexelsPerWorldUnits[0][1], pTexinfo->textureVecsTexelsPerWorldUnits[0][2] );
+ Vector vecTexV( pTexinfo->textureVecsTexelsPerWorldUnits[1][0], pTexinfo->textureVecsTexelsPerWorldUnits[1][1], pTexinfo->textureVecsTexelsPerWorldUnits[1][2] );
+ Vector vecLightU( pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][0], pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][1], pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][2] );
+ Vector vecLightV( pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][0], pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][1], pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][2] );
+
+ VectorNormalize( vecTexU );
+ VectorNormalize( vecTexV );
+ VectorNormalize( vecLightU );
+ VectorNormalize( vecLightV );
+
+ bool bDoConversion = false;
+ if ( fabs( vecTexU.Dot( vecLightU ) ) < 0.999f )
+ {
+ bDoConversion = true;
+ }
+
+ if ( fabs( vecTexV.Dot( vecLightV ) ) < 0.999f )
+ {
+ bDoConversion = true;
+ }
+
+ if ( bDoConversion )
+ {
+ matrix3x4_t matTex( vecTexU, vecTexV, vecNormal, vec3_origin );
+ matrix3x4_t matLight( vecLightU, vecLightV, vecNormal, vec3_origin );
+ matrix3x4_t matTmp;
+ ConcatTransforms ( matLight, matTex, matTmp );
+ MatrixGetColumn( matTmp, 0, vecU );
+ MatrixGetColumn( matTmp, 1, vecV );
+ MatrixGetColumn( matTmp, 2, vecNormal );
+
+ Assert( fabs( vecTexU.Dot( vecTexV ) ) <= 0.001f );
+ return;
+ }
+
+ vecU = vecTexU;
+ vecV = vecTexV;
+}
+
+void GatherLight (int threadnum, void *pUserData)
+{
+ int i, j, k;
+ transfer_t *trans;
+ int num;
+ CPatch *patch;
+ Vector sum, v;
+
+ while (1)
+ {
+ j = GetThreadWork ();
+ if (j == -1)
+ break;
+
+ patch = &g_Patches[j];
+
+ trans = patch->transfers;
+ num = patch->numtransfers;
+ if ( patch->needsBumpmap )
+ {
+ Vector delta;
+ Vector bumpSum[NUM_BUMP_VECTS+1];
+ Vector normals[NUM_BUMP_VECTS+1];
+
+ // Disps
+ bool bDisp = ( g_pFaces[patch->faceNumber].dispinfo != -1 );
+ if ( bDisp )
+ {
+ normals[0] = patch->normal;
+ texinfo_t *pTexinfo = &texinfo[g_pFaces[patch->faceNumber].texinfo];
+ Vector vecTexU, vecTexV;
+ PreGetBumpNormalsForDisp( pTexinfo, vecTexU, vecTexV, normals[0] );
+
+ // use facenormal along with the smooth normal to build the three bump map vectors
+ GetBumpNormals( vecTexU, vecTexV, normals[0], normals[0], &normals[1] );
+ }
+ else
+ {
+ GetPhongNormal( patch->faceNumber, patch->origin, normals[0] );
+
+ texinfo_t *pTexinfo = &texinfo[g_pFaces[patch->faceNumber].texinfo];
+ // use facenormal along with the smooth normal to build the three bump map vectors
+ GetBumpNormals( pTexinfo->textureVecsTexelsPerWorldUnits[0],
+ pTexinfo->textureVecsTexelsPerWorldUnits[1], patch->normal,
+ normals[0], &normals[1] );
+ }
+
+ // force the base lightmap to use the flat normal instead of the phong normal
+ // FIXME: why does the patch not use the phong normal?
+ normals[0] = patch->normal;
+
+ for ( i = 0; i < NUM_BUMP_VECTS+1; i++ )
+ {
+ VectorFill( bumpSum[i], 0 );
+ }
+
+ float dot;
+ for (k=0 ; k<num ; k++, trans++)
+ {
+ CPatch *patch2 = &g_Patches[trans->patch];
+
+ // get vector to other patch
+ VectorSubtract (patch2->origin, patch->origin, delta);
+ VectorNormalize (delta);
+ // find light emitted from other patch
+ for(i=0; i<3; i++)
+ {
+ v[i] = emitlight[trans->patch][i] * patch2->reflectivity[i];
+ }
+ // remove normal already factored into transfer steradian
+ float scale = 1.0f / DotProduct (delta, patch->normal);
+ VectorScale( v, trans->transfer * scale, v );
+
+ Vector bumpTransfer;
+ for ( i = 0; i < NUM_BUMP_VECTS+1; i++ )
+ {
+ dot = DotProduct( delta, normals[i] );
+ if ( dot <= 0 )
+ {
+// Assert( i > 0 ); // if this hits, then the transfer shouldn't be here. It doesn't face the flat normal of this face!
+ continue;
+ }
+ bumpTransfer = v * dot;
+ VectorAdd( bumpSum[i], bumpTransfer, bumpSum[i] );
+ }
+ }
+ for ( i = 0; i < NUM_BUMP_VECTS+1; i++ )
+ {
+ VectorCopy( bumpSum[i], addlight[j].light[i] );
+ }
+ }
+ else
+ {
+ VectorFill( sum, 0 );
+ for (k=0 ; k<num ; k++, trans++)
+ {
+ for(i=0; i<3; i++)
+ {
+ v[i] = emitlight[trans->patch][i] * g_Patches[trans->patch].reflectivity[i];
+ }
+ VectorScale( v, trans->transfer, v );
+ VectorAdd( sum, v, sum );
+ }
+ VectorCopy( sum, addlight[j].light[0] );
+ }
+ }
+}
+
+#ifdef _WIN32
+#pragma warning (default:4701)
+#endif
+
+
+/*
+=============
+BounceLight
+=============
+*/
+void BounceLight (void)
+{
+ unsigned i;
+ Vector added;
+ char name[64];
+ qboolean bouncing = numbounce > 0;
+
+ unsigned int uiPatchCount = g_Patches.Size();
+ for (i=0 ; i<uiPatchCount; i++)
+ {
+ // totallight has a copy of the direct lighting. Move it to the emitted light and zero it out (to integrate bounces only)
+ VectorCopy( g_Patches[i].totallight.light[0], emitlight[i] );
+
+ // NOTE: This means that only the bounced light is integrated into totallight!
+ VectorFill( g_Patches[i].totallight.light[0], 0 );
+ }
+
+#if 0
+ FileHandle_t dFp = g_pFileSystem->Open( "lightemit.txt", "w" );
+
+ unsigned int uiPatchCount = g_Patches.Size();
+ for (i=0 ; i<uiPatchCount; i++)
+ {
+ CmdLib_FPrintf( dFp, "Emit %d: %f %f %f\n", i, emitlight[i].x, emitlight[i].y, emitlight[i].z );
+ }
+
+ g_pFileSystem->Close( dFp );
+
+ for (i=0; i<num_patches ; i++)
+ {
+ Vector total;
+
+ VectorSubtract (g_Patches[i].maxs, g_Patches[i].mins, total);
+ Msg("%4d %4d %4d %4d (%d) %.0f", i, g_Patches[i].parent, g_Patches[i].child1, g_Patches[i].child2, g_Patches[i].samples, g_Patches[i].area );
+ Msg(" [%.0f %.0f %.0f]", total[0], total[1], total[2] );
+ if (g_Patches[i].child1 != g_Patches.InvalidIndex() )
+ {
+ Vector tmp;
+ VectorScale( g_Patches[i].totallight.light[0], g_Patches[i].area, tmp );
+
+ VectorMA( tmp, -g_Patches[g_Patches[i].child1].area, g_Patches[g_Patches[i].child1].totallight.light[0], tmp );
+ VectorMA( tmp, -g_Patches[g_Patches[i].child2].area, g_Patches[g_Patches[i].child2].totallight.light[0], tmp );
+ // Msg("%.0f ", VectorLength( tmp ) );
+ // Msg("%d ", g_Patches[i].samples - g_Patches[g_Patches[i].child1].samples - g_Patches[g_Patches[i].child2].samples );
+ // Msg("%d ", g_Patches[i].samples );
+ }
+ Msg("\n");
+ }
+#endif
+
+ i = 0;
+ while ( bouncing )
+ {
+ // transfer light from to the leaf patches from other patches via transfers
+ // this moves shooter->emitlight to receiver->addlight
+ unsigned int uiPatchCount = g_Patches.Size();
+ RunThreadsOn (uiPatchCount, true, GatherLight);
+ // move newly received light (addlight) to light to be sent out (emitlight)
+ // start at children and pull light up to parents
+ // light is always received to leaf patches
+ CollectLight( added );
+
+ qprintf ("\tBounce #%i added RGB(%.0f, %.0f, %.0f)\n", i+1, added[0], added[1], added[2] );
+
+ if ( i+1 == numbounce || (added[0] < 1.0 && added[1] < 1.0 && added[2] < 1.0) )
+ bouncing = false;
+
+ i++;
+ if ( g_bDumpPatches && !bouncing && i != 1)
+ {
+ sprintf (name, "bounce%i.txt", i);
+ WriteWorld (name, 0);
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Counts the number of clusters in a map with no visibility
+// Output : int
+//-----------------------------------------------------------------------------
+int CountClusters( void )
+{
+ int clusterCount = 0;
+
+ for ( int i = 0; i < numleafs; i++ )
+ {
+ if ( dleafs[i].cluster > clusterCount )
+ clusterCount = dleafs[i].cluster;
+ }
+
+ return clusterCount + 1;
+}
+
+
+/*
+=============
+RadWorld
+=============
+*/
+void RadWorld_Start()
+{
+ unsigned i;
+
+ if (luxeldensity < 1.0)
+ {
+ // Remember the old lightmap vectors.
+ float oldLightmapVecs[MAX_MAP_TEXINFO][2][4];
+ for (i = 0; i < texinfo.Count(); i++)
+ {
+ for( int j=0; j < 2; j++ )
+ {
+ for( int k=0; k < 3; k++ )
+ {
+ oldLightmapVecs[i][j][k] = texinfo[i].lightmapVecsLuxelsPerWorldUnits[j][k];
+ }
+ }
+ }
+
+ // rescale luxels to be no denser than "luxeldensity"
+ for (i = 0; i < texinfo.Count(); i++)
+ {
+ texinfo_t *tx = &texinfo[i];
+
+ for (int j = 0; j < 2; j++ )
+ {
+ Vector tmp( tx->lightmapVecsLuxelsPerWorldUnits[j][0], tx->lightmapVecsLuxelsPerWorldUnits[j][1], tx->lightmapVecsLuxelsPerWorldUnits[j][2] );
+ float scale = VectorNormalize( tmp );
+ // only rescale them if the current scale is "tighter" than the desired scale
+ // FIXME: since this writes out to the BSP file every run, once it's set high it can't be reset
+ // to a lower value.
+ if (fabs( scale ) > luxeldensity)
+ {
+ if (scale < 0)
+ {
+ scale = -luxeldensity;
+ }
+ else
+ {
+ scale = luxeldensity;
+ }
+ VectorScale( tmp, scale, tmp );
+ tx->lightmapVecsLuxelsPerWorldUnits[j][0] = tmp.x;
+ tx->lightmapVecsLuxelsPerWorldUnits[j][1] = tmp.y;
+ tx->lightmapVecsLuxelsPerWorldUnits[j][2] = tmp.z;
+ }
+ }
+ }
+
+ UpdateAllFaceLightmapExtents();
+ }
+
+ MakeParents (0, -1);
+
+ BuildClusterTable();
+
+ // turn each face into a single patch
+ MakePatches ();
+ PairEdges ();
+
+ // store the vertex normals calculated in PairEdges
+ // so that the can be written to the bsp file for
+ // use in the engine
+ SaveVertexNormals();
+
+ // subdivide patches to a maximum dimension
+ SubdividePatches ();
+
+ // add displacement faces to cluster table
+ AddDispsToClusterTable();
+
+ // create directlights out of patches and lights
+ CreateDirectLights ();
+
+ // set up sky cameras
+ ProcessSkyCameras();
+}
+
+
+// This function should fill in the indices into g_pFaces[] for the faces
+// with displacements that touch the specified leaf.
+void STUB_GetDisplacementsTouchingLeaf( int iLeaf, CUtlVector<int> &dispFaces )
+{
+}
+
+
+void BuildFacesVisibleToLights( bool bAllVisible )
+{
+ g_FacesVisibleToLights.SetSize( numfaces/8 + 1 );
+
+ if( bAllVisible )
+ {
+ memset( g_FacesVisibleToLights.Base(), 0xFF, g_FacesVisibleToLights.Count() );
+ return;
+ }
+
+ // First merge all the light PVSes.
+ CUtlVector<byte> aggregate;
+ aggregate.SetSize( (dvis->numclusters/8) + 1 );
+ memset( aggregate.Base(), 0, aggregate.Count() );
+
+ int nDWords = aggregate.Count() / 4;
+ int nBytes = aggregate.Count() - nDWords*4;
+
+ for( directlight_t *dl = activelights; dl != NULL; dl = dl->next )
+ {
+ byte *pIn = dl->pvs;
+ byte *pOut = aggregate.Base();
+ for( int iDWord=0; iDWord < nDWords; iDWord++ )
+ {
+ *((unsigned long*)pOut) |= *((unsigned long*)pIn);
+ pIn += 4;
+ pOut += 4;
+ }
+
+ for( int iByte=0; iByte < nBytes; iByte++ )
+ {
+ *pOut |= *pIn;
+ ++pOut;
+ ++pIn;
+ }
+ }
+
+
+ // Now tag any faces that are visible to this monster PVS.
+ for( int iCluster=0; iCluster < dvis->numclusters; iCluster++ )
+ {
+ if( g_ClusterLeaves[iCluster].leafCount )
+ {
+ if( aggregate[iCluster>>3] & (1 << (iCluster & 7)) )
+ {
+ for ( int i = 0; i < g_ClusterLeaves[iCluster].leafCount; i++ )
+ {
+ int iLeaf = g_ClusterLeaves[iCluster].leafs[i];
+
+ // Tag all the faces.
+ int iFace;
+ for( iFace=0; iFace < dleafs[iLeaf].numleaffaces; iFace++ )
+ {
+ int index = dleafs[iLeaf].firstleafface + iFace;
+ index = dleaffaces[index];
+
+ assert( index < numfaces );
+ g_FacesVisibleToLights[index >> 3] |= (1 << (index & 7));
+ }
+
+ // Fill in STUB_GetDisplacementsTouchingLeaf when it's available
+ // so displacements get relit.
+ CUtlVector<int> dispFaces;
+ STUB_GetDisplacementsTouchingLeaf( iLeaf, dispFaces );
+ for( iFace=0; iFace < dispFaces.Count(); iFace++ )
+ {
+ int index = dispFaces[iFace];
+ g_FacesVisibleToLights[index >> 3] |= (1 << (index & 7));
+ }
+ }
+ }
+ }
+ }
+
+ // For stats.. figure out how many faces it's going to touch.
+ int nFacesToProcess = 0;
+ for( int i=0; i < numfaces; i++ )
+ {
+ if( g_FacesVisibleToLights[i>>3] & (1 << (i & 7)) )
+ ++nFacesToProcess;
+ }
+}
+
+
+
+void MakeAllScales (void)
+{
+ // determine visibility between patches
+ BuildVisMatrix ();
+
+ // release visibility matrix
+ FreeVisMatrix ();
+
+ Msg("transfers %d, max %d\n", total_transfer, max_transfer );
+
+ qprintf ("transfer lists: %5.1f megs\n"
+ , (float)total_transfer * sizeof(transfer_t) / (1024*1024));
+}
+
+
+// Helper function. This can be useful to visualize the world and faces and see which face
+// corresponds to which dface.
+#if 0
+ #include "iscratchpad3d.h"
+ void ScratchPad_DrawWorld()
+ {
+ IScratchPad3D *pPad = ScratchPad3D_Create();
+ pPad->SetAutoFlush( false );
+
+ for ( int i=0; i < numfaces; i++ )
+ {
+ dface_t *f = &g_pFaces[i];
+
+ // Draw the face's outline, then put text for its face index on it too.
+ CUtlVector<Vector> points;
+ for ( int iEdge = 0; iEdge < f->numedges; iEdge++ )
+ {
+ int v;
+ int se = dsurfedges[f->firstedge + iEdge];
+ if ( se < 0 )
+ v = dedges[-se].v[1];
+ else
+ v = dedges[se].v[0];
+
+ dvertex_t *dv = &dvertexes[v];
+ points.AddToTail( dv->point );
+ }
+
+ // Draw the outline.
+ Vector vCenter( 0, 0, 0 );
+ for ( iEdge=0; iEdge < points.Count(); iEdge++ )
+ {
+ pPad->DrawLine( CSPVert( points[iEdge] ), CSPVert( points[(iEdge+1)%points.Count()] ) );
+ vCenter += points[iEdge];
+ }
+ vCenter /= points.Count();
+
+ // Draw the text.
+ char str[512];
+ Q_snprintf( str, sizeof( str ), "%d", i );
+
+ CTextParams params;
+
+ params.m_bCentered = true;
+ params.m_bOutline = true;
+ params.m_flLetterWidth = 2;
+ params.m_vColor.Init( 1, 0, 0 );
+
+ VectorAngles( dplanes[f->planenum].normal, params.m_vAngles );
+ params.m_bTwoSided = true;
+
+ params.m_vPos = vCenter;
+
+ pPad->DrawText( str, params );
+ }
+
+ pPad->Release();
+ }
+#endif
+
+
+bool RadWorld_Go()
+{
+ g_iCurFace = 0;
+
+ InitMacroTexture( source );
+
+ if( g_pIncremental )
+ {
+ g_pIncremental->PrepareForLighting();
+
+ // Cull out faces that aren't visible to any of the lights that we're updating with.
+ BuildFacesVisibleToLights( false );
+ }
+ else
+ {
+ // Mark all faces visible.. when not doing incremental lighting, it's highly
+ // likely that all faces are going to be touched by at least one light so don't
+ // waste time here.
+ BuildFacesVisibleToLights( true );
+ }
+
+ // build initial facelights
+ if (g_bUseMPI)
+ {
+ // RunThreadsOnIndividual (numfaces, true, BuildFacelights);
+ RunMPIBuildFacelights();
+ }
+ else
+ {
+ RunThreadsOnIndividual (numfaces, true, BuildFacelights);
+ }
+
+ // Was the process interrupted?
+ if( g_pIncremental && (g_iCurFace != numfaces) )
+ return false;
+
+ // Figure out the offset into lightmap data for each face.
+ PrecompLightmapOffsets();
+
+ // If we're doing incremental lighting, stop here.
+ if( g_pIncremental )
+ {
+ g_pIncremental->Finalize();
+ }
+ else
+ {
+ // free up the direct lights now that we have facelights
+ ExportDirectLightsToWorldLights();
+
+ if ( g_bDumpPatches )
+ {
+ for( int iBump = 0; iBump < 4; ++iBump )
+ {
+ char szName[64];
+ sprintf ( szName, "bounce0_%d.txt", iBump );
+ WriteWorld( szName, iBump );
+ }
+ }
+
+ if (numbounce > 0)
+ {
+ // allocate memory for emitlight/addlight
+ emitlight.SetSize( g_Patches.Size() );
+ memset( emitlight.Base(), 0, g_Patches.Size() * sizeof( Vector ) );
+ addlight.SetSize( g_Patches.Size() );
+ memset( addlight.Base(), 0, g_Patches.Size() * sizeof( bumplights_t ) );
+
+ MakeAllScales ();
+
+ // spread light around
+ BounceLight ();
+ }
+
+ //
+ // displacement surface luxel accumulation (make threaded!!!)
+ //
+ StaticDispMgr()->StartTimer( "Build Patch/Sample Hash Table(s)....." );
+ StaticDispMgr()->InsertSamplesDataIntoHashTable();
+ StaticDispMgr()->InsertPatchSampleDataIntoHashTable();
+ StaticDispMgr()->EndTimer();
+
+ // blend bounced light into direct light and save
+ VMPI_SetCurrentStage( "FinalLightFace" );
+ if ( !g_bUseMPI || g_bMPIMaster )
+ RunThreadsOnIndividual (numfaces, true, FinalLightFace);
+
+ // Distribute the lighting data to workers.
+ VMPI_DistributeLightData();
+
+ Msg("FinalLightFace Done\n"); fflush(stdout);
+ }
+
+ return true;
+}
+
+// declare the sample file pointer -- the whole debug print system should
+// be reworked at some point!!
+FileHandle_t pFileSamples[4][4];
+
+void LoadPhysicsDLL( void )
+{
+ PhysicsDLLPath( "VPHYSICS.DLL" );
+}
+
+
+void InitDumpPatchesFiles()
+{
+ for( int iStyle = 0; iStyle < 4; ++iStyle )
+ {
+ for ( int iBump = 0; iBump < 4; ++iBump )
+ {
+ char szFilename[MAX_PATH];
+ sprintf( szFilename, "samples_style%d_bump%d.txt", iStyle, iBump );
+ pFileSamples[iStyle][iBump] = g_pFileSystem->Open( szFilename, "w" );
+ if( !pFileSamples[iStyle][iBump] )
+ {
+ Error( "Can't open %s for -dump.\n", szFilename );
+ }
+ }
+ }
+}
+
+
+void VRAD_LoadBSP( char const *pFilename )
+{
+ ThreadSetDefault ();
+
+ g_flStartTime = Plat_FloatTime();
+
+ if( g_bLowPriority )
+ {
+ SetLowPriority();
+ }
+
+ strcpy( level_name, source );
+
+ // This must come after InitFileSystem because the file system pointer might change.
+ if ( g_bDumpPatches )
+ InitDumpPatchesFiles();
+
+ // This part is just for VMPI. VMPI's file system needs the basedir in front of all filenames,
+ // so we prepend qdir here.
+ strcpy( source, ExpandPath( source ) );
+
+ if ( !g_bUseMPI )
+ {
+ // Setup the logfile.
+ char logFile[512];
+ _snprintf( logFile, sizeof(logFile), "%s.log", source );
+ SetSpewFunctionLogFile( logFile );
+ }
+
+ LoadPhysicsDLL();
+
+ // Set the required global lights filename and try looking in qproject
+ strcpy( global_lights, "lights.rad" );
+ if ( !g_pFileSystem->FileExists( global_lights ) )
+ {
+ // Otherwise, try looking in the BIN directory from which we were run from
+ Msg( "Could not find lights.rad in %s.\nTrying VRAD BIN directory instead...\n",
+ global_lights );
+ GetModuleFileName( NULL, global_lights, sizeof( global_lights ) );
+ Q_ExtractFilePath( global_lights, global_lights, sizeof( global_lights ) );
+ strcat( global_lights, "lights.rad" );
+ }
+
+ // Set the optional level specific lights filename
+ strcpy( level_lights, source );
+
+ Q_DefaultExtension( level_lights, ".rad", sizeof( level_lights ) );
+ if ( !g_pFileSystem->FileExists( level_lights ) )
+ *level_lights = 0;
+
+ ReadLightFile(global_lights); // Required
+ if ( *designer_lights ) ReadLightFile(designer_lights); // Command-line
+ if ( *level_lights ) ReadLightFile(level_lights); // Optional & implied
+
+ strcpy(incrementfile, source);
+ Q_DefaultExtension(incrementfile, ".r0", sizeof(incrementfile));
+ Q_DefaultExtension(source, ".bsp", sizeof( source ));
+
+ GetPlatformMapPath( source, platformPath, 0, MAX_PATH );
+
+ Msg( "Loading %s\n", platformPath );
+ VMPI_SetCurrentStage( "LoadBSPFile" );
+ LoadBSPFile (platformPath);
+
+ // now, set whether or not static prop lighting is present
+ if (g_bStaticPropLighting)
+ g_LevelFlags |= g_bHDR? LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR : LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR;
+ else
+ {
+ g_LevelFlags &= ~( LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR | LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR );
+ }
+
+ // now, we need to set our face ptr depending upon hdr, and if hdr, init it
+ if (g_bHDR)
+ {
+ g_pFaces = dfaces_hdr;
+ if (numfaces_hdr==0)
+ {
+ numfaces_hdr = numfaces;
+ memcpy( dfaces_hdr, dfaces, numfaces*sizeof(dfaces[0]) );
+ }
+ }
+ else
+ {
+ g_pFaces = dfaces;
+ }
+
+
+ ParseEntities ();
+ ExtractBrushEntityShadowCasters();
+
+ StaticPropMgr()->Init();
+ StaticDispMgr()->Init();
+
+ if (!visdatasize)
+ {
+ Msg("No vis information, direct lighting only.\n");
+ numbounce = 0;
+ ambient[0] = ambient[1] = ambient[2] = 0.1f;
+ dvis->numclusters = CountClusters();
+ }
+
+ //
+ // patches and referencing data (ensure capacity)
+ //
+ // TODO: change the maxes to the amount from the bsp!!
+ //
+// g_Patches.EnsureCapacity( MAX_PATCHES );
+
+ g_FacePatches.SetSize( MAX_MAP_FACES );
+ faceParents.SetSize( MAX_MAP_FACES );
+ clusterChildren.SetSize( MAX_MAP_CLUSTERS );
+
+ int ndx;
+ for ( ndx = 0; ndx < MAX_MAP_FACES; ndx++ )
+ {
+ g_FacePatches[ndx] = g_FacePatches.InvalidIndex();
+ faceParents[ndx] = faceParents.InvalidIndex();
+ }
+
+ for ( ndx = 0; ndx < MAX_MAP_CLUSTERS; ndx++ )
+ {
+ clusterChildren[ndx] = clusterChildren.InvalidIndex();
+ }
+
+ // Setup ray tracer
+ AddBrushesForRayTrace();
+ StaticDispMgr()->AddPolysForRayTrace();
+ StaticPropMgr()->AddPolysForRayTrace();
+
+ // Dump raytracer for glview
+ if ( g_bDumpRtEnv )
+ WriteRTEnv("trace.txt");
+
+ // Build acceleration structure
+ printf ( "Setting up ray-trace acceleration structure... ");
+ float start = Plat_FloatTime();
+ g_RtEnv.SetupAccelerationStructure();
+ float end = Plat_FloatTime();
+ printf ( "Done (%.2f seconds)\n", end-start );
+
+#if 0 // To test only k-d build
+ exit(0);
+#endif
+
+ RadWorld_Start();
+
+ // Setup incremental lighting.
+ if( g_pIncremental )
+ {
+ if( !g_pIncremental->Init( source, incrementfile ) )
+ {
+ Error( "Unable to load incremental lighting file in %s.\n", incrementfile );
+ return;
+ }
+ }
+}
+
+
+void VRAD_ComputeOtherLighting()
+{
+ // Compute lighting for the bsp file
+ if ( !g_bNoDetailLighting )
+ {
+ ComputeDetailPropLighting( THREADINDEX_MAIN );
+ }
+
+ ComputePerLeafAmbientLighting();
+
+ // bake the static props high quality vertex lighting into the bsp
+ if ( !do_fast && g_bStaticPropLighting )
+ {
+ StaticPropMgr()->ComputeLighting( THREADINDEX_MAIN );
+ }
+}
+
+extern void CloseDispLuxels();
+
+void VRAD_Finish()
+{
+ Msg( "Ready to Finish\n" );
+ fflush( stdout );
+
+ if ( verbose )
+ {
+ PrintBSPFileSizes();
+ }
+
+ Msg( "Writing %s\n", platformPath );
+ VMPI_SetCurrentStage( "WriteBSPFile" );
+ WriteBSPFile(platformPath);
+
+ if ( g_bDumpPatches )
+ {
+ for ( int iStyle = 0; iStyle < 4; ++iStyle )
+ {
+ for ( int iBump = 0; iBump < 4; ++iBump )
+ {
+ g_pFileSystem->Close( pFileSamples[iStyle][iBump] );
+ }
+ }
+ }
+
+ CloseDispLuxels();
+
+ StaticPropMgr()->Shutdown();
+
+ double end = Plat_FloatTime();
+
+ char str[512];
+ GetHourMinuteSecondsString( (int)( end - g_flStartTime ), str, sizeof( str ) );
+ Msg( "%s elapsed\n", str );
+
+ ReleasePakFileLumps();
+}
+
+
+// Run startup code like initialize mathlib (called from main() and from the
+// WorldCraft interface into vrad).
+void VRAD_Init()
+{
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
+ InstallAllocationFunctions();
+ InstallSpewFunction();
+}
+
+
+int ParseCommandLine( int argc, char **argv, bool *onlydetail )
+{
+ *onlydetail = false;
+
+ // default to LDR
+ SetHDRMode( false );
+ int i;
+ for( i=1 ; i<argc ; i++ )
+ {
+ if ( !Q_stricmp( argv[i], "-StaticPropLighting" ) )
+ {
+ g_bStaticPropLighting = true;
+ }
+ else if ( !stricmp( argv[i], "-StaticPropNormals" ) )
+ {
+ g_bShowStaticPropNormals = true;
+ }
+ else if ( !stricmp( argv[i], "-OnlyStaticProps" ) )
+ {
+ g_bOnlyStaticProps = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-StaticPropPolys" ) )
+ {
+ g_bStaticPropPolys = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-nossprops" ) )
+ {
+ g_bDisablePropSelfShadowing = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-textureshadows" ) )
+ {
+ g_bTextureShadows = true;
+ }
+ else if ( !strcmp(argv[i], "-dump") )
+ {
+ g_bDumpPatches = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-nodetaillight" ) )
+ {
+ g_bNoDetailLighting = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-rederrors" ) )
+ {
+ bRed2Black = false;
+ }
+ else if ( !Q_stricmp( argv[i], "-dumpnormals" ) )
+ {
+ bDumpNormals = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-dumptrace" ) )
+ {
+ g_bDumpRtEnv = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-LargeDispSampleRadius" ) )
+ {
+ g_bLargeDispSampleRadius = true;
+ }
+ else if (!Q_stricmp(argv[i],"-bounce"))
+ {
+ if ( ++i < argc )
+ {
+ numbounce = atoi (argv[i]);
+ if ( numbounce < 0 )
+ {
+ Warning("Error: expected non-negative value after '-bounce'\n" );
+ return 1;
+ }
+ }
+ else
+ {
+ Warning("Error: expected a value after '-bounce'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-verbose") || !Q_stricmp(argv[i],"-v"))
+ {
+ verbose = true;
+ }
+ else if (!Q_stricmp(argv[i],"-threads"))
+ {
+ if ( ++i < argc )
+ {
+ numthreads = atoi (argv[i]);
+ if ( numthreads <= 0 )
+ {
+ Warning("Error: expected positive value after '-threads'\n" );
+ return 1;
+ }
+ }
+ else
+ {
+ Warning("Error: expected a value after '-threads'\n" );
+ return 1;
+ }
+ }
+ else if ( !Q_stricmp(argv[i], "-lights" ) )
+ {
+ if ( ++i < argc && *argv[i] )
+ {
+ strcpy( designer_lights, argv[i] );
+ }
+ else
+ {
+ Warning("Error: expected a filepath after '-lights'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-noextra"))
+ {
+ do_extra = false;
+ }
+ else if (!Q_stricmp(argv[i],"-debugextra"))
+ {
+ debug_extra = true;
+ }
+ else if ( !Q_stricmp(argv[i], "-fastambient") )
+ {
+ g_bFastAmbient = true;
+ }
+ else if (!Q_stricmp(argv[i],"-fast"))
+ {
+ do_fast = true;
+ }
+ else if (!Q_stricmp(argv[i],"-noskyboxrecurse"))
+ {
+ g_bNoSkyRecurse = true;
+ }
+ else if (!Q_stricmp(argv[i],"-final"))
+ {
+ g_flSkySampleScale = 16.0;
+ }
+ else if (!Q_stricmp(argv[i],"-extrasky"))
+ {
+ if ( ++i < argc && *argv[i] )
+ {
+ g_flSkySampleScale = atof( argv[i] );
+ }
+ else
+ {
+ Warning("Error: expected a scale factor after '-extrasky'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-centersamples"))
+ {
+ do_centersamples = true;
+ }
+ else if (!Q_stricmp(argv[i],"-smooth"))
+ {
+ if ( ++i < argc )
+ {
+ smoothing_threshold = (float)cos(atof(argv[i])*(M_PI/180.0));
+ }
+ else
+ {
+ Warning("Error: expected an angle after '-smooth'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-dlightmap"))
+ {
+ dlight_map = 1;
+ }
+ else if (!Q_stricmp(argv[i],"-luxeldensity"))
+ {
+ if ( ++i < argc )
+ {
+ luxeldensity = (float)atof (argv[i]);
+ if (luxeldensity > 1.0)
+ luxeldensity = 1.0 / luxeldensity;
+ }
+ else
+ {
+ Warning("Error: expected a value after '-luxeldensity'\n" );
+ return 1;
+ }
+ }
+ else if( !Q_stricmp( argv[i], "-low" ) )
+ {
+ g_bLowPriority = true;
+ }
+ else if( !Q_stricmp( argv[i], "-loghash" ) )
+ {
+ g_bLogHashData = true;
+ }
+ else if( !Q_stricmp( argv[i], "-onlydetail" ) )
+ {
+ *onlydetail = true;
+ }
+ else if (!Q_stricmp(argv[i],"-softsun"))
+ {
+ if ( ++i < argc )
+ {
+ g_SunAngularExtent=atof(argv[i]);
+ g_SunAngularExtent=sin((M_PI/180.0)*g_SunAngularExtent);
+ printf("sun extent=%f\n",g_SunAngularExtent);
+ }
+ else
+ {
+ Warning("Error: expected an angular extent value (0..180) '-softsun'\n" );
+ return 1;
+ }
+ }
+ else if ( !Q_stricmp( argv[i], "-maxdispsamplesize" ) )
+ {
+ if ( ++i < argc )
+ {
+ g_flMaxDispSampleSize = ( float )atof( argv[i] );
+ }
+ else
+ {
+ Warning( "Error: expected a sample size after '-maxdispsamplesize'\n" );
+ return 1;
+ }
+ }
+ else if ( stricmp( argv[i], "-StopOnExit" ) == 0 )
+ {
+ g_bStopOnExit = true;
+ }
+ else if ( stricmp( argv[i], "-steam" ) == 0 )
+ {
+ }
+ else if ( stricmp( argv[i], "-allowdebug" ) == 0 )
+ {
+ // Don't need to do anything, just don't error out.
+ }
+ else if ( !Q_stricmp( argv[i], CMDLINEOPTION_NOVCONFIG ) )
+ {
+ }
+ else if ( !Q_stricmp( argv[i], "-vproject" ) || !Q_stricmp( argv[i], "-game" ) )
+ {
+ ++i;
+ }
+ else if ( !Q_stricmp( argv[i], "-FullMinidumps" ) )
+ {
+ EnableFullMinidumps( true );
+ }
+ else if ( !Q_stricmp( argv[i], "-hdr" ) )
+ {
+ SetHDRMode( true );
+ }
+ else if ( !Q_stricmp( argv[i], "-ldr" ) )
+ {
+ SetHDRMode( false );
+ }
+ else if (!Q_stricmp(argv[i],"-maxchop"))
+ {
+ if ( ++i < argc )
+ {
+ maxchop = (float)atof (argv[i]);
+ if ( maxchop < 1 )
+ {
+ Warning("Error: expected positive value after '-maxchop'\n" );
+ return 1;
+ }
+ }
+ else
+ {
+ Warning("Error: expected a value after '-maxchop'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-chop"))
+ {
+ if ( ++i < argc )
+ {
+ minchop = (float)atof (argv[i]);
+ if ( minchop < 1 )
+ {
+ Warning("Error: expected positive value after '-chop'\n" );
+ return 1;
+ }
+ minchop = min( minchop, maxchop );
+ }
+ else
+ {
+ Warning("Error: expected a value after '-chop'\n" );
+ return 1;
+ }
+ }
+ else if ( !Q_stricmp( argv[i], "-dispchop" ) )
+ {
+ if ( ++i < argc )
+ {
+ dispchop = ( float )atof( argv[i] );
+ if ( dispchop < 1.0f )
+ {
+ Warning( "Error: expected positive value after '-dipschop'\n" );
+ return 1;
+ }
+ }
+ else
+ {
+ Warning( "Error: expected a value after '-dispchop'\n" );
+ return 1;
+ }
+ }
+ else if ( !Q_stricmp( argv[i], "-disppatchradius" ) )
+ {
+ if ( ++i < argc )
+ {
+ g_MaxDispPatchRadius = ( float )atof( argv[i] );
+ if ( g_MaxDispPatchRadius < 10.0f )
+ {
+ Warning( "Error: g_MaxDispPatchRadius < 10.0\n" );
+ return 1;
+ }
+ }
+ else
+ {
+ Warning( "Error: expected a value after '-disppatchradius'\n" );
+ return 1;
+ }
+ }
+
+#if ALLOWDEBUGOPTIONS
+ else if (!Q_stricmp(argv[i],"-scale"))
+ {
+ if ( ++i < argc )
+ {
+ lightscale = (float)atof (argv[i]);
+ }
+ else
+ {
+ Warning("Error: expected a value after '-scale'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-ambient"))
+ {
+ if ( i+3 < argc )
+ {
+ ambient[0] = (float)atof (argv[++i]) * 128;
+ ambient[1] = (float)atof (argv[++i]) * 128;
+ ambient[2] = (float)atof (argv[++i]) * 128;
+ }
+ else
+ {
+ Warning("Error: expected three color values after '-ambient'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-dlight"))
+ {
+ if ( ++i < argc )
+ {
+ dlight_threshold = (float)atof (argv[i]);
+ }
+ else
+ {
+ Warning("Error: expected a value after '-dlight'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-sky"))
+ {
+ if ( ++i < argc )
+ {
+ indirect_sun = (float)atof (argv[i]);
+ }
+ else
+ {
+ Warning("Error: expected a value after '-sky'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-notexscale"))
+ {
+ texscale = false;
+ }
+ else if (!Q_stricmp(argv[i],"-coring"))
+ {
+ if ( ++i < argc )
+ {
+ coring = (float)atof( argv[i] );
+ }
+ else
+ {
+ Warning("Error: expected a light threshold after '-coring'\n" );
+ return 1;
+ }
+ }
+#endif
+ // NOTE: the -mpi checks must come last here because they allow the previous argument
+ // to be -mpi as well. If it game before something else like -game, then if the previous
+ // argument was -mpi and the current argument was something valid like -game, it would skip it.
+ else if ( !Q_strncasecmp( argv[i], "-mpi", 4 ) || !Q_strncasecmp( argv[i-1], "-mpi", 4 ) )
+ {
+ if ( stricmp( argv[i], "-mpi" ) == 0 )
+ g_bUseMPI = true;
+
+ // Any other args that start with -mpi are ok too.
+ if ( i == argc - 1 && V_stricmp( argv[i], "-mpi_ListParams" ) != 0 )
+ break;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return i;
+}
+
+
+void PrintCommandLine( int argc, char **argv )
+{
+ Warning( "Command line: " );
+ for ( int z=0; z < argc; z++ )
+ {
+ Warning( "\"%s\" ", argv[z] );
+ }
+ Warning( "\n\n" );
+}
+
+
+void PrintUsage( int argc, char **argv )
+{
+ PrintCommandLine( argc, argv );
+
+ Warning(
+ "usage : vrad [options...] bspfile\n"
+ "example: vrad c:\\hl2\\hl2\\maps\\test\n"
+ "\n"
+ "Common options:\n"
+ "\n"
+ " -v (or -verbose): Turn on verbose output (also shows more command\n"
+ " -bounce # : Set max number of bounces (default: 100).\n"
+ " -fast : Quick and dirty lighting.\n"
+ " -fastambient : Per-leaf ambient sampling is lower quality to save compute time.\n"
+ " -final : High quality processing. equivalent to -extrasky 16.\n"
+ " -extrasky n : trace N times as many rays for indirect light and sky ambient.\n"
+ " -low : Run as an idle-priority process.\n"
+ " -mpi : Use VMPI to distribute computations.\n"
+ " -rederror : Show errors in red.\n"
+ "\n"
+ " -vproject <directory> : Override the VPROJECT environment variable.\n"
+ " -game <directory> : Same as -vproject.\n"
+ "\n"
+ "Other options:\n"
+ " -novconfig : Don't bring up graphical UI on vproject errors.\n"
+ " -dump : Write debugging .txt files.\n"
+ " -dumpnormals : Write normals to debug files.\n"
+ " -dumptrace : Write ray-tracing environment to debug files.\n"
+ " -threads : Control the number of threads vbsp uses (defaults to the #\n"
+ " or processors on your machine).\n"
+ " -lights <file> : Load a lights file in addition to lights.rad and the\n"
+ " level lights file.\n"
+ " -noextra : Disable supersampling.\n"
+ " -debugextra : Places debugging data in lightmaps to visualize\n"
+ " supersampling.\n"
+ " -smooth # : Set the threshold for smoothing groups, in degrees\n"
+ " (default 45).\n"
+ " -dlightmap : Force direct lighting into different lightmap than\n"
+ " radiosity.\n"
+ " -stoponexit : Wait for a keypress on exit.\n"
+ " -mpi_pw <pw> : Use a password to choose a specific set of VMPI workers.\n"
+ " -nodetaillight : Don't light detail props.\n"
+ " -centersamples : Move sample centers.\n"
+ " -luxeldensity # : Rescale all luxels by the specified amount (default: 1.0).\n"
+ " The number specified must be less than 1.0 or it will be\n"
+ " ignored.\n"
+ " -loghash : Log the sample hash table to samplehash.txt.\n"
+ " -onlydetail : Only light detail props and per-leaf lighting.\n"
+ " -maxdispsamplesize #: Set max displacement sample size (default: 512).\n"
+ " -softsun <n> : Treat the sun as an area light source of size <n> degrees."
+ " Produces soft shadows.\n"
+ " Recommended values are between 0 and 5. Default is 0.\n"
+ " -FullMinidumps : Write large minidumps on crash.\n"
+ " -chop : Smallest number of luxel widths for a bounce patch, used on edges\n"
+ " -maxchop : Coarsest allowed number of luxel widths for a patch, used in face interiors\n"
+ "\n"
+ " -LargeDispSampleRadius: This can be used if there are splotches of bounced light\n"
+ " on terrain. The compile will take longer, but it will gather\n"
+ " light across a wider area.\n"
+ " -StaticPropLighting : generate backed static prop vertex lighting\n"
+ " -StaticPropPolys : Perform shadow tests of static props at polygon precision\n"
+ " -OnlyStaticProps : Only perform direct static prop lighting (vrad debug option)\n"
+ " -StaticPropNormals : when lighting static props, just show their normal vector\n"
+ " -textureshadows : Allows texture alpha channels to block light - rays intersecting alpha surfaces will sample the texture\n"
+ " -noskyboxrecurse : Turn off recursion into 3d skybox (skybox shadows on world)\n"
+ " -nossprops : Globally disable self-shadowing on static props\n"
+ "\n"
+#if 1 // Disabled for the initial SDK release with VMPI so we can get feedback from selected users.
+ );
+#else
+ " -mpi_ListParams : Show a list of VMPI parameters.\n"
+ "\n"
+ );
+
+ // Show VMPI parameters?
+ for ( int i=1; i < argc; i++ )
+ {
+ if ( V_stricmp( argv[i], "-mpi_ListParams" ) == 0 )
+ {
+ Warning( "VMPI-specific options:\n\n" );
+
+ bool bIsSDKMode = VMPI_IsSDKMode();
+ for ( int i=k_eVMPICmdLineParam_FirstParam+1; i < k_eVMPICmdLineParam_LastParam; i++ )
+ {
+ if ( (VMPI_GetParamFlags( (EVMPICmdLineParam)i ) & VMPI_PARAM_SDK_HIDDEN) && bIsSDKMode )
+ continue;
+
+ Warning( "[%s]\n", VMPI_GetParamString( (EVMPICmdLineParam)i ) );
+ Warning( VMPI_GetParamHelpString( (EVMPICmdLineParam)i ) );
+ Warning( "\n\n" );
+ }
+ break;
+ }
+ }
+#endif
+}
+
+
+int RunVRAD( int argc, char **argv )
+{
+#if defined(_MSC_VER) && ( _MSC_VER >= 1310 )
+ Msg("Valve Software - vrad.exe SSE (" __DATE__ ")\n" );
+#else
+ Msg("Valve Software - vrad.exe (" __DATE__ ")\n" );
+#endif
+
+ Msg("\n Valve Radiosity Simulator \n");
+
+ verbose = true; // Originally FALSE
+
+ bool onlydetail;
+ int i = ParseCommandLine( argc, argv, &onlydetail );
+ if (i != argc - 1)
+ {
+ PrintUsage( argc, argv );
+ DeleteCmdLine( argc, argv );
+ CmdLib_Exit( 1 );
+ }
+
+ VRAD_LoadBSP( argv[i] );
+
+ if ( (! onlydetail) && (! g_bOnlyStaticProps ) )
+ {
+ RadWorld_Go();
+ }
+
+ VRAD_ComputeOtherLighting();
+
+ VRAD_Finish();
+
+ VMPI_SetCurrentStage( "master done" );
+
+ DeleteCmdLine( argc, argv );
+ CmdLib_Cleanup();
+ return 0;
+}
+
+
+int VRAD_Main(int argc, char **argv)
+{
+ g_pFileSystem = NULL; // Safeguard against using it before it's properly initialized.
+
+ VRAD_Init();
+
+ // This must come first.
+ VRAD_SetupMPI( argc, argv );
+
+ // Initialize the filesystem, so additional commandline options can be loaded
+ Q_StripExtension( argv[ argc - 1 ], source, sizeof( source ) );
+ CmdLib_InitFileSystem( argv[ argc - 1 ] );
+ Q_FileBase( source, source, sizeof( source ) );
+
+#if !defined( _DEBUG )
+ if ( g_bUseMPI && !g_bMPIMaster )
+ {
+ SetupToolsMinidumpHandler( VMPI_ExceptionFilter );
+ }
+ else
+#endif
+ {
+ LoadCmdLineFromFile( argc, argv, source, "vrad" ); // Don't do this if we're a VMPI worker..
+ SetupDefaultToolsMinidumpHandler();
+ }
+
+ return RunVRAD( argc, argv );
+}
+
+
+
+
+
diff --git a/mp/src/utils/vrad/vrad.h b/mp/src/utils/vrad/vrad.h
index b39d66c8..95fcd151 100644
--- a/mp/src/utils/vrad/vrad.h
+++ b/mp/src/utils/vrad/vrad.h
@@ -1,610 +1,610 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $Workfile: $
-// $Date: $
-//
-//-----------------------------------------------------------------------------
-// $Log: $
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef VRAD_H
-#define VRAD_H
-#pragma once
-
-
-#include "commonmacros.h"
-#include "worldsize.h"
-#include "cmdlib.h"
-#include "mathlib/mathlib.h"
-#include "bsplib.h"
-#include "polylib.h"
-#include "threads.h"
-#include "builddisp.h"
-#include "VRAD_DispColl.h"
-#include "UtlMemory.h"
-#include "UtlHash.h"
-#include "utlvector.h"
-#include "iincremental.h"
-#include "raytrace.h"
-
-
-#ifdef _WIN32
-#include <windows.h>
-#endif
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#pragma warning(disable: 4142 4028)
-#include <io.h>
-#pragma warning(default: 4142 4028)
-
-#include <fcntl.h>
-#include <direct.h>
-#include <ctype.h>
-
-
-// Can remove these options if they don't generate problems.
-//#define SAMPLEHASH_USE_AREA_PATCHES // Add patches to sample hash based on their AABB instead of as a single point.
-#define SAMPLEHASH_QUERY_ONCE // Big optimization - causes way less sample hash queries.
-
-extern float dispchop; // "-dispchop" tightest number of luxel widths for a patch, used on edges
-extern float g_MaxDispPatchRadius;
-
-//-----------------------------------------------------------------------------
-// forward declarations
-//-----------------------------------------------------------------------------
-
-struct Ray_t;
-
-#define TRANSFER_EPSILON 0.0000001
-
-struct directlight_t
-{
- int index;
-
- directlight_t *next;
- dworldlight_t light;
-
- byte *pvs; // accumulated domain of the light
- int facenum; // domain of attached lights
- int texdata; // texture source of traced lights
-
- Vector snormal;
- Vector tnormal;
- float sscale;
- float tscale;
- float soffset;
- float toffset;
-
- int dorecalc; // position, vector, spot angle, etc.
- IncrementalLightID m_IncrementalID;
-
- // hard-falloff lights (lights that fade to an actual zero). between m_flStartFadeDistance and
- // m_flEndFadeDistance, a smoothstep to zero will be done, so that the light goes to zero at
- // the end.
- float m_flStartFadeDistance;
- float m_flEndFadeDistance;
- float m_flCapDist; // max distance to feed in
-
- directlight_t(void)
- {
- m_flEndFadeDistance = -1.0; // end<start indicates not set
- m_flStartFadeDistance= 0.0;
- m_flCapDist = 1.0e22;
-
- }
-};
-
-struct bumplights_t
-{
- Vector light[NUM_BUMP_VECTS+1];
-};
-
-
-struct transfer_t
-{
- int patch;
- float transfer;
-};
-
-
-struct LightingValue_t
-{
- Vector m_vecLighting;
- float m_flDirectSunAmount;
-
- FORCEINLINE bool IsValid( void ) const
- {
- return ( m_vecLighting.x >= 0 &&
- m_vecLighting.y >= 0 &&
- m_vecLighting.z >= 0 &&
- m_vecLighting.x < 1e10 &&
- m_vecLighting.y < 1e10 &&
- m_vecLighting.z < 1e10 );
- }
-
- FORCEINLINE void Zero( void )
- {
- m_vecLighting.Init( 0, 0, 0 );
- m_flDirectSunAmount = 0.0;
- }
-
- FORCEINLINE void Scale( float m_flScale )
- {
- m_vecLighting *= m_flScale;
- m_flDirectSunAmount *= m_flScale;
- }
-
- FORCEINLINE void AddWeighted( LightingValue_t const &src, float flWeight )
- {
- m_vecLighting += flWeight * src.m_vecLighting;
- m_flDirectSunAmount += flWeight * src.m_flDirectSunAmount;
- }
-
- FORCEINLINE void AddWeighted( Vector const &src, float flWeight )
- {
- m_vecLighting += flWeight * src;
- }
-
- FORCEINLINE float Intensity( void ) const
- {
- return m_vecLighting.x + m_vecLighting.y + m_vecLighting.z;
- }
-
- FORCEINLINE void AddLight( float flAmount, Vector const &vecColor, float flSunAmount = 0.0 )
- {
- VectorMA( m_vecLighting, flAmount, vecColor, m_vecLighting );
- m_flDirectSunAmount += flSunAmount;
- Assert( this->IsValid() );
- }
-
-
- FORCEINLINE void AddLight( LightingValue_t const &src )
- {
- m_vecLighting += src.m_vecLighting;
- m_flDirectSunAmount += src.m_flDirectSunAmount;
- Assert( this->IsValid() );
- }
-
- FORCEINLINE void Init( float x, float y, float z )
- {
- m_vecLighting.Init( x, y, z );
- m_flDirectSunAmount = 0.0;
- }
-
-
-};
-
-
-#define MAX_PATCHES (4*65536)
-
-struct CPatch
-{
- winding_t *winding;
- Vector mins, maxs, face_mins, face_maxs;
-
- Vector origin; // adjusted off face by face normal
-
- dplane_t *plane; // plane (corrected for facing)
-
- unsigned short m_IterationKey; // Used to prevent touching the same patch multiple times in the same query.
- // See IncrementPatchIterationKey().
-
- // these are packed into one dword
- unsigned int normalMajorAxis : 2; // the major axis of base face normal
- unsigned int sky : 1;
- unsigned int needsBumpmap : 1;
- unsigned int pad : 28;
-
- Vector normal; // adjusted for phong shading
-
- float planeDist; // Fixes up patch planes for brush models with an origin brush
-
- float chop; // smallest acceptable width of patch face
- float luxscale; // average luxels per world coord
- float scale[2]; // Scaling of texture in s & t
-
- bumplights_t totallight; // accumulated by radiosity
- // does NOT include light
- // accounted for by direct lighting
- Vector baselight; // emissivity only
- float basearea; // surface per area per baselight instance
-
- Vector directlight; // direct light value
- float area;
-
- Vector reflectivity; // Average RGB of texture, modified by material type.
-
- Vector samplelight;
- float samplearea; // for averaging direct light
- int faceNumber;
- int clusterNumber;
-
- int parent; // patch index of parent
- int child1; // patch index for children
- int child2;
-
- int ndxNext; // next patch index in face
- int ndxNextParent; // next parent patch index in face
- int ndxNextClusterChild; // next terminal child index in cluster
-// struct patch_s *next; // next in face
-// struct patch_s *nextparent; // next in face
-// struct patch_s *nextclusterchild; // next terminal child in cluster
-
- int numtransfers;
- transfer_t *transfers;
-
- short indices[3]; // displacement use these for subdivision
-};
-
-
-extern CUtlVector<CPatch> g_Patches;
-extern CUtlVector<int> g_FacePatches; // constains all patches, children first
-extern CUtlVector<int> faceParents; // contains only root patches, use next parent to iterate
-extern CUtlVector<int> clusterChildren;
-
-
-struct sky_camera_t
-{
- Vector origin;
- float world_to_sky;
- float sky_to_world;
- int area;
-};
-
-extern int num_sky_cameras;
-extern sky_camera_t sky_cameras[MAX_MAP_AREAS];
-extern int area_sky_cameras[MAX_MAP_AREAS];
-void ProcessSkyCameras();
-
-extern entity_t *face_entity[MAX_MAP_FACES];
-extern Vector face_offset[MAX_MAP_FACES]; // for rotating bmodels
-extern Vector face_centroids[MAX_MAP_EDGES];
-extern int leafparents[MAX_MAP_LEAFS];
-extern int nodeparents[MAX_MAP_NODES];
-extern float lightscale;
-extern float dlight_threshold;
-extern float coring;
-extern qboolean g_bDumpPatches;
-extern bool bRed2Black;
-extern bool g_bNoSkyRecurse;
-extern bool bDumpNormals;
-extern bool g_bFastAmbient;
-extern float maxchop;
-extern FileHandle_t pFileSamples[4][4];
-extern qboolean g_bLowPriority;
-extern qboolean do_fast;
-extern bool g_bInterrupt; // Was used with background lighting in WC. Tells VRAD to stop lighting.
-extern IIncremental *g_pIncremental; // null if not doing incremental lighting
-
-extern float g_flSkySampleScale; // extra sampling factor for indirect light
-
-extern bool g_bLargeDispSampleRadius;
-extern bool g_bStaticPropPolys;
-extern bool g_bTextureShadows;
-extern bool g_bShowStaticPropNormals;
-extern bool g_bDisablePropSelfShadowing;
-
-extern CUtlVector<char const *> g_NonShadowCastingMaterialStrings;
-extern void ForceTextureShadowsOnModel( const char *pModelName );
-extern bool IsModelTextureShadowsForced( const char *pModelName );
-
-// Raytracing
-
-#define TRACE_ID_SKY 0x01000000 // sky face ray blocker
-#define TRACE_ID_OPAQUE 0x02000000 // everyday light blocking face
-#define TRACE_ID_STATICPROP 0x04000000 // static prop - lower bits are prop ID
-extern RayTracingEnvironment g_RtEnv;
-
-#include "mpivrad.h"
-
-void MakeShadowSplits (void);
-
-//==============================================
-
-void BuildVisMatrix (void);
-void BuildClusterTable( void );
-void AddDispsToClusterTable( void );
-void FreeVisMatrix (void);
-// qboolean CheckVisBit (unsigned int p1, unsigned int p2);
-void TouchVMFFile (void);
-
-//==============================================
-
-extern qboolean do_extra;
-extern qboolean do_fast;
-extern qboolean do_centersamples;
-extern int extrapasses;
-extern Vector ambient;
-extern float maxlight;
-extern unsigned numbounce;
-extern qboolean g_bLogHashData;
-extern bool debug_extra;
-extern directlight_t *activelights;
-extern directlight_t *freelights;
-
-// because of hdr having two face lumps (light styles can cause them to be different, among other
-// things), we need to always access (r/w) face data though this pointer
-extern dface_t *g_pFaces;
-
-
-extern bool g_bMPIProps;
-
-extern byte nodehit[MAX_MAP_NODES];
-extern float gamma_value;
-extern float indirect_sun;
-extern float smoothing_threshold;
-extern int dlight_map;
-
-extern float g_flMaxDispSampleSize;
-extern float g_SunAngularExtent;
-
-extern char source[MAX_PATH];
-
-// Used by incremental lighting to trivial-reject faces.
-// There is a bit in here for each face telling whether or not any of the
-// active lights can see the face.
-extern CUtlVector<byte> g_FacesVisibleToLights;
-
-void MakeTnodes (dmodel_t *bm);
-void PairEdges (void);
-
-void SaveVertexNormals( void );
-
-qboolean IsIncremental(char *filename);
-int SaveIncremental(char *filename);
-int PartialHead (void);
-void BuildFacelights (int facenum, int threadnum);
-void PrecompLightmapOffsets();
-void FinalLightFace (int threadnum, int facenum);
-void PvsForOrigin (Vector& org, byte *pvs);
-void ConvertRGBExp32ToRGBA8888( const ColorRGBExp32 *pSrc, unsigned char *pDst );
-
-inline byte PVSCheck( const byte *pvs, int iCluster )
-{
- if ( iCluster >= 0 )
- {
- return pvs[iCluster >> 3] & ( 1 << ( iCluster & 7 ) );
- }
- else
- {
- // PointInLeaf still returns -1 for valid points sometimes and rather than
- // have black samples, we assume the sample is in the PVS.
- return 1;
- }
-}
-
-// outputs 1 in fractionVisible if no occlusion, 0 if full occlusion, and in-between values
-void TestLine( FourVectors const& start, FourVectors const& stop, fltx4 *pFractionVisible, int static_prop_index_to_ignore=-1);
-
-// returns 1 if the ray sees the sky, 0 if it doesn't, and in-between values for partial coverage
-void TestLine_DoesHitSky( FourVectors const& start, FourVectors const& stop,
- fltx4 *pFractionVisible, bool canRecurse = true, int static_prop_to_skip=-1, bool bDoDebug = false );
-
-// converts any marked brush entities to triangles for shadow casting
-void ExtractBrushEntityShadowCasters ( void );
-void AddBrushesForRayTrace ( void );
-
-void BaseLightForFace( dface_t *f, Vector& light, float *parea, Vector& reflectivity );
-void CreateDirectLights (void);
-void GetPhongNormal( int facenum, Vector const& spot, Vector& phongnormal );
-int LightForString( char *pLight, Vector& intensity );
-void MakeTransfer( int ndxPatch1, int ndxPatch2, transfer_t *all_transfers );
-void MakeScales( int ndxPatch, transfer_t *all_transfers );
-
-// Run startup code like initialize mathlib.
-void VRAD_Init();
-
-// Load the BSP file and prepare to do the lighting.
-// This is called after any command-line parameters have been set.
-void VRAD_LoadBSP( char const *pFilename );
-
-int VRAD_Main(int argc, char **argv);
-
-// This performs an actual lighting pass.
-// Returns true if the process was interrupted (with g_bInterrupt).
-bool RadWorld_Go();
-
-dleaf_t *PointInLeaf (Vector const& point);
-int ClusterFromPoint( Vector const& point );
-winding_t *WindingFromFace (dface_t *f, Vector& origin );
-
-void WriteWinding (FileHandle_t out, winding_t *w, Vector& color );
-void WriteNormal( FileHandle_t out, Vector const &nPos, Vector const &nDir,
- float length, Vector const &color );
-void WriteLine( FileHandle_t out, const Vector &vecPos1, const Vector &vecPos2, const Vector &color );
-void WriteTrace( const char *pFileName, const FourRays &rays, const RayTracingResult& result );
-
-#ifdef STATIC_FOG
-qboolean IsFog( dface_t * f );
-#endif
-
-#define CONTENTS_EMPTY 0
-#define TEX_SPECIAL (SURF_SKY|SURF_NOLIGHT)
-
-//=============================================================================
-
-// trace.cpp
-
-bool AddDispCollTreesToWorld( void );
-int PointLeafnum( Vector const &point );
-float TraceLeafBrushes( int leafIndex, const Vector &start, const Vector &end, CBaseTrace &traceOut );
-
-//=============================================================================
-
-// dispinfo.cpp
-
-struct SSE_sampleLightOutput_t
-{
- fltx4 m_flDot[NUM_BUMP_VECTS+1];
- fltx4 m_flFalloff;
- fltx4 m_flSunAmount;
-};
-
-#define GATHERLFLAGS_FORCE_FAST 1
-#define GATHERLFLAGS_IGNORE_NORMALS 2
-
-// SSE Gather light stuff
-void GatherSampleLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
- FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
- int nLFlags = 0, // GATHERLFLAGS_xxx
- int static_prop_to_skip=-1,
- float flEpsilon = 0.0 );
-//void GatherSampleSkyLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
-// FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
-// int nLFlags = 0,
-// int static_prop_to_skip=-1,
-// float flEpsilon = 0.0 );
-//void GatherSampleAmbientSkySSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
-// FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
-// int nLFlags = 0, // GATHERLFLAGS_xxx
-// int static_prop_to_skip=-1,
-// float flEpsilon = 0.0 );
-//void GatherSampleStandardLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
-// FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
-// int nLFlags = 0, // GATHERLFLAGS_xxx
-// int static_prop_to_skip=-1,
-// float flEpsilon = 0.0 );
-
-//-----------------------------------------------------------------------------
-// VRad Displacements
-//-----------------------------------------------------------------------------
-
-struct facelight_t;
-typedef struct radial_s radial_t;
-struct lightinfo_t;
-
-// NOTE: should probably come up with a bsptreetested_t struct or something,
-// see below (PropTested_t)
-struct DispTested_t
-{
- int m_Enum;
- int *m_pTested;
-};
-
-class IVRadDispMgr
-{
-public:
- // creation/destruction
- virtual void Init( void ) = 0;
- virtual void Shutdown( void ) = 0;
-
- // "CalcPoints"
- virtual bool BuildDispSamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace ) = 0;
- virtual bool BuildDispLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace ) = 0;
- virtual bool BuildDispSamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace ) = 0;
-
- // patching functions
- virtual void MakePatches( void ) = 0;
- virtual void SubdividePatch( int iPatch ) = 0;
-
- // pre "FinalLightFace"
- virtual void InsertSamplesDataIntoHashTable( void ) = 0;
- virtual void InsertPatchSampleDataIntoHashTable( void ) = 0;
-
- // "FinalLightFace"
- virtual radial_t *BuildLuxelRadial( int ndxFace, int ndxStyle, bool bBump ) = 0;
- virtual bool SampleRadial( int ndxFace, radial_t *pRadial, Vector const &vPos, int ndxLxl, LightingValue_t *pLightSample, int sampleCount, bool bPatch ) = 0;
- virtual radial_t *BuildPatchRadial( int ndxFace, bool bBump ) = 0;
-
- // utility
- virtual void GetDispSurfNormal( int ndxFace, Vector &pt, Vector &ptNormal, bool bInside ) = 0;
- virtual void GetDispSurf( int ndxFace, CVRADDispColl **ppDispTree ) = 0;
-
- // bsp tree functions
- virtual bool ClipRayToDisp( DispTested_t &dispTested, Ray_t const &ray ) = 0;
- virtual bool ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf ) = 0;
- virtual void ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
- int ndxLeaf, float& dist, dface_t*& pFace, Vector2D& luxelCoord ) = 0;
- virtual void ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
- int ndxLeaf, float& dist, Vector *pNormal ) = 0;
- virtual void StartRayTest( DispTested_t &dispTested ) = 0;
- virtual void AddPolysForRayTrace() = 0;
-
- // general timing -- should be moved!!
- virtual void StartTimer( const char *name ) = 0;
- virtual void EndTimer( void ) = 0;
-};
-
-IVRadDispMgr *StaticDispMgr( void );
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-inline bool ValidDispFace( dface_t *pFace )
-{
- if( !pFace ) { return false; }
- if( pFace->dispinfo == -1 ) { return false; }
- if( pFace->numedges != 4 ) { return false; }
-
- return true;
-}
-
-#define SAMPLEHASH_VOXEL_SIZE 64.0f
-typedef unsigned int SampleHandle_t; // the upper 16 bits = facelight index (works because max face are 65536)
- // the lower 16 bits = sample index inside of facelight
-struct sample_t;
-struct SampleData_t
-{
- unsigned short x, y, z;
- CUtlVector<SampleHandle_t> m_Samples;
-};
-
-struct PatchSampleData_t
-{
- unsigned short x, y, z;
- CUtlVector<int> m_ndxPatches;
-};
-
-UtlHashHandle_t SampleData_AddSample( sample_t *pSample, SampleHandle_t sampleHandle );
-void PatchSampleData_AddSample( CPatch *pPatch, int ndxPatch );
-unsigned short IncrementPatchIterationKey();
-void SampleData_Log( void );
-
-extern CUtlHash<SampleData_t> g_SampleHashTable;
-extern CUtlHash<PatchSampleData_t> g_PatchSampleHashTable;
-
-extern int samplesAdded;
-extern int patchSamplesAdded;
-
-//-----------------------------------------------------------------------------
-// Computes lighting for the detail props
-//-----------------------------------------------------------------------------
-
-void ComputeDetailPropLighting( int iThread );
-void ComputeIndirectLightingAtPoint( Vector &position, Vector &normal, Vector &outColor,
- int iThread, bool force_fast = false, bool bIgnoreNormals = false );
-
-//-----------------------------------------------------------------------------
-// VRad static props
-//-----------------------------------------------------------------------------
-class IPhysicsCollision;
-struct PropTested_t
-{
- int m_Enum;
- int* m_pTested;
- IPhysicsCollision *pThreadedCollision;
-};
-
-class IVradStaticPropMgr
-{
-public:
- // methods of IStaticPropMgr
- virtual void Init() = 0;
- virtual void Shutdown() = 0;
- virtual void ComputeLighting( int iThread ) = 0;
- virtual void AddPolysForRayTrace() = 0;
-};
-
-//extern PropTested_t s_PropTested[MAX_TOOL_THREADS+1];
-extern DispTested_t s_DispTested[MAX_TOOL_THREADS+1];
-
-IVradStaticPropMgr* StaticPropMgr();
-
-extern float ComputeCoverageFromTexture( float b0, float b1, float b2, int32 hitID );
-
-#endif // VRAD_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef VRAD_H
+#define VRAD_H
+#pragma once
+
+
+#include "commonmacros.h"
+#include "worldsize.h"
+#include "cmdlib.h"
+#include "mathlib/mathlib.h"
+#include "bsplib.h"
+#include "polylib.h"
+#include "threads.h"
+#include "builddisp.h"
+#include "VRAD_DispColl.h"
+#include "UtlMemory.h"
+#include "UtlHash.h"
+#include "utlvector.h"
+#include "iincremental.h"
+#include "raytrace.h"
+
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#pragma warning(disable: 4142 4028)
+#include <io.h>
+#pragma warning(default: 4142 4028)
+
+#include <fcntl.h>
+#include <direct.h>
+#include <ctype.h>
+
+
+// Can remove these options if they don't generate problems.
+//#define SAMPLEHASH_USE_AREA_PATCHES // Add patches to sample hash based on their AABB instead of as a single point.
+#define SAMPLEHASH_QUERY_ONCE // Big optimization - causes way less sample hash queries.
+
+extern float dispchop; // "-dispchop" tightest number of luxel widths for a patch, used on edges
+extern float g_MaxDispPatchRadius;
+
+//-----------------------------------------------------------------------------
+// forward declarations
+//-----------------------------------------------------------------------------
+
+struct Ray_t;
+
+#define TRANSFER_EPSILON 0.0000001
+
+struct directlight_t
+{
+ int index;
+
+ directlight_t *next;
+ dworldlight_t light;
+
+ byte *pvs; // accumulated domain of the light
+ int facenum; // domain of attached lights
+ int texdata; // texture source of traced lights
+
+ Vector snormal;
+ Vector tnormal;
+ float sscale;
+ float tscale;
+ float soffset;
+ float toffset;
+
+ int dorecalc; // position, vector, spot angle, etc.
+ IncrementalLightID m_IncrementalID;
+
+ // hard-falloff lights (lights that fade to an actual zero). between m_flStartFadeDistance and
+ // m_flEndFadeDistance, a smoothstep to zero will be done, so that the light goes to zero at
+ // the end.
+ float m_flStartFadeDistance;
+ float m_flEndFadeDistance;
+ float m_flCapDist; // max distance to feed in
+
+ directlight_t(void)
+ {
+ m_flEndFadeDistance = -1.0; // end<start indicates not set
+ m_flStartFadeDistance= 0.0;
+ m_flCapDist = 1.0e22;
+
+ }
+};
+
+struct bumplights_t
+{
+ Vector light[NUM_BUMP_VECTS+1];
+};
+
+
+struct transfer_t
+{
+ int patch;
+ float transfer;
+};
+
+
+struct LightingValue_t
+{
+ Vector m_vecLighting;
+ float m_flDirectSunAmount;
+
+ FORCEINLINE bool IsValid( void ) const
+ {
+ return ( m_vecLighting.x >= 0 &&
+ m_vecLighting.y >= 0 &&
+ m_vecLighting.z >= 0 &&
+ m_vecLighting.x < 1e10 &&
+ m_vecLighting.y < 1e10 &&
+ m_vecLighting.z < 1e10 );
+ }
+
+ FORCEINLINE void Zero( void )
+ {
+ m_vecLighting.Init( 0, 0, 0 );
+ m_flDirectSunAmount = 0.0;
+ }
+
+ FORCEINLINE void Scale( float m_flScale )
+ {
+ m_vecLighting *= m_flScale;
+ m_flDirectSunAmount *= m_flScale;
+ }
+
+ FORCEINLINE void AddWeighted( LightingValue_t const &src, float flWeight )
+ {
+ m_vecLighting += flWeight * src.m_vecLighting;
+ m_flDirectSunAmount += flWeight * src.m_flDirectSunAmount;
+ }
+
+ FORCEINLINE void AddWeighted( Vector const &src, float flWeight )
+ {
+ m_vecLighting += flWeight * src;
+ }
+
+ FORCEINLINE float Intensity( void ) const
+ {
+ return m_vecLighting.x + m_vecLighting.y + m_vecLighting.z;
+ }
+
+ FORCEINLINE void AddLight( float flAmount, Vector const &vecColor, float flSunAmount = 0.0 )
+ {
+ VectorMA( m_vecLighting, flAmount, vecColor, m_vecLighting );
+ m_flDirectSunAmount += flSunAmount;
+ Assert( this->IsValid() );
+ }
+
+
+ FORCEINLINE void AddLight( LightingValue_t const &src )
+ {
+ m_vecLighting += src.m_vecLighting;
+ m_flDirectSunAmount += src.m_flDirectSunAmount;
+ Assert( this->IsValid() );
+ }
+
+ FORCEINLINE void Init( float x, float y, float z )
+ {
+ m_vecLighting.Init( x, y, z );
+ m_flDirectSunAmount = 0.0;
+ }
+
+
+};
+
+
+#define MAX_PATCHES (4*65536)
+
+struct CPatch
+{
+ winding_t *winding;
+ Vector mins, maxs, face_mins, face_maxs;
+
+ Vector origin; // adjusted off face by face normal
+
+ dplane_t *plane; // plane (corrected for facing)
+
+ unsigned short m_IterationKey; // Used to prevent touching the same patch multiple times in the same query.
+ // See IncrementPatchIterationKey().
+
+ // these are packed into one dword
+ unsigned int normalMajorAxis : 2; // the major axis of base face normal
+ unsigned int sky : 1;
+ unsigned int needsBumpmap : 1;
+ unsigned int pad : 28;
+
+ Vector normal; // adjusted for phong shading
+
+ float planeDist; // Fixes up patch planes for brush models with an origin brush
+
+ float chop; // smallest acceptable width of patch face
+ float luxscale; // average luxels per world coord
+ float scale[2]; // Scaling of texture in s & t
+
+ bumplights_t totallight; // accumulated by radiosity
+ // does NOT include light
+ // accounted for by direct lighting
+ Vector baselight; // emissivity only
+ float basearea; // surface per area per baselight instance
+
+ Vector directlight; // direct light value
+ float area;
+
+ Vector reflectivity; // Average RGB of texture, modified by material type.
+
+ Vector samplelight;
+ float samplearea; // for averaging direct light
+ int faceNumber;
+ int clusterNumber;
+
+ int parent; // patch index of parent
+ int child1; // patch index for children
+ int child2;
+
+ int ndxNext; // next patch index in face
+ int ndxNextParent; // next parent patch index in face
+ int ndxNextClusterChild; // next terminal child index in cluster
+// struct patch_s *next; // next in face
+// struct patch_s *nextparent; // next in face
+// struct patch_s *nextclusterchild; // next terminal child in cluster
+
+ int numtransfers;
+ transfer_t *transfers;
+
+ short indices[3]; // displacement use these for subdivision
+};
+
+
+extern CUtlVector<CPatch> g_Patches;
+extern CUtlVector<int> g_FacePatches; // constains all patches, children first
+extern CUtlVector<int> faceParents; // contains only root patches, use next parent to iterate
+extern CUtlVector<int> clusterChildren;
+
+
+struct sky_camera_t
+{
+ Vector origin;
+ float world_to_sky;
+ float sky_to_world;
+ int area;
+};
+
+extern int num_sky_cameras;
+extern sky_camera_t sky_cameras[MAX_MAP_AREAS];
+extern int area_sky_cameras[MAX_MAP_AREAS];
+void ProcessSkyCameras();
+
+extern entity_t *face_entity[MAX_MAP_FACES];
+extern Vector face_offset[MAX_MAP_FACES]; // for rotating bmodels
+extern Vector face_centroids[MAX_MAP_EDGES];
+extern int leafparents[MAX_MAP_LEAFS];
+extern int nodeparents[MAX_MAP_NODES];
+extern float lightscale;
+extern float dlight_threshold;
+extern float coring;
+extern qboolean g_bDumpPatches;
+extern bool bRed2Black;
+extern bool g_bNoSkyRecurse;
+extern bool bDumpNormals;
+extern bool g_bFastAmbient;
+extern float maxchop;
+extern FileHandle_t pFileSamples[4][4];
+extern qboolean g_bLowPriority;
+extern qboolean do_fast;
+extern bool g_bInterrupt; // Was used with background lighting in WC. Tells VRAD to stop lighting.
+extern IIncremental *g_pIncremental; // null if not doing incremental lighting
+
+extern float g_flSkySampleScale; // extra sampling factor for indirect light
+
+extern bool g_bLargeDispSampleRadius;
+extern bool g_bStaticPropPolys;
+extern bool g_bTextureShadows;
+extern bool g_bShowStaticPropNormals;
+extern bool g_bDisablePropSelfShadowing;
+
+extern CUtlVector<char const *> g_NonShadowCastingMaterialStrings;
+extern void ForceTextureShadowsOnModel( const char *pModelName );
+extern bool IsModelTextureShadowsForced( const char *pModelName );
+
+// Raytracing
+
+#define TRACE_ID_SKY 0x01000000 // sky face ray blocker
+#define TRACE_ID_OPAQUE 0x02000000 // everyday light blocking face
+#define TRACE_ID_STATICPROP 0x04000000 // static prop - lower bits are prop ID
+extern RayTracingEnvironment g_RtEnv;
+
+#include "mpivrad.h"
+
+void MakeShadowSplits (void);
+
+//==============================================
+
+void BuildVisMatrix (void);
+void BuildClusterTable( void );
+void AddDispsToClusterTable( void );
+void FreeVisMatrix (void);
+// qboolean CheckVisBit (unsigned int p1, unsigned int p2);
+void TouchVMFFile (void);
+
+//==============================================
+
+extern qboolean do_extra;
+extern qboolean do_fast;
+extern qboolean do_centersamples;
+extern int extrapasses;
+extern Vector ambient;
+extern float maxlight;
+extern unsigned numbounce;
+extern qboolean g_bLogHashData;
+extern bool debug_extra;
+extern directlight_t *activelights;
+extern directlight_t *freelights;
+
+// because of hdr having two face lumps (light styles can cause them to be different, among other
+// things), we need to always access (r/w) face data though this pointer
+extern dface_t *g_pFaces;
+
+
+extern bool g_bMPIProps;
+
+extern byte nodehit[MAX_MAP_NODES];
+extern float gamma_value;
+extern float indirect_sun;
+extern float smoothing_threshold;
+extern int dlight_map;
+
+extern float g_flMaxDispSampleSize;
+extern float g_SunAngularExtent;
+
+extern char source[MAX_PATH];
+
+// Used by incremental lighting to trivial-reject faces.
+// There is a bit in here for each face telling whether or not any of the
+// active lights can see the face.
+extern CUtlVector<byte> g_FacesVisibleToLights;
+
+void MakeTnodes (dmodel_t *bm);
+void PairEdges (void);
+
+void SaveVertexNormals( void );
+
+qboolean IsIncremental(char *filename);
+int SaveIncremental(char *filename);
+int PartialHead (void);
+void BuildFacelights (int facenum, int threadnum);
+void PrecompLightmapOffsets();
+void FinalLightFace (int threadnum, int facenum);
+void PvsForOrigin (Vector& org, byte *pvs);
+void ConvertRGBExp32ToRGBA8888( const ColorRGBExp32 *pSrc, unsigned char *pDst );
+
+inline byte PVSCheck( const byte *pvs, int iCluster )
+{
+ if ( iCluster >= 0 )
+ {
+ return pvs[iCluster >> 3] & ( 1 << ( iCluster & 7 ) );
+ }
+ else
+ {
+ // PointInLeaf still returns -1 for valid points sometimes and rather than
+ // have black samples, we assume the sample is in the PVS.
+ return 1;
+ }
+}
+
+// outputs 1 in fractionVisible if no occlusion, 0 if full occlusion, and in-between values
+void TestLine( FourVectors const& start, FourVectors const& stop, fltx4 *pFractionVisible, int static_prop_index_to_ignore=-1);
+
+// returns 1 if the ray sees the sky, 0 if it doesn't, and in-between values for partial coverage
+void TestLine_DoesHitSky( FourVectors const& start, FourVectors const& stop,
+ fltx4 *pFractionVisible, bool canRecurse = true, int static_prop_to_skip=-1, bool bDoDebug = false );
+
+// converts any marked brush entities to triangles for shadow casting
+void ExtractBrushEntityShadowCasters ( void );
+void AddBrushesForRayTrace ( void );
+
+void BaseLightForFace( dface_t *f, Vector& light, float *parea, Vector& reflectivity );
+void CreateDirectLights (void);
+void GetPhongNormal( int facenum, Vector const& spot, Vector& phongnormal );
+int LightForString( char *pLight, Vector& intensity );
+void MakeTransfer( int ndxPatch1, int ndxPatch2, transfer_t *all_transfers );
+void MakeScales( int ndxPatch, transfer_t *all_transfers );
+
+// Run startup code like initialize mathlib.
+void VRAD_Init();
+
+// Load the BSP file and prepare to do the lighting.
+// This is called after any command-line parameters have been set.
+void VRAD_LoadBSP( char const *pFilename );
+
+int VRAD_Main(int argc, char **argv);
+
+// This performs an actual lighting pass.
+// Returns true if the process was interrupted (with g_bInterrupt).
+bool RadWorld_Go();
+
+dleaf_t *PointInLeaf (Vector const& point);
+int ClusterFromPoint( Vector const& point );
+winding_t *WindingFromFace (dface_t *f, Vector& origin );
+
+void WriteWinding (FileHandle_t out, winding_t *w, Vector& color );
+void WriteNormal( FileHandle_t out, Vector const &nPos, Vector const &nDir,
+ float length, Vector const &color );
+void WriteLine( FileHandle_t out, const Vector &vecPos1, const Vector &vecPos2, const Vector &color );
+void WriteTrace( const char *pFileName, const FourRays &rays, const RayTracingResult& result );
+
+#ifdef STATIC_FOG
+qboolean IsFog( dface_t * f );
+#endif
+
+#define CONTENTS_EMPTY 0
+#define TEX_SPECIAL (SURF_SKY|SURF_NOLIGHT)
+
+//=============================================================================
+
+// trace.cpp
+
+bool AddDispCollTreesToWorld( void );
+int PointLeafnum( Vector const &point );
+float TraceLeafBrushes( int leafIndex, const Vector &start, const Vector &end, CBaseTrace &traceOut );
+
+//=============================================================================
+
+// dispinfo.cpp
+
+struct SSE_sampleLightOutput_t
+{
+ fltx4 m_flDot[NUM_BUMP_VECTS+1];
+ fltx4 m_flFalloff;
+ fltx4 m_flSunAmount;
+};
+
+#define GATHERLFLAGS_FORCE_FAST 1
+#define GATHERLFLAGS_IGNORE_NORMALS 2
+
+// SSE Gather light stuff
+void GatherSampleLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+ FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+ int nLFlags = 0, // GATHERLFLAGS_xxx
+ int static_prop_to_skip=-1,
+ float flEpsilon = 0.0 );
+//void GatherSampleSkyLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+// FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+// int nLFlags = 0,
+// int static_prop_to_skip=-1,
+// float flEpsilon = 0.0 );
+//void GatherSampleAmbientSkySSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+// FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+// int nLFlags = 0, // GATHERLFLAGS_xxx
+// int static_prop_to_skip=-1,
+// float flEpsilon = 0.0 );
+//void GatherSampleStandardLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+// FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+// int nLFlags = 0, // GATHERLFLAGS_xxx
+// int static_prop_to_skip=-1,
+// float flEpsilon = 0.0 );
+
+//-----------------------------------------------------------------------------
+// VRad Displacements
+//-----------------------------------------------------------------------------
+
+struct facelight_t;
+typedef struct radial_s radial_t;
+struct lightinfo_t;
+
+// NOTE: should probably come up with a bsptreetested_t struct or something,
+// see below (PropTested_t)
+struct DispTested_t
+{
+ int m_Enum;
+ int *m_pTested;
+};
+
+class IVRadDispMgr
+{
+public:
+ // creation/destruction
+ virtual void Init( void ) = 0;
+ virtual void Shutdown( void ) = 0;
+
+ // "CalcPoints"
+ virtual bool BuildDispSamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace ) = 0;
+ virtual bool BuildDispLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace ) = 0;
+ virtual bool BuildDispSamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace ) = 0;
+
+ // patching functions
+ virtual void MakePatches( void ) = 0;
+ virtual void SubdividePatch( int iPatch ) = 0;
+
+ // pre "FinalLightFace"
+ virtual void InsertSamplesDataIntoHashTable( void ) = 0;
+ virtual void InsertPatchSampleDataIntoHashTable( void ) = 0;
+
+ // "FinalLightFace"
+ virtual radial_t *BuildLuxelRadial( int ndxFace, int ndxStyle, bool bBump ) = 0;
+ virtual bool SampleRadial( int ndxFace, radial_t *pRadial, Vector const &vPos, int ndxLxl, LightingValue_t *pLightSample, int sampleCount, bool bPatch ) = 0;
+ virtual radial_t *BuildPatchRadial( int ndxFace, bool bBump ) = 0;
+
+ // utility
+ virtual void GetDispSurfNormal( int ndxFace, Vector &pt, Vector &ptNormal, bool bInside ) = 0;
+ virtual void GetDispSurf( int ndxFace, CVRADDispColl **ppDispTree ) = 0;
+
+ // bsp tree functions
+ virtual bool ClipRayToDisp( DispTested_t &dispTested, Ray_t const &ray ) = 0;
+ virtual bool ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf ) = 0;
+ virtual void ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
+ int ndxLeaf, float& dist, dface_t*& pFace, Vector2D& luxelCoord ) = 0;
+ virtual void ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
+ int ndxLeaf, float& dist, Vector *pNormal ) = 0;
+ virtual void StartRayTest( DispTested_t &dispTested ) = 0;
+ virtual void AddPolysForRayTrace() = 0;
+
+ // general timing -- should be moved!!
+ virtual void StartTimer( const char *name ) = 0;
+ virtual void EndTimer( void ) = 0;
+};
+
+IVRadDispMgr *StaticDispMgr( void );
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+inline bool ValidDispFace( dface_t *pFace )
+{
+ if( !pFace ) { return false; }
+ if( pFace->dispinfo == -1 ) { return false; }
+ if( pFace->numedges != 4 ) { return false; }
+
+ return true;
+}
+
+#define SAMPLEHASH_VOXEL_SIZE 64.0f
+typedef unsigned int SampleHandle_t; // the upper 16 bits = facelight index (works because max face are 65536)
+ // the lower 16 bits = sample index inside of facelight
+struct sample_t;
+struct SampleData_t
+{
+ unsigned short x, y, z;
+ CUtlVector<SampleHandle_t> m_Samples;
+};
+
+struct PatchSampleData_t
+{
+ unsigned short x, y, z;
+ CUtlVector<int> m_ndxPatches;
+};
+
+UtlHashHandle_t SampleData_AddSample( sample_t *pSample, SampleHandle_t sampleHandle );
+void PatchSampleData_AddSample( CPatch *pPatch, int ndxPatch );
+unsigned short IncrementPatchIterationKey();
+void SampleData_Log( void );
+
+extern CUtlHash<SampleData_t> g_SampleHashTable;
+extern CUtlHash<PatchSampleData_t> g_PatchSampleHashTable;
+
+extern int samplesAdded;
+extern int patchSamplesAdded;
+
+//-----------------------------------------------------------------------------
+// Computes lighting for the detail props
+//-----------------------------------------------------------------------------
+
+void ComputeDetailPropLighting( int iThread );
+void ComputeIndirectLightingAtPoint( Vector &position, Vector &normal, Vector &outColor,
+ int iThread, bool force_fast = false, bool bIgnoreNormals = false );
+
+//-----------------------------------------------------------------------------
+// VRad static props
+//-----------------------------------------------------------------------------
+class IPhysicsCollision;
+struct PropTested_t
+{
+ int m_Enum;
+ int* m_pTested;
+ IPhysicsCollision *pThreadedCollision;
+};
+
+class IVradStaticPropMgr
+{
+public:
+ // methods of IStaticPropMgr
+ virtual void Init() = 0;
+ virtual void Shutdown() = 0;
+ virtual void ComputeLighting( int iThread ) = 0;
+ virtual void AddPolysForRayTrace() = 0;
+};
+
+//extern PropTested_t s_PropTested[MAX_TOOL_THREADS+1];
+extern DispTested_t s_DispTested[MAX_TOOL_THREADS+1];
+
+IVradStaticPropMgr* StaticPropMgr();
+
+extern float ComputeCoverageFromTexture( float b0, float b1, float b2, int32 hitID );
+
+#endif // VRAD_H
diff --git a/mp/src/utils/vrad/vrad_dispcoll.cpp b/mp/src/utils/vrad/vrad_dispcoll.cpp
index 7e788d07..2edd1ca9 100644
--- a/mp/src/utils/vrad/vrad_dispcoll.cpp
+++ b/mp/src/utils/vrad/vrad_dispcoll.cpp
@@ -1,1080 +1,1080 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "vrad.h"
-#include "VRAD_DispColl.h"
-#include "DispColl_Common.h"
-#include "radial.h"
-#include "CollisionUtils.h"
-#include "tier0\dbg.h"
-
-#define SAMPLE_BBOX_SLOP 5.0f
-#define TRIEDGE_EPSILON 0.001f
-
-float g_flMaxDispSampleSize = 512.0f;
-
-static FileHandle_t pDispFile = FILESYSTEM_INVALID_HANDLE;
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-CVRADDispColl::CVRADDispColl()
-{
- m_iParent = -1;
-
- m_flSampleRadius2 = 0.0f;
- m_flPatchSampleRadius2 = 0.0f;
-
- m_flSampleWidth = 0.0f;
- m_flSampleHeight = 0.0f;
-
- m_aLuxelCoords.Purge();
- m_aVertNormals.Purge();
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-CVRADDispColl::~CVRADDispColl()
-{
- m_aLuxelCoords.Purge();
- m_aVertNormals.Purge();
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CVRADDispColl::Create( CCoreDispInfo *pDisp )
-{
- // Base class create.
- if( !CDispCollTree::Create( pDisp ) )
- return false;
-
- // Allocate VRad specific memory.
- m_aLuxelCoords.SetSize( GetSize() );
- m_aVertNormals.SetSize( GetSize() );
-
- // VRad specific base surface data.
- CCoreDispSurface *pSurf = pDisp->GetSurface();
- m_iParent = pSurf->GetHandle();
-
- // VRad specific displacement surface data.
- for ( int iVert = 0; iVert < m_aVerts.Count(); ++iVert )
- {
- pDisp->GetNormal( iVert, m_aVertNormals[iVert] );
- pDisp->GetLuxelCoord( 0, iVert, m_aLuxelCoords[iVert] );
- }
-
- // Re-calculate the lightmap size (in uv) so that the luxels give
- // a better world-space uniform approx. due to the non-linear nature
- // of the displacement surface in uv-space
- dface_t *pFace = &g_pFaces[m_iParent];
- if( pFace )
- {
- CalcSampleRadius2AndBox( pFace );
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CVRADDispColl::CalcSampleRadius2AndBox( dface_t *pFace )
-{
- // Get the luxel sample size.
- texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
- Assert ( pTexInfo );
- if ( !pTexInfo )
- return;
-
- // Todo: Width = Height now, should change all the code to look at one value.
- Vector vecTmp( pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0],
- pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1],
- pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2] );
- float flWidth = 1.0f / VectorLength( vecTmp );
- float flHeight = flWidth;
-
- // Save off the sample width and height.
- m_flSampleWidth = flWidth;
- m_flSampleHeight = flHeight;
-
- // Calculate the sample radius squared.
- float flSampleRadius = sqrt( ( ( flWidth * flWidth ) + ( flHeight * flHeight ) ) ) * 2.2f;//RADIALDIST2;
- if ( flSampleRadius > g_flMaxDispSampleSize )
- {
- flSampleRadius = g_flMaxDispSampleSize;
- }
- m_flSampleRadius2 = flSampleRadius * flSampleRadius;
-
- // Calculate the patch radius - the max sample edge length * the number of luxels per edge "chop."
- float flSampleSize = max( m_flSampleWidth, m_flSampleHeight );
- float flPatchSampleRadius = flSampleSize * dispchop * 2.2f;
- if ( flPatchSampleRadius > g_MaxDispPatchRadius )
- {
- flPatchSampleRadius = g_MaxDispPatchRadius;
- Warning( "Patch Sample Radius Clamped!\n" );
- }
- m_flPatchSampleRadius2 = flPatchSampleRadius * flPatchSampleRadius;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the min/max of the displacement surface.
-//-----------------------------------------------------------------------------
-void CVRADDispColl::GetSurfaceMinMax( Vector &boxMin, Vector &boxMax )
-{
- // Initialize the minimum and maximum box
- boxMin = m_aVerts[0];
- boxMax = m_aVerts[0];
-
- for( int i = 1; i < m_aVerts.Count(); i++ )
- {
- if( m_aVerts[i].x < boxMin.x ) { boxMin.x = m_aVerts[i].x; }
- if( m_aVerts[i].y < boxMin.y ) { boxMin.y = m_aVerts[i].y; }
- if( m_aVerts[i].z < boxMin.z ) { boxMin.z = m_aVerts[i].z; }
-
- if( m_aVerts[i].x > boxMax.x ) { boxMax.x = m_aVerts[i].x; }
- if( m_aVerts[i].y > boxMax.y ) { boxMax.y = m_aVerts[i].y; }
- if( m_aVerts[i].z > boxMax.z ) { boxMax.z = m_aVerts[i].z; }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Find the minor projection axes based on the given normal.
-//-----------------------------------------------------------------------------
-void CVRADDispColl::GetMinorAxes( Vector const &vecNormal, int &nAxis0, int &nAxis1 )
-{
- nAxis0 = 0;
- nAxis1 = 1;
-
- if( FloatMakePositive( vecNormal.x ) > FloatMakePositive( vecNormal.y ) )
- {
- if( FloatMakePositive( vecNormal.x ) > FloatMakePositive( vecNormal.z ) )
- {
- nAxis0 = 1;
- nAxis1 = 2;
- }
- }
- else
- {
- if( FloatMakePositive( vecNormal.y ) > FloatMakePositive( vecNormal.z ) )
- {
- nAxis0 = 0;
- nAxis1 = 2;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRADDispColl::BaseFacePlaneToDispUV( Vector const &vecPlanePt, Vector2D &dispUV )
-{
- PointInQuadToBarycentric( m_vecSurfPoints[0], m_vecSurfPoints[3], m_vecSurfPoints[2], m_vecSurfPoints[1], vecPlanePt, dispUV );
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRADDispColl::DispUVToSurfPoint( Vector2D const &dispUV, Vector &vecPoint, float flPushEps )
-{
- // Check to see that the point is on the surface.
- if ( dispUV.x < 0.0f || dispUV.x > 1.0f || dispUV.y < 0.0f || dispUV.y > 1.0f )
- return;
-
- // Get the displacement power.
- int nWidth = ( ( 1 << m_nPower ) + 1 );
- int nHeight = nWidth;
-
- // Scale the U, V coordinates to the displacement grid size.
- float flU = dispUV.x * static_cast<float>( nWidth - 1.000001f );
- float flV = dispUV.y * static_cast<float>( nHeight - 1.000001f );
-
- // Find the base U, V.
- int nSnapU = static_cast<int>( flU );
- int nSnapV = static_cast<int>( flV );
-
- // Use this to get the triangle orientation.
- bool bOdd = ( ( ( nSnapV * nWidth ) + nSnapU ) % 2 == 1 );
-
- // Top Left to Bottom Right
- if( bOdd )
- {
- DispUVToSurf_TriTLToBR( vecPoint, flPushEps, flU, flV, nSnapU, nSnapV, nWidth, nHeight );
- }
- // Bottom Left to Top Right
- else
- {
- DispUVToSurf_TriBLToTR( vecPoint, flPushEps, flU, flV, nSnapU, nSnapV, nWidth, nHeight );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CVRADDispColl::DispUVToSurf_TriTLToBR( Vector &vecPoint, float flPushEps,
- float flU, float flV, int nSnapU, int nSnapV,
- int nWidth, int nHeight )
-{
- int nNextU = nSnapU + 1;
- int nNextV = nSnapV + 1;
- if ( nNextU == nWidth) { --nNextU; }
- if ( nNextV == nHeight ) { --nNextV; }
-
- float flFracU = flU - static_cast<float>( nSnapU );
- float flFracV = flV - static_cast<float>( nSnapV );
-
- if( ( flFracU + flFracV ) >= ( 1.0f + TRIEDGE_EPSILON ) )
- {
- int nIndices[3];
- nIndices[0] = nNextV * nWidth + nSnapU;
- nIndices[1] = nNextV * nWidth + nNextU;
- nIndices[2] = nSnapV * nWidth + nNextU;
-
- Vector edgeU = m_aVerts[nIndices[0]] - m_aVerts[nIndices[1]];
- Vector edgeV = m_aVerts[nIndices[2]] - m_aVerts[nIndices[1]];
- vecPoint = m_aVerts[nIndices[1]] + edgeU * ( 1.0f - flFracU ) + edgeV * ( 1.0f - flFracV );
-
- if ( flPushEps != 0.0f )
- {
- Vector vecNormal;
- vecNormal = CrossProduct( edgeU, edgeV );
- VectorNormalize( vecNormal );
- vecPoint += ( vecNormal * flPushEps );
- }
- }
- else
- {
- int nIndices[3];
- nIndices[0] = nSnapV * nWidth + nSnapU;
- nIndices[1] = nNextV * nWidth + nSnapU;
- nIndices[2] = nSnapV * nWidth + nNextU;
-
- Vector edgeU = m_aVerts[nIndices[2]] - m_aVerts[nIndices[0]];
- Vector edgeV = m_aVerts[nIndices[1]] - m_aVerts[nIndices[0]];
- vecPoint = m_aVerts[nIndices[0]] + edgeU * flFracU + edgeV * flFracV;
-
- if ( flPushEps != 0.0f )
- {
- Vector vecNormal;
- vecNormal = CrossProduct( edgeU, edgeV );
- VectorNormalize( vecNormal );
- vecPoint += ( vecNormal * flPushEps );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CVRADDispColl::DispUVToSurf_TriBLToTR( Vector &vecPoint, float flPushEps,
- float flU, float flV, int nSnapU, int nSnapV,
- int nWidth, int nHeight )
-{
- int nNextU = nSnapU + 1;
- int nNextV = nSnapV + 1;
- if ( nNextU == nWidth) { --nNextU; }
- if ( nNextV == nHeight ) { --nNextV; }
-
- float flFracU = flU - static_cast<float>( nSnapU );
- float flFracV = flV - static_cast<float>( nSnapV );
-
- if( flFracU < flFracV )
- {
- int nIndices[3];
- nIndices[0] = nSnapV * nWidth + nSnapU;
- nIndices[1] = nNextV * nWidth + nSnapU;
- nIndices[2] = nNextV * nWidth + nNextU;
-
- Vector edgeU = m_aVerts[nIndices[2]] - m_aVerts[nIndices[1]];
- Vector edgeV = m_aVerts[nIndices[0]] - m_aVerts[nIndices[1]];
- vecPoint = m_aVerts[nIndices[1]] + edgeU * flFracU + edgeV * ( 1.0f - flFracV );
-
- if ( flPushEps != 0.0f )
- {
- Vector vecNormal;
- vecNormal = CrossProduct( edgeV, edgeU );
- VectorNormalize( vecNormal );
- vecPoint += ( vecNormal * flPushEps );
- }
- }
- else
- {
- int nIndices[3];
- nIndices[0] = nSnapV * nWidth + nSnapU;
- nIndices[1] = nNextV * nWidth + nNextU;
- nIndices[2] = nSnapV * nWidth + nNextU;
-
- Vector edgeU = m_aVerts[nIndices[0]] - m_aVerts[nIndices[2]];
- Vector edgeV = m_aVerts[nIndices[1]] - m_aVerts[nIndices[2]];
- vecPoint = m_aVerts[nIndices[2]] + edgeU * ( 1.0f - flFracU ) + edgeV * flFracV;
-
- if ( flPushEps != 0.0f )
- {
- Vector vecNormal;
- vecNormal = CrossProduct( edgeV, edgeU );
- VectorNormalize( vecNormal );
- vecPoint += ( vecNormal * flPushEps );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRADDispColl::DispUVToSurfNormal( Vector2D const &dispUV, Vector &vecNormal )
-{
- // Check to see that the point is on the surface.
- if ( dispUV.x < 0.0f || dispUV.x > 1.0f || dispUV.y < 0.0f || dispUV.y > 1.0f )
- return;
-
- // Get the displacement power.
- int nWidth = ( ( 1 << m_nPower ) + 1 );
- int nHeight = nWidth;
-
- // Scale the U, V coordinates to the displacement grid size.
- float flU = dispUV.x * static_cast<float>( nWidth - 1.000001f );
- float flV = dispUV.y * static_cast<float>( nHeight - 1.000001f );
-
- // Find the base U, V.
- int nSnapU = static_cast<int>( flU );
- int nSnapV = static_cast<int>( flV );
-
- int nNextU = nSnapU + 1;
- int nNextV = nSnapV + 1;
- if ( nNextU == nWidth) { --nNextU; }
- if ( nNextV == nHeight ) { --nNextV; }
-
- float flFracU = flU - static_cast<float>( nSnapU );
- float flFracV = flV - static_cast<float>( nSnapV );
-
- // Get the four normals "around" the "spot"
- int iQuad[VRAD_QUAD_SIZE];
- iQuad[0] = ( nSnapV * nWidth ) + nSnapU;
- iQuad[1] = ( nNextV * nWidth ) + nSnapU;
- iQuad[2] = ( nNextV * nWidth ) + nNextU;
- iQuad[3] = ( nSnapV * nWidth ) + nNextU;
-
- // Find the blended normal (bi-linear).
- Vector vecTmpNormals[2], vecBlendedNormals[2], vecDispNormals[4];
-
- for ( int iVert = 0; iVert < VRAD_QUAD_SIZE; ++iVert )
- {
- GetVertNormal( iQuad[iVert], vecDispNormals[iVert] );
- }
-
- vecTmpNormals[0] = vecDispNormals[0] * ( 1.0f - flFracU );
- vecTmpNormals[1] = vecDispNormals[3] * flFracU;
- vecBlendedNormals[0] = vecTmpNormals[0] + vecTmpNormals[1];
- VectorNormalize( vecBlendedNormals[0] );
-
- vecTmpNormals[0] = vecDispNormals[1] * ( 1.0f - flFracU );
- vecTmpNormals[1] = vecDispNormals[2] * flFracU;
- vecBlendedNormals[1] = vecTmpNormals[0] + vecTmpNormals[1];
- VectorNormalize( vecBlendedNormals[1] );
-
- vecTmpNormals[0] = vecBlendedNormals[0] * ( 1.0f - flFracV );
- vecTmpNormals[1] = vecBlendedNormals[1] * flFracV;
-
- vecNormal = vecTmpNormals[0] + vecTmpNormals[1];
- VectorNormalize( vecNormal );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : float
-//-----------------------------------------------------------------------------
-float CVRADDispColl::CreateParentPatches( void )
-{
- // Save the total surface area of the displacement.
- float flTotalArea = 0.0f;
-
- // Get the number of displacement subdivisions.
- int nInterval = GetWidth();
-
- Vector vecPoints[4];
- vecPoints[0].Init( m_aVerts[0].x, m_aVerts[0].y, m_aVerts[0].z );
- vecPoints[1].Init( m_aVerts[(nInterval*(nInterval-1))].x, m_aVerts[(nInterval*(nInterval-1))].y, m_aVerts[(nInterval*(nInterval-1))].z );
- vecPoints[2].Init( m_aVerts[((nInterval*nInterval)-1)].x, m_aVerts[((nInterval*nInterval)-1)].y, m_aVerts[((nInterval*nInterval)-1)].z );
- vecPoints[3].Init( m_aVerts[(nInterval-1)].x, m_aVerts[(nInterval-1)].y, m_aVerts[(nInterval-1)].z );
-
- // Create and initialize the patch.
- int iPatch = g_Patches.AddToTail();
- if ( iPatch == g_Patches.InvalidIndex() )
- return flTotalArea;
-
- // Keep track of the area of the patches.
- float flArea = 0.0f;
- if ( !InitParentPatch( iPatch, vecPoints, flArea ) )
- {
- g_Patches.Remove( iPatch );
- flArea = 0.0f;
- }
-
- // Return the displacement area.
- return flArea;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : iParentPatch -
-// nLevel -
-//-----------------------------------------------------------------------------
-void CVRADDispColl::CreateChildPatchesFromRoot( int iParentPatch, int *pChildPatch )
-{
- // Initialize the child patch indices.
- pChildPatch[0] = g_Patches.InvalidIndex();
- pChildPatch[1] = g_Patches.InvalidIndex();
-
- // Get the number of displacement subdivisions.
- int nInterval = GetWidth();
-
- // Get the parent patch.
- CPatch *pParentPatch = &g_Patches[iParentPatch];
- if ( !pParentPatch )
- return;
-
- // Split along the longest edge.
- Vector vecEdges[4];
- vecEdges[0] = pParentPatch->winding->p[1] - pParentPatch->winding->p[0];
- vecEdges[1] = pParentPatch->winding->p[2] - pParentPatch->winding->p[1];
- vecEdges[2] = pParentPatch->winding->p[3] - pParentPatch->winding->p[2];
- vecEdges[3] = pParentPatch->winding->p[3] - pParentPatch->winding->p[0];
-
- // Should the patch be subdivided - check the area.
- float flMaxLength = max( m_flSampleWidth, m_flSampleHeight );
- float flMinEdgeLength = flMaxLength * dispchop;
-
- // Find the longest edge.
- float flEdgeLength = 0.0f;
- int iLongEdge = -1;
- for ( int iEdge = 0; iEdge < 4; ++iEdge )
- {
- float flLength = vecEdges[iEdge].Length();
- if ( flEdgeLength < flLength )
- {
- flEdgeLength = vecEdges[iEdge].Length();
- iLongEdge = iEdge;
- }
- }
-
- // Small enough already, return.
- if ( flEdgeLength < flMinEdgeLength )
- return;
-
- // Test area as well so we don't allow slivers.
- float flMinArea = ( dispchop * flMaxLength ) * ( dispchop * flMaxLength );
- Vector vecNormal = vecEdges[3].Cross( vecEdges[0] );
- float flTestArea = VectorNormalize( vecNormal );
- if ( flTestArea < flMinArea )
- return;
-
- // Get the points for the first triangle.
- int iPoints[3];
- Vector vecPoints[3];
- float flArea;
-
- iPoints[0] = ( nInterval * nInterval ) - 1;
- iPoints[1] = 0;
- iPoints[2] = nInterval * ( nInterval - 1 );
- for ( int iPoint = 0; iPoint < 3; ++iPoint )
- {
- VectorCopy( m_aVerts[iPoints[iPoint]], vecPoints[iPoint] );
- }
-
- // Create and initialize the patch.
- pChildPatch[0] = g_Patches.AddToTail();
- if ( pChildPatch[0] == g_Patches.InvalidIndex() )
- return;
-
- if ( !InitPatch( pChildPatch[0], iParentPatch, 0, vecPoints, iPoints, flArea ) )
- {
- g_Patches.Remove( pChildPatch[0] );
- pChildPatch[0] = g_Patches.InvalidIndex();
- return;
- }
-
- // Get the points for the second triangle.
- iPoints[0] = 0;
- iPoints[1] = ( nInterval * nInterval ) - 1;
- iPoints[2] = nInterval - 1;
- for ( int iPoint = 0; iPoint < 3; ++iPoint )
- {
- VectorCopy( m_aVerts[iPoints[iPoint]], vecPoints[iPoint] );
- }
-
- // Create and initialize the patch.
- pChildPatch[1] = g_Patches.AddToTail();
- if ( pChildPatch[1] == g_Patches.InvalidIndex() )
- {
- g_Patches.Remove( pChildPatch[0] );
- pChildPatch[0] = g_Patches.InvalidIndex();
- return;
- }
-
- if ( !InitPatch( pChildPatch[1], iParentPatch, 1, vecPoints, iPoints, flArea ) )
- {
- g_Patches.Remove( pChildPatch[0] );
- pChildPatch[0] = g_Patches.InvalidIndex();
- g_Patches.Remove( pChildPatch[1] );
- pChildPatch[1] = g_Patches.InvalidIndex();
- return;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : flMinArea -
-// Output : float
-//-----------------------------------------------------------------------------
-void CVRADDispColl::CreateChildPatches( int iParentPatch, int nLevel )
-{
- // Get the parent patch.
- CPatch *pParentPatch = &g_Patches[iParentPatch];
- if ( !pParentPatch )
- return;
-
- // The root face is a quad - special case.
- if ( pParentPatch->winding->numpoints == 4 )
- {
- int iChildPatch[2];
- CreateChildPatchesFromRoot( iParentPatch, iChildPatch );
- if ( iChildPatch[0] != g_Patches.InvalidIndex() && iChildPatch[1] != g_Patches.InvalidIndex() )
- {
- CreateChildPatches( iChildPatch[0], 0 );
- CreateChildPatches( iChildPatch[1], 0 );
- }
- return;
- }
-
- // Calculate the the area of the patch (triangle!).
- Assert( pParentPatch->winding->numpoints == 3 );
- if ( pParentPatch->winding->numpoints != 3 )
- return;
-
- // Should the patch be subdivided - check the area.
- float flMaxLength = max( m_flSampleWidth, m_flSampleHeight );
- float flMinEdgeLength = flMaxLength * dispchop;
-
- // Split along the longest edge.
- Vector vecEdges[3];
- vecEdges[0] = pParentPatch->winding->p[1] - pParentPatch->winding->p[0];
- vecEdges[1] = pParentPatch->winding->p[2] - pParentPatch->winding->p[0];
- vecEdges[2] = pParentPatch->winding->p[2] - pParentPatch->winding->p[1];
-
- // Find the longest edge.
- float flEdgeLength = 0.0f;
- int iLongEdge = -1;
- for ( int iEdge = 0; iEdge < 3; ++iEdge )
- {
- if ( flEdgeLength < vecEdges[iEdge].Length() )
- {
- flEdgeLength = vecEdges[iEdge].Length();
- iLongEdge = iEdge;
- }
- }
-
- // Small enough already, return.
- if ( flEdgeLength < flMinEdgeLength )
- return;
-
- // Test area as well so we don't allow slivers.
- float flMinArea = ( dispchop * flMaxLength ) * ( dispchop * flMaxLength ) * 0.5f;
- Vector vecNormal = vecEdges[1].Cross( vecEdges[0] );
- float flTestArea = VectorNormalize( vecNormal );
- flTestArea *= 0.5f;
- if ( flTestArea < flMinArea )
- return;
-
- // Check to see if any more displacement verts exist - go to subdivision if not.
- if ( nLevel >= ( m_nPower * 2 ) )
- {
- CreateChildPatchesSub( iParentPatch );
- return;
- }
-
- int nChildIndices[2][3];
- int nNewIndex = ( pParentPatch->indices[1] + pParentPatch->indices[0] ) / 2;
- nChildIndices[0][0] = pParentPatch->indices[2];
- nChildIndices[0][1] = pParentPatch->indices[0];
- nChildIndices[0][2] = nNewIndex;
-
- nChildIndices[1][0] = pParentPatch->indices[1];
- nChildIndices[1][1] = pParentPatch->indices[2];
- nChildIndices[1][2] = nNewIndex;
-
- Vector vecChildPoints[2][3];
- for ( int iTri = 0; iTri < 2; ++iTri )
- {
- for ( int iPoint = 0; iPoint < 3; ++iPoint )
- {
- VectorCopy( m_aVerts[nChildIndices[iTri][iPoint]], vecChildPoints[iTri][iPoint] );
- }
- }
-
- // Create and initialize the children patches.
- int iChildPatch[2] = { -1, -1 };
- for ( int iChild = 0; iChild < 2; ++iChild )
- {
- iChildPatch[iChild] = g_Patches.AddToTail();
-
- float flArea = 0.0f;
- if ( !InitPatch( iChildPatch[iChild], iParentPatch, iChild, vecChildPoints[iChild], nChildIndices[iChild], flArea ) )
- {
- if ( iChild == 0 )
- {
- pParentPatch->child1 = g_Patches.InvalidIndex();
- g_Patches.Remove( iChildPatch[iChild] );
- break;
- }
- else
- {
- pParentPatch->child1 = g_Patches.InvalidIndex();
- pParentPatch->child2 = g_Patches.InvalidIndex();
- g_Patches.Remove( iChildPatch[iChild] );
- g_Patches.Remove( iChildPatch[0] );
- }
- }
- }
-
- // Continue creating children patches.
- int nNewLevel = ++nLevel;
- CreateChildPatches( iChildPatch[0], nNewLevel );
- CreateChildPatches( iChildPatch[1], nNewLevel );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : flMinArea -
-// Output : float
-//-----------------------------------------------------------------------------
-void CVRADDispColl::CreateChildPatchesSub( int iParentPatch )
-{
- // Get the parent patch.
- CPatch *pParentPatch = &g_Patches[iParentPatch];
- if ( !pParentPatch )
- return;
-
- // Calculate the the area of the patch (triangle!).
- Assert( pParentPatch->winding->numpoints == 3 );
- if ( pParentPatch->winding->numpoints != 3 )
- return;
-
- // Should the patch be subdivided - check the area.
- float flMaxLength = max( m_flSampleWidth, m_flSampleHeight );
- float flMinEdgeLength = flMaxLength * dispchop;
-
- // Split along the longest edge.
- Vector vecEdges[3];
- vecEdges[0] = pParentPatch->winding->p[1] - pParentPatch->winding->p[0];
- vecEdges[1] = pParentPatch->winding->p[2] - pParentPatch->winding->p[1];
- vecEdges[2] = pParentPatch->winding->p[0] - pParentPatch->winding->p[2];
-
- // Find the longest edge.
- float flEdgeLength = 0.0f;
- int iLongEdge = -1;
- for ( int iEdge = 0; iEdge < 3; ++iEdge )
- {
- if ( flEdgeLength < vecEdges[iEdge].Length() )
- {
- flEdgeLength = vecEdges[iEdge].Length();
- iLongEdge = iEdge;
- }
- }
-
- // Small enough already, return.
- if ( flEdgeLength < flMinEdgeLength )
- return;
-
- // Test area as well so we don't allow slivers.
- float flMinArea = ( dispchop * flMaxLength ) * ( dispchop * flMaxLength ) * 0.5f;
- Vector vecNormal = vecEdges[1].Cross( vecEdges[0] );
- float flTestArea = VectorNormalize( vecNormal );
- flTestArea *= 0.5f;
- if ( flTestArea < flMinArea )
- return;
-
- // Create children patchs - 2 of them.
- Vector vecChildPoints[2][3];
- switch ( iLongEdge )
- {
- case 0:
- {
- vecChildPoints[0][0] = pParentPatch->winding->p[0];
- vecChildPoints[0][1] = ( pParentPatch->winding->p[0] + pParentPatch->winding->p[1] ) * 0.5f;
- vecChildPoints[0][2] = pParentPatch->winding->p[2];
-
- vecChildPoints[1][0] = ( pParentPatch->winding->p[0] + pParentPatch->winding->p[1] ) * 0.5f;
- vecChildPoints[1][1] = pParentPatch->winding->p[1];
- vecChildPoints[1][2] = pParentPatch->winding->p[2];
- break;
- }
- case 1:
- {
- vecChildPoints[0][0] = pParentPatch->winding->p[0];
- vecChildPoints[0][1] = pParentPatch->winding->p[1];
- vecChildPoints[0][2] = ( pParentPatch->winding->p[1] + pParentPatch->winding->p[2] ) * 0.5f;
-
- vecChildPoints[1][0] = ( pParentPatch->winding->p[1] + pParentPatch->winding->p[2] ) * 0.5f;
- vecChildPoints[1][1] = pParentPatch->winding->p[2];
- vecChildPoints[1][2] = pParentPatch->winding->p[0];
- break;
- }
- case 2:
- {
- vecChildPoints[0][0] = pParentPatch->winding->p[0];
- vecChildPoints[0][1] = pParentPatch->winding->p[1];
- vecChildPoints[0][2] = ( pParentPatch->winding->p[0] + pParentPatch->winding->p[2] ) * 0.5f;
-
- vecChildPoints[1][0] = ( pParentPatch->winding->p[0] + pParentPatch->winding->p[2] ) * 0.5f;
- vecChildPoints[1][1] = pParentPatch->winding->p[1];
- vecChildPoints[1][2] = pParentPatch->winding->p[2];
- break;
- }
- }
-
-
- // Create and initialize the children patches.
- int iChildPatch[2] = { 0, 0 };
- int nChildIndices[3] = { -1, -1, -1 };
- for ( int iChild = 0; iChild < 2; ++iChild )
- {
- iChildPatch[iChild] = g_Patches.AddToTail();
-
- float flArea = 0.0f;
- if ( !InitPatch( iChildPatch[iChild], iParentPatch, iChild, vecChildPoints[iChild], nChildIndices, flArea ) )
- {
- if ( iChild == 0 )
- {
- pParentPatch->child1 = g_Patches.InvalidIndex();
- g_Patches.Remove( iChildPatch[iChild] );
- break;
- }
- else
- {
- pParentPatch->child1 = g_Patches.InvalidIndex();
- pParentPatch->child2 = g_Patches.InvalidIndex();
- g_Patches.Remove( iChildPatch[iChild] );
- g_Patches.Remove( iChildPatch[0] );
- }
- }
- }
-
- // Continue creating children patches.
- CreateChildPatchesSub( iChildPatch[0] );
- CreateChildPatchesSub( iChildPatch[1] );
-}
-
-int PlaneTypeForNormal (Vector& normal)
-{
- vec_t ax, ay, az;
-
- // NOTE: should these have an epsilon around 1.0?
- if (normal[0] == 1.0 || normal[0] == -1.0)
- return PLANE_X;
- if (normal[1] == 1.0 || normal[1] == -1.0)
- return PLANE_Y;
- if (normal[2] == 1.0 || normal[2] == -1.0)
- return PLANE_Z;
-
- ax = fabs(normal[0]);
- ay = fabs(normal[1]);
- az = fabs(normal[2]);
-
- if (ax >= ay && ax >= az)
- return PLANE_ANYX;
- if (ay >= ax && ay >= az)
- return PLANE_ANYY;
- return PLANE_ANYZ;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : iPatch -
-// iParentPatch -
-// iChild -
-// *pPoints -
-// *pIndices -
-// &flArea -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CVRADDispColl::InitParentPatch( int iPatch, Vector *pPoints, float &flArea )
-{
- // Get the current patch.
- CPatch *pPatch = &g_Patches[iPatch];
- if ( !pPatch )
- return false;
-
- // Clear the patch data.
- memset( pPatch, 0, sizeof( CPatch ) );
-
- // This is a parent.
- pPatch->ndxNext = g_FacePatches.Element( GetParentIndex() );
- g_FacePatches[GetParentIndex()] = iPatch;
- pPatch->faceNumber = GetParentIndex();
-
- // Initialize parent and children indices.
- pPatch->child1 = g_Patches.InvalidIndex();
- pPatch->child2 = g_Patches.InvalidIndex();
- pPatch->parent = g_Patches.InvalidIndex();
- pPatch->ndxNextClusterChild = g_Patches.InvalidIndex();
- pPatch->ndxNextParent = g_Patches.InvalidIndex();
-
- Vector vecEdges[2];
- vecEdges[0] = pPoints[1] - pPoints[0];
- vecEdges[1] = pPoints[3] - pPoints[0];
-
- // Calculate the triangle normal and area.
- Vector vecNormal = vecEdges[1].Cross( vecEdges[0] );
- flArea = VectorNormalize( vecNormal );
-
- // Initialize the patch scale.
- pPatch->scale[0] = pPatch->scale[1] = 1.0f;
-
- // Set the patch chop - minchop (that is what the minimum area is based on).
- pPatch->chop = dispchop;
-
- // Displacements are not sky!
- pPatch->sky = false;
-
- // Copy the winding.
- Vector vecCenter( 0.0f, 0.0f, 0.0f );
- pPatch->winding = AllocWinding( 4 );
- pPatch->winding->numpoints = 4;
- for ( int iPoint = 0; iPoint < 4; ++iPoint )
- {
- VectorCopy( pPoints[iPoint], pPatch->winding->p[iPoint] );
- VectorAdd( pPoints[iPoint], vecCenter, vecCenter );
- }
-
- // Set the origin and normal.
- VectorScale( vecCenter, ( 1.0f / 4.0f ), vecCenter );
- VectorCopy( vecCenter, pPatch->origin );
- VectorCopy( vecNormal, pPatch->normal );
-
- // Create the plane.
- pPatch->plane = new dplane_t;
- if ( !pPatch->plane )
- return false;
-
- VectorCopy( vecNormal, pPatch->plane->normal );
- pPatch->plane->dist = vecNormal.Dot( pPoints[0] );
- pPatch->plane->type = PlaneTypeForNormal( pPatch->plane->normal );
- pPatch->planeDist = pPatch->plane->dist;
-
- // Set the area.
- pPatch->area = flArea;
-
- // Calculate the mins/maxs.
- Vector vecMin( FLT_MAX, FLT_MAX, FLT_MAX );
- Vector vecMax( FLT_MIN, FLT_MIN, FLT_MIN );
- for ( int iPoint = 0; iPoint < 4; ++iPoint )
- {
- for ( int iAxis = 0; iAxis < 3; ++iAxis )
- {
- vecMin[iAxis] = min( vecMin[iAxis], pPoints[iPoint][iAxis] );
- vecMax[iAxis] = max( vecMax[iAxis], pPoints[iPoint][iAxis] );
- }
- }
-
- VectorCopy( vecMin, pPatch->mins );
- VectorCopy( vecMax, pPatch->maxs );
- VectorCopy( vecMin, pPatch->face_mins );
- VectorCopy( vecMax, pPatch->face_maxs );
-
- // Check for bumpmap.
- dface_t *pFace = dfaces + pPatch->faceNumber;
- texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
- pPatch->needsBumpmap = pTexInfo->flags & SURF_BUMPLIGHT ? true : false;
-
- // Misc...
- pPatch->m_IterationKey = 0;
-
- // Calculate the base light, area, and reflectivity.
- BaseLightForFace( &g_pFaces[pPatch->faceNumber], pPatch->baselight, &pPatch->basearea, pPatch->reflectivity );
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pPatch -
-// *pPoints -
-// &vecNormal -
-// flArea -
-//-----------------------------------------------------------------------------
-bool CVRADDispColl::InitPatch( int iPatch, int iParentPatch, int iChild, Vector *pPoints, int *pIndices, float &flArea )
-{
- // Get the current patch.
- CPatch *pPatch = &g_Patches[iPatch];
- if ( !pPatch )
- return false;
-
- // Clear the patch data.
- memset( pPatch, 0, sizeof( CPatch ) );
-
- // Setup the parent if we are not the parent.
- CPatch *pParentPatch = NULL;
- if ( iParentPatch != g_Patches.InvalidIndex() )
- {
- // Get the parent patch.
- pParentPatch = &g_Patches[iParentPatch];
- if ( !pParentPatch )
- return false;
- }
-
- // Attach the face to the correct lists.
- if ( !pParentPatch )
- {
- // This is a parent.
- pPatch->ndxNext = g_FacePatches.Element( GetParentIndex() );
- g_FacePatches[GetParentIndex()] = iPatch;
- pPatch->faceNumber = GetParentIndex();
- }
- else
- {
- pPatch->ndxNext = g_Patches.InvalidIndex();
- pPatch->faceNumber = pParentPatch->faceNumber;
-
- // Attach to the parent patch.
- if ( iChild == 0 )
- {
- pParentPatch->child1 = iPatch;
- }
- else
- {
- pParentPatch->child2 = iPatch;
- }
- }
-
- // Initialize parent and children indices.
- pPatch->child1 = g_Patches.InvalidIndex();
- pPatch->child2 = g_Patches.InvalidIndex();
- pPatch->ndxNextClusterChild = g_Patches.InvalidIndex();
- pPatch->ndxNextParent = g_Patches.InvalidIndex();
- pPatch->parent = iParentPatch;
-
- // Get triangle edges.
- Vector vecEdges[3];
- vecEdges[0] = pPoints[1] - pPoints[0];
- vecEdges[1] = pPoints[2] - pPoints[0];
- vecEdges[2] = pPoints[2] - pPoints[1];
-
- // Find the longest edge.
-// float flEdgeLength = 0.0f;
-// for ( int iEdge = 0; iEdge < 3; ++iEdge )
-// {
-// if ( flEdgeLength < vecEdges[iEdge].Length() )
-// {
-// flEdgeLength = vecEdges[iEdge].Length();
-// }
-// }
-
- // Calculate the triangle normal and area.
- Vector vecNormal = vecEdges[1].Cross( vecEdges[0] );
- flArea = VectorNormalize( vecNormal );
- flArea *= 0.5f;
-
- // Initialize the patch scale.
- pPatch->scale[0] = pPatch->scale[1] = 1.0f;
-
- // Set the patch chop - minchop (that is what the minimum area is based on).
- pPatch->chop = dispchop;
-
- // Displacements are not sky!
- pPatch->sky = false;
-
- // Copy the winding.
- Vector vecCenter( 0.0f, 0.0f, 0.0f );
- pPatch->winding = AllocWinding( 3 );
- pPatch->winding->numpoints = 3;
- for ( int iPoint = 0; iPoint < 3; ++iPoint )
- {
- VectorCopy( pPoints[iPoint], pPatch->winding->p[iPoint] );
- VectorAdd( pPoints[iPoint], vecCenter, vecCenter );
-
- pPatch->indices[iPoint] = static_cast<short>( pIndices[iPoint] );
- }
-
- // Set the origin and normal.
- VectorScale( vecCenter, ( 1.0f / 3.0f ), vecCenter );
- VectorCopy( vecCenter, pPatch->origin );
- VectorCopy( vecNormal, pPatch->normal );
-
- // Create the plane.
- pPatch->plane = new dplane_t;
- if ( !pPatch->plane )
- return false;
-
- VectorCopy( vecNormal, pPatch->plane->normal );
- pPatch->plane->dist = vecNormal.Dot( pPoints[0] );
- pPatch->plane->type = PlaneTypeForNormal( pPatch->plane->normal );
- pPatch->planeDist = pPatch->plane->dist;
-
- // Set the area.
- pPatch->area = flArea;
-
- // Calculate the mins/maxs.
- Vector vecMin( FLT_MAX, FLT_MAX, FLT_MAX );
- Vector vecMax( FLT_MIN, FLT_MIN, FLT_MIN );
- for ( int iPoint = 0; iPoint < 3; ++iPoint )
- {
- for ( int iAxis = 0; iAxis < 3; ++iAxis )
- {
- vecMin[iAxis] = min( vecMin[iAxis], pPoints[iPoint][iAxis] );
- vecMax[iAxis] = max( vecMax[iAxis], pPoints[iPoint][iAxis] );
- }
- }
-
- VectorCopy( vecMin, pPatch->mins );
- VectorCopy( vecMax, pPatch->maxs );
-
- if ( !pParentPatch )
- {
- VectorCopy( vecMin, pPatch->face_mins );
- VectorCopy( vecMax, pPatch->face_maxs );
- }
- else
- {
- VectorCopy( pParentPatch->face_mins, pPatch->face_mins );
- VectorCopy( pParentPatch->face_maxs, pPatch->face_maxs );
- }
-
- // Check for bumpmap.
- dface_t *pFace = dfaces + pPatch->faceNumber;
- texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
- pPatch->needsBumpmap = pTexInfo->flags & SURF_BUMPLIGHT ? true : false;
-
- // Misc...
- pPatch->m_IterationKey = 0;
-
- // Get the base light for the face.
- if ( !pParentPatch )
- {
- BaseLightForFace( &g_pFaces[pPatch->faceNumber], pPatch->baselight, &pPatch->basearea, pPatch->reflectivity );
- }
- else
- {
- VectorCopy( pParentPatch->baselight, pPatch->baselight );
- pPatch->basearea = pParentPatch->basearea;
- pPatch->reflectivity = pParentPatch->reflectivity;
- }
-
- return true;
-}
-
-void CVRADDispColl::AddPolysForRayTrace( void )
-{
- if ( !( m_nContents & MASK_OPAQUE ) )
- return;
-
- for ( int ndxTri = 0; ndxTri < m_aTris.Size(); ndxTri++ )
- {
- CDispCollTri *tri = m_aTris.Base() + ndxTri;
- int v[3];
- for ( int ndxv = 0; ndxv < 3; ndxv++ )
- v[ndxv] = tri->GetVert(ndxv);
-
- Vector fullCoverage;
- fullCoverage.x = 1.0f;
- g_RtEnv.AddTriangle( TRACE_ID_OPAQUE, m_aVerts[v[0]], m_aVerts[v[1]], m_aVerts[v[2]], fullCoverage );
- }
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vrad.h"
+#include "VRAD_DispColl.h"
+#include "DispColl_Common.h"
+#include "radial.h"
+#include "CollisionUtils.h"
+#include "tier0\dbg.h"
+
+#define SAMPLE_BBOX_SLOP 5.0f
+#define TRIEDGE_EPSILON 0.001f
+
+float g_flMaxDispSampleSize = 512.0f;
+
+static FileHandle_t pDispFile = FILESYSTEM_INVALID_HANDLE;
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CVRADDispColl::CVRADDispColl()
+{
+ m_iParent = -1;
+
+ m_flSampleRadius2 = 0.0f;
+ m_flPatchSampleRadius2 = 0.0f;
+
+ m_flSampleWidth = 0.0f;
+ m_flSampleHeight = 0.0f;
+
+ m_aLuxelCoords.Purge();
+ m_aVertNormals.Purge();
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CVRADDispColl::~CVRADDispColl()
+{
+ m_aLuxelCoords.Purge();
+ m_aVertNormals.Purge();
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRADDispColl::Create( CCoreDispInfo *pDisp )
+{
+ // Base class create.
+ if( !CDispCollTree::Create( pDisp ) )
+ return false;
+
+ // Allocate VRad specific memory.
+ m_aLuxelCoords.SetSize( GetSize() );
+ m_aVertNormals.SetSize( GetSize() );
+
+ // VRad specific base surface data.
+ CCoreDispSurface *pSurf = pDisp->GetSurface();
+ m_iParent = pSurf->GetHandle();
+
+ // VRad specific displacement surface data.
+ for ( int iVert = 0; iVert < m_aVerts.Count(); ++iVert )
+ {
+ pDisp->GetNormal( iVert, m_aVertNormals[iVert] );
+ pDisp->GetLuxelCoord( 0, iVert, m_aLuxelCoords[iVert] );
+ }
+
+ // Re-calculate the lightmap size (in uv) so that the luxels give
+ // a better world-space uniform approx. due to the non-linear nature
+ // of the displacement surface in uv-space
+ dface_t *pFace = &g_pFaces[m_iParent];
+ if( pFace )
+ {
+ CalcSampleRadius2AndBox( pFace );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CVRADDispColl::CalcSampleRadius2AndBox( dface_t *pFace )
+{
+ // Get the luxel sample size.
+ texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
+ Assert ( pTexInfo );
+ if ( !pTexInfo )
+ return;
+
+ // Todo: Width = Height now, should change all the code to look at one value.
+ Vector vecTmp( pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2] );
+ float flWidth = 1.0f / VectorLength( vecTmp );
+ float flHeight = flWidth;
+
+ // Save off the sample width and height.
+ m_flSampleWidth = flWidth;
+ m_flSampleHeight = flHeight;
+
+ // Calculate the sample radius squared.
+ float flSampleRadius = sqrt( ( ( flWidth * flWidth ) + ( flHeight * flHeight ) ) ) * 2.2f;//RADIALDIST2;
+ if ( flSampleRadius > g_flMaxDispSampleSize )
+ {
+ flSampleRadius = g_flMaxDispSampleSize;
+ }
+ m_flSampleRadius2 = flSampleRadius * flSampleRadius;
+
+ // Calculate the patch radius - the max sample edge length * the number of luxels per edge "chop."
+ float flSampleSize = max( m_flSampleWidth, m_flSampleHeight );
+ float flPatchSampleRadius = flSampleSize * dispchop * 2.2f;
+ if ( flPatchSampleRadius > g_MaxDispPatchRadius )
+ {
+ flPatchSampleRadius = g_MaxDispPatchRadius;
+ Warning( "Patch Sample Radius Clamped!\n" );
+ }
+ m_flPatchSampleRadius2 = flPatchSampleRadius * flPatchSampleRadius;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the min/max of the displacement surface.
+//-----------------------------------------------------------------------------
+void CVRADDispColl::GetSurfaceMinMax( Vector &boxMin, Vector &boxMax )
+{
+ // Initialize the minimum and maximum box
+ boxMin = m_aVerts[0];
+ boxMax = m_aVerts[0];
+
+ for( int i = 1; i < m_aVerts.Count(); i++ )
+ {
+ if( m_aVerts[i].x < boxMin.x ) { boxMin.x = m_aVerts[i].x; }
+ if( m_aVerts[i].y < boxMin.y ) { boxMin.y = m_aVerts[i].y; }
+ if( m_aVerts[i].z < boxMin.z ) { boxMin.z = m_aVerts[i].z; }
+
+ if( m_aVerts[i].x > boxMax.x ) { boxMax.x = m_aVerts[i].x; }
+ if( m_aVerts[i].y > boxMax.y ) { boxMax.y = m_aVerts[i].y; }
+ if( m_aVerts[i].z > boxMax.z ) { boxMax.z = m_aVerts[i].z; }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find the minor projection axes based on the given normal.
+//-----------------------------------------------------------------------------
+void CVRADDispColl::GetMinorAxes( Vector const &vecNormal, int &nAxis0, int &nAxis1 )
+{
+ nAxis0 = 0;
+ nAxis1 = 1;
+
+ if( FloatMakePositive( vecNormal.x ) > FloatMakePositive( vecNormal.y ) )
+ {
+ if( FloatMakePositive( vecNormal.x ) > FloatMakePositive( vecNormal.z ) )
+ {
+ nAxis0 = 1;
+ nAxis1 = 2;
+ }
+ }
+ else
+ {
+ if( FloatMakePositive( vecNormal.y ) > FloatMakePositive( vecNormal.z ) )
+ {
+ nAxis0 = 0;
+ nAxis1 = 2;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRADDispColl::BaseFacePlaneToDispUV( Vector const &vecPlanePt, Vector2D &dispUV )
+{
+ PointInQuadToBarycentric( m_vecSurfPoints[0], m_vecSurfPoints[3], m_vecSurfPoints[2], m_vecSurfPoints[1], vecPlanePt, dispUV );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRADDispColl::DispUVToSurfPoint( Vector2D const &dispUV, Vector &vecPoint, float flPushEps )
+{
+ // Check to see that the point is on the surface.
+ if ( dispUV.x < 0.0f || dispUV.x > 1.0f || dispUV.y < 0.0f || dispUV.y > 1.0f )
+ return;
+
+ // Get the displacement power.
+ int nWidth = ( ( 1 << m_nPower ) + 1 );
+ int nHeight = nWidth;
+
+ // Scale the U, V coordinates to the displacement grid size.
+ float flU = dispUV.x * static_cast<float>( nWidth - 1.000001f );
+ float flV = dispUV.y * static_cast<float>( nHeight - 1.000001f );
+
+ // Find the base U, V.
+ int nSnapU = static_cast<int>( flU );
+ int nSnapV = static_cast<int>( flV );
+
+ // Use this to get the triangle orientation.
+ bool bOdd = ( ( ( nSnapV * nWidth ) + nSnapU ) % 2 == 1 );
+
+ // Top Left to Bottom Right
+ if( bOdd )
+ {
+ DispUVToSurf_TriTLToBR( vecPoint, flPushEps, flU, flV, nSnapU, nSnapV, nWidth, nHeight );
+ }
+ // Bottom Left to Top Right
+ else
+ {
+ DispUVToSurf_TriBLToTR( vecPoint, flPushEps, flU, flV, nSnapU, nSnapV, nWidth, nHeight );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CVRADDispColl::DispUVToSurf_TriTLToBR( Vector &vecPoint, float flPushEps,
+ float flU, float flV, int nSnapU, int nSnapV,
+ int nWidth, int nHeight )
+{
+ int nNextU = nSnapU + 1;
+ int nNextV = nSnapV + 1;
+ if ( nNextU == nWidth) { --nNextU; }
+ if ( nNextV == nHeight ) { --nNextV; }
+
+ float flFracU = flU - static_cast<float>( nSnapU );
+ float flFracV = flV - static_cast<float>( nSnapV );
+
+ if( ( flFracU + flFracV ) >= ( 1.0f + TRIEDGE_EPSILON ) )
+ {
+ int nIndices[3];
+ nIndices[0] = nNextV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nNextU;
+ nIndices[2] = nSnapV * nWidth + nNextU;
+
+ Vector edgeU = m_aVerts[nIndices[0]] - m_aVerts[nIndices[1]];
+ Vector edgeV = m_aVerts[nIndices[2]] - m_aVerts[nIndices[1]];
+ vecPoint = m_aVerts[nIndices[1]] + edgeU * ( 1.0f - flFracU ) + edgeV * ( 1.0f - flFracV );
+
+ if ( flPushEps != 0.0f )
+ {
+ Vector vecNormal;
+ vecNormal = CrossProduct( edgeU, edgeV );
+ VectorNormalize( vecNormal );
+ vecPoint += ( vecNormal * flPushEps );
+ }
+ }
+ else
+ {
+ int nIndices[3];
+ nIndices[0] = nSnapV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nSnapU;
+ nIndices[2] = nSnapV * nWidth + nNextU;
+
+ Vector edgeU = m_aVerts[nIndices[2]] - m_aVerts[nIndices[0]];
+ Vector edgeV = m_aVerts[nIndices[1]] - m_aVerts[nIndices[0]];
+ vecPoint = m_aVerts[nIndices[0]] + edgeU * flFracU + edgeV * flFracV;
+
+ if ( flPushEps != 0.0f )
+ {
+ Vector vecNormal;
+ vecNormal = CrossProduct( edgeU, edgeV );
+ VectorNormalize( vecNormal );
+ vecPoint += ( vecNormal * flPushEps );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CVRADDispColl::DispUVToSurf_TriBLToTR( Vector &vecPoint, float flPushEps,
+ float flU, float flV, int nSnapU, int nSnapV,
+ int nWidth, int nHeight )
+{
+ int nNextU = nSnapU + 1;
+ int nNextV = nSnapV + 1;
+ if ( nNextU == nWidth) { --nNextU; }
+ if ( nNextV == nHeight ) { --nNextV; }
+
+ float flFracU = flU - static_cast<float>( nSnapU );
+ float flFracV = flV - static_cast<float>( nSnapV );
+
+ if( flFracU < flFracV )
+ {
+ int nIndices[3];
+ nIndices[0] = nSnapV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nSnapU;
+ nIndices[2] = nNextV * nWidth + nNextU;
+
+ Vector edgeU = m_aVerts[nIndices[2]] - m_aVerts[nIndices[1]];
+ Vector edgeV = m_aVerts[nIndices[0]] - m_aVerts[nIndices[1]];
+ vecPoint = m_aVerts[nIndices[1]] + edgeU * flFracU + edgeV * ( 1.0f - flFracV );
+
+ if ( flPushEps != 0.0f )
+ {
+ Vector vecNormal;
+ vecNormal = CrossProduct( edgeV, edgeU );
+ VectorNormalize( vecNormal );
+ vecPoint += ( vecNormal * flPushEps );
+ }
+ }
+ else
+ {
+ int nIndices[3];
+ nIndices[0] = nSnapV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nNextU;
+ nIndices[2] = nSnapV * nWidth + nNextU;
+
+ Vector edgeU = m_aVerts[nIndices[0]] - m_aVerts[nIndices[2]];
+ Vector edgeV = m_aVerts[nIndices[1]] - m_aVerts[nIndices[2]];
+ vecPoint = m_aVerts[nIndices[2]] + edgeU * ( 1.0f - flFracU ) + edgeV * flFracV;
+
+ if ( flPushEps != 0.0f )
+ {
+ Vector vecNormal;
+ vecNormal = CrossProduct( edgeV, edgeU );
+ VectorNormalize( vecNormal );
+ vecPoint += ( vecNormal * flPushEps );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRADDispColl::DispUVToSurfNormal( Vector2D const &dispUV, Vector &vecNormal )
+{
+ // Check to see that the point is on the surface.
+ if ( dispUV.x < 0.0f || dispUV.x > 1.0f || dispUV.y < 0.0f || dispUV.y > 1.0f )
+ return;
+
+ // Get the displacement power.
+ int nWidth = ( ( 1 << m_nPower ) + 1 );
+ int nHeight = nWidth;
+
+ // Scale the U, V coordinates to the displacement grid size.
+ float flU = dispUV.x * static_cast<float>( nWidth - 1.000001f );
+ float flV = dispUV.y * static_cast<float>( nHeight - 1.000001f );
+
+ // Find the base U, V.
+ int nSnapU = static_cast<int>( flU );
+ int nSnapV = static_cast<int>( flV );
+
+ int nNextU = nSnapU + 1;
+ int nNextV = nSnapV + 1;
+ if ( nNextU == nWidth) { --nNextU; }
+ if ( nNextV == nHeight ) { --nNextV; }
+
+ float flFracU = flU - static_cast<float>( nSnapU );
+ float flFracV = flV - static_cast<float>( nSnapV );
+
+ // Get the four normals "around" the "spot"
+ int iQuad[VRAD_QUAD_SIZE];
+ iQuad[0] = ( nSnapV * nWidth ) + nSnapU;
+ iQuad[1] = ( nNextV * nWidth ) + nSnapU;
+ iQuad[2] = ( nNextV * nWidth ) + nNextU;
+ iQuad[3] = ( nSnapV * nWidth ) + nNextU;
+
+ // Find the blended normal (bi-linear).
+ Vector vecTmpNormals[2], vecBlendedNormals[2], vecDispNormals[4];
+
+ for ( int iVert = 0; iVert < VRAD_QUAD_SIZE; ++iVert )
+ {
+ GetVertNormal( iQuad[iVert], vecDispNormals[iVert] );
+ }
+
+ vecTmpNormals[0] = vecDispNormals[0] * ( 1.0f - flFracU );
+ vecTmpNormals[1] = vecDispNormals[3] * flFracU;
+ vecBlendedNormals[0] = vecTmpNormals[0] + vecTmpNormals[1];
+ VectorNormalize( vecBlendedNormals[0] );
+
+ vecTmpNormals[0] = vecDispNormals[1] * ( 1.0f - flFracU );
+ vecTmpNormals[1] = vecDispNormals[2] * flFracU;
+ vecBlendedNormals[1] = vecTmpNormals[0] + vecTmpNormals[1];
+ VectorNormalize( vecBlendedNormals[1] );
+
+ vecTmpNormals[0] = vecBlendedNormals[0] * ( 1.0f - flFracV );
+ vecTmpNormals[1] = vecBlendedNormals[1] * flFracV;
+
+ vecNormal = vecTmpNormals[0] + vecTmpNormals[1];
+ VectorNormalize( vecNormal );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CVRADDispColl::CreateParentPatches( void )
+{
+ // Save the total surface area of the displacement.
+ float flTotalArea = 0.0f;
+
+ // Get the number of displacement subdivisions.
+ int nInterval = GetWidth();
+
+ Vector vecPoints[4];
+ vecPoints[0].Init( m_aVerts[0].x, m_aVerts[0].y, m_aVerts[0].z );
+ vecPoints[1].Init( m_aVerts[(nInterval*(nInterval-1))].x, m_aVerts[(nInterval*(nInterval-1))].y, m_aVerts[(nInterval*(nInterval-1))].z );
+ vecPoints[2].Init( m_aVerts[((nInterval*nInterval)-1)].x, m_aVerts[((nInterval*nInterval)-1)].y, m_aVerts[((nInterval*nInterval)-1)].z );
+ vecPoints[3].Init( m_aVerts[(nInterval-1)].x, m_aVerts[(nInterval-1)].y, m_aVerts[(nInterval-1)].z );
+
+ // Create and initialize the patch.
+ int iPatch = g_Patches.AddToTail();
+ if ( iPatch == g_Patches.InvalidIndex() )
+ return flTotalArea;
+
+ // Keep track of the area of the patches.
+ float flArea = 0.0f;
+ if ( !InitParentPatch( iPatch, vecPoints, flArea ) )
+ {
+ g_Patches.Remove( iPatch );
+ flArea = 0.0f;
+ }
+
+ // Return the displacement area.
+ return flArea;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iParentPatch -
+// nLevel -
+//-----------------------------------------------------------------------------
+void CVRADDispColl::CreateChildPatchesFromRoot( int iParentPatch, int *pChildPatch )
+{
+ // Initialize the child patch indices.
+ pChildPatch[0] = g_Patches.InvalidIndex();
+ pChildPatch[1] = g_Patches.InvalidIndex();
+
+ // Get the number of displacement subdivisions.
+ int nInterval = GetWidth();
+
+ // Get the parent patch.
+ CPatch *pParentPatch = &g_Patches[iParentPatch];
+ if ( !pParentPatch )
+ return;
+
+ // Split along the longest edge.
+ Vector vecEdges[4];
+ vecEdges[0] = pParentPatch->winding->p[1] - pParentPatch->winding->p[0];
+ vecEdges[1] = pParentPatch->winding->p[2] - pParentPatch->winding->p[1];
+ vecEdges[2] = pParentPatch->winding->p[3] - pParentPatch->winding->p[2];
+ vecEdges[3] = pParentPatch->winding->p[3] - pParentPatch->winding->p[0];
+
+ // Should the patch be subdivided - check the area.
+ float flMaxLength = max( m_flSampleWidth, m_flSampleHeight );
+ float flMinEdgeLength = flMaxLength * dispchop;
+
+ // Find the longest edge.
+ float flEdgeLength = 0.0f;
+ int iLongEdge = -1;
+ for ( int iEdge = 0; iEdge < 4; ++iEdge )
+ {
+ float flLength = vecEdges[iEdge].Length();
+ if ( flEdgeLength < flLength )
+ {
+ flEdgeLength = vecEdges[iEdge].Length();
+ iLongEdge = iEdge;
+ }
+ }
+
+ // Small enough already, return.
+ if ( flEdgeLength < flMinEdgeLength )
+ return;
+
+ // Test area as well so we don't allow slivers.
+ float flMinArea = ( dispchop * flMaxLength ) * ( dispchop * flMaxLength );
+ Vector vecNormal = vecEdges[3].Cross( vecEdges[0] );
+ float flTestArea = VectorNormalize( vecNormal );
+ if ( flTestArea < flMinArea )
+ return;
+
+ // Get the points for the first triangle.
+ int iPoints[3];
+ Vector vecPoints[3];
+ float flArea;
+
+ iPoints[0] = ( nInterval * nInterval ) - 1;
+ iPoints[1] = 0;
+ iPoints[2] = nInterval * ( nInterval - 1 );
+ for ( int iPoint = 0; iPoint < 3; ++iPoint )
+ {
+ VectorCopy( m_aVerts[iPoints[iPoint]], vecPoints[iPoint] );
+ }
+
+ // Create and initialize the patch.
+ pChildPatch[0] = g_Patches.AddToTail();
+ if ( pChildPatch[0] == g_Patches.InvalidIndex() )
+ return;
+
+ if ( !InitPatch( pChildPatch[0], iParentPatch, 0, vecPoints, iPoints, flArea ) )
+ {
+ g_Patches.Remove( pChildPatch[0] );
+ pChildPatch[0] = g_Patches.InvalidIndex();
+ return;
+ }
+
+ // Get the points for the second triangle.
+ iPoints[0] = 0;
+ iPoints[1] = ( nInterval * nInterval ) - 1;
+ iPoints[2] = nInterval - 1;
+ for ( int iPoint = 0; iPoint < 3; ++iPoint )
+ {
+ VectorCopy( m_aVerts[iPoints[iPoint]], vecPoints[iPoint] );
+ }
+
+ // Create and initialize the patch.
+ pChildPatch[1] = g_Patches.AddToTail();
+ if ( pChildPatch[1] == g_Patches.InvalidIndex() )
+ {
+ g_Patches.Remove( pChildPatch[0] );
+ pChildPatch[0] = g_Patches.InvalidIndex();
+ return;
+ }
+
+ if ( !InitPatch( pChildPatch[1], iParentPatch, 1, vecPoints, iPoints, flArea ) )
+ {
+ g_Patches.Remove( pChildPatch[0] );
+ pChildPatch[0] = g_Patches.InvalidIndex();
+ g_Patches.Remove( pChildPatch[1] );
+ pChildPatch[1] = g_Patches.InvalidIndex();
+ return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : flMinArea -
+// Output : float
+//-----------------------------------------------------------------------------
+void CVRADDispColl::CreateChildPatches( int iParentPatch, int nLevel )
+{
+ // Get the parent patch.
+ CPatch *pParentPatch = &g_Patches[iParentPatch];
+ if ( !pParentPatch )
+ return;
+
+ // The root face is a quad - special case.
+ if ( pParentPatch->winding->numpoints == 4 )
+ {
+ int iChildPatch[2];
+ CreateChildPatchesFromRoot( iParentPatch, iChildPatch );
+ if ( iChildPatch[0] != g_Patches.InvalidIndex() && iChildPatch[1] != g_Patches.InvalidIndex() )
+ {
+ CreateChildPatches( iChildPatch[0], 0 );
+ CreateChildPatches( iChildPatch[1], 0 );
+ }
+ return;
+ }
+
+ // Calculate the the area of the patch (triangle!).
+ Assert( pParentPatch->winding->numpoints == 3 );
+ if ( pParentPatch->winding->numpoints != 3 )
+ return;
+
+ // Should the patch be subdivided - check the area.
+ float flMaxLength = max( m_flSampleWidth, m_flSampleHeight );
+ float flMinEdgeLength = flMaxLength * dispchop;
+
+ // Split along the longest edge.
+ Vector vecEdges[3];
+ vecEdges[0] = pParentPatch->winding->p[1] - pParentPatch->winding->p[0];
+ vecEdges[1] = pParentPatch->winding->p[2] - pParentPatch->winding->p[0];
+ vecEdges[2] = pParentPatch->winding->p[2] - pParentPatch->winding->p[1];
+
+ // Find the longest edge.
+ float flEdgeLength = 0.0f;
+ int iLongEdge = -1;
+ for ( int iEdge = 0; iEdge < 3; ++iEdge )
+ {
+ if ( flEdgeLength < vecEdges[iEdge].Length() )
+ {
+ flEdgeLength = vecEdges[iEdge].Length();
+ iLongEdge = iEdge;
+ }
+ }
+
+ // Small enough already, return.
+ if ( flEdgeLength < flMinEdgeLength )
+ return;
+
+ // Test area as well so we don't allow slivers.
+ float flMinArea = ( dispchop * flMaxLength ) * ( dispchop * flMaxLength ) * 0.5f;
+ Vector vecNormal = vecEdges[1].Cross( vecEdges[0] );
+ float flTestArea = VectorNormalize( vecNormal );
+ flTestArea *= 0.5f;
+ if ( flTestArea < flMinArea )
+ return;
+
+ // Check to see if any more displacement verts exist - go to subdivision if not.
+ if ( nLevel >= ( m_nPower * 2 ) )
+ {
+ CreateChildPatchesSub( iParentPatch );
+ return;
+ }
+
+ int nChildIndices[2][3];
+ int nNewIndex = ( pParentPatch->indices[1] + pParentPatch->indices[0] ) / 2;
+ nChildIndices[0][0] = pParentPatch->indices[2];
+ nChildIndices[0][1] = pParentPatch->indices[0];
+ nChildIndices[0][2] = nNewIndex;
+
+ nChildIndices[1][0] = pParentPatch->indices[1];
+ nChildIndices[1][1] = pParentPatch->indices[2];
+ nChildIndices[1][2] = nNewIndex;
+
+ Vector vecChildPoints[2][3];
+ for ( int iTri = 0; iTri < 2; ++iTri )
+ {
+ for ( int iPoint = 0; iPoint < 3; ++iPoint )
+ {
+ VectorCopy( m_aVerts[nChildIndices[iTri][iPoint]], vecChildPoints[iTri][iPoint] );
+ }
+ }
+
+ // Create and initialize the children patches.
+ int iChildPatch[2] = { -1, -1 };
+ for ( int iChild = 0; iChild < 2; ++iChild )
+ {
+ iChildPatch[iChild] = g_Patches.AddToTail();
+
+ float flArea = 0.0f;
+ if ( !InitPatch( iChildPatch[iChild], iParentPatch, iChild, vecChildPoints[iChild], nChildIndices[iChild], flArea ) )
+ {
+ if ( iChild == 0 )
+ {
+ pParentPatch->child1 = g_Patches.InvalidIndex();
+ g_Patches.Remove( iChildPatch[iChild] );
+ break;
+ }
+ else
+ {
+ pParentPatch->child1 = g_Patches.InvalidIndex();
+ pParentPatch->child2 = g_Patches.InvalidIndex();
+ g_Patches.Remove( iChildPatch[iChild] );
+ g_Patches.Remove( iChildPatch[0] );
+ }
+ }
+ }
+
+ // Continue creating children patches.
+ int nNewLevel = ++nLevel;
+ CreateChildPatches( iChildPatch[0], nNewLevel );
+ CreateChildPatches( iChildPatch[1], nNewLevel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : flMinArea -
+// Output : float
+//-----------------------------------------------------------------------------
+void CVRADDispColl::CreateChildPatchesSub( int iParentPatch )
+{
+ // Get the parent patch.
+ CPatch *pParentPatch = &g_Patches[iParentPatch];
+ if ( !pParentPatch )
+ return;
+
+ // Calculate the the area of the patch (triangle!).
+ Assert( pParentPatch->winding->numpoints == 3 );
+ if ( pParentPatch->winding->numpoints != 3 )
+ return;
+
+ // Should the patch be subdivided - check the area.
+ float flMaxLength = max( m_flSampleWidth, m_flSampleHeight );
+ float flMinEdgeLength = flMaxLength * dispchop;
+
+ // Split along the longest edge.
+ Vector vecEdges[3];
+ vecEdges[0] = pParentPatch->winding->p[1] - pParentPatch->winding->p[0];
+ vecEdges[1] = pParentPatch->winding->p[2] - pParentPatch->winding->p[1];
+ vecEdges[2] = pParentPatch->winding->p[0] - pParentPatch->winding->p[2];
+
+ // Find the longest edge.
+ float flEdgeLength = 0.0f;
+ int iLongEdge = -1;
+ for ( int iEdge = 0; iEdge < 3; ++iEdge )
+ {
+ if ( flEdgeLength < vecEdges[iEdge].Length() )
+ {
+ flEdgeLength = vecEdges[iEdge].Length();
+ iLongEdge = iEdge;
+ }
+ }
+
+ // Small enough already, return.
+ if ( flEdgeLength < flMinEdgeLength )
+ return;
+
+ // Test area as well so we don't allow slivers.
+ float flMinArea = ( dispchop * flMaxLength ) * ( dispchop * flMaxLength ) * 0.5f;
+ Vector vecNormal = vecEdges[1].Cross( vecEdges[0] );
+ float flTestArea = VectorNormalize( vecNormal );
+ flTestArea *= 0.5f;
+ if ( flTestArea < flMinArea )
+ return;
+
+ // Create children patchs - 2 of them.
+ Vector vecChildPoints[2][3];
+ switch ( iLongEdge )
+ {
+ case 0:
+ {
+ vecChildPoints[0][0] = pParentPatch->winding->p[0];
+ vecChildPoints[0][1] = ( pParentPatch->winding->p[0] + pParentPatch->winding->p[1] ) * 0.5f;
+ vecChildPoints[0][2] = pParentPatch->winding->p[2];
+
+ vecChildPoints[1][0] = ( pParentPatch->winding->p[0] + pParentPatch->winding->p[1] ) * 0.5f;
+ vecChildPoints[1][1] = pParentPatch->winding->p[1];
+ vecChildPoints[1][2] = pParentPatch->winding->p[2];
+ break;
+ }
+ case 1:
+ {
+ vecChildPoints[0][0] = pParentPatch->winding->p[0];
+ vecChildPoints[0][1] = pParentPatch->winding->p[1];
+ vecChildPoints[0][2] = ( pParentPatch->winding->p[1] + pParentPatch->winding->p[2] ) * 0.5f;
+
+ vecChildPoints[1][0] = ( pParentPatch->winding->p[1] + pParentPatch->winding->p[2] ) * 0.5f;
+ vecChildPoints[1][1] = pParentPatch->winding->p[2];
+ vecChildPoints[1][2] = pParentPatch->winding->p[0];
+ break;
+ }
+ case 2:
+ {
+ vecChildPoints[0][0] = pParentPatch->winding->p[0];
+ vecChildPoints[0][1] = pParentPatch->winding->p[1];
+ vecChildPoints[0][2] = ( pParentPatch->winding->p[0] + pParentPatch->winding->p[2] ) * 0.5f;
+
+ vecChildPoints[1][0] = ( pParentPatch->winding->p[0] + pParentPatch->winding->p[2] ) * 0.5f;
+ vecChildPoints[1][1] = pParentPatch->winding->p[1];
+ vecChildPoints[1][2] = pParentPatch->winding->p[2];
+ break;
+ }
+ }
+
+
+ // Create and initialize the children patches.
+ int iChildPatch[2] = { 0, 0 };
+ int nChildIndices[3] = { -1, -1, -1 };
+ for ( int iChild = 0; iChild < 2; ++iChild )
+ {
+ iChildPatch[iChild] = g_Patches.AddToTail();
+
+ float flArea = 0.0f;
+ if ( !InitPatch( iChildPatch[iChild], iParentPatch, iChild, vecChildPoints[iChild], nChildIndices, flArea ) )
+ {
+ if ( iChild == 0 )
+ {
+ pParentPatch->child1 = g_Patches.InvalidIndex();
+ g_Patches.Remove( iChildPatch[iChild] );
+ break;
+ }
+ else
+ {
+ pParentPatch->child1 = g_Patches.InvalidIndex();
+ pParentPatch->child2 = g_Patches.InvalidIndex();
+ g_Patches.Remove( iChildPatch[iChild] );
+ g_Patches.Remove( iChildPatch[0] );
+ }
+ }
+ }
+
+ // Continue creating children patches.
+ CreateChildPatchesSub( iChildPatch[0] );
+ CreateChildPatchesSub( iChildPatch[1] );
+}
+
+int PlaneTypeForNormal (Vector& normal)
+{
+ vec_t ax, ay, az;
+
+ // NOTE: should these have an epsilon around 1.0?
+ if (normal[0] == 1.0 || normal[0] == -1.0)
+ return PLANE_X;
+ if (normal[1] == 1.0 || normal[1] == -1.0)
+ return PLANE_Y;
+ if (normal[2] == 1.0 || normal[2] == -1.0)
+ return PLANE_Z;
+
+ ax = fabs(normal[0]);
+ ay = fabs(normal[1]);
+ az = fabs(normal[2]);
+
+ if (ax >= ay && ax >= az)
+ return PLANE_ANYX;
+ if (ay >= ax && ay >= az)
+ return PLANE_ANYY;
+ return PLANE_ANYZ;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iPatch -
+// iParentPatch -
+// iChild -
+// *pPoints -
+// *pIndices -
+// &flArea -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CVRADDispColl::InitParentPatch( int iPatch, Vector *pPoints, float &flArea )
+{
+ // Get the current patch.
+ CPatch *pPatch = &g_Patches[iPatch];
+ if ( !pPatch )
+ return false;
+
+ // Clear the patch data.
+ memset( pPatch, 0, sizeof( CPatch ) );
+
+ // This is a parent.
+ pPatch->ndxNext = g_FacePatches.Element( GetParentIndex() );
+ g_FacePatches[GetParentIndex()] = iPatch;
+ pPatch->faceNumber = GetParentIndex();
+
+ // Initialize parent and children indices.
+ pPatch->child1 = g_Patches.InvalidIndex();
+ pPatch->child2 = g_Patches.InvalidIndex();
+ pPatch->parent = g_Patches.InvalidIndex();
+ pPatch->ndxNextClusterChild = g_Patches.InvalidIndex();
+ pPatch->ndxNextParent = g_Patches.InvalidIndex();
+
+ Vector vecEdges[2];
+ vecEdges[0] = pPoints[1] - pPoints[0];
+ vecEdges[1] = pPoints[3] - pPoints[0];
+
+ // Calculate the triangle normal and area.
+ Vector vecNormal = vecEdges[1].Cross( vecEdges[0] );
+ flArea = VectorNormalize( vecNormal );
+
+ // Initialize the patch scale.
+ pPatch->scale[0] = pPatch->scale[1] = 1.0f;
+
+ // Set the patch chop - minchop (that is what the minimum area is based on).
+ pPatch->chop = dispchop;
+
+ // Displacements are not sky!
+ pPatch->sky = false;
+
+ // Copy the winding.
+ Vector vecCenter( 0.0f, 0.0f, 0.0f );
+ pPatch->winding = AllocWinding( 4 );
+ pPatch->winding->numpoints = 4;
+ for ( int iPoint = 0; iPoint < 4; ++iPoint )
+ {
+ VectorCopy( pPoints[iPoint], pPatch->winding->p[iPoint] );
+ VectorAdd( pPoints[iPoint], vecCenter, vecCenter );
+ }
+
+ // Set the origin and normal.
+ VectorScale( vecCenter, ( 1.0f / 4.0f ), vecCenter );
+ VectorCopy( vecCenter, pPatch->origin );
+ VectorCopy( vecNormal, pPatch->normal );
+
+ // Create the plane.
+ pPatch->plane = new dplane_t;
+ if ( !pPatch->plane )
+ return false;
+
+ VectorCopy( vecNormal, pPatch->plane->normal );
+ pPatch->plane->dist = vecNormal.Dot( pPoints[0] );
+ pPatch->plane->type = PlaneTypeForNormal( pPatch->plane->normal );
+ pPatch->planeDist = pPatch->plane->dist;
+
+ // Set the area.
+ pPatch->area = flArea;
+
+ // Calculate the mins/maxs.
+ Vector vecMin( FLT_MAX, FLT_MAX, FLT_MAX );
+ Vector vecMax( FLT_MIN, FLT_MIN, FLT_MIN );
+ for ( int iPoint = 0; iPoint < 4; ++iPoint )
+ {
+ for ( int iAxis = 0; iAxis < 3; ++iAxis )
+ {
+ vecMin[iAxis] = min( vecMin[iAxis], pPoints[iPoint][iAxis] );
+ vecMax[iAxis] = max( vecMax[iAxis], pPoints[iPoint][iAxis] );
+ }
+ }
+
+ VectorCopy( vecMin, pPatch->mins );
+ VectorCopy( vecMax, pPatch->maxs );
+ VectorCopy( vecMin, pPatch->face_mins );
+ VectorCopy( vecMax, pPatch->face_maxs );
+
+ // Check for bumpmap.
+ dface_t *pFace = dfaces + pPatch->faceNumber;
+ texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
+ pPatch->needsBumpmap = pTexInfo->flags & SURF_BUMPLIGHT ? true : false;
+
+ // Misc...
+ pPatch->m_IterationKey = 0;
+
+ // Calculate the base light, area, and reflectivity.
+ BaseLightForFace( &g_pFaces[pPatch->faceNumber], pPatch->baselight, &pPatch->basearea, pPatch->reflectivity );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPatch -
+// *pPoints -
+// &vecNormal -
+// flArea -
+//-----------------------------------------------------------------------------
+bool CVRADDispColl::InitPatch( int iPatch, int iParentPatch, int iChild, Vector *pPoints, int *pIndices, float &flArea )
+{
+ // Get the current patch.
+ CPatch *pPatch = &g_Patches[iPatch];
+ if ( !pPatch )
+ return false;
+
+ // Clear the patch data.
+ memset( pPatch, 0, sizeof( CPatch ) );
+
+ // Setup the parent if we are not the parent.
+ CPatch *pParentPatch = NULL;
+ if ( iParentPatch != g_Patches.InvalidIndex() )
+ {
+ // Get the parent patch.
+ pParentPatch = &g_Patches[iParentPatch];
+ if ( !pParentPatch )
+ return false;
+ }
+
+ // Attach the face to the correct lists.
+ if ( !pParentPatch )
+ {
+ // This is a parent.
+ pPatch->ndxNext = g_FacePatches.Element( GetParentIndex() );
+ g_FacePatches[GetParentIndex()] = iPatch;
+ pPatch->faceNumber = GetParentIndex();
+ }
+ else
+ {
+ pPatch->ndxNext = g_Patches.InvalidIndex();
+ pPatch->faceNumber = pParentPatch->faceNumber;
+
+ // Attach to the parent patch.
+ if ( iChild == 0 )
+ {
+ pParentPatch->child1 = iPatch;
+ }
+ else
+ {
+ pParentPatch->child2 = iPatch;
+ }
+ }
+
+ // Initialize parent and children indices.
+ pPatch->child1 = g_Patches.InvalidIndex();
+ pPatch->child2 = g_Patches.InvalidIndex();
+ pPatch->ndxNextClusterChild = g_Patches.InvalidIndex();
+ pPatch->ndxNextParent = g_Patches.InvalidIndex();
+ pPatch->parent = iParentPatch;
+
+ // Get triangle edges.
+ Vector vecEdges[3];
+ vecEdges[0] = pPoints[1] - pPoints[0];
+ vecEdges[1] = pPoints[2] - pPoints[0];
+ vecEdges[2] = pPoints[2] - pPoints[1];
+
+ // Find the longest edge.
+// float flEdgeLength = 0.0f;
+// for ( int iEdge = 0; iEdge < 3; ++iEdge )
+// {
+// if ( flEdgeLength < vecEdges[iEdge].Length() )
+// {
+// flEdgeLength = vecEdges[iEdge].Length();
+// }
+// }
+
+ // Calculate the triangle normal and area.
+ Vector vecNormal = vecEdges[1].Cross( vecEdges[0] );
+ flArea = VectorNormalize( vecNormal );
+ flArea *= 0.5f;
+
+ // Initialize the patch scale.
+ pPatch->scale[0] = pPatch->scale[1] = 1.0f;
+
+ // Set the patch chop - minchop (that is what the minimum area is based on).
+ pPatch->chop = dispchop;
+
+ // Displacements are not sky!
+ pPatch->sky = false;
+
+ // Copy the winding.
+ Vector vecCenter( 0.0f, 0.0f, 0.0f );
+ pPatch->winding = AllocWinding( 3 );
+ pPatch->winding->numpoints = 3;
+ for ( int iPoint = 0; iPoint < 3; ++iPoint )
+ {
+ VectorCopy( pPoints[iPoint], pPatch->winding->p[iPoint] );
+ VectorAdd( pPoints[iPoint], vecCenter, vecCenter );
+
+ pPatch->indices[iPoint] = static_cast<short>( pIndices[iPoint] );
+ }
+
+ // Set the origin and normal.
+ VectorScale( vecCenter, ( 1.0f / 3.0f ), vecCenter );
+ VectorCopy( vecCenter, pPatch->origin );
+ VectorCopy( vecNormal, pPatch->normal );
+
+ // Create the plane.
+ pPatch->plane = new dplane_t;
+ if ( !pPatch->plane )
+ return false;
+
+ VectorCopy( vecNormal, pPatch->plane->normal );
+ pPatch->plane->dist = vecNormal.Dot( pPoints[0] );
+ pPatch->plane->type = PlaneTypeForNormal( pPatch->plane->normal );
+ pPatch->planeDist = pPatch->plane->dist;
+
+ // Set the area.
+ pPatch->area = flArea;
+
+ // Calculate the mins/maxs.
+ Vector vecMin( FLT_MAX, FLT_MAX, FLT_MAX );
+ Vector vecMax( FLT_MIN, FLT_MIN, FLT_MIN );
+ for ( int iPoint = 0; iPoint < 3; ++iPoint )
+ {
+ for ( int iAxis = 0; iAxis < 3; ++iAxis )
+ {
+ vecMin[iAxis] = min( vecMin[iAxis], pPoints[iPoint][iAxis] );
+ vecMax[iAxis] = max( vecMax[iAxis], pPoints[iPoint][iAxis] );
+ }
+ }
+
+ VectorCopy( vecMin, pPatch->mins );
+ VectorCopy( vecMax, pPatch->maxs );
+
+ if ( !pParentPatch )
+ {
+ VectorCopy( vecMin, pPatch->face_mins );
+ VectorCopy( vecMax, pPatch->face_maxs );
+ }
+ else
+ {
+ VectorCopy( pParentPatch->face_mins, pPatch->face_mins );
+ VectorCopy( pParentPatch->face_maxs, pPatch->face_maxs );
+ }
+
+ // Check for bumpmap.
+ dface_t *pFace = dfaces + pPatch->faceNumber;
+ texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
+ pPatch->needsBumpmap = pTexInfo->flags & SURF_BUMPLIGHT ? true : false;
+
+ // Misc...
+ pPatch->m_IterationKey = 0;
+
+ // Get the base light for the face.
+ if ( !pParentPatch )
+ {
+ BaseLightForFace( &g_pFaces[pPatch->faceNumber], pPatch->baselight, &pPatch->basearea, pPatch->reflectivity );
+ }
+ else
+ {
+ VectorCopy( pParentPatch->baselight, pPatch->baselight );
+ pPatch->basearea = pParentPatch->basearea;
+ pPatch->reflectivity = pParentPatch->reflectivity;
+ }
+
+ return true;
+}
+
+void CVRADDispColl::AddPolysForRayTrace( void )
+{
+ if ( !( m_nContents & MASK_OPAQUE ) )
+ return;
+
+ for ( int ndxTri = 0; ndxTri < m_aTris.Size(); ndxTri++ )
+ {
+ CDispCollTri *tri = m_aTris.Base() + ndxTri;
+ int v[3];
+ for ( int ndxv = 0; ndxv < 3; ndxv++ )
+ v[ndxv] = tri->GetVert(ndxv);
+
+ Vector fullCoverage;
+ fullCoverage.x = 1.0f;
+ g_RtEnv.AddTriangle( TRACE_ID_OPAQUE, m_aVerts[v[0]], m_aVerts[v[1]], m_aVerts[v[2]], fullCoverage );
+ }
} \ No newline at end of file
diff --git a/mp/src/utils/vrad/vrad_dispcoll.h b/mp/src/utils/vrad/vrad_dispcoll.h
index 668d3118..787c3137 100644
--- a/mp/src/utils/vrad/vrad_dispcoll.h
+++ b/mp/src/utils/vrad/vrad_dispcoll.h
@@ -1,80 +1,80 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef VRAD_DISPCOLL_H
-#define VRAD_DISPCOLL_H
-#pragma once
-
-#include <assert.h>
-#include "DispColl_Common.h"
-
-//=============================================================================
-//
-// VRAD specific collision
-//
-#define VRAD_QUAD_SIZE 4
-
-struct CPatch;
-
-class CVRADDispColl : public CDispCollTree
-{
-public:
-
- // Creation/Destruction Functions
- CVRADDispColl();
- ~CVRADDispColl();
- bool Create( CCoreDispInfo *pDisp );
-
- // Patches.
- bool InitPatch( int iPatch, int iParentPatch, int iChild, Vector *pPoints, int *pIndices, float &flArea );
- bool InitParentPatch( int iPatch, Vector *pPoints, float &flArea );
- float CreateParentPatches( void );
- void CreateChildPatches( int iParentPatch, int nLevel );
- void CreateChildPatchesFromRoot( int iParentPatch, int *pChildPatch );
- void CreateChildPatchesSub( int iParentPatch );
-
- // Operations Functions
- void BaseFacePlaneToDispUV( Vector const &vecPlanePt, Vector2D &dispUV );
- void DispUVToSurfPoint( Vector2D const &dispUV, Vector &vecPoint, float flPushEps );
- void DispUVToSurfNormal( Vector2D const &dispUV, Vector &vecNormal );
-
- // Data.
- inline float GetSampleRadius2( void ) { return m_flSampleRadius2; }
- inline float GetPatchSampleRadius2( void ) { return m_flPatchSampleRadius2; }
-
- inline int GetParentIndex( void ) { return m_iParent; }
- inline void GetParentFaceNormal( Vector &vecNormal ) { vecNormal = m_vecStabDir; }
-
- inline void GetVert( int iVert, Vector &vecVert ) { Assert( ( iVert >= 0 ) && ( iVert < GetSize() ) ); vecVert = m_aVerts[iVert]; }
- inline void GetVertNormal( int iVert, Vector &vecNormal ) { Assert( ( iVert >= 0 ) && ( iVert < GetSize() ) ); vecNormal = m_aVertNormals[iVert]; }
- inline Vector2D const& GetLuxelCoord( int iLuxel ) { Assert( ( iLuxel >= 0 ) && ( iLuxel < GetSize() ) ); return m_aLuxelCoords[iLuxel]; }
-
- // Raytracing
- void AddPolysForRayTrace( void );
-
-protected:
-
- void CalcSampleRadius2AndBox( dface_t *pFace );
-
- // Utility.
- void DispUVToSurf_TriTLToBR( Vector &vecPoint, float flPushEps, float flU, float flV, int nSnapU, int nSnapV, int nWidth, int nHeight );
- void DispUVToSurf_TriBLToTR( Vector &vecPoint, float flPushEps, float flU, float flV, int nSnapU, int nSnapV, int nWidth, int nHeight );
- void GetSurfaceMinMax( Vector &boxMin, Vector &boxMax );
- void GetMinorAxes( Vector const &vecNormal, int &nAxis0, int &nAxis1 );
-
-protected:
-
- int m_iParent; // Parent index
- float m_flSampleRadius2; // Sampling radius
- float m_flPatchSampleRadius2; // Patch sampling radius (max bound)
- float m_flSampleWidth;
- float m_flSampleHeight;
- CUtlVector<Vector2D> m_aLuxelCoords; // Lightmap coordinates.
- CUtlVector<Vector> m_aVertNormals; // Displacement vertex normals
-};
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef VRAD_DISPCOLL_H
+#define VRAD_DISPCOLL_H
+#pragma once
+
+#include <assert.h>
+#include "DispColl_Common.h"
+
+//=============================================================================
+//
+// VRAD specific collision
+//
+#define VRAD_QUAD_SIZE 4
+
+struct CPatch;
+
+class CVRADDispColl : public CDispCollTree
+{
+public:
+
+ // Creation/Destruction Functions
+ CVRADDispColl();
+ ~CVRADDispColl();
+ bool Create( CCoreDispInfo *pDisp );
+
+ // Patches.
+ bool InitPatch( int iPatch, int iParentPatch, int iChild, Vector *pPoints, int *pIndices, float &flArea );
+ bool InitParentPatch( int iPatch, Vector *pPoints, float &flArea );
+ float CreateParentPatches( void );
+ void CreateChildPatches( int iParentPatch, int nLevel );
+ void CreateChildPatchesFromRoot( int iParentPatch, int *pChildPatch );
+ void CreateChildPatchesSub( int iParentPatch );
+
+ // Operations Functions
+ void BaseFacePlaneToDispUV( Vector const &vecPlanePt, Vector2D &dispUV );
+ void DispUVToSurfPoint( Vector2D const &dispUV, Vector &vecPoint, float flPushEps );
+ void DispUVToSurfNormal( Vector2D const &dispUV, Vector &vecNormal );
+
+ // Data.
+ inline float GetSampleRadius2( void ) { return m_flSampleRadius2; }
+ inline float GetPatchSampleRadius2( void ) { return m_flPatchSampleRadius2; }
+
+ inline int GetParentIndex( void ) { return m_iParent; }
+ inline void GetParentFaceNormal( Vector &vecNormal ) { vecNormal = m_vecStabDir; }
+
+ inline void GetVert( int iVert, Vector &vecVert ) { Assert( ( iVert >= 0 ) && ( iVert < GetSize() ) ); vecVert = m_aVerts[iVert]; }
+ inline void GetVertNormal( int iVert, Vector &vecNormal ) { Assert( ( iVert >= 0 ) && ( iVert < GetSize() ) ); vecNormal = m_aVertNormals[iVert]; }
+ inline Vector2D const& GetLuxelCoord( int iLuxel ) { Assert( ( iLuxel >= 0 ) && ( iLuxel < GetSize() ) ); return m_aLuxelCoords[iLuxel]; }
+
+ // Raytracing
+ void AddPolysForRayTrace( void );
+
+protected:
+
+ void CalcSampleRadius2AndBox( dface_t *pFace );
+
+ // Utility.
+ void DispUVToSurf_TriTLToBR( Vector &vecPoint, float flPushEps, float flU, float flV, int nSnapU, int nSnapV, int nWidth, int nHeight );
+ void DispUVToSurf_TriBLToTR( Vector &vecPoint, float flPushEps, float flU, float flV, int nSnapU, int nSnapV, int nWidth, int nHeight );
+ void GetSurfaceMinMax( Vector &boxMin, Vector &boxMax );
+ void GetMinorAxes( Vector const &vecNormal, int &nAxis0, int &nAxis1 );
+
+protected:
+
+ int m_iParent; // Parent index
+ float m_flSampleRadius2; // Sampling radius
+ float m_flPatchSampleRadius2; // Patch sampling radius (max bound)
+ float m_flSampleWidth;
+ float m_flSampleHeight;
+ CUtlVector<Vector2D> m_aLuxelCoords; // Lightmap coordinates.
+ CUtlVector<Vector> m_aVertNormals; // Displacement vertex normals
+};
+
#endif // VRAD_DISPCOLL_H \ No newline at end of file
diff --git a/mp/src/utils/vrad/vrad_dll.vpc b/mp/src/utils/vrad/vrad_dll.vpc
index 02fb290a..a1ae67b4 100644
--- a/mp/src/utils/vrad/vrad_dll.vpc
+++ b/mp/src/utils/vrad/vrad_dll.vpc
@@ -1,225 +1,225 @@
-//-----------------------------------------------------------------------------
-// VRAD_DLL.VPC
-//
-// Project Script
-//-----------------------------------------------------------------------------
-
-$Macro SRCDIR "..\.."
-$Macro OUTBINDIR "$SRCDIR\..\game\bin"
-
-$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
-
-$Configuration
-{
- $Compiler
- {
- $AdditionalIncludeDirectories "$BASE,..\common,..\vmpi,..\vmpi\mysql\mysqlpp\include,..\vmpi\mysql\include"
- $PreprocessorDefinitions "$BASE;MPI;PROTECTED_THINGS_DISABLE;VRAD"
- }
-
- $Linker
- {
- $AdditionalDependencies "$BASE ws2_32.lib"
- }
-}
-
-$Project "Vrad_dll"
-{
- $Folder "Source Files"
- {
- $File "$SRCDIR\public\BSPTreeData.cpp"
- $File "$SRCDIR\public\disp_common.cpp"
- $File "$SRCDIR\public\disp_powerinfo.cpp"
- $File "disp_vrad.cpp"
- $File "imagepacker.cpp"
- $File "incremental.cpp"
- $File "leaf_ambient_lighting.cpp"
- $File "lightmap.cpp"
- $File "$SRCDIR\public\loadcmdline.cpp"
- $File "$SRCDIR\public\lumpfiles.cpp"
- $File "macro_texture.cpp"
- $File "..\common\mpi_stats.cpp"
- $File "mpivrad.cpp"
- $File "..\common\MySqlDatabase.cpp"
- $File "..\common\pacifier.cpp"
- $File "..\common\physdll.cpp"
- $File "radial.cpp"
- $File "SampleHash.cpp"
- $File "trace.cpp"
- $File "..\common\utilmatlib.cpp"
- $File "vismat.cpp"
- $File "..\common\vmpi_tools_shared.cpp"
- $File "..\common\vmpi_tools_shared.h"
- $File "vrad.cpp"
- $File "VRAD_DispColl.cpp"
- $File "VradDetailProps.cpp"
- $File "VRadDisps.cpp"
- $File "vraddll.cpp"
- $File "VRadStaticProps.cpp"
- $File "$SRCDIR\public\zip_utils.cpp"
-
- $Folder "Common Files"
- {
- $File "..\common\bsplib.cpp"
- $File "$SRCDIR\public\builddisp.cpp"
- $File "$SRCDIR\public\ChunkFile.cpp"
- $File "..\common\cmdlib.cpp"
- $File "$SRCDIR\public\DispColl_Common.cpp"
- $File "..\common\map_shared.cpp"
- $File "..\common\polylib.cpp"
- $File "..\common\scriplib.cpp"
- $File "..\common\threads.cpp"
- $File "..\common\tools_minidump.cpp"
- $File "..\common\tools_minidump.h"
- }
-
- $Folder "Public Files"
- {
- $File "$SRCDIR\public\CollisionUtils.cpp"
- $File "$SRCDIR\public\filesystem_helpers.cpp"
- $File "$SRCDIR\public\ScratchPad3D.cpp"
- $File "$SRCDIR\public\ScratchPadUtils.cpp"
- }
- }
-
- $Folder "Header Files"
- {
- $File "disp_vrad.h"
- $File "iincremental.h"
- $File "imagepacker.h"
- $File "incremental.h"
- $File "leaf_ambient_lighting.h"
- $File "lightmap.h"
- $File "macro_texture.h"
- $File "$SRCDIR\public\map_utils.h"
- $File "mpivrad.h"
- $File "radial.h"
- $File "$SRCDIR\public\bitmap\tgawriter.h"
- $File "vismat.h"
- $File "vrad.h"
- $File "VRAD_DispColl.h"
- $File "vraddetailprops.h"
- $File "vraddll.h"
-
- $Folder "Common Header Files"
- {
- $File "..\common\bsplib.h"
- $File "..\common\cmdlib.h"
- $File "..\common\consolewnd.h"
- $File "..\vmpi\ichannel.h"
- $File "..\vmpi\imysqlwrapper.h"
- $File "..\vmpi\iphelpers.h"
- $File "..\common\ISQLDBReplyTarget.h"
- $File "..\common\map_shared.h"
- $File "..\vmpi\messbuf.h"
- $File "..\common\mpi_stats.h"
- $File "..\common\MySqlDatabase.h"
- $File "..\common\pacifier.h"
- $File "..\common\polylib.h"
- $File "..\common\scriplib.h"
- $File "..\vmpi\threadhelpers.h"
- $File "..\common\threads.h"
- $File "..\common\utilmatlib.h"
- $File "..\vmpi\vmpi_defs.h"
- $File "..\vmpi\vmpi_dispatch.h"
- $File "..\vmpi\vmpi_distribute_work.h"
- $File "..\vmpi\vmpi_filesystem.h"
- }
-
- $Folder "Public Header Files"
- {
- $File "$SRCDIR\public\mathlib\amd3dx.h"
- $File "$SRCDIR\public\mathlib\ANORMS.H"
- $File "$SRCDIR\public\basehandle.h"
- $File "$SRCDIR\public\tier0\basetypes.h"
- $File "$SRCDIR\public\tier1\bitbuf.h"
- $File "$SRCDIR\public\bitvec.h"
- $File "$SRCDIR\public\BSPFILE.H"
- $File "$SRCDIR\public\bspflags.h"
- $File "$SRCDIR\public\BSPTreeData.h"
- $File "$SRCDIR\public\builddisp.h"
- $File "$SRCDIR\public\mathlib\bumpvects.h"
- $File "$SRCDIR\public\tier1\byteswap.h"
- $File "$SRCDIR\public\tier1\characterset.h"
- $File "$SRCDIR\public\tier1\checksum_crc.h"
- $File "$SRCDIR\public\tier1\checksum_md5.h"
- $File "$SRCDIR\public\ChunkFile.h"
- $File "$SRCDIR\public\cmodel.h"
- $File "$SRCDIR\public\CollisionUtils.h"
- $File "$SRCDIR\public\tier0\commonmacros.h"
- $File "$SRCDIR\public\mathlib\compressed_vector.h"
- $File "$SRCDIR\public\const.h"
- $File "$SRCDIR\public\coordsize.h"
- $File "$SRCDIR\public\tier0\dbg.h"
- $File "$SRCDIR\public\disp_common.h"
- $File "$SRCDIR\public\disp_powerinfo.h"
- $File "$SRCDIR\public\disp_vertindex.h"
- $File "$SRCDIR\public\DispColl_Common.h"
- $File "$SRCDIR\public\tier0\fasttimer.h"
- $File "$SRCDIR\public\filesystem.h"
- $File "$SRCDIR\public\filesystem_helpers.h"
- $File "$SRCDIR\public\GameBSPFile.h"
- $File "$SRCDIR\public\gametrace.h"
- $File "$SRCDIR\public\mathlib\halton.h"
- $File "$SRCDIR\public\materialsystem\hardwareverts.h"
- $File "$SRCDIR\public\appframework\IAppSystem.h"
- $File "$SRCDIR\public\tier0\icommandline.h"
- $File "$SRCDIR\public\ihandleentity.h"
- $File "$SRCDIR\public\materialsystem\imaterial.h"
- $File "$SRCDIR\public\materialsystem\imaterialsystem.h"
- $File "$SRCDIR\public\materialsystem\imaterialvar.h"
- $File "$SRCDIR\public\tier1\interface.h"
- $File "$SRCDIR\public\iscratchpad3d.h"
- $File "$SRCDIR\public\ivraddll.h"
- $File "$SRCDIR\public\materialsystem\materialsystem_config.h"
- $File "$SRCDIR\public\mathlib\mathlib.h"
- $File "$SRCDIR\public\tier0\memdbgon.h"
- $File "$SRCDIR\public\optimize.h"
- $File "$SRCDIR\public\phyfile.h"
- $File "..\common\physdll.h"
- $File "$SRCDIR\public\tier0\platform.h"
- $File "$SRCDIR\public\tier0\protected_things.h"
- $File "$SRCDIR\public\vstdlib\random.h"
- $File "$SRCDIR\public\ScratchPad3D.h"
- $File "$SRCDIR\public\ScratchPadUtils.h"
- $File "$SRCDIR\public\string_t.h"
- $File "$SRCDIR\public\tier1\strtools.h"
- $File "$SRCDIR\public\studio.h"
- $File "$SRCDIR\public\tier1\tokenreader.h"
- $File "$SRCDIR\public\trace.h"
- $File "$SRCDIR\public\tier1\utlbuffer.h"
- $File "$SRCDIR\public\tier1\utldict.h"
- $File "$SRCDIR\public\tier1\utlhash.h"
- $File "$SRCDIR\public\tier1\utllinkedlist.h"
- $File "$SRCDIR\public\tier1\utlmemory.h"
- $File "$SRCDIR\public\tier1\utlrbtree.h"
- $File "$SRCDIR\public\tier1\utlsymbol.h"
- $File "$SRCDIR\public\tier1\utlvector.h"
- $File "$SRCDIR\public\vcollide.h"
- $File "$SRCDIR\public\mathlib\vector.h"
- $File "$SRCDIR\public\mathlib\vector2d.h"
- $File "$SRCDIR\public\mathlib\vector4d.h"
- $File "$SRCDIR\public\mathlib\vmatrix.h"
- $File "..\vmpi\vmpi.h"
- $File "$SRCDIR\public\vphysics_interface.h"
- $File "$SRCDIR\public\mathlib\vplane.h"
- $File "$SRCDIR\public\tier0\vprof.h"
- $File "$SRCDIR\public\vstdlib\vstdlib.h"
- $File "$SRCDIR\public\vtf\vtf.h"
- $File "$SRCDIR\public\wadtypes.h"
- $File "$SRCDIR\public\worldsize.h"
- }
- }
-
- $Folder "Link Libraries"
- {
- $Lib bitmap
- $Lib mathlib
- $Lib raytrace
- $Lib tier2
- $Lib vmpi
- $Lib vtf
- }
-
- $File "notes.txt"
-}
+//-----------------------------------------------------------------------------
+// VRAD_DLL.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "$BASE,..\common,..\vmpi,..\vmpi\mysql\mysqlpp\include,..\vmpi\mysql\include"
+ $PreprocessorDefinitions "$BASE;MPI;PROTECTED_THINGS_DISABLE;VRAD"
+ }
+
+ $Linker
+ {
+ $AdditionalDependencies "$BASE ws2_32.lib"
+ }
+}
+
+$Project "Vrad_dll"
+{
+ $Folder "Source Files"
+ {
+ $File "$SRCDIR\public\BSPTreeData.cpp"
+ $File "$SRCDIR\public\disp_common.cpp"
+ $File "$SRCDIR\public\disp_powerinfo.cpp"
+ $File "disp_vrad.cpp"
+ $File "imagepacker.cpp"
+ $File "incremental.cpp"
+ $File "leaf_ambient_lighting.cpp"
+ $File "lightmap.cpp"
+ $File "$SRCDIR\public\loadcmdline.cpp"
+ $File "$SRCDIR\public\lumpfiles.cpp"
+ $File "macro_texture.cpp"
+ $File "..\common\mpi_stats.cpp"
+ $File "mpivrad.cpp"
+ $File "..\common\MySqlDatabase.cpp"
+ $File "..\common\pacifier.cpp"
+ $File "..\common\physdll.cpp"
+ $File "radial.cpp"
+ $File "SampleHash.cpp"
+ $File "trace.cpp"
+ $File "..\common\utilmatlib.cpp"
+ $File "vismat.cpp"
+ $File "..\common\vmpi_tools_shared.cpp"
+ $File "..\common\vmpi_tools_shared.h"
+ $File "vrad.cpp"
+ $File "VRAD_DispColl.cpp"
+ $File "VradDetailProps.cpp"
+ $File "VRadDisps.cpp"
+ $File "vraddll.cpp"
+ $File "VRadStaticProps.cpp"
+ $File "$SRCDIR\public\zip_utils.cpp"
+
+ $Folder "Common Files"
+ {
+ $File "..\common\bsplib.cpp"
+ $File "$SRCDIR\public\builddisp.cpp"
+ $File "$SRCDIR\public\ChunkFile.cpp"
+ $File "..\common\cmdlib.cpp"
+ $File "$SRCDIR\public\DispColl_Common.cpp"
+ $File "..\common\map_shared.cpp"
+ $File "..\common\polylib.cpp"
+ $File "..\common\scriplib.cpp"
+ $File "..\common\threads.cpp"
+ $File "..\common\tools_minidump.cpp"
+ $File "..\common\tools_minidump.h"
+ }
+
+ $Folder "Public Files"
+ {
+ $File "$SRCDIR\public\CollisionUtils.cpp"
+ $File "$SRCDIR\public\filesystem_helpers.cpp"
+ $File "$SRCDIR\public\ScratchPad3D.cpp"
+ $File "$SRCDIR\public\ScratchPadUtils.cpp"
+ }
+ }
+
+ $Folder "Header Files"
+ {
+ $File "disp_vrad.h"
+ $File "iincremental.h"
+ $File "imagepacker.h"
+ $File "incremental.h"
+ $File "leaf_ambient_lighting.h"
+ $File "lightmap.h"
+ $File "macro_texture.h"
+ $File "$SRCDIR\public\map_utils.h"
+ $File "mpivrad.h"
+ $File "radial.h"
+ $File "$SRCDIR\public\bitmap\tgawriter.h"
+ $File "vismat.h"
+ $File "vrad.h"
+ $File "VRAD_DispColl.h"
+ $File "vraddetailprops.h"
+ $File "vraddll.h"
+
+ $Folder "Common Header Files"
+ {
+ $File "..\common\bsplib.h"
+ $File "..\common\cmdlib.h"
+ $File "..\common\consolewnd.h"
+ $File "..\vmpi\ichannel.h"
+ $File "..\vmpi\imysqlwrapper.h"
+ $File "..\vmpi\iphelpers.h"
+ $File "..\common\ISQLDBReplyTarget.h"
+ $File "..\common\map_shared.h"
+ $File "..\vmpi\messbuf.h"
+ $File "..\common\mpi_stats.h"
+ $File "..\common\MySqlDatabase.h"
+ $File "..\common\pacifier.h"
+ $File "..\common\polylib.h"
+ $File "..\common\scriplib.h"
+ $File "..\vmpi\threadhelpers.h"
+ $File "..\common\threads.h"
+ $File "..\common\utilmatlib.h"
+ $File "..\vmpi\vmpi_defs.h"
+ $File "..\vmpi\vmpi_dispatch.h"
+ $File "..\vmpi\vmpi_distribute_work.h"
+ $File "..\vmpi\vmpi_filesystem.h"
+ }
+
+ $Folder "Public Header Files"
+ {
+ $File "$SRCDIR\public\mathlib\amd3dx.h"
+ $File "$SRCDIR\public\mathlib\ANORMS.H"
+ $File "$SRCDIR\public\basehandle.h"
+ $File "$SRCDIR\public\tier0\basetypes.h"
+ $File "$SRCDIR\public\tier1\bitbuf.h"
+ $File "$SRCDIR\public\bitvec.h"
+ $File "$SRCDIR\public\BSPFILE.H"
+ $File "$SRCDIR\public\bspflags.h"
+ $File "$SRCDIR\public\BSPTreeData.h"
+ $File "$SRCDIR\public\builddisp.h"
+ $File "$SRCDIR\public\mathlib\bumpvects.h"
+ $File "$SRCDIR\public\tier1\byteswap.h"
+ $File "$SRCDIR\public\tier1\characterset.h"
+ $File "$SRCDIR\public\tier1\checksum_crc.h"
+ $File "$SRCDIR\public\tier1\checksum_md5.h"
+ $File "$SRCDIR\public\ChunkFile.h"
+ $File "$SRCDIR\public\cmodel.h"
+ $File "$SRCDIR\public\CollisionUtils.h"
+ $File "$SRCDIR\public\tier0\commonmacros.h"
+ $File "$SRCDIR\public\mathlib\compressed_vector.h"
+ $File "$SRCDIR\public\const.h"
+ $File "$SRCDIR\public\coordsize.h"
+ $File "$SRCDIR\public\tier0\dbg.h"
+ $File "$SRCDIR\public\disp_common.h"
+ $File "$SRCDIR\public\disp_powerinfo.h"
+ $File "$SRCDIR\public\disp_vertindex.h"
+ $File "$SRCDIR\public\DispColl_Common.h"
+ $File "$SRCDIR\public\tier0\fasttimer.h"
+ $File "$SRCDIR\public\filesystem.h"
+ $File "$SRCDIR\public\filesystem_helpers.h"
+ $File "$SRCDIR\public\GameBSPFile.h"
+ $File "$SRCDIR\public\gametrace.h"
+ $File "$SRCDIR\public\mathlib\halton.h"
+ $File "$SRCDIR\public\materialsystem\hardwareverts.h"
+ $File "$SRCDIR\public\appframework\IAppSystem.h"
+ $File "$SRCDIR\public\tier0\icommandline.h"
+ $File "$SRCDIR\public\ihandleentity.h"
+ $File "$SRCDIR\public\materialsystem\imaterial.h"
+ $File "$SRCDIR\public\materialsystem\imaterialsystem.h"
+ $File "$SRCDIR\public\materialsystem\imaterialvar.h"
+ $File "$SRCDIR\public\tier1\interface.h"
+ $File "$SRCDIR\public\iscratchpad3d.h"
+ $File "$SRCDIR\public\ivraddll.h"
+ $File "$SRCDIR\public\materialsystem\materialsystem_config.h"
+ $File "$SRCDIR\public\mathlib\mathlib.h"
+ $File "$SRCDIR\public\tier0\memdbgon.h"
+ $File "$SRCDIR\public\optimize.h"
+ $File "$SRCDIR\public\phyfile.h"
+ $File "..\common\physdll.h"
+ $File "$SRCDIR\public\tier0\platform.h"
+ $File "$SRCDIR\public\tier0\protected_things.h"
+ $File "$SRCDIR\public\vstdlib\random.h"
+ $File "$SRCDIR\public\ScratchPad3D.h"
+ $File "$SRCDIR\public\ScratchPadUtils.h"
+ $File "$SRCDIR\public\string_t.h"
+ $File "$SRCDIR\public\tier1\strtools.h"
+ $File "$SRCDIR\public\studio.h"
+ $File "$SRCDIR\public\tier1\tokenreader.h"
+ $File "$SRCDIR\public\trace.h"
+ $File "$SRCDIR\public\tier1\utlbuffer.h"
+ $File "$SRCDIR\public\tier1\utldict.h"
+ $File "$SRCDIR\public\tier1\utlhash.h"
+ $File "$SRCDIR\public\tier1\utllinkedlist.h"
+ $File "$SRCDIR\public\tier1\utlmemory.h"
+ $File "$SRCDIR\public\tier1\utlrbtree.h"
+ $File "$SRCDIR\public\tier1\utlsymbol.h"
+ $File "$SRCDIR\public\tier1\utlvector.h"
+ $File "$SRCDIR\public\vcollide.h"
+ $File "$SRCDIR\public\mathlib\vector.h"
+ $File "$SRCDIR\public\mathlib\vector2d.h"
+ $File "$SRCDIR\public\mathlib\vector4d.h"
+ $File "$SRCDIR\public\mathlib\vmatrix.h"
+ $File "..\vmpi\vmpi.h"
+ $File "$SRCDIR\public\vphysics_interface.h"
+ $File "$SRCDIR\public\mathlib\vplane.h"
+ $File "$SRCDIR\public\tier0\vprof.h"
+ $File "$SRCDIR\public\vstdlib\vstdlib.h"
+ $File "$SRCDIR\public\vtf\vtf.h"
+ $File "$SRCDIR\public\wadtypes.h"
+ $File "$SRCDIR\public\worldsize.h"
+ }
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib bitmap
+ $Lib mathlib
+ $Lib raytrace
+ $Lib tier2
+ $Lib vmpi
+ $Lib vtf
+ }
+
+ $File "notes.txt"
+}
diff --git a/mp/src/utils/vrad/vraddetailprops.cpp b/mp/src/utils/vrad/vraddetailprops.cpp
index 822bc78c..93232595 100644
--- a/mp/src/utils/vrad/vraddetailprops.cpp
+++ b/mp/src/utils/vrad/vraddetailprops.cpp
@@ -1,1035 +1,1035 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $Revision: $
-// $NoKeywords: $
-//
-// This file contains code to allow us to associate client data with bsp leaves.
-//
-//=============================================================================//
-
-#include "vrad.h"
-#include "Bsplib.h"
-#include "GameBSPFile.h"
-#include "UtlBuffer.h"
-#include "utlvector.h"
-#include "CModel.h"
-#include "studio.h"
-#include "pacifier.h"
-#include "vraddetailprops.h"
-#include "mathlib/halton.h"
-#include "messbuf.h"
-#include "byteswap.h"
-
-bool LoadStudioModel( char const* pModelName, CUtlBuffer& buf );
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Writes a glview text file containing the collision surface in question
-// Input : *pCollide -
-// *pFilename -
-//-----------------------------------------------------------------------------
-void DumpRayToGlView( Ray_t const& ray, float dist, Vector* pColor, const char *pFilename )
-{
- Vector dir = ray.m_Delta;
- float len = VectorNormalize(dir);
- if (len < 1e-3)
- return;
-
- Vector up( 0, 0, 1 );
- Vector crossDir;
- if (fabs(DotProduct(up, dir)) - 1.0f < -1e-3 )
- {
- CrossProduct( dir, up, crossDir );
- VectorNormalize(crossDir);
- }
- else
- {
- up.Init( 0, 1, 0 );
- CrossProduct( dir, up, crossDir );
- VectorNormalize(crossDir);
- }
-
- Vector end;
- Vector start1, start2;
- VectorMA( ray.m_Start, dist, ray.m_Delta, end );
- VectorMA( ray.m_Start, -2, crossDir, start1 );
- VectorMA( ray.m_Start, 2, crossDir, start2 );
-
- FileHandle_t fp = g_pFileSystem->Open( pFilename, "a" );
- int vert = 0;
- CmdLib_FPrintf( fp, "3\n" );
- CmdLib_FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", start1.x, start1.y, start1.z,
- pColor->x, pColor->y, pColor->z );
- vert++;
- CmdLib_FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", start2.x, start2.y, start2.z,
- pColor->x, pColor->y, pColor->z );
- vert++;
- CmdLib_FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", end.x, end.y, end.z,
- pColor->x, pColor->y, pColor->z );
- vert++;
- g_pFileSystem->Close( fp );
-}
-
-
-//-----------------------------------------------------------------------------
-// This puppy is used to construct the game lumps
-//-----------------------------------------------------------------------------
-static CUtlVector<DetailPropLightstylesLump_t> s_DetailPropLightStyleLumpLDR;
-static CUtlVector<DetailPropLightstylesLump_t> s_DetailPropLightStyleLumpHDR;
-static CUtlVector<DetailPropLightstylesLump_t> *s_pDetailPropLightStyleLump = &s_DetailPropLightStyleLumpLDR;
-
-//-----------------------------------------------------------------------------
-// An amount to add to each model to get to the model center
-//-----------------------------------------------------------------------------
-CUtlVector<Vector> g_ModelCenterOffset;
-CUtlVector<Vector> g_SpriteCenterOffset;
-
-void VRadDetailProps_SetHDRMode( bool bHDR )
-{
- if( bHDR )
- {
- s_pDetailPropLightStyleLump = &s_DetailPropLightStyleLumpHDR;
- }
- else
- {
- s_pDetailPropLightStyleLump = &s_DetailPropLightStyleLumpLDR;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Finds ambient sky lights
-//-----------------------------------------------------------------------------
-static directlight_t* FindAmbientSkyLight()
-{
- static directlight_t *s_pCachedSkylight = NULL;
-
- // Don't keep searching for the same light.
- if ( !s_pCachedSkylight )
- {
- // find any ambient lights
- directlight_t* dl;
- for (dl = activelights; dl != 0; dl = dl->next)
- {
- if (dl->light.type == emit_skyambient)
- {
- s_pCachedSkylight = dl;
- break;
- }
- }
- }
-
- return s_pCachedSkylight;
-}
-
-
-//-----------------------------------------------------------------------------
-// Compute world center of a prop
-//-----------------------------------------------------------------------------
-static void ComputeWorldCenter( DetailObjectLump_t& prop, Vector& center, Vector& normal )
-{
- // Transform the offset into world space
- Vector forward, right;
- AngleVectors( prop.m_Angles, &forward, &right, &normal );
- VectorCopy( prop.m_Origin, center );
-
- // FIXME: Take orientation into account?
- switch (prop.m_Type )
- {
- case DETAIL_PROP_TYPE_MODEL:
- VectorMA( center, g_ModelCenterOffset[prop.m_DetailModel].x, forward, center );
- VectorMA( center, -g_ModelCenterOffset[prop.m_DetailModel].y, right, center );
- VectorMA( center, g_ModelCenterOffset[prop.m_DetailModel].z, normal, center );
- break;
-
- case DETAIL_PROP_TYPE_SPRITE:
- Vector vecOffset;
- VectorMultiply( g_SpriteCenterOffset[prop.m_DetailModel], prop.m_flScale, vecOffset );
- VectorMA( center, vecOffset.x, forward, center );
- VectorMA( center, -vecOffset.y, right, center );
- VectorMA( center, vecOffset.z, normal, center );
- break;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Computes max direct lighting for a single detal prop
-//-----------------------------------------------------------------------------
-static void ComputeMaxDirectLighting( DetailObjectLump_t& prop, Vector* maxcolor, int iThread )
-{
- // The max direct lighting must be along the direction to one
- // of the static lights....
-
- Vector origin, normal;
- ComputeWorldCenter( prop, origin, normal );
-
- if ( !origin.IsValid() || !normal.IsValid() )
- {
- static bool s_Warned = false;
- if ( !s_Warned )
- {
- Warning("WARNING: Bogus detail props encountered!\n" );
- s_Warned = true;
- }
-
- // fill with debug color
- for ( int i = 0; i < MAX_LIGHTSTYLES; ++i)
- {
- maxcolor[i].Init(1,0,0);
- }
- return;
- }
-
- int cluster = ClusterFromPoint(origin);
-
- Vector delta;
- CUtlVector< directlight_t* > lights;
- CUtlVector< Vector > directions;
-
- directlight_t* dl;
- for (dl = activelights; dl != 0; dl = dl->next)
- {
- // skyambient doesn't affect dlights..
- if (dl->light.type == emit_skyambient)
- continue;
-
- // is this lights cluster visible?
- if ( PVSCheck( dl->pvs, cluster ) )
- {
- lights.AddToTail(dl);
- VectorSubtract( dl->light.origin, origin, delta );
- VectorNormalize( delta );
- directions.AddToTail( delta );
- }
- }
-
- // Find the max illumination
- int i;
- for ( i = 0; i < MAX_LIGHTSTYLES; ++i)
- {
- maxcolor[i].Init(0,0,0);
- }
-
- // NOTE: See version 10 for a method where we choose a normal based on whichever
- // one produces the maximum possible illumination. This appeared to work better on
- // e3_town, so I'm trying it now; hopefully it'll be good for all cases.
- int j;
- for ( j = 0; j < lights.Count(); ++j)
- {
- dl = lights[j];
-
- SSE_sampleLightOutput_t out;
- FourVectors origin4;
- FourVectors normal4;
- origin4.DuplicateVector( origin );
- normal4.DuplicateVector( normal );
-
- GatherSampleLightSSE ( out, dl, -1, origin4, &normal4, 1, iThread );
- VectorMA( maxcolor[dl->light.style], out.m_flFalloff.m128_f32[0] * out.m_flDot[0].m128_f32[0], dl->light.intensity, maxcolor[dl->light.style] );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Computes the ambient term from a particular surface
-//-----------------------------------------------------------------------------
-
-static void ComputeAmbientFromSurface( dface_t* pFace, directlight_t* pSkylight,
- Vector& radcolor )
-{
- texinfo_t* pTex = &texinfo[pFace->texinfo];
- if (pTex)
- {
- // If we hit the sky, use the sky ambient
- if (pTex->flags & SURF_SKY)
- {
- if (pSkylight)
- {
- // add in sky ambient
- VectorDivide( pSkylight->light.intensity, 255.0f, radcolor );
- }
- }
- else
- {
- VectorMultiply( radcolor, dtexdata[pTex->texdata].reflectivity, radcolor );
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Computes the lightmap color at a particular point
-//-----------------------------------------------------------------------------
-
-static void ComputeLightmapColorFromAverage( dface_t* pFace, directlight_t* pSkylight, float scale, Vector pColor[MAX_LIGHTSTYLES] )
-{
- texinfo_t* pTex = &texinfo[pFace->texinfo];
- if (pTex->flags & SURF_SKY)
- {
- if (pSkylight)
- {
- // add in sky ambient
- Vector amb = pSkylight->light.intensity / 255.0f;
- pColor[0] += amb * scale;
- }
- return;
- }
-
- for (int maps = 0 ; maps < MAXLIGHTMAPS && pFace->styles[maps] != 255 ; ++maps)
- {
- ColorRGBExp32* pAvgColor = dface_AvgLightColor( pFace, maps );
-
- // this code expects values from [0..1] not [0..255]
- Vector color;
- color[0] = TexLightToLinear( pAvgColor->r, pAvgColor->exponent );
- color[1] = TexLightToLinear( pAvgColor->g, pAvgColor->exponent );
- color[2] = TexLightToLinear( pAvgColor->b, pAvgColor->exponent );
-
- ComputeAmbientFromSurface( pFace, pSkylight, color );
-
- int style = pFace->styles[maps];
- pColor[style] += color * scale;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Returns true if the surface has bumped lightmaps
-//-----------------------------------------------------------------------------
-
-static bool SurfHasBumpedLightmaps( dface_t *pSurf )
-{
- bool hasBumpmap = false;
- if( ( texinfo[pSurf->texinfo].flags & SURF_BUMPLIGHT ) &&
- ( !( texinfo[pSurf->texinfo].flags & SURF_NOLIGHT ) ) )
- {
- hasBumpmap = true;
- }
- return hasBumpmap;
-}
-
-//-----------------------------------------------------------------------------
-// Computes the lightmap color at a particular point
-//-----------------------------------------------------------------------------
-
-static void ComputeLightmapColorPointSample( dface_t* pFace, directlight_t* pSkylight, Vector2D const& luv, float scale, Vector pColor[MAX_LIGHTSTYLES] )
-{
- // face unaffected by light
- if (pFace->lightofs == -1 )
- return;
-
- int smax = ( pFace->m_LightmapTextureSizeInLuxels[0] ) + 1;
- int tmax = ( pFace->m_LightmapTextureSizeInLuxels[1] ) + 1;
-
- // luv is in the space of the accumulated lightmap page; we need to convert
- // it to be in the space of the surface
- int ds = clamp( (int)luv.x, 0, smax-1 );
- int dt = clamp( (int)luv.y, 0, tmax-1 );
-
- int offset = smax * tmax;
- if ( SurfHasBumpedLightmaps( pFace ) )
- offset *= ( NUM_BUMP_VECTS + 1 );
-
- ColorRGBExp32* pLightmap = (ColorRGBExp32*)&pdlightdata->Base()[pFace->lightofs];
- pLightmap += dt * smax + ds;
- for (int maps = 0 ; maps < MAXLIGHTMAPS && pFace->styles[maps] != 255 ; ++maps)
- {
- int style = pFace->styles[maps];
-
- Vector color;
- color[0] = TexLightToLinear( pLightmap->r, pLightmap->exponent );
- color[1] = TexLightToLinear( pLightmap->g, pLightmap->exponent );
- color[2] = TexLightToLinear( pLightmap->b, pLightmap->exponent );
-
- ComputeAmbientFromSurface( pFace, pSkylight, color );
- pColor[style] += color * scale;
-
- pLightmap += offset;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Tests a particular node
-//-----------------------------------------------------------------------------
-
-class CLightSurface : public IBSPNodeEnumerator
-{
-public:
- CLightSurface(int iThread) : m_pSurface(0), m_HitFrac(1.0f), m_bHasLuxel(false), m_iThread(iThread) {}
-
- // call back with a node and a context
- bool EnumerateNode( int node, Ray_t const& ray, float f, int context )
- {
- dface_t* pSkySurface = 0;
-
- // Compute the actual point
- Vector pt;
- VectorMA( ray.m_Start, f, ray.m_Delta, pt );
-
- dnode_t* pNode = &dnodes[node];
- dface_t* pFace = &g_pFaces[pNode->firstface];
- for (int i=0 ; i < pNode->numfaces ; ++i, ++pFace)
- {
- // Don't take into account faces that are int a leaf
- if ( !pFace->onNode )
- continue;
-
- // Don't test displacement faces
- if ( pFace->dispinfo != -1 )
- continue;
-
- texinfo_t* pTex = &texinfo[pFace->texinfo];
-
- // Don't immediately return when we hit sky;
- // we may actually hit another surface
- if (pTex->flags & SURF_SKY)
- {
- if (TestPointAgainstSkySurface( pt, pFace ))
- {
- pSkySurface = pFace;
- }
-
- continue;
- }
-
- if (TestPointAgainstSurface( pt, pFace, pTex ))
- {
- m_HitFrac = f;
- m_pSurface = pFace;
- m_bHasLuxel = true;
- return false;
- }
- }
-
- // if we hit a sky surface, return it
- m_pSurface = pSkySurface;
- return (m_pSurface == 0);
- }
-
- // call back with a leaf and a context
- virtual bool EnumerateLeaf( int leaf, Ray_t const& ray, float start, float end, int context )
- {
- bool hit = false;
- dleaf_t* pLeaf = &dleafs[leaf];
- for (int i=0 ; i < pLeaf->numleaffaces ; ++i)
- {
- Assert( pLeaf->firstleafface + i < numleaffaces );
- Assert( dleaffaces[pLeaf->firstleafface + i] < numfaces );
- dface_t* pFace = &g_pFaces[dleaffaces[pLeaf->firstleafface + i]];
-
- // Don't test displacement faces; we need to check another list
- if ( pFace->dispinfo != -1 )
- continue;
-
- // Don't take into account faces that are on a node
- if ( pFace->onNode )
- continue;
-
- // Find intersection point against detail brushes
- texinfo_t* pTex = &texinfo[pFace->texinfo];
-
- dplane_t* pPlane = &dplanes[pFace->planenum];
-
- // Backface cull...
- if (DotProduct( pPlane->normal, ray.m_Delta ) > 0)
- continue;
-
- float startDotN = DotProduct( ray.m_Start, pPlane->normal );
- float deltaDotN = DotProduct( ray.m_Delta, pPlane->normal );
-
- float front = startDotN + start * deltaDotN - pPlane->dist;
- float back = startDotN + end * deltaDotN - pPlane->dist;
-
- int side = front < 0;
-
- // Blow it off if it doesn't split the plane...
- if ( (back < 0) == side )
- continue;
-
- // Don't test a surface that is farther away from the closest found intersection
- float f = front / (front-back);
- float mid = start * (1.0f - f) + end * f;
- if (mid >= m_HitFrac)
- continue;
-
- Vector pt;
- VectorMA( ray.m_Start, mid, ray.m_Delta, pt );
-
- if (TestPointAgainstSurface( pt, pFace, pTex ))
- {
- m_HitFrac = mid;
- m_pSurface = pFace;
- hit = true;
- m_bHasLuxel = true;
- }
- }
-
- // Now try to clip against all displacements in the leaf
- float dist;
- Vector2D luxelCoord;
- dface_t *pDispFace;
- StaticDispMgr()->ClipRayToDispInLeaf( s_DispTested[m_iThread], ray, leaf, dist, pDispFace, luxelCoord );
- if (dist < m_HitFrac)
- {
- m_HitFrac = dist;
- m_pSurface = pDispFace;
- Vector2DCopy( luxelCoord, m_LuxelCoord );
- hit = true;
- m_bHasLuxel = true;
- }
- return !hit;
- }
-
- bool FindIntersection( Ray_t const& ray )
- {
- StaticDispMgr()->StartRayTest( s_DispTested[m_iThread] );
- return !EnumerateNodesAlongRay( ray, this, 0 );
- }
-
-private:
- bool TestPointAgainstSurface( Vector const& pt, dface_t* pFace, texinfo_t* pTex )
- {
- // no lightmaps on this surface? punt...
- // FIXME: should be water surface?
- if (pTex->flags & SURF_NOLIGHT)
- return false;
-
- // See where in lightmap space our intersection point is
- float s, t;
- s = DotProduct (pt.Base(), pTex->lightmapVecsLuxelsPerWorldUnits[0]) +
- pTex->lightmapVecsLuxelsPerWorldUnits[0][3];
- t = DotProduct (pt.Base(), pTex->lightmapVecsLuxelsPerWorldUnits[1]) +
- pTex->lightmapVecsLuxelsPerWorldUnits[1][3];
-
- // Not in the bounds of our lightmap? punt...
- if( s < pFace->m_LightmapTextureMinsInLuxels[0] || t < pFace->m_LightmapTextureMinsInLuxels[1] )
- return false;
-
- // assuming a square lightmap (FIXME: which ain't always the case),
- // lets see if it lies in that rectangle. If not, punt...
- float ds = s - pFace->m_LightmapTextureMinsInLuxels[0];
- float dt = t - pFace->m_LightmapTextureMinsInLuxels[1];
- if( ds > pFace->m_LightmapTextureSizeInLuxels[0] || dt > pFace->m_LightmapTextureSizeInLuxels[1] )
- return false;
-
- m_LuxelCoord.x = ds;
- m_LuxelCoord.y = dt;
-
- return true;
- }
-
- bool TestPointAgainstSkySurface( Vector const &pt, dface_t *pFace )
- {
- // Create sky face winding.
- Vector v( 0.0f, 0.0f, 0.0f );
- winding_t *pWinding = WindingFromFace( pFace, v );
-
- // Test point in winding. (Since it is at the node, it is in the plane.)
- bool bRet = PointInWinding( pt, pWinding );
-
- FreeWinding( pWinding );
-
- return bRet;
- }
-
-
-public:
- int m_iThread;
- dface_t* m_pSurface;
- float m_HitFrac;
- Vector2D m_LuxelCoord;
- bool m_bHasLuxel;
-};
-
-bool CastRayInLeaf( int iThread, const Vector &start, const Vector &end, int leafIndex, float *pFraction, Vector *pNormal )
-{
- pFraction[0] = 1.0f;
-
- Ray_t ray;
- ray.Init( start, end, vec3_origin, vec3_origin );
- CBaseTrace trace;
- if ( TraceLeafBrushes( leafIndex, start, end, trace ) != 1.0f )
- {
- pFraction[0] = trace.fraction;
- *pNormal = trace.plane.normal;
- }
- else
- {
- Assert(!trace.startsolid && !trace.allsolid);
- }
- StaticDispMgr()->StartRayTest( s_DispTested[iThread] );
- // Now try to clip against all displacements in the leaf
- float dist;
- Vector normal;
- StaticDispMgr()->ClipRayToDispInLeaf( s_DispTested[iThread], ray, leafIndex, dist, &normal );
- if ( dist < pFraction[0] )
- {
- pFraction[0] = dist;
- *pNormal = normal;
- }
- return pFraction[0] != 1.0f ? true : false;
-}
-
-//-----------------------------------------------------------------------------
-// Computes ambient lighting along a specified ray.
-// Ray represents a cone, tanTheta is the tan of the inner cone angle
-//-----------------------------------------------------------------------------
-void CalcRayAmbientLighting( int iThread, const Vector &vStart, const Vector &vEnd, float tanTheta, Vector color[MAX_LIGHTSTYLES] )
-{
- Ray_t ray;
- ray.Init( vStart, vEnd, vec3_origin, vec3_origin );
-
- directlight_t *pSkyLight = FindAmbientSkyLight();
-
- CLightSurface surfEnum(iThread);
- if (!surfEnum.FindIntersection( ray ))
- return;
-
- // compute the approximate radius of a circle centered around the intersection point
- float dist = ray.m_Delta.Length() * tanTheta * surfEnum.m_HitFrac;
-
- // until 20" we use the point sample, then blend in the average until we're covering 40"
- // This is attempting to model the ray as a cone - in the ideal case we'd simply sample all
- // luxels in the intersection of the cone with the surface. Since we don't have surface
- // neighbor information computed we'll just approximate that sampling with a blend between
- // a point sample and the face average.
- // This yields results that are similar in that aliasing is reduced at distance while
- // point samples provide accuracy for intersections with near geometry
- float scaleAvg = RemapValClamped( dist, 20, 40, 0.0f, 1.0f );
-
- if ( !surfEnum.m_bHasLuxel )
- {
- // don't have luxel UV, so just use average sample
- scaleAvg = 1.0;
- }
- float scaleSample = 1.0f - scaleAvg;
-
- if (scaleAvg != 0)
- {
- ComputeLightmapColorFromAverage( surfEnum.m_pSurface, pSkyLight, scaleAvg, color );
- }
- if (scaleSample != 0)
- {
- ComputeLightmapColorPointSample( surfEnum.m_pSurface, pSkyLight, surfEnum.m_LuxelCoord, scaleSample, color );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Compute ambient lighting component at specified position.
-//-----------------------------------------------------------------------------
-static void ComputeAmbientLightingAtPoint( int iThread, const Vector &origin, Vector radcolor[NUMVERTEXNORMALS], Vector color[MAX_LIGHTSTYLES] )
-{
- // NOTE: I'm not dealing with shadow-casting static props here
- // This is for speed, although we can add it if it turns out to
- // be important
-
- // sample world by casting N rays distributed across a sphere
- Vector upend;
-
- int j;
- for ( j = 0; j < MAX_LIGHTSTYLES; ++j)
- {
- color[j].Init( 0,0,0 );
- }
-
- float tanTheta = tan(VERTEXNORMAL_CONE_INNER_ANGLE);
- for (int i = 0; i < NUMVERTEXNORMALS; i++)
- {
- VectorMA( origin, COORD_EXTENT * 1.74, g_anorms[i], upend );
-
- // Now that we've got a ray, see what surface we've hit
- CalcRayAmbientLighting( iThread, origin, upend, tanTheta, color );
-
-// DumpRayToGlView( ray, surfEnum.m_HitFrac, &color[0], "test.out" );
- }
-
- for ( j = 0; j < MAX_LIGHTSTYLES; ++j)
- {
- VectorMultiply( color[j], 255.0f / (float)NUMVERTEXNORMALS, color[j] );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Trace hemispherical rays from a vertex, accumulating indirect
-// sources at each ray termination.
-//-----------------------------------------------------------------------------
-void ComputeIndirectLightingAtPoint( Vector &position, Vector &normal, Vector &outColor,
- int iThread, bool force_fast, bool bIgnoreNormals )
-{
- Ray_t ray;
- CLightSurface surfEnum(iThread);
-
- outColor.Init();
-
-
- int nSamples = NUMVERTEXNORMALS;
- if ( do_fast || force_fast )
- nSamples /= 4;
- else
- nSamples *= g_flSkySampleScale;
-
- float totalDot = 0;
- DirectionalSampler_t sampler;
- for (int j = 0; j < nSamples; j++)
- {
- Vector samplingNormal = sampler.NextValue();
- float dot;
-
- if ( bIgnoreNormals )
- dot = (0.7071/2);
- else
- dot = DotProduct( normal, samplingNormal );
-
- if ( dot <= EQUAL_EPSILON )
- {
- // reject angles behind our plane
- continue;
- }
-
- totalDot += dot;
-
- // trace to determine surface
- Vector vEnd;
- VectorScale( samplingNormal, MAX_TRACE_LENGTH, vEnd );
- VectorAdd( position, vEnd, vEnd );
-
- ray.Init( position, vEnd, vec3_origin, vec3_origin );
- if ( !surfEnum.FindIntersection( ray ) )
- continue;
-
- // get color from surface lightmap
- texinfo_t* pTex = &texinfo[surfEnum.m_pSurface->texinfo];
- if ( !pTex || pTex->flags & SURF_SKY )
- {
- // ignore contribution from sky
- // sky ambient already accounted for during direct pass
- continue;
- }
-
- if ( surfEnum.m_pSurface->styles[0] == 255 || surfEnum.m_pSurface->lightofs < 0 )
- {
- // no light affects this face
- continue;
- }
-
-
- Vector lightmapColor;
- if ( !surfEnum.m_bHasLuxel )
- {
- ColorRGBExp32* pAvgLightmapColor = dface_AvgLightColor( surfEnum.m_pSurface, 0 );
- ColorRGBExp32ToVector( *pAvgLightmapColor, lightmapColor );
- }
- else
- {
- // get color from displacement
- int smax = ( surfEnum.m_pSurface->m_LightmapTextureSizeInLuxels[0] ) + 1;
- int tmax = ( surfEnum.m_pSurface->m_LightmapTextureSizeInLuxels[1] ) + 1;
-
- // luxelcoord is in the space of the accumulated lightmap page; we need to convert
- // it to be in the space of the surface
- int ds = clamp( (int)surfEnum.m_LuxelCoord.x, 0, smax-1 );
- int dt = clamp( (int)surfEnum.m_LuxelCoord.y, 0, tmax-1 );
-
- ColorRGBExp32* pLightmap = (ColorRGBExp32*)&(*pdlightdata)[surfEnum.m_pSurface->lightofs];
- pLightmap += dt * smax + ds;
- ColorRGBExp32ToVector( *pLightmap, lightmapColor );
- }
-
- VectorMultiply( lightmapColor, dtexdata[pTex->texdata].reflectivity, lightmapColor );
- VectorAdd( outColor, lightmapColor, outColor );
- }
-
- if ( totalDot )
- {
- VectorScale( outColor, 1.0f/totalDot, outColor );
- }
-}
-
-static void ComputeAmbientLighting( int iThread, DetailObjectLump_t& prop, Vector color[MAX_LIGHTSTYLES] )
-{
- Vector origin, normal;
- ComputeWorldCenter( prop, origin, normal );
-
- if ( !origin.IsValid() || !normal.IsValid() )
- {
- static bool s_Warned = false;
- if ( !s_Warned )
- {
- Warning("WARNING: Bogus detail props encountered!\n" );
- s_Warned = true;
- }
-
- // fill with debug color
- for ( int i = 0; i < MAX_LIGHTSTYLES; ++i)
- {
- color[i].Init(1,0,0);
- }
- return;
- }
-
- Vector radcolor[NUMVERTEXNORMALS];
- ComputeAmbientLightingAtPoint( iThread, origin, radcolor, color );
-}
-
-
-//-----------------------------------------------------------------------------
-// Computes lighting for a single detal prop
-//-----------------------------------------------------------------------------
-
-static void ComputeLighting( DetailObjectLump_t& prop, int iThread )
-{
- // We're going to take the maximum of the ambient lighting and
- // the strongest directional light. This works because we're assuming
- // the props will have built-in faked lighting.
-
- Vector directColor[MAX_LIGHTSTYLES];
- Vector ambColor[MAX_LIGHTSTYLES];
-
- // Get the max influence of all direct lights
- ComputeMaxDirectLighting( prop, directColor, iThread );
-
- // Get the ambient lighting + lightstyles
- ComputeAmbientLighting( iThread, prop, ambColor );
-
- // Base lighting
- Vector totalColor;
- VectorAdd( directColor[0], ambColor[0], totalColor );
- VectorToColorRGBExp32( totalColor, prop.m_Lighting );
-
- bool hasLightstyles = false;
- prop.m_LightStyleCount = 0;
-
- // lightstyles
- for (int i = 1; i < MAX_LIGHTSTYLES; ++i )
- {
- VectorAdd( directColor[i], ambColor[i], totalColor );
- totalColor *= 0.5f;
-
- if ((totalColor[0] != 0.0f) || (totalColor[1] != 0.0f) ||
- (totalColor[2] != 0.0f) )
- {
- if (!hasLightstyles)
- {
- prop.m_LightStyles = s_pDetailPropLightStyleLump->Size();
- hasLightstyles = true;
- }
-
- int j = s_pDetailPropLightStyleLump->AddToTail();
- VectorToColorRGBExp32( totalColor, (*s_pDetailPropLightStyleLump)[j].m_Lighting );
- (*s_pDetailPropLightStyleLump)[j].m_Style = i;
- ++prop.m_LightStyleCount;
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Unserialization
-//-----------------------------------------------------------------------------
-static void UnserializeModelDict( CUtlBuffer& buf )
-{
- // Get origin offset for each model...
- int count = buf.GetInt();
- while ( --count >= 0 )
- {
- DetailObjectDictLump_t lump;
- buf.Get( &lump, sizeof(DetailObjectDictLump_t) );
-
- int i = g_ModelCenterOffset.AddToTail();
-
- CUtlBuffer mdlbuf;
- if (LoadStudioModel( lump.m_Name, mdlbuf ))
- {
- studiohdr_t* pHdr = (studiohdr_t*)mdlbuf.Base();
- VectorAdd( pHdr->hull_min, pHdr->hull_max, g_ModelCenterOffset[i] );
- g_ModelCenterOffset[i] *= 0.5f;
- }
- else
- {
- g_ModelCenterOffset[i].Init(0,0,0);
- }
- }
-}
-
-static void UnserializeSpriteDict( CUtlBuffer& buf )
-{
- // Get origin offset for each model...
- int count = buf.GetInt();
- while ( --count >= 0 )
- {
- DetailSpriteDictLump_t lump;
- buf.Get( &lump, sizeof(DetailSpriteDictLump_t) );
-
- // For these sprites, x goes out the front, y right, z up
- int i = g_SpriteCenterOffset.AddToTail();
- g_SpriteCenterOffset[i].x = 0.0f;
- g_SpriteCenterOffset[i].y = lump.m_LR.x + lump.m_UL.x;
- g_SpriteCenterOffset[i].z = lump.m_LR.y + lump.m_UL.y;
- g_SpriteCenterOffset[i] *= 0.5f;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Unserializes the detail props
-//-----------------------------------------------------------------------------
-static int UnserializeDetailProps( DetailObjectLump_t*& pProps )
-{
- GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROPS );
-
- if (g_GameLumps.GetGameLumpVersion(handle) != GAMELUMP_DETAIL_PROPS_VERSION)
- return 0;
-
- // Unserialize
- CUtlBuffer buf( g_GameLumps.GetGameLump(handle), g_GameLumps.GameLumpSize( handle ), CUtlBuffer::READ_ONLY );
-
- UnserializeModelDict( buf );
- UnserializeSpriteDict( buf );
-
- // Now we're pointing to the detail prop data
- // This actually works because the scope of the game lump data
- // is global and the buf was just pointing to it.
- int count = buf.GetInt();
- if (count)
- {
- pProps = (DetailObjectLump_t*)buf.PeekGet();
- }
- else
- {
- pProps = 0;
- }
- return count;
-}
-
-
-//-----------------------------------------------------------------------------
-// Writes the detail lighting lump
-//-----------------------------------------------------------------------------
-static void WriteDetailLightingLump( int lumpID, int lumpVersion, CUtlVector<DetailPropLightstylesLump_t> &lumpData )
-{
- GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle(lumpID);
- if (handle != g_GameLumps.InvalidGameLump())
- g_GameLumps.DestroyGameLump(handle);
- int lightsize = lumpData.Size() * sizeof(DetailPropLightstylesLump_t);
- int lumpsize = lightsize + sizeof(int);
-
- handle = g_GameLumps.CreateGameLump( lumpID, lumpsize, 0, lumpVersion );
-
- // Serialize the data
- CUtlBuffer buf( g_GameLumps.GetGameLump(handle), lumpsize );
- buf.PutInt( lumpData.Size() );
- if (lightsize)
- buf.Put( lumpData.Base(), lightsize );
-}
-
-static void WriteDetailLightingLumps( void )
-{
- WriteDetailLightingLump( GAMELUMP_DETAIL_PROP_LIGHTING, GAMELUMP_DETAIL_PROP_LIGHTING_VERSION, s_DetailPropLightStyleLumpLDR );
- WriteDetailLightingLump( GAMELUMP_DETAIL_PROP_LIGHTING_HDR, GAMELUMP_DETAIL_PROP_LIGHTING_HDR_VERSION, s_DetailPropLightStyleLumpHDR );
-}
-
-// need to do this so that if we are building HDR data, the LDR data is intact, and vice versa.s
-void UnserializeDetailPropLighting( int lumpID, int lumpVersion, CUtlVector<DetailPropLightstylesLump_t> &lumpData )
-{
- GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle( lumpID );
-
- if( handle == g_GameLumps.InvalidGameLump() )
- {
- return;
- }
-
- if (g_GameLumps.GetGameLumpVersion(handle) != lumpVersion)
- return;
-
- // Unserialize
- CUtlBuffer buf( g_GameLumps.GetGameLump(handle), g_GameLumps.GameLumpSize( handle ), CUtlBuffer::READ_ONLY );
-
- int count = buf.GetInt();
- if( !count )
- {
- return;
- }
- lumpData.SetCount( count );
- int lightsize = lumpData.Size() * sizeof(DetailPropLightstylesLump_t);
- buf.Get( lumpData.Base(), lightsize );
-}
-
-DetailObjectLump_t *g_pMPIDetailProps = NULL;
-
-void VMPI_ProcessDetailPropWU( int iThread, int iWorkUnit, MessageBuffer *pBuf )
-{
- CUtlVector<DetailPropLightstylesLump_t> *pDetailPropLump = s_pDetailPropLightStyleLump;
-
- DetailObjectLump_t& prop = g_pMPIDetailProps[iWorkUnit];
- ComputeLighting( prop, iThread );
-
- // Send the results back...
- pBuf->write( &prop.m_Lighting, sizeof( prop.m_Lighting ) );
- pBuf->write( &prop.m_LightStyleCount, sizeof( prop.m_LightStyleCount ) );
- pBuf->write( &prop.m_LightStyles, sizeof( prop.m_LightStyles ) );
-
- for ( int i=0; i < prop.m_LightStyleCount; i++ )
- {
- DetailPropLightstylesLump_t *l = &pDetailPropLump->Element( i + prop.m_LightStyles );
- pBuf->write( &l->m_Lighting, sizeof( l->m_Lighting ) );
- pBuf->write( &l->m_Style, sizeof( l->m_Style ) );
- }
-}
-
-
-void VMPI_ReceiveDetailPropWU( int iWorkUnit, MessageBuffer *pBuf, int iWorker )
-{
- CUtlVector<DetailPropLightstylesLump_t> *pDetailPropLump = s_pDetailPropLightStyleLump;
-
- DetailObjectLump_t& prop = g_pMPIDetailProps[iWorkUnit];
-
- pBuf->read( &prop.m_Lighting, sizeof( prop.m_Lighting ) );
- pBuf->read( &prop.m_LightStyleCount, sizeof( prop.m_LightStyleCount ) );
- pBuf->read( &prop.m_LightStyles, sizeof( prop.m_LightStyles ) );
-
- pDetailPropLump->EnsureCount( prop.m_LightStyles + prop.m_LightStyleCount );
-
- for ( int i=0; i < prop.m_LightStyleCount; i++ )
- {
- DetailPropLightstylesLump_t *l = &pDetailPropLump->Element( i + prop.m_LightStyles );
- pBuf->read( &l->m_Lighting, sizeof( l->m_Lighting ) );
- pBuf->read( &l->m_Style, sizeof( l->m_Style ) );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Computes lighting for the detail props
-//-----------------------------------------------------------------------------
-void ComputeDetailPropLighting( int iThread )
-{
- // illuminate them all
- DetailObjectLump_t* pProps;
- int count = UnserializeDetailProps( pProps );
- if (!count)
- return;
-
- // unserialize the lump that we aren't computing.
- if( g_bHDR )
- {
- UnserializeDetailPropLighting( GAMELUMP_DETAIL_PROP_LIGHTING, GAMELUMP_DETAIL_PROP_LIGHTING_VERSION, s_DetailPropLightStyleLumpLDR );
- }
- else
- {
- UnserializeDetailPropLighting( GAMELUMP_DETAIL_PROP_LIGHTING_HDR, GAMELUMP_DETAIL_PROP_LIGHTING_HDR_VERSION, s_DetailPropLightStyleLumpHDR );
- }
-
- StartPacifier("Computing detail prop lighting : ");
-
- for (int i = 0; i < count; ++i)
- {
- UpdatePacifier( (float)i / (float)count );
- ComputeLighting( pProps[i], iThread );
- }
-
- // Write detail prop lightstyle lump...
- WriteDetailLightingLumps();
- EndPacifier( true );
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Revision: $
+// $NoKeywords: $
+//
+// This file contains code to allow us to associate client data with bsp leaves.
+//
+//=============================================================================//
+
+#include "vrad.h"
+#include "Bsplib.h"
+#include "GameBSPFile.h"
+#include "UtlBuffer.h"
+#include "utlvector.h"
+#include "CModel.h"
+#include "studio.h"
+#include "pacifier.h"
+#include "vraddetailprops.h"
+#include "mathlib/halton.h"
+#include "messbuf.h"
+#include "byteswap.h"
+
+bool LoadStudioModel( char const* pModelName, CUtlBuffer& buf );
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Writes a glview text file containing the collision surface in question
+// Input : *pCollide -
+// *pFilename -
+//-----------------------------------------------------------------------------
+void DumpRayToGlView( Ray_t const& ray, float dist, Vector* pColor, const char *pFilename )
+{
+ Vector dir = ray.m_Delta;
+ float len = VectorNormalize(dir);
+ if (len < 1e-3)
+ return;
+
+ Vector up( 0, 0, 1 );
+ Vector crossDir;
+ if (fabs(DotProduct(up, dir)) - 1.0f < -1e-3 )
+ {
+ CrossProduct( dir, up, crossDir );
+ VectorNormalize(crossDir);
+ }
+ else
+ {
+ up.Init( 0, 1, 0 );
+ CrossProduct( dir, up, crossDir );
+ VectorNormalize(crossDir);
+ }
+
+ Vector end;
+ Vector start1, start2;
+ VectorMA( ray.m_Start, dist, ray.m_Delta, end );
+ VectorMA( ray.m_Start, -2, crossDir, start1 );
+ VectorMA( ray.m_Start, 2, crossDir, start2 );
+
+ FileHandle_t fp = g_pFileSystem->Open( pFilename, "a" );
+ int vert = 0;
+ CmdLib_FPrintf( fp, "3\n" );
+ CmdLib_FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", start1.x, start1.y, start1.z,
+ pColor->x, pColor->y, pColor->z );
+ vert++;
+ CmdLib_FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", start2.x, start2.y, start2.z,
+ pColor->x, pColor->y, pColor->z );
+ vert++;
+ CmdLib_FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", end.x, end.y, end.z,
+ pColor->x, pColor->y, pColor->z );
+ vert++;
+ g_pFileSystem->Close( fp );
+}
+
+
+//-----------------------------------------------------------------------------
+// This puppy is used to construct the game lumps
+//-----------------------------------------------------------------------------
+static CUtlVector<DetailPropLightstylesLump_t> s_DetailPropLightStyleLumpLDR;
+static CUtlVector<DetailPropLightstylesLump_t> s_DetailPropLightStyleLumpHDR;
+static CUtlVector<DetailPropLightstylesLump_t> *s_pDetailPropLightStyleLump = &s_DetailPropLightStyleLumpLDR;
+
+//-----------------------------------------------------------------------------
+// An amount to add to each model to get to the model center
+//-----------------------------------------------------------------------------
+CUtlVector<Vector> g_ModelCenterOffset;
+CUtlVector<Vector> g_SpriteCenterOffset;
+
+void VRadDetailProps_SetHDRMode( bool bHDR )
+{
+ if( bHDR )
+ {
+ s_pDetailPropLightStyleLump = &s_DetailPropLightStyleLumpHDR;
+ }
+ else
+ {
+ s_pDetailPropLightStyleLump = &s_DetailPropLightStyleLumpLDR;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Finds ambient sky lights
+//-----------------------------------------------------------------------------
+static directlight_t* FindAmbientSkyLight()
+{
+ static directlight_t *s_pCachedSkylight = NULL;
+
+ // Don't keep searching for the same light.
+ if ( !s_pCachedSkylight )
+ {
+ // find any ambient lights
+ directlight_t* dl;
+ for (dl = activelights; dl != 0; dl = dl->next)
+ {
+ if (dl->light.type == emit_skyambient)
+ {
+ s_pCachedSkylight = dl;
+ break;
+ }
+ }
+ }
+
+ return s_pCachedSkylight;
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute world center of a prop
+//-----------------------------------------------------------------------------
+static void ComputeWorldCenter( DetailObjectLump_t& prop, Vector& center, Vector& normal )
+{
+ // Transform the offset into world space
+ Vector forward, right;
+ AngleVectors( prop.m_Angles, &forward, &right, &normal );
+ VectorCopy( prop.m_Origin, center );
+
+ // FIXME: Take orientation into account?
+ switch (prop.m_Type )
+ {
+ case DETAIL_PROP_TYPE_MODEL:
+ VectorMA( center, g_ModelCenterOffset[prop.m_DetailModel].x, forward, center );
+ VectorMA( center, -g_ModelCenterOffset[prop.m_DetailModel].y, right, center );
+ VectorMA( center, g_ModelCenterOffset[prop.m_DetailModel].z, normal, center );
+ break;
+
+ case DETAIL_PROP_TYPE_SPRITE:
+ Vector vecOffset;
+ VectorMultiply( g_SpriteCenterOffset[prop.m_DetailModel], prop.m_flScale, vecOffset );
+ VectorMA( center, vecOffset.x, forward, center );
+ VectorMA( center, -vecOffset.y, right, center );
+ VectorMA( center, vecOffset.z, normal, center );
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes max direct lighting for a single detal prop
+//-----------------------------------------------------------------------------
+static void ComputeMaxDirectLighting( DetailObjectLump_t& prop, Vector* maxcolor, int iThread )
+{
+ // The max direct lighting must be along the direction to one
+ // of the static lights....
+
+ Vector origin, normal;
+ ComputeWorldCenter( prop, origin, normal );
+
+ if ( !origin.IsValid() || !normal.IsValid() )
+ {
+ static bool s_Warned = false;
+ if ( !s_Warned )
+ {
+ Warning("WARNING: Bogus detail props encountered!\n" );
+ s_Warned = true;
+ }
+
+ // fill with debug color
+ for ( int i = 0; i < MAX_LIGHTSTYLES; ++i)
+ {
+ maxcolor[i].Init(1,0,0);
+ }
+ return;
+ }
+
+ int cluster = ClusterFromPoint(origin);
+
+ Vector delta;
+ CUtlVector< directlight_t* > lights;
+ CUtlVector< Vector > directions;
+
+ directlight_t* dl;
+ for (dl = activelights; dl != 0; dl = dl->next)
+ {
+ // skyambient doesn't affect dlights..
+ if (dl->light.type == emit_skyambient)
+ continue;
+
+ // is this lights cluster visible?
+ if ( PVSCheck( dl->pvs, cluster ) )
+ {
+ lights.AddToTail(dl);
+ VectorSubtract( dl->light.origin, origin, delta );
+ VectorNormalize( delta );
+ directions.AddToTail( delta );
+ }
+ }
+
+ // Find the max illumination
+ int i;
+ for ( i = 0; i < MAX_LIGHTSTYLES; ++i)
+ {
+ maxcolor[i].Init(0,0,0);
+ }
+
+ // NOTE: See version 10 for a method where we choose a normal based on whichever
+ // one produces the maximum possible illumination. This appeared to work better on
+ // e3_town, so I'm trying it now; hopefully it'll be good for all cases.
+ int j;
+ for ( j = 0; j < lights.Count(); ++j)
+ {
+ dl = lights[j];
+
+ SSE_sampleLightOutput_t out;
+ FourVectors origin4;
+ FourVectors normal4;
+ origin4.DuplicateVector( origin );
+ normal4.DuplicateVector( normal );
+
+ GatherSampleLightSSE ( out, dl, -1, origin4, &normal4, 1, iThread );
+ VectorMA( maxcolor[dl->light.style], out.m_flFalloff.m128_f32[0] * out.m_flDot[0].m128_f32[0], dl->light.intensity, maxcolor[dl->light.style] );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the ambient term from a particular surface
+//-----------------------------------------------------------------------------
+
+static void ComputeAmbientFromSurface( dface_t* pFace, directlight_t* pSkylight,
+ Vector& radcolor )
+{
+ texinfo_t* pTex = &texinfo[pFace->texinfo];
+ if (pTex)
+ {
+ // If we hit the sky, use the sky ambient
+ if (pTex->flags & SURF_SKY)
+ {
+ if (pSkylight)
+ {
+ // add in sky ambient
+ VectorDivide( pSkylight->light.intensity, 255.0f, radcolor );
+ }
+ }
+ else
+ {
+ VectorMultiply( radcolor, dtexdata[pTex->texdata].reflectivity, radcolor );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the lightmap color at a particular point
+//-----------------------------------------------------------------------------
+
+static void ComputeLightmapColorFromAverage( dface_t* pFace, directlight_t* pSkylight, float scale, Vector pColor[MAX_LIGHTSTYLES] )
+{
+ texinfo_t* pTex = &texinfo[pFace->texinfo];
+ if (pTex->flags & SURF_SKY)
+ {
+ if (pSkylight)
+ {
+ // add in sky ambient
+ Vector amb = pSkylight->light.intensity / 255.0f;
+ pColor[0] += amb * scale;
+ }
+ return;
+ }
+
+ for (int maps = 0 ; maps < MAXLIGHTMAPS && pFace->styles[maps] != 255 ; ++maps)
+ {
+ ColorRGBExp32* pAvgColor = dface_AvgLightColor( pFace, maps );
+
+ // this code expects values from [0..1] not [0..255]
+ Vector color;
+ color[0] = TexLightToLinear( pAvgColor->r, pAvgColor->exponent );
+ color[1] = TexLightToLinear( pAvgColor->g, pAvgColor->exponent );
+ color[2] = TexLightToLinear( pAvgColor->b, pAvgColor->exponent );
+
+ ComputeAmbientFromSurface( pFace, pSkylight, color );
+
+ int style = pFace->styles[maps];
+ pColor[style] += color * scale;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns true if the surface has bumped lightmaps
+//-----------------------------------------------------------------------------
+
+static bool SurfHasBumpedLightmaps( dface_t *pSurf )
+{
+ bool hasBumpmap = false;
+ if( ( texinfo[pSurf->texinfo].flags & SURF_BUMPLIGHT ) &&
+ ( !( texinfo[pSurf->texinfo].flags & SURF_NOLIGHT ) ) )
+ {
+ hasBumpmap = true;
+ }
+ return hasBumpmap;
+}
+
+//-----------------------------------------------------------------------------
+// Computes the lightmap color at a particular point
+//-----------------------------------------------------------------------------
+
+static void ComputeLightmapColorPointSample( dface_t* pFace, directlight_t* pSkylight, Vector2D const& luv, float scale, Vector pColor[MAX_LIGHTSTYLES] )
+{
+ // face unaffected by light
+ if (pFace->lightofs == -1 )
+ return;
+
+ int smax = ( pFace->m_LightmapTextureSizeInLuxels[0] ) + 1;
+ int tmax = ( pFace->m_LightmapTextureSizeInLuxels[1] ) + 1;
+
+ // luv is in the space of the accumulated lightmap page; we need to convert
+ // it to be in the space of the surface
+ int ds = clamp( (int)luv.x, 0, smax-1 );
+ int dt = clamp( (int)luv.y, 0, tmax-1 );
+
+ int offset = smax * tmax;
+ if ( SurfHasBumpedLightmaps( pFace ) )
+ offset *= ( NUM_BUMP_VECTS + 1 );
+
+ ColorRGBExp32* pLightmap = (ColorRGBExp32*)&pdlightdata->Base()[pFace->lightofs];
+ pLightmap += dt * smax + ds;
+ for (int maps = 0 ; maps < MAXLIGHTMAPS && pFace->styles[maps] != 255 ; ++maps)
+ {
+ int style = pFace->styles[maps];
+
+ Vector color;
+ color[0] = TexLightToLinear( pLightmap->r, pLightmap->exponent );
+ color[1] = TexLightToLinear( pLightmap->g, pLightmap->exponent );
+ color[2] = TexLightToLinear( pLightmap->b, pLightmap->exponent );
+
+ ComputeAmbientFromSurface( pFace, pSkylight, color );
+ pColor[style] += color * scale;
+
+ pLightmap += offset;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Tests a particular node
+//-----------------------------------------------------------------------------
+
+class CLightSurface : public IBSPNodeEnumerator
+{
+public:
+ CLightSurface(int iThread) : m_pSurface(0), m_HitFrac(1.0f), m_bHasLuxel(false), m_iThread(iThread) {}
+
+ // call back with a node and a context
+ bool EnumerateNode( int node, Ray_t const& ray, float f, int context )
+ {
+ dface_t* pSkySurface = 0;
+
+ // Compute the actual point
+ Vector pt;
+ VectorMA( ray.m_Start, f, ray.m_Delta, pt );
+
+ dnode_t* pNode = &dnodes[node];
+ dface_t* pFace = &g_pFaces[pNode->firstface];
+ for (int i=0 ; i < pNode->numfaces ; ++i, ++pFace)
+ {
+ // Don't take into account faces that are int a leaf
+ if ( !pFace->onNode )
+ continue;
+
+ // Don't test displacement faces
+ if ( pFace->dispinfo != -1 )
+ continue;
+
+ texinfo_t* pTex = &texinfo[pFace->texinfo];
+
+ // Don't immediately return when we hit sky;
+ // we may actually hit another surface
+ if (pTex->flags & SURF_SKY)
+ {
+ if (TestPointAgainstSkySurface( pt, pFace ))
+ {
+ pSkySurface = pFace;
+ }
+
+ continue;
+ }
+
+ if (TestPointAgainstSurface( pt, pFace, pTex ))
+ {
+ m_HitFrac = f;
+ m_pSurface = pFace;
+ m_bHasLuxel = true;
+ return false;
+ }
+ }
+
+ // if we hit a sky surface, return it
+ m_pSurface = pSkySurface;
+ return (m_pSurface == 0);
+ }
+
+ // call back with a leaf and a context
+ virtual bool EnumerateLeaf( int leaf, Ray_t const& ray, float start, float end, int context )
+ {
+ bool hit = false;
+ dleaf_t* pLeaf = &dleafs[leaf];
+ for (int i=0 ; i < pLeaf->numleaffaces ; ++i)
+ {
+ Assert( pLeaf->firstleafface + i < numleaffaces );
+ Assert( dleaffaces[pLeaf->firstleafface + i] < numfaces );
+ dface_t* pFace = &g_pFaces[dleaffaces[pLeaf->firstleafface + i]];
+
+ // Don't test displacement faces; we need to check another list
+ if ( pFace->dispinfo != -1 )
+ continue;
+
+ // Don't take into account faces that are on a node
+ if ( pFace->onNode )
+ continue;
+
+ // Find intersection point against detail brushes
+ texinfo_t* pTex = &texinfo[pFace->texinfo];
+
+ dplane_t* pPlane = &dplanes[pFace->planenum];
+
+ // Backface cull...
+ if (DotProduct( pPlane->normal, ray.m_Delta ) > 0)
+ continue;
+
+ float startDotN = DotProduct( ray.m_Start, pPlane->normal );
+ float deltaDotN = DotProduct( ray.m_Delta, pPlane->normal );
+
+ float front = startDotN + start * deltaDotN - pPlane->dist;
+ float back = startDotN + end * deltaDotN - pPlane->dist;
+
+ int side = front < 0;
+
+ // Blow it off if it doesn't split the plane...
+ if ( (back < 0) == side )
+ continue;
+
+ // Don't test a surface that is farther away from the closest found intersection
+ float f = front / (front-back);
+ float mid = start * (1.0f - f) + end * f;
+ if (mid >= m_HitFrac)
+ continue;
+
+ Vector pt;
+ VectorMA( ray.m_Start, mid, ray.m_Delta, pt );
+
+ if (TestPointAgainstSurface( pt, pFace, pTex ))
+ {
+ m_HitFrac = mid;
+ m_pSurface = pFace;
+ hit = true;
+ m_bHasLuxel = true;
+ }
+ }
+
+ // Now try to clip against all displacements in the leaf
+ float dist;
+ Vector2D luxelCoord;
+ dface_t *pDispFace;
+ StaticDispMgr()->ClipRayToDispInLeaf( s_DispTested[m_iThread], ray, leaf, dist, pDispFace, luxelCoord );
+ if (dist < m_HitFrac)
+ {
+ m_HitFrac = dist;
+ m_pSurface = pDispFace;
+ Vector2DCopy( luxelCoord, m_LuxelCoord );
+ hit = true;
+ m_bHasLuxel = true;
+ }
+ return !hit;
+ }
+
+ bool FindIntersection( Ray_t const& ray )
+ {
+ StaticDispMgr()->StartRayTest( s_DispTested[m_iThread] );
+ return !EnumerateNodesAlongRay( ray, this, 0 );
+ }
+
+private:
+ bool TestPointAgainstSurface( Vector const& pt, dface_t* pFace, texinfo_t* pTex )
+ {
+ // no lightmaps on this surface? punt...
+ // FIXME: should be water surface?
+ if (pTex->flags & SURF_NOLIGHT)
+ return false;
+
+ // See where in lightmap space our intersection point is
+ float s, t;
+ s = DotProduct (pt.Base(), pTex->lightmapVecsLuxelsPerWorldUnits[0]) +
+ pTex->lightmapVecsLuxelsPerWorldUnits[0][3];
+ t = DotProduct (pt.Base(), pTex->lightmapVecsLuxelsPerWorldUnits[1]) +
+ pTex->lightmapVecsLuxelsPerWorldUnits[1][3];
+
+ // Not in the bounds of our lightmap? punt...
+ if( s < pFace->m_LightmapTextureMinsInLuxels[0] || t < pFace->m_LightmapTextureMinsInLuxels[1] )
+ return false;
+
+ // assuming a square lightmap (FIXME: which ain't always the case),
+ // lets see if it lies in that rectangle. If not, punt...
+ float ds = s - pFace->m_LightmapTextureMinsInLuxels[0];
+ float dt = t - pFace->m_LightmapTextureMinsInLuxels[1];
+ if( ds > pFace->m_LightmapTextureSizeInLuxels[0] || dt > pFace->m_LightmapTextureSizeInLuxels[1] )
+ return false;
+
+ m_LuxelCoord.x = ds;
+ m_LuxelCoord.y = dt;
+
+ return true;
+ }
+
+ bool TestPointAgainstSkySurface( Vector const &pt, dface_t *pFace )
+ {
+ // Create sky face winding.
+ Vector v( 0.0f, 0.0f, 0.0f );
+ winding_t *pWinding = WindingFromFace( pFace, v );
+
+ // Test point in winding. (Since it is at the node, it is in the plane.)
+ bool bRet = PointInWinding( pt, pWinding );
+
+ FreeWinding( pWinding );
+
+ return bRet;
+ }
+
+
+public:
+ int m_iThread;
+ dface_t* m_pSurface;
+ float m_HitFrac;
+ Vector2D m_LuxelCoord;
+ bool m_bHasLuxel;
+};
+
+bool CastRayInLeaf( int iThread, const Vector &start, const Vector &end, int leafIndex, float *pFraction, Vector *pNormal )
+{
+ pFraction[0] = 1.0f;
+
+ Ray_t ray;
+ ray.Init( start, end, vec3_origin, vec3_origin );
+ CBaseTrace trace;
+ if ( TraceLeafBrushes( leafIndex, start, end, trace ) != 1.0f )
+ {
+ pFraction[0] = trace.fraction;
+ *pNormal = trace.plane.normal;
+ }
+ else
+ {
+ Assert(!trace.startsolid && !trace.allsolid);
+ }
+ StaticDispMgr()->StartRayTest( s_DispTested[iThread] );
+ // Now try to clip against all displacements in the leaf
+ float dist;
+ Vector normal;
+ StaticDispMgr()->ClipRayToDispInLeaf( s_DispTested[iThread], ray, leafIndex, dist, &normal );
+ if ( dist < pFraction[0] )
+ {
+ pFraction[0] = dist;
+ *pNormal = normal;
+ }
+ return pFraction[0] != 1.0f ? true : false;
+}
+
+//-----------------------------------------------------------------------------
+// Computes ambient lighting along a specified ray.
+// Ray represents a cone, tanTheta is the tan of the inner cone angle
+//-----------------------------------------------------------------------------
+void CalcRayAmbientLighting( int iThread, const Vector &vStart, const Vector &vEnd, float tanTheta, Vector color[MAX_LIGHTSTYLES] )
+{
+ Ray_t ray;
+ ray.Init( vStart, vEnd, vec3_origin, vec3_origin );
+
+ directlight_t *pSkyLight = FindAmbientSkyLight();
+
+ CLightSurface surfEnum(iThread);
+ if (!surfEnum.FindIntersection( ray ))
+ return;
+
+ // compute the approximate radius of a circle centered around the intersection point
+ float dist = ray.m_Delta.Length() * tanTheta * surfEnum.m_HitFrac;
+
+ // until 20" we use the point sample, then blend in the average until we're covering 40"
+ // This is attempting to model the ray as a cone - in the ideal case we'd simply sample all
+ // luxels in the intersection of the cone with the surface. Since we don't have surface
+ // neighbor information computed we'll just approximate that sampling with a blend between
+ // a point sample and the face average.
+ // This yields results that are similar in that aliasing is reduced at distance while
+ // point samples provide accuracy for intersections with near geometry
+ float scaleAvg = RemapValClamped( dist, 20, 40, 0.0f, 1.0f );
+
+ if ( !surfEnum.m_bHasLuxel )
+ {
+ // don't have luxel UV, so just use average sample
+ scaleAvg = 1.0;
+ }
+ float scaleSample = 1.0f - scaleAvg;
+
+ if (scaleAvg != 0)
+ {
+ ComputeLightmapColorFromAverage( surfEnum.m_pSurface, pSkyLight, scaleAvg, color );
+ }
+ if (scaleSample != 0)
+ {
+ ComputeLightmapColorPointSample( surfEnum.m_pSurface, pSkyLight, surfEnum.m_LuxelCoord, scaleSample, color );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Compute ambient lighting component at specified position.
+//-----------------------------------------------------------------------------
+static void ComputeAmbientLightingAtPoint( int iThread, const Vector &origin, Vector radcolor[NUMVERTEXNORMALS], Vector color[MAX_LIGHTSTYLES] )
+{
+ // NOTE: I'm not dealing with shadow-casting static props here
+ // This is for speed, although we can add it if it turns out to
+ // be important
+
+ // sample world by casting N rays distributed across a sphere
+ Vector upend;
+
+ int j;
+ for ( j = 0; j < MAX_LIGHTSTYLES; ++j)
+ {
+ color[j].Init( 0,0,0 );
+ }
+
+ float tanTheta = tan(VERTEXNORMAL_CONE_INNER_ANGLE);
+ for (int i = 0; i < NUMVERTEXNORMALS; i++)
+ {
+ VectorMA( origin, COORD_EXTENT * 1.74, g_anorms[i], upend );
+
+ // Now that we've got a ray, see what surface we've hit
+ CalcRayAmbientLighting( iThread, origin, upend, tanTheta, color );
+
+// DumpRayToGlView( ray, surfEnum.m_HitFrac, &color[0], "test.out" );
+ }
+
+ for ( j = 0; j < MAX_LIGHTSTYLES; ++j)
+ {
+ VectorMultiply( color[j], 255.0f / (float)NUMVERTEXNORMALS, color[j] );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Trace hemispherical rays from a vertex, accumulating indirect
+// sources at each ray termination.
+//-----------------------------------------------------------------------------
+void ComputeIndirectLightingAtPoint( Vector &position, Vector &normal, Vector &outColor,
+ int iThread, bool force_fast, bool bIgnoreNormals )
+{
+ Ray_t ray;
+ CLightSurface surfEnum(iThread);
+
+ outColor.Init();
+
+
+ int nSamples = NUMVERTEXNORMALS;
+ if ( do_fast || force_fast )
+ nSamples /= 4;
+ else
+ nSamples *= g_flSkySampleScale;
+
+ float totalDot = 0;
+ DirectionalSampler_t sampler;
+ for (int j = 0; j < nSamples; j++)
+ {
+ Vector samplingNormal = sampler.NextValue();
+ float dot;
+
+ if ( bIgnoreNormals )
+ dot = (0.7071/2);
+ else
+ dot = DotProduct( normal, samplingNormal );
+
+ if ( dot <= EQUAL_EPSILON )
+ {
+ // reject angles behind our plane
+ continue;
+ }
+
+ totalDot += dot;
+
+ // trace to determine surface
+ Vector vEnd;
+ VectorScale( samplingNormal, MAX_TRACE_LENGTH, vEnd );
+ VectorAdd( position, vEnd, vEnd );
+
+ ray.Init( position, vEnd, vec3_origin, vec3_origin );
+ if ( !surfEnum.FindIntersection( ray ) )
+ continue;
+
+ // get color from surface lightmap
+ texinfo_t* pTex = &texinfo[surfEnum.m_pSurface->texinfo];
+ if ( !pTex || pTex->flags & SURF_SKY )
+ {
+ // ignore contribution from sky
+ // sky ambient already accounted for during direct pass
+ continue;
+ }
+
+ if ( surfEnum.m_pSurface->styles[0] == 255 || surfEnum.m_pSurface->lightofs < 0 )
+ {
+ // no light affects this face
+ continue;
+ }
+
+
+ Vector lightmapColor;
+ if ( !surfEnum.m_bHasLuxel )
+ {
+ ColorRGBExp32* pAvgLightmapColor = dface_AvgLightColor( surfEnum.m_pSurface, 0 );
+ ColorRGBExp32ToVector( *pAvgLightmapColor, lightmapColor );
+ }
+ else
+ {
+ // get color from displacement
+ int smax = ( surfEnum.m_pSurface->m_LightmapTextureSizeInLuxels[0] ) + 1;
+ int tmax = ( surfEnum.m_pSurface->m_LightmapTextureSizeInLuxels[1] ) + 1;
+
+ // luxelcoord is in the space of the accumulated lightmap page; we need to convert
+ // it to be in the space of the surface
+ int ds = clamp( (int)surfEnum.m_LuxelCoord.x, 0, smax-1 );
+ int dt = clamp( (int)surfEnum.m_LuxelCoord.y, 0, tmax-1 );
+
+ ColorRGBExp32* pLightmap = (ColorRGBExp32*)&(*pdlightdata)[surfEnum.m_pSurface->lightofs];
+ pLightmap += dt * smax + ds;
+ ColorRGBExp32ToVector( *pLightmap, lightmapColor );
+ }
+
+ VectorMultiply( lightmapColor, dtexdata[pTex->texdata].reflectivity, lightmapColor );
+ VectorAdd( outColor, lightmapColor, outColor );
+ }
+
+ if ( totalDot )
+ {
+ VectorScale( outColor, 1.0f/totalDot, outColor );
+ }
+}
+
+static void ComputeAmbientLighting( int iThread, DetailObjectLump_t& prop, Vector color[MAX_LIGHTSTYLES] )
+{
+ Vector origin, normal;
+ ComputeWorldCenter( prop, origin, normal );
+
+ if ( !origin.IsValid() || !normal.IsValid() )
+ {
+ static bool s_Warned = false;
+ if ( !s_Warned )
+ {
+ Warning("WARNING: Bogus detail props encountered!\n" );
+ s_Warned = true;
+ }
+
+ // fill with debug color
+ for ( int i = 0; i < MAX_LIGHTSTYLES; ++i)
+ {
+ color[i].Init(1,0,0);
+ }
+ return;
+ }
+
+ Vector radcolor[NUMVERTEXNORMALS];
+ ComputeAmbientLightingAtPoint( iThread, origin, radcolor, color );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes lighting for a single detal prop
+//-----------------------------------------------------------------------------
+
+static void ComputeLighting( DetailObjectLump_t& prop, int iThread )
+{
+ // We're going to take the maximum of the ambient lighting and
+ // the strongest directional light. This works because we're assuming
+ // the props will have built-in faked lighting.
+
+ Vector directColor[MAX_LIGHTSTYLES];
+ Vector ambColor[MAX_LIGHTSTYLES];
+
+ // Get the max influence of all direct lights
+ ComputeMaxDirectLighting( prop, directColor, iThread );
+
+ // Get the ambient lighting + lightstyles
+ ComputeAmbientLighting( iThread, prop, ambColor );
+
+ // Base lighting
+ Vector totalColor;
+ VectorAdd( directColor[0], ambColor[0], totalColor );
+ VectorToColorRGBExp32( totalColor, prop.m_Lighting );
+
+ bool hasLightstyles = false;
+ prop.m_LightStyleCount = 0;
+
+ // lightstyles
+ for (int i = 1; i < MAX_LIGHTSTYLES; ++i )
+ {
+ VectorAdd( directColor[i], ambColor[i], totalColor );
+ totalColor *= 0.5f;
+
+ if ((totalColor[0] != 0.0f) || (totalColor[1] != 0.0f) ||
+ (totalColor[2] != 0.0f) )
+ {
+ if (!hasLightstyles)
+ {
+ prop.m_LightStyles = s_pDetailPropLightStyleLump->Size();
+ hasLightstyles = true;
+ }
+
+ int j = s_pDetailPropLightStyleLump->AddToTail();
+ VectorToColorRGBExp32( totalColor, (*s_pDetailPropLightStyleLump)[j].m_Lighting );
+ (*s_pDetailPropLightStyleLump)[j].m_Style = i;
+ ++prop.m_LightStyleCount;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Unserialization
+//-----------------------------------------------------------------------------
+static void UnserializeModelDict( CUtlBuffer& buf )
+{
+ // Get origin offset for each model...
+ int count = buf.GetInt();
+ while ( --count >= 0 )
+ {
+ DetailObjectDictLump_t lump;
+ buf.Get( &lump, sizeof(DetailObjectDictLump_t) );
+
+ int i = g_ModelCenterOffset.AddToTail();
+
+ CUtlBuffer mdlbuf;
+ if (LoadStudioModel( lump.m_Name, mdlbuf ))
+ {
+ studiohdr_t* pHdr = (studiohdr_t*)mdlbuf.Base();
+ VectorAdd( pHdr->hull_min, pHdr->hull_max, g_ModelCenterOffset[i] );
+ g_ModelCenterOffset[i] *= 0.5f;
+ }
+ else
+ {
+ g_ModelCenterOffset[i].Init(0,0,0);
+ }
+ }
+}
+
+static void UnserializeSpriteDict( CUtlBuffer& buf )
+{
+ // Get origin offset for each model...
+ int count = buf.GetInt();
+ while ( --count >= 0 )
+ {
+ DetailSpriteDictLump_t lump;
+ buf.Get( &lump, sizeof(DetailSpriteDictLump_t) );
+
+ // For these sprites, x goes out the front, y right, z up
+ int i = g_SpriteCenterOffset.AddToTail();
+ g_SpriteCenterOffset[i].x = 0.0f;
+ g_SpriteCenterOffset[i].y = lump.m_LR.x + lump.m_UL.x;
+ g_SpriteCenterOffset[i].z = lump.m_LR.y + lump.m_UL.y;
+ g_SpriteCenterOffset[i] *= 0.5f;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Unserializes the detail props
+//-----------------------------------------------------------------------------
+static int UnserializeDetailProps( DetailObjectLump_t*& pProps )
+{
+ GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROPS );
+
+ if (g_GameLumps.GetGameLumpVersion(handle) != GAMELUMP_DETAIL_PROPS_VERSION)
+ return 0;
+
+ // Unserialize
+ CUtlBuffer buf( g_GameLumps.GetGameLump(handle), g_GameLumps.GameLumpSize( handle ), CUtlBuffer::READ_ONLY );
+
+ UnserializeModelDict( buf );
+ UnserializeSpriteDict( buf );
+
+ // Now we're pointing to the detail prop data
+ // This actually works because the scope of the game lump data
+ // is global and the buf was just pointing to it.
+ int count = buf.GetInt();
+ if (count)
+ {
+ pProps = (DetailObjectLump_t*)buf.PeekGet();
+ }
+ else
+ {
+ pProps = 0;
+ }
+ return count;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes the detail lighting lump
+//-----------------------------------------------------------------------------
+static void WriteDetailLightingLump( int lumpID, int lumpVersion, CUtlVector<DetailPropLightstylesLump_t> &lumpData )
+{
+ GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle(lumpID);
+ if (handle != g_GameLumps.InvalidGameLump())
+ g_GameLumps.DestroyGameLump(handle);
+ int lightsize = lumpData.Size() * sizeof(DetailPropLightstylesLump_t);
+ int lumpsize = lightsize + sizeof(int);
+
+ handle = g_GameLumps.CreateGameLump( lumpID, lumpsize, 0, lumpVersion );
+
+ // Serialize the data
+ CUtlBuffer buf( g_GameLumps.GetGameLump(handle), lumpsize );
+ buf.PutInt( lumpData.Size() );
+ if (lightsize)
+ buf.Put( lumpData.Base(), lightsize );
+}
+
+static void WriteDetailLightingLumps( void )
+{
+ WriteDetailLightingLump( GAMELUMP_DETAIL_PROP_LIGHTING, GAMELUMP_DETAIL_PROP_LIGHTING_VERSION, s_DetailPropLightStyleLumpLDR );
+ WriteDetailLightingLump( GAMELUMP_DETAIL_PROP_LIGHTING_HDR, GAMELUMP_DETAIL_PROP_LIGHTING_HDR_VERSION, s_DetailPropLightStyleLumpHDR );
+}
+
+// need to do this so that if we are building HDR data, the LDR data is intact, and vice versa.s
+void UnserializeDetailPropLighting( int lumpID, int lumpVersion, CUtlVector<DetailPropLightstylesLump_t> &lumpData )
+{
+ GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle( lumpID );
+
+ if( handle == g_GameLumps.InvalidGameLump() )
+ {
+ return;
+ }
+
+ if (g_GameLumps.GetGameLumpVersion(handle) != lumpVersion)
+ return;
+
+ // Unserialize
+ CUtlBuffer buf( g_GameLumps.GetGameLump(handle), g_GameLumps.GameLumpSize( handle ), CUtlBuffer::READ_ONLY );
+
+ int count = buf.GetInt();
+ if( !count )
+ {
+ return;
+ }
+ lumpData.SetCount( count );
+ int lightsize = lumpData.Size() * sizeof(DetailPropLightstylesLump_t);
+ buf.Get( lumpData.Base(), lightsize );
+}
+
+DetailObjectLump_t *g_pMPIDetailProps = NULL;
+
+void VMPI_ProcessDetailPropWU( int iThread, int iWorkUnit, MessageBuffer *pBuf )
+{
+ CUtlVector<DetailPropLightstylesLump_t> *pDetailPropLump = s_pDetailPropLightStyleLump;
+
+ DetailObjectLump_t& prop = g_pMPIDetailProps[iWorkUnit];
+ ComputeLighting( prop, iThread );
+
+ // Send the results back...
+ pBuf->write( &prop.m_Lighting, sizeof( prop.m_Lighting ) );
+ pBuf->write( &prop.m_LightStyleCount, sizeof( prop.m_LightStyleCount ) );
+ pBuf->write( &prop.m_LightStyles, sizeof( prop.m_LightStyles ) );
+
+ for ( int i=0; i < prop.m_LightStyleCount; i++ )
+ {
+ DetailPropLightstylesLump_t *l = &pDetailPropLump->Element( i + prop.m_LightStyles );
+ pBuf->write( &l->m_Lighting, sizeof( l->m_Lighting ) );
+ pBuf->write( &l->m_Style, sizeof( l->m_Style ) );
+ }
+}
+
+
+void VMPI_ReceiveDetailPropWU( int iWorkUnit, MessageBuffer *pBuf, int iWorker )
+{
+ CUtlVector<DetailPropLightstylesLump_t> *pDetailPropLump = s_pDetailPropLightStyleLump;
+
+ DetailObjectLump_t& prop = g_pMPIDetailProps[iWorkUnit];
+
+ pBuf->read( &prop.m_Lighting, sizeof( prop.m_Lighting ) );
+ pBuf->read( &prop.m_LightStyleCount, sizeof( prop.m_LightStyleCount ) );
+ pBuf->read( &prop.m_LightStyles, sizeof( prop.m_LightStyles ) );
+
+ pDetailPropLump->EnsureCount( prop.m_LightStyles + prop.m_LightStyleCount );
+
+ for ( int i=0; i < prop.m_LightStyleCount; i++ )
+ {
+ DetailPropLightstylesLump_t *l = &pDetailPropLump->Element( i + prop.m_LightStyles );
+ pBuf->read( &l->m_Lighting, sizeof( l->m_Lighting ) );
+ pBuf->read( &l->m_Style, sizeof( l->m_Style ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Computes lighting for the detail props
+//-----------------------------------------------------------------------------
+void ComputeDetailPropLighting( int iThread )
+{
+ // illuminate them all
+ DetailObjectLump_t* pProps;
+ int count = UnserializeDetailProps( pProps );
+ if (!count)
+ return;
+
+ // unserialize the lump that we aren't computing.
+ if( g_bHDR )
+ {
+ UnserializeDetailPropLighting( GAMELUMP_DETAIL_PROP_LIGHTING, GAMELUMP_DETAIL_PROP_LIGHTING_VERSION, s_DetailPropLightStyleLumpLDR );
+ }
+ else
+ {
+ UnserializeDetailPropLighting( GAMELUMP_DETAIL_PROP_LIGHTING_HDR, GAMELUMP_DETAIL_PROP_LIGHTING_HDR_VERSION, s_DetailPropLightStyleLumpHDR );
+ }
+
+ StartPacifier("Computing detail prop lighting : ");
+
+ for (int i = 0; i < count; ++i)
+ {
+ UpdatePacifier( (float)i / (float)count );
+ ComputeLighting( pProps[i], iThread );
+ }
+
+ // Write detail prop lightstyle lump...
+ WriteDetailLightingLumps();
+ EndPacifier( true );
+}
diff --git a/mp/src/utils/vrad/vraddetailprops.h b/mp/src/utils/vrad/vraddetailprops.h
index f9cc8da1..0345d96a 100644
--- a/mp/src/utils/vrad/vraddetailprops.h
+++ b/mp/src/utils/vrad/vraddetailprops.h
@@ -1,34 +1,34 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#ifndef VRADDETAILPROPS_H
-#define VRADDETAILPROPS_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-#include "bspfile.h"
-#include "mathlib/anorms.h"
-
-
-// Calculate the lighting at whatever surface the ray hits.
-// Note: this ADDS to the values already in color. So if you want absolute
-// values in there, then clear the values in color[] first.
-void CalcRayAmbientLighting(
- int iThread,
- const Vector &vStart,
- const Vector &vEnd,
- float tanTheta, // tangent of the inner angle of the cone
- Vector color[MAX_LIGHTSTYLES] // The color contribution from each lightstyle.
- );
-
-bool CastRayInLeaf( int iThread, const Vector &start, const Vector &end, int leafIndex, float *pFraction, Vector *pNormal );
-
-void ComputeDetailPropLighting( int iThread );
-
-
-#endif // VRADDETAILPROPS_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef VRADDETAILPROPS_H
+#define VRADDETAILPROPS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "bspfile.h"
+#include "mathlib/anorms.h"
+
+
+// Calculate the lighting at whatever surface the ray hits.
+// Note: this ADDS to the values already in color. So if you want absolute
+// values in there, then clear the values in color[] first.
+void CalcRayAmbientLighting(
+ int iThread,
+ const Vector &vStart,
+ const Vector &vEnd,
+ float tanTheta, // tangent of the inner angle of the cone
+ Vector color[MAX_LIGHTSTYLES] // The color contribution from each lightstyle.
+ );
+
+bool CastRayInLeaf( int iThread, const Vector &start, const Vector &end, int leafIndex, float *pFraction, Vector *pNormal );
+
+void ComputeDetailPropLighting( int iThread );
+
+
+#endif // VRADDETAILPROPS_H
diff --git a/mp/src/utils/vrad/vraddisps.cpp b/mp/src/utils/vrad/vraddisps.cpp
index 0bbafd40..d6bc6f7a 100644
--- a/mp/src/utils/vrad/vraddisps.cpp
+++ b/mp/src/utils/vrad/vraddisps.cpp
@@ -1,1759 +1,1759 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "vrad.h"
-#include "utlvector.h"
-#include "cmodel.h"
-#include "BSPTreeData.h"
-#include "VRAD_DispColl.h"
-#include "CollisionUtils.h"
-#include "lightmap.h"
-#include "Radial.h"
-#include "CollisionUtils.h"
-#include "mathlib/bumpvects.h"
-#include "utlrbtree.h"
-#include "tier0/fasttimer.h"
-#include "disp_vrad.h"
-
-class CBSPDispRayDistanceEnumerator;
-
-//=============================================================================
-//
-// Displacement/Face List
-//
-class CBSPDispFaceListEnumerator : public ISpatialLeafEnumerator, public IBSPTreeDataEnumerator
-{
-public:
-
- //=========================================================================
- //
- // Construction/Deconstruction
- //
- CBSPDispFaceListEnumerator() {};
- virtual ~CBSPDispFaceListEnumerator()
- {
- m_DispList.Purge();
- m_FaceList.Purge();
- }
-
- // ISpatialLeafEnumerator
- bool EnumerateLeaf( int ndxLeaf, int context );
-
- // IBSPTreeDataEnumerator
- bool FASTCALL EnumerateElement( int userId, int context );
-
-public:
-
- CUtlVector<CVRADDispColl*> m_DispList;
- CUtlVector<int> m_FaceList;
-};
-
-
-//=============================================================================
-//
-// RayEnumerator
-//
-class CBSPDispRayEnumerator : public ISpatialLeafEnumerator, public IBSPTreeDataEnumerator
-{
-public:
- // ISpatialLeafEnumerator
- bool EnumerateLeaf( int ndxLeaf, int context );
-
- // IBSPTreeDataEnumerator
- bool FASTCALL EnumerateElement( int userId, int context );
-};
-
-//=============================================================================
-//
-// VRad Displacement Manager
-//
-class CVRadDispMgr : public IVRadDispMgr
-{
-public:
-
- //=========================================================================
- //
- // Construction/Deconstruction
- //
- CVRadDispMgr();
- virtual ~CVRadDispMgr();
-
- // creation/destruction
- void Init( void );
- void Shutdown( void );
-
- // "CalcPoints"
- bool BuildDispSamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace );
- bool BuildDispLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace );
- bool BuildDispSamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace );
-
- // patching functions
- void MakePatches( void );
- void SubdividePatch( int iPatch );
-
- // pre "FinalLightFace"
- void InsertSamplesDataIntoHashTable( void );
- void InsertPatchSampleDataIntoHashTable( void );
-
- // "FinalLightFace"
- radial_t *BuildLuxelRadial( int ndxFace, int ndxStyle, bool bBump );
- bool SampleRadial( int ndxFace, radial_t *pRadial, Vector const &vPos, int ndxLxl, LightingValue_t *pLightSample, int sampleCount, bool bPatch );
- radial_t *BuildPatchRadial( int ndxFace, bool bBump );
-
- // utility
- void GetDispSurfNormal( int ndxFace, Vector &pt, Vector &ptNormal, bool bInside );
- void GetDispSurf( int ndxFace, CVRADDispColl **ppDispTree );
-
- // bsp tree functions
- bool ClipRayToDisp( DispTested_t &dispTested, Ray_t const &ray );
- bool ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf );
- void ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf,
- float& dist, dface_t*& pFace, Vector2D& luxelCoord );
- void ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
- int ndxLeaf, float& dist, Vector *pNormal );
-
- void StartRayTest( DispTested_t &dispTested );
- void AddPolysForRayTrace( void );
-
- // general timing -- should be moved!!
- void StartTimer( const char *name );
- void EndTimer( void );
-
- //=========================================================================
- //
- // Enumeration Methods
- //
- bool DispRay_EnumerateLeaf( int ndxLeaf, int context );
- bool DispRay_EnumerateElement( int userId, int context );
- bool DispRayDistance_EnumerateElement( int userId, CBSPDispRayDistanceEnumerator* pEnum );
-
- bool DispFaceList_EnumerateLeaf( int ndxLeaf, int context );
- bool DispFaceList_EnumerateElement( int userId, int context );
-
-private:
-
- //=========================================================================
- //
- // BSP Tree Helpers
- //
- void InsertDispIntoTree( int ndxDisp );
- void RemoveDispFromTree( int ndxDisp );
-
- //=========================================================================
- //
- // Displacement Data Loader (from .bsp)
- //
- void UnserializeDisps( void );
- void DispBuilderInit( CCoreDispInfo *pBuilderDisp, dface_t *pFace, int ndxFace );
-
- //=========================================================================
- //
- // Sampling Helpers
- //
- void RadialLuxelBuild( CVRADDispColl *pDispTree, radial_t *pRadial, int ndxStyle, bool bBump );
- void RadialLuxelAddSamples( int ndxFace, Vector const &luxelPt, Vector const &luxelNormal, float radius,
- radial_t *pRadial, int ndxRadial, bool bBump, int lightStyle );
-
- void RadialPatchBuild( CVRADDispColl *pDispTree, radial_t *pRadial, bool bBump );
- void RadialLuxelAddPatch( int ndxFace, Vector const &luxelPt,
- Vector const &luxelNormal, float radius,
- radial_t *pRadial, int ndxRadial, bool bBump,
- CUtlVector<CPatch*> &interestingPatches );
-
- bool IsNeighbor( int iDispFace, int iNeighborFace );
-
- void GetInterestingPatchesForLuxels(
- int ndxFace,
- CUtlVector<CPatch*> &interestingPatches,
- float patchSampleRadius );
-
-private:
-
- struct DispCollTree_t
- {
- CVRADDispColl *m_pDispTree;
- BSPTreeDataHandle_t m_Handle;
- };
-
- struct EnumContext_t
- {
- DispTested_t *m_pDispTested;
- Ray_t const *m_pRay;
- };
-
- CUtlVector<DispCollTree_t> m_DispTrees;
-
- IBSPTreeData *m_pBSPTreeData;
-
- CBSPDispRayEnumerator m_EnumDispRay;
- CBSPDispFaceListEnumerator m_EnumDispFaceList;
-
- int sampleCount;
- Vector *m_pSamplePos;
-
- CFastTimer m_Timer;
-};
-
-//-----------------------------------------------------------------------------
-// Purpose: expose IVRadDispMgr to vrad
-//-----------------------------------------------------------------------------
-
-static CVRadDispMgr s_DispMgr;
-
-IVRadDispMgr *StaticDispMgr( void )
-{
- return &s_DispMgr;
-}
-
-
-//=============================================================================
-//
-// Displacement/Face List
-//
-// ISpatialLeafEnumerator
-bool CBSPDispFaceListEnumerator::EnumerateLeaf( int ndxLeaf, int context )
-{
- return s_DispMgr.DispFaceList_EnumerateLeaf( ndxLeaf, context );
-}
-
-// IBSPTreeDataEnumerator
-bool FASTCALL CBSPDispFaceListEnumerator::EnumerateElement( int userId, int context )
-{
- return s_DispMgr.DispFaceList_EnumerateElement( userId, context );
-}
-
-
-//=============================================================================
-//
-// RayEnumerator
-//
-bool CBSPDispRayEnumerator::EnumerateLeaf( int ndxLeaf, int context )
-{
- return s_DispMgr.DispRay_EnumerateLeaf( ndxLeaf, context );
-}
-
-bool FASTCALL CBSPDispRayEnumerator::EnumerateElement( int userId, int context )
-{
- return s_DispMgr.DispRay_EnumerateElement( userId, context );
-}
-
-
-//-----------------------------------------------------------------------------
-// Here's an enumerator that we use for testing against disps in a leaf...
-//-----------------------------------------------------------------------------
-
-class CBSPDispRayDistanceEnumerator : public IBSPTreeDataEnumerator
-{
-public:
- CBSPDispRayDistanceEnumerator() : m_Distance(1.0f), m_pSurface(0) {}
-
- // IBSPTreeDataEnumerator
- bool FASTCALL EnumerateElement( int userId, int context )
- {
- return s_DispMgr.DispRayDistance_EnumerateElement( userId, this );
- }
-
- float m_Distance;
- dface_t* m_pSurface;
- DispTested_t *m_pDispTested;
- Ray_t const *m_pRay;
- Vector2D m_LuxelCoord;
- Vector m_Normal;
-};
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-CVRadDispMgr::CVRadDispMgr()
-{
- m_pBSPTreeData = CreateBSPTreeData();
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-CVRadDispMgr::~CVRadDispMgr()
-{
- DestroyBSPTreeData( m_pBSPTreeData );
-}
-
-
-//-----------------------------------------------------------------------------
-// Insert a displacement into the tree for collision
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::InsertDispIntoTree( int ndxDisp )
-{
- DispCollTree_t &dispTree = m_DispTrees[ndxDisp];
- CDispCollTree *pDispTree = dispTree.m_pDispTree;
-
- // get the bounding box of the tree
- Vector boxMin, boxMax;
- pDispTree->GetBounds( boxMin, boxMax );
-
- // add the displacement to the tree so we will collide against it
- dispTree.m_Handle = m_pBSPTreeData->Insert( ndxDisp, boxMin, boxMax );
-}
-
-
-//-----------------------------------------------------------------------------
-// Remove a displacement from the tree for collision
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::RemoveDispFromTree( int ndxDisp )
-{
- // release the tree handle
- if( m_DispTrees[ndxDisp].m_Handle != TREEDATA_INVALID_HANDLE )
- {
- m_pBSPTreeData->Remove( m_DispTrees[ndxDisp].m_Handle );
- m_DispTrees[ndxDisp].m_Handle = TREEDATA_INVALID_HANDLE;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::Init( void )
-{
- // initialize the bsp tree
- m_pBSPTreeData->Init( ToolBSPTree() );
-
- // read in displacements that have been compiled into the bsp file
- UnserializeDisps();
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::Shutdown( void )
-{
- // remove all displacements from the tree
- for( int ndxDisp = m_DispTrees.Size(); ndxDisp >= 0; ndxDisp-- )
- {
- RemoveDispFromTree( ndxDisp );
- }
-
- // shutdown the bsp tree
- m_pBSPTreeData->Shutdown();
-
- // purge the displacement collision tree list
- m_DispTrees.Purge();
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::DispBuilderInit( CCoreDispInfo *pBuilderDisp, dface_t *pFace, int ndxFace )
-{
- // get the .bsp displacement
- ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
- if( !pDisp )
- return;
-
- //
- // initlialize the displacement base surface
- //
- CCoreDispSurface *pSurf = pBuilderDisp->GetSurface();
- pSurf->SetPointCount( 4 );
- pSurf->SetHandle( ndxFace );
- pSurf->SetContents( pDisp->contents );
-
- Vector pt[4];
- int ndxPt;
- for( ndxPt = 0; ndxPt < 4; ndxPt++ )
- {
- int eIndex = dsurfedges[pFace->firstedge+ndxPt];
- if( eIndex < 0 )
- {
- pSurf->SetPoint( ndxPt, dvertexes[dedges[-eIndex].v[1]].point );
- }
- else
- {
- pSurf->SetPoint( ndxPt, dvertexes[dedges[eIndex].v[0]].point );
- }
-
- VectorCopy( pSurf->GetPoint(ndxPt), pt[ndxPt] );
- }
-
- //
- // calculate the displacement surface normal
- //
- Vector vFaceNormal;
- pSurf->GetNormal( vFaceNormal );
- for( ndxPt = 0; ndxPt < 4; ndxPt++ )
- {
- pSurf->SetPointNormal( ndxPt, vFaceNormal );
- }
-
- // set the surface initial point info
- pSurf->SetPointStart( pDisp->startPosition );
- pSurf->FindSurfPointStartIndex();
- pSurf->AdjustSurfPointData();
-
- Vector vecTmp( texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][0],
- texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][1],
- texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][2] );
- int nLuxelsPerWorldUnit = static_cast<int>( 1.0f / VectorLength( vecTmp ) );
- Vector vecU( texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][0],
- texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][1],
- texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][2] );
- Vector vecV( texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[1][0],
- texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[1][1],
- texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[1][2] );
- pSurf->CalcLuxelCoords( nLuxelsPerWorldUnit, false, vecU, vecV );
-
- pBuilderDisp->SetNeighborData( pDisp->m_EdgeNeighbors, pDisp->m_CornerNeighbors );
-
- CDispVert *pVerts = &g_DispVerts[ pDisp->m_iDispVertStart ];
- CDispTri *pTris = &g_DispTris[pDisp->m_iDispTriStart];
-
- //
- // initialize the displacement data
- //
- pBuilderDisp->InitDispInfo(
- pDisp->power,
- pDisp->minTess,
- pDisp->smoothingAngle,
- pVerts,
- pTris );
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::UnserializeDisps( void )
-{
- // temporarily create the "builder" displacements
- CUtlVector<CCoreDispInfo*> builderDisps;
- for ( int iDisp = 0; iDisp < g_dispinfo.Count(); ++iDisp )
- {
- CCoreDispInfo *pDisp = new CCoreDispInfo;
- if ( !pDisp )
- {
- builderDisps.Purge();
- return;
- }
-
- int nIndex = builderDisps.AddToTail();
- pDisp->SetListIndex( nIndex );
- builderDisps[nIndex] = pDisp;
- }
-
- // Set them up as CDispUtilsHelpers.
- for ( int iDisp = 0; iDisp < g_dispinfo.Count(); ++iDisp )
- {
- builderDisps[iDisp]->SetDispUtilsHelperInfo( builderDisps.Base(), g_dispinfo.Count() );
- }
-
- //
- // find all faces with displacement data and initialize
- //
- for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
- {
- dface_t *pFace = &g_pFaces[ndxFace];
- if( ValidDispFace( pFace ) )
- {
- DispBuilderInit( builderDisps[pFace->dispinfo], pFace, ndxFace );
- }
- }
-
- // generate the displacement surfaces
- for( int iDisp = 0; iDisp < g_dispinfo.Count(); ++iDisp )
- {
- builderDisps[iDisp]->Create();
- }
-
- // smooth edge normals
- SmoothNeighboringDispSurfNormals( builderDisps.Base(), g_dispinfo.Count() );
-
- //
- // create the displacement collision tree and add it to the bsp tree
- //
- CVRADDispColl *pDispTrees = new CVRADDispColl[g_dispinfo.Count()];
- if( !pDispTrees )
- return;
-
- m_DispTrees.AddMultipleToTail( g_dispinfo.Count() );
-
- for( int iDisp = 0; iDisp < g_dispinfo.Count(); iDisp++ )
- {
- pDispTrees[iDisp].Create( builderDisps[iDisp] );
-
- m_DispTrees[iDisp].m_pDispTree = &pDispTrees[iDisp];
- m_DispTrees[iDisp].m_Handle = TREEDATA_INVALID_HANDLE;
-
- InsertDispIntoTree( iDisp );
- }
-
- // free "builder" disps
- builderDisps.Purge();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: create a set of patches for each displacement surface to transfer
-// bounced light around with
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::MakePatches( void )
-{
- // Collect stats - keep track of the total displacement surface area.
- float flTotalArea = 0.0f;
-
- // Create patches for all of the displacements.
- int nTreeCount = m_DispTrees.Size();
- for( int iTree = 0; iTree < nTreeCount; ++iTree )
- {
- // Get the current displacement collision tree.
- CVRADDispColl *pDispTree = m_DispTrees[iTree].m_pDispTree;
- if( !pDispTree )
- continue;
-
- flTotalArea += pDispTree->CreateParentPatches();
- }
-
- // Print stats.
- qprintf( "%i Displacements\n", nTreeCount );
- qprintf( "%i Square Feet [%.2f Square Inches]\n", ( int )( flTotalArea / 144.0f ), flTotalArea );
-}
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::SubdividePatch( int iPatch )
-{
- // Get the current patch to subdivide.
- CPatch *pPatch = &g_Patches[iPatch];
- if ( !pPatch )
- return;
-
- // Create children patches.
- DispCollTree_t &dispTree = m_DispTrees[g_pFaces[pPatch->faceNumber].dispinfo];
- CVRADDispColl *pTree = dispTree.m_pDispTree;
- if( pTree )
- {
- pTree->CreateChildPatches( iPatch, 0 );
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::StartRayTest( DispTested_t &dispTested )
-{
- if( m_DispTrees.Size() > 0 )
- {
- if( dispTested.m_pTested == 0 )
- {
- dispTested.m_pTested = new int[m_DispTrees.Size()];
- memset( dispTested.m_pTested, 0, m_DispTrees.Size() * sizeof( int ) );
- dispTested.m_Enum = 0;
- }
- ++dispTested.m_Enum;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CVRadDispMgr::ClipRayToDisp( DispTested_t &dispTested, Ray_t const &ray )
-{
- StartRayTest( dispTested );
-
- EnumContext_t ctx;
- ctx.m_pRay = &ray;
- ctx.m_pDispTested = &dispTested;
-
- // If it got through without a hit, it returns true
- return !m_pBSPTreeData->EnumerateLeavesAlongRay( ray, &m_EnumDispRay, ( int )&ctx );
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CVRadDispMgr::ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
- int ndxLeaf )
-{
- EnumContext_t ctx;
- ctx.m_pRay = &ray;
- ctx.m_pDispTested = &dispTested;
-
- return !m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &m_EnumDispRay, ( int )&ctx );
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
- int ndxLeaf, float& dist, dface_t*& pFace, Vector2D& luxelCoord )
-{
- CBSPDispRayDistanceEnumerator rayTestEnum;
- rayTestEnum.m_pRay = &ray;
- rayTestEnum.m_pDispTested = &dispTested;
-
- m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &rayTestEnum, 0 );
-
- dist = rayTestEnum.m_Distance;
- pFace = rayTestEnum.m_pSurface;
- if (pFace)
- {
- Vector2DCopy( rayTestEnum.m_LuxelCoord, luxelCoord );
- }
-}
-
-void CVRadDispMgr::ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
- int ndxLeaf, float& dist, Vector *pNormal )
-{
- CBSPDispRayDistanceEnumerator rayTestEnum;
- rayTestEnum.m_pRay = &ray;
- rayTestEnum.m_pDispTested = &dispTested;
-
- m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &rayTestEnum, 0 );
- dist = rayTestEnum.m_Distance;
- if ( rayTestEnum.m_pSurface )
- {
- *pNormal = rayTestEnum.m_Normal;
- }
-}
-
-void CVRadDispMgr::AddPolysForRayTrace( void )
-{
- int nTreeCount = m_DispTrees.Size();
- for( int iTree = 0; iTree < nTreeCount; ++iTree )
- {
- // Get the current displacement collision tree.
- CVRADDispColl *pDispTree = m_DispTrees[iTree].m_pDispTree;
-
- // Add the triangles of the tree to the RT environment
- pDispTree->AddPolysForRayTrace();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::GetDispSurfNormal( int ndxFace, Vector &pt, Vector &ptNormal,
- bool bInside )
-{
- // get the displacement surface data
- DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
- CVRADDispColl *pDispTree = dispTree.m_pDispTree;
-
- // find the parameterized displacement indices
- Vector2D uv;
- pDispTree->BaseFacePlaneToDispUV( pt, uv );
-
- if( bInside )
- {
- if( uv[0] < 0.0f || uv[0] > 1.0f ) { Msg( "Disp UV (%f) outside bounds!\n", uv[0] ); }
- if( uv[1] < 0.0f || uv[1] > 1.0f ) { Msg( "Disp UV (%f) outside bounds!\n", uv[1] ); }
- }
-
- if( uv[0] < 0.0f ) { uv[0] = 0.0f; }
- if( uv[0] > 1.0f ) { uv[0] = 1.0f; }
- if( uv[1] < 0.0f ) { uv[1] = 0.0f; }
- if( uv[1] > 1.0f ) { uv[1] = 1.0f; }
-
- // get the normal at "pt"
- pDispTree->DispUVToSurfNormal( uv, ptNormal );
-
- // get the new "pt"
- pDispTree->DispUVToSurfPoint( uv, pt, 1.0f );
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::GetDispSurf( int ndxFace, CVRADDispColl **ppDispTree )
-{
- DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
- *ppDispTree = dispTree.m_pDispTree;
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CVRadDispMgr::DispRay_EnumerateLeaf( int ndxLeaf, int context )
-{
- return m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &m_EnumDispRay, context );
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CVRadDispMgr::DispRay_EnumerateElement( int userId, int context )
-{
- DispCollTree_t &dispTree = m_DispTrees[userId];
- EnumContext_t *pCtx = ( EnumContext_t* )context;
-
- // don't test twice (check tested value)
- if( pCtx->m_pDispTested->m_pTested[userId] == pCtx->m_pDispTested->m_Enum )
- return true;
-
- // set the tested value
- pCtx->m_pDispTested->m_pTested[userId] = pCtx->m_pDispTested->m_Enum;
-
- // false mean stop iterating -- return false if we hit! (NOTE: opposite return
- // result of the collision tree's ray test, thus the !)
- CBaseTrace trace;
- trace.fraction = 1.0f;
- return ( !dispTree.m_pDispTree->AABBTree_Ray( *pCtx->m_pRay, pCtx->m_pRay->InvDelta(), &trace, true ) );
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-
-bool CVRadDispMgr::DispRayDistance_EnumerateElement( int userId, CBSPDispRayDistanceEnumerator* pCtx )
-{
- DispCollTree_t &dispTree = m_DispTrees[userId];
-
- // don't test twice (check tested value)
- if( pCtx->m_pDispTested->m_pTested[userId] == pCtx->m_pDispTested->m_Enum )
- return true;
-
- // set the tested value
- pCtx->m_pDispTested->m_pTested[userId] = pCtx->m_pDispTested->m_Enum;
-
- // Test the ray, if it's closer than previous tests, use it!
- RayDispOutput_t output;
- output.ndxVerts[0] = -1;
- output.ndxVerts[1] = -1;
- output.ndxVerts[2] = -1;
- output.ndxVerts[3] = -1;
- output.u = -1.0f;
- output.v = -1.0f;
- output.dist = FLT_MAX;
-
- if (dispTree.m_pDispTree->AABBTree_Ray( *pCtx->m_pRay, output ))
- {
- if (output.dist < pCtx->m_Distance)
- {
- pCtx->m_Distance = output.dist;
- pCtx->m_pSurface = &g_pFaces[dispTree.m_pDispTree->GetParentIndex()];
-
- // Get the luxel coordinate
- ComputePointFromBarycentric(
- dispTree.m_pDispTree->GetLuxelCoord(output.ndxVerts[0]),
- dispTree.m_pDispTree->GetLuxelCoord(output.ndxVerts[1]),
- dispTree.m_pDispTree->GetLuxelCoord(output.ndxVerts[2]),
- output.u, output.v, pCtx->m_LuxelCoord );
-
- Vector v0,v1,v2;
- dispTree.m_pDispTree->GetVert( output.ndxVerts[0], v0 );
- dispTree.m_pDispTree->GetVert( output.ndxVerts[1], v1 );
- dispTree.m_pDispTree->GetVert( output.ndxVerts[2], v2 );
- Vector e0 = v1-v0;
- Vector e1 = v2-v0;
- pCtx->m_Normal = CrossProduct( e0, e1 );
- VectorNormalize(pCtx->m_Normal);
- }
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Test a ray against a particular dispinfo
-//-----------------------------------------------------------------------------
-
-/*
-float CVRadDispMgr::ClipRayToDisp( Ray_t const &ray, int dispinfo )
-{
- assert( m_DispTrees.IsValidIndex(dispinfo) );
-
- RayDispOutput_t output;
- if (!m_DispTrees[dispinfo].m_pDispTree->AABBTree_Ray( ray, output ))
- return 1.0f;
- return output.dist;
-}
-*/
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CVRadDispMgr::DispFaceList_EnumerateLeaf( int ndxLeaf, int context )
-{
- //
- // add the faces found in this leaf to the face list
- //
- dleaf_t *pLeaf = &dleafs[ndxLeaf];
- for( int ndxFace = 0; ndxFace < pLeaf->numleaffaces; ndxFace++ )
- {
- // get the current face index
- int ndxLeafFace = pLeaf->firstleafface + ndxFace;
-
- // check to see if the face already lives in the list
- int ndx;
- int size = m_EnumDispFaceList.m_FaceList.Size();
- for( ndx = 0; ndx < size; ndx++ )
- {
- if( m_EnumDispFaceList.m_FaceList[ndx] == ndxLeafFace )
- break;
- }
-
- if( ndx == size )
- {
- int ndxList = m_EnumDispFaceList.m_FaceList.AddToTail();
- m_EnumDispFaceList.m_FaceList[ndxList] = ndxLeafFace;
- }
- }
-
- return m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &m_EnumDispFaceList, context );
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CVRadDispMgr::DispFaceList_EnumerateElement( int userId, int context )
-{
- DispCollTree_t &dispTree = m_DispTrees[userId];
- CVRADDispColl *pDispTree = dispTree.m_pDispTree;
- if( !pDispTree )
- return false;
-
- // check to see if the displacement already lives in the list
- int ndx;
- int size = m_EnumDispFaceList.m_DispList.Size();
- for( ndx = 0; ndx < size; ndx++ )
- {
- if( m_EnumDispFaceList.m_DispList[ndx] == pDispTree )
- break;
- }
-
- if( ndx == size )
- {
- int ndxList = m_EnumDispFaceList.m_DispList.AddToTail();
- m_EnumDispFaceList.m_DispList[ndxList] = pDispTree;
- }
-
- return true;
-}
-
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-inline void GetSampleLight( facelight_t *pFaceLight, int ndxStyle, bool bBumped,
- int ndxSample, LightingValue_t *pSampleLight )
-{
-// SampleLight[0].Init( 20.0f, 10.0f, 10.0f );
-// return;
-
- // get sample from bumped lighting data
- if( bBumped )
- {
- for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
- {
- pSampleLight[ndxBump] = pFaceLight->light[ndxStyle][ndxBump][ndxSample];
- }
- }
- // just a generally lit surface
- else
- {
- pSampleLight[0] = pFaceLight->light[ndxStyle][0][ndxSample];
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void AddSampleLightToRadial( Vector const &samplePos, Vector const &sampleNormal,
- LightingValue_t *pSampleLight, float sampleRadius2,
- Vector const &luxelPos, Vector const &luxelNormal,
- radial_t *pRadial, int ndxRadial, bool bBumped,
- bool bNeighborBumped )
-{
- // check normals to see if sample contributes any light at all
- float angle = sampleNormal.Dot( luxelNormal );
- if ( angle < 0.15f )
- return;
-
- // calculate the light vector
- Vector vSegment = samplePos - luxelPos;
-
- // get the distance to the light
- float dist = vSegment.Length();
- float dist2 = dist * dist;
-
- // Check to see if the light is within the influence.
- float influence = 1.0f - ( dist2 / ( sampleRadius2 ) );
- if( influence <= 0.0f )
- return;
-
- influence *= angle;
-
- if( bBumped )
- {
- if( bNeighborBumped )
- {
- for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
- {
- pRadial->light[ndxBump][ndxRadial].AddWeighted( pSampleLight[ndxBump], influence );
- }
- pRadial->weight[ndxRadial] += influence;
- }
- else
- {
- influence *= 0.05f;
- for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
- {
- pRadial->light[ndxBump][ndxRadial].AddWeighted( pSampleLight[0], influence );
- }
- pRadial->weight[ndxRadial] += influence;
- }
- }
- else
- {
- pRadial->light[0][ndxRadial].AddWeighted( pSampleLight[0], influence );
- pRadial->weight[ndxRadial] += influence;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CVRadDispMgr::IsNeighbor( int iFace, int iNeighborFace )
-{
- if ( iFace == iNeighborFace )
- return true;
-
- faceneighbor_t *pFaceNeighbor = &faceneighbor[iFace];
- for ( int iNeighbor = 0; iNeighbor < pFaceNeighbor->numneighbors; iNeighbor++ )
- {
- if ( pFaceNeighbor->neighbor[iNeighbor] == iNeighborFace )
- return true;
- }
-
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::RadialLuxelAddSamples( int ndxFace, Vector const &luxelPt, Vector const &luxelNormal, float radius,
- radial_t *pRadial, int ndxRadial, bool bBump, int lightStyle )
-{
- // calculate one over the voxel size
- float ooVoxelSize = 1.0f / SAMPLEHASH_VOXEL_SIZE;
-
- //
- // find voxel info
- //
- int voxelMin[3], voxelMax[3];
- for( int axis = 0; axis < 3; axis++ )
- {
- voxelMin[axis] = ( int )( ( luxelPt[axis] - radius ) * ooVoxelSize );
- voxelMax[axis] = ( int )( ( luxelPt[axis] + radius ) * ooVoxelSize ) + 1;
- }
-
- SampleData_t sampleData;
- for( int ndxZ = voxelMin[2]; ndxZ < voxelMax[2] + 1; ndxZ++ )
- {
- for( int ndxY = voxelMin[1]; ndxY < voxelMax[1] + 1; ndxY++ )
- {
- for( int ndxX = voxelMin[0]; ndxX < voxelMax[0] + 1; ndxX++ )
- {
- sampleData.x = ndxX * 100;
- sampleData.y = ndxY * 10;
- sampleData.z = ndxZ;
-
- UtlHashHandle_t handle = g_SampleHashTable.Find( sampleData );
- if( handle != g_SampleHashTable.InvalidHandle() )
- {
- SampleData_t *pSampleData = &g_SampleHashTable.Element( handle );
- int count = pSampleData->m_Samples.Count();
- for( int ndx = 0; ndx < count; ndx++ )
- {
- SampleHandle_t sampleHandle = pSampleData->m_Samples.Element( ndx );
- int ndxSample = ( sampleHandle & 0x0000ffff );
- int ndxFaceLight = ( ( sampleHandle >> 16 ) & 0x0000ffff );
-
- facelight_t *pFaceLight = &facelight[ndxFaceLight];
- if( pFaceLight && IsNeighbor( ndxFace, ndxFaceLight ) )
- {
- //
- // check for similar lightstyles
- //
- dface_t *pFace = &g_pFaces[ndxFaceLight];
- if( pFace )
- {
- int ndxNeighborStyle = -1;
- for( int ndxLightStyle = 0; ndxLightStyle < MAXLIGHTMAPS; ndxLightStyle++ )
- {
- if( pFace->styles[ndxLightStyle] == lightStyle )
- {
- ndxNeighborStyle = ndxLightStyle;
- break;
- }
- }
- if( ndxNeighborStyle == -1 )
- continue;
-
- // is this surface bumped???
- bool bNeighborBump = texinfo[pFace->texinfo].flags & SURF_BUMPLIGHT ? true : false;
-
- LightingValue_t sampleLight[NUM_BUMP_VECTS+1];
- GetSampleLight( pFaceLight, ndxNeighborStyle, bNeighborBump, ndxSample, sampleLight );
- AddSampleLightToRadial( pFaceLight->sample[ndxSample].pos, pFaceLight->sample[ndxSample].normal,
- sampleLight, radius*radius, luxelPt, luxelNormal, pRadial, ndxRadial,
- bBump, bNeighborBump );
- }
- }
- }
- }
- }
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::RadialLuxelBuild( CVRADDispColl *pDispTree, radial_t *pRadial,
- int ndxStyle, bool bBump )
-{
- //
- // get data lighting data
- //
- int ndxFace = pDispTree->GetParentIndex();
-
- dface_t *pFace = &g_pFaces[ndxFace];
- facelight_t *pFaceLight = &facelight[ndxFace];
-
- // get the influence radius
- float radius2 = pDispTree->GetSampleRadius2();
- float radius = ( float )sqrt( radius2 );
-
- int radialSize = pRadial->w * pRadial->h;
- for( int ndxRadial = 0; ndxRadial < radialSize; ndxRadial++ )
- {
- RadialLuxelAddSamples( ndxFace, pFaceLight->luxel[ndxRadial], pFaceLight->luxelNormals[ndxRadial],
- radius, pRadial, ndxRadial, bBump, pFace->styles[ndxStyle] );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-radial_t *CVRadDispMgr::BuildLuxelRadial( int ndxFace, int ndxStyle, bool bBump )
-{
- // allocate the radial
- radial_t *pRadial = AllocateRadial( ndxFace );
- if( !pRadial )
- return NULL;
-
- //
- // step 1: get the displacement surface to be lit
- //
- DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
- CVRADDispColl *pDispTree = dispTree.m_pDispTree;
- if( !pDispTree )
- return NULL;
-
- // step 2: build radial luxels
- RadialLuxelBuild( pDispTree, pRadial, ndxStyle, bBump );
-
- // step 3: return the built radial
- return pRadial;
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CVRadDispMgr::SampleRadial( int ndxFace, radial_t *pRadial, Vector const &vPos, int ndxLxl,
- LightingValue_t *pLightSample, int sampleCount, bool bPatch )
-{
- bool bGoodSample = true;
- for ( int count = 0; count < sampleCount; count++ )
- {
- pLightSample[count].Zero();
-
- if ( pRadial->weight[ndxLxl] > 0.0f )
- {
- pLightSample[count].AddWeighted( pRadial->light[count][ndxLxl], ( 1.0f / pRadial->weight[ndxLxl] ) );
- }
- else
- {
- // error, luxel has no samples (not for patches)
- if ( !bPatch )
- {
- // Yes, 2550 is correct!
- // pLightSample[count].Init( 2550.0f, 0.0f, 2550.0f );
- if( count == 0 )
- bGoodSample = false;
- }
- }
- }
-
- return bGoodSample;
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void GetPatchLight( CPatch *pPatch, bool bBump, Vector *pPatchLight )
-{
- VectorCopy( pPatch->totallight.light[0], pPatchLight[0] );
-
- if( bBump )
- {
- for( int ndxBump = 1; ndxBump < ( NUM_BUMP_VECTS + 1 ); ndxBump++ )
- {
- VectorCopy( pPatch->totallight.light[ndxBump], pPatchLight[ndxBump] );
- }
- }
-}
-
-extern void GetBumpNormals( const float* sVect, const float* tVect, const Vector& flatNormal,
- const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] );
-extern void PreGetBumpNormalsForDisp( texinfo_t *pTexinfo, Vector &vecU, Vector &vecV, Vector &vecNormal );
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void AddPatchLightToRadial( Vector const &patchOrigin, Vector const &patchNormal,
- Vector *pPatchLight, float patchRadius2,
- Vector const &luxelPos, Vector const &luxelNormal,
- radial_t *pRadial, int ndxRadial, bool bBump,
- bool bNeighborBump )
-{
- // calculate the light vector
- Vector vSegment = patchOrigin - luxelPos;
-
- // get the distance to the light
- float dist = vSegment.Length();
- float dist2 = dist * dist;
-
- // Check to see if the light is within the sample influence.
- float influence = 1.0f - ( dist2 / ( patchRadius2 ) );
- if ( influence <= 0.0f )
- return;
-
- if( bBump )
- {
- Vector normals[NUM_BUMP_VECTS+1];
- normals[0] = luxelNormal;
- texinfo_t *pTexinfo = &texinfo[g_pFaces[pRadial->facenum].texinfo];
- Vector vecTexU, vecTexV;
- PreGetBumpNormalsForDisp( pTexinfo, vecTexU, vecTexV, normals[0] );
- GetBumpNormals( vecTexU, vecTexV, normals[0], normals[0], &normals[1] );
-
- if( bNeighborBump )
- {
- float flScale = patchNormal.Dot( normals[0] );
- flScale = max( 0.0f, flScale );
- float flBumpInfluence = influence * flScale;
-
- for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
- {
- pRadial->light[ndxBump][ndxRadial].AddWeighted( pPatchLight[ndxBump], flBumpInfluence );
- }
-
- pRadial->weight[ndxRadial] += flBumpInfluence;
- }
- else
- {
- float flScale = patchNormal.Dot( normals[0] );
- flScale = max( 0.0f, flScale );
- float flBumpInfluence = influence * flScale * 0.05f;
-
- for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
- {
- pRadial->light[ndxBump][ndxRadial].AddWeighted( pPatchLight[0], flBumpInfluence );
- }
-
- pRadial->weight[ndxRadial] += flBumpInfluence;
- }
- }
- else
- {
- float flScale = patchNormal.Dot( luxelNormal );
- flScale = max( 0.0f, flScale );
- influence *= flScale;
- pRadial->light[0][ndxRadial].AddWeighted( pPatchLight[0], influence );
-
- // add the weight value
- pRadial->weight[ndxRadial] += influence;
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::RadialLuxelAddPatch( int ndxFace, Vector const &luxelPt,
- Vector const &luxelNormal, float radius,
- radial_t *pRadial, int ndxRadial, bool bBump,
- CUtlVector<CPatch*> &interestingPatches )
-{
-#ifdef SAMPLEHASH_QUERY_ONCE
- for ( int i=0; i < interestingPatches.Count(); i++ )
- {
- CPatch *pPatch = interestingPatches[i];
- bool bNeighborBump = texinfo[g_pFaces[pPatch->faceNumber].texinfo].flags & SURF_BUMPLIGHT ? true : false;
-
- Vector patchLight[NUM_BUMP_VECTS+1];
- GetPatchLight( pPatch, bBump, patchLight );
- AddPatchLightToRadial( pPatch->origin, pPatch->normal, patchLight, radius*radius,
- luxelPt, luxelNormal, pRadial, ndxRadial, bBump, bNeighborBump );
- }
-#else
- // calculate one over the voxel size
- float ooVoxelSize = 1.0f / SAMPLEHASH_VOXEL_SIZE;
-
- //
- // find voxel info
- //
- int voxelMin[3], voxelMax[3];
- for ( int axis = 0; axis < 3; axis++ )
- {
- voxelMin[axis] = ( int )( ( luxelPt[axis] - radius ) * ooVoxelSize );
- voxelMax[axis] = ( int )( ( luxelPt[axis] + radius ) * ooVoxelSize ) + 1;
- }
-
- unsigned short curIterationKey = IncrementPatchIterationKey();
- PatchSampleData_t patchData;
- for ( int ndxZ = voxelMin[2]; ndxZ < voxelMax[2] + 1; ndxZ++ )
- {
- for ( int ndxY = voxelMin[1]; ndxY < voxelMax[1] + 1; ndxY++ )
- {
- for ( int ndxX = voxelMin[0]; ndxX < voxelMax[0] + 1; ndxX++ )
- {
- patchData.x = ndxX * 100;
- patchData.y = ndxY * 10;
- patchData.z = ndxZ;
-
- UtlHashHandle_t handle = g_PatchSampleHashTable.Find( patchData );
- if ( handle != g_PatchSampleHashTable.InvalidHandle() )
- {
- PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element( handle );
- int count = pPatchData->m_ndxPatches.Count();
- for ( int ndx = 0; ndx < count; ndx++ )
- {
- int ndxPatch = pPatchData->m_ndxPatches.Element( ndx );
- CPatch *pPatch = &g_Patches.Element( ndxPatch );
- if ( pPatch && pPatch->m_IterationKey != curIterationKey )
- {
- pPatch->m_IterationKey = curIterationKey;
-
- if ( IsNeighbor( ndxFace, pPatch->faceNumber ) )
- {
- bool bNeighborBump = texinfo[g_pFaces[pPatch->faceNumber].texinfo].flags & SURF_BUMPLIGHT ? true : false;
-
- Vector patchLight[NUM_BUMP_VECTS+1];
- GetPatchLight( pPatch, bBump, patchLight );
- AddPatchLightToRadial( pPatch->origin, pPatch->normal, patchLight, radius*radius,
- luxelPt, luxelNormal, pRadial, ndxRadial, bBump, bNeighborBump );
- }
- }
- }
- }
- }
- }
- }
-#endif
-}
-
-
-void CVRadDispMgr::GetInterestingPatchesForLuxels(
- int ndxFace,
- CUtlVector<CPatch*> &interestingPatches,
- float patchSampleRadius )
-{
- facelight_t *pFaceLight = &facelight[ndxFace];
-
- // Get the max bounds of all voxels that these luxels touch.
- Vector vLuxelMin( FLT_MAX, FLT_MAX, FLT_MAX );
- Vector vLuxelMax( -FLT_MAX, -FLT_MAX, -FLT_MAX );
- for ( int i=0; i < pFaceLight->numluxels; i++ )
- {
- VectorMin( pFaceLight->luxel[i], vLuxelMin, vLuxelMin );
- VectorMax( pFaceLight->luxel[i], vLuxelMax, vLuxelMax );
- }
-
- int allVoxelMin[3], allVoxelMax[3];
- for ( int axis = 0; axis < 3; axis++ )
- {
- allVoxelMin[axis] = ( int )( ( vLuxelMin[axis] - patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE );
- allVoxelMax[axis] = ( int )( ( vLuxelMax[axis] + patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE ) + 1;
- }
- int allVoxelSize[3] = { allVoxelMax[0] - allVoxelMin[0], allVoxelMax[1] - allVoxelMin[1], allVoxelMax[2] - allVoxelMin[2] };
-
-
- // Now figure out exactly which voxels these luxels touch.
- CUtlVector<unsigned char> voxelBits;
- voxelBits.SetSize( ((allVoxelSize[0] * allVoxelSize[1] * allVoxelSize[2]) + 7) / 8 );
- memset( voxelBits.Base(), 0, voxelBits.Count() );
-
- for ( int i=0; i < pFaceLight->numluxels; i++ )
- {
- int voxelMin[3], voxelMax[3];
- for ( int axis=0; axis < 3; axis++ )
- {
- voxelMin[axis] = ( int )( ( pFaceLight->luxel[i][axis] - patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE );
- voxelMax[axis] = ( int )( ( pFaceLight->luxel[i][axis] + patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE ) + 1;
- }
-
- for ( int x=voxelMin[0]; x < voxelMax[0]; x++ )
- {
- for ( int y=voxelMin[1]; y < voxelMax[1]; y++ )
- {
- for ( int z=voxelMin[2]; z < voxelMax[2]; z++ )
- {
- int iBit = (z - allVoxelMin[2])*(allVoxelSize[0]*allVoxelSize[1]) +
- (y-allVoxelMin[1])*allVoxelSize[0] +
- (x-allVoxelMin[0]);
- voxelBits[iBit>>3] |= (1 << (iBit & 7));
- }
- }
- }
- }
-
-
- // Now get the list of patches that touch those voxels.
- unsigned short curIterationKey = IncrementPatchIterationKey();
-
- for ( int x=0; x < allVoxelSize[0]; x++ )
- {
- for ( int y=0; y < allVoxelSize[1]; y++ )
- {
- for ( int z=0; z < allVoxelSize[2]; z++ )
- {
- // Make sure this voxel has any luxels that care about it.
- int iBit = z*(allVoxelSize[0]*allVoxelSize[1]) + y*allVoxelSize[0] + x;
- unsigned char val = voxelBits[iBit>>3] & (1 << (iBit & 7));
- if ( !val )
- continue;
-
- PatchSampleData_t patchData;
- patchData.x = (x + allVoxelMin[0]) * 100;
- patchData.y = (y + allVoxelMin[1]) * 10;
- patchData.z = (z + allVoxelMin[2]);
-
- UtlHashHandle_t handle = g_PatchSampleHashTable.Find( patchData );
- if ( handle != g_PatchSampleHashTable.InvalidHandle() )
- {
- PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element( handle );
-
- // For all patches that touch this hash table element..
- for ( int ndx = 0; ndx < pPatchData->m_ndxPatches.Count(); ndx++ )
- {
- int ndxPatch = pPatchData->m_ndxPatches.Element( ndx );
- CPatch *pPatch = &g_Patches.Element( ndxPatch );
-
- // If we haven't touched the patch already and it's a valid neighbor, then we want to use it.
- if ( pPatch && pPatch->m_IterationKey != curIterationKey )
- {
- pPatch->m_IterationKey = curIterationKey;
-
- if ( IsNeighbor( ndxFace, pPatch->faceNumber ) )
- {
- interestingPatches.AddToTail( pPatch );
- }
- }
- }
- }
- }
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::RadialPatchBuild( CVRADDispColl *pDispTree, radial_t *pRadial,
- bool bBump )
-{
- //
- // get data lighting data
- //
- int ndxFace = pDispTree->GetParentIndex();
- facelight_t *pFaceLight = &facelight[ndxFace];
-
- // get the influence radius
- float radius2 = pDispTree->GetPatchSampleRadius2();
- float radius = ( float )sqrt( radius2 );
-
- CUtlVector<CPatch*> interestingPatches;
-#ifdef SAMPLEHASH_QUERY_ONCE
- GetInterestingPatchesForLuxels( ndxFace, interestingPatches, radius );
-#endif
-
- int radialSize = pRadial->w * pRadial->h;
- for( int ndxRadial = 0; ndxRadial < radialSize; ndxRadial++ )
- {
- RadialLuxelAddPatch(
- ndxFace,
- pFaceLight->luxel[ndxRadial],
- pFaceLight->luxelNormals[ndxRadial],
- radius,
- pRadial,
- ndxRadial,
- bBump,
- interestingPatches );
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-radial_t *CVRadDispMgr::BuildPatchRadial( int ndxFace, bool bBump )
-{
- // allocate the radial
- radial_t *pRadial = AllocateRadial( ndxFace );
- if( !pRadial )
- return NULL;
-
- //
- // step 1: get the displacement surface to be lit
- //
- DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
- CVRADDispColl *pDispTree = dispTree.m_pDispTree;
- if( !pDispTree )
- return NULL;
-
- // step 2: build radial of patch light
- RadialPatchBuild( pDispTree, pRadial, bBump );
-
- // step 3: return the built radial
- return pRadial;
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool SampleInSolid( sample_t *pSample )
-{
- int ndxLeaf = PointLeafnum( pSample->pos );
- return ( dleafs[ndxLeaf].contents == CONTENTS_SOLID );
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::InsertSamplesDataIntoHashTable( void )
-{
- int totalSamples = 0;
-#if 0
- int totalSamplesInSolid = 0;
-#endif
-
- for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
- {
- dface_t *pFace = &g_pFaces[ndxFace];
- facelight_t *pFaceLight = &facelight[ndxFace];
- if( !pFace || !pFaceLight )
- continue;
-
- if( texinfo[pFace->texinfo].flags & TEX_SPECIAL )
- continue;
-
-#if 0
- bool bDisp = ( pFace->dispinfo != -1 );
-#endif
- //
- // for each sample
- //
- for( int ndxSample = 0; ndxSample < pFaceLight->numsamples; ndxSample++ )
- {
- sample_t *pSample = &pFaceLight->sample[ndxSample];
- if( pSample )
- {
-#if 0
- if( bDisp )
- {
- // test sample to see if the displacement samples resides in solid
- if( SampleInSolid( pSample ) )
- {
- totalSamplesInSolid++;
- continue;
- }
- }
-#endif
-
- // create the sample handle
- SampleHandle_t sampleHandle = ndxSample;
- sampleHandle |= ( ndxFace << 16 );
-
- SampleData_AddSample( pSample, sampleHandle );
- }
-
- }
-
- totalSamples += pFaceLight->numsamples;
- }
-
-#if 0
- // not implemented yet!!!
- Msg( "%d samples in solid\n", totalSamplesInSolid );
-#endif
-
- // log the distribution
- SampleData_Log();
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::InsertPatchSampleDataIntoHashTable( void )
-{
- // don't insert patch samples if we are not bouncing light
- if( numbounce <= 0 )
- return;
-
- int totalPatchSamples = 0;
-
- for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
- {
- dface_t *pFace = &g_pFaces[ndxFace];
- facelight_t *pFaceLight = &facelight[ndxFace];
- if( !pFace || !pFaceLight )
- continue;
-
- if( texinfo[pFace->texinfo].flags & TEX_SPECIAL )
- continue;
-
- //
- // for each patch
- //
- CPatch *pNextPatch = NULL;
- if( g_FacePatches.Element( ndxFace ) != g_FacePatches.InvalidIndex() )
- {
- for( CPatch *pPatch = &g_Patches.Element( g_FacePatches.Element( ndxFace ) ); pPatch; pPatch = pNextPatch )
- {
- // next patch
- pNextPatch = NULL;
- if( pPatch->ndxNext != g_Patches.InvalidIndex() )
- {
- pNextPatch = &g_Patches.Element( pPatch->ndxNext );
- }
-
- // skip patches with children
- if( pPatch->child1 != g_Patches.InvalidIndex() )
- continue;
-
- int ndxPatch = pPatch - g_Patches.Base();
- PatchSampleData_AddSample( pPatch, ndxPatch );
-
- totalPatchSamples++;
- }
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::StartTimer( const char *name )
-{
- Msg( name );
- m_Timer.Start();
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CVRadDispMgr::EndTimer( void )
-{
- m_Timer.End();
- CCycleCount duration = m_Timer.GetDuration();
- double seconds = duration.GetSeconds();
-
- Msg( "Done<%1.4lf sec>\n", seconds );
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CVRadDispMgr::BuildDispSamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
-{
- // get the tree assosciated with the face
- DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
- CVRADDispColl *pDispTree = dispTree.m_pDispTree;
- if( !pDispTree )
- return false;
-
- // lightmap size
- int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
- int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
-
- // calculate the steps in uv space
- float stepU = 1.0f / ( float )width;
- float stepV = 1.0f / ( float )height;
- float halfStepU = stepU * 0.5f;
- float halfStepV = stepV * 0.5f;
-
- //
- // build the winding points (used to generate world space winding and
- // calculate the area of the "sample")
- //
- int ndxU, ndxV;
-
- CUtlVector<sample_t> samples;
- samples.SetCount( SINGLEMAP );
- sample_t *pSamples = samples.Base();
-
- CUtlVector<Vector> worldPoints;
- worldPoints.SetCount( SINGLEMAP );
- Vector *pWorldPoints = worldPoints.Base();
-
- for( ndxV = 0; ndxV < ( height + 1 ); ndxV++ )
- {
- for( ndxU = 0; ndxU < ( width + 1 ); ndxU++ )
- {
- int ndx = ( ndxV * ( width + 1 ) ) + ndxU;
-
- Vector2D uv( ndxU * stepU, ndxV * stepV );
- pDispTree->DispUVToSurfPoint( uv, pWorldPoints[ndx], 0.0f );
- }
- }
-
- for( ndxV = 0; ndxV < height; ndxV++ )
- {
- for( ndxU = 0; ndxU < width; ndxU++ )
- {
- // build the winding
- winding_t *pWinding = AllocWinding( 4 );
- if( pWinding )
- {
- pWinding->numpoints = 4;
- pWinding->p[0] = pWorldPoints[(ndxV*(width+1))+ndxU];
- pWinding->p[1] = pWorldPoints[((ndxV+1)*(width+1))+ndxU];
- pWinding->p[2] = pWorldPoints[((ndxV+1)*(width+1))+(ndxU+1)];
- pWinding->p[3] = pWorldPoints[(ndxV*(width+1))+(ndxU+1)];
-
- // calculate the area
- float area = WindingArea( pWinding );
-
- int ndxSample = ( ndxV * width ) + ndxU;
- pSamples[ndxSample].w = pWinding;
- pSamples[ndxSample].area = area;
- }
- else
- {
- Msg( "BuildDispSamples: WARNING - failed winding allocation\n" );
- }
- }
- }
-
- //
- // build the samples points (based on s, t and sampleoffset (center of samples);
- // generates world space position and normal)
- //
- for( ndxV = 0; ndxV < height; ndxV++ )
- {
- for( ndxU = 0; ndxU < width; ndxU++ )
- {
- int ndxSample = ( ndxV * width ) + ndxU;
- pSamples[ndxSample].s = ndxU;
- pSamples[ndxSample].t = ndxV;
- pSamples[ndxSample].coord[0] = ( ndxU * stepU ) + halfStepU;
- pSamples[ndxSample].coord[1] = ( ndxV * stepV ) + halfStepV;
- pDispTree->DispUVToSurfPoint( pSamples[ndxSample].coord, pSamples[ndxSample].pos, 1.0f );
- pDispTree->DispUVToSurfNormal( pSamples[ndxSample].coord, pSamples[ndxSample].normal );
- }
- }
-
- //
- // copy over samples
- //
- pFaceLight->numsamples = width * height;
- pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
- if( !pFaceLight->sample )
- return false;
-
- memcpy( pFaceLight->sample, pSamples, pFaceLight->numsamples * sizeof( *pFaceLight->sample ) );
-
- // statistics - warning?!
- if( pFaceLight->numsamples == 0 )
- {
- Msg( "BuildDispSamples: WARNING - no samples %d\n", pLightInfo->face - g_pFaces );
- }
-
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CVRadDispMgr::BuildDispLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
-{
- // get the tree assosciated with the face
- DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
- CVRADDispColl *pDispTree = dispTree.m_pDispTree;
- if( !pDispTree )
- return false;
-
- // lightmap size
- int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
- int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
-
- // calcuate actual luxel points
- pFaceLight->numluxels = width * height;
- pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
- pFaceLight->luxelNormals = ( Vector* )calloc( pFaceLight->numluxels, sizeof( Vector ) );
- if( !pFaceLight->luxel || !pFaceLight->luxelNormals )
- return false;
-
- float stepU = 1.0f / ( float )( width - 1 );
- float stepV = 1.0f / ( float )( height - 1 );
-
- for( int ndxV = 0; ndxV < height; ndxV++ )
- {
- for( int ndxU = 0; ndxU < width; ndxU++ )
- {
- int ndxLuxel = ( ndxV * width ) + ndxU;
-
- Vector2D uv( ndxU * stepU, ndxV * stepV );
- pDispTree->DispUVToSurfPoint( uv, pFaceLight->luxel[ndxLuxel], 1.0f );
- pDispTree->DispUVToSurfNormal( uv, pFaceLight->luxelNormals[ndxLuxel] );
- }
- }
-
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CVRadDispMgr::BuildDispSamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
-{
- // get the tree assosciated with the face
- DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
- CVRADDispColl *pDispTree = dispTree.m_pDispTree;
- if( !pDispTree )
- return false;
-
- // lightmap size
- int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
- int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
-
- // calcuate actual luxel points
- pFaceLight->numsamples = width * height;
- pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
- if( !pFaceLight->sample )
- return false;
-
- pFaceLight->numluxels = width * height;
- pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
- pFaceLight->luxelNormals = ( Vector* )calloc( pFaceLight->numluxels, sizeof( Vector ) );
- if( !pFaceLight->luxel || !pFaceLight->luxelNormals )
- return false;
-
- float stepU = 1.0f / ( float )( width - 1 );
- float stepV = 1.0f / ( float )( height - 1 );
- float halfStepU = stepU * 0.5f;
- float halfStepV = stepV * 0.5f;
-
- for( int ndxV = 0; ndxV < height; ndxV++ )
- {
- for( int ndxU = 0; ndxU < width; ndxU++ )
- {
- int ndx = ( ndxV * width ) + ndxU;
-
- pFaceLight->sample[ndx].s = ndxU;
- pFaceLight->sample[ndx].t = ndxV;
- pFaceLight->sample[ndx].coord[0] = ( ndxU * stepU ) + halfStepU;
- pFaceLight->sample[ndx].coord[1] = ( ndxV * stepV ) + halfStepV;
-
- pDispTree->DispUVToSurfPoint( pFaceLight->sample[ndx].coord, pFaceLight->sample[ndx].pos, 1.0f );
- pDispTree->DispUVToSurfNormal( pFaceLight->sample[ndx].coord, pFaceLight->sample[ndx].normal );
-
- pFaceLight->luxel[ndx] = pFaceLight->sample[ndx].pos;
- pFaceLight->luxelNormals[ndx] = pFaceLight->sample[ndx].normal;
- }
- }
-
- return true;
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vrad.h"
+#include "utlvector.h"
+#include "cmodel.h"
+#include "BSPTreeData.h"
+#include "VRAD_DispColl.h"
+#include "CollisionUtils.h"
+#include "lightmap.h"
+#include "Radial.h"
+#include "CollisionUtils.h"
+#include "mathlib/bumpvects.h"
+#include "utlrbtree.h"
+#include "tier0/fasttimer.h"
+#include "disp_vrad.h"
+
+class CBSPDispRayDistanceEnumerator;
+
+//=============================================================================
+//
+// Displacement/Face List
+//
+class CBSPDispFaceListEnumerator : public ISpatialLeafEnumerator, public IBSPTreeDataEnumerator
+{
+public:
+
+ //=========================================================================
+ //
+ // Construction/Deconstruction
+ //
+ CBSPDispFaceListEnumerator() {};
+ virtual ~CBSPDispFaceListEnumerator()
+ {
+ m_DispList.Purge();
+ m_FaceList.Purge();
+ }
+
+ // ISpatialLeafEnumerator
+ bool EnumerateLeaf( int ndxLeaf, int context );
+
+ // IBSPTreeDataEnumerator
+ bool FASTCALL EnumerateElement( int userId, int context );
+
+public:
+
+ CUtlVector<CVRADDispColl*> m_DispList;
+ CUtlVector<int> m_FaceList;
+};
+
+
+//=============================================================================
+//
+// RayEnumerator
+//
+class CBSPDispRayEnumerator : public ISpatialLeafEnumerator, public IBSPTreeDataEnumerator
+{
+public:
+ // ISpatialLeafEnumerator
+ bool EnumerateLeaf( int ndxLeaf, int context );
+
+ // IBSPTreeDataEnumerator
+ bool FASTCALL EnumerateElement( int userId, int context );
+};
+
+//=============================================================================
+//
+// VRad Displacement Manager
+//
+class CVRadDispMgr : public IVRadDispMgr
+{
+public:
+
+ //=========================================================================
+ //
+ // Construction/Deconstruction
+ //
+ CVRadDispMgr();
+ virtual ~CVRadDispMgr();
+
+ // creation/destruction
+ void Init( void );
+ void Shutdown( void );
+
+ // "CalcPoints"
+ bool BuildDispSamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace );
+ bool BuildDispLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace );
+ bool BuildDispSamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace );
+
+ // patching functions
+ void MakePatches( void );
+ void SubdividePatch( int iPatch );
+
+ // pre "FinalLightFace"
+ void InsertSamplesDataIntoHashTable( void );
+ void InsertPatchSampleDataIntoHashTable( void );
+
+ // "FinalLightFace"
+ radial_t *BuildLuxelRadial( int ndxFace, int ndxStyle, bool bBump );
+ bool SampleRadial( int ndxFace, radial_t *pRadial, Vector const &vPos, int ndxLxl, LightingValue_t *pLightSample, int sampleCount, bool bPatch );
+ radial_t *BuildPatchRadial( int ndxFace, bool bBump );
+
+ // utility
+ void GetDispSurfNormal( int ndxFace, Vector &pt, Vector &ptNormal, bool bInside );
+ void GetDispSurf( int ndxFace, CVRADDispColl **ppDispTree );
+
+ // bsp tree functions
+ bool ClipRayToDisp( DispTested_t &dispTested, Ray_t const &ray );
+ bool ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf );
+ void ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf,
+ float& dist, dface_t*& pFace, Vector2D& luxelCoord );
+ void ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
+ int ndxLeaf, float& dist, Vector *pNormal );
+
+ void StartRayTest( DispTested_t &dispTested );
+ void AddPolysForRayTrace( void );
+
+ // general timing -- should be moved!!
+ void StartTimer( const char *name );
+ void EndTimer( void );
+
+ //=========================================================================
+ //
+ // Enumeration Methods
+ //
+ bool DispRay_EnumerateLeaf( int ndxLeaf, int context );
+ bool DispRay_EnumerateElement( int userId, int context );
+ bool DispRayDistance_EnumerateElement( int userId, CBSPDispRayDistanceEnumerator* pEnum );
+
+ bool DispFaceList_EnumerateLeaf( int ndxLeaf, int context );
+ bool DispFaceList_EnumerateElement( int userId, int context );
+
+private:
+
+ //=========================================================================
+ //
+ // BSP Tree Helpers
+ //
+ void InsertDispIntoTree( int ndxDisp );
+ void RemoveDispFromTree( int ndxDisp );
+
+ //=========================================================================
+ //
+ // Displacement Data Loader (from .bsp)
+ //
+ void UnserializeDisps( void );
+ void DispBuilderInit( CCoreDispInfo *pBuilderDisp, dface_t *pFace, int ndxFace );
+
+ //=========================================================================
+ //
+ // Sampling Helpers
+ //
+ void RadialLuxelBuild( CVRADDispColl *pDispTree, radial_t *pRadial, int ndxStyle, bool bBump );
+ void RadialLuxelAddSamples( int ndxFace, Vector const &luxelPt, Vector const &luxelNormal, float radius,
+ radial_t *pRadial, int ndxRadial, bool bBump, int lightStyle );
+
+ void RadialPatchBuild( CVRADDispColl *pDispTree, radial_t *pRadial, bool bBump );
+ void RadialLuxelAddPatch( int ndxFace, Vector const &luxelPt,
+ Vector const &luxelNormal, float radius,
+ radial_t *pRadial, int ndxRadial, bool bBump,
+ CUtlVector<CPatch*> &interestingPatches );
+
+ bool IsNeighbor( int iDispFace, int iNeighborFace );
+
+ void GetInterestingPatchesForLuxels(
+ int ndxFace,
+ CUtlVector<CPatch*> &interestingPatches,
+ float patchSampleRadius );
+
+private:
+
+ struct DispCollTree_t
+ {
+ CVRADDispColl *m_pDispTree;
+ BSPTreeDataHandle_t m_Handle;
+ };
+
+ struct EnumContext_t
+ {
+ DispTested_t *m_pDispTested;
+ Ray_t const *m_pRay;
+ };
+
+ CUtlVector<DispCollTree_t> m_DispTrees;
+
+ IBSPTreeData *m_pBSPTreeData;
+
+ CBSPDispRayEnumerator m_EnumDispRay;
+ CBSPDispFaceListEnumerator m_EnumDispFaceList;
+
+ int sampleCount;
+ Vector *m_pSamplePos;
+
+ CFastTimer m_Timer;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: expose IVRadDispMgr to vrad
+//-----------------------------------------------------------------------------
+
+static CVRadDispMgr s_DispMgr;
+
+IVRadDispMgr *StaticDispMgr( void )
+{
+ return &s_DispMgr;
+}
+
+
+//=============================================================================
+//
+// Displacement/Face List
+//
+// ISpatialLeafEnumerator
+bool CBSPDispFaceListEnumerator::EnumerateLeaf( int ndxLeaf, int context )
+{
+ return s_DispMgr.DispFaceList_EnumerateLeaf( ndxLeaf, context );
+}
+
+// IBSPTreeDataEnumerator
+bool FASTCALL CBSPDispFaceListEnumerator::EnumerateElement( int userId, int context )
+{
+ return s_DispMgr.DispFaceList_EnumerateElement( userId, context );
+}
+
+
+//=============================================================================
+//
+// RayEnumerator
+//
+bool CBSPDispRayEnumerator::EnumerateLeaf( int ndxLeaf, int context )
+{
+ return s_DispMgr.DispRay_EnumerateLeaf( ndxLeaf, context );
+}
+
+bool FASTCALL CBSPDispRayEnumerator::EnumerateElement( int userId, int context )
+{
+ return s_DispMgr.DispRay_EnumerateElement( userId, context );
+}
+
+
+//-----------------------------------------------------------------------------
+// Here's an enumerator that we use for testing against disps in a leaf...
+//-----------------------------------------------------------------------------
+
+class CBSPDispRayDistanceEnumerator : public IBSPTreeDataEnumerator
+{
+public:
+ CBSPDispRayDistanceEnumerator() : m_Distance(1.0f), m_pSurface(0) {}
+
+ // IBSPTreeDataEnumerator
+ bool FASTCALL EnumerateElement( int userId, int context )
+ {
+ return s_DispMgr.DispRayDistance_EnumerateElement( userId, this );
+ }
+
+ float m_Distance;
+ dface_t* m_pSurface;
+ DispTested_t *m_pDispTested;
+ Ray_t const *m_pRay;
+ Vector2D m_LuxelCoord;
+ Vector m_Normal;
+};
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CVRadDispMgr::CVRadDispMgr()
+{
+ m_pBSPTreeData = CreateBSPTreeData();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CVRadDispMgr::~CVRadDispMgr()
+{
+ DestroyBSPTreeData( m_pBSPTreeData );
+}
+
+
+//-----------------------------------------------------------------------------
+// Insert a displacement into the tree for collision
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::InsertDispIntoTree( int ndxDisp )
+{
+ DispCollTree_t &dispTree = m_DispTrees[ndxDisp];
+ CDispCollTree *pDispTree = dispTree.m_pDispTree;
+
+ // get the bounding box of the tree
+ Vector boxMin, boxMax;
+ pDispTree->GetBounds( boxMin, boxMax );
+
+ // add the displacement to the tree so we will collide against it
+ dispTree.m_Handle = m_pBSPTreeData->Insert( ndxDisp, boxMin, boxMax );
+}
+
+
+//-----------------------------------------------------------------------------
+// Remove a displacement from the tree for collision
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::RemoveDispFromTree( int ndxDisp )
+{
+ // release the tree handle
+ if( m_DispTrees[ndxDisp].m_Handle != TREEDATA_INVALID_HANDLE )
+ {
+ m_pBSPTreeData->Remove( m_DispTrees[ndxDisp].m_Handle );
+ m_DispTrees[ndxDisp].m_Handle = TREEDATA_INVALID_HANDLE;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::Init( void )
+{
+ // initialize the bsp tree
+ m_pBSPTreeData->Init( ToolBSPTree() );
+
+ // read in displacements that have been compiled into the bsp file
+ UnserializeDisps();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::Shutdown( void )
+{
+ // remove all displacements from the tree
+ for( int ndxDisp = m_DispTrees.Size(); ndxDisp >= 0; ndxDisp-- )
+ {
+ RemoveDispFromTree( ndxDisp );
+ }
+
+ // shutdown the bsp tree
+ m_pBSPTreeData->Shutdown();
+
+ // purge the displacement collision tree list
+ m_DispTrees.Purge();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::DispBuilderInit( CCoreDispInfo *pBuilderDisp, dface_t *pFace, int ndxFace )
+{
+ // get the .bsp displacement
+ ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
+ if( !pDisp )
+ return;
+
+ //
+ // initlialize the displacement base surface
+ //
+ CCoreDispSurface *pSurf = pBuilderDisp->GetSurface();
+ pSurf->SetPointCount( 4 );
+ pSurf->SetHandle( ndxFace );
+ pSurf->SetContents( pDisp->contents );
+
+ Vector pt[4];
+ int ndxPt;
+ for( ndxPt = 0; ndxPt < 4; ndxPt++ )
+ {
+ int eIndex = dsurfedges[pFace->firstedge+ndxPt];
+ if( eIndex < 0 )
+ {
+ pSurf->SetPoint( ndxPt, dvertexes[dedges[-eIndex].v[1]].point );
+ }
+ else
+ {
+ pSurf->SetPoint( ndxPt, dvertexes[dedges[eIndex].v[0]].point );
+ }
+
+ VectorCopy( pSurf->GetPoint(ndxPt), pt[ndxPt] );
+ }
+
+ //
+ // calculate the displacement surface normal
+ //
+ Vector vFaceNormal;
+ pSurf->GetNormal( vFaceNormal );
+ for( ndxPt = 0; ndxPt < 4; ndxPt++ )
+ {
+ pSurf->SetPointNormal( ndxPt, vFaceNormal );
+ }
+
+ // set the surface initial point info
+ pSurf->SetPointStart( pDisp->startPosition );
+ pSurf->FindSurfPointStartIndex();
+ pSurf->AdjustSurfPointData();
+
+ Vector vecTmp( texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][0],
+ texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][1],
+ texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][2] );
+ int nLuxelsPerWorldUnit = static_cast<int>( 1.0f / VectorLength( vecTmp ) );
+ Vector vecU( texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][0],
+ texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][1],
+ texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][2] );
+ Vector vecV( texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[1][0],
+ texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[1][1],
+ texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[1][2] );
+ pSurf->CalcLuxelCoords( nLuxelsPerWorldUnit, false, vecU, vecV );
+
+ pBuilderDisp->SetNeighborData( pDisp->m_EdgeNeighbors, pDisp->m_CornerNeighbors );
+
+ CDispVert *pVerts = &g_DispVerts[ pDisp->m_iDispVertStart ];
+ CDispTri *pTris = &g_DispTris[pDisp->m_iDispTriStart];
+
+ //
+ // initialize the displacement data
+ //
+ pBuilderDisp->InitDispInfo(
+ pDisp->power,
+ pDisp->minTess,
+ pDisp->smoothingAngle,
+ pVerts,
+ pTris );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::UnserializeDisps( void )
+{
+ // temporarily create the "builder" displacements
+ CUtlVector<CCoreDispInfo*> builderDisps;
+ for ( int iDisp = 0; iDisp < g_dispinfo.Count(); ++iDisp )
+ {
+ CCoreDispInfo *pDisp = new CCoreDispInfo;
+ if ( !pDisp )
+ {
+ builderDisps.Purge();
+ return;
+ }
+
+ int nIndex = builderDisps.AddToTail();
+ pDisp->SetListIndex( nIndex );
+ builderDisps[nIndex] = pDisp;
+ }
+
+ // Set them up as CDispUtilsHelpers.
+ for ( int iDisp = 0; iDisp < g_dispinfo.Count(); ++iDisp )
+ {
+ builderDisps[iDisp]->SetDispUtilsHelperInfo( builderDisps.Base(), g_dispinfo.Count() );
+ }
+
+ //
+ // find all faces with displacement data and initialize
+ //
+ for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
+ {
+ dface_t *pFace = &g_pFaces[ndxFace];
+ if( ValidDispFace( pFace ) )
+ {
+ DispBuilderInit( builderDisps[pFace->dispinfo], pFace, ndxFace );
+ }
+ }
+
+ // generate the displacement surfaces
+ for( int iDisp = 0; iDisp < g_dispinfo.Count(); ++iDisp )
+ {
+ builderDisps[iDisp]->Create();
+ }
+
+ // smooth edge normals
+ SmoothNeighboringDispSurfNormals( builderDisps.Base(), g_dispinfo.Count() );
+
+ //
+ // create the displacement collision tree and add it to the bsp tree
+ //
+ CVRADDispColl *pDispTrees = new CVRADDispColl[g_dispinfo.Count()];
+ if( !pDispTrees )
+ return;
+
+ m_DispTrees.AddMultipleToTail( g_dispinfo.Count() );
+
+ for( int iDisp = 0; iDisp < g_dispinfo.Count(); iDisp++ )
+ {
+ pDispTrees[iDisp].Create( builderDisps[iDisp] );
+
+ m_DispTrees[iDisp].m_pDispTree = &pDispTrees[iDisp];
+ m_DispTrees[iDisp].m_Handle = TREEDATA_INVALID_HANDLE;
+
+ InsertDispIntoTree( iDisp );
+ }
+
+ // free "builder" disps
+ builderDisps.Purge();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: create a set of patches for each displacement surface to transfer
+// bounced light around with
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::MakePatches( void )
+{
+ // Collect stats - keep track of the total displacement surface area.
+ float flTotalArea = 0.0f;
+
+ // Create patches for all of the displacements.
+ int nTreeCount = m_DispTrees.Size();
+ for( int iTree = 0; iTree < nTreeCount; ++iTree )
+ {
+ // Get the current displacement collision tree.
+ CVRADDispColl *pDispTree = m_DispTrees[iTree].m_pDispTree;
+ if( !pDispTree )
+ continue;
+
+ flTotalArea += pDispTree->CreateParentPatches();
+ }
+
+ // Print stats.
+ qprintf( "%i Displacements\n", nTreeCount );
+ qprintf( "%i Square Feet [%.2f Square Inches]\n", ( int )( flTotalArea / 144.0f ), flTotalArea );
+}
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::SubdividePatch( int iPatch )
+{
+ // Get the current patch to subdivide.
+ CPatch *pPatch = &g_Patches[iPatch];
+ if ( !pPatch )
+ return;
+
+ // Create children patches.
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[pPatch->faceNumber].dispinfo];
+ CVRADDispColl *pTree = dispTree.m_pDispTree;
+ if( pTree )
+ {
+ pTree->CreateChildPatches( iPatch, 0 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::StartRayTest( DispTested_t &dispTested )
+{
+ if( m_DispTrees.Size() > 0 )
+ {
+ if( dispTested.m_pTested == 0 )
+ {
+ dispTested.m_pTested = new int[m_DispTrees.Size()];
+ memset( dispTested.m_pTested, 0, m_DispTrees.Size() * sizeof( int ) );
+ dispTested.m_Enum = 0;
+ }
+ ++dispTested.m_Enum;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::ClipRayToDisp( DispTested_t &dispTested, Ray_t const &ray )
+{
+ StartRayTest( dispTested );
+
+ EnumContext_t ctx;
+ ctx.m_pRay = &ray;
+ ctx.m_pDispTested = &dispTested;
+
+ // If it got through without a hit, it returns true
+ return !m_pBSPTreeData->EnumerateLeavesAlongRay( ray, &m_EnumDispRay, ( int )&ctx );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
+ int ndxLeaf )
+{
+ EnumContext_t ctx;
+ ctx.m_pRay = &ray;
+ ctx.m_pDispTested = &dispTested;
+
+ return !m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &m_EnumDispRay, ( int )&ctx );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
+ int ndxLeaf, float& dist, dface_t*& pFace, Vector2D& luxelCoord )
+{
+ CBSPDispRayDistanceEnumerator rayTestEnum;
+ rayTestEnum.m_pRay = &ray;
+ rayTestEnum.m_pDispTested = &dispTested;
+
+ m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &rayTestEnum, 0 );
+
+ dist = rayTestEnum.m_Distance;
+ pFace = rayTestEnum.m_pSurface;
+ if (pFace)
+ {
+ Vector2DCopy( rayTestEnum.m_LuxelCoord, luxelCoord );
+ }
+}
+
+void CVRadDispMgr::ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
+ int ndxLeaf, float& dist, Vector *pNormal )
+{
+ CBSPDispRayDistanceEnumerator rayTestEnum;
+ rayTestEnum.m_pRay = &ray;
+ rayTestEnum.m_pDispTested = &dispTested;
+
+ m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &rayTestEnum, 0 );
+ dist = rayTestEnum.m_Distance;
+ if ( rayTestEnum.m_pSurface )
+ {
+ *pNormal = rayTestEnum.m_Normal;
+ }
+}
+
+void CVRadDispMgr::AddPolysForRayTrace( void )
+{
+ int nTreeCount = m_DispTrees.Size();
+ for( int iTree = 0; iTree < nTreeCount; ++iTree )
+ {
+ // Get the current displacement collision tree.
+ CVRADDispColl *pDispTree = m_DispTrees[iTree].m_pDispTree;
+
+ // Add the triangles of the tree to the RT environment
+ pDispTree->AddPolysForRayTrace();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::GetDispSurfNormal( int ndxFace, Vector &pt, Vector &ptNormal,
+ bool bInside )
+{
+ // get the displacement surface data
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
+ CVRADDispColl *pDispTree = dispTree.m_pDispTree;
+
+ // find the parameterized displacement indices
+ Vector2D uv;
+ pDispTree->BaseFacePlaneToDispUV( pt, uv );
+
+ if( bInside )
+ {
+ if( uv[0] < 0.0f || uv[0] > 1.0f ) { Msg( "Disp UV (%f) outside bounds!\n", uv[0] ); }
+ if( uv[1] < 0.0f || uv[1] > 1.0f ) { Msg( "Disp UV (%f) outside bounds!\n", uv[1] ); }
+ }
+
+ if( uv[0] < 0.0f ) { uv[0] = 0.0f; }
+ if( uv[0] > 1.0f ) { uv[0] = 1.0f; }
+ if( uv[1] < 0.0f ) { uv[1] = 0.0f; }
+ if( uv[1] > 1.0f ) { uv[1] = 1.0f; }
+
+ // get the normal at "pt"
+ pDispTree->DispUVToSurfNormal( uv, ptNormal );
+
+ // get the new "pt"
+ pDispTree->DispUVToSurfPoint( uv, pt, 1.0f );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::GetDispSurf( int ndxFace, CVRADDispColl **ppDispTree )
+{
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
+ *ppDispTree = dispTree.m_pDispTree;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::DispRay_EnumerateLeaf( int ndxLeaf, int context )
+{
+ return m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &m_EnumDispRay, context );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::DispRay_EnumerateElement( int userId, int context )
+{
+ DispCollTree_t &dispTree = m_DispTrees[userId];
+ EnumContext_t *pCtx = ( EnumContext_t* )context;
+
+ // don't test twice (check tested value)
+ if( pCtx->m_pDispTested->m_pTested[userId] == pCtx->m_pDispTested->m_Enum )
+ return true;
+
+ // set the tested value
+ pCtx->m_pDispTested->m_pTested[userId] = pCtx->m_pDispTested->m_Enum;
+
+ // false mean stop iterating -- return false if we hit! (NOTE: opposite return
+ // result of the collision tree's ray test, thus the !)
+ CBaseTrace trace;
+ trace.fraction = 1.0f;
+ return ( !dispTree.m_pDispTree->AABBTree_Ray( *pCtx->m_pRay, pCtx->m_pRay->InvDelta(), &trace, true ) );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+bool CVRadDispMgr::DispRayDistance_EnumerateElement( int userId, CBSPDispRayDistanceEnumerator* pCtx )
+{
+ DispCollTree_t &dispTree = m_DispTrees[userId];
+
+ // don't test twice (check tested value)
+ if( pCtx->m_pDispTested->m_pTested[userId] == pCtx->m_pDispTested->m_Enum )
+ return true;
+
+ // set the tested value
+ pCtx->m_pDispTested->m_pTested[userId] = pCtx->m_pDispTested->m_Enum;
+
+ // Test the ray, if it's closer than previous tests, use it!
+ RayDispOutput_t output;
+ output.ndxVerts[0] = -1;
+ output.ndxVerts[1] = -1;
+ output.ndxVerts[2] = -1;
+ output.ndxVerts[3] = -1;
+ output.u = -1.0f;
+ output.v = -1.0f;
+ output.dist = FLT_MAX;
+
+ if (dispTree.m_pDispTree->AABBTree_Ray( *pCtx->m_pRay, output ))
+ {
+ if (output.dist < pCtx->m_Distance)
+ {
+ pCtx->m_Distance = output.dist;
+ pCtx->m_pSurface = &g_pFaces[dispTree.m_pDispTree->GetParentIndex()];
+
+ // Get the luxel coordinate
+ ComputePointFromBarycentric(
+ dispTree.m_pDispTree->GetLuxelCoord(output.ndxVerts[0]),
+ dispTree.m_pDispTree->GetLuxelCoord(output.ndxVerts[1]),
+ dispTree.m_pDispTree->GetLuxelCoord(output.ndxVerts[2]),
+ output.u, output.v, pCtx->m_LuxelCoord );
+
+ Vector v0,v1,v2;
+ dispTree.m_pDispTree->GetVert( output.ndxVerts[0], v0 );
+ dispTree.m_pDispTree->GetVert( output.ndxVerts[1], v1 );
+ dispTree.m_pDispTree->GetVert( output.ndxVerts[2], v2 );
+ Vector e0 = v1-v0;
+ Vector e1 = v2-v0;
+ pCtx->m_Normal = CrossProduct( e0, e1 );
+ VectorNormalize(pCtx->m_Normal);
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Test a ray against a particular dispinfo
+//-----------------------------------------------------------------------------
+
+/*
+float CVRadDispMgr::ClipRayToDisp( Ray_t const &ray, int dispinfo )
+{
+ assert( m_DispTrees.IsValidIndex(dispinfo) );
+
+ RayDispOutput_t output;
+ if (!m_DispTrees[dispinfo].m_pDispTree->AABBTree_Ray( ray, output ))
+ return 1.0f;
+ return output.dist;
+}
+*/
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::DispFaceList_EnumerateLeaf( int ndxLeaf, int context )
+{
+ //
+ // add the faces found in this leaf to the face list
+ //
+ dleaf_t *pLeaf = &dleafs[ndxLeaf];
+ for( int ndxFace = 0; ndxFace < pLeaf->numleaffaces; ndxFace++ )
+ {
+ // get the current face index
+ int ndxLeafFace = pLeaf->firstleafface + ndxFace;
+
+ // check to see if the face already lives in the list
+ int ndx;
+ int size = m_EnumDispFaceList.m_FaceList.Size();
+ for( ndx = 0; ndx < size; ndx++ )
+ {
+ if( m_EnumDispFaceList.m_FaceList[ndx] == ndxLeafFace )
+ break;
+ }
+
+ if( ndx == size )
+ {
+ int ndxList = m_EnumDispFaceList.m_FaceList.AddToTail();
+ m_EnumDispFaceList.m_FaceList[ndxList] = ndxLeafFace;
+ }
+ }
+
+ return m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &m_EnumDispFaceList, context );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::DispFaceList_EnumerateElement( int userId, int context )
+{
+ DispCollTree_t &dispTree = m_DispTrees[userId];
+ CVRADDispColl *pDispTree = dispTree.m_pDispTree;
+ if( !pDispTree )
+ return false;
+
+ // check to see if the displacement already lives in the list
+ int ndx;
+ int size = m_EnumDispFaceList.m_DispList.Size();
+ for( ndx = 0; ndx < size; ndx++ )
+ {
+ if( m_EnumDispFaceList.m_DispList[ndx] == pDispTree )
+ break;
+ }
+
+ if( ndx == size )
+ {
+ int ndxList = m_EnumDispFaceList.m_DispList.AddToTail();
+ m_EnumDispFaceList.m_DispList[ndxList] = pDispTree;
+ }
+
+ return true;
+}
+
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+inline void GetSampleLight( facelight_t *pFaceLight, int ndxStyle, bool bBumped,
+ int ndxSample, LightingValue_t *pSampleLight )
+{
+// SampleLight[0].Init( 20.0f, 10.0f, 10.0f );
+// return;
+
+ // get sample from bumped lighting data
+ if( bBumped )
+ {
+ for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
+ {
+ pSampleLight[ndxBump] = pFaceLight->light[ndxStyle][ndxBump][ndxSample];
+ }
+ }
+ // just a generally lit surface
+ else
+ {
+ pSampleLight[0] = pFaceLight->light[ndxStyle][0][ndxSample];
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void AddSampleLightToRadial( Vector const &samplePos, Vector const &sampleNormal,
+ LightingValue_t *pSampleLight, float sampleRadius2,
+ Vector const &luxelPos, Vector const &luxelNormal,
+ radial_t *pRadial, int ndxRadial, bool bBumped,
+ bool bNeighborBumped )
+{
+ // check normals to see if sample contributes any light at all
+ float angle = sampleNormal.Dot( luxelNormal );
+ if ( angle < 0.15f )
+ return;
+
+ // calculate the light vector
+ Vector vSegment = samplePos - luxelPos;
+
+ // get the distance to the light
+ float dist = vSegment.Length();
+ float dist2 = dist * dist;
+
+ // Check to see if the light is within the influence.
+ float influence = 1.0f - ( dist2 / ( sampleRadius2 ) );
+ if( influence <= 0.0f )
+ return;
+
+ influence *= angle;
+
+ if( bBumped )
+ {
+ if( bNeighborBumped )
+ {
+ for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
+ {
+ pRadial->light[ndxBump][ndxRadial].AddWeighted( pSampleLight[ndxBump], influence );
+ }
+ pRadial->weight[ndxRadial] += influence;
+ }
+ else
+ {
+ influence *= 0.05f;
+ for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
+ {
+ pRadial->light[ndxBump][ndxRadial].AddWeighted( pSampleLight[0], influence );
+ }
+ pRadial->weight[ndxRadial] += influence;
+ }
+ }
+ else
+ {
+ pRadial->light[0][ndxRadial].AddWeighted( pSampleLight[0], influence );
+ pRadial->weight[ndxRadial] += influence;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::IsNeighbor( int iFace, int iNeighborFace )
+{
+ if ( iFace == iNeighborFace )
+ return true;
+
+ faceneighbor_t *pFaceNeighbor = &faceneighbor[iFace];
+ for ( int iNeighbor = 0; iNeighbor < pFaceNeighbor->numneighbors; iNeighbor++ )
+ {
+ if ( pFaceNeighbor->neighbor[iNeighbor] == iNeighborFace )
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::RadialLuxelAddSamples( int ndxFace, Vector const &luxelPt, Vector const &luxelNormal, float radius,
+ radial_t *pRadial, int ndxRadial, bool bBump, int lightStyle )
+{
+ // calculate one over the voxel size
+ float ooVoxelSize = 1.0f / SAMPLEHASH_VOXEL_SIZE;
+
+ //
+ // find voxel info
+ //
+ int voxelMin[3], voxelMax[3];
+ for( int axis = 0; axis < 3; axis++ )
+ {
+ voxelMin[axis] = ( int )( ( luxelPt[axis] - radius ) * ooVoxelSize );
+ voxelMax[axis] = ( int )( ( luxelPt[axis] + radius ) * ooVoxelSize ) + 1;
+ }
+
+ SampleData_t sampleData;
+ for( int ndxZ = voxelMin[2]; ndxZ < voxelMax[2] + 1; ndxZ++ )
+ {
+ for( int ndxY = voxelMin[1]; ndxY < voxelMax[1] + 1; ndxY++ )
+ {
+ for( int ndxX = voxelMin[0]; ndxX < voxelMax[0] + 1; ndxX++ )
+ {
+ sampleData.x = ndxX * 100;
+ sampleData.y = ndxY * 10;
+ sampleData.z = ndxZ;
+
+ UtlHashHandle_t handle = g_SampleHashTable.Find( sampleData );
+ if( handle != g_SampleHashTable.InvalidHandle() )
+ {
+ SampleData_t *pSampleData = &g_SampleHashTable.Element( handle );
+ int count = pSampleData->m_Samples.Count();
+ for( int ndx = 0; ndx < count; ndx++ )
+ {
+ SampleHandle_t sampleHandle = pSampleData->m_Samples.Element( ndx );
+ int ndxSample = ( sampleHandle & 0x0000ffff );
+ int ndxFaceLight = ( ( sampleHandle >> 16 ) & 0x0000ffff );
+
+ facelight_t *pFaceLight = &facelight[ndxFaceLight];
+ if( pFaceLight && IsNeighbor( ndxFace, ndxFaceLight ) )
+ {
+ //
+ // check for similar lightstyles
+ //
+ dface_t *pFace = &g_pFaces[ndxFaceLight];
+ if( pFace )
+ {
+ int ndxNeighborStyle = -1;
+ for( int ndxLightStyle = 0; ndxLightStyle < MAXLIGHTMAPS; ndxLightStyle++ )
+ {
+ if( pFace->styles[ndxLightStyle] == lightStyle )
+ {
+ ndxNeighborStyle = ndxLightStyle;
+ break;
+ }
+ }
+ if( ndxNeighborStyle == -1 )
+ continue;
+
+ // is this surface bumped???
+ bool bNeighborBump = texinfo[pFace->texinfo].flags & SURF_BUMPLIGHT ? true : false;
+
+ LightingValue_t sampleLight[NUM_BUMP_VECTS+1];
+ GetSampleLight( pFaceLight, ndxNeighborStyle, bNeighborBump, ndxSample, sampleLight );
+ AddSampleLightToRadial( pFaceLight->sample[ndxSample].pos, pFaceLight->sample[ndxSample].normal,
+ sampleLight, radius*radius, luxelPt, luxelNormal, pRadial, ndxRadial,
+ bBump, bNeighborBump );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::RadialLuxelBuild( CVRADDispColl *pDispTree, radial_t *pRadial,
+ int ndxStyle, bool bBump )
+{
+ //
+ // get data lighting data
+ //
+ int ndxFace = pDispTree->GetParentIndex();
+
+ dface_t *pFace = &g_pFaces[ndxFace];
+ facelight_t *pFaceLight = &facelight[ndxFace];
+
+ // get the influence radius
+ float radius2 = pDispTree->GetSampleRadius2();
+ float radius = ( float )sqrt( radius2 );
+
+ int radialSize = pRadial->w * pRadial->h;
+ for( int ndxRadial = 0; ndxRadial < radialSize; ndxRadial++ )
+ {
+ RadialLuxelAddSamples( ndxFace, pFaceLight->luxel[ndxRadial], pFaceLight->luxelNormals[ndxRadial],
+ radius, pRadial, ndxRadial, bBump, pFace->styles[ndxStyle] );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+radial_t *CVRadDispMgr::BuildLuxelRadial( int ndxFace, int ndxStyle, bool bBump )
+{
+ // allocate the radial
+ radial_t *pRadial = AllocateRadial( ndxFace );
+ if( !pRadial )
+ return NULL;
+
+ //
+ // step 1: get the displacement surface to be lit
+ //
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
+ CVRADDispColl *pDispTree = dispTree.m_pDispTree;
+ if( !pDispTree )
+ return NULL;
+
+ // step 2: build radial luxels
+ RadialLuxelBuild( pDispTree, pRadial, ndxStyle, bBump );
+
+ // step 3: return the built radial
+ return pRadial;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::SampleRadial( int ndxFace, radial_t *pRadial, Vector const &vPos, int ndxLxl,
+ LightingValue_t *pLightSample, int sampleCount, bool bPatch )
+{
+ bool bGoodSample = true;
+ for ( int count = 0; count < sampleCount; count++ )
+ {
+ pLightSample[count].Zero();
+
+ if ( pRadial->weight[ndxLxl] > 0.0f )
+ {
+ pLightSample[count].AddWeighted( pRadial->light[count][ndxLxl], ( 1.0f / pRadial->weight[ndxLxl] ) );
+ }
+ else
+ {
+ // error, luxel has no samples (not for patches)
+ if ( !bPatch )
+ {
+ // Yes, 2550 is correct!
+ // pLightSample[count].Init( 2550.0f, 0.0f, 2550.0f );
+ if( count == 0 )
+ bGoodSample = false;
+ }
+ }
+ }
+
+ return bGoodSample;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void GetPatchLight( CPatch *pPatch, bool bBump, Vector *pPatchLight )
+{
+ VectorCopy( pPatch->totallight.light[0], pPatchLight[0] );
+
+ if( bBump )
+ {
+ for( int ndxBump = 1; ndxBump < ( NUM_BUMP_VECTS + 1 ); ndxBump++ )
+ {
+ VectorCopy( pPatch->totallight.light[ndxBump], pPatchLight[ndxBump] );
+ }
+ }
+}
+
+extern void GetBumpNormals( const float* sVect, const float* tVect, const Vector& flatNormal,
+ const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] );
+extern void PreGetBumpNormalsForDisp( texinfo_t *pTexinfo, Vector &vecU, Vector &vecV, Vector &vecNormal );
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void AddPatchLightToRadial( Vector const &patchOrigin, Vector const &patchNormal,
+ Vector *pPatchLight, float patchRadius2,
+ Vector const &luxelPos, Vector const &luxelNormal,
+ radial_t *pRadial, int ndxRadial, bool bBump,
+ bool bNeighborBump )
+{
+ // calculate the light vector
+ Vector vSegment = patchOrigin - luxelPos;
+
+ // get the distance to the light
+ float dist = vSegment.Length();
+ float dist2 = dist * dist;
+
+ // Check to see if the light is within the sample influence.
+ float influence = 1.0f - ( dist2 / ( patchRadius2 ) );
+ if ( influence <= 0.0f )
+ return;
+
+ if( bBump )
+ {
+ Vector normals[NUM_BUMP_VECTS+1];
+ normals[0] = luxelNormal;
+ texinfo_t *pTexinfo = &texinfo[g_pFaces[pRadial->facenum].texinfo];
+ Vector vecTexU, vecTexV;
+ PreGetBumpNormalsForDisp( pTexinfo, vecTexU, vecTexV, normals[0] );
+ GetBumpNormals( vecTexU, vecTexV, normals[0], normals[0], &normals[1] );
+
+ if( bNeighborBump )
+ {
+ float flScale = patchNormal.Dot( normals[0] );
+ flScale = max( 0.0f, flScale );
+ float flBumpInfluence = influence * flScale;
+
+ for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
+ {
+ pRadial->light[ndxBump][ndxRadial].AddWeighted( pPatchLight[ndxBump], flBumpInfluence );
+ }
+
+ pRadial->weight[ndxRadial] += flBumpInfluence;
+ }
+ else
+ {
+ float flScale = patchNormal.Dot( normals[0] );
+ flScale = max( 0.0f, flScale );
+ float flBumpInfluence = influence * flScale * 0.05f;
+
+ for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
+ {
+ pRadial->light[ndxBump][ndxRadial].AddWeighted( pPatchLight[0], flBumpInfluence );
+ }
+
+ pRadial->weight[ndxRadial] += flBumpInfluence;
+ }
+ }
+ else
+ {
+ float flScale = patchNormal.Dot( luxelNormal );
+ flScale = max( 0.0f, flScale );
+ influence *= flScale;
+ pRadial->light[0][ndxRadial].AddWeighted( pPatchLight[0], influence );
+
+ // add the weight value
+ pRadial->weight[ndxRadial] += influence;
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::RadialLuxelAddPatch( int ndxFace, Vector const &luxelPt,
+ Vector const &luxelNormal, float radius,
+ radial_t *pRadial, int ndxRadial, bool bBump,
+ CUtlVector<CPatch*> &interestingPatches )
+{
+#ifdef SAMPLEHASH_QUERY_ONCE
+ for ( int i=0; i < interestingPatches.Count(); i++ )
+ {
+ CPatch *pPatch = interestingPatches[i];
+ bool bNeighborBump = texinfo[g_pFaces[pPatch->faceNumber].texinfo].flags & SURF_BUMPLIGHT ? true : false;
+
+ Vector patchLight[NUM_BUMP_VECTS+1];
+ GetPatchLight( pPatch, bBump, patchLight );
+ AddPatchLightToRadial( pPatch->origin, pPatch->normal, patchLight, radius*radius,
+ luxelPt, luxelNormal, pRadial, ndxRadial, bBump, bNeighborBump );
+ }
+#else
+ // calculate one over the voxel size
+ float ooVoxelSize = 1.0f / SAMPLEHASH_VOXEL_SIZE;
+
+ //
+ // find voxel info
+ //
+ int voxelMin[3], voxelMax[3];
+ for ( int axis = 0; axis < 3; axis++ )
+ {
+ voxelMin[axis] = ( int )( ( luxelPt[axis] - radius ) * ooVoxelSize );
+ voxelMax[axis] = ( int )( ( luxelPt[axis] + radius ) * ooVoxelSize ) + 1;
+ }
+
+ unsigned short curIterationKey = IncrementPatchIterationKey();
+ PatchSampleData_t patchData;
+ for ( int ndxZ = voxelMin[2]; ndxZ < voxelMax[2] + 1; ndxZ++ )
+ {
+ for ( int ndxY = voxelMin[1]; ndxY < voxelMax[1] + 1; ndxY++ )
+ {
+ for ( int ndxX = voxelMin[0]; ndxX < voxelMax[0] + 1; ndxX++ )
+ {
+ patchData.x = ndxX * 100;
+ patchData.y = ndxY * 10;
+ patchData.z = ndxZ;
+
+ UtlHashHandle_t handle = g_PatchSampleHashTable.Find( patchData );
+ if ( handle != g_PatchSampleHashTable.InvalidHandle() )
+ {
+ PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element( handle );
+ int count = pPatchData->m_ndxPatches.Count();
+ for ( int ndx = 0; ndx < count; ndx++ )
+ {
+ int ndxPatch = pPatchData->m_ndxPatches.Element( ndx );
+ CPatch *pPatch = &g_Patches.Element( ndxPatch );
+ if ( pPatch && pPatch->m_IterationKey != curIterationKey )
+ {
+ pPatch->m_IterationKey = curIterationKey;
+
+ if ( IsNeighbor( ndxFace, pPatch->faceNumber ) )
+ {
+ bool bNeighborBump = texinfo[g_pFaces[pPatch->faceNumber].texinfo].flags & SURF_BUMPLIGHT ? true : false;
+
+ Vector patchLight[NUM_BUMP_VECTS+1];
+ GetPatchLight( pPatch, bBump, patchLight );
+ AddPatchLightToRadial( pPatch->origin, pPatch->normal, patchLight, radius*radius,
+ luxelPt, luxelNormal, pRadial, ndxRadial, bBump, bNeighborBump );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+#endif
+}
+
+
+void CVRadDispMgr::GetInterestingPatchesForLuxels(
+ int ndxFace,
+ CUtlVector<CPatch*> &interestingPatches,
+ float patchSampleRadius )
+{
+ facelight_t *pFaceLight = &facelight[ndxFace];
+
+ // Get the max bounds of all voxels that these luxels touch.
+ Vector vLuxelMin( FLT_MAX, FLT_MAX, FLT_MAX );
+ Vector vLuxelMax( -FLT_MAX, -FLT_MAX, -FLT_MAX );
+ for ( int i=0; i < pFaceLight->numluxels; i++ )
+ {
+ VectorMin( pFaceLight->luxel[i], vLuxelMin, vLuxelMin );
+ VectorMax( pFaceLight->luxel[i], vLuxelMax, vLuxelMax );
+ }
+
+ int allVoxelMin[3], allVoxelMax[3];
+ for ( int axis = 0; axis < 3; axis++ )
+ {
+ allVoxelMin[axis] = ( int )( ( vLuxelMin[axis] - patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE );
+ allVoxelMax[axis] = ( int )( ( vLuxelMax[axis] + patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE ) + 1;
+ }
+ int allVoxelSize[3] = { allVoxelMax[0] - allVoxelMin[0], allVoxelMax[1] - allVoxelMin[1], allVoxelMax[2] - allVoxelMin[2] };
+
+
+ // Now figure out exactly which voxels these luxels touch.
+ CUtlVector<unsigned char> voxelBits;
+ voxelBits.SetSize( ((allVoxelSize[0] * allVoxelSize[1] * allVoxelSize[2]) + 7) / 8 );
+ memset( voxelBits.Base(), 0, voxelBits.Count() );
+
+ for ( int i=0; i < pFaceLight->numluxels; i++ )
+ {
+ int voxelMin[3], voxelMax[3];
+ for ( int axis=0; axis < 3; axis++ )
+ {
+ voxelMin[axis] = ( int )( ( pFaceLight->luxel[i][axis] - patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE );
+ voxelMax[axis] = ( int )( ( pFaceLight->luxel[i][axis] + patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE ) + 1;
+ }
+
+ for ( int x=voxelMin[0]; x < voxelMax[0]; x++ )
+ {
+ for ( int y=voxelMin[1]; y < voxelMax[1]; y++ )
+ {
+ for ( int z=voxelMin[2]; z < voxelMax[2]; z++ )
+ {
+ int iBit = (z - allVoxelMin[2])*(allVoxelSize[0]*allVoxelSize[1]) +
+ (y-allVoxelMin[1])*allVoxelSize[0] +
+ (x-allVoxelMin[0]);
+ voxelBits[iBit>>3] |= (1 << (iBit & 7));
+ }
+ }
+ }
+ }
+
+
+ // Now get the list of patches that touch those voxels.
+ unsigned short curIterationKey = IncrementPatchIterationKey();
+
+ for ( int x=0; x < allVoxelSize[0]; x++ )
+ {
+ for ( int y=0; y < allVoxelSize[1]; y++ )
+ {
+ for ( int z=0; z < allVoxelSize[2]; z++ )
+ {
+ // Make sure this voxel has any luxels that care about it.
+ int iBit = z*(allVoxelSize[0]*allVoxelSize[1]) + y*allVoxelSize[0] + x;
+ unsigned char val = voxelBits[iBit>>3] & (1 << (iBit & 7));
+ if ( !val )
+ continue;
+
+ PatchSampleData_t patchData;
+ patchData.x = (x + allVoxelMin[0]) * 100;
+ patchData.y = (y + allVoxelMin[1]) * 10;
+ patchData.z = (z + allVoxelMin[2]);
+
+ UtlHashHandle_t handle = g_PatchSampleHashTable.Find( patchData );
+ if ( handle != g_PatchSampleHashTable.InvalidHandle() )
+ {
+ PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element( handle );
+
+ // For all patches that touch this hash table element..
+ for ( int ndx = 0; ndx < pPatchData->m_ndxPatches.Count(); ndx++ )
+ {
+ int ndxPatch = pPatchData->m_ndxPatches.Element( ndx );
+ CPatch *pPatch = &g_Patches.Element( ndxPatch );
+
+ // If we haven't touched the patch already and it's a valid neighbor, then we want to use it.
+ if ( pPatch && pPatch->m_IterationKey != curIterationKey )
+ {
+ pPatch->m_IterationKey = curIterationKey;
+
+ if ( IsNeighbor( ndxFace, pPatch->faceNumber ) )
+ {
+ interestingPatches.AddToTail( pPatch );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::RadialPatchBuild( CVRADDispColl *pDispTree, radial_t *pRadial,
+ bool bBump )
+{
+ //
+ // get data lighting data
+ //
+ int ndxFace = pDispTree->GetParentIndex();
+ facelight_t *pFaceLight = &facelight[ndxFace];
+
+ // get the influence radius
+ float radius2 = pDispTree->GetPatchSampleRadius2();
+ float radius = ( float )sqrt( radius2 );
+
+ CUtlVector<CPatch*> interestingPatches;
+#ifdef SAMPLEHASH_QUERY_ONCE
+ GetInterestingPatchesForLuxels( ndxFace, interestingPatches, radius );
+#endif
+
+ int radialSize = pRadial->w * pRadial->h;
+ for( int ndxRadial = 0; ndxRadial < radialSize; ndxRadial++ )
+ {
+ RadialLuxelAddPatch(
+ ndxFace,
+ pFaceLight->luxel[ndxRadial],
+ pFaceLight->luxelNormals[ndxRadial],
+ radius,
+ pRadial,
+ ndxRadial,
+ bBump,
+ interestingPatches );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+radial_t *CVRadDispMgr::BuildPatchRadial( int ndxFace, bool bBump )
+{
+ // allocate the radial
+ radial_t *pRadial = AllocateRadial( ndxFace );
+ if( !pRadial )
+ return NULL;
+
+ //
+ // step 1: get the displacement surface to be lit
+ //
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
+ CVRADDispColl *pDispTree = dispTree.m_pDispTree;
+ if( !pDispTree )
+ return NULL;
+
+ // step 2: build radial of patch light
+ RadialPatchBuild( pDispTree, pRadial, bBump );
+
+ // step 3: return the built radial
+ return pRadial;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool SampleInSolid( sample_t *pSample )
+{
+ int ndxLeaf = PointLeafnum( pSample->pos );
+ return ( dleafs[ndxLeaf].contents == CONTENTS_SOLID );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::InsertSamplesDataIntoHashTable( void )
+{
+ int totalSamples = 0;
+#if 0
+ int totalSamplesInSolid = 0;
+#endif
+
+ for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
+ {
+ dface_t *pFace = &g_pFaces[ndxFace];
+ facelight_t *pFaceLight = &facelight[ndxFace];
+ if( !pFace || !pFaceLight )
+ continue;
+
+ if( texinfo[pFace->texinfo].flags & TEX_SPECIAL )
+ continue;
+
+#if 0
+ bool bDisp = ( pFace->dispinfo != -1 );
+#endif
+ //
+ // for each sample
+ //
+ for( int ndxSample = 0; ndxSample < pFaceLight->numsamples; ndxSample++ )
+ {
+ sample_t *pSample = &pFaceLight->sample[ndxSample];
+ if( pSample )
+ {
+#if 0
+ if( bDisp )
+ {
+ // test sample to see if the displacement samples resides in solid
+ if( SampleInSolid( pSample ) )
+ {
+ totalSamplesInSolid++;
+ continue;
+ }
+ }
+#endif
+
+ // create the sample handle
+ SampleHandle_t sampleHandle = ndxSample;
+ sampleHandle |= ( ndxFace << 16 );
+
+ SampleData_AddSample( pSample, sampleHandle );
+ }
+
+ }
+
+ totalSamples += pFaceLight->numsamples;
+ }
+
+#if 0
+ // not implemented yet!!!
+ Msg( "%d samples in solid\n", totalSamplesInSolid );
+#endif
+
+ // log the distribution
+ SampleData_Log();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::InsertPatchSampleDataIntoHashTable( void )
+{
+ // don't insert patch samples if we are not bouncing light
+ if( numbounce <= 0 )
+ return;
+
+ int totalPatchSamples = 0;
+
+ for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
+ {
+ dface_t *pFace = &g_pFaces[ndxFace];
+ facelight_t *pFaceLight = &facelight[ndxFace];
+ if( !pFace || !pFaceLight )
+ continue;
+
+ if( texinfo[pFace->texinfo].flags & TEX_SPECIAL )
+ continue;
+
+ //
+ // for each patch
+ //
+ CPatch *pNextPatch = NULL;
+ if( g_FacePatches.Element( ndxFace ) != g_FacePatches.InvalidIndex() )
+ {
+ for( CPatch *pPatch = &g_Patches.Element( g_FacePatches.Element( ndxFace ) ); pPatch; pPatch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( pPatch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( pPatch->ndxNext );
+ }
+
+ // skip patches with children
+ if( pPatch->child1 != g_Patches.InvalidIndex() )
+ continue;
+
+ int ndxPatch = pPatch - g_Patches.Base();
+ PatchSampleData_AddSample( pPatch, ndxPatch );
+
+ totalPatchSamples++;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::StartTimer( const char *name )
+{
+ Msg( name );
+ m_Timer.Start();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::EndTimer( void )
+{
+ m_Timer.End();
+ CCycleCount duration = m_Timer.GetDuration();
+ double seconds = duration.GetSeconds();
+
+ Msg( "Done<%1.4lf sec>\n", seconds );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::BuildDispSamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
+{
+ // get the tree assosciated with the face
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
+ CVRADDispColl *pDispTree = dispTree.m_pDispTree;
+ if( !pDispTree )
+ return false;
+
+ // lightmap size
+ int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
+ int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
+
+ // calculate the steps in uv space
+ float stepU = 1.0f / ( float )width;
+ float stepV = 1.0f / ( float )height;
+ float halfStepU = stepU * 0.5f;
+ float halfStepV = stepV * 0.5f;
+
+ //
+ // build the winding points (used to generate world space winding and
+ // calculate the area of the "sample")
+ //
+ int ndxU, ndxV;
+
+ CUtlVector<sample_t> samples;
+ samples.SetCount( SINGLEMAP );
+ sample_t *pSamples = samples.Base();
+
+ CUtlVector<Vector> worldPoints;
+ worldPoints.SetCount( SINGLEMAP );
+ Vector *pWorldPoints = worldPoints.Base();
+
+ for( ndxV = 0; ndxV < ( height + 1 ); ndxV++ )
+ {
+ for( ndxU = 0; ndxU < ( width + 1 ); ndxU++ )
+ {
+ int ndx = ( ndxV * ( width + 1 ) ) + ndxU;
+
+ Vector2D uv( ndxU * stepU, ndxV * stepV );
+ pDispTree->DispUVToSurfPoint( uv, pWorldPoints[ndx], 0.0f );
+ }
+ }
+
+ for( ndxV = 0; ndxV < height; ndxV++ )
+ {
+ for( ndxU = 0; ndxU < width; ndxU++ )
+ {
+ // build the winding
+ winding_t *pWinding = AllocWinding( 4 );
+ if( pWinding )
+ {
+ pWinding->numpoints = 4;
+ pWinding->p[0] = pWorldPoints[(ndxV*(width+1))+ndxU];
+ pWinding->p[1] = pWorldPoints[((ndxV+1)*(width+1))+ndxU];
+ pWinding->p[2] = pWorldPoints[((ndxV+1)*(width+1))+(ndxU+1)];
+ pWinding->p[3] = pWorldPoints[(ndxV*(width+1))+(ndxU+1)];
+
+ // calculate the area
+ float area = WindingArea( pWinding );
+
+ int ndxSample = ( ndxV * width ) + ndxU;
+ pSamples[ndxSample].w = pWinding;
+ pSamples[ndxSample].area = area;
+ }
+ else
+ {
+ Msg( "BuildDispSamples: WARNING - failed winding allocation\n" );
+ }
+ }
+ }
+
+ //
+ // build the samples points (based on s, t and sampleoffset (center of samples);
+ // generates world space position and normal)
+ //
+ for( ndxV = 0; ndxV < height; ndxV++ )
+ {
+ for( ndxU = 0; ndxU < width; ndxU++ )
+ {
+ int ndxSample = ( ndxV * width ) + ndxU;
+ pSamples[ndxSample].s = ndxU;
+ pSamples[ndxSample].t = ndxV;
+ pSamples[ndxSample].coord[0] = ( ndxU * stepU ) + halfStepU;
+ pSamples[ndxSample].coord[1] = ( ndxV * stepV ) + halfStepV;
+ pDispTree->DispUVToSurfPoint( pSamples[ndxSample].coord, pSamples[ndxSample].pos, 1.0f );
+ pDispTree->DispUVToSurfNormal( pSamples[ndxSample].coord, pSamples[ndxSample].normal );
+ }
+ }
+
+ //
+ // copy over samples
+ //
+ pFaceLight->numsamples = width * height;
+ pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
+ if( !pFaceLight->sample )
+ return false;
+
+ memcpy( pFaceLight->sample, pSamples, pFaceLight->numsamples * sizeof( *pFaceLight->sample ) );
+
+ // statistics - warning?!
+ if( pFaceLight->numsamples == 0 )
+ {
+ Msg( "BuildDispSamples: WARNING - no samples %d\n", pLightInfo->face - g_pFaces );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::BuildDispLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
+{
+ // get the tree assosciated with the face
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
+ CVRADDispColl *pDispTree = dispTree.m_pDispTree;
+ if( !pDispTree )
+ return false;
+
+ // lightmap size
+ int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
+ int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
+
+ // calcuate actual luxel points
+ pFaceLight->numluxels = width * height;
+ pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
+ pFaceLight->luxelNormals = ( Vector* )calloc( pFaceLight->numluxels, sizeof( Vector ) );
+ if( !pFaceLight->luxel || !pFaceLight->luxelNormals )
+ return false;
+
+ float stepU = 1.0f / ( float )( width - 1 );
+ float stepV = 1.0f / ( float )( height - 1 );
+
+ for( int ndxV = 0; ndxV < height; ndxV++ )
+ {
+ for( int ndxU = 0; ndxU < width; ndxU++ )
+ {
+ int ndxLuxel = ( ndxV * width ) + ndxU;
+
+ Vector2D uv( ndxU * stepU, ndxV * stepV );
+ pDispTree->DispUVToSurfPoint( uv, pFaceLight->luxel[ndxLuxel], 1.0f );
+ pDispTree->DispUVToSurfNormal( uv, pFaceLight->luxelNormals[ndxLuxel] );
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::BuildDispSamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
+{
+ // get the tree assosciated with the face
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
+ CVRADDispColl *pDispTree = dispTree.m_pDispTree;
+ if( !pDispTree )
+ return false;
+
+ // lightmap size
+ int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
+ int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
+
+ // calcuate actual luxel points
+ pFaceLight->numsamples = width * height;
+ pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
+ if( !pFaceLight->sample )
+ return false;
+
+ pFaceLight->numluxels = width * height;
+ pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
+ pFaceLight->luxelNormals = ( Vector* )calloc( pFaceLight->numluxels, sizeof( Vector ) );
+ if( !pFaceLight->luxel || !pFaceLight->luxelNormals )
+ return false;
+
+ float stepU = 1.0f / ( float )( width - 1 );
+ float stepV = 1.0f / ( float )( height - 1 );
+ float halfStepU = stepU * 0.5f;
+ float halfStepV = stepV * 0.5f;
+
+ for( int ndxV = 0; ndxV < height; ndxV++ )
+ {
+ for( int ndxU = 0; ndxU < width; ndxU++ )
+ {
+ int ndx = ( ndxV * width ) + ndxU;
+
+ pFaceLight->sample[ndx].s = ndxU;
+ pFaceLight->sample[ndx].t = ndxV;
+ pFaceLight->sample[ndx].coord[0] = ( ndxU * stepU ) + halfStepU;
+ pFaceLight->sample[ndx].coord[1] = ( ndxV * stepV ) + halfStepV;
+
+ pDispTree->DispUVToSurfPoint( pFaceLight->sample[ndx].coord, pFaceLight->sample[ndx].pos, 1.0f );
+ pDispTree->DispUVToSurfNormal( pFaceLight->sample[ndx].coord, pFaceLight->sample[ndx].normal );
+
+ pFaceLight->luxel[ndx] = pFaceLight->sample[ndx].pos;
+ pFaceLight->luxelNormals[ndx] = pFaceLight->sample[ndx].normal;
+ }
+ }
+
+ return true;
+}
diff --git a/mp/src/utils/vrad/vraddll.cpp b/mp/src/utils/vrad/vraddll.cpp
index 87f8d7fc..bc9f2f45 100644
--- a/mp/src/utils/vrad/vraddll.cpp
+++ b/mp/src/utils/vrad/vraddll.cpp
@@ -1,243 +1,243 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-//#include <strstrea.h>
-#include "vraddll.h"
-#include "bsplib.h"
-#include "vrad.h"
-#include "map_shared.h"
-#include "lightmap.h"
-#include "threads.h"
-
-
-static CUtlVector<unsigned char> g_LastGoodLightData;
-static CUtlVector<unsigned char> g_FacesTouched;
-
-
-static CVRadDLL g_VRadDLL;
-EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CVRadDLL, IVRadDLL, VRAD_INTERFACE_VERSION, g_VRadDLL );
-EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CVRadDLL, ILaunchableDLL, LAUNCHABLE_DLL_INTERFACE_VERSION, g_VRadDLL );
-
-
-// ---------------------------------------------------------------------------- //
-// temporary static array data size tracking
-// original data size = 143 megs
-// - converting ddispindices, ddispverts, g_dispinfo, and dlightdata to CUtlVector
-// - 51 megs
-// ---------------------------------------------------------------------------- //
-
-class dat
-{
-public:
- char *name;
- int size;
-};
-#define DATENTRY(name) {#name, sizeof(name)}
-
-dat g_Dats[] =
-{
- DATENTRY(dmodels),
- DATENTRY(dvisdata),
- DATENTRY(dlightdataLDR),
- DATENTRY(dlightdataHDR),
- DATENTRY(dentdata),
- DATENTRY(dleafs),
- DATENTRY(dplanes),
- DATENTRY(dvertexes),
- DATENTRY(g_vertnormalindices),
- DATENTRY(g_vertnormals),
- DATENTRY(texinfo),
- DATENTRY(dtexdata),
- DATENTRY(g_dispinfo),
- DATENTRY(dorigfaces),
- DATENTRY(g_primitives),
- DATENTRY(g_primverts),
- DATENTRY(g_primindices),
- DATENTRY(dfaces),
- DATENTRY(dedges),
- DATENTRY(dleaffaces),
- DATENTRY(dleafbrushes),
- DATENTRY(dsurfedges),
- DATENTRY(dbrushes),
- DATENTRY(dbrushsides),
- DATENTRY(dareas),
- DATENTRY(dareaportals),
- DATENTRY(dworldlights),
- DATENTRY(dleafwaterdata),
- DATENTRY(g_ClipPortalVerts),
- DATENTRY(g_CubemapSamples),
- DATENTRY(g_TexDataStringData),
- DATENTRY(g_TexDataStringTable),
- DATENTRY(g_Overlays)
-};
-
-int CalcDatSize()
-{
- int ret = 0;
- int count = sizeof( g_Dats ) / sizeof( g_Dats[0] );
-
- int i;
- for( i=1; i < count; i++ )
- {
- if( g_Dats[i-1].size > g_Dats[i].size )
- {
- dat temp = g_Dats[i-1];
- g_Dats[i-1] = g_Dats[i];
- g_Dats[i] = temp;
-
- if( i > 1 )
- i -= 2;
- else
- i -= 1;
- }
- }
-
- for( i=0; i < count; i++ )
- ret += g_Dats[i].size;
-
- return ret;
-}
-
-int g_TotalDatSize = CalcDatSize();
-
-
-
-
-int CVRadDLL::main( int argc, char **argv )
-{
- return VRAD_Main( argc, argv );
-}
-
-
-bool CVRadDLL::Init( char const *pFilename )
-{
- VRAD_Init();
-
- // Set options and run vrad startup code.
- do_fast = true;
- g_bLowPriorityThreads = true;
- g_pIncremental = GetIncremental();
-
- VRAD_LoadBSP( pFilename );
- return true;
-}
-
-
-void CVRadDLL::Release()
-{
-}
-
-
-void CVRadDLL::GetBSPInfo( CBSPInfo *pInfo )
-{
- pInfo->dlightdata = pdlightdata->Base();
- pInfo->lightdatasize = pdlightdata->Count();
-
- pInfo->dfaces = dfaces;
- pInfo->m_pFacesTouched = g_FacesTouched.Base();
- pInfo->numfaces = numfaces;
-
- pInfo->dvertexes = dvertexes;
- pInfo->numvertexes = numvertexes;
-
- pInfo->dedges = dedges;
- pInfo->numedges = numedges;
-
- pInfo->dsurfedges = dsurfedges;
- pInfo->numsurfedges = numsurfedges;
-
- pInfo->texinfo = texinfo.Base();
- pInfo->numtexinfo = texinfo.Count();
-
- pInfo->g_dispinfo = g_dispinfo.Base();
- pInfo->g_numdispinfo = g_dispinfo.Count();
-
- pInfo->dtexdata = dtexdata;
- pInfo->numtexdata = numtexdata;
-
- pInfo->texDataStringData = g_TexDataStringData.Base();
- pInfo->nTexDataStringData = g_TexDataStringData.Count();
-
- pInfo->texDataStringTable = g_TexDataStringTable.Base();
- pInfo->nTexDataStringTable = g_TexDataStringTable.Count();
-}
-
-
-bool CVRadDLL::DoIncrementalLight( char const *pVMFFile )
-{
- char tempPath[MAX_PATH], tempFilename[MAX_PATH];
- GetTempPath( sizeof( tempPath ), tempPath );
- GetTempFileName( tempPath, "vmf_entities_", 0, tempFilename );
-
- FileHandle_t fp = g_pFileSystem->Open( tempFilename, "wb" );
- if( !fp )
- return false;
-
- g_pFileSystem->Write( pVMFFile, strlen(pVMFFile)+1, fp );
- g_pFileSystem->Close( fp );
-
- // Parse the new entities.
- if( !LoadEntsFromMapFile( tempFilename ) )
- return false;
-
- // Create lights.
- CreateDirectLights();
-
- // set up sky cameras
- ProcessSkyCameras();
-
- g_bInterrupt = false;
- if( RadWorld_Go() )
- {
- // Save off the last finished lighting results for the BSP.
- g_LastGoodLightData.CopyArray( pdlightdata->Base(), pdlightdata->Count() );
- if( g_pIncremental )
- g_pIncremental->GetFacesTouched( g_FacesTouched );
-
- return true;
- }
- else
- {
- g_iCurFace = 0;
- return false;
- }
-}
-
-
-bool CVRadDLL::Serialize()
-{
- if( !g_pIncremental )
- return false;
-
- if( g_LastGoodLightData.Count() > 0 )
- {
- pdlightdata->CopyArray( g_LastGoodLightData.Base(), g_LastGoodLightData.Count() );
-
- if( g_pIncremental->Serialize() )
- {
- // Delete this so it doesn't keep re-saving it.
- g_LastGoodLightData.Purge();
- return true;
- }
- }
-
- return false;
-}
-
-
-float CVRadDLL::GetPercentComplete()
-{
- return (float)g_iCurFace / numfaces;
-}
-
-
-void CVRadDLL::Interrupt()
-{
- g_bInterrupt = true;
-}
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+//#include <strstrea.h>
+#include "vraddll.h"
+#include "bsplib.h"
+#include "vrad.h"
+#include "map_shared.h"
+#include "lightmap.h"
+#include "threads.h"
+
+
+static CUtlVector<unsigned char> g_LastGoodLightData;
+static CUtlVector<unsigned char> g_FacesTouched;
+
+
+static CVRadDLL g_VRadDLL;
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CVRadDLL, IVRadDLL, VRAD_INTERFACE_VERSION, g_VRadDLL );
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CVRadDLL, ILaunchableDLL, LAUNCHABLE_DLL_INTERFACE_VERSION, g_VRadDLL );
+
+
+// ---------------------------------------------------------------------------- //
+// temporary static array data size tracking
+// original data size = 143 megs
+// - converting ddispindices, ddispverts, g_dispinfo, and dlightdata to CUtlVector
+// - 51 megs
+// ---------------------------------------------------------------------------- //
+
+class dat
+{
+public:
+ char *name;
+ int size;
+};
+#define DATENTRY(name) {#name, sizeof(name)}
+
+dat g_Dats[] =
+{
+ DATENTRY(dmodels),
+ DATENTRY(dvisdata),
+ DATENTRY(dlightdataLDR),
+ DATENTRY(dlightdataHDR),
+ DATENTRY(dentdata),
+ DATENTRY(dleafs),
+ DATENTRY(dplanes),
+ DATENTRY(dvertexes),
+ DATENTRY(g_vertnormalindices),
+ DATENTRY(g_vertnormals),
+ DATENTRY(texinfo),
+ DATENTRY(dtexdata),
+ DATENTRY(g_dispinfo),
+ DATENTRY(dorigfaces),
+ DATENTRY(g_primitives),
+ DATENTRY(g_primverts),
+ DATENTRY(g_primindices),
+ DATENTRY(dfaces),
+ DATENTRY(dedges),
+ DATENTRY(dleaffaces),
+ DATENTRY(dleafbrushes),
+ DATENTRY(dsurfedges),
+ DATENTRY(dbrushes),
+ DATENTRY(dbrushsides),
+ DATENTRY(dareas),
+ DATENTRY(dareaportals),
+ DATENTRY(dworldlights),
+ DATENTRY(dleafwaterdata),
+ DATENTRY(g_ClipPortalVerts),
+ DATENTRY(g_CubemapSamples),
+ DATENTRY(g_TexDataStringData),
+ DATENTRY(g_TexDataStringTable),
+ DATENTRY(g_Overlays)
+};
+
+int CalcDatSize()
+{
+ int ret = 0;
+ int count = sizeof( g_Dats ) / sizeof( g_Dats[0] );
+
+ int i;
+ for( i=1; i < count; i++ )
+ {
+ if( g_Dats[i-1].size > g_Dats[i].size )
+ {
+ dat temp = g_Dats[i-1];
+ g_Dats[i-1] = g_Dats[i];
+ g_Dats[i] = temp;
+
+ if( i > 1 )
+ i -= 2;
+ else
+ i -= 1;
+ }
+ }
+
+ for( i=0; i < count; i++ )
+ ret += g_Dats[i].size;
+
+ return ret;
+}
+
+int g_TotalDatSize = CalcDatSize();
+
+
+
+
+int CVRadDLL::main( int argc, char **argv )
+{
+ return VRAD_Main( argc, argv );
+}
+
+
+bool CVRadDLL::Init( char const *pFilename )
+{
+ VRAD_Init();
+
+ // Set options and run vrad startup code.
+ do_fast = true;
+ g_bLowPriorityThreads = true;
+ g_pIncremental = GetIncremental();
+
+ VRAD_LoadBSP( pFilename );
+ return true;
+}
+
+
+void CVRadDLL::Release()
+{
+}
+
+
+void CVRadDLL::GetBSPInfo( CBSPInfo *pInfo )
+{
+ pInfo->dlightdata = pdlightdata->Base();
+ pInfo->lightdatasize = pdlightdata->Count();
+
+ pInfo->dfaces = dfaces;
+ pInfo->m_pFacesTouched = g_FacesTouched.Base();
+ pInfo->numfaces = numfaces;
+
+ pInfo->dvertexes = dvertexes;
+ pInfo->numvertexes = numvertexes;
+
+ pInfo->dedges = dedges;
+ pInfo->numedges = numedges;
+
+ pInfo->dsurfedges = dsurfedges;
+ pInfo->numsurfedges = numsurfedges;
+
+ pInfo->texinfo = texinfo.Base();
+ pInfo->numtexinfo = texinfo.Count();
+
+ pInfo->g_dispinfo = g_dispinfo.Base();
+ pInfo->g_numdispinfo = g_dispinfo.Count();
+
+ pInfo->dtexdata = dtexdata;
+ pInfo->numtexdata = numtexdata;
+
+ pInfo->texDataStringData = g_TexDataStringData.Base();
+ pInfo->nTexDataStringData = g_TexDataStringData.Count();
+
+ pInfo->texDataStringTable = g_TexDataStringTable.Base();
+ pInfo->nTexDataStringTable = g_TexDataStringTable.Count();
+}
+
+
+bool CVRadDLL::DoIncrementalLight( char const *pVMFFile )
+{
+ char tempPath[MAX_PATH], tempFilename[MAX_PATH];
+ GetTempPath( sizeof( tempPath ), tempPath );
+ GetTempFileName( tempPath, "vmf_entities_", 0, tempFilename );
+
+ FileHandle_t fp = g_pFileSystem->Open( tempFilename, "wb" );
+ if( !fp )
+ return false;
+
+ g_pFileSystem->Write( pVMFFile, strlen(pVMFFile)+1, fp );
+ g_pFileSystem->Close( fp );
+
+ // Parse the new entities.
+ if( !LoadEntsFromMapFile( tempFilename ) )
+ return false;
+
+ // Create lights.
+ CreateDirectLights();
+
+ // set up sky cameras
+ ProcessSkyCameras();
+
+ g_bInterrupt = false;
+ if( RadWorld_Go() )
+ {
+ // Save off the last finished lighting results for the BSP.
+ g_LastGoodLightData.CopyArray( pdlightdata->Base(), pdlightdata->Count() );
+ if( g_pIncremental )
+ g_pIncremental->GetFacesTouched( g_FacesTouched );
+
+ return true;
+ }
+ else
+ {
+ g_iCurFace = 0;
+ return false;
+ }
+}
+
+
+bool CVRadDLL::Serialize()
+{
+ if( !g_pIncremental )
+ return false;
+
+ if( g_LastGoodLightData.Count() > 0 )
+ {
+ pdlightdata->CopyArray( g_LastGoodLightData.Base(), g_LastGoodLightData.Count() );
+
+ if( g_pIncremental->Serialize() )
+ {
+ // Delete this so it doesn't keep re-saving it.
+ g_LastGoodLightData.Purge();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+float CVRadDLL::GetPercentComplete()
+{
+ return (float)g_iCurFace / numfaces;
+}
+
+
+void CVRadDLL::Interrupt()
+{
+ g_bInterrupt = true;
+}
+
+
diff --git a/mp/src/utils/vrad/vraddll.h b/mp/src/utils/vrad/vraddll.h
index 506e8eb6..988daca5 100644
--- a/mp/src/utils/vrad/vraddll.h
+++ b/mp/src/utils/vrad/vraddll.h
@@ -1,34 +1,34 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef VRADDLL_H
-#define VRADDLL_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-#include "ivraddll.h"
-#include "ilaunchabledll.h"
-
-
-class CVRadDLL : public IVRadDLL, public ILaunchableDLL
-{
-// IVRadDLL overrides.
-public:
- virtual int main( int argc, char **argv );
- virtual bool Init( char const *pFilename );
- virtual void Release();
- virtual void GetBSPInfo( CBSPInfo *pInfo );
- virtual bool DoIncrementalLight( char const *pVMFFile );
- virtual bool Serialize();
- virtual float GetPercentComplete();
- virtual void Interrupt();
-};
-
-
-#endif // VRADDLL_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef VRADDLL_H
+#define VRADDLL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "ivraddll.h"
+#include "ilaunchabledll.h"
+
+
+class CVRadDLL : public IVRadDLL, public ILaunchableDLL
+{
+// IVRadDLL overrides.
+public:
+ virtual int main( int argc, char **argv );
+ virtual bool Init( char const *pFilename );
+ virtual void Release();
+ virtual void GetBSPInfo( CBSPInfo *pInfo );
+ virtual bool DoIncrementalLight( char const *pVMFFile );
+ virtual bool Serialize();
+ virtual float GetPercentComplete();
+ virtual void Interrupt();
+};
+
+
+#endif // VRADDLL_H
diff --git a/mp/src/utils/vrad/vradstaticprops.cpp b/mp/src/utils/vrad/vradstaticprops.cpp
index f240d94f..f4f04a69 100644
--- a/mp/src/utils/vrad/vradstaticprops.cpp
+++ b/mp/src/utils/vrad/vradstaticprops.cpp
@@ -1,1915 +1,1915 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $Revision: $
-// $NoKeywords: $
-//
-// This file contains code to allow us to associate client data with bsp leaves.
-//
-//=============================================================================//
-
-#include "vrad.h"
-#include "mathlib/vector.h"
-#include "UtlBuffer.h"
-#include "utlvector.h"
-#include "GameBSPFile.h"
-#include "BSPTreeData.h"
-#include "VPhysics_Interface.h"
-#include "Studio.h"
-#include "Optimize.h"
-#include "Bsplib.h"
-#include "CModel.h"
-#include "PhysDll.h"
-#include "phyfile.h"
-#include "collisionutils.h"
-#include "tier1/KeyValues.h"
-#include "pacifier.h"
-#include "materialsystem/imaterial.h"
-#include "materialsystem/hardwareverts.h"
-#include "byteswap.h"
-#include "mpivrad.h"
-#include "vtf/vtf.h"
-#include "tier1/utldict.h"
-#include "tier1/utlsymbol.h"
-
-#include "messbuf.h"
-#include "vmpi.h"
-#include "vmpi_distribute_work.h"
-
-
-#define ALIGN_TO_POW2(x,y) (((x)+(y-1))&~(y-1))
-
-// identifies a vertex embedded in solid
-// lighting will be copied from nearest valid neighbor
-struct badVertex_t
-{
- int m_ColorVertex;
- Vector m_Position;
- Vector m_Normal;
-};
-
-// a final colored vertex
-struct colorVertex_t
-{
- Vector m_Color;
- Vector m_Position;
- bool m_bValid;
-};
-
-class CComputeStaticPropLightingResults
-{
-public:
- ~CComputeStaticPropLightingResults()
- {
- m_ColorVertsArrays.PurgeAndDeleteElements();
- }
-
- CUtlVector< CUtlVector<colorVertex_t>* > m_ColorVertsArrays;
-};
-
-//-----------------------------------------------------------------------------
-// Globals
-//-----------------------------------------------------------------------------
-CUtlSymbolTable g_ForcedTextureShadowsModels;
-
-// DON'T USE THIS FROM WITHIN A THREAD. THERE IS A THREAD CONTEXT CREATED
-// INSIDE PropTested_t. USE THAT INSTEAD.
-IPhysicsCollision *s_pPhysCollision = NULL;
-
-//-----------------------------------------------------------------------------
-// Vrad's static prop manager
-//-----------------------------------------------------------------------------
-
-class CVradStaticPropMgr : public IVradStaticPropMgr
-{
-public:
- // constructor, destructor
- CVradStaticPropMgr();
- virtual ~CVradStaticPropMgr();
-
- // methods of IStaticPropMgr
- void Init();
- void Shutdown();
-
- // iterate all the instanced static props and compute their vertex lighting
- void ComputeLighting( int iThread );
-
-private:
- // VMPI stuff.
- static void VMPI_ProcessStaticProp_Static( int iThread, uint64 iStaticProp, MessageBuffer *pBuf );
- static void VMPI_ReceiveStaticPropResults_Static( uint64 iStaticProp, MessageBuffer *pBuf, int iWorker );
- void VMPI_ProcessStaticProp( int iThread, int iStaticProp, MessageBuffer *pBuf );
- void VMPI_ReceiveStaticPropResults( int iStaticProp, MessageBuffer *pBuf, int iWorker );
-
- // local thread version
- static void ThreadComputeStaticPropLighting( int iThread, void *pUserData );
- void ComputeLightingForProp( int iThread, int iStaticProp );
-
- // Methods associated with unserializing static props
- void UnserializeModelDict( CUtlBuffer& buf );
- void UnserializeModels( CUtlBuffer& buf );
- void UnserializeStaticProps();
-
- // Creates a collision model
- void CreateCollisionModel( char const* pModelName );
-
-private:
- // Unique static prop models
- struct StaticPropDict_t
- {
- vcollide_t m_loadedModel;
- CPhysCollide* m_pModel;
- Vector m_Mins; // Bounding box is in local coordinates
- Vector m_Maxs;
- studiohdr_t* m_pStudioHdr;
- CUtlBuffer m_VtxBuf;
- CUtlVector<int> m_textureShadowIndex; // each texture has an index if this model casts texture shadows
- CUtlVector<int> m_triangleMaterialIndex;// each triangle has an index if this model casts texture shadows
- };
-
- struct MeshData_t
- {
- CUtlVector<Vector> m_Verts;
- int m_nLod;
- };
-
- // A static prop instance
- struct CStaticProp
- {
- Vector m_Origin;
- QAngle m_Angles;
- Vector m_mins;
- Vector m_maxs;
- Vector m_LightingOrigin;
- int m_ModelIdx;
- BSPTreeDataHandle_t m_Handle;
- CUtlVector<MeshData_t> m_MeshData;
- int m_Flags;
- bool m_bLightingOriginValid;
- };
-
- // Enumeration context
- struct EnumContext_t
- {
- PropTested_t* m_pPropTested;
- Ray_t const* m_pRay;
- };
-
- // The list of all static props
- CUtlVector <StaticPropDict_t> m_StaticPropDict;
- CUtlVector <CStaticProp> m_StaticProps;
-
- bool m_bIgnoreStaticPropTrace;
-
- void ComputeLighting( CStaticProp &prop, int iThread, int prop_index, CComputeStaticPropLightingResults *pResults );
- void ApplyLightingToStaticProp( CStaticProp &prop, const CComputeStaticPropLightingResults *pResults );
-
- void SerializeLighting();
- void AddPolysForRayTrace();
- void BuildTriList( CStaticProp &prop );
-};
-
-
-//-----------------------------------------------------------------------------
-// Expose IVradStaticPropMgr to vrad
-//-----------------------------------------------------------------------------
-
-static CVradStaticPropMgr g_StaticPropMgr;
-IVradStaticPropMgr* StaticPropMgr()
-{
- return &g_StaticPropMgr;
-}
-
-
-//-----------------------------------------------------------------------------
-// constructor, destructor
-//-----------------------------------------------------------------------------
-
-CVradStaticPropMgr::CVradStaticPropMgr()
-{
- // set to ignore static prop traces
- m_bIgnoreStaticPropTrace = false;
-}
-
-CVradStaticPropMgr::~CVradStaticPropMgr()
-{
-}
-
-//-----------------------------------------------------------------------------
-// Makes sure the studio model is a static prop
-//-----------------------------------------------------------------------------
-
-bool IsStaticProp( studiohdr_t* pHdr )
-{
- if (!(pHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP))
- return false;
-
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Load a file into a Utlbuf
-//-----------------------------------------------------------------------------
-static bool LoadFile( char const* pFileName, CUtlBuffer& buf )
-{
- if ( !g_pFullFileSystem )
- return false;
-
- return g_pFullFileSystem->ReadFile( pFileName, NULL, buf );
-}
-
-
-//-----------------------------------------------------------------------------
-// Constructs the file name from the model name
-//-----------------------------------------------------------------------------
-static char const* ConstructFileName( char const* pModelName )
-{
- static char buf[1024];
- sprintf( buf, "%s%s", gamedir, pModelName );
- return buf;
-}
-
-
-//-----------------------------------------------------------------------------
-// Computes a convex hull from a studio mesh
-//-----------------------------------------------------------------------------
-static CPhysConvex* ComputeConvexHull( mstudiomesh_t* pMesh, studiohdr_t *pStudioHdr )
-{
- const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( (void *)pStudioHdr );
- Assert( vertData ); // This can only return NULL on X360 for now
-
- // Generate a list of all verts in the mesh
- Vector** ppVerts = (Vector**)_alloca(pMesh->numvertices * sizeof(Vector*) );
- for (int i = 0; i < pMesh->numvertices; ++i)
- {
- ppVerts[i] = vertData->Position(i);
- }
-
- // Generate a convex hull from the verts
- return s_pPhysCollision->ConvexFromVerts( ppVerts, pMesh->numvertices );
-}
-
-
-//-----------------------------------------------------------------------------
-// Computes a convex hull from the studio model
-//-----------------------------------------------------------------------------
-CPhysCollide* ComputeConvexHull( studiohdr_t* pStudioHdr )
-{
- CUtlVector<CPhysConvex*> convexHulls;
-
- for (int body = 0; body < pStudioHdr->numbodyparts; ++body )
- {
- mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( body );
- for( int model = 0; model < pBodyPart->nummodels; ++model )
- {
- mstudiomodel_t *pStudioModel = pBodyPart->pModel( model );
- for( int mesh = 0; mesh < pStudioModel->nummeshes; ++mesh )
- {
- // Make a convex hull for each mesh
- // NOTE: This won't work unless the model has been compiled
- // with $staticprop
- mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( mesh );
- convexHulls.AddToTail( ComputeConvexHull( pStudioMesh, pStudioHdr ) );
- }
- }
- }
-
- // Convert an array of convex elements to a compiled collision model
- // (this deletes the convex elements)
- return s_pPhysCollision->ConvertConvexToCollide( convexHulls.Base(), convexHulls.Size() );
-}
-
-
-//-----------------------------------------------------------------------------
-// Load studio model vertex data from a file...
-//-----------------------------------------------------------------------------
-
-bool LoadStudioModel( char const* pModelName, CUtlBuffer& buf )
-{
- // No luck, gotta build it
- // Construct the file name...
- if (!LoadFile( pModelName, buf ))
- {
- Warning("Error! Unable to load model \"%s\"\n", pModelName );
- return false;
- }
-
- // Check that it's valid
- if (strncmp ((const char *) buf.PeekGet(), "IDST", 4) &&
- strncmp ((const char *) buf.PeekGet(), "IDAG", 4))
- {
- Warning("Error! Invalid model file \"%s\"\n", pModelName );
- return false;
- }
-
- studiohdr_t* pHdr = (studiohdr_t*)buf.PeekGet();
-
- Studio_ConvertStudioHdrToNewVersion( pHdr );
-
- if (pHdr->version != STUDIO_VERSION)
- {
- Warning("Error! Invalid model version \"%s\"\n", pModelName );
- return false;
- }
-
- if (!IsStaticProp(pHdr))
- {
- Warning("Error! To use model \"%s\"\n"
- " as a static prop, it must be compiled with $staticprop!\n", pModelName );
- return false;
- }
-
- // ensure reset
- pHdr->pVertexBase = NULL;
- pHdr->pIndexBase = NULL;
-
- return true;
-}
-
-bool LoadStudioCollisionModel( char const* pModelName, CUtlBuffer& buf )
-{
- char tmp[1024];
- Q_strncpy( tmp, pModelName, sizeof( tmp ) );
- Q_SetExtension( tmp, ".phy", sizeof( tmp ) );
- // No luck, gotta build it
- if (!LoadFile( tmp, buf ))
- {
- // this is not an error, the model simply has no PHY file
- return false;
- }
-
- phyheader_t *header = (phyheader_t *)buf.PeekGet();
-
- if ( header->size != sizeof(*header) || header->solidCount <= 0 )
- return false;
-
- return true;
-}
-
-bool LoadVTXFile( char const* pModelName, const studiohdr_t *pStudioHdr, CUtlBuffer& buf )
-{
- char filename[MAX_PATH];
-
- // construct filename
- Q_StripExtension( pModelName, filename, sizeof( filename ) );
- strcat( filename, ".dx80.vtx" );
-
- if ( !LoadFile( filename, buf ) )
- {
- Warning( "Error! Unable to load file \"%s\"\n", filename );
- return false;
- }
-
- OptimizedModel::FileHeader_t* pVtxHdr = (OptimizedModel::FileHeader_t *)buf.Base();
-
- // Check that it's valid
- if ( pVtxHdr->version != OPTIMIZED_MODEL_FILE_VERSION )
- {
- Warning( "Error! Invalid VTX file version: %d, expected %d \"%s\"\n", pVtxHdr->version, OPTIMIZED_MODEL_FILE_VERSION, filename );
- return false;
- }
- if ( pVtxHdr->checkSum != pStudioHdr->checksum )
- {
- Warning( "Error! Invalid VTX file checksum: %d, expected %d \"%s\"\n", pVtxHdr->checkSum, pStudioHdr->checksum, filename );
- return false;
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Gets a vertex position from a strip index
-//-----------------------------------------------------------------------------
-inline static Vector* PositionFromIndex( const mstudio_meshvertexdata_t *vertData, mstudiomesh_t* pMesh, OptimizedModel::StripGroupHeader_t* pStripGroup, int i )
-{
- OptimizedModel::Vertex_t* pVert = pStripGroup->pVertex( i );
- return vertData->Position( pVert->origMeshVertID );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Writes a glview text file containing the collision surface in question
-// Input : *pCollide -
-// *pFilename -
-//-----------------------------------------------------------------------------
-void DumpCollideToGlView( vcollide_t *pCollide, const char *pFilename )
-{
- if ( !pCollide )
- return;
-
- Msg("Writing %s...\n", pFilename );
-
- FILE *fp = fopen( pFilename, "w" );
- for (int i = 0; i < pCollide->solidCount; ++i)
- {
- Vector *outVerts;
- int vertCount = s_pPhysCollision->CreateDebugMesh( pCollide->solids[i], &outVerts );
- int triCount = vertCount / 3;
- int vert = 0;
-
- unsigned char r = (i & 1) * 64 + 64;
- unsigned char g = (i & 2) * 64 + 64;
- unsigned char b = (i & 4) * 64 + 64;
-
- float fr = r / 255.0f;
- float fg = g / 255.0f;
- float fb = b / 255.0f;
-
- for ( int i = 0; i < triCount; i++ )
- {
- fprintf( fp, "3\n" );
- fprintf( fp, "%6.3f %6.3f %6.3f %.2f %.3f %.3f\n",
- outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fr, fg, fb );
- vert++;
- fprintf( fp, "%6.3f %6.3f %6.3f %.2f %.3f %.3f\n",
- outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fr, fg, fb );
- vert++;
- fprintf( fp, "%6.3f %6.3f %6.3f %.2f %.3f %.3f\n",
- outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fr, fg, fb );
- vert++;
- }
- s_pPhysCollision->DestroyDebugMesh( vertCount, outVerts );
- }
- fclose( fp );
-}
-
-
-static bool PointInTriangle( const Vector2D &p, const Vector2D &v0, const Vector2D &v1, const Vector2D &v2 )
-{
- float coords[3];
- GetBarycentricCoords2D( v0, v1, v2, p, coords );
- for ( int i = 0; i < 3; i++ )
- {
- if ( coords[i] < 0.0f || coords[i] > 1.0f )
- return false;
- }
- float sum = coords[0] + coords[1] + coords[2];
- if ( sum > 1.0f )
- return false;
- return true;
-}
-
-bool LoadFileIntoBuffer( CUtlBuffer &buf, const char *pFilename )
-{
- FileHandle_t fileHandle = g_pFileSystem->Open( pFilename, "rb" );
- if ( !fileHandle )
- return false;
-
- // Get the file size
- int texSize = g_pFileSystem->Size( fileHandle );
- buf.EnsureCapacity( texSize );
- int nBytesRead = g_pFileSystem->Read( buf.Base(), texSize, fileHandle );
- g_pFileSystem->Close( fileHandle );
- buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
- buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
- return true;
-}
-
-// keeps a list of all textures that cast shadows via alpha channel
-class CShadowTextureList
-{
-public:
- // This loads a vtf and converts it to RGB8888 format
- unsigned char *LoadVTFRGB8888( const char *pName, int *pWidth, int *pHeight, bool *pClampU, bool *pClampV )
- {
- char szPath[MAX_PATH];
- Q_strncpy( szPath, "materials/", sizeof( szPath ) );
- Q_strncat( szPath, pName, sizeof( szPath ), COPY_ALL_CHARACTERS );
- Q_strncat( szPath, ".vtf", sizeof( szPath ), COPY_ALL_CHARACTERS );
- Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
-
- CUtlBuffer buf;
- if ( !LoadFileIntoBuffer( buf, szPath ) )
- return NULL;
- IVTFTexture *pTex = CreateVTFTexture();
- if (!pTex->Unserialize( buf ))
- return NULL;
- Msg("Loaded alpha texture %s\n", szPath );
- unsigned char *pSrcImage = pTex->ImageData( 0, 0, 0, 0, 0, 0 );
- int iWidth = pTex->Width();
- int iHeight = pTex->Height();
- ImageFormat dstFormat = IMAGE_FORMAT_RGBA8888;
- ImageFormat srcFormat = pTex->Format();
- *pClampU = (pTex->Flags() & TEXTUREFLAGS_CLAMPS) ? true : false;
- *pClampV = (pTex->Flags() & TEXTUREFLAGS_CLAMPT) ? true : false;
- unsigned char *pDstImage = new unsigned char[ImageLoader::GetMemRequired( iWidth, iHeight, 1, dstFormat, false )];
-
- if( !ImageLoader::ConvertImageFormat( pSrcImage, srcFormat,
- pDstImage, dstFormat, iWidth, iHeight, 0, 0 ) )
- {
- delete[] pDstImage;
- return NULL;
- }
-
- *pWidth = iWidth;
- *pHeight = iHeight;
- return pDstImage;
- }
-
- // Checks the database for the material and loads if necessary
- // returns true if found and pIndex will be the index, -1 if no alpha shadows
- bool FindOrLoadIfValid( const char *pMaterialName, int *pIndex )
- {
- *pIndex = -1;
- int index = m_Textures.Find(pMaterialName);
- bool bFound = false;
- if ( index != m_Textures.InvalidIndex() )
- {
- bFound = true;
- *pIndex = index;
- }
- else
- {
- KeyValues *pVMT = new KeyValues("vmt");
- CUtlBuffer buf(0,0,CUtlBuffer::TEXT_BUFFER);
- LoadFileIntoBuffer( buf, pMaterialName );
- if ( pVMT->LoadFromBuffer( pMaterialName, buf ) )
- {
- bFound = true;
- if ( pVMT->FindKey("$translucent") || pVMT->FindKey("$alphatest") )
- {
- KeyValues *pBaseTexture = pVMT->FindKey("$basetexture");
- if ( pBaseTexture )
- {
- const char *pBaseTextureName = pBaseTexture->GetString();
- if ( pBaseTextureName )
- {
- int w, h;
- bool bClampU = false;
- bool bClampV = false;
- unsigned char *pImageBits = LoadVTFRGB8888( pBaseTextureName, &w, &h, &bClampU, &bClampV );
- if ( pImageBits )
- {
- int index = m_Textures.Insert( pMaterialName );
- m_Textures[index].InitFromRGB8888( w, h, pImageBits );
- *pIndex = index;
- if ( pVMT->FindKey("$nocull") )
- {
- // UNDONE: Support this? Do we need to emit two triangles?
- m_Textures[index].allowBackface = true;
- }
- m_Textures[index].clampU = bClampU;
- m_Textures[index].clampV = bClampV;
- delete[] pImageBits;
- }
- }
- }
- }
-
- }
- pVMT->deleteThis();
- }
-
- return bFound;
- }
-
-
- // iterate the textures for the model and load each one into the database
- // this is used on models marked to cast texture shadows
- void LoadAllTexturesForModel( studiohdr_t *pHdr, int *pTextureList )
- {
- for ( int i = 0; i < pHdr->numtextures; i++ )
- {
- int textureIndex = -1;
- // try to add each texture to the transparent shadow manager
- char szPath[MAX_PATH];
-
- // iterate quietly through all specified directories until a valid material is found
- for ( int j = 0; j < pHdr->numcdtextures; j++ )
- {
- Q_strncpy( szPath, "materials/", sizeof( szPath ) );
- Q_strncat( szPath, pHdr->pCdtexture( j ), sizeof( szPath ) );
- const char *textureName = pHdr->pTexture( i )->pszName();
- Q_strncat( szPath, textureName, sizeof( szPath ), COPY_ALL_CHARACTERS );
- Q_strncat( szPath, ".vmt", sizeof( szPath ), COPY_ALL_CHARACTERS );
- Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
- if ( FindOrLoadIfValid( szPath, &textureIndex ) )
- break;
- }
-
- pTextureList[i] = textureIndex;
- }
- }
-
- int AddMaterialEntry( int shadowTextureIndex, const Vector2D &t0, const Vector2D &t1, const Vector2D &t2 )
- {
- int index = m_MaterialEntries.AddToTail();
- m_MaterialEntries[index].textureIndex = shadowTextureIndex;
- m_MaterialEntries[index].uv[0] = t0;
- m_MaterialEntries[index].uv[1] = t1;
- m_MaterialEntries[index].uv[2] = t2;
- return index;
- }
-
- // HACKHACK: Compute the average coverage for this triangle by sampling the AABB of its texture space
- float ComputeCoverageForTriangle( int shadowTextureIndex, const Vector2D &t0, const Vector2D &t1, const Vector2D &t2 )
- {
- float umin = min(t0.x, t1.x);
- umin = min(umin, t2.x);
- float umax = max(t0.x, t1.x);
- umax = max(umax, t2.x);
-
- float vmin = min(t0.y, t1.y);
- vmin = min(vmin, t2.y);
- float vmax = max(t0.y, t1.y);
- vmax = max(vmax, t2.y);
-
- // UNDONE: Do something about tiling
- umin = clamp(umin, 0, 1);
- umax = clamp(umax, 0, 1);
- vmin = clamp(vmin, 0, 1);
- vmax = clamp(vmax, 0, 1);
- Assert(umin>=0.0f && umax <= 1.0f);
- Assert(vmin>=0.0f && vmax <= 1.0f);
- const alphatexture_t &tex = m_Textures.Element(shadowTextureIndex);
- int u0 = umin * (tex.width-1);
- int u1 = umax * (tex.width-1);
- int v0 = vmin * (tex.height-1);
- int v1 = vmax * (tex.height-1);
-
- int total = 0;
- int count = 0;
- for ( int v = v0; v <= v1; v++ )
- {
- int row = (v * tex.width);
- for ( int u = u0; u <= u1; u++ )
- {
- total += tex.pAlphaTexels[row + u];
- count++;
- }
- }
- if ( count )
- {
- float coverage = float(total) / (count * 255.0f);
- return coverage;
- }
- return 1.0f;
- }
-
- int SampleMaterial( int materialIndex, const Vector &coords, bool bBackface )
- {
- const materialentry_t &mat = m_MaterialEntries[materialIndex];
- const alphatexture_t &tex = m_Textures.Element(m_MaterialEntries[materialIndex].textureIndex);
- if ( bBackface && !tex.allowBackface )
- return 0;
- Vector2D uv = coords.x * mat.uv[0] + coords.y * mat.uv[1] + coords.z * mat.uv[2];
- int u = RoundFloatToInt( uv[0] * tex.width );
- int v = RoundFloatToInt( uv[1] * tex.height );
-
- // asume power of 2, clamp or wrap
- // UNDONE: Support clamp? This code should work
-#if 0
- u = tex.clampU ? clamp(u,0,(tex.width-1)) : (u & (tex.width-1));
- v = tex.clampV ? clamp(v,0,(tex.height-1)) : (v & (tex.height-1));
-#else
- // for now always wrap
- u &= (tex.width-1);
- v &= (tex.height-1);
-#endif
-
- return tex.pAlphaTexels[v * tex.width + u];
- }
-
- struct alphatexture_t
- {
- short width;
- short height;
- bool allowBackface;
- bool clampU;
- bool clampV;
- unsigned char *pAlphaTexels;
-
- void InitFromRGB8888( int w, int h, unsigned char *pTexels )
- {
- width = w;
- height = h;
- pAlphaTexels = new unsigned char[w*h];
- for ( int i = 0; i < h; i++ )
- {
- for ( int j = 0; j < w; j++ )
- {
- int index = (i*w) + j;
- pAlphaTexels[index] = pTexels[index*4 + 3];
- }
- }
- }
- };
- struct materialentry_t
- {
- int textureIndex;
- Vector2D uv[3];
- };
- // this is the list of textures we've loaded
- // only load each one once
- CUtlDict< alphatexture_t, unsigned short > m_Textures;
- CUtlVector<materialentry_t> m_MaterialEntries;
-};
-
-// global to keep the shadow-casting texture list and their alpha bits
-CShadowTextureList g_ShadowTextureList;
-
-float ComputeCoverageFromTexture( float b0, float b1, float b2, int32 hitID )
-{
- const float alphaScale = 1.0f / 255.0f;
- // UNDONE: Pass ray down to determine backfacing?
- //Vector normal( tri.m_flNx, tri.m_flNy, tri.m_flNz );
- //bool bBackface = DotProduct(delta, tri.N) > 0 ? true : false;
- Vector coords(b0,b1,b2);
- return alphaScale * g_ShadowTextureList.SampleMaterial( g_RtEnv.GetTriangleMaterial(hitID), coords, false );
-}
-
-// this is here to strip models/ or .mdl or whatnot
-void CleanModelName( const char *pModelName, char *pOutput, int outLen )
-{
- // strip off leading models/ if it exists
- const char *pModelDir = "models/";
- int modelLen = Q_strlen(pModelDir);
-
- if ( !Q_strnicmp(pModelName, pModelDir, modelLen ) )
- {
- pModelName += modelLen;
- }
- Q_strncpy( pOutput, pModelName, outLen );
-
- // truncate any .mdl extension
- char *dot = strchr(pOutput,'.');
- if ( dot )
- {
- *dot = 0;
- }
-
-}
-
-
-void ForceTextureShadowsOnModel( const char *pModelName )
-{
- char buf[1024];
- CleanModelName( pModelName, buf, sizeof(buf) );
- if ( !g_ForcedTextureShadowsModels.Find(buf).IsValid())
- {
- g_ForcedTextureShadowsModels.AddString(buf);
- }
-}
-
-bool IsModelTextureShadowsForced( const char *pModelName )
-{
- char buf[1024];
- CleanModelName( pModelName, buf, sizeof(buf) );
- return g_ForcedTextureShadowsModels.Find(buf).IsValid();
-}
-
-
-//-----------------------------------------------------------------------------
-// Creates a collision model (based on the render geometry!)
-//-----------------------------------------------------------------------------
-void CVradStaticPropMgr::CreateCollisionModel( char const* pModelName )
-{
- CUtlBuffer buf;
- CUtlBuffer bufvtx;
- CUtlBuffer bufphy;
-
- int i = m_StaticPropDict.AddToTail();
- m_StaticPropDict[i].m_pModel = NULL;
- m_StaticPropDict[i].m_pStudioHdr = NULL;
-
- if ( !LoadStudioModel( pModelName, buf ) )
- {
- VectorCopy( vec3_origin, m_StaticPropDict[i].m_Mins );
- VectorCopy( vec3_origin, m_StaticPropDict[i].m_Maxs );
- return;
- }
-
- studiohdr_t* pHdr = (studiohdr_t*)buf.Base();
-
- VectorCopy( pHdr->hull_min, m_StaticPropDict[i].m_Mins );
- VectorCopy( pHdr->hull_max, m_StaticPropDict[i].m_Maxs );
-
- if ( LoadStudioCollisionModel( pModelName, bufphy ) )
- {
- phyheader_t header;
- bufphy.Get( &header, sizeof(header) );
-
- vcollide_t *pCollide = &m_StaticPropDict[i].m_loadedModel;
- s_pPhysCollision->VCollideLoad( pCollide, header.solidCount, (const char *)bufphy.PeekGet(), bufphy.TellPut() - bufphy.TellGet() );
- m_StaticPropDict[i].m_pModel = m_StaticPropDict[i].m_loadedModel.solids[0];
-
- /*
- static int propNum = 0;
- char tmp[128];
- sprintf( tmp, "staticprop%03d.txt", propNum );
- DumpCollideToGlView( pCollide, tmp );
- ++propNum;
- */
- }
- else
- {
- // mark this as unused
- m_StaticPropDict[i].m_loadedModel.solidCount = 0;
-
- // CPhysCollide* pPhys = CreatePhysCollide( pHdr, pVtxHdr );
- m_StaticPropDict[i].m_pModel = ComputeConvexHull( pHdr );
- }
-
- // clone it
- m_StaticPropDict[i].m_pStudioHdr = (studiohdr_t *)malloc( buf.Size() );
- memcpy( m_StaticPropDict[i].m_pStudioHdr, (studiohdr_t*)buf.Base(), buf.Size() );
-
- if ( !LoadVTXFile( pModelName, m_StaticPropDict[i].m_pStudioHdr, m_StaticPropDict[i].m_VtxBuf ) )
- {
- // failed, leave state identified as disabled
- m_StaticPropDict[i].m_VtxBuf.Purge();
- }
-
- if ( g_bTextureShadows )
- {
- if ( (pHdr->flags & STUDIOHDR_FLAGS_CAST_TEXTURE_SHADOWS) || IsModelTextureShadowsForced(pModelName) )
- {
- m_StaticPropDict[i].m_textureShadowIndex.RemoveAll();
- m_StaticPropDict[i].m_triangleMaterialIndex.RemoveAll();
- m_StaticPropDict[i].m_textureShadowIndex.AddMultipleToTail( pHdr->numtextures );
- g_ShadowTextureList.LoadAllTexturesForModel( pHdr, m_StaticPropDict[i].m_textureShadowIndex.Base() );
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Unserialize static prop model dictionary
-//-----------------------------------------------------------------------------
-void CVradStaticPropMgr::UnserializeModelDict( CUtlBuffer& buf )
-{
- int count = buf.GetInt();
- while ( --count >= 0 )
- {
- StaticPropDictLump_t lump;
- buf.Get( &lump, sizeof(StaticPropDictLump_t) );
-
- CreateCollisionModel( lump.m_Name );
- }
-}
-
-void CVradStaticPropMgr::UnserializeModels( CUtlBuffer& buf )
-{
- int count = buf.GetInt();
-
- m_StaticProps.AddMultipleToTail(count);
- for ( int i = 0; i < count; ++i )
- {
- StaticPropLump_t lump;
- buf.Get( &lump, sizeof(StaticPropLump_t) );
-
- VectorCopy( lump.m_Origin, m_StaticProps[i].m_Origin );
- VectorCopy( lump.m_Angles, m_StaticProps[i].m_Angles );
- VectorCopy( lump.m_LightingOrigin, m_StaticProps[i].m_LightingOrigin );
- m_StaticProps[i].m_bLightingOriginValid = ( lump.m_Flags & STATIC_PROP_USE_LIGHTING_ORIGIN ) > 0;
- m_StaticProps[i].m_ModelIdx = lump.m_PropType;
- m_StaticProps[i].m_Handle = TREEDATA_INVALID_HANDLE;
- m_StaticProps[i].m_Flags = lump.m_Flags;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Unserialize static props
-//-----------------------------------------------------------------------------
-
-void CVradStaticPropMgr::UnserializeStaticProps()
-{
- // Unserialize static props, insert them into the appropriate leaves
- GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle( GAMELUMP_STATIC_PROPS );
- int size = g_GameLumps.GameLumpSize( handle );
- if (!size)
- return;
-
- if ( g_GameLumps.GetGameLumpVersion( handle ) != GAMELUMP_STATIC_PROPS_VERSION )
- {
- Error( "Cannot load the static props... encountered a stale map version. Re-vbsp the map." );
- }
-
- if ( g_GameLumps.GetGameLump( handle ) )
- {
- CUtlBuffer buf( g_GameLumps.GetGameLump(handle), size, CUtlBuffer::READ_ONLY );
- UnserializeModelDict( buf );
-
- // Skip the leaf list data
- int count = buf.GetInt();
- buf.SeekGet( CUtlBuffer::SEEK_CURRENT, count * sizeof(StaticPropLeafLump_t) );
-
- UnserializeModels( buf );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Level init, shutdown
-//-----------------------------------------------------------------------------
-
-void CVradStaticPropMgr::Init()
-{
- CreateInterfaceFn physicsFactory = GetPhysicsFactory();
- if ( !physicsFactory )
- Error( "Unable to load vphysics DLL." );
-
- s_pPhysCollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
- if( !s_pPhysCollision )
- {
- Error( "Unable to get '%s' for physics interface.", VPHYSICS_COLLISION_INTERFACE_VERSION );
- return;
- }
-
- // Read in static props that have been compiled into the bsp file
- UnserializeStaticProps();
-}
-
-void CVradStaticPropMgr::Shutdown()
-{
-
- // Remove all static prop model data
- for (int i = m_StaticPropDict.Size(); --i >= 0; )
- {
- studiohdr_t *pStudioHdr = m_StaticPropDict[i].m_pStudioHdr;
- if ( pStudioHdr )
- {
- if ( pStudioHdr->pVertexBase )
- {
- free( pStudioHdr->pVertexBase );
- }
- free( pStudioHdr );
- }
- }
-
- m_StaticProps.Purge();
- m_StaticPropDict.Purge();
-}
-
-void ComputeLightmapColor( dface_t* pFace, Vector &color )
-{
- texinfo_t* pTex = &texinfo[pFace->texinfo];
- if ( pTex->flags & SURF_SKY )
- {
- // sky ambient already accounted for in direct component
- return;
- }
-}
-
-bool PositionInSolid( Vector &position )
-{
- int ndxLeaf = PointLeafnum( position );
- if ( dleafs[ndxLeaf].contents & CONTENTS_SOLID )
- {
- // position embedded in solid
- return true;
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Trace from a vertex to each direct light source, accumulating its contribution.
-//-----------------------------------------------------------------------------
-void ComputeDirectLightingAtPoint( Vector &position, Vector &normal, Vector &outColor, int iThread,
- int static_prop_id_to_skip=-1, int nLFlags = 0)
-{
- SSE_sampleLightOutput_t sampleOutput;
-
- outColor.Init();
-
- // Iterate over all direct lights and accumulate their contribution
- int cluster = ClusterFromPoint( position );
- for ( directlight_t *dl = activelights; dl != NULL; dl = dl->next )
- {
- if ( dl->light.style )
- {
- // skip lights with style
- continue;
- }
-
- // is this lights cluster visible?
- if ( !PVSCheck( dl->pvs, cluster ) )
- continue;
-
- // push the vertex towards the light to avoid surface acne
- Vector adjusted_pos = position;
- float flEpsilon = 0.0;
-
- if (dl->light.type != emit_skyambient)
- {
- // push towards the light
- Vector fudge;
- if ( dl->light.type == emit_skylight )
- fudge = -( dl->light.normal);
- else
- {
- fudge = dl->light.origin-position;
- VectorNormalize( fudge );
- }
- fudge *= 4.0;
- adjusted_pos += fudge;
- }
- else
- {
- // push out along normal
- adjusted_pos += 4.0 * normal;
-// flEpsilon = 1.0;
- }
-
- FourVectors adjusted_pos4;
- FourVectors normal4;
- adjusted_pos4.DuplicateVector( adjusted_pos );
- normal4.DuplicateVector( normal );
-
- GatherSampleLightSSE( sampleOutput, dl, -1, adjusted_pos4, &normal4, 1, iThread, nLFlags | GATHERLFLAGS_FORCE_FAST,
- static_prop_id_to_skip, flEpsilon );
-
- VectorMA( outColor, sampleOutput.m_flFalloff.m128_f32[0] * sampleOutput.m_flDot[0].m128_f32[0], dl->light.intensity, outColor );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Takes the results from a ComputeLighting call and applies it to the static prop in question.
-//-----------------------------------------------------------------------------
-void CVradStaticPropMgr::ApplyLightingToStaticProp( CStaticProp &prop, const CComputeStaticPropLightingResults *pResults )
-{
- if ( pResults->m_ColorVertsArrays.Count() == 0 )
- return;
-
- StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
- studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
- OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
- Assert( pStudioHdr && pVtxHdr );
-
- int iCurColorVertsArray = 0;
- for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
- {
- OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
- mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
-
- for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
- {
- OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID );
- mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
-
- const CUtlVector<colorVertex_t> &colorVerts = *pResults->m_ColorVertsArrays[iCurColorVertsArray++];
-
- for ( int nLod = 0; nLod < pVtxHdr->numLODs; nLod++ )
- {
- OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod );
-
- for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
- {
- mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh );
- OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh );
-
- for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
- {
- OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup );
- int nMeshIdx = prop.m_MeshData.AddToTail();
- prop.m_MeshData[nMeshIdx].m_Verts.AddMultipleToTail( pStripGroup->numVerts );
- prop.m_MeshData[nMeshIdx].m_nLod = nLod;
-
- for ( int nVertex = 0; nVertex < pStripGroup->numVerts; ++nVertex )
- {
- int nIndex = pMesh->vertexoffset + pStripGroup->pVertex( nVertex )->origMeshVertID;
-
- Assert( nIndex < pStudioModel->numvertices );
- prop.m_MeshData[nMeshIdx].m_Verts[nVertex] = colorVerts[nIndex].m_Color;
- }
- }
- }
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Trace rays from each unique vertex, accumulating direct and indirect
-// sources at each ray termination. Use the winding data to distribute the unique vertexes
-// into the rendering layout.
-//-----------------------------------------------------------------------------
-void CVradStaticPropMgr::ComputeLighting( CStaticProp &prop, int iThread, int prop_index, CComputeStaticPropLightingResults *pResults )
-{
- CUtlVector<badVertex_t> badVerts;
-
- StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
- studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
- OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
- if ( !pStudioHdr || !pVtxHdr )
- {
- // must have model and its verts for lighting computation
- // game will fallback to fullbright
- return;
- }
-
- if (prop.m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING )
- return;
-
- VMPI_SetCurrentStage( "ComputeLighting" );
-
- for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
- {
- mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
-
- for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
- {
- mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
-
- // light all unique vertexes
- CUtlVector<colorVertex_t> *pColorVertsArray = new CUtlVector<colorVertex_t>;
- pResults->m_ColorVertsArrays.AddToTail( pColorVertsArray );
-
- CUtlVector<colorVertex_t> &colorVerts = *pColorVertsArray;
- colorVerts.EnsureCount( pStudioModel->numvertices );
- memset( colorVerts.Base(), 0, colorVerts.Count() * sizeof(colorVertex_t) );
-
- int numVertexes = 0;
- for ( int meshID = 0; meshID < pStudioModel->nummeshes; ++meshID )
- {
- mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( meshID );
- const mstudio_meshvertexdata_t *vertData = pStudioMesh->GetVertexData((void *)pStudioHdr);
- Assert( vertData ); // This can only return NULL on X360 for now
- for ( int vertexID = 0; vertexID < pStudioMesh->numvertices; ++vertexID )
- {
- Vector sampleNormal;
- Vector samplePosition;
- // transform position and normal into world coordinate system
- matrix3x4_t matrix;
- AngleMatrix( prop.m_Angles, prop.m_Origin, matrix );
- VectorTransform( *vertData->Position( vertexID ), matrix, samplePosition );
- AngleMatrix( prop.m_Angles, matrix );
- VectorTransform( *vertData->Normal( vertexID ), matrix, sampleNormal );
-
- if ( PositionInSolid( samplePosition ) )
- {
- // vertex is in solid, add to the bad list, and recover later
- badVertex_t badVertex;
- badVertex.m_ColorVertex = numVertexes;
- badVertex.m_Position = samplePosition;
- badVertex.m_Normal = sampleNormal;
- badVerts.AddToTail( badVertex );
- }
- else
- {
- Vector direct_pos=samplePosition;
- int skip_prop = -1;
- if ( g_bDisablePropSelfShadowing || ( prop.m_Flags & STATIC_PROP_NO_SELF_SHADOWING ) )
- {
- skip_prop = prop_index;
- }
-
- int nFlags = ( prop.m_Flags & STATIC_PROP_IGNORE_NORMALS ) ? GATHERLFLAGS_IGNORE_NORMALS : 0;
-
- Vector directColor(0,0,0);
- ComputeDirectLightingAtPoint( direct_pos,
- sampleNormal, directColor, iThread,
- skip_prop, nFlags );
- Vector indirectColor(0,0,0);
-
- if (g_bShowStaticPropNormals)
- {
- directColor= sampleNormal;
- directColor += Vector(1.0,1.0,1.0);
- directColor *= 50.0;
- }
- else
- {
- if (numbounce >= 1)
- ComputeIndirectLightingAtPoint(
- samplePosition, sampleNormal,
- indirectColor, iThread, true,
- ( prop.m_Flags & STATIC_PROP_IGNORE_NORMALS) != 0 );
- }
-
- colorVerts[numVertexes].m_bValid = true;
- colorVerts[numVertexes].m_Position = samplePosition;
- VectorAdd( directColor, indirectColor, colorVerts[numVertexes].m_Color );
- }
-
- numVertexes++;
- }
- }
-
- // color in the bad vertexes
- // when entire model has no lighting origin and no valid neighbors
- // must punt, leave black coloring
- if ( badVerts.Count() && ( prop.m_bLightingOriginValid || badVerts.Count() != numVertexes ) )
- {
- for ( int nBadVertex = 0; nBadVertex < badVerts.Count(); nBadVertex++ )
- {
- Vector bestPosition;
- if ( prop.m_bLightingOriginValid )
- {
- // use the specified lighting origin
- VectorCopy( prop.m_LightingOrigin, bestPosition );
- }
- else
- {
- // find the closest valid neighbor
- int best = 0;
- float closest = FLT_MAX;
- for ( int nColorVertex = 0; nColorVertex < numVertexes; nColorVertex++ )
- {
- if ( !colorVerts[nColorVertex].m_bValid )
- {
- // skip invalid neighbors
- continue;
- }
- Vector delta;
- VectorSubtract( colorVerts[nColorVertex].m_Position, badVerts[nBadVertex].m_Position, delta );
- float distance = VectorLength( delta );
- if ( distance < closest )
- {
- closest = distance;
- best = nColorVertex;
- }
- }
-
- // use the best neighbor as the direction to crawl
- VectorCopy( colorVerts[best].m_Position, bestPosition );
- }
-
- // crawl toward best position
- // sudivide to determine a closer valid point to the bad vertex, and re-light
- Vector midPosition;
- int numIterations = 20;
- while ( --numIterations > 0 )
- {
- VectorAdd( bestPosition, badVerts[nBadVertex].m_Position, midPosition );
- VectorScale( midPosition, 0.5f, midPosition );
- if ( PositionInSolid( midPosition ) )
- break;
- bestPosition = midPosition;
- }
-
- // re-light from better position
- Vector directColor;
- ComputeDirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normal, directColor, iThread );
-
- Vector indirectColor;
- ComputeIndirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normal,
- indirectColor, iThread, true );
-
- // save results, not changing valid status
- // to ensure this offset position is not considered as a viable candidate
- colorVerts[badVerts[nBadVertex].m_ColorVertex].m_Position = bestPosition;
- VectorAdd( directColor, indirectColor, colorVerts[badVerts[nBadVertex].m_ColorVertex].m_Color );
- }
- }
-
- // discard bad verts
- badVerts.Purge();
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Write the lighitng to bsp pak lump
-//-----------------------------------------------------------------------------
-void CVradStaticPropMgr::SerializeLighting()
-{
- char filename[MAX_PATH];
- CUtlBuffer utlBuf;
-
- // illuminate them all
- int count = m_StaticProps.Count();
- if ( !count )
- {
- // nothing to do
- return;
- }
-
- char mapName[MAX_PATH];
- Q_FileBase( source, mapName, sizeof( mapName ) );
-
- int size;
- for (int i = 0; i < count; ++i)
- {
- // no need to write this file if we didn't compute the data
- // props marked this way will not load the info anyway
- if ( m_StaticProps[i].m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING )
- continue;
-
- if (g_bHDR)
- {
- sprintf( filename, "sp_hdr_%d.vhv", i );
- }
- else
- {
- sprintf( filename, "sp_%d.vhv", i );
- }
-
- int totalVertexes = 0;
- for ( int j=0; j<m_StaticProps[i].m_MeshData.Count(); j++ )
- {
- totalVertexes += m_StaticProps[i].m_MeshData[j].m_Verts.Count();
- }
-
- // allocate a buffer with enough padding for alignment
- size = sizeof( HardwareVerts::FileHeader_t ) +
- m_StaticProps[i].m_MeshData.Count()*sizeof(HardwareVerts::MeshHeader_t) +
- totalVertexes*4 + 2*512;
- utlBuf.EnsureCapacity( size );
- Q_memset( utlBuf.Base(), 0, size );
-
- HardwareVerts::FileHeader_t *pVhvHdr = (HardwareVerts::FileHeader_t *)utlBuf.Base();
-
- // align to start of vertex data
- unsigned char *pVertexData = (unsigned char *)(sizeof( HardwareVerts::FileHeader_t ) + m_StaticProps[i].m_MeshData.Count()*sizeof(HardwareVerts::MeshHeader_t));
- pVertexData = (unsigned char*)pVhvHdr + ALIGN_TO_POW2( (unsigned int)pVertexData, 512 );
-
- // construct header
- pVhvHdr->m_nVersion = VHV_VERSION;
- pVhvHdr->m_nChecksum = m_StaticPropDict[m_StaticProps[i].m_ModelIdx].m_pStudioHdr->checksum;
- pVhvHdr->m_nVertexFlags = VERTEX_COLOR;
- pVhvHdr->m_nVertexSize = 4;
- pVhvHdr->m_nVertexes = totalVertexes;
- pVhvHdr->m_nMeshes = m_StaticProps[i].m_MeshData.Count();
-
- for (int n=0; n<pVhvHdr->m_nMeshes; n++)
- {
- // construct mesh dictionary
- HardwareVerts::MeshHeader_t *pMesh = pVhvHdr->pMesh( n );
- pMesh->m_nLod = m_StaticProps[i].m_MeshData[n].m_nLod;
- pMesh->m_nVertexes = m_StaticProps[i].m_MeshData[n].m_Verts.Count();
- pMesh->m_nOffset = (unsigned int)pVertexData - (unsigned int)pVhvHdr;
-
- // construct vertexes
- for (int k=0; k<pMesh->m_nVertexes; k++)
- {
- Vector &vector = m_StaticProps[i].m_MeshData[n].m_Verts[k];
-
- ColorRGBExp32 rgbColor;
- VectorToColorRGBExp32( vector, rgbColor );
- unsigned char dstColor[4];
- ConvertRGBExp32ToRGBA8888( &rgbColor, dstColor );
-
- // b,g,r,a order
- pVertexData[0] = dstColor[2];
- pVertexData[1] = dstColor[1];
- pVertexData[2] = dstColor[0];
- pVertexData[3] = dstColor[3];
- pVertexData += 4;
- }
- }
-
- // align to end of file
- pVertexData = (unsigned char *)((unsigned int)pVertexData - (unsigned int)pVhvHdr);
- pVertexData = (unsigned char*)pVhvHdr + ALIGN_TO_POW2( (unsigned int)pVertexData, 512 );
-
- AddBufferToPak( GetPakFile(), filename, (void*)pVhvHdr, pVertexData - (unsigned char*)pVhvHdr, false );
- }
-}
-
-void CVradStaticPropMgr::VMPI_ProcessStaticProp_Static( int iThread, uint64 iStaticProp, MessageBuffer *pBuf )
-{
- g_StaticPropMgr.VMPI_ProcessStaticProp( iThread, iStaticProp, pBuf );
-}
-
-void CVradStaticPropMgr::VMPI_ReceiveStaticPropResults_Static( uint64 iStaticProp, MessageBuffer *pBuf, int iWorker )
-{
- g_StaticPropMgr.VMPI_ReceiveStaticPropResults( iStaticProp, pBuf, iWorker );
-}
-
-//-----------------------------------------------------------------------------
-// Called on workers to do the computation for a static prop and send
-// it to the master.
-//-----------------------------------------------------------------------------
-void CVradStaticPropMgr::VMPI_ProcessStaticProp( int iThread, int iStaticProp, MessageBuffer *pBuf )
-{
- // Compute the lighting.
- CComputeStaticPropLightingResults results;
- ComputeLighting( m_StaticProps[iStaticProp], iThread, iStaticProp, &results );
-
- VMPI_SetCurrentStage( "EncodeLightingResults" );
-
- // Encode the results.
- int nLists = results.m_ColorVertsArrays.Count();
- pBuf->write( &nLists, sizeof( nLists ) );
-
- for ( int i=0; i < nLists; i++ )
- {
- CUtlVector<colorVertex_t> &curList = *results.m_ColorVertsArrays[i];
- int count = curList.Count();
- pBuf->write( &count, sizeof( count ) );
- pBuf->write( curList.Base(), curList.Count() * sizeof( colorVertex_t ) );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Called on the master when a worker finishes processing a static prop.
-//-----------------------------------------------------------------------------
-void CVradStaticPropMgr::VMPI_ReceiveStaticPropResults( int iStaticProp, MessageBuffer *pBuf, int iWorker )
-{
- // Read in the results.
- CComputeStaticPropLightingResults results;
-
- int nLists;
- pBuf->read( &nLists, sizeof( nLists ) );
-
- for ( int i=0; i < nLists; i++ )
- {
- CUtlVector<colorVertex_t> *pList = new CUtlVector<colorVertex_t>;
- results.m_ColorVertsArrays.AddToTail( pList );
-
- int count;
- pBuf->read( &count, sizeof( count ) );
- pList->SetSize( count );
- pBuf->read( pList->Base(), count * sizeof( colorVertex_t ) );
- }
-
- // Apply the results.
- ApplyLightingToStaticProp( m_StaticProps[iStaticProp], &results );
-}
-
-
-void CVradStaticPropMgr::ComputeLightingForProp( int iThread, int iStaticProp )
-{
- // Compute the lighting.
- CComputeStaticPropLightingResults results;
- ComputeLighting( m_StaticProps[iStaticProp], iThread, iStaticProp, &results );
- ApplyLightingToStaticProp( m_StaticProps[iStaticProp], &results );
-}
-
-void CVradStaticPropMgr::ThreadComputeStaticPropLighting( int iThread, void *pUserData )
-{
- while (1)
- {
- int j = GetThreadWork ();
- if (j == -1)
- break;
- CComputeStaticPropLightingResults results;
- g_StaticPropMgr.ComputeLightingForProp( iThread, j );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Computes lighting for the static props.
-// Must be after all other surface lighting has been computed for the indirect sampling.
-//-----------------------------------------------------------------------------
-void CVradStaticPropMgr::ComputeLighting( int iThread )
-{
- // illuminate them all
- int count = m_StaticProps.Count();
- if ( !count )
- {
- // nothing to do
- return;
- }
-
- StartPacifier( "Computing static prop lighting : " );
-
- // ensure any traces against us are ignored because we have no inherit lighting contribution
- m_bIgnoreStaticPropTrace = true;
-
- if ( g_bUseMPI )
- {
- // Distribute the work among the workers.
- VMPI_SetCurrentStage( "CVradStaticPropMgr::ComputeLighting" );
-
- DistributeWork(
- count,
- VMPI_DISTRIBUTEWORK_PACKETID,
- &CVradStaticPropMgr::VMPI_ProcessStaticProp_Static,
- &CVradStaticPropMgr::VMPI_ReceiveStaticPropResults_Static );
- }
- else
- {
- RunThreadsOn(count, true, ThreadComputeStaticPropLighting);
- }
-
- // restore default
- m_bIgnoreStaticPropTrace = false;
-
- // save data to bsp
- SerializeLighting();
-
- EndPacifier( true );
-}
-
-//-----------------------------------------------------------------------------
-// Adds all static prop polys to the ray trace store.
-//-----------------------------------------------------------------------------
-void CVradStaticPropMgr::AddPolysForRayTrace( void )
-{
- int count = m_StaticProps.Count();
- if ( !count )
- {
- // nothing to do
- return;
- }
-
- // Triangle coverage of 1 (full coverage)
- Vector fullCoverage;
- fullCoverage.x = 1.0f;
-
- for ( int nProp = 0; nProp < count; ++nProp )
- {
- CStaticProp &prop = m_StaticProps[nProp];
- StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
-
- if ( prop.m_Flags & STATIC_PROP_NO_SHADOW )
- continue;
-
- // If not using static prop polys, use AABB
- if ( !g_bStaticPropPolys )
- {
- if ( dict.m_pModel )
- {
- VMatrix xform;
- xform.SetupMatrixOrgAngles ( prop.m_Origin, prop.m_Angles );
- ICollisionQuery *queryModel = s_pPhysCollision->CreateQueryModel( dict.m_pModel );
- for ( int nConvex = 0; nConvex < queryModel->ConvexCount(); ++nConvex )
- {
- for ( int nTri = 0; nTri < queryModel->TriangleCount( nConvex ); ++nTri )
- {
- Vector verts[3];
- queryModel->GetTriangleVerts( nConvex, nTri, verts );
- for ( int nVert = 0; nVert < 3; ++nVert )
- verts[nVert] = xform.VMul4x3(verts[nVert]);
- g_RtEnv.AddTriangle ( TRACE_ID_STATICPROP | nProp, verts[0], verts[1], verts[2], fullCoverage );
- }
- }
- s_pPhysCollision->DestroyQueryModel( queryModel );
- }
- else
- {
- VectorAdd ( dict.m_Mins, prop.m_Origin, prop.m_mins );
- VectorAdd ( dict.m_Maxs, prop.m_Origin, prop.m_maxs );
- g_RtEnv.AddAxisAlignedRectangularSolid ( TRACE_ID_STATICPROP | nProp, prop.m_mins, prop.m_maxs, fullCoverage );
- }
-
- continue;
- }
-
- studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
- OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
- if ( !pStudioHdr || !pVtxHdr )
- {
- // must have model and its verts for decoding triangles
- return;
- }
- // only init the triangle table the first time
- bool bInitTriangles = dict.m_triangleMaterialIndex.Count() ? false : true;
- int triangleIndex = 0;
-
- // meshes are deeply hierarchial, divided between three stores, follow the white rabbit
- // body parts -> models -> lod meshes -> strip groups -> strips
- // the vertices and indices are pooled, the trick is knowing the offset to determine your indexed base
- for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
- {
- OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
- mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
-
- for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
- {
- OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID );
- mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
-
- // assuming lod 0, could iterate if required
- int nLod = 0;
- OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod );
-
- for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
- {
- // check if this mesh's material is in the no shadow material name list
- mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh );
- mstudiotexture_t *pTxtr=pStudioHdr->pTexture(pMesh->material);
- //printf("mat idx=%d mat name=%s\n",pMesh->material,pTxtr->pszName());
- bool bSkipThisMesh = false;
- for(int check=0; check<g_NonShadowCastingMaterialStrings.Count(); check++)
- {
- if ( Q_stristr( pTxtr->pszName(),
- g_NonShadowCastingMaterialStrings[check] ) )
- {
- //printf("skip mat name=%s\n",pTxtr->pszName());
- bSkipThisMesh = true;
- break;
- }
- }
- if ( bSkipThisMesh)
- continue;
-
- int shadowTextureIndex = -1;
- if ( dict.m_textureShadowIndex.Count() )
- {
- shadowTextureIndex = dict.m_textureShadowIndex[pMesh->material];
- }
-
-
- OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh );
- const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( (void *)pStudioHdr );
- Assert( vertData ); // This can only return NULL on X360 for now
-
- for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
- {
- OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup );
-
- int nStrip;
- for ( nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
- {
- OptimizedModel::StripHeader_t *pStrip = pStripGroup->pStrip( nStrip );
-
- if ( pStrip->flags & OptimizedModel::STRIP_IS_TRILIST )
- {
- for ( int i = 0; i < pStrip->numIndices; i += 3 )
- {
- int idx = pStrip->indexOffset + i;
-
- unsigned short i1 = *pStripGroup->pIndex( idx );
- unsigned short i2 = *pStripGroup->pIndex( idx + 1 );
- unsigned short i3 = *pStripGroup->pIndex( idx + 2 );
-
- int vertex1 = pStripGroup->pVertex( i1 )->origMeshVertID;
- int vertex2 = pStripGroup->pVertex( i2 )->origMeshVertID;
- int vertex3 = pStripGroup->pVertex( i3 )->origMeshVertID;
-
- // transform position into world coordinate system
- matrix3x4_t matrix;
- AngleMatrix( prop.m_Angles, prop.m_Origin, matrix );
-
- Vector position1;
- Vector position2;
- Vector position3;
- VectorTransform( *vertData->Position( vertex1 ), matrix, position1 );
- VectorTransform( *vertData->Position( vertex2 ), matrix, position2 );
- VectorTransform( *vertData->Position( vertex3 ), matrix, position3 );
- unsigned short flags = 0;
- int materialIndex = -1;
- Vector color = vec3_origin;
- if ( shadowTextureIndex >= 0 )
- {
- if ( bInitTriangles )
- {
- // add texture space and texture index to material database
- // now
- float coverage = g_ShadowTextureList.ComputeCoverageForTriangle(shadowTextureIndex, *vertData->Texcoord(vertex1), *vertData->Texcoord(vertex2), *vertData->Texcoord(vertex3) );
- if ( coverage < 1.0f )
- {
- materialIndex = g_ShadowTextureList.AddMaterialEntry( shadowTextureIndex, *vertData->Texcoord(vertex1), *vertData->Texcoord(vertex2), *vertData->Texcoord(vertex3) );
- color.x = coverage;
- }
- else
- {
- materialIndex = -1;
- }
- dict.m_triangleMaterialIndex.AddToTail(materialIndex);
- }
- else
- {
- materialIndex = dict.m_triangleMaterialIndex[triangleIndex];
- triangleIndex++;
- }
- if ( materialIndex >= 0 )
- {
- flags = FCACHETRI_TRANSPARENT;
- }
- }
-// printf( "\ngl 3\n" );
-// printf( "gl %6.3f %6.3f %6.3f 1 0 0\n", XYZ(position1));
-// printf( "gl %6.3f %6.3f %6.3f 0 1 0\n", XYZ(position2));
-// printf( "gl %6.3f %6.3f %6.3f 0 0 1\n", XYZ(position3));
- g_RtEnv.AddTriangle( TRACE_ID_STATICPROP | nProp,
- position1, position2, position3,
- color, flags, materialIndex);
- }
- }
- else
- {
- // all tris expected to be discrete tri lists
- // must fixme if stripping ever occurs
- printf( "unexpected strips found\n" );
- Assert( 0 );
- return;
- }
- }
- }
- }
- }
- }
- }
-}
-
-struct tl_tri_t
-{
- Vector p0;
- Vector p1;
- Vector p2;
- Vector n0;
- Vector n1;
- Vector n2;
-
- bool operator == (const tl_tri_t &t) const
- {
- return ( p0 == t.p0 &&
- p1 == t.p1 &&
- p2 == t.p2 &&
- n0 == t.n0 &&
- n1 == t.n1 &&
- n2 == t.n2 );
- }
-};
-
-struct tl_vert_t
-{
- Vector m_position;
- CUtlLinkedList< tl_tri_t, int > m_triList;
-};
-
-void AddTriVertsToList( CUtlVector< tl_vert_t > &triListVerts, int vertIndex, Vector vertPosition, Vector p0, Vector p1, Vector p2, Vector n0, Vector n1, Vector n2 )
-{
- tl_tri_t tlTri;
-
- tlTri.p0 = p0;
- tlTri.p1 = p1;
- tlTri.p2 = p2;
- tlTri.n0 = n0;
- tlTri.n1 = n1;
- tlTri.n2 = n2;
-
- triListVerts.EnsureCapacity( vertIndex+1 );
-
- triListVerts[vertIndex].m_position = vertPosition;
-
- int index = triListVerts[vertIndex].m_triList.Find( tlTri );
- if ( !triListVerts[vertIndex].m_triList.IsValidIndex( index ) )
- {
- // not in list, add to list of triangles
- triListVerts[vertIndex].m_triList.AddToTail( tlTri );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Builds a list of tris for every vertex
-//-----------------------------------------------------------------------------
-void CVradStaticPropMgr::BuildTriList( CStaticProp &prop )
-{
- // the generated list will consist of a list of verts
- // each vert will have a linked list of triangles that it belongs to
- CUtlVector< tl_vert_t > triListVerts;
-
- StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
- studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
- OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
- if ( !pStudioHdr || !pVtxHdr )
- {
- // must have model and its verts for decoding triangles
- return;
- }
-
- // meshes are deeply hierarchial, divided between three stores, follow the white rabbit
- // body parts -> models -> lod meshes -> strip groups -> strips
- // the vertices and indices are pooled, the trick is knowing the offset to determine your indexed base
- for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
- {
- OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
- mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
-
- for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
- {
- OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID );
- mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
-
- // get the specified lod, assuming lod 0
- int nLod = 0;
- OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod );
-
- // must reset because each model has their own vertexes [0..n]
- // in order for this to be monolithic for the entire prop the list must be segmented
- triListVerts.Purge();
-
- for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
- {
- mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh );
- OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh );
- const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( (void *)pStudioHdr );
- Assert( vertData ); // This can only return NULL on X360 for now
-
- for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
- {
- OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup );
-
- int nStrip;
- for ( nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
- {
- OptimizedModel::StripHeader_t *pStrip = pStripGroup->pStrip( nStrip );
-
- if ( pStrip->flags & OptimizedModel::STRIP_IS_TRILIST )
- {
- for ( int i = 0; i < pStrip->numIndices; i += 3 )
- {
- int idx = pStrip->indexOffset + i;
-
- unsigned short i1 = *pStripGroup->pIndex( idx );
- unsigned short i2 = *pStripGroup->pIndex( idx + 1 );
- unsigned short i3 = *pStripGroup->pIndex( idx + 2 );
-
- int vertex1 = pStripGroup->pVertex( i1 )->origMeshVertID;
- int vertex2 = pStripGroup->pVertex( i2 )->origMeshVertID;
- int vertex3 = pStripGroup->pVertex( i3 )->origMeshVertID;
-
- // transform position into world coordinate system
- matrix3x4_t matrix;
- AngleMatrix( prop.m_Angles, prop.m_Origin, matrix );
-
- Vector position1;
- Vector position2;
- Vector position3;
- VectorTransform( *vertData->Position( vertex1 ), matrix, position1 );
- VectorTransform( *vertData->Position( vertex2 ), matrix, position2 );
- VectorTransform( *vertData->Position( vertex3 ), matrix, position3 );
-
- Vector normal1;
- Vector normal2;
- Vector normal3;
- VectorTransform( *vertData->Normal( vertex1 ), matrix, normal1 );
- VectorTransform( *vertData->Normal( vertex2 ), matrix, normal2 );
- VectorTransform( *vertData->Normal( vertex3 ), matrix, normal3 );
-
- AddTriVertsToList( triListVerts, pMesh->vertexoffset + vertex1, position1, position1, position2, position3, normal1, normal2, normal3 );
- AddTriVertsToList( triListVerts, pMesh->vertexoffset + vertex2, position2, position1, position2, position3, normal1, normal2, normal3 );
- AddTriVertsToList( triListVerts, pMesh->vertexoffset + vertex3, position3, position1, position2, position3, normal1, normal2, normal3 );
- }
- }
- else
- {
- // all tris expected to be discrete tri lists
- // must fixme if stripping ever occurs
- printf( "unexpected strips found\n" );
- Assert( 0 );
- return;
- }
- }
- }
- }
- }
- }
-}
-
-const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void *pModelData )
-{
- studiohdr_t *pActiveStudioHdr = static_cast<studiohdr_t *>(pModelData);
- Assert( pActiveStudioHdr );
-
- if ( pActiveStudioHdr->pVertexBase )
- {
- return (vertexFileHeader_t *)pActiveStudioHdr->pVertexBase;
- }
-
- // mandatory callback to make requested data resident
- // load and persist the vertex file
- char fileName[MAX_PATH];
- strcpy( fileName, "models/" );
- strcat( fileName, pActiveStudioHdr->pszName() );
- Q_StripExtension( fileName, fileName, sizeof( fileName ) );
- strcat( fileName, ".vvd" );
-
- // load the model
- FileHandle_t fileHandle = g_pFileSystem->Open( fileName, "rb" );
- if ( !fileHandle )
- {
- Error( "Unable to load vertex data \"%s\"\n", fileName );
- }
-
- // Get the file size
- int vvdSize = g_pFileSystem->Size( fileHandle );
- if ( vvdSize == 0 )
- {
- g_pFileSystem->Close( fileHandle );
- Error( "Bad size for vertex data \"%s\"\n", fileName );
- }
-
- vertexFileHeader_t *pVvdHdr = (vertexFileHeader_t *)malloc( vvdSize );
- g_pFileSystem->Read( pVvdHdr, vvdSize, fileHandle );
- g_pFileSystem->Close( fileHandle );
-
- // check header
- if ( pVvdHdr->id != MODEL_VERTEX_FILE_ID )
- {
- Error("Error Vertex File %s id %d should be %d\n", fileName, pVvdHdr->id, MODEL_VERTEX_FILE_ID);
- }
- if ( pVvdHdr->version != MODEL_VERTEX_FILE_VERSION )
- {
- Error("Error Vertex File %s version %d should be %d\n", fileName, pVvdHdr->version, MODEL_VERTEX_FILE_VERSION);
- }
- if ( pVvdHdr->checksum != pActiveStudioHdr->checksum )
- {
- Error("Error Vertex File %s checksum %d should be %d\n", fileName, pVvdHdr->checksum, pActiveStudioHdr->checksum);
- }
-
- // need to perform mesh relocation fixups
- // allocate a new copy
- vertexFileHeader_t *pNewVvdHdr = (vertexFileHeader_t *)malloc( vvdSize );
- if ( !pNewVvdHdr )
- {
- Error( "Error allocating %d bytes for Vertex File '%s'\n", vvdSize, fileName );
- }
-
- // load vertexes and run fixups
- Studio_LoadVertexes( pVvdHdr, pNewVvdHdr, 0, true );
-
- // discard original
- free( pVvdHdr );
- pVvdHdr = pNewVvdHdr;
-
- pActiveStudioHdr->pVertexBase = (void*)pVvdHdr;
- return pVvdHdr;
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Revision: $
+// $NoKeywords: $
+//
+// This file contains code to allow us to associate client data with bsp leaves.
+//
+//=============================================================================//
+
+#include "vrad.h"
+#include "mathlib/vector.h"
+#include "UtlBuffer.h"
+#include "utlvector.h"
+#include "GameBSPFile.h"
+#include "BSPTreeData.h"
+#include "VPhysics_Interface.h"
+#include "Studio.h"
+#include "Optimize.h"
+#include "Bsplib.h"
+#include "CModel.h"
+#include "PhysDll.h"
+#include "phyfile.h"
+#include "collisionutils.h"
+#include "tier1/KeyValues.h"
+#include "pacifier.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/hardwareverts.h"
+#include "byteswap.h"
+#include "mpivrad.h"
+#include "vtf/vtf.h"
+#include "tier1/utldict.h"
+#include "tier1/utlsymbol.h"
+
+#include "messbuf.h"
+#include "vmpi.h"
+#include "vmpi_distribute_work.h"
+
+
+#define ALIGN_TO_POW2(x,y) (((x)+(y-1))&~(y-1))
+
+// identifies a vertex embedded in solid
+// lighting will be copied from nearest valid neighbor
+struct badVertex_t
+{
+ int m_ColorVertex;
+ Vector m_Position;
+ Vector m_Normal;
+};
+
+// a final colored vertex
+struct colorVertex_t
+{
+ Vector m_Color;
+ Vector m_Position;
+ bool m_bValid;
+};
+
+class CComputeStaticPropLightingResults
+{
+public:
+ ~CComputeStaticPropLightingResults()
+ {
+ m_ColorVertsArrays.PurgeAndDeleteElements();
+ }
+
+ CUtlVector< CUtlVector<colorVertex_t>* > m_ColorVertsArrays;
+};
+
+//-----------------------------------------------------------------------------
+// Globals
+//-----------------------------------------------------------------------------
+CUtlSymbolTable g_ForcedTextureShadowsModels;
+
+// DON'T USE THIS FROM WITHIN A THREAD. THERE IS A THREAD CONTEXT CREATED
+// INSIDE PropTested_t. USE THAT INSTEAD.
+IPhysicsCollision *s_pPhysCollision = NULL;
+
+//-----------------------------------------------------------------------------
+// Vrad's static prop manager
+//-----------------------------------------------------------------------------
+
+class CVradStaticPropMgr : public IVradStaticPropMgr
+{
+public:
+ // constructor, destructor
+ CVradStaticPropMgr();
+ virtual ~CVradStaticPropMgr();
+
+ // methods of IStaticPropMgr
+ void Init();
+ void Shutdown();
+
+ // iterate all the instanced static props and compute their vertex lighting
+ void ComputeLighting( int iThread );
+
+private:
+ // VMPI stuff.
+ static void VMPI_ProcessStaticProp_Static( int iThread, uint64 iStaticProp, MessageBuffer *pBuf );
+ static void VMPI_ReceiveStaticPropResults_Static( uint64 iStaticProp, MessageBuffer *pBuf, int iWorker );
+ void VMPI_ProcessStaticProp( int iThread, int iStaticProp, MessageBuffer *pBuf );
+ void VMPI_ReceiveStaticPropResults( int iStaticProp, MessageBuffer *pBuf, int iWorker );
+
+ // local thread version
+ static void ThreadComputeStaticPropLighting( int iThread, void *pUserData );
+ void ComputeLightingForProp( int iThread, int iStaticProp );
+
+ // Methods associated with unserializing static props
+ void UnserializeModelDict( CUtlBuffer& buf );
+ void UnserializeModels( CUtlBuffer& buf );
+ void UnserializeStaticProps();
+
+ // Creates a collision model
+ void CreateCollisionModel( char const* pModelName );
+
+private:
+ // Unique static prop models
+ struct StaticPropDict_t
+ {
+ vcollide_t m_loadedModel;
+ CPhysCollide* m_pModel;
+ Vector m_Mins; // Bounding box is in local coordinates
+ Vector m_Maxs;
+ studiohdr_t* m_pStudioHdr;
+ CUtlBuffer m_VtxBuf;
+ CUtlVector<int> m_textureShadowIndex; // each texture has an index if this model casts texture shadows
+ CUtlVector<int> m_triangleMaterialIndex;// each triangle has an index if this model casts texture shadows
+ };
+
+ struct MeshData_t
+ {
+ CUtlVector<Vector> m_Verts;
+ int m_nLod;
+ };
+
+ // A static prop instance
+ struct CStaticProp
+ {
+ Vector m_Origin;
+ QAngle m_Angles;
+ Vector m_mins;
+ Vector m_maxs;
+ Vector m_LightingOrigin;
+ int m_ModelIdx;
+ BSPTreeDataHandle_t m_Handle;
+ CUtlVector<MeshData_t> m_MeshData;
+ int m_Flags;
+ bool m_bLightingOriginValid;
+ };
+
+ // Enumeration context
+ struct EnumContext_t
+ {
+ PropTested_t* m_pPropTested;
+ Ray_t const* m_pRay;
+ };
+
+ // The list of all static props
+ CUtlVector <StaticPropDict_t> m_StaticPropDict;
+ CUtlVector <CStaticProp> m_StaticProps;
+
+ bool m_bIgnoreStaticPropTrace;
+
+ void ComputeLighting( CStaticProp &prop, int iThread, int prop_index, CComputeStaticPropLightingResults *pResults );
+ void ApplyLightingToStaticProp( CStaticProp &prop, const CComputeStaticPropLightingResults *pResults );
+
+ void SerializeLighting();
+ void AddPolysForRayTrace();
+ void BuildTriList( CStaticProp &prop );
+};
+
+
+//-----------------------------------------------------------------------------
+// Expose IVradStaticPropMgr to vrad
+//-----------------------------------------------------------------------------
+
+static CVradStaticPropMgr g_StaticPropMgr;
+IVradStaticPropMgr* StaticPropMgr()
+{
+ return &g_StaticPropMgr;
+}
+
+
+//-----------------------------------------------------------------------------
+// constructor, destructor
+//-----------------------------------------------------------------------------
+
+CVradStaticPropMgr::CVradStaticPropMgr()
+{
+ // set to ignore static prop traces
+ m_bIgnoreStaticPropTrace = false;
+}
+
+CVradStaticPropMgr::~CVradStaticPropMgr()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Makes sure the studio model is a static prop
+//-----------------------------------------------------------------------------
+
+bool IsStaticProp( studiohdr_t* pHdr )
+{
+ if (!(pHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP))
+ return false;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Load a file into a Utlbuf
+//-----------------------------------------------------------------------------
+static bool LoadFile( char const* pFileName, CUtlBuffer& buf )
+{
+ if ( !g_pFullFileSystem )
+ return false;
+
+ return g_pFullFileSystem->ReadFile( pFileName, NULL, buf );
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructs the file name from the model name
+//-----------------------------------------------------------------------------
+static char const* ConstructFileName( char const* pModelName )
+{
+ static char buf[1024];
+ sprintf( buf, "%s%s", gamedir, pModelName );
+ return buf;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes a convex hull from a studio mesh
+//-----------------------------------------------------------------------------
+static CPhysConvex* ComputeConvexHull( mstudiomesh_t* pMesh, studiohdr_t *pStudioHdr )
+{
+ const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( (void *)pStudioHdr );
+ Assert( vertData ); // This can only return NULL on X360 for now
+
+ // Generate a list of all verts in the mesh
+ Vector** ppVerts = (Vector**)_alloca(pMesh->numvertices * sizeof(Vector*) );
+ for (int i = 0; i < pMesh->numvertices; ++i)
+ {
+ ppVerts[i] = vertData->Position(i);
+ }
+
+ // Generate a convex hull from the verts
+ return s_pPhysCollision->ConvexFromVerts( ppVerts, pMesh->numvertices );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes a convex hull from the studio model
+//-----------------------------------------------------------------------------
+CPhysCollide* ComputeConvexHull( studiohdr_t* pStudioHdr )
+{
+ CUtlVector<CPhysConvex*> convexHulls;
+
+ for (int body = 0; body < pStudioHdr->numbodyparts; ++body )
+ {
+ mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( body );
+ for( int model = 0; model < pBodyPart->nummodels; ++model )
+ {
+ mstudiomodel_t *pStudioModel = pBodyPart->pModel( model );
+ for( int mesh = 0; mesh < pStudioModel->nummeshes; ++mesh )
+ {
+ // Make a convex hull for each mesh
+ // NOTE: This won't work unless the model has been compiled
+ // with $staticprop
+ mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( mesh );
+ convexHulls.AddToTail( ComputeConvexHull( pStudioMesh, pStudioHdr ) );
+ }
+ }
+ }
+
+ // Convert an array of convex elements to a compiled collision model
+ // (this deletes the convex elements)
+ return s_pPhysCollision->ConvertConvexToCollide( convexHulls.Base(), convexHulls.Size() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Load studio model vertex data from a file...
+//-----------------------------------------------------------------------------
+
+bool LoadStudioModel( char const* pModelName, CUtlBuffer& buf )
+{
+ // No luck, gotta build it
+ // Construct the file name...
+ if (!LoadFile( pModelName, buf ))
+ {
+ Warning("Error! Unable to load model \"%s\"\n", pModelName );
+ return false;
+ }
+
+ // Check that it's valid
+ if (strncmp ((const char *) buf.PeekGet(), "IDST", 4) &&
+ strncmp ((const char *) buf.PeekGet(), "IDAG", 4))
+ {
+ Warning("Error! Invalid model file \"%s\"\n", pModelName );
+ return false;
+ }
+
+ studiohdr_t* pHdr = (studiohdr_t*)buf.PeekGet();
+
+ Studio_ConvertStudioHdrToNewVersion( pHdr );
+
+ if (pHdr->version != STUDIO_VERSION)
+ {
+ Warning("Error! Invalid model version \"%s\"\n", pModelName );
+ return false;
+ }
+
+ if (!IsStaticProp(pHdr))
+ {
+ Warning("Error! To use model \"%s\"\n"
+ " as a static prop, it must be compiled with $staticprop!\n", pModelName );
+ return false;
+ }
+
+ // ensure reset
+ pHdr->pVertexBase = NULL;
+ pHdr->pIndexBase = NULL;
+
+ return true;
+}
+
+bool LoadStudioCollisionModel( char const* pModelName, CUtlBuffer& buf )
+{
+ char tmp[1024];
+ Q_strncpy( tmp, pModelName, sizeof( tmp ) );
+ Q_SetExtension( tmp, ".phy", sizeof( tmp ) );
+ // No luck, gotta build it
+ if (!LoadFile( tmp, buf ))
+ {
+ // this is not an error, the model simply has no PHY file
+ return false;
+ }
+
+ phyheader_t *header = (phyheader_t *)buf.PeekGet();
+
+ if ( header->size != sizeof(*header) || header->solidCount <= 0 )
+ return false;
+
+ return true;
+}
+
+bool LoadVTXFile( char const* pModelName, const studiohdr_t *pStudioHdr, CUtlBuffer& buf )
+{
+ char filename[MAX_PATH];
+
+ // construct filename
+ Q_StripExtension( pModelName, filename, sizeof( filename ) );
+ strcat( filename, ".dx80.vtx" );
+
+ if ( !LoadFile( filename, buf ) )
+ {
+ Warning( "Error! Unable to load file \"%s\"\n", filename );
+ return false;
+ }
+
+ OptimizedModel::FileHeader_t* pVtxHdr = (OptimizedModel::FileHeader_t *)buf.Base();
+
+ // Check that it's valid
+ if ( pVtxHdr->version != OPTIMIZED_MODEL_FILE_VERSION )
+ {
+ Warning( "Error! Invalid VTX file version: %d, expected %d \"%s\"\n", pVtxHdr->version, OPTIMIZED_MODEL_FILE_VERSION, filename );
+ return false;
+ }
+ if ( pVtxHdr->checkSum != pStudioHdr->checksum )
+ {
+ Warning( "Error! Invalid VTX file checksum: %d, expected %d \"%s\"\n", pVtxHdr->checkSum, pStudioHdr->checksum, filename );
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Gets a vertex position from a strip index
+//-----------------------------------------------------------------------------
+inline static Vector* PositionFromIndex( const mstudio_meshvertexdata_t *vertData, mstudiomesh_t* pMesh, OptimizedModel::StripGroupHeader_t* pStripGroup, int i )
+{
+ OptimizedModel::Vertex_t* pVert = pStripGroup->pVertex( i );
+ return vertData->Position( pVert->origMeshVertID );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Writes a glview text file containing the collision surface in question
+// Input : *pCollide -
+// *pFilename -
+//-----------------------------------------------------------------------------
+void DumpCollideToGlView( vcollide_t *pCollide, const char *pFilename )
+{
+ if ( !pCollide )
+ return;
+
+ Msg("Writing %s...\n", pFilename );
+
+ FILE *fp = fopen( pFilename, "w" );
+ for (int i = 0; i < pCollide->solidCount; ++i)
+ {
+ Vector *outVerts;
+ int vertCount = s_pPhysCollision->CreateDebugMesh( pCollide->solids[i], &outVerts );
+ int triCount = vertCount / 3;
+ int vert = 0;
+
+ unsigned char r = (i & 1) * 64 + 64;
+ unsigned char g = (i & 2) * 64 + 64;
+ unsigned char b = (i & 4) * 64 + 64;
+
+ float fr = r / 255.0f;
+ float fg = g / 255.0f;
+ float fb = b / 255.0f;
+
+ for ( int i = 0; i < triCount; i++ )
+ {
+ fprintf( fp, "3\n" );
+ fprintf( fp, "%6.3f %6.3f %6.3f %.2f %.3f %.3f\n",
+ outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fr, fg, fb );
+ vert++;
+ fprintf( fp, "%6.3f %6.3f %6.3f %.2f %.3f %.3f\n",
+ outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fr, fg, fb );
+ vert++;
+ fprintf( fp, "%6.3f %6.3f %6.3f %.2f %.3f %.3f\n",
+ outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fr, fg, fb );
+ vert++;
+ }
+ s_pPhysCollision->DestroyDebugMesh( vertCount, outVerts );
+ }
+ fclose( fp );
+}
+
+
+static bool PointInTriangle( const Vector2D &p, const Vector2D &v0, const Vector2D &v1, const Vector2D &v2 )
+{
+ float coords[3];
+ GetBarycentricCoords2D( v0, v1, v2, p, coords );
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( coords[i] < 0.0f || coords[i] > 1.0f )
+ return false;
+ }
+ float sum = coords[0] + coords[1] + coords[2];
+ if ( sum > 1.0f )
+ return false;
+ return true;
+}
+
+bool LoadFileIntoBuffer( CUtlBuffer &buf, const char *pFilename )
+{
+ FileHandle_t fileHandle = g_pFileSystem->Open( pFilename, "rb" );
+ if ( !fileHandle )
+ return false;
+
+ // Get the file size
+ int texSize = g_pFileSystem->Size( fileHandle );
+ buf.EnsureCapacity( texSize );
+ int nBytesRead = g_pFileSystem->Read( buf.Base(), texSize, fileHandle );
+ g_pFileSystem->Close( fileHandle );
+ buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
+ buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ return true;
+}
+
+// keeps a list of all textures that cast shadows via alpha channel
+class CShadowTextureList
+{
+public:
+ // This loads a vtf and converts it to RGB8888 format
+ unsigned char *LoadVTFRGB8888( const char *pName, int *pWidth, int *pHeight, bool *pClampU, bool *pClampV )
+ {
+ char szPath[MAX_PATH];
+ Q_strncpy( szPath, "materials/", sizeof( szPath ) );
+ Q_strncat( szPath, pName, sizeof( szPath ), COPY_ALL_CHARACTERS );
+ Q_strncat( szPath, ".vtf", sizeof( szPath ), COPY_ALL_CHARACTERS );
+ Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
+
+ CUtlBuffer buf;
+ if ( !LoadFileIntoBuffer( buf, szPath ) )
+ return NULL;
+ IVTFTexture *pTex = CreateVTFTexture();
+ if (!pTex->Unserialize( buf ))
+ return NULL;
+ Msg("Loaded alpha texture %s\n", szPath );
+ unsigned char *pSrcImage = pTex->ImageData( 0, 0, 0, 0, 0, 0 );
+ int iWidth = pTex->Width();
+ int iHeight = pTex->Height();
+ ImageFormat dstFormat = IMAGE_FORMAT_RGBA8888;
+ ImageFormat srcFormat = pTex->Format();
+ *pClampU = (pTex->Flags() & TEXTUREFLAGS_CLAMPS) ? true : false;
+ *pClampV = (pTex->Flags() & TEXTUREFLAGS_CLAMPT) ? true : false;
+ unsigned char *pDstImage = new unsigned char[ImageLoader::GetMemRequired( iWidth, iHeight, 1, dstFormat, false )];
+
+ if( !ImageLoader::ConvertImageFormat( pSrcImage, srcFormat,
+ pDstImage, dstFormat, iWidth, iHeight, 0, 0 ) )
+ {
+ delete[] pDstImage;
+ return NULL;
+ }
+
+ *pWidth = iWidth;
+ *pHeight = iHeight;
+ return pDstImage;
+ }
+
+ // Checks the database for the material and loads if necessary
+ // returns true if found and pIndex will be the index, -1 if no alpha shadows
+ bool FindOrLoadIfValid( const char *pMaterialName, int *pIndex )
+ {
+ *pIndex = -1;
+ int index = m_Textures.Find(pMaterialName);
+ bool bFound = false;
+ if ( index != m_Textures.InvalidIndex() )
+ {
+ bFound = true;
+ *pIndex = index;
+ }
+ else
+ {
+ KeyValues *pVMT = new KeyValues("vmt");
+ CUtlBuffer buf(0,0,CUtlBuffer::TEXT_BUFFER);
+ LoadFileIntoBuffer( buf, pMaterialName );
+ if ( pVMT->LoadFromBuffer( pMaterialName, buf ) )
+ {
+ bFound = true;
+ if ( pVMT->FindKey("$translucent") || pVMT->FindKey("$alphatest") )
+ {
+ KeyValues *pBaseTexture = pVMT->FindKey("$basetexture");
+ if ( pBaseTexture )
+ {
+ const char *pBaseTextureName = pBaseTexture->GetString();
+ if ( pBaseTextureName )
+ {
+ int w, h;
+ bool bClampU = false;
+ bool bClampV = false;
+ unsigned char *pImageBits = LoadVTFRGB8888( pBaseTextureName, &w, &h, &bClampU, &bClampV );
+ if ( pImageBits )
+ {
+ int index = m_Textures.Insert( pMaterialName );
+ m_Textures[index].InitFromRGB8888( w, h, pImageBits );
+ *pIndex = index;
+ if ( pVMT->FindKey("$nocull") )
+ {
+ // UNDONE: Support this? Do we need to emit two triangles?
+ m_Textures[index].allowBackface = true;
+ }
+ m_Textures[index].clampU = bClampU;
+ m_Textures[index].clampV = bClampV;
+ delete[] pImageBits;
+ }
+ }
+ }
+ }
+
+ }
+ pVMT->deleteThis();
+ }
+
+ return bFound;
+ }
+
+
+ // iterate the textures for the model and load each one into the database
+ // this is used on models marked to cast texture shadows
+ void LoadAllTexturesForModel( studiohdr_t *pHdr, int *pTextureList )
+ {
+ for ( int i = 0; i < pHdr->numtextures; i++ )
+ {
+ int textureIndex = -1;
+ // try to add each texture to the transparent shadow manager
+ char szPath[MAX_PATH];
+
+ // iterate quietly through all specified directories until a valid material is found
+ for ( int j = 0; j < pHdr->numcdtextures; j++ )
+ {
+ Q_strncpy( szPath, "materials/", sizeof( szPath ) );
+ Q_strncat( szPath, pHdr->pCdtexture( j ), sizeof( szPath ) );
+ const char *textureName = pHdr->pTexture( i )->pszName();
+ Q_strncat( szPath, textureName, sizeof( szPath ), COPY_ALL_CHARACTERS );
+ Q_strncat( szPath, ".vmt", sizeof( szPath ), COPY_ALL_CHARACTERS );
+ Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
+ if ( FindOrLoadIfValid( szPath, &textureIndex ) )
+ break;
+ }
+
+ pTextureList[i] = textureIndex;
+ }
+ }
+
+ int AddMaterialEntry( int shadowTextureIndex, const Vector2D &t0, const Vector2D &t1, const Vector2D &t2 )
+ {
+ int index = m_MaterialEntries.AddToTail();
+ m_MaterialEntries[index].textureIndex = shadowTextureIndex;
+ m_MaterialEntries[index].uv[0] = t0;
+ m_MaterialEntries[index].uv[1] = t1;
+ m_MaterialEntries[index].uv[2] = t2;
+ return index;
+ }
+
+ // HACKHACK: Compute the average coverage for this triangle by sampling the AABB of its texture space
+ float ComputeCoverageForTriangle( int shadowTextureIndex, const Vector2D &t0, const Vector2D &t1, const Vector2D &t2 )
+ {
+ float umin = min(t0.x, t1.x);
+ umin = min(umin, t2.x);
+ float umax = max(t0.x, t1.x);
+ umax = max(umax, t2.x);
+
+ float vmin = min(t0.y, t1.y);
+ vmin = min(vmin, t2.y);
+ float vmax = max(t0.y, t1.y);
+ vmax = max(vmax, t2.y);
+
+ // UNDONE: Do something about tiling
+ umin = clamp(umin, 0, 1);
+ umax = clamp(umax, 0, 1);
+ vmin = clamp(vmin, 0, 1);
+ vmax = clamp(vmax, 0, 1);
+ Assert(umin>=0.0f && umax <= 1.0f);
+ Assert(vmin>=0.0f && vmax <= 1.0f);
+ const alphatexture_t &tex = m_Textures.Element(shadowTextureIndex);
+ int u0 = umin * (tex.width-1);
+ int u1 = umax * (tex.width-1);
+ int v0 = vmin * (tex.height-1);
+ int v1 = vmax * (tex.height-1);
+
+ int total = 0;
+ int count = 0;
+ for ( int v = v0; v <= v1; v++ )
+ {
+ int row = (v * tex.width);
+ for ( int u = u0; u <= u1; u++ )
+ {
+ total += tex.pAlphaTexels[row + u];
+ count++;
+ }
+ }
+ if ( count )
+ {
+ float coverage = float(total) / (count * 255.0f);
+ return coverage;
+ }
+ return 1.0f;
+ }
+
+ int SampleMaterial( int materialIndex, const Vector &coords, bool bBackface )
+ {
+ const materialentry_t &mat = m_MaterialEntries[materialIndex];
+ const alphatexture_t &tex = m_Textures.Element(m_MaterialEntries[materialIndex].textureIndex);
+ if ( bBackface && !tex.allowBackface )
+ return 0;
+ Vector2D uv = coords.x * mat.uv[0] + coords.y * mat.uv[1] + coords.z * mat.uv[2];
+ int u = RoundFloatToInt( uv[0] * tex.width );
+ int v = RoundFloatToInt( uv[1] * tex.height );
+
+ // asume power of 2, clamp or wrap
+ // UNDONE: Support clamp? This code should work
+#if 0
+ u = tex.clampU ? clamp(u,0,(tex.width-1)) : (u & (tex.width-1));
+ v = tex.clampV ? clamp(v,0,(tex.height-1)) : (v & (tex.height-1));
+#else
+ // for now always wrap
+ u &= (tex.width-1);
+ v &= (tex.height-1);
+#endif
+
+ return tex.pAlphaTexels[v * tex.width + u];
+ }
+
+ struct alphatexture_t
+ {
+ short width;
+ short height;
+ bool allowBackface;
+ bool clampU;
+ bool clampV;
+ unsigned char *pAlphaTexels;
+
+ void InitFromRGB8888( int w, int h, unsigned char *pTexels )
+ {
+ width = w;
+ height = h;
+ pAlphaTexels = new unsigned char[w*h];
+ for ( int i = 0; i < h; i++ )
+ {
+ for ( int j = 0; j < w; j++ )
+ {
+ int index = (i*w) + j;
+ pAlphaTexels[index] = pTexels[index*4 + 3];
+ }
+ }
+ }
+ };
+ struct materialentry_t
+ {
+ int textureIndex;
+ Vector2D uv[3];
+ };
+ // this is the list of textures we've loaded
+ // only load each one once
+ CUtlDict< alphatexture_t, unsigned short > m_Textures;
+ CUtlVector<materialentry_t> m_MaterialEntries;
+};
+
+// global to keep the shadow-casting texture list and their alpha bits
+CShadowTextureList g_ShadowTextureList;
+
+float ComputeCoverageFromTexture( float b0, float b1, float b2, int32 hitID )
+{
+ const float alphaScale = 1.0f / 255.0f;
+ // UNDONE: Pass ray down to determine backfacing?
+ //Vector normal( tri.m_flNx, tri.m_flNy, tri.m_flNz );
+ //bool bBackface = DotProduct(delta, tri.N) > 0 ? true : false;
+ Vector coords(b0,b1,b2);
+ return alphaScale * g_ShadowTextureList.SampleMaterial( g_RtEnv.GetTriangleMaterial(hitID), coords, false );
+}
+
+// this is here to strip models/ or .mdl or whatnot
+void CleanModelName( const char *pModelName, char *pOutput, int outLen )
+{
+ // strip off leading models/ if it exists
+ const char *pModelDir = "models/";
+ int modelLen = Q_strlen(pModelDir);
+
+ if ( !Q_strnicmp(pModelName, pModelDir, modelLen ) )
+ {
+ pModelName += modelLen;
+ }
+ Q_strncpy( pOutput, pModelName, outLen );
+
+ // truncate any .mdl extension
+ char *dot = strchr(pOutput,'.');
+ if ( dot )
+ {
+ *dot = 0;
+ }
+
+}
+
+
+void ForceTextureShadowsOnModel( const char *pModelName )
+{
+ char buf[1024];
+ CleanModelName( pModelName, buf, sizeof(buf) );
+ if ( !g_ForcedTextureShadowsModels.Find(buf).IsValid())
+ {
+ g_ForcedTextureShadowsModels.AddString(buf);
+ }
+}
+
+bool IsModelTextureShadowsForced( const char *pModelName )
+{
+ char buf[1024];
+ CleanModelName( pModelName, buf, sizeof(buf) );
+ return g_ForcedTextureShadowsModels.Find(buf).IsValid();
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a collision model (based on the render geometry!)
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::CreateCollisionModel( char const* pModelName )
+{
+ CUtlBuffer buf;
+ CUtlBuffer bufvtx;
+ CUtlBuffer bufphy;
+
+ int i = m_StaticPropDict.AddToTail();
+ m_StaticPropDict[i].m_pModel = NULL;
+ m_StaticPropDict[i].m_pStudioHdr = NULL;
+
+ if ( !LoadStudioModel( pModelName, buf ) )
+ {
+ VectorCopy( vec3_origin, m_StaticPropDict[i].m_Mins );
+ VectorCopy( vec3_origin, m_StaticPropDict[i].m_Maxs );
+ return;
+ }
+
+ studiohdr_t* pHdr = (studiohdr_t*)buf.Base();
+
+ VectorCopy( pHdr->hull_min, m_StaticPropDict[i].m_Mins );
+ VectorCopy( pHdr->hull_max, m_StaticPropDict[i].m_Maxs );
+
+ if ( LoadStudioCollisionModel( pModelName, bufphy ) )
+ {
+ phyheader_t header;
+ bufphy.Get( &header, sizeof(header) );
+
+ vcollide_t *pCollide = &m_StaticPropDict[i].m_loadedModel;
+ s_pPhysCollision->VCollideLoad( pCollide, header.solidCount, (const char *)bufphy.PeekGet(), bufphy.TellPut() - bufphy.TellGet() );
+ m_StaticPropDict[i].m_pModel = m_StaticPropDict[i].m_loadedModel.solids[0];
+
+ /*
+ static int propNum = 0;
+ char tmp[128];
+ sprintf( tmp, "staticprop%03d.txt", propNum );
+ DumpCollideToGlView( pCollide, tmp );
+ ++propNum;
+ */
+ }
+ else
+ {
+ // mark this as unused
+ m_StaticPropDict[i].m_loadedModel.solidCount = 0;
+
+ // CPhysCollide* pPhys = CreatePhysCollide( pHdr, pVtxHdr );
+ m_StaticPropDict[i].m_pModel = ComputeConvexHull( pHdr );
+ }
+
+ // clone it
+ m_StaticPropDict[i].m_pStudioHdr = (studiohdr_t *)malloc( buf.Size() );
+ memcpy( m_StaticPropDict[i].m_pStudioHdr, (studiohdr_t*)buf.Base(), buf.Size() );
+
+ if ( !LoadVTXFile( pModelName, m_StaticPropDict[i].m_pStudioHdr, m_StaticPropDict[i].m_VtxBuf ) )
+ {
+ // failed, leave state identified as disabled
+ m_StaticPropDict[i].m_VtxBuf.Purge();
+ }
+
+ if ( g_bTextureShadows )
+ {
+ if ( (pHdr->flags & STUDIOHDR_FLAGS_CAST_TEXTURE_SHADOWS) || IsModelTextureShadowsForced(pModelName) )
+ {
+ m_StaticPropDict[i].m_textureShadowIndex.RemoveAll();
+ m_StaticPropDict[i].m_triangleMaterialIndex.RemoveAll();
+ m_StaticPropDict[i].m_textureShadowIndex.AddMultipleToTail( pHdr->numtextures );
+ g_ShadowTextureList.LoadAllTexturesForModel( pHdr, m_StaticPropDict[i].m_textureShadowIndex.Base() );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Unserialize static prop model dictionary
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::UnserializeModelDict( CUtlBuffer& buf )
+{
+ int count = buf.GetInt();
+ while ( --count >= 0 )
+ {
+ StaticPropDictLump_t lump;
+ buf.Get( &lump, sizeof(StaticPropDictLump_t) );
+
+ CreateCollisionModel( lump.m_Name );
+ }
+}
+
+void CVradStaticPropMgr::UnserializeModels( CUtlBuffer& buf )
+{
+ int count = buf.GetInt();
+
+ m_StaticProps.AddMultipleToTail(count);
+ for ( int i = 0; i < count; ++i )
+ {
+ StaticPropLump_t lump;
+ buf.Get( &lump, sizeof(StaticPropLump_t) );
+
+ VectorCopy( lump.m_Origin, m_StaticProps[i].m_Origin );
+ VectorCopy( lump.m_Angles, m_StaticProps[i].m_Angles );
+ VectorCopy( lump.m_LightingOrigin, m_StaticProps[i].m_LightingOrigin );
+ m_StaticProps[i].m_bLightingOriginValid = ( lump.m_Flags & STATIC_PROP_USE_LIGHTING_ORIGIN ) > 0;
+ m_StaticProps[i].m_ModelIdx = lump.m_PropType;
+ m_StaticProps[i].m_Handle = TREEDATA_INVALID_HANDLE;
+ m_StaticProps[i].m_Flags = lump.m_Flags;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Unserialize static props
+//-----------------------------------------------------------------------------
+
+void CVradStaticPropMgr::UnserializeStaticProps()
+{
+ // Unserialize static props, insert them into the appropriate leaves
+ GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle( GAMELUMP_STATIC_PROPS );
+ int size = g_GameLumps.GameLumpSize( handle );
+ if (!size)
+ return;
+
+ if ( g_GameLumps.GetGameLumpVersion( handle ) != GAMELUMP_STATIC_PROPS_VERSION )
+ {
+ Error( "Cannot load the static props... encountered a stale map version. Re-vbsp the map." );
+ }
+
+ if ( g_GameLumps.GetGameLump( handle ) )
+ {
+ CUtlBuffer buf( g_GameLumps.GetGameLump(handle), size, CUtlBuffer::READ_ONLY );
+ UnserializeModelDict( buf );
+
+ // Skip the leaf list data
+ int count = buf.GetInt();
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, count * sizeof(StaticPropLeafLump_t) );
+
+ UnserializeModels( buf );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Level init, shutdown
+//-----------------------------------------------------------------------------
+
+void CVradStaticPropMgr::Init()
+{
+ CreateInterfaceFn physicsFactory = GetPhysicsFactory();
+ if ( !physicsFactory )
+ Error( "Unable to load vphysics DLL." );
+
+ s_pPhysCollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
+ if( !s_pPhysCollision )
+ {
+ Error( "Unable to get '%s' for physics interface.", VPHYSICS_COLLISION_INTERFACE_VERSION );
+ return;
+ }
+
+ // Read in static props that have been compiled into the bsp file
+ UnserializeStaticProps();
+}
+
+void CVradStaticPropMgr::Shutdown()
+{
+
+ // Remove all static prop model data
+ for (int i = m_StaticPropDict.Size(); --i >= 0; )
+ {
+ studiohdr_t *pStudioHdr = m_StaticPropDict[i].m_pStudioHdr;
+ if ( pStudioHdr )
+ {
+ if ( pStudioHdr->pVertexBase )
+ {
+ free( pStudioHdr->pVertexBase );
+ }
+ free( pStudioHdr );
+ }
+ }
+
+ m_StaticProps.Purge();
+ m_StaticPropDict.Purge();
+}
+
+void ComputeLightmapColor( dface_t* pFace, Vector &color )
+{
+ texinfo_t* pTex = &texinfo[pFace->texinfo];
+ if ( pTex->flags & SURF_SKY )
+ {
+ // sky ambient already accounted for in direct component
+ return;
+ }
+}
+
+bool PositionInSolid( Vector &position )
+{
+ int ndxLeaf = PointLeafnum( position );
+ if ( dleafs[ndxLeaf].contents & CONTENTS_SOLID )
+ {
+ // position embedded in solid
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Trace from a vertex to each direct light source, accumulating its contribution.
+//-----------------------------------------------------------------------------
+void ComputeDirectLightingAtPoint( Vector &position, Vector &normal, Vector &outColor, int iThread,
+ int static_prop_id_to_skip=-1, int nLFlags = 0)
+{
+ SSE_sampleLightOutput_t sampleOutput;
+
+ outColor.Init();
+
+ // Iterate over all direct lights and accumulate their contribution
+ int cluster = ClusterFromPoint( position );
+ for ( directlight_t *dl = activelights; dl != NULL; dl = dl->next )
+ {
+ if ( dl->light.style )
+ {
+ // skip lights with style
+ continue;
+ }
+
+ // is this lights cluster visible?
+ if ( !PVSCheck( dl->pvs, cluster ) )
+ continue;
+
+ // push the vertex towards the light to avoid surface acne
+ Vector adjusted_pos = position;
+ float flEpsilon = 0.0;
+
+ if (dl->light.type != emit_skyambient)
+ {
+ // push towards the light
+ Vector fudge;
+ if ( dl->light.type == emit_skylight )
+ fudge = -( dl->light.normal);
+ else
+ {
+ fudge = dl->light.origin-position;
+ VectorNormalize( fudge );
+ }
+ fudge *= 4.0;
+ adjusted_pos += fudge;
+ }
+ else
+ {
+ // push out along normal
+ adjusted_pos += 4.0 * normal;
+// flEpsilon = 1.0;
+ }
+
+ FourVectors adjusted_pos4;
+ FourVectors normal4;
+ adjusted_pos4.DuplicateVector( adjusted_pos );
+ normal4.DuplicateVector( normal );
+
+ GatherSampleLightSSE( sampleOutput, dl, -1, adjusted_pos4, &normal4, 1, iThread, nLFlags | GATHERLFLAGS_FORCE_FAST,
+ static_prop_id_to_skip, flEpsilon );
+
+ VectorMA( outColor, sampleOutput.m_flFalloff.m128_f32[0] * sampleOutput.m_flDot[0].m128_f32[0], dl->light.intensity, outColor );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Takes the results from a ComputeLighting call and applies it to the static prop in question.
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::ApplyLightingToStaticProp( CStaticProp &prop, const CComputeStaticPropLightingResults *pResults )
+{
+ if ( pResults->m_ColorVertsArrays.Count() == 0 )
+ return;
+
+ StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
+ studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
+ OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
+ Assert( pStudioHdr && pVtxHdr );
+
+ int iCurColorVertsArray = 0;
+ for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
+ {
+ OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
+ mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
+
+ for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
+ {
+ OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID );
+ mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
+
+ const CUtlVector<colorVertex_t> &colorVerts = *pResults->m_ColorVertsArrays[iCurColorVertsArray++];
+
+ for ( int nLod = 0; nLod < pVtxHdr->numLODs; nLod++ )
+ {
+ OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod );
+
+ for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
+ {
+ mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh );
+ OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh );
+
+ for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
+ {
+ OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup );
+ int nMeshIdx = prop.m_MeshData.AddToTail();
+ prop.m_MeshData[nMeshIdx].m_Verts.AddMultipleToTail( pStripGroup->numVerts );
+ prop.m_MeshData[nMeshIdx].m_nLod = nLod;
+
+ for ( int nVertex = 0; nVertex < pStripGroup->numVerts; ++nVertex )
+ {
+ int nIndex = pMesh->vertexoffset + pStripGroup->pVertex( nVertex )->origMeshVertID;
+
+ Assert( nIndex < pStudioModel->numvertices );
+ prop.m_MeshData[nMeshIdx].m_Verts[nVertex] = colorVerts[nIndex].m_Color;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Trace rays from each unique vertex, accumulating direct and indirect
+// sources at each ray termination. Use the winding data to distribute the unique vertexes
+// into the rendering layout.
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::ComputeLighting( CStaticProp &prop, int iThread, int prop_index, CComputeStaticPropLightingResults *pResults )
+{
+ CUtlVector<badVertex_t> badVerts;
+
+ StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
+ studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
+ OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
+ if ( !pStudioHdr || !pVtxHdr )
+ {
+ // must have model and its verts for lighting computation
+ // game will fallback to fullbright
+ return;
+ }
+
+ if (prop.m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING )
+ return;
+
+ VMPI_SetCurrentStage( "ComputeLighting" );
+
+ for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
+ {
+ mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
+
+ for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
+ {
+ mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
+
+ // light all unique vertexes
+ CUtlVector<colorVertex_t> *pColorVertsArray = new CUtlVector<colorVertex_t>;
+ pResults->m_ColorVertsArrays.AddToTail( pColorVertsArray );
+
+ CUtlVector<colorVertex_t> &colorVerts = *pColorVertsArray;
+ colorVerts.EnsureCount( pStudioModel->numvertices );
+ memset( colorVerts.Base(), 0, colorVerts.Count() * sizeof(colorVertex_t) );
+
+ int numVertexes = 0;
+ for ( int meshID = 0; meshID < pStudioModel->nummeshes; ++meshID )
+ {
+ mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( meshID );
+ const mstudio_meshvertexdata_t *vertData = pStudioMesh->GetVertexData((void *)pStudioHdr);
+ Assert( vertData ); // This can only return NULL on X360 for now
+ for ( int vertexID = 0; vertexID < pStudioMesh->numvertices; ++vertexID )
+ {
+ Vector sampleNormal;
+ Vector samplePosition;
+ // transform position and normal into world coordinate system
+ matrix3x4_t matrix;
+ AngleMatrix( prop.m_Angles, prop.m_Origin, matrix );
+ VectorTransform( *vertData->Position( vertexID ), matrix, samplePosition );
+ AngleMatrix( prop.m_Angles, matrix );
+ VectorTransform( *vertData->Normal( vertexID ), matrix, sampleNormal );
+
+ if ( PositionInSolid( samplePosition ) )
+ {
+ // vertex is in solid, add to the bad list, and recover later
+ badVertex_t badVertex;
+ badVertex.m_ColorVertex = numVertexes;
+ badVertex.m_Position = samplePosition;
+ badVertex.m_Normal = sampleNormal;
+ badVerts.AddToTail( badVertex );
+ }
+ else
+ {
+ Vector direct_pos=samplePosition;
+ int skip_prop = -1;
+ if ( g_bDisablePropSelfShadowing || ( prop.m_Flags & STATIC_PROP_NO_SELF_SHADOWING ) )
+ {
+ skip_prop = prop_index;
+ }
+
+ int nFlags = ( prop.m_Flags & STATIC_PROP_IGNORE_NORMALS ) ? GATHERLFLAGS_IGNORE_NORMALS : 0;
+
+ Vector directColor(0,0,0);
+ ComputeDirectLightingAtPoint( direct_pos,
+ sampleNormal, directColor, iThread,
+ skip_prop, nFlags );
+ Vector indirectColor(0,0,0);
+
+ if (g_bShowStaticPropNormals)
+ {
+ directColor= sampleNormal;
+ directColor += Vector(1.0,1.0,1.0);
+ directColor *= 50.0;
+ }
+ else
+ {
+ if (numbounce >= 1)
+ ComputeIndirectLightingAtPoint(
+ samplePosition, sampleNormal,
+ indirectColor, iThread, true,
+ ( prop.m_Flags & STATIC_PROP_IGNORE_NORMALS) != 0 );
+ }
+
+ colorVerts[numVertexes].m_bValid = true;
+ colorVerts[numVertexes].m_Position = samplePosition;
+ VectorAdd( directColor, indirectColor, colorVerts[numVertexes].m_Color );
+ }
+
+ numVertexes++;
+ }
+ }
+
+ // color in the bad vertexes
+ // when entire model has no lighting origin and no valid neighbors
+ // must punt, leave black coloring
+ if ( badVerts.Count() && ( prop.m_bLightingOriginValid || badVerts.Count() != numVertexes ) )
+ {
+ for ( int nBadVertex = 0; nBadVertex < badVerts.Count(); nBadVertex++ )
+ {
+ Vector bestPosition;
+ if ( prop.m_bLightingOriginValid )
+ {
+ // use the specified lighting origin
+ VectorCopy( prop.m_LightingOrigin, bestPosition );
+ }
+ else
+ {
+ // find the closest valid neighbor
+ int best = 0;
+ float closest = FLT_MAX;
+ for ( int nColorVertex = 0; nColorVertex < numVertexes; nColorVertex++ )
+ {
+ if ( !colorVerts[nColorVertex].m_bValid )
+ {
+ // skip invalid neighbors
+ continue;
+ }
+ Vector delta;
+ VectorSubtract( colorVerts[nColorVertex].m_Position, badVerts[nBadVertex].m_Position, delta );
+ float distance = VectorLength( delta );
+ if ( distance < closest )
+ {
+ closest = distance;
+ best = nColorVertex;
+ }
+ }
+
+ // use the best neighbor as the direction to crawl
+ VectorCopy( colorVerts[best].m_Position, bestPosition );
+ }
+
+ // crawl toward best position
+ // sudivide to determine a closer valid point to the bad vertex, and re-light
+ Vector midPosition;
+ int numIterations = 20;
+ while ( --numIterations > 0 )
+ {
+ VectorAdd( bestPosition, badVerts[nBadVertex].m_Position, midPosition );
+ VectorScale( midPosition, 0.5f, midPosition );
+ if ( PositionInSolid( midPosition ) )
+ break;
+ bestPosition = midPosition;
+ }
+
+ // re-light from better position
+ Vector directColor;
+ ComputeDirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normal, directColor, iThread );
+
+ Vector indirectColor;
+ ComputeIndirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normal,
+ indirectColor, iThread, true );
+
+ // save results, not changing valid status
+ // to ensure this offset position is not considered as a viable candidate
+ colorVerts[badVerts[nBadVertex].m_ColorVertex].m_Position = bestPosition;
+ VectorAdd( directColor, indirectColor, colorVerts[badVerts[nBadVertex].m_ColorVertex].m_Color );
+ }
+ }
+
+ // discard bad verts
+ badVerts.Purge();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Write the lighitng to bsp pak lump
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::SerializeLighting()
+{
+ char filename[MAX_PATH];
+ CUtlBuffer utlBuf;
+
+ // illuminate them all
+ int count = m_StaticProps.Count();
+ if ( !count )
+ {
+ // nothing to do
+ return;
+ }
+
+ char mapName[MAX_PATH];
+ Q_FileBase( source, mapName, sizeof( mapName ) );
+
+ int size;
+ for (int i = 0; i < count; ++i)
+ {
+ // no need to write this file if we didn't compute the data
+ // props marked this way will not load the info anyway
+ if ( m_StaticProps[i].m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING )
+ continue;
+
+ if (g_bHDR)
+ {
+ sprintf( filename, "sp_hdr_%d.vhv", i );
+ }
+ else
+ {
+ sprintf( filename, "sp_%d.vhv", i );
+ }
+
+ int totalVertexes = 0;
+ for ( int j=0; j<m_StaticProps[i].m_MeshData.Count(); j++ )
+ {
+ totalVertexes += m_StaticProps[i].m_MeshData[j].m_Verts.Count();
+ }
+
+ // allocate a buffer with enough padding for alignment
+ size = sizeof( HardwareVerts::FileHeader_t ) +
+ m_StaticProps[i].m_MeshData.Count()*sizeof(HardwareVerts::MeshHeader_t) +
+ totalVertexes*4 + 2*512;
+ utlBuf.EnsureCapacity( size );
+ Q_memset( utlBuf.Base(), 0, size );
+
+ HardwareVerts::FileHeader_t *pVhvHdr = (HardwareVerts::FileHeader_t *)utlBuf.Base();
+
+ // align to start of vertex data
+ unsigned char *pVertexData = (unsigned char *)(sizeof( HardwareVerts::FileHeader_t ) + m_StaticProps[i].m_MeshData.Count()*sizeof(HardwareVerts::MeshHeader_t));
+ pVertexData = (unsigned char*)pVhvHdr + ALIGN_TO_POW2( (unsigned int)pVertexData, 512 );
+
+ // construct header
+ pVhvHdr->m_nVersion = VHV_VERSION;
+ pVhvHdr->m_nChecksum = m_StaticPropDict[m_StaticProps[i].m_ModelIdx].m_pStudioHdr->checksum;
+ pVhvHdr->m_nVertexFlags = VERTEX_COLOR;
+ pVhvHdr->m_nVertexSize = 4;
+ pVhvHdr->m_nVertexes = totalVertexes;
+ pVhvHdr->m_nMeshes = m_StaticProps[i].m_MeshData.Count();
+
+ for (int n=0; n<pVhvHdr->m_nMeshes; n++)
+ {
+ // construct mesh dictionary
+ HardwareVerts::MeshHeader_t *pMesh = pVhvHdr->pMesh( n );
+ pMesh->m_nLod = m_StaticProps[i].m_MeshData[n].m_nLod;
+ pMesh->m_nVertexes = m_StaticProps[i].m_MeshData[n].m_Verts.Count();
+ pMesh->m_nOffset = (unsigned int)pVertexData - (unsigned int)pVhvHdr;
+
+ // construct vertexes
+ for (int k=0; k<pMesh->m_nVertexes; k++)
+ {
+ Vector &vector = m_StaticProps[i].m_MeshData[n].m_Verts[k];
+
+ ColorRGBExp32 rgbColor;
+ VectorToColorRGBExp32( vector, rgbColor );
+ unsigned char dstColor[4];
+ ConvertRGBExp32ToRGBA8888( &rgbColor, dstColor );
+
+ // b,g,r,a order
+ pVertexData[0] = dstColor[2];
+ pVertexData[1] = dstColor[1];
+ pVertexData[2] = dstColor[0];
+ pVertexData[3] = dstColor[3];
+ pVertexData += 4;
+ }
+ }
+
+ // align to end of file
+ pVertexData = (unsigned char *)((unsigned int)pVertexData - (unsigned int)pVhvHdr);
+ pVertexData = (unsigned char*)pVhvHdr + ALIGN_TO_POW2( (unsigned int)pVertexData, 512 );
+
+ AddBufferToPak( GetPakFile(), filename, (void*)pVhvHdr, pVertexData - (unsigned char*)pVhvHdr, false );
+ }
+}
+
+void CVradStaticPropMgr::VMPI_ProcessStaticProp_Static( int iThread, uint64 iStaticProp, MessageBuffer *pBuf )
+{
+ g_StaticPropMgr.VMPI_ProcessStaticProp( iThread, iStaticProp, pBuf );
+}
+
+void CVradStaticPropMgr::VMPI_ReceiveStaticPropResults_Static( uint64 iStaticProp, MessageBuffer *pBuf, int iWorker )
+{
+ g_StaticPropMgr.VMPI_ReceiveStaticPropResults( iStaticProp, pBuf, iWorker );
+}
+
+//-----------------------------------------------------------------------------
+// Called on workers to do the computation for a static prop and send
+// it to the master.
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::VMPI_ProcessStaticProp( int iThread, int iStaticProp, MessageBuffer *pBuf )
+{
+ // Compute the lighting.
+ CComputeStaticPropLightingResults results;
+ ComputeLighting( m_StaticProps[iStaticProp], iThread, iStaticProp, &results );
+
+ VMPI_SetCurrentStage( "EncodeLightingResults" );
+
+ // Encode the results.
+ int nLists = results.m_ColorVertsArrays.Count();
+ pBuf->write( &nLists, sizeof( nLists ) );
+
+ for ( int i=0; i < nLists; i++ )
+ {
+ CUtlVector<colorVertex_t> &curList = *results.m_ColorVertsArrays[i];
+ int count = curList.Count();
+ pBuf->write( &count, sizeof( count ) );
+ pBuf->write( curList.Base(), curList.Count() * sizeof( colorVertex_t ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Called on the master when a worker finishes processing a static prop.
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::VMPI_ReceiveStaticPropResults( int iStaticProp, MessageBuffer *pBuf, int iWorker )
+{
+ // Read in the results.
+ CComputeStaticPropLightingResults results;
+
+ int nLists;
+ pBuf->read( &nLists, sizeof( nLists ) );
+
+ for ( int i=0; i < nLists; i++ )
+ {
+ CUtlVector<colorVertex_t> *pList = new CUtlVector<colorVertex_t>;
+ results.m_ColorVertsArrays.AddToTail( pList );
+
+ int count;
+ pBuf->read( &count, sizeof( count ) );
+ pList->SetSize( count );
+ pBuf->read( pList->Base(), count * sizeof( colorVertex_t ) );
+ }
+
+ // Apply the results.
+ ApplyLightingToStaticProp( m_StaticProps[iStaticProp], &results );
+}
+
+
+void CVradStaticPropMgr::ComputeLightingForProp( int iThread, int iStaticProp )
+{
+ // Compute the lighting.
+ CComputeStaticPropLightingResults results;
+ ComputeLighting( m_StaticProps[iStaticProp], iThread, iStaticProp, &results );
+ ApplyLightingToStaticProp( m_StaticProps[iStaticProp], &results );
+}
+
+void CVradStaticPropMgr::ThreadComputeStaticPropLighting( int iThread, void *pUserData )
+{
+ while (1)
+ {
+ int j = GetThreadWork ();
+ if (j == -1)
+ break;
+ CComputeStaticPropLightingResults results;
+ g_StaticPropMgr.ComputeLightingForProp( iThread, j );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Computes lighting for the static props.
+// Must be after all other surface lighting has been computed for the indirect sampling.
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::ComputeLighting( int iThread )
+{
+ // illuminate them all
+ int count = m_StaticProps.Count();
+ if ( !count )
+ {
+ // nothing to do
+ return;
+ }
+
+ StartPacifier( "Computing static prop lighting : " );
+
+ // ensure any traces against us are ignored because we have no inherit lighting contribution
+ m_bIgnoreStaticPropTrace = true;
+
+ if ( g_bUseMPI )
+ {
+ // Distribute the work among the workers.
+ VMPI_SetCurrentStage( "CVradStaticPropMgr::ComputeLighting" );
+
+ DistributeWork(
+ count,
+ VMPI_DISTRIBUTEWORK_PACKETID,
+ &CVradStaticPropMgr::VMPI_ProcessStaticProp_Static,
+ &CVradStaticPropMgr::VMPI_ReceiveStaticPropResults_Static );
+ }
+ else
+ {
+ RunThreadsOn(count, true, ThreadComputeStaticPropLighting);
+ }
+
+ // restore default
+ m_bIgnoreStaticPropTrace = false;
+
+ // save data to bsp
+ SerializeLighting();
+
+ EndPacifier( true );
+}
+
+//-----------------------------------------------------------------------------
+// Adds all static prop polys to the ray trace store.
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::AddPolysForRayTrace( void )
+{
+ int count = m_StaticProps.Count();
+ if ( !count )
+ {
+ // nothing to do
+ return;
+ }
+
+ // Triangle coverage of 1 (full coverage)
+ Vector fullCoverage;
+ fullCoverage.x = 1.0f;
+
+ for ( int nProp = 0; nProp < count; ++nProp )
+ {
+ CStaticProp &prop = m_StaticProps[nProp];
+ StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
+
+ if ( prop.m_Flags & STATIC_PROP_NO_SHADOW )
+ continue;
+
+ // If not using static prop polys, use AABB
+ if ( !g_bStaticPropPolys )
+ {
+ if ( dict.m_pModel )
+ {
+ VMatrix xform;
+ xform.SetupMatrixOrgAngles ( prop.m_Origin, prop.m_Angles );
+ ICollisionQuery *queryModel = s_pPhysCollision->CreateQueryModel( dict.m_pModel );
+ for ( int nConvex = 0; nConvex < queryModel->ConvexCount(); ++nConvex )
+ {
+ for ( int nTri = 0; nTri < queryModel->TriangleCount( nConvex ); ++nTri )
+ {
+ Vector verts[3];
+ queryModel->GetTriangleVerts( nConvex, nTri, verts );
+ for ( int nVert = 0; nVert < 3; ++nVert )
+ verts[nVert] = xform.VMul4x3(verts[nVert]);
+ g_RtEnv.AddTriangle ( TRACE_ID_STATICPROP | nProp, verts[0], verts[1], verts[2], fullCoverage );
+ }
+ }
+ s_pPhysCollision->DestroyQueryModel( queryModel );
+ }
+ else
+ {
+ VectorAdd ( dict.m_Mins, prop.m_Origin, prop.m_mins );
+ VectorAdd ( dict.m_Maxs, prop.m_Origin, prop.m_maxs );
+ g_RtEnv.AddAxisAlignedRectangularSolid ( TRACE_ID_STATICPROP | nProp, prop.m_mins, prop.m_maxs, fullCoverage );
+ }
+
+ continue;
+ }
+
+ studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
+ OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
+ if ( !pStudioHdr || !pVtxHdr )
+ {
+ // must have model and its verts for decoding triangles
+ return;
+ }
+ // only init the triangle table the first time
+ bool bInitTriangles = dict.m_triangleMaterialIndex.Count() ? false : true;
+ int triangleIndex = 0;
+
+ // meshes are deeply hierarchial, divided between three stores, follow the white rabbit
+ // body parts -> models -> lod meshes -> strip groups -> strips
+ // the vertices and indices are pooled, the trick is knowing the offset to determine your indexed base
+ for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
+ {
+ OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
+ mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
+
+ for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
+ {
+ OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID );
+ mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
+
+ // assuming lod 0, could iterate if required
+ int nLod = 0;
+ OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod );
+
+ for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
+ {
+ // check if this mesh's material is in the no shadow material name list
+ mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh );
+ mstudiotexture_t *pTxtr=pStudioHdr->pTexture(pMesh->material);
+ //printf("mat idx=%d mat name=%s\n",pMesh->material,pTxtr->pszName());
+ bool bSkipThisMesh = false;
+ for(int check=0; check<g_NonShadowCastingMaterialStrings.Count(); check++)
+ {
+ if ( Q_stristr( pTxtr->pszName(),
+ g_NonShadowCastingMaterialStrings[check] ) )
+ {
+ //printf("skip mat name=%s\n",pTxtr->pszName());
+ bSkipThisMesh = true;
+ break;
+ }
+ }
+ if ( bSkipThisMesh)
+ continue;
+
+ int shadowTextureIndex = -1;
+ if ( dict.m_textureShadowIndex.Count() )
+ {
+ shadowTextureIndex = dict.m_textureShadowIndex[pMesh->material];
+ }
+
+
+ OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh );
+ const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( (void *)pStudioHdr );
+ Assert( vertData ); // This can only return NULL on X360 for now
+
+ for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
+ {
+ OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup );
+
+ int nStrip;
+ for ( nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
+ {
+ OptimizedModel::StripHeader_t *pStrip = pStripGroup->pStrip( nStrip );
+
+ if ( pStrip->flags & OptimizedModel::STRIP_IS_TRILIST )
+ {
+ for ( int i = 0; i < pStrip->numIndices; i += 3 )
+ {
+ int idx = pStrip->indexOffset + i;
+
+ unsigned short i1 = *pStripGroup->pIndex( idx );
+ unsigned short i2 = *pStripGroup->pIndex( idx + 1 );
+ unsigned short i3 = *pStripGroup->pIndex( idx + 2 );
+
+ int vertex1 = pStripGroup->pVertex( i1 )->origMeshVertID;
+ int vertex2 = pStripGroup->pVertex( i2 )->origMeshVertID;
+ int vertex3 = pStripGroup->pVertex( i3 )->origMeshVertID;
+
+ // transform position into world coordinate system
+ matrix3x4_t matrix;
+ AngleMatrix( prop.m_Angles, prop.m_Origin, matrix );
+
+ Vector position1;
+ Vector position2;
+ Vector position3;
+ VectorTransform( *vertData->Position( vertex1 ), matrix, position1 );
+ VectorTransform( *vertData->Position( vertex2 ), matrix, position2 );
+ VectorTransform( *vertData->Position( vertex3 ), matrix, position3 );
+ unsigned short flags = 0;
+ int materialIndex = -1;
+ Vector color = vec3_origin;
+ if ( shadowTextureIndex >= 0 )
+ {
+ if ( bInitTriangles )
+ {
+ // add texture space and texture index to material database
+ // now
+ float coverage = g_ShadowTextureList.ComputeCoverageForTriangle(shadowTextureIndex, *vertData->Texcoord(vertex1), *vertData->Texcoord(vertex2), *vertData->Texcoord(vertex3) );
+ if ( coverage < 1.0f )
+ {
+ materialIndex = g_ShadowTextureList.AddMaterialEntry( shadowTextureIndex, *vertData->Texcoord(vertex1), *vertData->Texcoord(vertex2), *vertData->Texcoord(vertex3) );
+ color.x = coverage;
+ }
+ else
+ {
+ materialIndex = -1;
+ }
+ dict.m_triangleMaterialIndex.AddToTail(materialIndex);
+ }
+ else
+ {
+ materialIndex = dict.m_triangleMaterialIndex[triangleIndex];
+ triangleIndex++;
+ }
+ if ( materialIndex >= 0 )
+ {
+ flags = FCACHETRI_TRANSPARENT;
+ }
+ }
+// printf( "\ngl 3\n" );
+// printf( "gl %6.3f %6.3f %6.3f 1 0 0\n", XYZ(position1));
+// printf( "gl %6.3f %6.3f %6.3f 0 1 0\n", XYZ(position2));
+// printf( "gl %6.3f %6.3f %6.3f 0 0 1\n", XYZ(position3));
+ g_RtEnv.AddTriangle( TRACE_ID_STATICPROP | nProp,
+ position1, position2, position3,
+ color, flags, materialIndex);
+ }
+ }
+ else
+ {
+ // all tris expected to be discrete tri lists
+ // must fixme if stripping ever occurs
+ printf( "unexpected strips found\n" );
+ Assert( 0 );
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+struct tl_tri_t
+{
+ Vector p0;
+ Vector p1;
+ Vector p2;
+ Vector n0;
+ Vector n1;
+ Vector n2;
+
+ bool operator == (const tl_tri_t &t) const
+ {
+ return ( p0 == t.p0 &&
+ p1 == t.p1 &&
+ p2 == t.p2 &&
+ n0 == t.n0 &&
+ n1 == t.n1 &&
+ n2 == t.n2 );
+ }
+};
+
+struct tl_vert_t
+{
+ Vector m_position;
+ CUtlLinkedList< tl_tri_t, int > m_triList;
+};
+
+void AddTriVertsToList( CUtlVector< tl_vert_t > &triListVerts, int vertIndex, Vector vertPosition, Vector p0, Vector p1, Vector p2, Vector n0, Vector n1, Vector n2 )
+{
+ tl_tri_t tlTri;
+
+ tlTri.p0 = p0;
+ tlTri.p1 = p1;
+ tlTri.p2 = p2;
+ tlTri.n0 = n0;
+ tlTri.n1 = n1;
+ tlTri.n2 = n2;
+
+ triListVerts.EnsureCapacity( vertIndex+1 );
+
+ triListVerts[vertIndex].m_position = vertPosition;
+
+ int index = triListVerts[vertIndex].m_triList.Find( tlTri );
+ if ( !triListVerts[vertIndex].m_triList.IsValidIndex( index ) )
+ {
+ // not in list, add to list of triangles
+ triListVerts[vertIndex].m_triList.AddToTail( tlTri );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Builds a list of tris for every vertex
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::BuildTriList( CStaticProp &prop )
+{
+ // the generated list will consist of a list of verts
+ // each vert will have a linked list of triangles that it belongs to
+ CUtlVector< tl_vert_t > triListVerts;
+
+ StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
+ studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
+ OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
+ if ( !pStudioHdr || !pVtxHdr )
+ {
+ // must have model and its verts for decoding triangles
+ return;
+ }
+
+ // meshes are deeply hierarchial, divided between three stores, follow the white rabbit
+ // body parts -> models -> lod meshes -> strip groups -> strips
+ // the vertices and indices are pooled, the trick is knowing the offset to determine your indexed base
+ for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
+ {
+ OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
+ mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
+
+ for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
+ {
+ OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID );
+ mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
+
+ // get the specified lod, assuming lod 0
+ int nLod = 0;
+ OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod );
+
+ // must reset because each model has their own vertexes [0..n]
+ // in order for this to be monolithic for the entire prop the list must be segmented
+ triListVerts.Purge();
+
+ for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
+ {
+ mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh );
+ OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh );
+ const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( (void *)pStudioHdr );
+ Assert( vertData ); // This can only return NULL on X360 for now
+
+ for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
+ {
+ OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup );
+
+ int nStrip;
+ for ( nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
+ {
+ OptimizedModel::StripHeader_t *pStrip = pStripGroup->pStrip( nStrip );
+
+ if ( pStrip->flags & OptimizedModel::STRIP_IS_TRILIST )
+ {
+ for ( int i = 0; i < pStrip->numIndices; i += 3 )
+ {
+ int idx = pStrip->indexOffset + i;
+
+ unsigned short i1 = *pStripGroup->pIndex( idx );
+ unsigned short i2 = *pStripGroup->pIndex( idx + 1 );
+ unsigned short i3 = *pStripGroup->pIndex( idx + 2 );
+
+ int vertex1 = pStripGroup->pVertex( i1 )->origMeshVertID;
+ int vertex2 = pStripGroup->pVertex( i2 )->origMeshVertID;
+ int vertex3 = pStripGroup->pVertex( i3 )->origMeshVertID;
+
+ // transform position into world coordinate system
+ matrix3x4_t matrix;
+ AngleMatrix( prop.m_Angles, prop.m_Origin, matrix );
+
+ Vector position1;
+ Vector position2;
+ Vector position3;
+ VectorTransform( *vertData->Position( vertex1 ), matrix, position1 );
+ VectorTransform( *vertData->Position( vertex2 ), matrix, position2 );
+ VectorTransform( *vertData->Position( vertex3 ), matrix, position3 );
+
+ Vector normal1;
+ Vector normal2;
+ Vector normal3;
+ VectorTransform( *vertData->Normal( vertex1 ), matrix, normal1 );
+ VectorTransform( *vertData->Normal( vertex2 ), matrix, normal2 );
+ VectorTransform( *vertData->Normal( vertex3 ), matrix, normal3 );
+
+ AddTriVertsToList( triListVerts, pMesh->vertexoffset + vertex1, position1, position1, position2, position3, normal1, normal2, normal3 );
+ AddTriVertsToList( triListVerts, pMesh->vertexoffset + vertex2, position2, position1, position2, position3, normal1, normal2, normal3 );
+ AddTriVertsToList( triListVerts, pMesh->vertexoffset + vertex3, position3, position1, position2, position3, normal1, normal2, normal3 );
+ }
+ }
+ else
+ {
+ // all tris expected to be discrete tri lists
+ // must fixme if stripping ever occurs
+ printf( "unexpected strips found\n" );
+ Assert( 0 );
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void *pModelData )
+{
+ studiohdr_t *pActiveStudioHdr = static_cast<studiohdr_t *>(pModelData);
+ Assert( pActiveStudioHdr );
+
+ if ( pActiveStudioHdr->pVertexBase )
+ {
+ return (vertexFileHeader_t *)pActiveStudioHdr->pVertexBase;
+ }
+
+ // mandatory callback to make requested data resident
+ // load and persist the vertex file
+ char fileName[MAX_PATH];
+ strcpy( fileName, "models/" );
+ strcat( fileName, pActiveStudioHdr->pszName() );
+ Q_StripExtension( fileName, fileName, sizeof( fileName ) );
+ strcat( fileName, ".vvd" );
+
+ // load the model
+ FileHandle_t fileHandle = g_pFileSystem->Open( fileName, "rb" );
+ if ( !fileHandle )
+ {
+ Error( "Unable to load vertex data \"%s\"\n", fileName );
+ }
+
+ // Get the file size
+ int vvdSize = g_pFileSystem->Size( fileHandle );
+ if ( vvdSize == 0 )
+ {
+ g_pFileSystem->Close( fileHandle );
+ Error( "Bad size for vertex data \"%s\"\n", fileName );
+ }
+
+ vertexFileHeader_t *pVvdHdr = (vertexFileHeader_t *)malloc( vvdSize );
+ g_pFileSystem->Read( pVvdHdr, vvdSize, fileHandle );
+ g_pFileSystem->Close( fileHandle );
+
+ // check header
+ if ( pVvdHdr->id != MODEL_VERTEX_FILE_ID )
+ {
+ Error("Error Vertex File %s id %d should be %d\n", fileName, pVvdHdr->id, MODEL_VERTEX_FILE_ID);
+ }
+ if ( pVvdHdr->version != MODEL_VERTEX_FILE_VERSION )
+ {
+ Error("Error Vertex File %s version %d should be %d\n", fileName, pVvdHdr->version, MODEL_VERTEX_FILE_VERSION);
+ }
+ if ( pVvdHdr->checksum != pActiveStudioHdr->checksum )
+ {
+ Error("Error Vertex File %s checksum %d should be %d\n", fileName, pVvdHdr->checksum, pActiveStudioHdr->checksum);
+ }
+
+ // need to perform mesh relocation fixups
+ // allocate a new copy
+ vertexFileHeader_t *pNewVvdHdr = (vertexFileHeader_t *)malloc( vvdSize );
+ if ( !pNewVvdHdr )
+ {
+ Error( "Error allocating %d bytes for Vertex File '%s'\n", vvdSize, fileName );
+ }
+
+ // load vertexes and run fixups
+ Studio_LoadVertexes( pVvdHdr, pNewVvdHdr, 0, true );
+
+ // discard original
+ free( pVvdHdr );
+ pVvdHdr = pNewVvdHdr;
+
+ pActiveStudioHdr->pVertexBase = (void*)pVvdHdr;
+ return pVvdHdr;
+}