#include "common.h" #include "main.h" #include "patcher.h" #include "General.h" #include "Timer.h" #include "Weather.h" #include "Camera.h" #include "World.h" #include "Vehicle.h" #include "ZoneCull.h" #include "TxdStore.h" #include "RenderBuffer.h" #include "Rubbish.h" #define RUBBISH_MAX_DIST (18.0f) #define RUBBISH_FADE_DIST (16.5f) RwTexture *gpRubbishTexture[4]; RwImVertexIndex RubbishIndexList[6]; RwImVertexIndex RubbishIndexList2[6]; // unused RwIm3DVertex RubbishVertices[4]; bool CRubbish::bRubbishInvisible; int CRubbish::RubbishVisibility; COneSheet CRubbish::aSheets[NUM_RUBBISH_SHEETS]; COneSheet CRubbish::StartEmptyList; COneSheet CRubbish::EndEmptyList; COneSheet CRubbish::StartStaticsList; COneSheet CRubbish::EndStaticsList; COneSheet CRubbish::StartMoversList; COneSheet CRubbish::EndMoversList; void COneSheet::AddToList(COneSheet *list) { this->m_next = list->m_next; this->m_prev = list; list->m_next = this; this->m_next->m_prev = this; } void COneSheet::RemoveFromList(void) { m_next->m_prev = m_prev; m_prev->m_next = m_next; } void CRubbish::Render(void) { int type; RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); for(type = 0; type < 4; type++){ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpRubbishTexture[type])); TempBufferIndicesStored = 0; TempBufferVerticesStored = 0; COneSheet *sheet; for(sheet = &aSheets[type*NUM_RUBBISH_SHEETS / 4]; sheet < &aSheets[(type+1)*NUM_RUBBISH_SHEETS / 4]; sheet++){ if(sheet->m_state == 0) continue; uint32 alpha = 128; CVector pos; if(sheet->m_state == 1){ pos = sheet->m_basePos; if(!sheet->m_isVisible) alpha = 0; }else{ pos = sheet->m_animatedPos; // Not fully visible during animation, calculate current alpha if(!sheet->m_isVisible || !sheet->m_targetIsVisible){ float t = (float)(CTimer::GetTimeInMilliseconds() - sheet->m_moveStart)/sheet->m_moveDuration; float f1 = sheet->m_isVisible ? 1.0f-t : 0.0f; float f2 = sheet->m_targetIsVisible ? t : 0.0f; alpha = 128 * (f1+f2); } } float camDist = (pos - TheCamera.GetPosition()).Magnitude2D(); if(camDist < RUBBISH_MAX_DIST){ if(camDist >= RUBBISH_FADE_DIST) alpha -= alpha*(camDist-RUBBISH_FADE_DIST)/(RUBBISH_MAX_DIST-RUBBISH_FADE_DIST); alpha = (RubbishVisibility*alpha)/256; float vx = Sin(sheet->m_angle) * 0.4f; float vy = Cos(sheet->m_angle) * 0.4f; int v = TempBufferVerticesStored; RwIm3DVertexSetPos(&TempBufferRenderVertices[v+0], pos.x + vx, pos.y + vy, pos.z); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+0], 255, 255, 255, alpha); RwIm3DVertexSetPos(&TempBufferRenderVertices[v+1], pos.x - vy, pos.y + vx, pos.z); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+1], 255, 255, 255, alpha); RwIm3DVertexSetPos(&TempBufferRenderVertices[v+2], pos.x + vy, pos.y - vx, pos.z); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+2], 255, 255, 255, alpha); RwIm3DVertexSetPos(&TempBufferRenderVertices[v+3], pos.x - vx, pos.y - vy, pos.z); RwIm3DVertexSetRGBA(&TempBufferRenderVertices[v+3], 255, 255, 255, alpha); RwIm3DVertexSetU(&TempBufferRenderVertices[v+0], 0.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[v+0], 0.0f); RwIm3DVertexSetU(&TempBufferRenderVertices[v+1], 1.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[v+1], 0.0f); RwIm3DVertexSetU(&TempBufferRenderVertices[v+2], 0.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[v+2], 1.0f); RwIm3DVertexSetU(&TempBufferRenderVertices[v+3], 1.0f); RwIm3DVertexSetV(&TempBufferRenderVertices[v+3], 1.0f); int i = TempBufferIndicesStored; TempBufferRenderIndexList[i+0] = RubbishIndexList[0] + TempBufferVerticesStored; TempBufferRenderIndexList[i+1] = RubbishIndexList[1] + TempBufferVerticesStored; TempBufferRenderIndexList[i+2] = RubbishIndexList[2] + TempBufferVerticesStored; TempBufferRenderIndexList[i+3] = RubbishIndexList[3] + TempBufferVerticesStored; TempBufferRenderIndexList[i+4] = RubbishIndexList[4] + TempBufferVerticesStored; TempBufferRenderIndexList[i+5] = RubbishIndexList[5] + TempBufferVerticesStored; TempBufferVerticesStored += 4; TempBufferIndicesStored += 6; } } if(TempBufferIndicesStored != 0){ LittleTest(); if(RwIm3DTransform(TempBufferRenderVertices, TempBufferVerticesStored, nil, rwIM3D_VERTEXUV)){ RwIm3DRenderIndexedPrimitive(rwPRIMTYPETRILIST, TempBufferRenderIndexList, TempBufferIndicesStored); RwIm3DEnd(); } } } RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); } void CRubbish::StirUp(CVehicle *veh) { if((CTimer::GetFrameCounter() ^ (veh->m_randomSeed&3)) == 0) return; if(Abs(veh->GetPosition().x - TheCamera.GetPosition().x) < 20.0f && Abs(veh->GetPosition().y - TheCamera.GetPosition().y) < 20.0f) if(Abs(veh->GetMoveSpeed().x) > 0.05f || Abs(veh->GetMoveSpeed().y) > 0.05f){ float speed = veh->GetMoveSpeed().Magnitude2D(); if(speed > 0.05f){ bool movingForward = DotProduct2D(veh->GetMoveSpeed(), veh->GetForward()) > 0.0f; COneSheet *sheet = StartStaticsList.m_next; CVector2D size = veh->GetColModel()->boundingBox.max; // Check all static sheets while(sheet != &EndStaticsList){ COneSheet *next = sheet->m_next; CVector2D carToSheet = sheet->m_basePos - veh->GetPosition(); float distFwd = DotProduct2D(carToSheet, veh->GetForward()); // sheet has to be a bit behind car if(movingForward && distFwd < -0.5f*size.y && distFwd > -1.5f*size.y || !movingForward && distFwd > 0.5f*size.y && distFwd < 1.5f*size.y){ float distSide = Abs(DotProduct2D(carToSheet, veh->GetRight())); if(distSide < 1.5*size.x){ // Check with higher speed for sheet directly behind car float speedToCheck = distSide < size.x ? speed : speed*0.5f; if(speedToCheck > 0.05f){ sheet->m_state = 2; if(speedToCheck > 0.15f) sheet->m_animationType = 2; else sheet->m_animationType = 1; sheet->m_moveDuration = 2000; sheet->m_xDist = veh->GetMoveSpeed().x; sheet->m_yDist = veh->GetMoveSpeed().y; float dist = Sqrt(SQR(sheet->m_xDist)+SQR(sheet->m_yDist)); sheet->m_xDist *= 25.0f*speed/dist; sheet->m_yDist *= 25.0f*speed/dist; sheet->m_animHeight = 3.0f*speed; sheet->m_moveStart = CTimer::GetTimeInMilliseconds(); float tx = sheet->m_basePos.x + sheet->m_xDist; float ty = sheet->m_basePos.y + sheet->m_yDist; float tz = sheet->m_basePos.z + 3.0f; sheet->m_targetZ = CWorld::FindGroundZFor3DCoord(tx, ty, tz, nil) + 0.1f; sheet->RemoveFromList(); sheet->AddToList(&StartMoversList); } } } sheet = next; } } } } static float aAnimations[3][34] = { { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, // Normal move { 0.0f, 0.05f, 0.12f, 0.25f, 0.42f, 0.57f, 0.68f, 0.8f, 0.86f, 0.9f, 0.93f, 0.95f, 0.96f, 0.97f, 0.98f, 0.99f, 1.0f, // XY movemnt 0.15f, 0.35f, 0.6f, 0.9f, 1.2f, 1.25f, 1.3f, 1.2f, 1.1f, 0.95f, 0.8f, 0.6f, 0.45f, 0.3f, 0.2f, 0.1f, 0 }, // Z movement // Stirred up by fast vehicle { 0.0f, 0.05f, 0.12f, 0.25f, 0.42f, 0.57f, 0.68f, 0.8f, 0.95f, 1.1f, 1.15f, 1.18f, 1.15f, 1.1f, 1.05f, 1.03f, 1.0f, 0.15f, 0.35f, 0.6f, 0.9f, 1.2f, 1.25f, 1.3f, 1.2f, 1.1f, 0.95f, 0.8f, 0.6f, 0.45f, 0.3f, 0.2f, 0.1f, 0 } }; void CRubbish::Update(void) { bool foundGround; // FRAMETIME if(bRubbishInvisible) RubbishVisibility = max(RubbishVisibility-5, 0); else RubbishVisibility = min(RubbishVisibility+5, 255); // Spawn a new sheet COneSheet *sheet = StartEmptyList.m_next; if(sheet != &EndEmptyList){ float spawnDist; float spawnAngle; spawnDist = (CGeneral::GetRandomNumber()&0xFF)/256.0f + RUBBISH_MAX_DIST; uint8 r = CGeneral::GetRandomNumber(); if(r&1) spawnAngle = (CGeneral::GetRandomNumber()&0xFF)/256.0f * 6.28f; else spawnAngle = (r-128)/160.0f + TheCamera.Orientation; sheet->m_basePos.x = TheCamera.GetPosition().x + spawnDist*Sin(spawnAngle); sheet->m_basePos.y = TheCamera.GetPosition().y + spawnDist*Cos(spawnAngle); sheet->m_basePos.z = CWorld::FindGroundZFor3DCoord(sheet->m_basePos.x, sheet->m_basePos.y, TheCamera.GetPosition().z, &foundGround) + 0.1f; if(foundGround){ // Found ground, so add to statics list sheet->m_angle = (CGeneral::GetRandomNumber()&0xFF)/256.0f * 6.28f; sheet->m_state = 1; if(CCullZones::FindAttributesForCoors(sheet->m_basePos, nil) & ATTRZONE_NORAIN) sheet->m_isVisible = false; else sheet->m_isVisible = true; sheet->RemoveFromList(); sheet->AddToList(&StartStaticsList); } } // Process animation sheet = StartMoversList.m_next; while(sheet != &EndMoversList){ uint32 currentTime = CTimer::GetTimeInMilliseconds() - sheet->m_moveStart; if(currentTime < sheet->m_moveDuration){ // Animation int step = 16 * currentTime / sheet->m_moveDuration; // 16 steps in animation int stepTime = sheet->m_moveDuration/16; // time in each step float s = (float)(currentTime - stepTime*step) / stepTime; // position on step float t = (float)currentTime / sheet->m_moveDuration; // position on total animation // factors for xy and z-movment float fxy = aAnimations[sheet->m_animationType][step]*(1.0f-s) + aAnimations[sheet->m_animationType][step+1]*s; float fz = aAnimations[sheet->m_animationType][step+17]*(1.0f-s) + aAnimations[sheet->m_animationType][step+1+17]*s; sheet->m_animatedPos.x = sheet->m_basePos.x + fxy*sheet->m_xDist; sheet->m_animatedPos.y = sheet->m_basePos.y + fxy*sheet->m_yDist; sheet->m_animatedPos.z = (1.0f-t)*sheet->m_basePos.z + t*sheet->m_targetZ + fz*sheet->m_animHeight; sheet->m_angle += CTimer::GetTimeStep()*0.04f; if(sheet->m_angle > 6.28f) sheet->m_angle -= 6.28f; sheet = sheet->m_next; }else{ // End of animation, back into statics list sheet->m_basePos.x += sheet->m_xDist; sheet->m_basePos.y += sheet->m_yDist; sheet->m_basePos.z = sheet->m_targetZ; sheet->m_state = 1; sheet->m_isVisible = sheet->m_targetIsVisible; COneSheet *next = sheet->m_next; sheet->RemoveFromList(); sheet->AddToList(&StartStaticsList); sheet = next; } } // Stir up a sheet by wind // FRAMETIME int freq; if(CWeather::Wind < 0.1f) freq = 31; else if(CWeather::Wind < 0.4f) freq = 7; else if(CWeather::Wind < 0.7f) freq = 1; else freq = 0; if((CTimer::GetFrameCounter() & freq) == 0){ // Pick a random sheet and set animation state if static int i = CGeneral::GetRandomNumber() % NUM_RUBBISH_SHEETS; if(aSheets[i].m_state == 1){ aSheets[i].m_moveStart = CTimer::GetTimeInMilliseconds(); aSheets[i].m_moveDuration = CWeather::Wind*1500.0f + 1000.0f; aSheets[i].m_animHeight = 0.2f; aSheets[i].m_xDist = 3.0f*CWeather::Wind; aSheets[i].m_yDist = 3.0f*CWeather::Wind; // Check if target position is ok float tx = aSheets[i].m_basePos.x + aSheets[i].m_xDist; float ty = aSheets[i].m_basePos.y + aSheets[i].m_yDist; float tz = aSheets[i].m_basePos.z + 3.0f; aSheets[i].m_targetZ = CWorld::FindGroundZFor3DCoord(tx, ty, tz, &foundGround) + 0.1f; if(CCullZones::FindAttributesForCoors(CVector(tx, ty, aSheets[i].m_targetZ), nil) & ATTRZONE_NORAIN) aSheets[i].m_targetIsVisible = false; else aSheets[i].m_targetIsVisible = true; if(foundGround){ // start animation aSheets[i].m_state = 2; aSheets[i].m_animationType = 1; aSheets[i].RemoveFromList(); aSheets[i].AddToList(&StartMoversList); } } } // Remove sheets that are too far away int i = (CTimer::GetFrameCounter()%(NUM_RUBBISH_SHEETS/4))*4; int last = ((CTimer::GetFrameCounter()%(NUM_RUBBISH_SHEETS/4)) + 1)*4; for(; i < last; i++){ if(aSheets[i].m_state == 1 && (aSheets[i].m_basePos - TheCamera.GetPosition()).MagnitudeSqr2D() > SQR(RUBBISH_MAX_DIST+1.0f)){ aSheets[i].m_state = 0; aSheets[i].RemoveFromList(); aSheets[i].AddToList(&StartEmptyList); } } } void CRubbish::SetVisibility(bool visible) { bRubbishInvisible = !visible; } void CRubbish::Init(void) { int i; for(i = 0; i < NUM_RUBBISH_SHEETS; i++){ aSheets[i].m_state = 0; if(i < NUM_RUBBISH_SHEETS-1) aSheets[i].m_next = &aSheets[i+1]; else aSheets[i].m_next = &EndEmptyList; if(i > 0) aSheets[i].m_prev = &aSheets[i-1]; else aSheets[i].m_prev = &StartEmptyList; } StartEmptyList.m_next = &aSheets[0]; StartEmptyList.m_prev = nil; EndEmptyList.m_next = nil; EndEmptyList.m_prev = &aSheets[NUM_RUBBISH_SHEETS-1]; StartStaticsList.m_next = &EndStaticsList; StartStaticsList.m_prev = nil; EndStaticsList.m_next = nil; EndStaticsList.m_prev = &StartStaticsList; StartMoversList.m_next = &EndMoversList; StartMoversList.m_prev = nil; EndMoversList.m_next = nil; EndMoversList.m_prev = &StartMoversList; // unused RwIm3DVertexSetU(&RubbishVertices[0], 0.0f); RwIm3DVertexSetV(&RubbishVertices[0], 0.0f); RwIm3DVertexSetU(&RubbishVertices[1], 1.0f); RwIm3DVertexSetV(&RubbishVertices[1], 0.0f); RwIm3DVertexSetU(&RubbishVertices[2], 0.0f); RwIm3DVertexSetV(&RubbishVertices[2], 1.0f); RwIm3DVertexSetU(&RubbishVertices[3], 1.0f); RwIm3DVertexSetV(&RubbishVertices[3], 1.0f); // unused RubbishIndexList2[0] = 0; RubbishIndexList2[1] = 2; RubbishIndexList2[2] = 1; RubbishIndexList2[3] = 1; RubbishIndexList2[4] = 2; RubbishIndexList2[5] = 3; RubbishIndexList[0] = 0; RubbishIndexList[1] = 1; RubbishIndexList[2] = 2; RubbishIndexList[3] = 1; RubbishIndexList[4] = 3; RubbishIndexList[5] = 2; CTxdStore::PushCurrentTxd(); int slot = CTxdStore::FindTxdSlot("particle"); CTxdStore::SetCurrentTxd(slot); gpRubbishTexture[0] = RwTextureRead("gameleaf01_64", nil); gpRubbishTexture[1] = RwTextureRead("gameleaf02_64", nil); gpRubbishTexture[2] = RwTextureRead("newspaper01_64", nil); gpRubbishTexture[3] = RwTextureRead("newspaper02_64", nil); CTxdStore::PopCurrentTxd(); RubbishVisibility = 255; bRubbishInvisible = false; } void CRubbish::Shutdown(void) { RwTextureDestroy(gpRubbishTexture[0]); RwTextureDestroy(gpRubbishTexture[1]); RwTextureDestroy(gpRubbishTexture[2]); RwTextureDestroy(gpRubbishTexture[3]); }