#include "common.h" #include "main.h" #include "TxdStore.h" #include "Timer.h" #include "Camera.h" #include "Timecycle.h" #include "CutsceneMgr.h" #include "Automobile.h" #include "Bike.h" #include "Ped.h" #include "PlayerPed.h" #include "World.h" #include "Weather.h" #include "ModelIndices.h" #include "RenderBuffer.h" #ifdef FIX_BUGS #include "Replay.h" #endif #include "PointLights.h" #include "SpecialFX.h" #include "Script.h" #include "TimeStep.h" #include "Shadows.h" #include "CutsceneObject.h" #include "CutsceneShadow.h" #include "Clock.h" #include "VarConsole.h" #ifdef DEBUGMENU SETTWEAKPATH("Shadows"); TWEAKBOOL(gbPrintShite); #endif RwImVertexIndex ShadowIndexList[24]; RwTexture *gpShadowCarTex; RwTexture *gpShadowPedTex; RwTexture *gpShadowHeliTex; RwTexture *gpShadowBikeTex; RwTexture *gpShadowBaronTex; RwTexture *gpShadowExplosionTex; RwTexture *gpShadowHeadLightsTex; RwTexture *gpOutline1Tex; RwTexture *gpOutline2Tex; RwTexture *gpOutline3Tex; RwTexture *gpBloodPoolTex; RwTexture *gpReflectionTex; RwTexture *gpWalkDontTex; RwTexture *gpCrackedGlassTex; RwTexture *gpPostShadowTex; RwTexture *gpGoalTex; int16 CShadows::ShadowsStoredToBeRendered; CStoredShadow CShadows::asShadowsStored [MAX_STOREDSHADOWS]; CPolyBunch CShadows::aPolyBunches [MAX_POLYBUNCHES]; CStaticShadow CShadows::aStaticShadows [MAX_STATICSHADOWS]; CPolyBunch *CShadows::pEmptyBunchList; CPermanentShadow CShadows::aPermanentShadows[MAX_PERMAMENTSHADOWS]; #ifndef MASTER bool gbCountPolysInShadow; #endif void CShadows::Init(void) { CTxdStore::PushCurrentTxd(); int32 slut = CTxdStore::FindTxdSlot("particle"); CTxdStore::SetCurrentTxd(slut); gpShadowCarTex = RwTextureRead("shad_car", nil); gpShadowPedTex = RwTextureRead("shad_ped", nil); gpShadowHeliTex = RwTextureRead("shad_heli", nil); gpShadowBikeTex = RwTextureRead("shad_bike", nil); gpShadowBaronTex = RwTextureRead("shad_rcbaron", nil); gpShadowExplosionTex = RwTextureRead("shad_exp", nil); gpShadowHeadLightsTex = RwTextureRead("headlight", nil); gpOutline1Tex = RwTextureRead("outline_64", nil); gpOutline2Tex = RwTextureRead("outline2_64", nil); gpOutline3Tex = RwTextureRead("outline3_64", nil); gpBloodPoolTex = RwTextureRead("bloodpool_64", nil); gpReflectionTex = RwTextureRead("reflection01", nil); gpWalkDontTex = RwTextureRead("walk_dont", nil); gpCrackedGlassTex = RwTextureRead("wincrack_32", nil); gpPostShadowTex = RwTextureRead("lamp_shad_64", nil); CTxdStore::PopCurrentTxd(); ASSERT(gpShadowCarTex != nil); ASSERT(gpShadowPedTex != nil); ASSERT(gpShadowHeliTex != nil); ASSERT(gpShadowBikeTex != nil); ASSERT(gpShadowBaronTex != nil); ASSERT(gpShadowExplosionTex != nil); ASSERT(gpShadowHeadLightsTex != nil); ASSERT(gpOutline1Tex != nil); ASSERT(gpOutline2Tex != nil); ASSERT(gpOutline3Tex != nil); ASSERT(gpBloodPoolTex != nil); ASSERT(gpReflectionTex != nil); ASSERT(gpWalkDontTex != nil); ASSERT(gpCrackedGlassTex != nil); ASSERT(gpPostShadowTex != nil); ShadowIndexList[0] = 0; ShadowIndexList[1] = 2; ShadowIndexList[2] = 1; ShadowIndexList[3] = 0; ShadowIndexList[4] = 3; ShadowIndexList[5] = 2; ShadowIndexList[6] = 0; ShadowIndexList[7] = 4; ShadowIndexList[8] = 3; ShadowIndexList[9] = 0; ShadowIndexList[10] = 5; ShadowIndexList[11] = 4; ShadowIndexList[12] = 0; ShadowIndexList[13] = 6; ShadowIndexList[14] = 5; ShadowIndexList[15] = 0; ShadowIndexList[16] = 7; ShadowIndexList[17] = 6; ShadowIndexList[18] = 0; ShadowIndexList[19] = 8; ShadowIndexList[20] = 7; ShadowIndexList[21] = 0; ShadowIndexList[22] = 9; ShadowIndexList[23] = 8; for ( int32 i = 0; i < MAX_STATICSHADOWS; i++ ) { aStaticShadows[i].m_nId = 0; aStaticShadows[i].m_pPolyBunch = nil; } pEmptyBunchList = &aPolyBunches[0]; for ( int32 i = 0; i < MAX_POLYBUNCHES; i++ ) { if ( i == MAX_POLYBUNCHES - 1 ) aPolyBunches[i].m_pNext = nil; else aPolyBunches[i].m_pNext = &aPolyBunches[i + 1]; } for ( int32 i = 0; i < MAX_PERMAMENTSHADOWS; i++ ) { aPermanentShadows[i].m_nType = SHADOWTYPE_NONE; } #ifndef MASTER VarConsole.Add("Count polys in shadow", &gbCountPolysInShadow, true); #endif } void CShadows::Shutdown(void) { ASSERT(gpShadowCarTex != nil); ASSERT(gpShadowPedTex != nil); ASSERT(gpShadowHeliTex != nil); ASSERT(gpShadowBikeTex != nil); ASSERT(gpShadowBaronTex != nil); ASSERT(gpShadowExplosionTex != nil); ASSERT(gpShadowHeadLightsTex != nil); ASSERT(gpOutline1Tex != nil); ASSERT(gpOutline2Tex != nil); ASSERT(gpOutline3Tex != nil); ASSERT(gpBloodPoolTex != nil); ASSERT(gpReflectionTex != nil); ASSERT(gpWalkDontTex != nil); ASSERT(gpCrackedGlassTex != nil); ASSERT(gpPostShadowTex != nil); RwTextureDestroy(gpShadowCarTex); RwTextureDestroy(gpShadowPedTex); RwTextureDestroy(gpShadowHeliTex); RwTextureDestroy(gpShadowBikeTex); RwTextureDestroy(gpShadowBaronTex); RwTextureDestroy(gpShadowExplosionTex); RwTextureDestroy(gpShadowHeadLightsTex); RwTextureDestroy(gpOutline1Tex); RwTextureDestroy(gpOutline2Tex); RwTextureDestroy(gpOutline3Tex); RwTextureDestroy(gpBloodPoolTex); RwTextureDestroy(gpReflectionTex); RwTextureDestroy(gpWalkDontTex); RwTextureDestroy(gpCrackedGlassTex); RwTextureDestroy(gpPostShadowTex); } void CShadows::AddPermanentShadow(uint8 ShadowType, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, uint32 nTime, float fScale) { ASSERT(pTexture != nil); ASSERT(pPosn != nil); // find free slot int32 nSlot = 0; while ( nSlot < MAX_PERMAMENTSHADOWS && aPermanentShadows[nSlot].m_nType != SHADOWTYPE_NONE ) nSlot++; if ( nSlot < MAX_PERMAMENTSHADOWS ) { aPermanentShadows[nSlot].m_nType = ShadowType; aPermanentShadows[nSlot].m_pTexture = pTexture; aPermanentShadows[nSlot].m_vecPos = *pPosn; aPermanentShadows[nSlot].m_vecFront.x = fFrontX; aPermanentShadows[nSlot].m_vecFront.y = fFrontY; aPermanentShadows[nSlot].m_vecSide.x = fSideX; aPermanentShadows[nSlot].m_vecSide.y = fSideY; aPermanentShadows[nSlot].m_nIntensity = nIntensity; aPermanentShadows[nSlot].m_nRed = nRed; aPermanentShadows[nSlot].m_nGreen = nGreen; aPermanentShadows[nSlot].m_nBlue = nBlue; aPermanentShadows[nSlot].m_fZDistance = fZDistance; aPermanentShadows[nSlot].m_nLifeTime = nTime; aPermanentShadows[nSlot].m_nTimeCreated = CTimer::GetTimeInMilliseconds(); } } bool CShadows::StoreStaticShadow(uint32 nID, uint8 ShadowType, RwTexture *pTexture, Const CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, float fDrawDistance, bool bTempShadow, float fUpDistance) { ASSERT(pPosn != nil); float fDistToCamSqr = (*pPosn - TheCamera.GetPosition()).MagnitudeSqr2D(); if ( SQR(fDrawDistance) > fDistToCamSqr || fDrawDistance == 0.0f ) { if ( fDrawDistance != 0.0f ) { float fDistToCam = Sqrt(fDistToCamSqr); if ( fDistToCam >= (fDrawDistance*(1.0f-(1.0f/4.0f))) ) { //fDistToCam == 0 -> 4 //fDistToCam == fDrawDistance -> 0 float fMult = 1.0f - (4.0f / fDrawDistance) * (fDistToCam - (fDrawDistance*(1.0f-(1.0f/4.0f)))); nIntensity = (int32)(nIntensity * fMult); nRed = (int32)(nRed * fMult); nGreen = (int32)(nGreen * fMult); nBlue = (int32)(nBlue * fMult); } } int32 nSlot; nSlot = 0; while ( nSlot < MAX_STATICSHADOWS && !(nID == aStaticShadows[nSlot].m_nId && aStaticShadows[nSlot].m_pPolyBunch != nil) ) nSlot++; if ( nSlot < MAX_STATICSHADOWS ) { if ( Abs(pPosn->x - aStaticShadows[nSlot].m_vecPosn.x) < fUpDistance && Abs(pPosn->y - aStaticShadows[nSlot].m_vecPosn.y) < fUpDistance ) { aStaticShadows[nSlot].m_bJustCreated = true; aStaticShadows[nSlot].m_nType = ShadowType; aStaticShadows[nSlot].m_pTexture = pTexture; aStaticShadows[nSlot].m_nIntensity = nIntensity; aStaticShadows[nSlot].m_nRed = nRed; aStaticShadows[nSlot].m_nGreen = nGreen; aStaticShadows[nSlot].m_nBlue = nBlue; aStaticShadows[nSlot].m_fZDistance = fZDistance; aStaticShadows[nSlot].m_fScale = fScale; aStaticShadows[nSlot].m_bTemp = bTempShadow; aStaticShadows[nSlot].m_nTimeCreated = CTimer::GetTimeInMilliseconds(); return true; } else if ( Abs(pPosn->x - aStaticShadows[nSlot].m_vecPosn.x) < 0.05f && Abs(pPosn->y - aStaticShadows[nSlot].m_vecPosn.y) < 0.05f && Abs(pPosn->z - aStaticShadows[nSlot].m_vecPosn.z) < 2.0f && fFrontX == aStaticShadows[nSlot].m_vecFront.x && fFrontY == aStaticShadows[nSlot].m_vecFront.y && fSideX == aStaticShadows[nSlot].m_vecSide.x && fSideY == aStaticShadows[nSlot].m_vecSide.y ) { aStaticShadows[nSlot].m_bJustCreated = true; aStaticShadows[nSlot].m_nType = ShadowType; aStaticShadows[nSlot].m_pTexture = pTexture; aStaticShadows[nSlot].m_nIntensity = nIntensity; aStaticShadows[nSlot].m_nRed = nRed; aStaticShadows[nSlot].m_nGreen = nGreen; aStaticShadows[nSlot].m_nBlue = nBlue; aStaticShadows[nSlot].m_fZDistance = fZDistance; aStaticShadows[nSlot].m_fScale = fScale; aStaticShadows[nSlot].m_bTemp = bTempShadow; aStaticShadows[nSlot].m_nTimeCreated = CTimer::GetTimeInMilliseconds(); return true; } else { aStaticShadows[nSlot].Free(); aStaticShadows[nSlot].m_nId = nID; aStaticShadows[nSlot].m_nType = ShadowType; aStaticShadows[nSlot].m_pTexture = pTexture; aStaticShadows[nSlot].m_nIntensity = nIntensity; aStaticShadows[nSlot].m_nRed = nRed; aStaticShadows[nSlot].m_nGreen = nGreen; aStaticShadows[nSlot].m_nBlue = nBlue; aStaticShadows[nSlot].m_fZDistance = fZDistance; aStaticShadows[nSlot].m_fScale = fScale; aStaticShadows[nSlot].m_vecPosn = *pPosn; aStaticShadows[nSlot].m_vecFront.x = fFrontX; aStaticShadows[nSlot].m_vecFront.y = fFrontY; aStaticShadows[nSlot].m_vecSide.x = fSideX; aStaticShadows[nSlot].m_vecSide.y = fSideY; aStaticShadows[nSlot].m_bJustCreated = true; aStaticShadows[nSlot].m_bTemp = bTempShadow; aStaticShadows[nSlot].m_nTimeCreated = CTimer::GetTimeInMilliseconds(); GeneratePolysForStaticShadow(nSlot); return aStaticShadows[nSlot].m_pPolyBunch != nil; } } else { nSlot = 0; while ( nSlot < MAX_STATICSHADOWS && aStaticShadows[nSlot].m_pPolyBunch != nil ) nSlot++; if ( nSlot != MAX_STATICSHADOWS ) { aStaticShadows[nSlot].m_nId = nID; aStaticShadows[nSlot].m_nType = ShadowType; aStaticShadows[nSlot].m_pTexture = pTexture; aStaticShadows[nSlot].m_nIntensity = nIntensity; aStaticShadows[nSlot].m_nRed = nRed; aStaticShadows[nSlot].m_nGreen = nGreen; aStaticShadows[nSlot].m_nBlue = nBlue; aStaticShadows[nSlot].m_fZDistance = fZDistance; aStaticShadows[nSlot].m_fScale = fScale; aStaticShadows[nSlot].m_vecPosn = *pPosn; aStaticShadows[nSlot].m_vecFront.x = fFrontX; aStaticShadows[nSlot].m_vecFront.y = fFrontY; aStaticShadows[nSlot].m_vecSide.x = fSideX; aStaticShadows[nSlot].m_vecSide.y = fSideY; aStaticShadows[nSlot].m_bJustCreated = true; aStaticShadows[nSlot].m_bTemp = bTempShadow; aStaticShadows[nSlot].m_nTimeCreated = CTimer::GetTimeInMilliseconds(); GeneratePolysForStaticShadow(nSlot); return aStaticShadows[nSlot].m_pPolyBunch != nil; } } } return true; } void CShadows::StoreShadowToBeRendered(uint8 ShadowTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue) { ASSERT(pPosn != nil); switch ( ShadowTexture ) { case SHADOWTEX_NONE: { break; } case SHADOWTEX_CAR: { StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowCarTex, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, 15.0f, false, 1.0f, nil, false); break; } case SHADOWTEX_PED: { StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowPedTex, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, 15.0f, false, 1.0f, nil, false); break; } case SHADOWTEX_EXPLOSION: { StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, 15.0f, false, 1.0f, nil, false); break; } case SHADOWTEX_HELI: { StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowHeliTex, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, 15.0f, false, 1.0f, nil, false); break; } case SHADOWTEX_HEADLIGHTS: { StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, gpShadowHeadLightsTex, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, 15.0f, false, 1.0f, nil, false); break; } case SHADOWTEX_BLOOD: { StoreShadowToBeRendered(SHADOWTYPE_DARK, gpBloodPoolTex, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, 150, 0, 15.0f, false, 1.0f, nil, false); break; } } //ASSERT(false); } void CShadows::StoreShadowToBeRendered(uint8 ShadowType, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, bool bDrawOnWater, float fScale, CCutsceneShadow *pShadow, bool bDrawOnBuildings) { ASSERT(pTexture != nil); ASSERT(pPosn != nil); if ( ShadowsStoredToBeRendered < MAX_STOREDSHADOWS ) { asShadowsStored[ShadowsStoredToBeRendered].m_ShadowType = ShadowType; asShadowsStored[ShadowsStoredToBeRendered].m_pTexture = pTexture; asShadowsStored[ShadowsStoredToBeRendered].m_vecPos = *pPosn; asShadowsStored[ShadowsStoredToBeRendered].m_vecFront.x = fFrontX; asShadowsStored[ShadowsStoredToBeRendered].m_vecFront.y = fFrontY; asShadowsStored[ShadowsStoredToBeRendered].m_vecSide.x = fSideX; asShadowsStored[ShadowsStoredToBeRendered].m_vecSide.y = fSideY; asShadowsStored[ShadowsStoredToBeRendered].m_nIntensity = nIntensity; asShadowsStored[ShadowsStoredToBeRendered].m_nRed = nRed; asShadowsStored[ShadowsStoredToBeRendered].m_nGreen = nGreen; asShadowsStored[ShadowsStoredToBeRendered].m_nBlue = nBlue; asShadowsStored[ShadowsStoredToBeRendered].m_fZDistance = fZDistance; asShadowsStored[ShadowsStoredToBeRendered].m_nFlags.bDrawOnWater = bDrawOnWater; asShadowsStored[ShadowsStoredToBeRendered].m_nFlags.bDrawOnBuildings = bDrawOnBuildings; asShadowsStored[ShadowsStoredToBeRendered].m_fScale = fScale; asShadowsStored[ShadowsStoredToBeRendered].m_pCutsceneShadow = pShadow; ShadowsStoredToBeRendered++; } } void CShadows::StoreShadowForVehicle(CVehicle *pCar, VEH_SHD_TYPE type) { ASSERT(pCar != nil); if ( CTimeCycle::GetShadowStrength() != 0 ) { CVector CarPos = pCar->GetPosition(); float fDistToCamSqr = (CarPos - TheCamera.GetPosition()).MagnitudeSqr2D(); if ( CCutsceneMgr::IsRunning() ) fDistToCamSqr /= SQR(TheCamera.LODDistMultiplier) * 4.0f; float fDrawDistance; switch ( type ) { case VEH_SHD_TYPE_SEAPLANE: case VEH_SHD_TYPE_RCPLANE: fDrawDistance = 144.0f; break; case VEH_SHD_TYPE_HELI: fDrawDistance = 144.0f; break; default: fDrawDistance = 18.0f; break; } if ( fDistToCamSqr < SQR(fDrawDistance) ) { float fDistToCam = Sqrt(fDistToCamSqr); //fDistToCam == 0 -> 4 //fDistToCam == fDrawDistance -> 0 float fMult = 1.0f - (fDistToCam - (fDrawDistance*(1.0f-(1.0f/4.0f)))) / (fDrawDistance*(1.0f/4.0f)); int32 nColorStrength; if ( fDistToCam >= (fDrawDistance*(1.0f-(1.0f/4.0f))) ) nColorStrength = (int32)(fMult * CTimeCycle::GetShadowStrength()); else nColorStrength = CTimeCycle::GetShadowStrength(); float fVehicleHeight = pCar->GetColModel()->boundingBox.GetSize().y; float fVehicleWidth = pCar->GetColModel()->boundingBox.GetSize().x; float size = 1.0f; if ( pCar->GetModelIndex() == MI_HUNTER ) { fVehicleWidth *= 3.0f; fVehicleHeight *= 1.4f; size *= 0.5f; } else if ( pCar->GetModelIndex() == MI_ANGEL ) { fVehicleHeight = fVehicleHeight * 1.5f; size = 0.03f; } else if ( pCar->GetModelIndex() == MI_SEASPAR ) { fVehicleWidth *= 3.0f; fVehicleHeight *= 1.4f; size *= 0.5f; } else if ( pCar->GetModelIndex() == MI_PIZZABOY || pCar->GetModelIndex() == MI_PCJ600 || pCar->GetModelIndex() == MI_FAGGIO ) { fVehicleHeight *= 1.2f; size = 0.05f; } else if ( pCar->GetModelIndex() == MI_FREEWAY ) { fVehicleHeight *= 1.5f; size = 0.03f; } else if ( pCar->GetModelIndex() == MI_RCRAIDER ) { fVehicleHeight *= 1.5f; fVehicleWidth *= 2.0f; size = 0.2f; } else if ( pCar->GetModelIndex() == MI_SANCHEZ ) { fVehicleHeight *= 1.5f; size = 0.03f; } else if ( pCar->GetModelIndex() == MI_SPARROW || pCar->GetModelIndex() == MI_MAVERICK || pCar->GetModelIndex() == MI_VCNMAV || pCar->GetModelIndex() == MI_POLMAV ) { fVehicleWidth *= 3.0f; fVehicleHeight *= 1.4f; size *= 0.5f; } else if ( pCar->GetModelIndex() == MI_RCGOBLIN ) { fVehicleHeight *= 1.5f; fVehicleWidth *= 2.0f; size = 0.2f; } else if ( pCar->GetModelIndex() == MI_DODO ) { fVehicleHeight *= 0.9f; fVehicleWidth *= 0.4f; } CarPos.x -= pCar->GetForward().x * (((fVehicleHeight/2) - pCar->GetColModel()->boundingBox.max.y)*size); CarPos.y -= pCar->GetForward().y * (((fVehicleHeight/2) - pCar->GetColModel()->boundingBox.max.y)*size); RwTexture *tex = gpShadowCarTex; switch ( type ) { case VEH_SHD_TYPE_BIKE: { float wheelZ = Abs(((CBike*)pCar)->m_fLeanLRAngle); float mul = 5.092958f * wheelZ + 1.0f; if (pCar->GetStatus() == STATUS_PHYSICS) { float z = pCar->GetRight().z; if (z > 0.6f) mul += 4.0f * z; } fVehicleWidth *= mul; tex = gpShadowBikeTex; break; } case VEH_SHD_TYPE_HELI: tex = gpShadowHeliTex; break; case VEH_SHD_TYPE_SEAPLANE: nColorStrength = CTimeCycle::GetShadowStrength(); tex = gpShadowBaronTex; break; case VEH_SHD_TYPE_RCPLANE: tex = gpShadowBaronTex; fVehicleHeight *= 1.5f; fVehicleWidth *= 2.2f; break; case VEH_SHD_TYPE_CAR: tex = gpShadowCarTex; break; } float frontx = pCar->GetForward().x; float fronty = pCar->GetForward().y; float sidex = pCar->GetRight().x; float sidey = pCar->GetRight().y; switch ( type ) { case VEH_SHD_TYPE_BIKE: if ( Abs(pCar->GetRight().z) > 0.6f ) { sidex = pCar->GetUp().x; sidey = pCar->GetUp().y; } break; case VEH_SHD_TYPE_HELI: if ( Abs(pCar->GetRight().z) > 0.57f ) { sidex = pCar->GetUp().x; sidey = pCar->GetUp().y; } if ( Abs(pCar->GetForward().z) > 0.57f ) { frontx = pCar->GetUp().x; fronty = pCar->GetUp().y; } break; } bool bDrawOnBuildings = false; if ( pCar->GetModelIndex() == MI_RCBANDIT || pCar->GetModelIndex() == MI_RCBARON || pCar->GetModelIndex() == MI_RCRAIDER || pCar->GetModelIndex() == MI_RCGOBLIN || pCar == FindPlayerVehicle() ) { bDrawOnBuildings = true; } if ( pCar->m_vecMoveSpeed.Magnitude() * CTimeStep::ms_fTimeStep > 0.1f || bDrawOnBuildings ) { if ( pCar->GetUp().z > 0.0f ) { StoreShadowToBeRendered(SHADOWTYPE_DARK, tex, &CarPos, frontx * (fVehicleHeight / 2), fronty * (fVehicleHeight / 2), sidex * (fVehicleWidth / 2), sidey * (fVehicleWidth / 2), nColorStrength, nColorStrength, nColorStrength, nColorStrength, 4.5f, false, 1.0f, nil, bDrawOnBuildings); } else { StoreShadowToBeRendered(SHADOWTYPE_DARK, tex, &CarPos, frontx * (fVehicleHeight / 2), fronty * (fVehicleHeight / 2), -sidex * (fVehicleWidth / 2), -sidey * (fVehicleWidth / 2), nColorStrength, nColorStrength, nColorStrength, nColorStrength, 4.5f, false, 1.0f, nil, bDrawOnBuildings); } } else { if ( pCar->GetUp().z > 0.0f ) { StoreStaticShadow((uintptr)pCar + 1, SHADOWTYPE_DARK, tex, &CarPos, frontx * (fVehicleHeight / 2), fronty * (fVehicleHeight / 2), sidex * (fVehicleWidth / 2), sidey * (fVehicleWidth / 2), nColorStrength, nColorStrength, nColorStrength, nColorStrength, 4.5f, 1.0f, 0.0f, false, 0.1f); } else { StoreStaticShadow((uintptr)pCar + 1, SHADOWTYPE_DARK, tex, &CarPos, frontx * (fVehicleHeight / 2), fronty * (fVehicleHeight / 2), -sidex * (fVehicleWidth / 2), -sidey * (fVehicleWidth / 2), nColorStrength, nColorStrength, nColorStrength, nColorStrength, 4.5f, 1.0f, 0.0f, false, 0.1f); } } } } } void CShadows::StoreCarLightShadow(CVehicle *pCar, int32 nID, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, uint8 nRed, uint8 nGreen, uint8 nBlue, float fMaxViewAngle) { ASSERT(pCar != nil); ASSERT(pPosn != nil); float fDistToCamSqr = (*pPosn - TheCamera.GetPosition()).MagnitudeSqr2D(); bool bSpecialCam = TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED || CCutsceneMgr::IsRunning(); float fDrawDistance = 27.0f; if ( fDistToCamSqr < SQR(fDrawDistance) || bSpecialCam ) { if ( bSpecialCam || DotProduct2D(CVector2D(TheCamera.CamFrontXNorm, TheCamera.CamFrontYNorm), *pPosn - TheCamera.GetPosition() ) > -fMaxViewAngle ) { float fDistToCam = Sqrt(fDistToCamSqr); if ( fDistToCam >= (fDrawDistance*(1.0f-(1.0f/4.0f))) && !bSpecialCam ) // BUG? must be 3.0? { //fDistToCam == 0 -> 3 //fDistToCam == fDrawDistance -> 0 float fMult = 1.0f - (3.0f / fDrawDistance) * (fDistToCam - (fDrawDistance*(1.0f-(1.0f/3.0f))) ); nRed = (int32)(nRed * fMult); nGreen = (int32)(nGreen * fMult); nBlue = (int32)(nBlue * fMult); } if ( pCar->m_vecMoveSpeed.Magnitude() * CTimeStep::ms_fTimeStep > 0.4f || pCar == FindPlayerVehicle() ) { StoreShadowToBeRendered(SHADOWTYPE_ADDITIVE, pTexture, pPosn, fFrontX, fFrontY, fSideX, fSideY, 128, nRed, nGreen, nBlue, 6.0f, false, 1.0f, nil, pCar == FindPlayerVehicle()); } else { StoreStaticShadow((uintptr)pCar + nID, SHADOWTYPE_ADDITIVE, pTexture, pPosn, fFrontX, fFrontY, fSideX, fSideY, 128, nRed, nGreen, nBlue, 6.0f, 1.0f, 27.0f, false, 0.4f); } } } } #ifdef USE_CUTSCENE_SHADOW_FOR_PED void StoreShadowForCutscenePedObject(CPed *pObject, float fDisplacementX, float fDisplacementY, float fFrontX, float fFrontY, float fSideX, float fSideY) { ASSERT(pObject != nil); CCutsceneShadow *shadow = pObject->m_pRTShadow; if ( shadow == nil ) return; if ( !shadow->IsInitialized() ) return; CVector pos = pObject->GetPosition(); float fDistToCamSqr = (pos - TheCamera.GetPosition()).MagnitudeSqr2D(); float fDrawDistance = 100.0f; if ( fDistToCamSqr < SQR(fDrawDistance*0.5f) ) { if ( (CEntity*)pObject == FindPlayerPed() || TheCamera.IsSphereVisible(pos, 2.0f) ) { float fDistToCam = Sqrt(fDistToCamSqr); float fMult = 1.0f - (4.0f / fDrawDistance) * (fDistToCam - (fDrawDistance*(1.0f/4.0f))); int32 nColorStrength; if ( fDistToCam >= (fDrawDistance*(1.0f/4.0f)) ) nColorStrength = (int32)(CTimeCycle::GetShadowStrength() * fMult); else nColorStrength = CTimeCycle::GetShadowStrength(); int32 color = int32(nColorStrength * 0.8f); pos.x += fDisplacementX; pos.y += fDisplacementY; RwTexture *texture = shadow->GetShadowRwTexture(); ASSERT(texture); RwRGBA bordercolor = {0, 0, 0, 0}; shadow->DrawBorderAroundTexture(bordercolor); pos.x -= fDisplacementX; pos.y -= fDisplacementY; float angleY = 360.0f - RADTODEG((CClock::ms_nGameClockMinutes +60*CClock::ms_nGameClockHours+CClock::ms_nGameClockSeconds/60)*(HALFPI/360.0f)); RwFrame *frame = shadow->SetLightProperties(angleY, -85.0f, true); ASSERT(frame); CVector at(RwFrameGetMatrix(frame)->at); at.Normalise(); CShadows::CalcPedShadowValues(at, &fFrontX, &fFrontY, &fSideX, &fSideY, &fDisplacementX, &fDisplacementY); pos.x -= 2.5f * fDisplacementX; pos.y -= 2.5f * fDisplacementY; CShadows::StoreShadowToBeRendered(SHADOWTYPE_INVCOLOR, texture, &pos, fFrontX * 1.5f, fFrontY * 1.5f, fSideX * 1.5f, fSideY * 1.5f, color, color, color, color, 4.0f, false, 1.0f, shadow, false); } } } #endif void CShadows::StoreShadowForPed(CPed *pPed, float fDisplacementX, float fDisplacementY, float fFrontX, float fFrontY, float fSideX, float fSideY) { ASSERT(pPed != nil); if ( pPed->bIsVisible ) { if ( !(pPed->bInVehicle && pPed->m_nPedState != PED_DRAG_FROM_CAR && pPed->m_nPedState != PED_EXIT_CAR) ) { if ( CTimeCycle::GetShadowStrength() != 0 ) { #ifdef USE_CUTSCENE_SHADOW_FOR_PED CCutsceneShadow *pShadow = pPed->m_pRTShadow; if (pShadow) { if (pShadow->IsInitialized()) pShadow->UpdateForCutscene(); ::StoreShadowForCutscenePedObject(pPed, fDisplacementX, fDisplacementY, fFrontX, fFrontY, fSideX, fSideY); } return; #endif StoreShadowForPedObject(pPed, fDisplacementX, fDisplacementY, fFrontX, fFrontY, fSideX, fSideY); } } } } void CShadows::StoreShadowForPedObject(CEntity *pPedObject, float fDisplacementX, float fDisplacementY, float fFrontX, float fFrontY, float fSideX, float fSideY) { ASSERT(pPedObject != nil); CVector PedPos = pPedObject->GetPosition(); float fDistToCamSqr = (PedPos - TheCamera.GetPosition()).MagnitudeSqr2D(); float fDrawDistance = 26.0f; if ( fDistToCamSqr < SQR(fDrawDistance*0.5f) ) { if ( pPedObject == FindPlayerPed() || TheCamera.IsSphereVisible(PedPos, 2.0f) != false ) { float fDistToCam = Sqrt(fDistToCamSqr); //fDistToCam == 0 -> 2 //fDistToCam == fDrawDistance -> -2 float fMult = 1.0f - (4.0f / fDrawDistance) * (fDistToCam - (fDrawDistance*(1.0f/4.0f))); // BUG ? negative int32 nColorStrength; if ( fDistToCam >= (fDrawDistance*(1.0f/4.0f)) ) // BUG ? negative nColorStrength = (int32)(CTimeCycle::GetShadowStrength() * fMult); else nColorStrength = CTimeCycle::GetShadowStrength(); PedPos.x += fDisplacementX; PedPos.y += fDisplacementY; StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowPedTex, &PedPos, fFrontX, fFrontY, fSideX, fSideY, nColorStrength, nColorStrength, nColorStrength, nColorStrength, 4.0f, false, 1.0f, nil, pPedObject == FindPlayerPed()); } } } void CShadows::StoreShadowForCutscenePedObject(CCutsceneObject *pObject, float fDisplacementX, float fDisplacementY, float fFrontX, float fFrontY, float fSideX, float fSideY) { #ifdef DISABLE_CUTSCENE_SHADOWS return; #endif ASSERT(pObject != nil); CCutsceneShadow *shadow = pObject->m_pShadow; if ( shadow == nil ) return; if ( !shadow->IsInitialized() ) return; CVector pos = pObject->GetPosition(); float fDistToCamSqr = (pos - TheCamera.GetPosition()).MagnitudeSqr2D(); float fDrawDistance = 100.0f; if ( fDistToCamSqr < SQR(fDrawDistance*0.5f) ) { if ( (CEntity*)pObject == FindPlayerPed() || TheCamera.IsSphereVisible(pos, 2.0f) ) { float fDistToCam = Sqrt(fDistToCamSqr); float fMult = 1.0f - (4.0f / fDrawDistance) * (fDistToCam - (fDrawDistance*(1.0f/4.0f))); int32 nColorStrength; if ( fDistToCam >= (fDrawDistance*(1.0f/4.0f)) ) nColorStrength = (int32)(CTimeCycle::GetShadowStrength() * fMult); else nColorStrength = CTimeCycle::GetShadowStrength(); int32 color = int32(nColorStrength * 0.8f); pos.x += fDisplacementX; pos.y += fDisplacementY; RwTexture *texture = shadow->GetShadowRwTexture(); ASSERT(texture); RwRGBA bordercolor = {0, 0, 0, 0}; shadow->DrawBorderAroundTexture(bordercolor); pos.x -= fDisplacementX; pos.y -= fDisplacementY; float angleY = 360.0f - RADTODEG((CClock::ms_nGameClockMinutes+60* CClock::ms_nGameClockHours+CClock::ms_nGameClockSeconds/60)*(HALFPI/360.0f)); RwFrame *frame = shadow->SetLightProperties(angleY, -85.0f, true); ASSERT(frame); CVector at(RwFrameGetMatrix(frame)->at); at.Normalise(); CalcPedShadowValues(at, &fFrontX, &fFrontY, &fSideX, &fSideY, &fDisplacementX, &fDisplacementY); pos.x -= 2.5f * fDisplacementX; pos.y -= 2.5f * fDisplacementY; StoreShadowToBeRendered(SHADOWTYPE_INVCOLOR, texture, &pos, fFrontX * 1.5f, fFrontY * 1.5f, fSideX * 1.5f, fSideY * 1.5f, color, color, color, color, 4.0f, false, 1.0f, shadow, false); } } } void CShadows::StoreShadowForTree(CEntity *pTree) { ASSERT(pTree != nil); } void CShadows::StoreShadowForPole(CEntity *pPole, float fOffsetX, float fOffsetY, float fOffsetZ, float fPoleHeight, float fPoleWidth, uint32 nID) { ASSERT(pPole != nil); if ( CTimeCycle::GetShadowStrength() != 0 ) { if ( pPole->GetUp().z < 0.5f ) return; CVector PolePos = pPole->GetPosition(); PolePos.x += fOffsetX * pPole->GetRight().x + fOffsetY * pPole->GetForward().x; PolePos.y += fOffsetX * pPole->GetRight().y + fOffsetY * pPole->GetForward().y; PolePos.z += fOffsetZ; PolePos.x += -CTimeCycle::GetSunDirection().x * (fPoleHeight / 2); PolePos.y += -CTimeCycle::GetSunDirection().y * (fPoleHeight / 2); StoreStaticShadow((uintptr)pPole + nID + _TODOCONST(51), SHADOWTYPE_DARK, gpPostShadowTex, &PolePos, -CTimeCycle::GetSunDirection().x * (fPoleHeight / 2), -CTimeCycle::GetSunDirection().y * (fPoleHeight / 2), CTimeCycle::GetShadowSideX() * fPoleWidth, CTimeCycle::GetShadowSideY() * fPoleWidth, 2 * (int32)((pPole->GetUp().z - 0.5f) * CTimeCycle::GetShadowStrength() * 2.0f) / 3, 0, 0, 0, 15.0f, 1.0f, 40.0f, false, 0.0f); } } void CShadows::SetRenderModeForShadowType(uint8 ShadowType) { switch ( ShadowType ) { case SHADOWTYPE_DARK: { RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); break; } case SHADOWTYPE_ADDITIVE: { RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDONE); break; } case SHADOWTYPE_INVCOLOR: { RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDZERO); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCCOLOR); break; } } } void CShadows::RenderStoredShadows(void) { RenderBuffer::ClearRenderBuffer(); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void *)rwTEXTUREADDRESSCLAMP); for ( int32 i = 0; i < ShadowsStoredToBeRendered; i++ ) asShadowsStored[i].m_nFlags.bRendered = false; for ( int32 i = 0; i < ShadowsStoredToBeRendered; i++ ) { if ( !asShadowsStored[i].m_nFlags.bRendered ) { SetRenderModeForShadowType(asShadowsStored[i].m_ShadowType); ASSERT(asShadowsStored[i].m_pTexture != nil); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(asShadowsStored[i].m_pTexture)); for ( int32 j = i; j < ShadowsStoredToBeRendered; j++ ) { if ( asShadowsStored[i].m_ShadowType == asShadowsStored[j].m_ShadowType && asShadowsStored[i].m_pTexture == asShadowsStored[j].m_pTexture ) { float fWidth = Abs(asShadowsStored[j].m_vecFront.x) + Abs(asShadowsStored[j].m_vecSide.x); float fHeight = Abs(asShadowsStored[j].m_vecFront.y) + Abs(asShadowsStored[j].m_vecSide.y); CVector shadowPos = asShadowsStored[j].m_vecPos; float fStartX = shadowPos.x - fWidth; float fEndX = shadowPos.x + fWidth; float fStartY = shadowPos.y - fHeight; float fEndY = shadowPos.y + fHeight; int32 nStartX = Max(CWorld::GetSectorIndexX(fStartX), 0); int32 nStartY = Max(CWorld::GetSectorIndexY(fStartY), 0); int32 nEndX = Min(CWorld::GetSectorIndexX(fEndX), NUMSECTORS_X-1); int32 nEndY = Min(CWorld::GetSectorIndexY(fEndY), NUMSECTORS_Y-1); CWorld::AdvanceCurrentScanCode(); for ( int32 y = nStartY; y <= nEndY; y++ ) { for ( int32 x = nStartX; x <= nEndX; x++ ) { CSector *pCurSector = CWorld::GetSector(x, y); ASSERT(pCurSector != nil); if ( asShadowsStored[j].m_pCutsceneShadow ) { CastCutsceneShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS], fStartX, fStartY, fEndX, fEndY, &shadowPos, asShadowsStored[j].m_vecFront.x, asShadowsStored[j].m_vecFront.y, asShadowsStored[j].m_vecSide.x, asShadowsStored[j].m_vecSide.y, asShadowsStored[j].m_nIntensity, asShadowsStored[j].m_nRed, asShadowsStored[j].m_nGreen, asShadowsStored[j].m_nBlue, asShadowsStored[j].m_fZDistance, asShadowsStored[j].m_fScale, nil, asShadowsStored[j].m_pCutsceneShadow); CastCutsceneShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], fStartX, fStartY, fEndX, fEndY, &shadowPos, asShadowsStored[j].m_vecFront.x, asShadowsStored[j].m_vecFront.y, asShadowsStored[j].m_vecSide.x, asShadowsStored[j].m_vecSide.y, asShadowsStored[j].m_nIntensity, asShadowsStored[j].m_nRed, asShadowsStored[j].m_nGreen, asShadowsStored[j].m_nBlue, asShadowsStored[j].m_fZDistance, asShadowsStored[j].m_fScale, nil, asShadowsStored[j].m_pCutsceneShadow); } else if ( asShadowsStored[j].m_nFlags.bDrawOnBuildings ) { CastPlayerShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS], fStartX, fStartY, fEndX, fEndY, &shadowPos, asShadowsStored[j].m_vecFront.x, asShadowsStored[j].m_vecFront.y, asShadowsStored[j].m_vecSide.x, asShadowsStored[j].m_vecSide.y, asShadowsStored[j].m_nIntensity, asShadowsStored[j].m_nRed, asShadowsStored[j].m_nGreen, asShadowsStored[j].m_nBlue, asShadowsStored[j].m_fZDistance, asShadowsStored[j].m_fScale, nil); CastPlayerShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], fStartX, fStartY, fEndX, fEndY, &shadowPos, asShadowsStored[j].m_vecFront.x, asShadowsStored[j].m_vecFront.y, asShadowsStored[j].m_vecSide.x, asShadowsStored[j].m_vecSide.y, asShadowsStored[j].m_nIntensity, asShadowsStored[j].m_nRed, asShadowsStored[j].m_nGreen, asShadowsStored[j].m_nBlue, asShadowsStored[j].m_fZDistance, asShadowsStored[j].m_fScale, nil); } else { CastShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS], fStartX, fStartY, fEndX, fEndY, &shadowPos, asShadowsStored[j].m_vecFront.x, asShadowsStored[j].m_vecFront.y, asShadowsStored[j].m_vecSide.x, asShadowsStored[j].m_vecSide.y, asShadowsStored[j].m_nIntensity, asShadowsStored[j].m_nRed, asShadowsStored[j].m_nGreen, asShadowsStored[j].m_nBlue, asShadowsStored[j].m_fZDistance, asShadowsStored[j].m_fScale, nil); CastShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], fStartX, fStartY, fEndX, fEndY, &shadowPos, asShadowsStored[j].m_vecFront.x, asShadowsStored[j].m_vecFront.y, asShadowsStored[j].m_vecSide.x, asShadowsStored[j].m_vecSide.y, asShadowsStored[j].m_nIntensity, asShadowsStored[j].m_nRed, asShadowsStored[j].m_nGreen, asShadowsStored[j].m_nBlue, asShadowsStored[j].m_fZDistance, asShadowsStored[j].m_fScale, nil); } } } asShadowsStored[j].m_nFlags.bRendered = true; } } RenderBuffer::RenderStuffInBuffer(); } } RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void *)rwTEXTUREADDRESSWRAP); ShadowsStoredToBeRendered = 0; } void CShadows::RenderStaticShadows(void) { RenderBuffer::ClearRenderBuffer(); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void *)FALSE); SetAlphaTest(0); for ( int32 i = 0; i < MAX_STATICSHADOWS; i++ ) aStaticShadows[i].m_bRendered = false; for ( int32 i = 0; i < MAX_STATICSHADOWS; i++ ) { if ( aStaticShadows[i].m_pPolyBunch && !aStaticShadows[i].m_bRendered ) { SetRenderModeForShadowType(aStaticShadows[i].m_nType); RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(aStaticShadows[i].m_pTexture)); // optimization trick, render all shadows with same renderstate and texture for ( int32 j = i; j < MAX_STATICSHADOWS; j++ ) { if ( aStaticShadows[j].m_pPolyBunch != nil && aStaticShadows[i].m_nType == aStaticShadows[j].m_nType && aStaticShadows[i].m_pTexture == aStaticShadows[j].m_pTexture ) { for ( CPolyBunch *bunch = aStaticShadows[j].m_pPolyBunch; bunch != nil; bunch = bunch->m_pNext ) { RwImVertexIndex *pIndexes; RwIm3DVertex *pVerts; RenderBuffer::StartStoring(3 * (bunch->m_nNumVerts - 2), bunch->m_nNumVerts, &pIndexes, &pVerts); ASSERT(pIndexes != nil); ASSERT(pVerts != nil); for ( int32 k = 0; k < bunch->m_nNumVerts; k++ ) { RwIm3DVertexSetRGBA(&pVerts[k], aStaticShadows[j].m_nRed, aStaticShadows[j].m_nGreen, aStaticShadows[j].m_nBlue, (int32)(aStaticShadows[j].m_nIntensity * (1.0f - CWeather::Foggyness * 0.5f))); RwIm3DVertexSetU (&pVerts[k], bunch->m_aU[k] / 200.0f); RwIm3DVertexSetV (&pVerts[k], bunch->m_aV[k] / 200.0f); RwIm3DVertexSetPos(&pVerts[k], bunch->m_aVerts[k].x, bunch->m_aVerts[k].y, bunch->m_aVerts[k].z + 0.03f); } for ( int32 k = 0; k < 3 * (bunch->m_nNumVerts - 2); k++ ) pIndexes[k] = ShadowIndexList[k]; RenderBuffer::StopStoring(); } aStaticShadows[j].m_bRendered = true; } } RenderBuffer::RenderStuffInBuffer(); } } RestoreAlphaTest(); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE); } void CShadows::GeneratePolysForStaticShadow(int16 nStaticShadowID) { float fWidth = Abs(aStaticShadows[nStaticShadowID].m_vecFront.x) + Abs(aStaticShadows[nStaticShadowID].m_vecSide.x); float fHeight = Abs(aStaticShadows[nStaticShadowID].m_vecFront.y) + Abs(aStaticShadows[nStaticShadowID].m_vecSide.y); CVector shadowPos = aStaticShadows[nStaticShadowID].m_vecPosn; float fStartX = shadowPos.x - fWidth; float fEndX = shadowPos.x + fWidth; float fStartY = shadowPos.y - fHeight; float fEndY = shadowPos.y + fHeight; int32 nStartX = Max(CWorld::GetSectorIndexX(fStartX), 0); int32 nStartY = Max(CWorld::GetSectorIndexY(fStartY), 0); int32 nEndX = Min(CWorld::GetSectorIndexX(fEndX), NUMSECTORS_X-1); int32 nEndY = Min(CWorld::GetSectorIndexY(fEndY), NUMSECTORS_Y-1); CWorld::AdvanceCurrentScanCode(); for ( int32 y = nStartY; y <= nEndY; y++ ) { for ( int32 x = nStartX; x <= nEndX; x++ ) { CSector *pCurSector = CWorld::GetSector(x, y); ASSERT(pCurSector != nil); CastShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS], fStartX, fStartY, fEndX, fEndY, &shadowPos, aStaticShadows[nStaticShadowID].m_vecFront.x, aStaticShadows[nStaticShadowID].m_vecFront.y, aStaticShadows[nStaticShadowID].m_vecSide.x, aStaticShadows[nStaticShadowID].m_vecSide.y, 0, 0, 0, 0, aStaticShadows[nStaticShadowID].m_fZDistance, aStaticShadows[nStaticShadowID].m_fScale, &aStaticShadows[nStaticShadowID].m_pPolyBunch); CastShadowSectorList(pCurSector->m_lists[ENTITYLIST_BUILDINGS_OVERLAP], fStartX, fStartY, fEndX, fEndY, &shadowPos, aStaticShadows[nStaticShadowID].m_vecFront.x, aStaticShadows[nStaticShadowID].m_vecFront.y, aStaticShadows[nStaticShadowID].m_vecSide.x, aStaticShadows[nStaticShadowID].m_vecSide.y, 0, 0, 0, 0, aStaticShadows[nStaticShadowID].m_fZDistance, aStaticShadows[nStaticShadowID].m_fScale, &aStaticShadows[nStaticShadowID].m_pPolyBunch); } } } void CShadows::CastShadowSectorList(CPtrList &PtrList, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch) { ASSERT(pPosn != nil); CPtrNode *pNode = PtrList.first; CRect Bound; while ( pNode != nil ) { CEntity *pEntity = (CEntity *)pNode->item; uint16 nScanCode = pEntity->m_scanCode; pNode = pNode->next; ASSERT( pEntity != nil ); if ( nScanCode != CWorld::GetCurrentScanCode() ) { pEntity->m_scanCode = CWorld::GetCurrentScanCode(); if ( pEntity->bUsesCollision && !pEntity->m_flagE2 ) { if ( IsAreaVisible(pEntity->m_area) ) { Bound = pEntity->GetBoundRect(); if ( fStartX < Bound.right && fEndX > Bound.left && fStartY < Bound.bottom && fEndY > Bound.top ) { if ( pPosn->z - fZDistance < pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.max.z && pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.min.z < pPosn->z ) { CastShadowEntityXY(pEntity, fStartX, fStartY, fEndX, fEndY, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, fZDistance, fScale, ppPolyBunch); } } } } } } } void CShadows::CastPlayerShadowSectorList(CPtrList &PtrList, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch) { ASSERT(pPosn != nil); CPtrNode *pNode = PtrList.first; CRect Bound; while ( pNode != nil ) { CEntity *pEntity = (CEntity *)pNode->item; uint16 nScanCode = pEntity->m_scanCode; pNode = pNode->next; ASSERT( pEntity != nil ); if ( nScanCode != CWorld::GetCurrentScanCode() ) { pEntity->m_scanCode = CWorld::GetCurrentScanCode(); if ( pEntity->bUsesCollision ) { if ( IsAreaVisible(pEntity->m_area) ) { Bound = pEntity->GetBoundRect(); if ( fStartX < Bound.right && fEndX > Bound.left && fStartY < Bound.bottom && fEndY > Bound.top ) { if ( pPosn->z - fZDistance < pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.max.z && pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.min.z < pPosn->z ) { CastShadowEntityXY(pEntity, fStartX, fStartY, fEndX, fEndY, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, fZDistance, fScale, ppPolyBunch); } } } } } } } void CShadows::CastCutsceneShadowSectorList(CPtrList &PtrList, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch, CCutsceneShadow *pShadow) { ASSERT(pPosn != nil); ASSERT(pShadow != nil); CPtrNode *pNode = PtrList.first; CRect Bound; while ( pNode != nil ) { CEntity *pEntity = (CEntity *)pNode->item; uint16 nScanCode = pEntity->m_scanCode; pNode = pNode->next; ASSERT( pEntity != nil ); if ( nScanCode != CWorld::GetCurrentScanCode() ) { pEntity->m_scanCode = CWorld::GetCurrentScanCode(); if ( pEntity->bUsesCollision ) { if ( IsAreaVisible(pEntity->m_area) ) { Bound = pEntity->GetBoundRect(); if ( fStartX < Bound.right && fEndX > Bound.left && fStartY < Bound.bottom && fEndY > Bound.top ) { if ( pPosn->z - fZDistance < pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.max.z && pEntity->GetPosition().z + pEntity->GetColModel()->boundingBox.min.z < pPosn->z ) { CastShadowEntityXYZ(pEntity, pPosn, fFrontX, fFrontY, fSideX, fSideY, nIntensity, nRed, nGreen, nBlue, fZDistance, fScale, ppPolyBunch, pShadow); } } } } } } } void CShadows::CastShadowEntityXY(CEntity *pEntity, float fStartX, float fStartY, float fEndX, float fEndY, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch) { ASSERT(pEntity != nil); ASSERT(pPosn != nil); static CVector List [20]; static CVector Texture[20]; static CVector Points [4]; CColModel *pCol = pEntity->GetColModel(); ASSERT(pCol != nil); #ifndef MASTER if ( gbPrintShite ) printf("MI:%d Triangles:%d Coors:%f %f BBoxXY:%f %f\n", pEntity->GetModelIndex(), pCol->numTriangles, pEntity->GetPosition().x, pEntity->GetPosition().y, pCol->boundingBox.GetSize().x, pCol->boundingBox.GetSize().y); #endif CCollision::CalculateTrianglePlanes(pCol); float fFrontRight = DotProduct2D(CVector2D(fFrontX, fFrontY), pEntity->GetRight()); float fFrontForward = DotProduct2D(CVector2D(fFrontX, fFrontY), pEntity->GetForward()); float fSideRight = DotProduct2D(CVector2D(fSideX, fSideY), pEntity->GetRight()); float fSideForward = DotProduct2D(CVector2D(fSideX, fSideY), pEntity->GetForward()); float fLengthRight = DotProduct2D(*pPosn - pEntity->GetPosition(), pEntity->GetRight()); float fLengthForward = DotProduct2D(*pPosn - pEntity->GetPosition(), pEntity->GetForward()); Points[0].x = (fLengthRight + fFrontRight ) - fSideRight; Points[0].y = (fLengthForward + fFrontForward) - fSideForward; Points[1].x = fSideRight + (fLengthRight + fFrontRight); Points[1].y = fSideForward + (fLengthForward + fFrontForward); Points[2].x = fSideRight + (fLengthRight - fFrontRight); Points[2].y = fSideForward + (fLengthForward - fFrontForward); Points[3].x = (fLengthRight - fFrontRight) - fSideRight; Points[3].y = (fLengthForward - fFrontForward) - fSideForward; float MinX = Min(Min(Points[0].x, Points[1].x), Min(Points[2].x, Points[3].x)); float MaxX = Max(Max(Points[0].x, Points[1].x), Max(Points[2].x, Points[3].x)); float MinY = Min(Min(Points[0].y, Points[1].y), Min(Points[2].y, Points[3].y)); float MaxY = Max(Max(Points[0].y, Points[1].y), Max(Points[2].y, Points[3].y)); float MaxZ = pPosn->z - pEntity->GetPosition().z; float MinZ = MaxZ - fZDistance; for ( int32 i = 0; i < pCol->numTriangles; i++ ) { CColTrianglePlane *pColTriPlanes = pCol->trianglePlanes; ASSERT(pColTriPlanes != nil); CVector normal; pColTriPlanes[i].GetNormal(normal); if ( Abs(normal.z) > 0.1f ) { CColTriangle *pColTri = pCol->triangles; ASSERT(pColTri != nil); CVector PointA, PointB, PointC; pCol->GetTrianglePoint(PointA, pColTri[i].a); pCol->GetTrianglePoint(PointB, pColTri[i].b); pCol->GetTrianglePoint(PointC, pColTri[i].c); if ( (PointA.x > MinX || PointB.x > MinX || PointC.x > MinX) && (PointA.x < MaxX || PointB.x < MaxX || PointC.x < MaxX) && (PointA.y > MinY || PointB.y > MinY || PointC.y > MinY) && (PointA.y < MaxY || PointB.y < MaxY || PointC.y < MaxY) && (PointA.z < MaxZ || PointB.z < MaxZ || PointC.z < MaxZ) && (PointA.z > MinZ || PointB.z > MinZ || PointC.z > MinZ) ) { List[0].x = Points[0].x; List[0].y = Points[0].y; List[1].x = Points[1].x; List[1].y = Points[1].y; List[2].x = Points[2].x; List[2].y = Points[2].y; List[3].x = Points[3].x; List[3].y = Points[3].y; Texture[0].x = 0.0f; Texture[0].y = 0.0f; Texture[1].x = 1.0f; Texture[1].y = 0.0f; Texture[2].x = 1.0f; Texture[2].y = 1.0f; Texture[3].x = 0.0f; Texture[3].y = 1.0f; CVector2D start; CVector2D dist; int32 numVerts1 = 0; int16 vertType1 = 0; { for ( int32 j = 0; j < 4; j++ ) { start = PointA; dist = PointB - PointA; int32 in = j; float cp = CrossProduct2D(CVector2D(List[in]) - start, dist); if ( cp > 0.0f ) { switch ( vertType1 ) { case 0: { int32 out = numVerts1++ + 10; Texture[out].x = Texture[in].x; Texture[out].y = Texture[in].y; List[out].x = List[in].x; List[out].y = List[in].y; break; } case 1: { int32 out = numVerts1++ + 10; Texture[out].x = Texture[in].x; Texture[out].y = Texture[in].y; List[out].x = List[in].x; List[out].y = List[in].y; break; } case 2: { float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); float Compl = 1.0f - Scale; int32 out1 = numVerts1++ + 10; int32 out2 = numVerts1++ + 10; Texture[out1].x = Compl*Texture[in-1].x + Scale*Texture[in].x; Texture[out1].y = Compl*Texture[in-1].y + Scale*Texture[in].y; List[out1].x = Compl*List[in-1].x + Scale*List[in].x; List[out1].y = Compl*List[in-1].y + Scale*List[in].y; Texture[out2].x = Texture[in].x; Texture[out2].y = Texture[in].y; List[out2].x = List[in].x; List[out2].y = List[in].y; break; } } vertType1 = 1; } else { switch ( vertType1 ) { case 1: { float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); float Compl = 1.0f - Scale; int32 out = numVerts1++ + 10; Texture[out].x = Compl*Texture[in-1].x + Scale*Texture[in].x; Texture[out].y = Compl*Texture[in-1].y + Scale*Texture[in].y; List[out].x = Compl*List[in-1].x + Scale*List[in].x; List[out].y = Compl*List[in-1].y + Scale*List[in].y; break; } } vertType1 = 2; } } float cp1 = CrossProduct2D(CVector2D(List[0]) - start, dist); if ( cp1 > 0.0f && vertType1 == 2 || cp1 <= 0.0f && vertType1 == 1 ) { float cp2 = CrossProduct2D(CVector2D(List[3]) - start, dist); float Scale = Abs(cp2) / (Abs(cp2) + Abs(cp1)); float Compl = 1.0f - Scale; int32 out = numVerts1++ + 10; Texture[out].x = Compl*Texture[3].x + Scale*Texture[0].x; Texture[out].y = Compl*Texture[3].y + Scale*Texture[0].y; List[out].x = Compl*List[3].x + Scale*List[0].x; List[out].y = Compl*List[3].y + Scale*List[0].y; } } int32 numVerts2 = 0; int16 vertType2 = 0; { for ( int32 j = 0; j < numVerts1; j++ ) { start = PointB; dist = PointC - PointB; int32 in = j + 10; float cp = CrossProduct2D(CVector2D(List[in]) - start, dist); if ( cp > 0.0f ) { switch ( vertType2 ) { case 0: { int32 out = numVerts2++; Texture[out].x = Texture[in].x; Texture[out].y = Texture[in].y; List[out].x = List[in].x; List[out].y = List[in].y; break; } case 1: { int32 out = numVerts2++; Texture[out].x = Texture[in].x; Texture[out].y = Texture[in].y; List[out].x = List[in].x; List[out].y = List[in].y; break; } case 2: { float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); float Compl = 1.0f - Scale; int32 out1 = numVerts2++; int32 out2 = numVerts2++; Texture[out1].x = Compl*Texture[in-1].x + Scale*Texture[in].x; Texture[out1].y = Compl*Texture[in-1].y + Scale*Texture[in].y; List[out1].x = Compl*List[in-1].x + Scale*List[in].x; List[out1].y = Compl*List[in-1].y + Scale*List[in].y; Texture[out2].x = Texture[in].x; Texture[out2].y = Texture[in].y; List[out2].x = List[in].x; List[out2].y = List[in].y; break; } } vertType2 = 1; } else { switch ( vertType2 ) { case 1: { float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); float Compl = 1.0f - Scale; int32 out = numVerts2++; Texture[out].x = Compl*Texture[in-1].x + Scale*Texture[in].x; Texture[out].y = Compl*Texture[in-1].y + Scale*Texture[in].y; List[out].x = Compl*List[in-1].x + Scale*List[in].x; List[out].y = Compl*List[in-1].y + Scale*List[in].y; break; } } vertType2 = 2; } } float cp1 = CrossProduct2D(CVector2D(List[10]) - start, dist); if ( cp1 > 0.0f && vertType2 == 2 || cp1 <= 0.0f && vertType2 == 1 ) { int32 in = numVerts1 + 10; float cp2 = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(cp2) / (Abs(cp2) + Abs(cp1)); float Compl = 1.0f - Scale; int32 out = numVerts2++; Texture[out].x = Compl*Texture[in-1].x + Scale*Texture[10].x; Texture[out].y = Compl*Texture[in-1].y + Scale*Texture[10].y; List[out].x = Compl*List[in-1].x + Scale*List[10].x; List[out].y = Compl*List[in-1].y + Scale*List[10].y; } } int32 numVerts3 = 0; int16 vertType3 = 0; { for ( int32 j = 0; j < numVerts2; j++ ) { start = PointC; dist = PointA - PointC; int32 in = j; float cp = CrossProduct2D(CVector2D(List[in]) - start, dist); if ( cp > 0.0f ) { switch ( vertType3 ) { case 0: { int32 out = numVerts3++ + 10; Texture[out].x = Texture[in].x; Texture[out].y = Texture[in].y; List[out].x = List[in].x; List[out].y = List[in].y; break; } case 1: { int32 out = numVerts3++ + 10; Texture[out].x = Texture[in].x; Texture[out].y = Texture[in].y; List[out].x = List[in].x; List[out].y = List[in].y; break; } case 2: { float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); float Compl = 1.0f - Scale; int32 out1 = numVerts3++ + 10; int32 out2 = numVerts3++ + 10; Texture[out1].x = Compl*Texture[in-1].x + Scale*Texture[in].x; Texture[out1].y = Compl*Texture[in-1].y + Scale*Texture[in].y; List[out1].x = Compl*List[in-1].x + Scale*List[in].x; List[out1].y = Compl*List[in-1].y + Scale*List[in].y; Texture[out2].x = Texture[in].x; Texture[out2].y = Texture[in].y; List[out2].x = List[in].x; List[out2].y = List[in].y; break; } } vertType3 = 1; } else { switch ( vertType3 ) { case 1: { float prevcp = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(prevcp) / (Abs(prevcp) + Abs(cp)); float Compl = 1.0f - Scale; int32 out = numVerts3++ + 10; Texture[out].x = Compl*Texture[in-1].x + Scale*Texture[in].x; Texture[out].y = Compl*Texture[in-1].y + Scale*Texture[in].y; List[out].x = Compl*List[in-1].x + Scale*List[in].x; List[out].y = Compl*List[in-1].y + Scale*List[in].y; break; } } vertType3 = 2; } } float cp1 = CrossProduct2D(CVector2D(List[0]) - start, dist); if ( cp1 > 0.0f && vertType3 == 2 || cp1 <= 0.0f && vertType3 == 1 ) { int32 in = numVerts2; float cp2 = CrossProduct2D(CVector2D(List[in-1]) - start, dist); float Scale = Abs(cp2) / (Abs(cp2) + Abs(cp1)); float Compl = 1.0f - Scale; int32 out = numVerts3++ + 10; Texture[out].x = Compl*Texture[in-1].x + Scale*Texture[0].x; Texture[out].y = Compl*Texture[in-1].y + Scale*Texture[0].y; List[out].x = Compl*List[in-1].x + Scale*List[0].x; List[out].y = Compl*List[in-1].y + Scale*List[0].y; } } if ( numVerts3 >= 3 ) { CVector norm; pColTriPlanes[i].GetNormal(norm); float dot = DotProduct(norm, PointA); for ( int32 j = 0; j < numVerts3; j++ ) { int32 idx = j + 10; List[idx].z = -(DotProduct2D(norm, List[idx]) - dot) / norm.z; } for ( int32 j = 0; j < numVerts3; j++ ) { int32 idx = j + 10; CVector p = List[idx]; List[idx].x = p.y * pEntity->GetForward().x + p.x * pEntity->GetRight().x + pEntity->GetPosition().x; List[idx].y = p.y * pEntity->GetForward().y + p.x * pEntity->GetRight().y + pEntity->GetPosition().y; List[idx].z = p.z + pEntity->GetPosition().z; } if ( ppPolyBunch != nil ) { if ( pEmptyBunchList != nil ) { CPolyBunch *pBunch = pEmptyBunchList; ASSERT(pBunch != nil); pEmptyBunchList = pEmptyBunchList->m_pNext; pBunch->m_pNext = *ppPolyBunch; *ppPolyBunch = pBunch; pBunch->m_nNumVerts = numVerts3; for ( int32 j = 0; j < numVerts3; j++ ) { int32 in = j + 10; pBunch->m_aVerts[j] = List[in]; pBunch->m_aU[j] = (int32)(Texture[in].x * 200.0f); pBunch->m_aV[j] = (int32)(Texture[in].y * 200.0f); } } } else { RwImVertexIndex *pIndexes; RwIm3DVertex *pVerts; RenderBuffer::StartStoring(3 * (numVerts3 - 2), numVerts3, &pIndexes, &pVerts); ASSERT(pIndexes != nil); ASSERT(pVerts != nil); for ( int32 j = 0; j < numVerts3; j++ ) { int32 in = j + 10; RwIm3DVertexSetRGBA(&pVerts[j], nRed, nGreen, nBlue, nIntensity); RwIm3DVertexSetU (&pVerts[j], Texture[in].x*fScale); RwIm3DVertexSetV (&pVerts[j], Texture[in].y*fScale); RwIm3DVertexSetPos (&pVerts[j], List[in].x, List[in].y, List[in].z + 0.03f); } for ( int32 j = 0; j < 3*(numVerts3 - 2); j++ ) pIndexes[j] = ShadowIndexList[j]; RenderBuffer::StopStoring(); } } } } } } typedef struct _ProjectionParam { RwV3d at; /* Camera at vector */ RwMatrix invMatrix; /* Transforms to shadow camera space */ RwUInt8 shadowValue; /* Shadow opacity value */ RwBool fade; /* Shadow fades with distance */ RwUInt32 numIm3DBatch; /* Number of buffer flushes */ RwMatrix entityMatrix; } ProjectionParam; RwV3d *ShadowRenderTriangleCB(RwV3d *points, RwV3d *normal, ProjectionParam *param) { RwV3d vIn[3]; RwV3d vShad[3]; RwV3dTransformPoints(&vIn[0], points, 3, ¶m->entityMatrix); /* * Reject backfacing triangles * This reject the triangles parallel to the light as well */ if (RwV3dDotProduct(normal, ¶m->at) > 0.0f) { return points; } RwV3dTransformPoints(&vShad[0], &vIn[0], 3, ¶m->invMatrix); /* * Reject triangles behind the camera (z test). Note that any world * triangles lying in front of the camera but before the object may * have a shadow applied. To minimize such artefacts, this test could * be modified to use a specific value rather than 0.0f, perhaps * to reject triangles behind the center plane of the object. * * Reject triangles that lie entirely outside the shadow texture range * (x,y test). */ if (((vShad[0].z < 0.0f) && (vShad[1].z < 0.0f) && (vShad[2].z < 0.0f)) || ((vShad[0].x < 0.0f) && (vShad[1].x < 0.0f) && (vShad[2].x < 0.0f)) || ((vShad[0].x > 1.0f) && (vShad[1].x > 1.0f) && (vShad[2].x > 1.0f)) || ((vShad[0].y < 0.0f) && (vShad[1].y < 0.0f) && (vShad[2].y < 0.0f)) || ((vShad[0].y > 1.0f) && (vShad[1].y > 1.0f) && (vShad[2].y > 1.0f))) { return points; } RwIm3DVertex *imv = nil; RwImVertexIndex *imi = nil; RenderBuffer::StartStoring(3, 3, &imi, &imv); /* * Set the immediate mode vertices for this triangle */ RwIm3DVertexSetPos(imv, vIn[0].x, vIn[0].y, vIn[0].z); RwIm3DVertexSetPos(imv + 1, vIn[1].x, vIn[1].y, vIn[1].z); RwIm3DVertexSetPos(imv + 2, vIn[2].x, vIn[2].y, vIn[2].z); RwIm3DVertexSetU(imv, vShad[0].x); RwIm3DVertexSetU(imv + 1, vShad[1].x); RwIm3DVertexSetU(imv + 2, vShad[2].x); RwIm3DVertexSetV(imv, vShad[0].y); RwIm3DVertexSetV(imv + 1, vShad[1].y); RwIm3DVertexSetV(imv + 2, vShad[2].y); /* * Do we fade out the shadow with distance? */ if (param->fade) { RwReal fadeVal; RwUInt8 val; fadeVal = 1.0f - vShad[0].z * vShad[0].z; val = (fadeVal < 0.0f) ? 0 : (RwUInt8) (fadeVal * param->shadowValue); RwIm3DVertexSetRGBA(imv, val, val, val, val); fadeVal = 1.0f - vShad[1].z * vShad[1].z; val = (fadeVal < 0.0f) ? 0 : (RwUInt8) (fadeVal * param->shadowValue); RwIm3DVertexSetRGBA(imv + 1, val, val, val, val); fadeVal = 1.0f - vShad[2].z * vShad[2].z; val = (fadeVal < 0.0f) ? 0 : (RwUInt8) (fadeVal * param->shadowValue); RwIm3DVertexSetRGBA(imv + 2, val, val, val, val); } else { RwUInt8 val = param->shadowValue; RwIm3DVertexSetRGBA(imv, val, val, val, val); RwIm3DVertexSetRGBA(imv + 1, val, val, val, val); RwIm3DVertexSetRGBA(imv + 2, val, val, val, val); } /* * Update buffer position */ imi[0] = 0; imi[1] = 1; imi[2] = 2; RenderBuffer::StopStoring(); return points; } void CShadows::CastShadowEntityXYZ(CEntity *pEntity, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity, uint8 nRed, uint8 nGreen, uint8 nBlue, float fZDistance, float fScale, CPolyBunch **ppPolyBunch, CCutsceneShadow *pShadow) { ASSERT(pEntity != nil); ASSERT(pPosn != nil); if ( pShadow ) { ProjectionParam proj; RwV3d scl; RwV3d tr; CShadowCamera *shadow = pShadow->GetShadowCamera(); CColModel *collision = pEntity->GetColModel(); CCollision::CalculateTrianglePlanes(collision); RwMatrix mat; mat = *RwFrameGetMatrix(RwCameraGetFrame(shadow->GetRwCamera())); RwV3d Xaxis = { 1.0f, 0.0f, 0.0f }; RwMatrixRotate(&mat, &Xaxis, -45.0f, rwCOMBINEPRECONCAT); proj.at = mat.at; pEntity->GetMatrix().CopyToRwMatrix(&proj.entityMatrix); RwMatrixInvert(&proj.invMatrix, &mat); RwReal radius = RwCameraGetViewWindow(shadow->GetRwCamera())->x; scl.x = scl.y = -0.5f / (radius*0.9f); scl.z = 1.0f / (radius*0.8f); RwMatrixScale(&proj.invMatrix, &scl, rwCOMBINEPOSTCONCAT); tr.x = 0.5f; tr.y = tr.z = 0.0f; RwMatrixTranslate(&proj.invMatrix, &tr, rwCOMBINEPOSTCONCAT); proj.shadowValue = nIntensity; proj.fade = 0; RwMatrix matrix; pEntity->GetMatrix().CopyToRwMatrix(&matrix); RwMatrix invMatrix; RwMatrixInvert(&invMatrix, &matrix); CVector center(pShadow->GetBaseSphere().center); center += CVector(-fFrontX * 1.1f, -fFrontY * 1.1f, -0.5f); CSphere sphere; sphere.Set(2.0f, center); RwV3d point; RwV3dTransformPoints(&point, ¢er, 1, &invMatrix); CColSphere colSphere; colSphere.Set(2.0f, CVector(point), 0, 0); int i = 0; while ( i < collision->numTriangles ) { CVector p[3]; collision->GetTrianglePoint(p[0], collision->triangles[i].a); collision->GetTrianglePoint(p[1], collision->triangles[i].b); collision->GetTrianglePoint(p[2], collision->triangles[i].c); if ( CCollision::TestSphereTriangle(colSphere, collision->vertices, collision->triangles[i], collision->trianglePlanes[i]) ) { CVector n(collision->trianglePlanes[i].GetNormalX(), collision->trianglePlanes[i].GetNormalY(), collision->trianglePlanes[i].GetNormalZ()); CVector offset = n * 0.028f; p[0] += offset; p[1] += offset; p[2] += offset; if ( !ShadowRenderTriangleCB(p, &n, &proj) ) break; } i++; } } } void CShadows::UpdateStaticShadows(void) { for ( int32 i = 0; i < MAX_STATICSHADOWS; i++ ) { if ( aStaticShadows[i].m_pPolyBunch != nil && !aStaticShadows[i].m_bJustCreated && (!aStaticShadows[i].m_bTemp || CTimer::GetTimeInMilliseconds() > aStaticShadows[i].m_nTimeCreated + 5000) ) { aStaticShadows[i].Free(); } aStaticShadows[i].m_bJustCreated = false; } } void CShadows::UpdatePermanentShadows(void) { for ( int32 i = 0; i < MAX_PERMAMENTSHADOWS; i++ ) { if ( aPermanentShadows[i].m_nType != SHADOWTYPE_NONE ) { uint32 timePassed = CTimer::GetTimeInMilliseconds() - aPermanentShadows[i].m_nTimeCreated; if ( timePassed >= aPermanentShadows[i].m_nLifeTime ) aPermanentShadows[i].m_nType = SHADOWTYPE_NONE; else { bool bOk; if ( timePassed >= (aPermanentShadows[i].m_nLifeTime * 3 / 4) ) { // timePassed == 0 -> 4 // timePassed == aPermanentShadows[i].m_nLifeTime -> 0 float fMult = 1.0f - float(timePassed - (aPermanentShadows[i].m_nLifeTime * 3 / 4)) / (aPermanentShadows[i].m_nLifeTime / 4); bOk = StoreStaticShadow((uintptr)&aPermanentShadows[i], aPermanentShadows[i].m_nType, aPermanentShadows[i].m_pTexture, &aPermanentShadows[i].m_vecPos, aPermanentShadows[i].m_vecFront.x, aPermanentShadows[i].m_vecFront.y, aPermanentShadows[i].m_vecSide.x, aPermanentShadows[i].m_vecSide.y, (int32)(aPermanentShadows[i].m_nIntensity * fMult), (int32)(aPermanentShadows[i].m_nRed * fMult), (int32)(aPermanentShadows[i].m_nGreen * fMult), (int32)(aPermanentShadows[i].m_nBlue * fMult), aPermanentShadows[i].m_fZDistance, 1.0f, 40.0f, false, 0.0f); } else { bOk = StoreStaticShadow((uintptr)&aPermanentShadows[i], aPermanentShadows[i].m_nType, aPermanentShadows[i].m_pTexture, &aPermanentShadows[i].m_vecPos, aPermanentShadows[i].m_vecFront.x, aPermanentShadows[i].m_vecFront.y, aPermanentShadows[i].m_vecSide.x, aPermanentShadows[i].m_vecSide.y, aPermanentShadows[i].m_nIntensity, aPermanentShadows[i].m_nRed, aPermanentShadows[i].m_nGreen, aPermanentShadows[i].m_nBlue, aPermanentShadows[i].m_fZDistance, 1.0f, 40.0f, false, 0.0f); } if ( !bOk ) aPermanentShadows[i].m_nType = SHADOWTYPE_NONE; } } } } void CStaticShadow::Free(void) { if ( m_pPolyBunch != nil ) { CPolyBunch *pFree = CShadows::pEmptyBunchList; CShadows::pEmptyBunchList = m_pPolyBunch; CPolyBunch *pUsed = m_pPolyBunch; while (pUsed->m_pNext != nil) pUsed = pUsed->m_pNext; pUsed->m_pNext = pFree; } m_pPolyBunch = nil; m_nId = 0; } void CShadows::CalcPedShadowValues(CVector vecLightDir, float *pfFrontX, float *pfFrontY, float *pfSideX, float *pfSideY, float *pfDisplacementX, float *pfDisplacementY) { ASSERT(pfFrontX != nil); ASSERT(pfFrontY != nil); ASSERT(pfSideX != nil); ASSERT(pfSideY != nil); ASSERT(pfDisplacementX != nil); ASSERT(pfDisplacementY != nil); *pfFrontX = -vecLightDir.x; *pfFrontY = -vecLightDir.y; float fDist = Sqrt(*pfFrontY * *pfFrontY + *pfFrontX * *pfFrontX); float fMult = (fDist + 1.0f) / fDist; *pfFrontX *= fMult; *pfFrontY *= fMult; *pfSideX = -vecLightDir.y / fDist; *pfSideY = vecLightDir.x / fDist; *pfDisplacementX = -vecLightDir.x; *pfDisplacementY = -vecLightDir.y; *pfFrontX /= 2; *pfFrontY /= 2; *pfSideX /= 2; *pfSideY /= 2; *pfDisplacementX /= 2; *pfDisplacementY /= 2; } void CShadows::RenderExtraPlayerShadows(void) { #ifdef FIX_BUGS if (CReplay::IsPlayingBack()) return; #endif if ( CTimeCycle::GetLightShadowStrength() != 0 ) { CVehicle *pCar = FindPlayerVehicle(); if ( pCar == nil ) ; // R* cut it out for playerped else { if ( pCar->GetModelIndex() != MI_RCBANDIT && pCar->GetVehicleAppearance() != VEHICLE_APPEARANCE_BIKE && !pCar->IsBike() && !pCar->IsPlane() && !pCar->IsBoat() ) { for ( int32 i = 0; i < CPointLights::NumLights; i++ ) { if ( CPointLights::aLights[i].type == CPointLights::LIGHT_POINT && CPointLights::aLights[i].castExtraShadows &&(0.0f != CPointLights::aLights[i].red || 0.0f != CPointLights::aLights[i].green || 0.0f != CPointLights::aLights[i].blue) ) { CVector vecLight = CPointLights::aLights[i].coors - FindPlayerCoors(); float fLightDist = vecLight.Magnitude(); float fRadius = CPointLights::aLights[i].radius; if ( fLightDist < fRadius ) { // fLightDist == 0 -> 2.0f // fLightDist == fRadius -> 0.0f float fMult = (1.0f - (2.0f * fLightDist - fRadius) / fRadius); int32 nColorStrength; if ( fLightDist < fRadius*0.5f ) nColorStrength = (5*CTimeCycle::GetLightShadowStrength()/8); else nColorStrength = int32((5*CTimeCycle::GetLightShadowStrength()/8) * fMult); float fInv = 1.0f / fLightDist; vecLight.x *= fInv; vecLight.y *= fInv; vecLight.z *= fInv; CVector shadowPos = pCar->GetPosition(); shadowPos.x -= vecLight.x * 1.2f; shadowPos.y -= vecLight.y * 1.2f; float fVehicleWidth = pCar->GetColModel()->boundingBox.GetSize().x; float fVehicleHeight = pCar->GetColModel()->boundingBox.GetSize().y; shadowPos.x -= ((fVehicleHeight/2) - pCar->GetColModel()->boundingBox.max.y) * pCar->GetForward().x; shadowPos.y -= ((fVehicleHeight/2) - pCar->GetColModel()->boundingBox.max.y) * pCar->GetForward().y; if ( pCar->GetUp().z > 0.0f ) { StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowCarTex, &shadowPos, pCar->GetForward().x * (fVehicleHeight/2), pCar->GetForward().y * (fVehicleHeight/2), pCar->GetRight().x * (fVehicleWidth/3), pCar->GetRight().y * (fVehicleWidth/3), nColorStrength, 0, 0, 0, 4.5f, false, 1.0f, nil, false); } else { StoreShadowToBeRendered(SHADOWTYPE_DARK, gpShadowCarTex, &shadowPos, pCar->GetForward().x * (fVehicleHeight/2), pCar->GetForward().y * (fVehicleHeight/2), -pCar->GetRight().x * (fVehicleWidth/2), -pCar->GetRight().y * (fVehicleWidth/2), nColorStrength, 0, 0, 0, 4.5f, false, 1.0f, nil, false); } } } } } } } } void CShadows::TidyUpShadows(void) { for ( int32 i = 0; i < MAX_PERMAMENTSHADOWS; i++ ) aPermanentShadows[i].m_nType = SHADOWTYPE_NONE; } void CShadows::RenderIndicatorShadow(uint32 nID, uint8 ShadowType, RwTexture *pTexture, CVector *pPosn, float fFrontX, float fFrontY, float fSideX, float fSideY, int16 nIntensity) { ASSERT(pPosn != nil); C3dMarkers::PlaceMarkerSet(nID, MARKERTYPE_CYLINDER, *pPosn, Max(fFrontX, -fSideY), SPHERE_MARKER_R, SPHERE_MARKER_G, SPHERE_MARKER_B, SPHERE_MARKER_A, SPHERE_MARKER_PULSE_PERIOD, 0.2f, 0); }