diff options
| author | Narendra Umate <[email protected]> | 2013-12-02 23:36:05 -0800 |
|---|---|---|
| committer | Narendra Umate <[email protected]> | 2013-12-02 23:36:05 -0800 |
| commit | 8737f191f3b59f001a77bf6c08091109211c1c9f (patch) | |
| tree | dbbf05c004d9b026f2c1f23f06600fe0add82c36 /mp/src/utils/vrad | |
| parent | Update .gitignore. (diff) | |
| parent | Make .xcconfigs text files too. (diff) | |
| download | source-sdk-2013-8737f191f3b59f001a77bf6c08091109211c1c9f.tar.xz source-sdk-2013-8737f191f3b59f001a77bf6c08091109211c1c9f.zip | |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'mp/src/utils/vrad')
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 ¢roid = 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 ¢roid = 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; +} |