You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
864 lines
25 KiB
864 lines
25 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: The thread which performs lighting preview |
|
// |
|
//===========================================================================// |
|
|
|
#include "stdafx.h" |
|
#include "lpreview_thread.h" |
|
#include "mathlib/simdvectormatrix.h" |
|
#include "raytrace.h" |
|
#include "hammer.h" |
|
#include "mainfrm.h" |
|
#include "lprvwindow.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include <tier0/memdbgon.h> |
|
|
|
CInterlockedInt n_gbufs_queued; |
|
CInterlockedInt n_result_bms_queued; |
|
|
|
// the current lighting preview output, if we have one |
|
Bitmap_t *g_pLPreviewOutputBitmap; |
|
|
|
enum IncrementalLightState |
|
{ |
|
INCR_STATE_NO_RESULTS=0, // we threw away the results for this light |
|
INCR_STATE_PARTIAL_RESULTS=1, // have done some but not all |
|
INCR_STATE_NEW=2, // we know nothing about this light |
|
INCR_STATE_HAVE_FULL_RESULTS=3, // we are done |
|
}; |
|
|
|
|
|
class CLightingPreviewThread; |
|
|
|
class CIncrementalLightInfo |
|
{ |
|
public: |
|
CIncrementalLightInfo *m_pNext; |
|
CLightingPreviewLightDescription *m_pLight; |
|
// incremental lighting tracking information |
|
int m_nObjectID; |
|
int m_PartialResultsStage; |
|
IncrementalLightState m_eIncrState; |
|
CSIMDVectorMatrix m_CalculatedContribution; |
|
float m_fTotalContribution; // current magnitude of light effect |
|
int m_nBitmapGenerationCounter; // set on receive of new data from master |
|
float m_fDistanceToEye; |
|
int m_nMostRecentNonZeroContributionTimeStamp; |
|
|
|
CIncrementalLightInfo( void ) |
|
{ |
|
m_nObjectID = -1; |
|
m_pNext = NULL; |
|
m_eIncrState = INCR_STATE_NEW; |
|
m_fTotalContribution = 0.; |
|
m_PartialResultsStage = 0; |
|
m_nMostRecentNonZeroContributionTimeStamp = 0; |
|
} |
|
|
|
|
|
void DiscardResults( void ) |
|
{ |
|
m_CalculatedContribution.SetSize(0,0); |
|
if ( m_eIncrState != INCR_STATE_NEW ) |
|
m_eIncrState = INCR_STATE_NO_RESULTS; |
|
} |
|
|
|
void ClearIncremental( void ) |
|
{ |
|
m_eIncrState = INCR_STATE_NEW; |
|
// free calculated lighting matrix |
|
DiscardResults(); |
|
} |
|
|
|
bool HasWorkToDo( void ) const |
|
{ |
|
return ( m_eIncrState != INCR_STATE_HAVE_FULL_RESULTS ); |
|
} |
|
|
|
|
|
bool IsLowerPriorityThan( CLightingPreviewThread *pLPV, |
|
CIncrementalLightInfo const &other ) const; |
|
|
|
bool IsHighPriority( CLightingPreviewThread *pLPV ) const; |
|
}; |
|
|
|
#define N_INCREMENTAL_STEPS 32 |
|
|
|
class CLightingPreviewThread |
|
{ |
|
public: |
|
CUtlVector<CLightingPreviewLightDescription> *m_pLightList; |
|
|
|
CSIMDVectorMatrix m_Positions; |
|
CSIMDVectorMatrix m_Normals; |
|
CSIMDVectorMatrix m_Albedos; |
|
CSIMDVectorMatrix m_ResultImage; |
|
|
|
RayTracingEnvironment *m_pRtEnv; |
|
CIncrementalLightInfo *m_pIncrementalLightInfoList; |
|
|
|
bool m_bAccStructureBuilt; |
|
Vector m_LastEyePosition; |
|
|
|
bool m_bResultChangedSinceLastSend; |
|
float m_fLastSendTime; |
|
|
|
int m_LineMask[N_INCREMENTAL_STEPS]; |
|
int m_ClosestLineOffset[N_INCREMENTAL_STEPS][N_INCREMENTAL_STEPS]; |
|
int m_nBitmapGenerationCounter; |
|
int m_nContributionCounter; |
|
|
|
// bounidng box of the rendered scene+ the eye |
|
Vector m_MinViewCoords; |
|
Vector m_MaxViewCoords; |
|
|
|
CLightingPreviewThread(void) |
|
{ |
|
m_nBitmapGenerationCounter = -1; |
|
m_pLightList = NULL; |
|
m_pRtEnv = NULL; |
|
m_bAccStructureBuilt = false; |
|
m_pIncrementalLightInfoList = NULL; |
|
m_fLastSendTime = -1.0e6; |
|
m_bResultChangedSinceLastSend = false; |
|
m_nContributionCounter = 1000000; |
|
InitIncrementalInformation(); |
|
} |
|
|
|
void InitIncrementalInformation( void ); |
|
|
|
~CLightingPreviewThread( void ) |
|
{ |
|
if ( m_pLightList ) |
|
delete m_pLightList; |
|
while ( m_pIncrementalLightInfoList ) |
|
{ |
|
CIncrementalLightInfo *n=m_pIncrementalLightInfoList->m_pNext; |
|
delete m_pIncrementalLightInfoList; |
|
m_pIncrementalLightInfoList = n; |
|
} |
|
} |
|
|
|
// check if the master has new work for us to do, meaning we should abort rendering |
|
bool ShouldAbort( void ) |
|
{ |
|
return g_HammerToLPreviewMsgQueue.MessageWaiting(); |
|
} |
|
|
|
// main loop |
|
void Run(void); |
|
|
|
// handle new g-buffers from master |
|
void HandleGBuffersMessage( MessageToLPreview &msg_in ); |
|
|
|
// accept triangle list from master |
|
void HandleGeomMessage( MessageToLPreview &msg_in ); |
|
|
|
// send one of our output images back |
|
void SendVectorMatrixAsRendering( CSIMDVectorMatrix const &src ); |
|
|
|
// calculate m_MinViewCoords, m_MaxViewCoords - the bounding box of the rendered pixels+the eye |
|
void CalculateSceneBounds( void ); |
|
|
|
// inner lighting loop. meant to be multithreaded on dual-core (or more) |
|
void CalculateForLightTask( int nLineMask, int nLineMatch, |
|
CLightingPreviewLightDescription &l, |
|
int calc_mask, |
|
float *fContributionOut ); |
|
|
|
void CalculateForLight( CLightingPreviewLightDescription &l ); |
|
|
|
// send our current output back |
|
void SendResult( void ); |
|
|
|
void UpdateIncrementalForNewLightList( void ); |
|
|
|
void DiscardResults( void ) |
|
{ |
|
// invalidate all per light result data |
|
for( CIncrementalLightInfo *i=m_pIncrementalLightInfoList; i; i=i->m_pNext) |
|
{ |
|
i->DiscardResults(); |
|
} |
|
|
|
// bump time stamp |
|
m_nContributionCounter++; |
|
// update distances to lights |
|
if ( m_pLightList ) |
|
for(int i=0;i<m_pLightList->Count();i++) |
|
{ |
|
CLightingPreviewLightDescription &l=(*m_pLightList)[i]; |
|
CIncrementalLightInfo *l_info=l.m_pIncrementalInfo; |
|
if ( l.m_Type == MATERIAL_LIGHT_DIRECTIONAL ) |
|
l_info->m_fDistanceToEye = 0; // high priority |
|
else |
|
l_info->m_fDistanceToEye = m_LastEyePosition.DistTo( l.m_Position ); |
|
} |
|
m_bResultChangedSinceLastSend = true; |
|
m_fLastSendTime = Plat_FloatTime()-9; // force send |
|
} |
|
|
|
// handle a message. returns true if the thread shuold exit |
|
bool HandleAMessage( void ); |
|
|
|
// returns whether or not there is useful work to do |
|
bool AnyUsefulWorkToDo( void ); |
|
|
|
// do some work, like a rendering for one light |
|
void DoWork(void); |
|
|
|
Vector EstimatedUnshotAmbient( void ) |
|
{ |
|
// return Vector( 1,1,1 ); |
|
float sum_weights=0.0001; |
|
Vector sum_colors( sum_weights, sum_weights, sum_weights); |
|
// calculate an ambient color based on light calculcated so far |
|
if ( m_pLightList ) |
|
for(int i=0;i<m_pLightList->Count();i++) |
|
{ |
|
CLightingPreviewLightDescription &l=(*m_pLightList)[i]; |
|
CIncrementalLightInfo *l_info=l.m_pIncrementalInfo; |
|
if ( l_info && |
|
( l_info->m_eIncrState==INCR_STATE_HAVE_FULL_RESULTS ) || |
|
( l_info->m_eIncrState==INCR_STATE_PARTIAL_RESULTS) ) |
|
{ |
|
sum_weights+=l_info->m_fTotalContribution; |
|
sum_colors.x+=l_info->m_fTotalContribution*l.m_Color.x; |
|
sum_colors.y+=l_info->m_fTotalContribution*l.m_Color.y; |
|
sum_colors.z+=l_info->m_fTotalContribution*l.m_Color.z; |
|
} |
|
} |
|
sum_colors.NormalizeInPlace(); |
|
sum_colors *= 0.05; |
|
return sum_colors; |
|
} |
|
}; |
|
|
|
|
|
bool CIncrementalLightInfo::IsHighPriority( CLightingPreviewThread *pLPV ) const |
|
{ |
|
// is this lighjt prioirty-boosted in some way? |
|
if ( m_eIncrState == INCR_STATE_NEW ) |
|
{ |
|
// uncalculated lights within the view range are highest priority |
|
if ( m_pLight->m_Position.WithinAABox( pLPV->m_MinViewCoords, |
|
pLPV->m_MaxViewCoords ) ) |
|
return true; |
|
} |
|
return false; |
|
|
|
} |
|
|
|
bool CIncrementalLightInfo::IsLowerPriorityThan( CLightingPreviewThread *pLPV, |
|
CIncrementalLightInfo const &other ) const |
|
{ |
|
// a NEW light within the view volume is highest priority |
|
bool highpriority=IsHighPriority( pLPV ); |
|
bool other_highpriority=other.IsHighPriority( pLPV ); |
|
|
|
if ( highpriority && (! other_highpriority ) ) |
|
return false; |
|
if ( other_highpriority && (! highpriority ) ) |
|
return true; |
|
|
|
int state_combo = m_eIncrState + 16*other.m_eIncrState; |
|
switch ( state_combo ) |
|
{ |
|
case INCR_STATE_NEW+16*INCR_STATE_NEW: |
|
{ |
|
// if both are new, closest to eye is best |
|
return ( m_fDistanceToEye > other.m_fDistanceToEye ); |
|
} |
|
|
|
case INCR_STATE_NEW+16*INCR_STATE_NO_RESULTS: |
|
{ |
|
// new loses to something we know is probably going to contribute light |
|
return ( other.m_fTotalContribution > 0 ); |
|
} |
|
|
|
case INCR_STATE_NEW+16*INCR_STATE_PARTIAL_RESULTS: |
|
{ |
|
return false; |
|
} |
|
|
|
case INCR_STATE_PARTIAL_RESULTS+16*INCR_STATE_NEW: |
|
{ |
|
return true; |
|
} |
|
|
|
case INCR_STATE_NO_RESULTS+16*INCR_STATE_NEW: |
|
{ |
|
// partial or discarded with no brightness loses to new |
|
return ( m_fTotalContribution == 0 ); |
|
} |
|
|
|
|
|
case INCR_STATE_PARTIAL_RESULTS+16*INCR_STATE_PARTIAL_RESULTS: |
|
{ |
|
// if incrmental vs incremental, and no light from either, do most recently lit one |
|
if (( m_fTotalContribution == 0.0) && (other.m_fTotalContribution == 0.0) && |
|
( other.m_nMostRecentNonZeroContributionTimeStamp > m_nMostRecentNonZeroContributionTimeStamp ) ) |
|
return true; |
|
|
|
// if other is black, keep this one |
|
if ( (other.m_fTotalContribution == 0.0) && (m_fTotalContribution >0 ) ) |
|
return false; |
|
if ( (m_fTotalContribution == 0.0) && (other.m_fTotalContribution >0 ) ) |
|
return true; |
|
|
|
// if incremental states are close, do brightest |
|
if ( abs( m_PartialResultsStage-other.m_PartialResultsStage)<=1 ) |
|
return ( m_fTotalContribution < other.m_fTotalContribution ); |
|
|
|
// else do least refined |
|
return ( m_PartialResultsStage > other.m_PartialResultsStage ); |
|
} |
|
case INCR_STATE_PARTIAL_RESULTS+16*INCR_STATE_NO_RESULTS: |
|
{ |
|
if ( other.m_fTotalContribution ) |
|
return true; |
|
if (( m_fTotalContribution == 0.0) && (other.m_fTotalContribution == 0.0) ) |
|
return ( other.m_nMostRecentNonZeroContributionTimeStamp > m_nMostRecentNonZeroContributionTimeStamp ); |
|
return ( m_fTotalContribution < other.m_fTotalContribution ); |
|
} |
|
case INCR_STATE_NO_RESULTS+16*INCR_STATE_PARTIAL_RESULTS: |
|
{ |
|
if ( m_fTotalContribution ) |
|
return false; |
|
if (( m_fTotalContribution == 0.0) && (other.m_fTotalContribution == 0.0) ) |
|
return ( other.m_nMostRecentNonZeroContributionTimeStamp > m_nMostRecentNonZeroContributionTimeStamp ); |
|
return ( m_fTotalContribution < other.m_fTotalContribution ); |
|
} |
|
case INCR_STATE_NO_RESULTS*16+INCR_STATE_NO_RESULTS: |
|
{ |
|
// if incrmental vs discarded, brightest or most recently bright wins |
|
if (( m_fTotalContribution == 0.0) && (other.m_fTotalContribution == 0.0) ) |
|
return ( other.m_nMostRecentNonZeroContributionTimeStamp > m_nMostRecentNonZeroContributionTimeStamp ); |
|
return ( m_fTotalContribution < other.m_fTotalContribution ); |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
void CLightingPreviewThread::InitIncrementalInformation( void ) |
|
{ |
|
int calculated_bit_mask=0; |
|
for(int i=0;i<N_INCREMENTAL_STEPS;i++) |
|
{ |
|
// bit reverse i |
|
int msk=0; |
|
int msk_or=1; |
|
int msk_test=(N_INCREMENTAL_STEPS >> 1); |
|
while( msk_test ) |
|
{ |
|
if ( i & msk_test ) |
|
msk |= msk_or; |
|
msk_or <<= 1; |
|
msk_test >>= 1; |
|
} |
|
calculated_bit_mask |= (1<< msk); |
|
m_LineMask[i] = calculated_bit_mask; |
|
} |
|
// now, find which line to use when resampling a partial result |
|
for( int lvl=0; lvl < N_INCREMENTAL_STEPS; lvl++) |
|
{ |
|
for(int linemod=0; linemod <=N_INCREMENTAL_STEPS; linemod++) |
|
{ |
|
int closest_line=1000000; |
|
for( int chk=0; chk <= linemod; chk++) |
|
if ( m_LineMask[lvl] & ( 1 << chk )) |
|
{ |
|
if (abs( chk-linemod ) < abs( closest_line-linemod ) ) |
|
closest_line = chk; |
|
} |
|
m_ClosestLineOffset[lvl][linemod] = closest_line; |
|
} |
|
} |
|
} |
|
|
|
float cg[3]={ 1,0,0}; |
|
float cr[3]={ 0,1,0 }; |
|
float cb[3]={ 0,0,1 }; |
|
|
|
void CLightingPreviewThread::HandleGeomMessage( MessageToLPreview &msg_in ) |
|
{ |
|
if (m_pRtEnv) |
|
{ |
|
delete m_pRtEnv; |
|
m_pRtEnv = NULL; |
|
} |
|
CUtlVector<Vector> &tris=*( msg_in.m_pShadowTriangleList); |
|
if (tris.Count()) |
|
{ |
|
// FILE *fp = fopen( "c:\\gl.out", "w" ); |
|
m_pRtEnv = new RayTracingEnvironment; |
|
for(int i=0;i<tris.Count();i+=3) |
|
{ |
|
// fprintf(fp,"3\n"); |
|
// for(int j=0;j<3;j++) |
|
// fprintf( fp,"%f %f %f %f %f %f\n", tris[j+i].x,tris[j+i].y,tris[j+i].z, cr[j],cg[j],cb[j] ); |
|
m_pRtEnv->AddTriangle( i, tris[i],tris[1+i],tris[2+i], Vector( .5,.5,.5) ); |
|
} |
|
// fclose( fp ); |
|
} |
|
delete msg_in.m_pShadowTriangleList; |
|
m_bAccStructureBuilt = false; |
|
DiscardResults(); |
|
|
|
} |
|
|
|
|
|
void CLightingPreviewThread::CalculateSceneBounds( void ) |
|
{ |
|
FourVectors minbound, maxbound; |
|
minbound.DuplicateVector( m_LastEyePosition ); |
|
maxbound.DuplicateVector( m_LastEyePosition ); |
|
for(int y=0;y<m_Positions.m_nHeight;y++) |
|
{ |
|
FourVectors const *cptr= &(m_Positions.CompoundElement(0, y ) ); |
|
for(int x=0; x<m_Positions.m_nPaddedWidth; x++) |
|
{ |
|
minbound.x=MinSIMD( cptr->x, minbound.x); |
|
minbound.y=MinSIMD( cptr->y, minbound.y); |
|
minbound.z=MinSIMD( cptr->z, minbound.z); |
|
|
|
maxbound.x=MaxSIMD( cptr->x, maxbound.x); |
|
maxbound.y=MaxSIMD( cptr->y, maxbound.y); |
|
maxbound.z=MaxSIMD( cptr->z, maxbound.z); |
|
cptr++; |
|
} |
|
} |
|
m_MinViewCoords=minbound.Vec(0); |
|
m_MaxViewCoords=maxbound.Vec(0); |
|
for(int v=1; v<4; v++) |
|
{ |
|
m_MinViewCoords=m_MinViewCoords.Min( minbound.Vec(v) ); |
|
m_MaxViewCoords=m_MaxViewCoords.Max( maxbound.Vec(v) ); |
|
} |
|
} |
|
|
|
|
|
void CLightingPreviewThread::UpdateIncrementalForNewLightList( void ) |
|
{ |
|
for( int iLight=0; iLight<m_pLightList->Count(); iLight++) |
|
{ |
|
CLightingPreviewLightDescription &descr=(*m_pLightList)[iLight]; |
|
// see if we know about this light |
|
for( CIncrementalLightInfo *i=m_pIncrementalLightInfoList; i; i=i->m_pNext) |
|
{ |
|
if (i->m_nObjectID == descr.m_nObjectID ) |
|
{ |
|
// found it! |
|
descr.m_pIncrementalInfo = i; |
|
i->m_pLight = &descr; |
|
break; |
|
} |
|
} |
|
if ( ! descr.m_pIncrementalInfo ) |
|
{ |
|
descr.m_pIncrementalInfo = new CIncrementalLightInfo; |
|
descr.m_pIncrementalInfo->m_nObjectID = descr.m_nObjectID; |
|
descr.m_pIncrementalInfo->m_pLight = &descr; |
|
|
|
// add to list |
|
descr.m_pIncrementalInfo->m_pNext = m_pIncrementalLightInfoList; |
|
m_pIncrementalLightInfoList = descr.m_pIncrementalInfo; |
|
} |
|
} |
|
} |
|
|
|
|
|
void CLightingPreviewThread::Run(void) |
|
{ |
|
bool should_quit = false; |
|
while(! should_quit ) |
|
{ |
|
while ( |
|
(! should_quit ) && |
|
( (! AnyUsefulWorkToDo() ) || ( g_HammerToLPreviewMsgQueue.MessageWaiting() ) ) ) |
|
should_quit |= HandleAMessage(); |
|
if ( (! should_quit) && (AnyUsefulWorkToDo() ) ) |
|
DoWork(); |
|
if ( m_bResultChangedSinceLastSend ) |
|
{ |
|
float newtime=Plat_FloatTime(); |
|
if ( (newtime-m_fLastSendTime > 10.0) || ( ! AnyUsefulWorkToDo() ) ) |
|
{ |
|
SendResult(); |
|
m_bResultChangedSinceLastSend = false; |
|
m_fLastSendTime = newtime; |
|
} |
|
} |
|
|
|
} |
|
} |
|
|
|
bool CLightingPreviewThread::HandleAMessage( void ) |
|
{ |
|
MessageToLPreview msg_in; |
|
g_HammerToLPreviewMsgQueue.WaitMessage( &msg_in ); |
|
switch( msg_in.m_MsgType) |
|
{ |
|
case LPREVIEW_MSG_EXIT: |
|
return true; // return from thread |
|
|
|
case LPREVIEW_MSG_LIGHT_DATA: |
|
{ |
|
if ( m_pLightList ) |
|
delete m_pLightList; |
|
m_pLightList = msg_in.m_pLightList; |
|
m_LastEyePosition = msg_in.m_EyePosition; |
|
UpdateIncrementalForNewLightList(); |
|
DiscardResults(); |
|
} |
|
break; |
|
|
|
case LPREVIEW_MSG_GEOM_DATA: |
|
HandleGeomMessage( msg_in ); |
|
DiscardResults(); |
|
break; |
|
|
|
case LPREVIEW_MSG_G_BUFFERS: |
|
HandleGBuffersMessage( msg_in ); |
|
DiscardResults(); |
|
break; |
|
} |
|
return false; |
|
} |
|
|
|
bool CLightingPreviewThread::AnyUsefulWorkToDo( void ) |
|
{ |
|
if ( m_pLightList ) |
|
{ |
|
for(int i=0;i<m_pLightList->Count();i++) |
|
{ |
|
CLightingPreviewLightDescription &l=(*m_pLightList)[i]; |
|
CIncrementalLightInfo *l_info=l.m_pIncrementalInfo; |
|
if ( l_info->HasWorkToDo() ) |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
void CLightingPreviewThread::DoWork( void ) |
|
{ |
|
if ( m_pLightList ) |
|
{ |
|
CLightingPreviewLightDescription *best_l=NULL; |
|
CIncrementalLightInfo *best_l_info=NULL; |
|
for(int i=0;i<m_pLightList->Count();i++) |
|
{ |
|
CLightingPreviewLightDescription &l=(*m_pLightList)[i]; |
|
CIncrementalLightInfo *l_info=l.m_pIncrementalInfo; |
|
if ( l_info->HasWorkToDo() ) |
|
{ |
|
if ( (! best_l) || |
|
(best_l->m_pIncrementalInfo->IsLowerPriorityThan( this, *l_info )) ) |
|
{ |
|
best_l_info=l_info; |
|
best_l=&l; |
|
} |
|
} |
|
} |
|
if ( best_l ) |
|
{ |
|
CalculateForLight( *best_l ); |
|
if ( best_l->m_pIncrementalInfo->m_fTotalContribution ) |
|
{ |
|
m_bResultChangedSinceLastSend = true; |
|
} |
|
return; |
|
} |
|
} |
|
} |
|
|
|
|
|
void CLightingPreviewThread::HandleGBuffersMessage( MessageToLPreview &msg_in ) |
|
{ |
|
m_Albedos.CreateFromRGBA_FloatImageData( |
|
msg_in.m_pDefferedRenderingBMs[0]->Width,msg_in.m_pDefferedRenderingBMs[0]->Height, |
|
msg_in.m_pDefferedRenderingBMs[0]->RGBAData); |
|
m_Normals.CreateFromRGBA_FloatImageData( |
|
msg_in.m_pDefferedRenderingBMs[1]->Width,msg_in.m_pDefferedRenderingBMs[1]->Height, |
|
msg_in.m_pDefferedRenderingBMs[1]->RGBAData); |
|
m_Positions.CreateFromRGBA_FloatImageData( |
|
msg_in.m_pDefferedRenderingBMs[2]->Width,msg_in.m_pDefferedRenderingBMs[2]->Height, |
|
msg_in.m_pDefferedRenderingBMs[2]->RGBAData); |
|
|
|
m_LastEyePosition = msg_in.m_EyePosition; |
|
for( int i = 0;i < ARRAYSIZE( msg_in.m_pDefferedRenderingBMs ); i++ ) |
|
delete msg_in.m_pDefferedRenderingBMs[i]; |
|
n_gbufs_queued--; |
|
m_nBitmapGenerationCounter = msg_in.m_nBitmapGenerationCounter; |
|
CalculateSceneBounds(); |
|
|
|
} |
|
|
|
|
|
void CLightingPreviewThread::SendResult( void ) |
|
{ |
|
m_ResultImage = m_Albedos; |
|
m_ResultImage *= EstimatedUnshotAmbient(); |
|
for( int i = 0 ; i < m_pLightList->Count(); i ++ ) |
|
{ |
|
CLightingPreviewLightDescription & l = ( *m_pLightList )[i]; |
|
CIncrementalLightInfo * l_info = l.m_pIncrementalInfo; |
|
if ( ( l_info->m_fTotalContribution > 0.0 ) && |
|
( l_info->m_eIncrState >= INCR_STATE_PARTIAL_RESULTS ) ) |
|
{ |
|
// need to add partials, replicated to handle undone lines |
|
CSIMDVectorMatrix & src = l_info->m_CalculatedContribution; |
|
for( int y = 0;y < m_ResultImage.m_nHeight;y ++ ) |
|
{ |
|
int yo = y & ( N_INCREMENTAL_STEPS - 1 ); |
|
int src_y = ( y & ~( N_INCREMENTAL_STEPS - 1 )) |
|
+ m_ClosestLineOffset[l_info->m_PartialResultsStage][yo]; |
|
FourVectors const * cptr = &( src.CompoundElement( 0, src_y )); |
|
FourVectors * dest =& ( m_ResultImage.CompoundElement( 0, y )); |
|
FourVectors const *pAlbedo =&( m_Albedos.CompoundElement( 0, y )); |
|
for( int x = 0;x < m_ResultImage.m_nPaddedWidth;x ++ ) |
|
{ |
|
FourVectors albedo_value = *( pAlbedo++ ); |
|
albedo_value *= *( cptr++ ); |
|
* ( dest++ ) += albedo_value; |
|
} |
|
} |
|
} |
|
} |
|
SendVectorMatrixAsRendering( m_ResultImage ); |
|
m_fLastSendTime = Plat_FloatTime(); |
|
m_bResultChangedSinceLastSend = false; |
|
} |
|
|
|
void CLightingPreviewThread::CalculateForLightTask( int nLineMask, int nLineMatch, |
|
CLightingPreviewLightDescription &l, |
|
int calc_mask, |
|
float *fContributionOut ) |
|
{ |
|
FourVectors zero_vector; |
|
zero_vector.x=Four_Zeros; |
|
zero_vector.y=Four_Zeros; |
|
zero_vector.z=Four_Zeros; |
|
|
|
FourVectors total_light=zero_vector; |
|
|
|
CIncrementalLightInfo *l_info=l.m_pIncrementalInfo; |
|
CSIMDVectorMatrix &rslt=l_info->m_CalculatedContribution; |
|
// figure out what lines to do |
|
fltx4 ThresholdBrightness=ReplicateX4( 0.1 / 1024.0 ); |
|
FourVectors LastLinesTotalLight=zero_vector; |
|
int work_line_number=0; // for task masking |
|
for(int y=0;y<rslt.m_nHeight;y++) |
|
{ |
|
FourVectors ThisLinesTotalLight=zero_vector; |
|
int ybit=(1<<(y & (N_INCREMENTAL_STEPS-1) ) ); |
|
if ( (ybit & calc_mask)==0) // do this line? |
|
ThisLinesTotalLight=LastLinesTotalLight; |
|
else |
|
{ |
|
if ( (work_line_number & nLineMatch) == nLineMatch) |
|
{ |
|
for(int x=0;x<rslt.m_nPaddedWidth;x++) |
|
{ |
|
// shadow check |
|
FourVectors pos=m_Positions.CompoundElement( x, y ); |
|
FourVectors normal=m_Normals.CompoundElement( x, y ); |
|
|
|
FourVectors l_add=zero_vector; |
|
l.ComputeLightAtPoints( pos, normal, l_add, false ); |
|
fltx4 v_or=OrSIMD( l_add.x, OrSIMD( l_add.y, l_add.z ) ); |
|
if ( ! IsAllZeros( v_or ) ) |
|
{ |
|
FourVectors lpos; |
|
lpos.DuplicateVector( l.m_Position ); |
|
|
|
FourRays myray; |
|
myray.direction=lpos; |
|
myray.direction-=pos; |
|
fltx4 len=myray.direction.length(); |
|
myray.direction *= ReciprocalSIMD( len ); |
|
|
|
// slide towards light to avoid self-intersection |
|
myray.origin=myray.direction; |
|
myray.origin *= 0.02; |
|
myray.origin += pos; |
|
|
|
RayTracingResult r_rslt; |
|
m_pRtEnv->Trace4Rays( myray, Four_Zeros, ReplicateX4( 1.0e9 ), &r_rslt ); |
|
|
|
for(int c=0;c<4;c++) // !!speed!! use sse logic ops here |
|
{ |
|
if ( (r_rslt.HitIds[c] != -1) && |
|
(r_rslt.HitDistance.m128_f32[c] < len.m128_f32[c] ) ) |
|
{ |
|
l_add.x.m128_f32[c]=0.0; |
|
l_add.y.m128_f32[c]=0.0; |
|
l_add.z.m128_f32[c]=0.0; |
|
} |
|
} |
|
rslt.CompoundElement( x, y ) = l_add; |
|
l_add *= m_Albedos.CompoundElement( x, y ); |
|
// now, supress brightness < threshold so as to not falsely think |
|
// far away lights are interesting |
|
l_add.x = AndSIMD( l_add.x, CmpGtSIMD( l_add.x, ThresholdBrightness ) ); |
|
l_add.y = AndSIMD( l_add.y, CmpGtSIMD( l_add.y, ThresholdBrightness ) ); |
|
l_add.z = AndSIMD( l_add.z, CmpGtSIMD( l_add.z, ThresholdBrightness ) ); |
|
ThisLinesTotalLight += l_add; |
|
} |
|
else |
|
rslt.CompoundElement( x, y ) = l_add; |
|
} |
|
total_light += ThisLinesTotalLight; |
|
} |
|
work_line_number++; |
|
} |
|
} |
|
fltx4 lmag=total_light.length(); |
|
*(fContributionOut)=lmag.m128_f32[0]+lmag.m128_f32[1]+lmag.m128_f32[2]+lmag.m128_f32[3]; |
|
} |
|
|
|
void CLightingPreviewThread::CalculateForLight( CLightingPreviewLightDescription &l ) |
|
{ |
|
if ( m_pRtEnv && (! m_bAccStructureBuilt ) ) |
|
{ |
|
m_bAccStructureBuilt = true; |
|
m_pRtEnv->SetupAccelerationStructure(); |
|
} |
|
CIncrementalLightInfo *l_info=l.m_pIncrementalInfo; |
|
Assert( l_info ); |
|
l_info->m_CalculatedContribution.SetSize( m_Albedos.m_nWidth, m_Albedos.m_nHeight ); |
|
|
|
// figure out which lines need to be calculated |
|
int prev_msk=0; |
|
int new_incr_level=0; |
|
if ( l_info->m_eIncrState == INCR_STATE_PARTIAL_RESULTS ) |
|
{ |
|
new_incr_level = 1+l_info->m_PartialResultsStage; |
|
prev_msk = m_LineMask[l_info->m_PartialResultsStage]; |
|
} |
|
int calc_mask=m_LineMask[new_incr_level] &~ prev_msk; |
|
|
|
// multihread here |
|
float total_light; |
|
CalculateForLightTask( 0, 0, l, calc_mask, &total_light ); |
|
l_info->m_fTotalContribution = total_light; |
|
|
|
// throw away light array if no contribution |
|
if ( l_info->m_fTotalContribution == 0.0 ) |
|
l_info->m_CalculatedContribution.SetSize( 0, 0 ); |
|
else |
|
{ |
|
l_info->m_nMostRecentNonZeroContributionTimeStamp = m_nContributionCounter; |
|
} |
|
l_info->m_PartialResultsStage = new_incr_level; |
|
if ( new_incr_level == N_INCREMENTAL_STEPS-1) |
|
l_info->m_eIncrState = INCR_STATE_HAVE_FULL_RESULTS; |
|
else |
|
l_info->m_eIncrState = INCR_STATE_PARTIAL_RESULTS; |
|
} |
|
|
|
void CLightingPreviewThread::SendVectorMatrixAsRendering( CSIMDVectorMatrix const &src ) |
|
{ |
|
Bitmap_t *ret_bm=new Bitmap_t; |
|
ret_bm->Init( src.m_nWidth, src.m_nHeight, IMAGE_FORMAT_RGBA8888 ); |
|
// lets copy into the output bitmap |
|
for(int y=0;y<src.m_nHeight;y++) |
|
for(int x=0;x<src.m_nWidth;x++) |
|
{ |
|
Vector color=src.Element( x, y ); |
|
*(ret_bm->GetPixel( x, y )+0)= (uint8) min(255, (int)(255.0*pow(color.z,(float) (1/2.2)))); |
|
*(ret_bm->GetPixel( x, y )+1)= (uint8) min(255, (int)(255.0*pow(color.y,(float) (1/2.2)))); |
|
*(ret_bm->GetPixel( x, y )+2)= (uint8) min(255, (int)(255.0*pow(color.x,(float) (1/2.2)))); |
|
*(ret_bm->GetPixel( x, y )+3)=0; |
|
} |
|
MessageFromLPreview ret_msg( LPREVIEW_MSG_DISPLAY_RESULT ); |
|
// n_result_bms_queued++; |
|
ret_msg.m_pBitmapToDisplay = ret_bm; |
|
ret_msg.m_nBitmapGenerationCounter = m_nBitmapGenerationCounter; |
|
g_LPreviewToHammerMsgQueue.QueueMessage( ret_msg ); |
|
} |
|
|
|
|
|
|
|
|
|
// master side of lighting preview |
|
unsigned LightingPreviewThreadFN( void *thread_start_arg ) |
|
{ |
|
CLightingPreviewThread LPreviewObject; |
|
ThreadSetPriority( -2 ); // low |
|
LPreviewObject.Run(); |
|
return 0; |
|
} |
|
|
|
|
|
void HandleLightingPreview( void ) |
|
{ |
|
if ( GetMainWnd()->m_pLightingPreviewOutputWindow && !GetMainWnd()->m_bLightingPreviewOutputWindowShowing ) |
|
{ |
|
delete GetMainWnd()->m_pLightingPreviewOutputWindow; |
|
GetMainWnd()->m_pLightingPreviewOutputWindow = NULL; |
|
} |
|
|
|
// called during main loop |
|
while ( g_LPreviewToHammerMsgQueue.MessageWaiting() ) |
|
{ |
|
MessageFromLPreview msg; |
|
g_LPreviewToHammerMsgQueue.WaitMessage( &msg ); |
|
switch( msg.m_MsgType ) |
|
{ |
|
case LPREVIEW_MSG_DISPLAY_RESULT: |
|
{ |
|
n_result_bms_queued--; |
|
if (g_pLPreviewOutputBitmap) |
|
delete g_pLPreviewOutputBitmap; |
|
g_pLPreviewOutputBitmap = NULL; |
|
// if ( msg.m_nBitmapGenerationCounter == g_nBitmapGenerationCounter ) |
|
{ |
|
g_pLPreviewOutputBitmap = msg.m_pBitmapToDisplay; |
|
if ( g_pLPreviewOutputBitmap && (g_pLPreviewOutputBitmap->Width() > 10) ) |
|
{ |
|
SignalUpdate( EVTYPE_BITMAP_RECEIVED_FROM_LPREVIEW ); |
|
CLightingPreviewResultsWindow *w=GetMainWnd()->m_pLightingPreviewOutputWindow; |
|
if ( !GetMainWnd()->m_bLightingPreviewOutputWindowShowing ) |
|
{ |
|
w = new CLightingPreviewResultsWindow; |
|
GetMainWnd()->m_pLightingPreviewOutputWindow = w; |
|
w->Create( GetMainWnd() ); |
|
GetMainWnd()->m_bLightingPreviewOutputWindowShowing = true; |
|
} |
|
if (! w->IsWindowVisible() ) |
|
w->ShowWindow( SW_SHOW ); |
|
RECT existing_rect; |
|
w->GetClientRect( &existing_rect ); |
|
if ( |
|
(existing_rect.right != g_pLPreviewOutputBitmap->Width()-1) || |
|
(existing_rect.bottom != g_pLPreviewOutputBitmap->Height()-1) ) |
|
{ |
|
CRect myRect; |
|
myRect.top=0; |
|
myRect.left=0; |
|
myRect.right=g_pLPreviewOutputBitmap->Width()-1; |
|
myRect.bottom=g_pLPreviewOutputBitmap->Height()-1; |
|
w->CalcWindowRect(&myRect); |
|
w->SetWindowPos( |
|
NULL,0,0, |
|
myRect.Width(), myRect.Height(), |
|
SWP_NOMOVE | SWP_NOZORDER ); |
|
} |
|
|
|
w->Invalidate( false ); |
|
w->UpdateWindow(); |
|
} |
|
} |
|
// else |
|
// delete msg.m_pBitmapToDisplay; // its old |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|