//========= 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 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 *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;iCount();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;iCount();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> 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 &tris=*( msg_in.m_pShadowTriangleList); if (tris.Count()) { // FILE *fp = fopen( "c:\\gl.out", "w" ); m_pRtEnv = new RayTracingEnvironment; for(int i=0;iAddTriangle( 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;yx, 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; iLightCount(); 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;iCount();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;iCount();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;yTrace4Rays( 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;yGetPixel( 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; } } } }