/* Copyright (c) 2002-2012 Croteam Ltd. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "Engine/StdH.h" #include #include #include #include #include #include #include #include #include #include #include #include #include //#include //#include INDEX _ctShadowLayers=0; INDEX _ctShadowClusters=0; // class used for making shadow layers (used only locally) class CLayerMaker { // implementation: public: // information about current polygon CBrushPolygon *lm_pbpoPolygon; // the polygon CBrushShadowMap *lm_pbsmShadowMap; // the shadow map on the polygon FLOAT lm_fMipFactor; // the mip factor of this polygon CWorld *lm_pwoWorld; // world for casting rays in CBrushShadowLayer *lm_pbslLayer;// current layer // dimensions of currently processed shadow map MEX lm_mexSizeU; // size in mex MEX lm_mexSizeV; MEX lm_mexOffsetU; // offsets in mex MEX lm_mexOffsetV; INDEX lm_iMipLevel; // mip level PIX lm_pixSizeU; // size in pixels PIX lm_pixSizeV; struct MipmapTable lm_mmtPolygonMask; // mip map table of the polygon mask PIX lm_pixLayerMinU; // layer rectangle inside shadow map PIX lm_pixLayerMinV; PIX lm_pixLayerSizeU; PIX lm_pixLayerSizeV; FLOAT lm_fpixHotU; FLOAT lm_fpixHotV; FLOAT lm_fLightPlaneDistance; struct MipmapTable lm_mmtLayer; // mip map table of the layer UBYTE *lm_pubPolygonMask; // bit-packed mask of where the current polygon is UBYTE *lm_pubLayer; // bit-packed mask of where the current light lights the polygon FLOAT3D lm_vLight; // position of the light source // gradients for shadow map walking FLOAT3D lm_vO; // upper left corner of shadow map in 3D FLOAT3D lm_vStepU; // step between pixels in same row FLOAT3D lm_vStepV; // step between rows ANGLE3D lm_aMappingOrientation; // orientation of the texture map in 3D ANGLE3D lm_aInverseMappingOrientation; FLOATmatrix3D lm_mToInverseMapping; // matrix for parallel lights CLightSource *lm_plsLight; // current light // remember general data void CalculateData(void); // make bit-packed mask of where the polygon is in the shadow map void MakePolygonMask(void); // make shadow mask for the light ULONG MakeShadowMask(CBrushShadowLayer *pbsl); ULONG MakeOneShadowMaskMip(INDEX iMip); // flip shadow mask around V axis (for parallel lights) void FlipShadowMask(INDEX iMip); /* Spread the shadow towards pixels outside of polygon. */ void SpreadShadowMaskOutwards(void); /* Spread the shadow towards pixels inside of polygon. */ void SpreadShadowMaskInwards(void); public: // interface: /* Constructor. */ CLayerMaker(void); /* Cast shadows for all layers of a given polygon. */ BOOL CreateLayers(CBrushPolygon &bpo, CWorld &woWorld, BOOL bDoDirectionalLights); }; /* Make mip-maps of the shadow mask. */ static void MakeMipmapsForMask(UBYTE *pubMask, PIX pixSizeU, PIX pixSizeV, SLONG slTotalSize) { // remember pointer after first mip map UBYTE *pubSecond = pubMask+pixSizeU*pixSizeV; // start at the first mip map UBYTE *pubThis = pubMask; PIX pixThisSizeU = pixSizeU; PIX pixThisSizeV = pixSizeV; UBYTE *pubNext; PIX pixNextSizeU; PIX pixNextSizeV; // repeat for(;;) { // calculate size and position of next mip map pubNext = pubThis+pixThisSizeU*pixThisSizeV; pixNextSizeU = pixThisSizeU/2; pixNextSizeV = pixThisSizeV/2; // if the next mip map is too small if (pixNextSizeU<1 || pixNextSizeV<1) { // stop break; } // for each pixel in the next mip map UBYTE *pub = pubNext; for (PIX pixNextV=0; pixNextVsm_mexWidth; lm_mexSizeV = lm_pbsmShadowMap->sm_mexHeight; lm_mexOffsetU = lm_pbsmShadowMap->sm_mexOffsetX; lm_mexOffsetV = lm_pbsmShadowMap->sm_mexOffsetY; lm_iMipLevel = lm_pbsmShadowMap->sm_iFirstMipLevel; lm_pixSizeU = lm_mexSizeU>>lm_iMipLevel; lm_pixSizeV = lm_mexSizeV>>lm_iMipLevel; // find mip-mapping information for the polygon mask MakeMipmapTable( lm_pixSizeU, lm_pixSizeV, lm_mmtPolygonMask); CEntity *penWithPolygon = lm_pbpoPolygon->bpo_pbscSector->bsc_pbmBrushMip->bm_pbrBrush->br_penEntity; ASSERT(penWithPolygon!=NULL); const FLOATmatrix3D &mPolygonRotation = penWithPolygon->en_mRotation; const FLOAT3D &vPolygonTranslation = penWithPolygon->GetPlacement().pl_PositionVector; // get first pixel in texture in 3D Vector vmex0; vmex0(1) = -lm_mexOffsetU; //+(1<<(lm_iMipLevel-1)); vmex0(2) = -lm_mexOffsetV; //+(1<<(lm_iMipLevel-1)); lm_pbpoPolygon->bpo_mdShadow.GetSpaceCoordinates( lm_pbpoPolygon->bpo_pbplPlane->bpl_pwplWorking->wpl_mvRelative, vmex0, lm_vO); lm_vO = lm_vO*mPolygonRotation+vPolygonTranslation; // get steps for walking in texture in 3D Vector vmexU, vmexV; vmexU(1) = (1<bpo_mdShadow.GetSpaceCoordinates( lm_pbpoPolygon->bpo_pbplPlane->bpl_pwplWorking->wpl_mvRelative, vmexU, lm_vStepU); lm_vStepU = lm_vStepU*mPolygonRotation+vPolygonTranslation; lm_pbpoPolygon->bpo_mdShadow.GetSpaceCoordinates( lm_pbpoPolygon->bpo_pbplPlane->bpl_pwplWorking->wpl_mvRelative, vmexV, lm_vStepV); lm_vStepV = lm_vStepV*mPolygonRotation+vPolygonTranslation; lm_vStepU-=lm_vO; lm_vStepV-=lm_vO; // make 3 orthogonal vectors that define mapping orientation FLOAT3D vX = lm_vStepU; FLOAT3D vY = -lm_vStepV; FLOAT3D vZ = vX*vY; // make a rotation matrix from those vectors vX.Normalize(); vY.Normalize(); vZ.Normalize(); FLOATmatrix3D mOrientation; mOrientation(1,1) = vX(1); mOrientation(1,2) = vY(1); mOrientation(1,3) = vZ(1); mOrientation(2,1) = vX(2); mOrientation(2,2) = vY(2); mOrientation(2,3) = vZ(2); mOrientation(3,1) = vX(3); mOrientation(3,2) = vY(3); mOrientation(3,3) = vZ(3); FLOATmatrix3D mInvOrientation; mInvOrientation(1,1) = -vX(1); mInvOrientation(1,2) = vY(1); mInvOrientation(1,3) = -vZ(1); mInvOrientation(2,1) = -vX(2); mInvOrientation(2,2) = vY(2); mInvOrientation(2,3) = -vZ(2); mInvOrientation(3,1) = -vX(3); mInvOrientation(3,2) = vY(3); mInvOrientation(3,3) = -vZ(3); // make orientation angles from the matrix DecomposeRotationMatrixNoSnap(lm_aMappingOrientation, mOrientation); DecomposeRotationMatrixNoSnap(lm_aInverseMappingOrientation, mInvOrientation); // remember matrix for parallel lights lm_mToInverseMapping = !mInvOrientation; } // test if a point is inside a brush polygon inline BOOL TestPointInPolygon(CBrushPolygon &bpo, const FLOAT3D &v) { // find major axes of the polygon plane INDEX iMajorAxis1 = bpo.bpo_pbplPlane->bpl_iPlaneMajorAxis1; INDEX iMajorAxis2 = bpo.bpo_pbplPlane->bpl_iPlaneMajorAxis2; // if the point is not inside the bounding box of polygon (projected to the major plane) if (v(iMajorAxis1)bpo.bpo_boxBoundingBox.Max()(iMajorAxis1) ||v(iMajorAxis2)bpo.bpo_boxBoundingBox.Max()(iMajorAxis2) ) { // it is not inside the polygon return FALSE; } // create an intersector CIntersector isIntersector(v(iMajorAxis1), v(iMajorAxis2)); // for all edges in the polygon FOREACHINSTATICARRAY(bpo.bpo_abpePolygonEdges, CBrushPolygonEdge, itbpe) { // get edge vertices (edge direction is irrelevant here!) const FLOAT3D &vVertex0 = itbpe->bpe_pbedEdge->bed_pbvxVertex0->bvx_vAbsolute; const FLOAT3D &vVertex1 = itbpe->bpe_pbedEdge->bed_pbvxVertex1->bvx_vAbsolute; // pass the edge to the intersector isIntersector.AddEdge( vVertex0(iMajorAxis1), vVertex0(iMajorAxis2), vVertex1(iMajorAxis1), vVertex1(iMajorAxis2)); } // ask the intersector if the point is inside the polygon return isIntersector.IsIntersecting(); } ///////////////////////////////////////////////////////////////////// // CLayerMaker /* * Constructor. */ CLayerMaker::CLayerMaker(void) { } /* Spread the shadow towards pixels outside of polygon. */ void CLayerMaker::SpreadShadowMaskOutwards(void) { // for each mip map of the layer for(INDEX iMipmap=0; iMipmap>iMipmap; PIX pixLayerMinV = lm_pixLayerMinV>>iMipmap; PIX pixLayerSizeU = lm_pixLayerSizeU>>iMipmap; PIX pixLayerSizeV = lm_pixLayerSizeV>>iMipmap; PIX pixSizeU = lm_pixSizeU>>iMipmap; PIX pixSizeV = lm_pixSizeV>>iMipmap; PIX pixSizeULog2 = FastLog2(lm_pixSizeU)-iMipmap; UBYTE *pubLayer = lm_pubLayer+lm_mmtLayer.mmt_aslOffsets[iMipmap]; UBYTE *pubPolygonMask = lm_pubPolygonMask+lm_mmtPolygonMask.mmt_aslOffsets[iMipmap]; SLONG slOffsetLayer = 0; // for each pixel in the layer shadow mask for (PIX pixLayerV=0; pixLayerV=0) \ &&(pixLayerU+(du)=0) \ &&(pixLayerV+(dv)0) && (ctLighted>ctInPolygon/2)) { // make this one lighted pubLayer[slOffsetLayer] = 255; // otherwise } else { // make this one shadowed pubLayer[slOffsetLayer] = 0; } // if the pixel is inside the polygon } else { NOTHING; } slOffsetLayer++; } } } } /* Spread the shadow towards pixels inside of polygon. */ void CLayerMaker::SpreadShadowMaskInwards(void) { // for each mip map of the layer for(INDEX iMipmap=0; iMipmap>iMipmap; PIX pixLayerMinV = lm_pixLayerMinV>>iMipmap; PIX pixLayerSizeU = lm_pixLayerSizeU>>iMipmap; PIX pixLayerSizeV = lm_pixLayerSizeV>>iMipmap; PIX pixSizeU = lm_pixSizeU>>iMipmap; PIX pixSizeV = lm_pixSizeV>>iMipmap; PIX pixSizeULog2 = FastLog2(lm_pixSizeU)-iMipmap; UBYTE *pubLayer = lm_pubLayer+lm_mmtLayer.mmt_aslOffsets[iMipmap]; UBYTE *pubPolygonMask = lm_pubPolygonMask+lm_mmtPolygonMask.mmt_aslOffsets[iMipmap]; SLONG slOffsetLayer = 0; // for each pixel in the layer shadow mask for (PIX pixLayerV=0; pixLayerV=0) \ &&(pixLayerU+(du)=0) \ &&(pixLayerV+(dv)0) { // if some are not lighted if (ctLightedbsm_pubPolygonMask!=NULL) { // convert it from bit-packed into byte-packed mask ConvertBitsToBytes( lm_pbsmShadowMap->bsm_pubPolygonMask, lm_pubPolygonMask, lm_mmtPolygonMask.mmt_slTotalSize); } else { UBYTE *pub = lm_pubPolygonMask; // for each mip-map for (INDEX iMipmap=0; iMipmap>iMipmap; pixV++) { FLOAT3D vPoint = vRow; for (PIX pixU=0; pixU>iMipmap; pixU++) { // if the point is in the polygon if (TestPointInPolygon(*lm_pbpoPolygon, vPoint)) { // set the pixel *pub = 255; // if the point is not in the polygon } else { // clear the pixel *pub = 0; } // go to the next pixel pub++; vPoint+=lm_vStepU*FLOAT(1<bsm_pubPolygonMask = (UBYTE *)AllocMemory((lm_mmtPolygonMask.mmt_slTotalSize+7)/8); ConvertBytesToBits( lm_pubPolygonMask, lm_pbsmShadowMap->bsm_pubPolygonMask, lm_mmtPolygonMask.mmt_slTotalSize); } } // flip shadow mask around V axis (for parallel lights) void CLayerMaker::FlipShadowMask(INDEX iMip) { PIX pixLayerMinU = lm_pixLayerMinU>>iMip; PIX pixLayerMinV = lm_pixLayerMinV>>iMip; PIX pixLayerSizeU = lm_pixLayerSizeU>>iMip; PIX pixLayerSizeV = lm_pixLayerSizeV>>iMip; UBYTE *pubLayer = lm_pubLayer+lm_mmtLayer.mmt_aslOffsets[iMip]; UBYTE *pubRow = pubLayer; // for each row for (PIX pixV=0; pixVbsl_plsLightSource->ls_ulFlags&LSF_CASTSHADOWS) || (lm_pbpoPolygon->bpo_ulFlags&BPOF_DOESNOTRECEIVESHADOW) ) { // do nothing return BSLF_ALLLIGHT; } // remember current layer and its light source lm_plsLight = pbsl->bsl_plsLightSource; lm_pbslLayer = pbsl; lm_vLight = lm_plsLight->ls_penEntity->GetPlacement().pl_PositionVector; // find the influenced rectangle CLightRectangle lr; lm_pbsmShadowMap->FindLightRectangle(*lm_plsLight, lr); ASSERT(lr.lr_pixSizeU == lm_pbslLayer->bsl_pixSizeU); ASSERT(lr.lr_pixSizeV == lm_pbslLayer->bsl_pixSizeV); lm_fpixHotU = lr.lr_fpixHotU; lm_fpixHotV = lr.lr_fpixHotV; lm_fLightPlaneDistance = lr.lr_fLightPlaneDistance; lm_pixLayerMinU = lr.lr_pixMinU; lm_pixLayerMinV = lr.lr_pixMinV; lm_pixLayerSizeU= lr.lr_pixSizeU; lm_pixLayerSizeV= lr.lr_pixSizeV; // find mip-mapping information for the rectangle MakeMipmapTable(lm_pixLayerSizeU, lm_pixLayerSizeV, lm_mmtLayer); ASSERT(lr.lr_pixSizeU == lm_pbslLayer->bsl_pixSizeU); ASSERT(lr.lr_pixSizeV == lm_pbslLayer->bsl_pixSizeV); ASSERT(lr.lr_pixSizeU <= lm_pixSizeU); ASSERT(lr.lr_pixSizeV <= lm_pixSizeV); // if there is no influence, do nothing if ((lr.lr_pixSizeU==0) || (lr.lr_pixSizeV==0)) return BSLF_ALLDARK; lm_pbslLayer->bsl_pixMinU = lr.lr_pixMinU; lm_pbslLayer->bsl_pixMinV = lr.lr_pixMinV; lm_pbslLayer->bsl_pixSizeU = lr.lr_pixSizeU; lm_pbslLayer->bsl_pixSizeV = lr.lr_pixSizeV; lm_pbslLayer->bsl_slSizeInPixels = lm_mmtLayer.mmt_slTotalSize; // allocate shadow mask for the light (+8 is safety wall for fast conversions) lm_pubLayer = (UBYTE *)AllocMemory(lm_mmtLayer.mmt_slTotalSize+8); const FLOAT fEpsilon = (1<bpo_ulFlags & BPOF_ACCURATESHADOWS) { // make each mip-map of mask for itself for(INDEX iMip=0; iMipbpo_ulFlags&BPOF_DARKCORNERS)) { SpreadShadowMaskOutwards(); } else { SpreadShadowMaskInwards(); SpreadShadowMaskOutwards(); } // convert the shadow mask from byte-packed into bit-packed mask ConvertBytesToBits(lm_pubLayer, lm_pubLayer, lm_mmtLayer.mmt_slTotalSize); ShrinkMemory((void **)&lm_pubLayer, (lm_mmtLayer.mmt_slTotalSize+7)/8); pbsl->bsl_pubLayer = lm_pubLayer; // update statistics _ctShadowLayers++; _ctShadowClusters+=lm_mmtLayer.mmt_slTotalSize; return ulLighted; } ULONG CLayerMaker::MakeOneShadowMaskMip(INDEX iMip) { ULONG ulLighted=BSLF_ALLLIGHT|BSLF_ALLDARK; PIX pixLayerMinU = lm_pixLayerMinU>>iMip; PIX pixLayerMinV = lm_pixLayerMinV>>iMip; PIX pixLayerSizeU = lm_pixLayerSizeU>>iMip; PIX pixLayerSizeV = lm_pixLayerSizeV>>iMip; INDEX iMipLevel = lm_iMipLevel+iMip; FLOAT3D vO = lm_vO+(lm_vStepU+lm_vStepV)*(FLOAT(1<ls_ulFlags&LSF_DIRECTIONAL) { // prepare parallel projection as if viewing from polygon and the shadow map is screen CParallelProjection3D prProjection; prProjection.ScreenBBoxL() = FLOATaabbox2D( FLOAT2D(pixLayerMinU, pixLayerMinV), FLOAT2D(pixLayerMinU+pixLayerSizeU, pixLayerMinV+pixLayerSizeV) ); prProjection.AspectRatioL() = 1.0f; prProjection.NearClipDistanceL() = 0.00f; prProjection.pr_vZoomFactors(1) = prProjection.pr_vZoomFactors(2) = 1024.0f/(1<ls_penEntity->GetPlacement().pl_OrientationAngle, vDirection); // if polygon is turned away from the light if ((vDirection%(const FLOAT3D &)lm_pbpoPolygon->bpo_pbplPlane->bpl_plAbsolute)>-0.001) { // layer is all dark return BSLF_ALLDARK; } vDirection = vDirection*lm_mToInverseMapping; prProjection.pr_vStepFactors(1) = -vDirection(1)/vDirection(3); prProjection.pr_vStepFactors(2) = -vDirection(2)/vDirection(3); prProjection.pr_vStepFactors(1)*=prProjection.pr_vZoomFactors(1); prProjection.pr_vStepFactors(2)*=prProjection.pr_vZoomFactors(2); CPlacement3D plCenter; plCenter.pl_OrientationAngle = lm_aInverseMappingOrientation; plCenter.pl_PositionVector = vO +vStepU*(FLOAT(pixLayerSizeU)/2-0.5f) // !!!! +vStepV*(FLOAT(pixLayerSizeV)/2);//+5.0f); prProjection.ViewerPlacementL() = plCenter; // render the view to the shadow layer (but ignore the target polygon) CAnyProjection3D apr; apr = prProjection; ULONG ulFlagsBefore = lm_pbpoPolygon->bpo_ulFlags; lm_pbpoPolygon->bpo_ulFlags |= BPOF_INVISIBLE; ulLighted&=RenderShadows(*lm_pwoWorld, *(CEntity*)NULL, apr, lm_pbpoPolygon->bpo_boxBoundingBox, pubLayer, pixLayerSizeU, pixLayerSizeV, lm_plsLight->ls_ubPolygonalMask); lm_pbpoPolygon->bpo_ulFlags = ulFlagsBefore; // flip the shadow mask around v axis (left-right) FlipShadowMask(iMip); // if the light is point } else { // prepare perspective projection as if viewing from light and the shadow map is screen CPerspectiveProjection3D prProjection; if( !(lm_pbpoPolygon->bpo_ulFlags&BPOF_DARKCORNERS)) { prProjection.ScreenBBoxL() = FLOATaabbox2D( FLOAT2D(pixLayerMinU-fpixHotU+1, pixLayerMinV-fpixHotV+1), FLOAT2D(pixLayerMinU+pixLayerSizeU-fpixHotU+1, pixLayerMinV+pixLayerSizeV-fpixHotV+1) ); } else { prProjection.ScreenBBoxL() = FLOATaabbox2D( FLOAT2D(pixLayerMinU-fpixHotU, pixLayerMinV-fpixHotV), FLOAT2D(pixLayerMinU+pixLayerSizeU-fpixHotU, pixLayerMinV+pixLayerSizeV-fpixHotV) ); } prProjection.AspectRatioL() = 1.0f; prProjection.NearClipDistanceL() = lm_plsLight->ls_fNearClipDistance; prProjection.FarClipDistanceL() = lm_fLightPlaneDistance-lm_plsLight->ls_fFarClipDistance; //-fEpsilon/2; !!!! use minimal epsilon for polygon prProjection.ppr_fMetersPerPixel = (1<bpo_ulFlags; lm_pbpoPolygon->bpo_ulFlags |= BPOF_INVISIBLE; // if light is not illumination light if (lm_plsLight->ls_ubPolygonalMask==0) { // just render starting at the light entity position ulLighted&=RenderShadows(*lm_pwoWorld, *lm_plsLight->ls_penEntity, apr, FLOATaabbox3D(), pubLayer, pixLayerSizeU, pixLayerSizeV, lm_plsLight->ls_ubPolygonalMask); // if light is illumination light } else { // add entire box around target polygon and light position to rendering FLOATaabbox3D box = lm_pbpoPolygon->bpo_boxBoundingBox; box|=lm_plsLight->ls_penEntity->GetPlacement().pl_PositionVector; ulLighted&=RenderShadows(*lm_pwoWorld, *(CEntity*)NULL, apr, box, pubLayer, pixLayerSizeU, pixLayerSizeV, lm_plsLight->ls_ubPolygonalMask); } lm_pbpoPolygon->bpo_ulFlags = ulFlagsBefore; } return ulLighted; } /* * Create a shadow map for a given polygon. */ BOOL CLayerMaker::CreateLayers(CBrushPolygon &bpo, CWorld &woWorld, BOOL bDoDirectionalLights) { // __pfWorldEditingProfile.IncrementAveragingCounter(); // __pfWorldEditingProfile.StartTimer(CWorldEditingProfile::PTI_MAKESHADOWS); BOOL bInitialized = FALSE; BOOL bCalculatedSome = FALSE; BOOL bSomeAreUncalculated = FALSE; // remember the world lm_pwoWorld = &woWorld; lm_pbsmShadowMap = &bpo.bpo_smShadowMap; lm_pbpoPolygon = &bpo; // for each layer that should be calculated, but isn't FORDELETELIST(CBrushShadowLayer, bsl_lnInShadowMap, lm_pbsmShadowMap->bsm_lhLayers, itbsl) { // if already calculated, or dynamic if (itbsl->bsl_ulFlags&BSLF_CALCULATED || itbsl->bsl_plsLightSource->ls_ulFlags&LSF_DYNAMIC) { // skip it continue; } // if we are not doing directional lights and it is directional if (!bDoDirectionalLights && (itbsl->bsl_plsLightSource->ls_ulFlags&LSF_DIRECTIONAL)) { // skip it bSomeAreUncalculated = TRUE; continue; } // if not yet initialized if( !bInitialized) { // remember general data CalculateData(); // make bit-packed mask of where the polygon is in the shadow map MakePolygonMask(); bInitialized = TRUE; } CBrushShadowLayer &bsl = *itbsl; // mark the layer is calculated bsl.bsl_ulFlags |= BSLF_CALCULATED; // make shadow mask for the light ULONG ulLighted=MakeShadowMask(itbsl); ASSERT((ulLighted==0) || (ulLighted==BSLF_ALLLIGHT) || (ulLighted==BSLF_ALLDARK)); bsl.bsl_ulFlags &= ~(BSLF_ALLLIGHT|BSLF_ALLDARK); bsl.bsl_ulFlags |= ulLighted; // if the layer is not needed if( ulLighted&(BSLF_ALLLIGHT|BSLF_ALLDARK)) { // free it if( bsl.bsl_pubLayer!=NULL) FreeMemory( bsl.bsl_pubLayer); bsl.bsl_pubLayer = NULL; } bCalculatedSome = TRUE; } // if was intialized if( bInitialized) { // free bit-packed polygon mask FreeMemory( lm_pubPolygonMask); } // if some new layers have been calculated if( bCalculatedSome) { // invalidate mixed layers bpo.bpo_smShadowMap.Invalidate(); } return bSomeAreUncalculated; } /* * Create shadow map for the polygon. */ void CBrushPolygon::MakeShadowMap(CWorld *pwoWorld, BOOL bDoDirectionalLights) { _pfWorldEditingProfile.StartTimer(CWorldEditingProfile::PTI_MAKESHADOWMAP); // create new shadow map CLayerMaker lmMaker; BOOL bSomeAreUncalculated = lmMaker.CreateLayers(*this, *pwoWorld, bDoDirectionalLights); // unqueue the shadow map if (!bSomeAreUncalculated && bpo_smShadowMap.bsm_lnInUncalculatedShadowMaps.IsLinked()) { bpo_smShadowMap.bsm_lnInUncalculatedShadowMaps.Remove(); } _pfWorldEditingProfile.StopTimer(CWorldEditingProfile::PTI_MAKESHADOWMAP); }