/* 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. */ extern INDEX mdl_iShadowQuality; // model shadow precision // 0 = no shadows // 1 = one simple shadow // 2 = one complex shadow // 3 = all shadows /* * Compare two models for sorting. */ static inline int CompareDelayedModels( const CDelayedModel &dm0, const CDelayedModel &dm1) { BOOL bHasAlpha0 = dm0.dm_ulFlags&DMF_HASALPHA; BOOL bHasAlpha1 = dm1.dm_ulFlags&DMF_HASALPHA; if (! bHasAlpha0 && bHasAlpha1) return -1; else if ( bHasAlpha0 && !bHasAlpha1) return +1; if (dm0.dm_fDistancedm1.dm_fDistance) return +1; else return 0; } static int qsort_CompareDelayedModels( const void *ppdm0, const void *ppdm1) { CDelayedModel &dm0 = **(CDelayedModel **)ppdm0; CDelayedModel &dm1 = **(CDelayedModel **)ppdm1; return +CompareDelayedModels(dm0, dm1); } static inline FLOAT IntensityAtDistance( FLOAT fFallOff, FLOAT fHotSpot, FLOAT fDistance) { // intensity is zero if further than fall-off range if( fDistance>fFallOff) return 0.0f; // intensity is maximum if closer than hot-spot range if( fDistancesi_pbpoPolygon==NULL*/) { // no shadow _pfRenderProfile.StopTimer(CRenderProfile::PTI_FINDLIGHTS); return FALSE; } // if there is valid shading info else { // if model is above terrain if(en.en_psiShadingInfo->si_ptrTerrain!=NULL) { CTerrain *ptrTerrain = en.en_psiShadingInfo->si_ptrTerrain; // if full bright rendering if (_wrpWorldRenderPrefs.wrp_shtShadows==CWorldRenderPrefs::SHT_NONE) { // no model shading colLight = C_BLACK; colAmbient = C_GRAY; vTotalLightDirection = FLOAT3D(1.0f, -1.0f, 1.0f); _pfRenderProfile.StopTimer(CRenderProfile::PTI_FINDLIGHTS); return FALSE; } // create floor plane for shadow ASSERT(en.en_psiShadingInfo->si_lnInPolygon.IsLinked()); COLOR colShade = ptrTerrain->GetShadeColor(en.en_psiShadingInfo); colLight = MulColors(colShade,0xD8D8D8FF); colAmbient = MulColors(colShade,0xB2B2B2FF); vTotalLightDirection = FLOAT3D(1.0f, -1.0f, 1.0f); fTotalShadowIntensity = 0.5f; plFloorPlane = ptrTerrain->GetPlaneFromPoint(en.en_psiShadingInfo->si_vNearPoint); // else if model is above polygon } else if(en.en_psiShadingInfo->si_pbpoPolygon!=NULL) { // if full bright rendering if (_wrpWorldRenderPrefs.wrp_shtShadows==CWorldRenderPrefs::SHT_NONE) { // no model shading colLight = C_BLACK; colAmbient = C_GRAY; vTotalLightDirection = FLOAT3D(1.0f, -1.0f, 1.0f); _pfRenderProfile.StopTimer(CRenderProfile::PTI_FINDLIGHTS); return FALSE; } // create floor plane for shadow ASSERT(en.en_psiShadingInfo->si_lnInPolygon.IsLinked()); plFloorPlane = en.en_psiShadingInfo->si_pbpoPolygon->bpo_pbplPlane->bpl_plAbsolute; // if full bright polygon if( en.en_psiShadingInfo->si_pbpoPolygon->bpo_ulFlags&BPOF_FULLBRIGHT) { // take light from polygon shadow COLOR col = en.en_psiShadingInfo->si_pbpoPolygon->bpo_colShadow; colLight = LerpColor( C_BLACK, col, 0.25f); colAmbient = LerpColor( C_BLACK, col, 0.33f); fTotalShadowIntensity = NormByteToFloat((en.en_psiShadingInfo->si_pbpoPolygon->bpo_colShadow&CT_AMASK)>>CT_ASHIFT); vTotalLightDirection = FLOAT3D(1.0f, -1.0f, 1.0f); _pfRenderProfile.StopTimer(CRenderProfile::PTI_FINDLIGHTS); return TRUE; } // get the shadow map of the underlying polygon CBrushShadowMap &bsm = en.en_psiShadingInfo->si_pbpoPolygon->bpo_smShadowMap; // get ambient light UBYTE ubAR, ubAG, ubAB; ColorToRGB( bsm.GetBrushPolygon()->bpo_pbscSector->bsc_colAmbient, ubAR, ubAG, ubAB); SLONG slSAR=ubAR, slSAG=ubAG, slSAB=ubAB; fTotalShadowIntensity = 0.0f; // for each shadow layer {FOREACHINLIST(CBrushShadowLayer, bsl_lnInShadowMap, bsm.bsm_lhLayers, itbsl) { // get the light source CLightSource *plsLight = itbsl->bsl_plsLightSource; // remember the light parameters UBYTE ubR, ubG, ubB; UBYTE ubDAR, ubDAG, ubDAB; plsLight->GetLightColorAndAmbient( ubR, ubG, ubB, ubDAR, ubDAG, ubDAB); // add directional ambient if needed if( en.en_psiShadingInfo->si_pbpoPolygon->bpo_ulFlags&BPOF_HASDIRECTIONALAMBIENT) { slSAR += ubDAR; slSAG += ubDAG; slSAB += ubDAB; } // get the layer intensity at the point FLOAT fShadowFactor; if (en.en_ulFlags&ENF_CLUSTERSHADOWS) { fShadowFactor = 1.0f; } else { fShadowFactor = itbsl->GetLightStrength( en.en_psiShadingInfo->si_pixShadowU, en.en_psiShadingInfo->si_pixShadowV, en.en_psiShadingInfo->si_fUDRatio, en.en_psiShadingInfo->si_fLRRatio); } // skip this light if no intensity if( fShadowFactor<0.01f) continue; const CPlacement3D &plLight = plsLight->ls_penEntity->GetPlacement(); const FLOAT3D &vLight = plLight.pl_PositionVector; // get its parameters at the model position FLOAT3D vDirection; FLOAT fDistance; FLOAT fFallOffFactor; if (plsLight->ls_ulFlags&LSF_DIRECTIONAL) { fFallOffFactor = 1.0f; AnglesToDirectionVector(plLight.pl_OrientationAngle, vDirection); plModel.pl_PositionVector-vLight; if (!(en.en_psiShadingInfo->si_pbpoPolygon->bpo_ulFlags&BPOF_HASDIRECTIONALLIGHT)) { ubR = ubG = ubB = 0; } fDistance = 1.0f; } else { vDirection = plModel.pl_PositionVector-vLight; fDistance = vDirection.Length(); if (fDistance>plsLight->ls_rFallOff) { continue; } else if (fDistancels_rHotSpot) { fFallOffFactor = 1.0f; } else { fFallOffFactor = (plsLight->ls_rFallOff-fDistance)/ (plsLight->ls_rFallOff-plsLight->ls_rHotSpot); } } // add the light to active lights struct ModelLight &ml = _amlLights.Push(); ml.ml_plsLight = plsLight; // normalize direction vector if (fDistance>0.001f) { ml.ml_vDirection = vDirection/fDistance; } else { ml.ml_vDirection = FLOAT3D(0.0f,0.0f,0.0f); } // special case for substract sector ambient light if (plsLight->ls_ulFlags&LSF_SUBSTRACTSECTORAMBIENT) { ubR = (UBYTE)Clamp( (SLONG)ubR-slSAR, 0L, 255L); ubG = (UBYTE)Clamp( (SLONG)ubG-slSAG, 0L, 255L); ubB = (UBYTE)Clamp( (SLONG)ubB-slSAB, 0L, 255L); } // calculate light intensity FLOAT fShade = (ubR+ubG+ubB)*(2.0f/(3.0f*255.0f)); ml.ml_fShadowIntensity = fShade*fShadowFactor; fTotalShadowIntensity += ml.ml_fShadowIntensity; // special case for dark light if (plsLight->ls_ulFlags&LSF_DARKLIGHT) { ml.ml_fR = -ubR*fFallOffFactor; ml.ml_fG = -ubG*fFallOffFactor; ml.ml_fB = -ubB*fFallOffFactor; } else { ml.ml_fR = +ubR*fFallOffFactor; ml.ml_fG = +ubG*fFallOffFactor; ml.ml_fB = +ubB*fFallOffFactor; } }} FLOAT fTR=0.0f; FLOAT fTG=0.0f; FLOAT fTB=0.0f; FLOAT3D vDirection(0.0f,0.0f,0.0f); // for each active light {for(INDEX iLight=0; iLight<_amlLights.Count(); iLight++) { struct ModelLight &ml = _amlLights[iLight]; // add it to total intensity fTR += ml.ml_fR; fTG += ml.ml_fG; fTB += ml.ml_fB; // add it to direction vector FLOAT fWeight = Abs(ml.ml_fR+ml.ml_fG+ml.ml_fB) * (1.0f/(3.0f*255.0f)); vDirection+=ml.ml_vDirection*fWeight; }} // normalize average direction vector FLOAT fDirection = vDirection.Length(); if (fDirection>0.001f) { vDirection /= fDirection; } else { vDirection = FLOAT3D(0.0f,0.0f,0.0f); } // for each active light FLOAT fDR=0.0f; FLOAT fDG=0.0f; FLOAT fDB=0.0f; {for(INDEX iLight=0; iLight<_amlLights.Count(); iLight++) { struct ModelLight &ml = _amlLights[iLight]; // find its contribution to direction vector const FLOAT fFactor = ClampDn( vDirection%ml.ml_vDirection, 0.0f); // add it to directional intensity fDR += ml.ml_fR*fFactor; fDG += ml.ml_fG*fFactor; fDB += ml.ml_fB*fFactor; }} // adjust ambient light with gradient if needed ULONG ulGradientType = en.en_psiShadingInfo->si_pbpoPolygon->bpo_bppProperties.bpp_ubGradientType; if( ulGradientType>0) { CGradientParameters gp; COLOR colGradientPoint; CEntity *pen = en.en_psiShadingInfo->si_pbpoPolygon->bpo_pbscSector->bsc_pbmBrushMip->bm_pbrBrush->br_penEntity; if( pen!=NULL && pen->GetGradient( ulGradientType, gp)) { FLOAT fGrPt = (en.en_psiShadingInfo->si_vNearPoint % gp.gp_vGradientDir - gp.gp_fH0) / (gp.gp_fH1-gp.gp_fH0); fGrPt = Clamp( fGrPt, 0.0f, 1.0f); colGradientPoint = LerpColor( gp.gp_col0, gp.gp_col1, fGrPt); UBYTE ubGR,ubGG,ubGB; ColorToRGB( colGradientPoint, ubGR,ubGG,ubGB); // add or substract gradient component to total ambient if( gp.gp_bDark) { slSAR-=ubGR; slSAG-=ubGG; slSAB-=ubGB; } else { slSAR+=ubGR; slSAG+=ubGG; slSAB+=ubGB; } } } // clamp ambient component slSAR = Clamp( slSAR, 0L, 255L); slSAG = Clamp( slSAG, 0L, 255L); slSAB = Clamp( slSAB, 0L, 255L); // calculate average light properties SLONG slAR = Clamp( (SLONG)FloatToInt(fTR-fDR) +slSAR, 0L, 255L); SLONG slAG = Clamp( (SLONG)FloatToInt(fTG-fDG) +slSAG, 0L, 255L); SLONG slAB = Clamp( (SLONG)FloatToInt(fTB-fDB) +slSAB, 0L, 255L); SLONG slLR = Clamp( (SLONG)FloatToInt(fDR), 0L, 255L); SLONG slLG = Clamp( (SLONG)FloatToInt(fDG), 0L, 255L); SLONG slLB = Clamp( (SLONG)FloatToInt(fDB), 0L, 255L); colLight = RGBToColor( slLR,slLG,slLB); colAmbient = RGBToColor( slAR,slAG,slAB); // adjust for changed polygon shadow color COLOR colShadowMap = en.en_psiShadingInfo->si_pbpoPolygon->bpo_colShadow; CTextureBlending &tbShadow = re_pwoWorld->wo_atbTextureBlendings[ en.en_psiShadingInfo->si_pbpoPolygon->bpo_bppProperties.bpp_ubShadowBlend]; COLOR colShadowMapAdjusted = MulColors(colShadowMap, tbShadow.tb_colMultiply); colLight = MulColors( colLight, colShadowMapAdjusted); colAmbient = MulColors( colAmbient, colShadowMapAdjusted); vTotalLightDirection = vDirection; // else no valid shading info } else { // no shadow _pfRenderProfile.StopTimer(CRenderProfile::PTI_FINDLIGHTS); return FALSE; } } _pfRenderProfile.StopTimer(CRenderProfile::PTI_FINDLIGHTS); return TRUE; } /* * Render one model with shadow (eventually) */ void CRenderer::RenderOneModel( CEntity &en, CModelObject &moModel, const CPlacement3D &plModel, const FLOAT fDistanceFactor, BOOL bRenderShadow, ULONG ulDMFlags) { // skip invisible models if( moModel.mo_Stretch == FLOAT3D(0,0,0)) return; // do nothing, if rendering shadows and this model doesn't cast cluster shadows if( re_bRenderingShadows && !(en.en_ulFlags&ENF_CLUSTERSHADOWS)) return; _pfRenderProfile.StartTimer(CRenderProfile::PTI_RENDERONEMODEL); CPlacement3D plLight; plLight.pl_PositionVector = plModel.pl_PositionVector + FLOAT3D(3.0f,3.0f,3.0f); plLight.pl_OrientationAngle = ANGLE3D(AngleDeg(30.0f), AngleDeg(-45.0f),0); // create a default light COLOR colLight = C_GRAY; COLOR colAmbient = C_dGRAY; FLOAT3D vTotalLightDirection( 1.0f, -1.0f, 1.0f); FLOATplane3D plFloorPlane(FLOAT3D( 0.0f, 1.0f, 0.0f), 0.0f); BOOL bRenderModelShadow = FALSE; FLOAT fTotalShadowIntensity = 0.0f; // if not rendering cluster shadows if( !re_bRenderingShadows) { // find model lights bRenderModelShadow = FindModelLights( en, plModel, colLight, colAmbient, fTotalShadowIntensity, vTotalLightDirection, plFloorPlane); } // let the entity adjust shading parameters if it wants to mdl_iShadowQuality = Clamp( mdl_iShadowQuality, 0L, 3L); const BOOL bAllowShadows = en.AdjustShadingParameters( vTotalLightDirection, colLight, colAmbient); bRenderModelShadow = (bRenderModelShadow && bAllowShadows && bRenderShadow && mdl_iShadowQuality>0); // prepare render model structure CRenderModel rm; rm.rm_vLightDirection = vTotalLightDirection; rm.rm_fDistanceFactor = fDistanceFactor; rm.rm_colLight = colLight; rm.rm_colAmbient = colAmbient; rm.SetObjectPlacement(plModel); if( ulDMFlags & DMF_FOG) rm.rm_ulFlags |= RMF_FOG; if( ulDMFlags & DMF_HAZE) rm.rm_ulFlags |= RMF_HAZE; if( ulDMFlags & DMF_INSIDE) rm.rm_ulFlags |= RMF_INSIDE; if( ulDMFlags & DMF_INMIRROR) rm.rm_ulFlags |= RMF_INMIRROR; // mark that we don't actualy need entire model if( re_penViewer==&en) { rm.rm_ulFlags |= RMF_SPECTATOR; bRenderModelShadow = FALSE; } // TEMP: disable Truform usage on weapon models if( IsOfClass( &en, "Player Weapons")) rm.rm_ulFlags |= RMF_WEAPON; // set tesselation level of models rm.rm_iTesselationLevel = en.GetMaxTessellationLevel(); // prepare CRenderModel structure for rendering of one model moModel.SetupModelRendering(rm); // determine shadow intensity fTotalShadowIntensity *= NormByteToFloat( (moModel.mo_colBlendColor&CT_AMASK)>>CT_ASHIFT); fTotalShadowIntensity = Clamp( fTotalShadowIntensity, 0.0f, 1.0f); // if should render shadow for this model if( bRenderModelShadow && !(en.en_ulFlags&ENF_CLUSTERSHADOWS) && moModel.HasShadow(rm.rm_iMipLevel)) { // if only simple shadow if( mdl_iShadowQuality==1) { // render simple shadow fTotalShadowIntensity = 0.1f + fTotalShadowIntensity*0.9f; moModel.AddSimpleShadow( rm, fTotalShadowIntensity, plFloorPlane); } // if only one shadow else if( mdl_iShadowQuality==2) { // render one shadow of model from shading light direction const FLOAT fHotSpot = 1E10f; const FLOAT fFallOff = 1E11f; CPlacement3D plLight; plLight.pl_PositionVector = plModel.pl_PositionVector - rm.rm_vLightDirection*1000.0f; moModel.RenderShadow( rm, plLight, fFallOff, fHotSpot, fTotalShadowIntensity, plFloorPlane); } // if full shadows else if( mdl_iShadowQuality==3) { // for each active light for( INDEX iLight=0; iLight<_amlLights.Count(); iLight++) { struct ModelLight &ml = _amlLights[iLight]; // skip light if doesn't cast shadows if( !(ml.ml_plsLight->ls_ulFlags&LSF_CASTSHADOWS)) continue; // get light parameters CPlacement3D plLight = ml.ml_plsLight->ls_penEntity->en_plPlacement; FLOAT fHotSpot = ml.ml_plsLight->ls_rHotSpot; FLOAT fFallOff = ml.ml_plsLight->ls_rFallOff; if (ml.ml_plsLight->ls_ulFlags & LSF_DIRECTIONAL) { fHotSpot = 1E10f; fFallOff = 1E11f; FLOAT3D vDirection; AnglesToDirectionVector( plLight.pl_OrientationAngle, vDirection); plLight.pl_PositionVector = plModel.pl_PositionVector-(vDirection*1000.0f); } // render one shadow of model const FLOAT fShadowIntensity = Clamp( ml.ml_fShadowIntensity, 0.0f, 1.0f); moModel.RenderShadow( rm, plLight, fFallOff, fHotSpot, fShadowIntensity, plFloorPlane); } } } // if the entity is not the viewer, or this is not primary renderer if( re_penViewer!=&en) { // render model moModel.RenderModel(rm); // if the entity is viewer } else { // just remember the shading info (needed for first-person-weapon rendering) _vViewerLightDirection = rm.rm_vLightDirection; _colViewerLight = rm.rm_colLight; _colViewerAmbient = rm.rm_colAmbient; } // all done _pfRenderProfile.StopTimer(CRenderProfile::PTI_RENDERONEMODEL); } /* * Render one ska model with shadow (eventually) */ void CRenderer::RenderOneSkaModel( CEntity &en, const CPlacement3D &plModel, const FLOAT fDistanceFactor, BOOL bRenderShadow, ULONG ulDMFlags) { // skip invisible models if( en.GetModelInstance()->mi_vStretch == FLOAT3D(0,0,0)) return; // do nothing, if rendering shadows and this model doesn't cast cluster shadows if( re_bRenderingShadows && !(en.en_ulFlags&ENF_CLUSTERSHADOWS)) return; _pfRenderProfile.StartTimer(CRenderProfile::PTI_RENDERONEMODEL); CPlacement3D plLight; plLight.pl_PositionVector = plModel.pl_PositionVector + FLOAT3D(3.0f,3.0f,3.0f); plLight.pl_OrientationAngle = ANGLE3D(AngleDeg(30.0f), AngleDeg(-45.0f),0); // create a default light COLOR colLight = C_GRAY; COLOR colAmbient = C_dGRAY; FLOAT3D vTotalLightDirection( 1.0f, -1.0f, 1.0f); FLOATplane3D plFloorPlane(FLOAT3D( 0.0f, 1.0f, 0.0f), 0.0f); BOOL bRenderModelShadow = FALSE; FLOAT fTotalShadowIntensity = 0.0f; // if not rendering cluster shadows if( !re_bRenderingShadows) { // find model lights bRenderModelShadow = FindModelLights( en, plModel, colLight, colAmbient, fTotalShadowIntensity, vTotalLightDirection, plFloorPlane); } // let the entity adjust shading parameters if it wants to mdl_iShadowQuality = Clamp( mdl_iShadowQuality, 0L, 3L); const BOOL bAllowShadows = en.AdjustShadingParameters( vTotalLightDirection, colLight, colAmbient); bRenderModelShadow = (bRenderModelShadow && bAllowShadows && bRenderShadow && mdl_iShadowQuality>0); ULONG &ulRenFlags = RM_GetRenderFlags(); ulRenFlags = 0; if( ulDMFlags & DMF_FOG) ulRenFlags |= RMF_FOG; if( ulDMFlags & DMF_HAZE) ulRenFlags |= RMF_HAZE; if( ulDMFlags & DMF_INSIDE) ulRenFlags |= RMF_INSIDE; if( ulDMFlags & DMF_INMIRROR) ulRenFlags |= RMF_INMIRROR; // mark that we don't actualy need entire model if( re_penViewer==&en) { ulRenFlags |= RMF_SPECTATOR; bRenderModelShadow = FALSE; } RM_SetObjectPlacement(en.GetLerpedPlacement()); RM_SetLightColor(colAmbient,colLight); RM_SetLightDirection(vTotalLightDirection); // determine shadow intensity fTotalShadowIntensity *= NormByteToFloat( (en.GetModelInstance()->GetModelColor()&CT_AMASK)>>CT_ASHIFT); fTotalShadowIntensity = Clamp( fTotalShadowIntensity, 0.0f, 1.0f); // if should render shadow for this model if( bRenderModelShadow && !(en.en_ulFlags&ENF_CLUSTERSHADOWS) && en.GetModelInstance()->HasShadow(1/*rm.rm_iMipLevel*/)) { // if only simple shadow if( mdl_iShadowQuality==1) { // render simple shadow fTotalShadowIntensity = 0.1f + fTotalShadowIntensity*0.9f; en.GetModelInstance()->AddSimpleShadow(fTotalShadowIntensity, plFloorPlane); } // if only one shadow else if( mdl_iShadowQuality==2) { /* // render one shadow of model from shading light direction const FLOAT fHotSpot = 1E10f; const FLOAT fFallOff = 1E11f; CPlacement3D plLight; plLight.pl_PositionVector = plModel.pl_PositionVector - vTotalLightDirection*1000.0f; // moModel.RenderShadow( rm, plLight, fFallOff, fHotSpot, fTotalShadowIntensity, plFloorPlane); */ fTotalShadowIntensity = 0.1f + fTotalShadowIntensity*0.9f; en.GetModelInstance()->AddSimpleShadow(fTotalShadowIntensity, plFloorPlane); } // if full shadows else if( mdl_iShadowQuality==3) { /* // for each active light for( INDEX iLight=0; iLight<_amlLights.Count(); iLight++) { struct ModelLight &ml = _amlLights[iLight]; // skip light if doesn't cast shadows if( !(ml.ml_plsLight->ls_ulFlags&LSF_CASTSHADOWS)) continue; // get light parameters CPlacement3D plLight = ml.ml_plsLight->ls_penEntity->en_plPlacement; FLOAT fHotSpot = ml.ml_plsLight->ls_rHotSpot; FLOAT fFallOff = ml.ml_plsLight->ls_rFallOff; if (ml.ml_plsLight->ls_ulFlags & LSF_DIRECTIONAL) { fHotSpot = 1E10f; fFallOff = 1E11f; FLOAT3D vDirection; AnglesToDirectionVector( plLight.pl_OrientationAngle, vDirection); plLight.pl_PositionVector = plModel.pl_PositionVector-(vDirection*1000.0f); } // render one shadow of model const FLOAT fShadowIntensity = Clamp( ml.ml_fShadowIntensity, 0.0f, 1.0f); // moModel.RenderShadow( rm, plLight, fFallOff, fHotSpot, fShadowIntensity, plFloorPlane); } */ fTotalShadowIntensity = 0.1f + fTotalShadowIntensity*0.9f; en.GetModelInstance()->AddSimpleShadow(fTotalShadowIntensity, plFloorPlane); } } // if the entity is not the viewer, or this is not primary renderer if( re_penViewer!=&en) { // render model RM_SetBoneAdjustCallback(&EntityAdjustBonesCallback,&en); RM_SetShaderParamsAdjustCallback(&EntityAdjustShaderParamsCallback,&en); RM_RenderSKA(*en.GetModelInstanceForRendering()); // if the entity is viewer } else { // just remember the shading info (needed for first-person-weapon rendering) _vViewerLightDirection = vTotalLightDirection; _colViewerLight = colLight; _colViewerAmbient = colAmbient; } // all done // _pfRenderProfile.StopTimer(CRenderProfile::PTI_RENDERONEMODEL); } /* * Render models that were kept for delayed rendering. */ void CRenderer::RenderModels( BOOL bBackground) { if( _bMultiPlayer) gfx_bRenderModels = 1; // must render in multiplayer mode! if( !gfx_bRenderModels && !re_bRenderingShadows) return; _pfRenderProfile.StartTimer(CRenderProfile::PTI_RENDERMODELS); // sort all the delayed models by distance qsort(re_admDelayedModels.GetArrayOfPointers(), re_admDelayedModels.Count(), sizeof(CDelayedModel *), qsort_CompareDelayedModels); CAnyProjection3D *papr; if( bBackground) { papr = &re_prBackgroundProjection; } else { papr = &re_prProjection; } // begin model rendering if( !re_bRenderingShadows) { BeginModelRenderingView( *papr, re_pdpDrawPort); RM_BeginRenderingView( *papr, re_pdpDrawPort); } else { BeginModelRenderingMask( *papr, re_pubShadow, re_slShadowWidth, re_slShadowHeight); RM_BeginModelRenderingMask( *papr, re_pubShadow, re_slShadowWidth, re_slShadowHeight); } // for each of models that were kept for delayed rendering for( INDEX iModel=0; iModelGetCurrentColisionBox(boxModel); // if model has collision if( en.en_pciCollisionInfo!=NULL ) { // get its collision box INDEX iCollision = en.GetCollisionBoxIndex(); FLOAT3D vMin = en.GetModelInstance()->GetCollisionBoxMin(iCollision); FLOAT3D vMax = en.GetModelInstance()->GetCollisionBoxMax(iCollision); // extend the box by the collision box boxModel|=FLOATaabbox3D(vMin, vMax); } // set position of marker at top of the model and it size to be proportional to the model boxModel.StretchByVector(en.GetModelInstance()->mi_vStretch); FLOAT fSize = boxModel.Size().Length()*0.3f; _wrpWorldRenderPrefs.wrp_pmoSelectedEntity->mo_Stretch = FLOAT3D( fSize, fSize, fSize); CPlacement3D plSelection = en.GetLerpedPlacement(); plSelection.Translate_OwnSystem( FLOAT3D(0.0f, boxModel.Max()(2), 0.0f)); // render the selection model without shadow RenderOneModel( en, *_wrpWorldRenderPrefs.wrp_pmoSelectedEntity, plSelection, dm.dm_fMipFactor, FALSE, 0); } } else { // render the model with its shadow CModelObject &moModelObject = *dm.dm_pmoModel; RenderOneModel( en, moModelObject, en.GetLerpedPlacement(), dm.dm_fMipFactor, TRUE, dm.dm_ulFlags); // if selected entities should be drawn and this one is selected if( !re_bRenderingShadows && _wrpWorldRenderPrefs.wrp_stSelection==CWorldRenderPrefs::ST_ENTITIES && _wrpWorldRenderPrefs.wrp_pmoSelectedEntity!=NULL && en.IsSelected(ENF_SELECTED)) { // get bounding box of current frame FLOATaabbox3D boxModel; moModelObject.GetCurrentFrameBBox(boxModel); // if model has collision if( en.en_pciCollisionInfo!=NULL && (en.GetRenderType()==CEntity::RT_MODEL || en.GetRenderType()==CEntity::RT_EDITORMODEL)) { // get its collision box INDEX iCollision = en.GetCollisionBoxIndex(); FLOAT3D vMin = moModelObject.GetCollisionBoxMin(iCollision); FLOAT3D vMax = moModelObject.GetCollisionBoxMax(iCollision); // extend the box by the collision box boxModel|=FLOATaabbox3D(vMin, vMax); } // set position of marker at top of the model and it size to be proportional to the model boxModel.StretchByVector(moModelObject.mo_Stretch); FLOAT fSize = boxModel.Size().Length()*0.3f; _wrpWorldRenderPrefs.wrp_pmoSelectedEntity->mo_Stretch = FLOAT3D( fSize, fSize, fSize); CPlacement3D plSelection = en.GetLerpedPlacement(); plSelection.Translate_OwnSystem( FLOAT3D(0.0f, boxModel.Max()(2), 0.0f)); // render the selection model without shadow RenderOneModel( en, *_wrpWorldRenderPrefs.wrp_pmoSelectedEntity, plSelection, dm.dm_fMipFactor, FALSE, 0); } } } // end model rendering if( !re_bRenderingShadows) { EndModelRenderingView(FALSE); // don't restore ortho projection for now RM_EndRenderingView(FALSE); } else { EndModelRenderingMask(); RM_EndModelRenderingMask(); } // done _pfRenderProfile.StopTimer(CRenderProfile::PTI_RENDERMODELS); } extern CEntity *_Particle_penCurrentViewer; extern FLOAT _Particle_fCurrentMip; extern BOOL _Particle_bHasFog; extern BOOL _Particle_bHasHaze; void Particle_PrepareEntity( FLOAT fMipFactor, BOOL bHasFog, BOOL bHasHaze, CEntity *penViewer) { _Particle_fCurrentMip = fMipFactor; _Particle_bHasFog = bHasFog; _Particle_bHasHaze = bHasHaze; _Particle_penCurrentViewer = penViewer; } /* Render particles for models that were kept for delayed rendering. */ void CRenderer::RenderParticles(BOOL bBackground) { if( _bMultiPlayer) gfx_bRenderParticles = 1; // must render in multiplayer mode! if( re_bRenderingShadows || !gfx_bRenderParticles) return; _pfRenderProfile.StartTimer(CRenderProfile::PTI_RENDERPARTICLES); // prepare gfx library for particles if (bBackground) { Particle_PrepareSystem(re_pdpDrawPort, re_prBackgroundProjection); } else { Particle_PrepareSystem(re_pdpDrawPort, re_prProjection); } // for each of models that were kept for delayed rendering for(INDEX iModel=0; iModelSetOrtho(); // if there are no flares of flares are off, do nothing gfx_iLensFlareQuality = Clamp( gfx_iLensFlareQuality, 0L, 3L); if( gfx_iLensFlareQuality==0 || re_alfiLensFlares.Count()==0) return; // get drawport ID ASSERT( re_pdpDrawPort!=NULL); const ULONG ulDrawPortID = re_pdpDrawPort->GetID(); // for each lens flare of this drawport {for(INDEX iFlare=0; iFlareIsPointVisible( lfi.lfi_fI, lfi.lfi_fJ, lfi.lfi_fOoK, lfi.lfi_iID, lfi.lfi_iMirrorLevel)) { lfi.lfi_ulFlags |= LFF_VISIBLE; } }} // get count of currently existing flares and time INDEX ctFlares = re_alfiLensFlares.Count(); const TIME tmNow = _pTimer->GetRealTimeTick(); // for each lens flare INDEX iFlare=0; while(iFlarels_plftLensFlare==NULL)) || (lfi.lfi_ulDrawPortID!=ulDrawPortID && lfi.lfi_tmLastFramels_plftLensFlare==NULL) { continue; } // clear active flag for next frame lfi.lfi_ulFlags &= ~LFF_ACTIVE; // fade the flare in/out #define FLAREINSPEED (0.2f) #define FLAREOUTSPEED (0.1f) if( lfi.lfi_ulFlags&LFF_VISIBLE) { lfi.lfi_fFadeFactor += (tmNow-lfi.lfi_tmLastFrame) / FLAREINSPEED; } else { lfi.lfi_fFadeFactor -= (tmNow-lfi.lfi_tmLastFrame) / FLAREOUTSPEED; } lfi.lfi_fFadeFactor = Max( Min(lfi.lfi_fFadeFactor, 1.0f), 0.0f); // reset timer of flare lfi.lfi_tmLastFrame = tmNow; // skip if the flare is invisible if( lfi.lfi_fFadeFactor<0.01f) continue; // calculate general flare factors FLOAT fScreenSizeI = re_pdpDrawPort->GetWidth(); FLOAT fScreenSizeJ = re_pdpDrawPort->GetHeight(); FLOAT fScreenCenterI = fScreenSizeI*0.5f; FLOAT fScreenCenterJ = fScreenSizeJ*0.5f; FLOAT fLightI = lfi.lfi_fI; FLOAT fLightJ = lfi.lfi_fJ; FLOAT fIPositionFactor = (fLightI-fScreenCenterI)/fScreenSizeI; FLOAT fReflectionDirI = fScreenCenterI-fLightI; FLOAT fReflectionDirJ = fScreenCenterJ-fLightJ; UBYTE ubR, ubG, ubB; lfi.lfi_plsLightSource->GetLightColor(ubR, ubG, ubB); UBYTE ubI = (ULONG(ubR)+ULONG(ubG)+ULONG(ubB))/3; FLOAT fReflectionDistance = sqrt(fReflectionDirI*fReflectionDirI+fReflectionDirJ*fReflectionDirJ); FLOAT fOfCenterFadeFactor = 1.0f-2.0f*fReflectionDistance/fScreenSizeI; fOfCenterFadeFactor = Max(fOfCenterFadeFactor, 0.0f); FLOAT fFogHazeFade = 1.0f; // if in haze if( lfi.lfi_ulFlags&LFF_HAZE) { // get haze strength at light position FLOAT fS = (-lfi.lfi_vProjected(3)+_haze_fAdd)*_haze_fMul; FLOAT fHazeStrength = NormByteToFloat(GetHazeAlpha(fS)); // fade flare with haze fFogHazeFade *= 1-fHazeStrength; } // if in fog if( lfi.lfi_ulFlags&LFF_FOG) { // get fog strength at light position GFXTexCoord tex; tex.s = -lfi.lfi_vProjected(3)*_fog_fMulZ; tex.t = (lfi.lfi_vProjected%_fog_vHDirView+_fog_fAddH)*_fog_fMulH; FLOAT fFogStrength = NormByteToFloat(GetFogAlpha(tex)); // fade flare with fog fFogHazeFade *= 1-fFogStrength; } // get number of flares to render for this light CStaticArray &aolf = lfi.lfi_plsLightSource->ls_plftLensFlare->lft_aolfFlares; INDEX ctReflections = aolf.Count(); // clamp number reflections if required if( gfx_iLensFlareQuality<2 || _wrpWorldRenderPrefs.wrp_lftLensFlares==CWorldRenderPrefs::LFT_SINGLE_FLARE) { ctReflections = 1; } // for each flare in the lens flare effect {for(INDEX iReflection=0; iReflectionls_rFallOff*olf.oft_fFallOffFactor, lfi.lfi_plsLightSource->ls_rHotSpot*olf.oft_fFallOffFactor, lfi.lfi_fDistance); fFadeFactor*=fFogHazeFade; if (olf.olf_ulFlags&OLF_FADEOFCENTER) { fFadeFactor*=fOfCenterFadeFactor; } FLOAT fSizeIFactor = fScreenSizeI; if (olf.olf_ulFlags&OLF_FADESIZE) { fSizeIFactor *= fFadeFactor; } FLOAT fIntensityFactor = olf.olf_fLightAmplification; if (olf.olf_ulFlags&OLF_FADEINTENSITY) { fIntensityFactor *= fFadeFactor; } // calculate flare size FLOAT fSizeI = olf.olf_fSizeIOverScreenSizeI*fSizeIFactor; FLOAT fSizeJ = olf.olf_fSizeJOverScreenSizeI*fSizeIFactor; // skip if this flare is invisible if( fIntensityFactor<0.01f || fSizeI<2.0f || fSizeJ<2.0f) continue; // determine color FLOAT fThisR = (ubR + (FLOAT(ubI)-ubR)*olf.olf_fLightDesaturation)*fIntensityFactor; FLOAT fThisG = (ubG + (FLOAT(ubI)-ubG)*olf.olf_fLightDesaturation)*fIntensityFactor; FLOAT fThisB = (ubB + (FLOAT(ubI)-ubB)*olf.olf_fLightDesaturation)*fIntensityFactor; UBYTE ubThisR = Min( fThisR, 255.0f); UBYTE ubThisG = Min( fThisG, 255.0f); UBYTE ubThisB = Min( fThisB, 255.0f); COLOR colBlending = RGBToColor( ubThisR,ubThisG,ubThisB); // render the flare re_pdpDrawPort->RenderLensFlare( &olf.olf_toTexture, fLightI+olf.olf_fReflectionPosition*fReflectionDirI, fLightJ+olf.olf_fReflectionPosition*fReflectionDirJ, fSizeI, fSizeJ, olf.olf_aRotationFactor*fIPositionFactor, colBlending); }} // for each flare in the lens flare effect // if screen glare is on CLensFlareType &lft = *lfi.lfi_plsLightSource->ls_plftLensFlare; FLOAT fGlearCompression = lft.lft_fGlareCompression; if( gfx_iLensFlareQuality>2 && _wrpWorldRenderPrefs.wrp_lftLensFlares >= CWorldRenderPrefs::LFT_REFLECTIONS_AND_GLARE && lft.lft_fGlareIntensity>0.01f) { // calculate glare factor for current position FLOAT fIntensity = IntensityAtDistance( lfi.lfi_plsLightSource->ls_rFallOff*lft.lft_fGlareFallOffFactor, lfi.lfi_plsLightSource->ls_rHotSpot*lft.lft_fGlareFallOffFactor, lfi.lfi_fDistance); FLOAT fCenterFactor = (1-fOfCenterFadeFactor); FLOAT fGlare = lft.lft_fGlareIntensity*fIntensity * (exp(1.0f/(1.0f+fGlearCompression*fCenterFactor*fCenterFactor)) -1.0f) / (exp(1.0f)-1.0f); ULONG ulGlareA = ClampUp( NormFloatToByte(fGlare), 255UL); // if there is any relevant glare if( ulGlareA>1) { // calculate glare color FLOAT fGlareR = (ubR + (FLOAT(ubI)-ubR) *lft.lft_fGlareDesaturation); FLOAT fGlareG = (ubG + (FLOAT(ubI)-ubG) *lft.lft_fGlareDesaturation); FLOAT fGlareB = (ubB + (FLOAT(ubI)-ubB) *lft.lft_fGlareDesaturation); FLOAT fMax = Max( fGlareR, Max(fGlareG, fGlareB)); FLOAT fBrightFactor = 255.0f/fMax; fGlareR *= fBrightFactor; fGlareG *= fBrightFactor; fGlareB *= fBrightFactor; ULONG ulGlareR = ClampUp( FloatToInt(fGlareR), 255L); ULONG ulGlareG = ClampUp( FloatToInt(fGlareG), 255L); ULONG ulGlareB = ClampUp( FloatToInt(fGlareB), 255L); // add the glare to screen blending re_pdpDrawPort->dp_ulBlendingRA += ulGlareR*ulGlareA; re_pdpDrawPort->dp_ulBlendingGA += ulGlareG*ulGlareA; re_pdpDrawPort->dp_ulBlendingBA += ulGlareB*ulGlareA; re_pdpDrawPort->dp_ulBlendingA += ulGlareA; } } }} }