#define WITH_D3D #include "common.h" #include "main.h" #include "Lights.h" #include "ModelInfo.h" #include "Treadable.h" #include "Ped.h" #include "Vehicle.h" #include "Boat.h" #include "Heli.h" #include "Bike.h" #include "Object.h" #include "PathFind.h" #include "Collision.h" #include "VisibilityPlugins.h" #include "Clock.h" #include "World.h" #include "Camera.h" #include "ModelIndices.h" #include "Streaming.h" #include "Shadows.h" #include "PointLights.h" #include "Occlusion.h" #include "Renderer.h" #include "custompipes.h" //--MIAMI: file done bool gbShowCollisionPolys; bool gbShowCollisionLines; bool gbBigWhiteDebugLightSwitchedOn; bool gbDontRenderBuildings; bool gbDontRenderBigBuildings; bool gbDontRenderPeds; bool gbDontRenderObjects; bool gbDontRenderVehicles; struct EntityInfo { CEntity *ent; float sort; }; CLinkList gSortedVehiclesAndPeds; int32 CRenderer::ms_nNoOfVisibleEntities; CEntity *CRenderer::ms_aVisibleEntityPtrs[NUMVISIBLEENTITIES]; CEntity *CRenderer::ms_aInVisibleEntityPtrs[NUMINVISIBLEENTITIES]; int32 CRenderer::ms_nNoOfInVisibleEntities; #ifdef NEW_RENDERER int32 CRenderer::ms_nNoOfVisibleVehicles; CEntity *CRenderer::ms_aVisibleVehiclePtrs[NUMVISIBLEENTITIES]; int32 CRenderer::ms_nNoOfVisibleBuildings; CEntity *CRenderer::ms_aVisibleBuildingPtrs[NUMVISIBLEENTITIES]; #endif CVector CRenderer::ms_vecCameraPosition; CVehicle *CRenderer::m_pFirstPersonVehicle; bool CRenderer::m_loadingPriority; float CRenderer::ms_lodDistScale = 1.2f; #ifdef FIX_BUGS #define LOD_DISTANCE (300.0f*TheCamera.LODDistMultiplier) #else #define LOD_DISTANCE 300.0f #endif #define FADE_DISTANCE 20.0f #define STREAM_DISTANCE 30.0f void CRenderer::Init(void) { gSortedVehiclesAndPeds.Init(40); SortBIGBuildings(); } void CRenderer::Shutdown(void) { gSortedVehiclesAndPeds.Shutdown(); } void CRenderer::PreRender(void) { int i; CLink *node; for(i = 0; i < ms_nNoOfVisibleEntities; i++) ms_aVisibleEntityPtrs[i]->PreRender(); #ifdef NEW_RENDERER if(gbNewRenderer){ for(i = 0; i < ms_nNoOfVisibleVehicles; i++) ms_aVisibleVehiclePtrs[i]->PreRender(); // How is this done with cWorldStream? for(i = 0; i < ms_nNoOfVisibleBuildings; i++) ms_aVisibleBuildingPtrs[i]->PreRender(); for(node = CVisibilityPlugins::m_alphaBuildingList.head.next; node != &CVisibilityPlugins::m_alphaBuildingList.tail; node = node->next) ((CEntity*)node->item.entity)->PreRender(); } #endif for (i = 0; i < ms_nNoOfInVisibleEntities; i++) { #ifdef SQUEEZE_PERFORMANCE if (ms_aInVisibleEntityPtrs[i]->IsVehicle() && ((CVehicle*)ms_aInVisibleEntityPtrs[i])->IsHeli()) #endif ms_aInVisibleEntityPtrs[i]->PreRender(); } for(node = CVisibilityPlugins::m_alphaEntityList.head.next; node != &CVisibilityPlugins::m_alphaEntityList.tail; node = node->next) ((CEntity*)node->item.entity)->PreRender(); CHeli::SpecialHeliPreRender(); CShadows::RenderExtraPlayerShadows(); } void CRenderer::RenderOneRoad(CEntity *e) { if(gbDontRenderBuildings) return; if(gbShowCollisionPolys) CCollision::DrawColModel_Coloured(e->GetMatrix(), *CModelInfo::GetModelInfo(e->GetModelIndex())->GetColModel(), e->GetModelIndex()); else e->Render(); } void CRenderer::RenderOneNonRoad(CEntity *e) { CPed *ped; CVehicle *veh; int i; bool resetLights; #ifndef MASTER if(gbShowCollisionPolys){ if(!e->IsVehicle()){ CCollision::DrawColModel_Coloured(e->GetMatrix(), *CModelInfo::GetModelInfo(e->GetModelIndex())->GetColModel(), e->GetModelIndex()); return; } }else if(e->IsBuilding()){ if(e->bIsBIGBuilding){ if(gbDontRenderBigBuildings) return; }else{ if(gbDontRenderBuildings) return; } }else #endif if(e->IsPed()){ #ifndef MASTER if(gbDontRenderPeds) return; #endif ped = (CPed*)e; if(ped->m_nPedState == PED_DRIVING) return; } #ifndef MASTER else if(e->IsObject() || e->IsDummy()){ if(gbDontRenderObjects) return; }else if(e->IsVehicle()){ // re3 addition if(gbDontRenderVehicles) return; } #endif resetLights = e->SetupLighting(); if(e->IsVehicle()){ // unfortunately can't use GetClump here CVisibilityPlugins::SetupVehicleVariables((RpClump*)e->m_rwObject); CVisibilityPlugins::InitAlphaAtomicList(); } // Render Peds in vehicle before vehicle itself if(e->IsVehicle()){ veh = (CVehicle*)e; if(veh->pDriver && veh->pDriver->m_nPedState == PED_DRIVING) veh->pDriver->Render(); for(i = 0; i < 8; i++) if(veh->pPassengers[i] && veh->pPassengers[i]->m_nPedState == PED_DRIVING) veh->pPassengers[i]->Render(); SetCullMode(rwCULLMODECULLNONE); } e->Render(); if(e->IsVehicle()){ e->bImBeingRendered = true; CVisibilityPlugins::RenderAlphaAtomics(); e->bImBeingRendered = false; SetCullMode(rwCULLMODECULLBACK); } e->RemoveLighting(resetLights); } void CRenderer::RenderFirstPersonVehicle(void) { if(m_pFirstPersonVehicle == nil) return; RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RenderOneNonRoad(m_pFirstPersonVehicle); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); } inline bool IsRoad(CEntity *e) { return e->IsBuilding() && ((CSimpleModelInfo*)CModelInfo::GetModelInfo(e->GetModelIndex()))->m_wetRoadReflection; } void CRenderer::RenderRoads(void) { int i; CEntity *e; RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); SetCullMode(rwCULLMODECULLBACK); DeActivateDirectional(); SetAmbientColours(); for(i = 0; i < ms_nNoOfVisibleEntities; i++){ e = ms_aVisibleEntityPtrs[i]; if(IsRoad(e)) RenderOneRoad(e); } } inline bool PutIntoSortedVehicleList(CVehicle *veh) { if(veh->IsBoat()){ int mode = TheCamera.Cams[TheCamera.ActiveCam].Mode; if(mode == CCam::MODE_WHEELCAM || mode == CCam::MODE_1STPERSON && TheCamera.GetLookDirection() != LOOKING_FORWARD && TheCamera.GetLookDirection() != LOOKING_BEHIND || CVisibilityPlugins::GetClumpAlpha(veh->GetClump()) != 255) return false; return true; }else return veh->bTouchingWater; } void CRenderer::RenderEverythingBarRoads(void) { int i; CEntity *e; EntityInfo ei; RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); SetCullMode(rwCULLMODECULLBACK); gSortedVehiclesAndPeds.Clear(); for(i = 0; i < ms_nNoOfVisibleEntities; i++){ e = ms_aVisibleEntityPtrs[i]; if(IsRoad(e)) continue; #ifdef EXTENDED_PIPELINES if(CustomPipes::bRenderingEnvMap && (e->IsPed() || e->IsVehicle())) continue; #endif if(e->IsVehicle() || e->IsPed() && CVisibilityPlugins::GetClumpAlpha((RpClump*)e->m_rwObject) != 255){ if(e->IsVehicle() && PutIntoSortedVehicleList((CVehicle*)e)){ ei.ent = e; ei.sort = (ms_vecCameraPosition - e->GetPosition()).MagnitudeSqr(); gSortedVehiclesAndPeds.InsertSorted(ei); }else{ if(!CVisibilityPlugins::InsertEntityIntoSortedList(e, (ms_vecCameraPosition - e->GetPosition()).Magnitude())){ printf("Ran out of space in alpha entity list"); RenderOneNonRoad(e); } } }else RenderOneNonRoad(e); } } void CRenderer::RenderBoats(void) { CLink *node; RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); SetCullMode(rwCULLMODECULLBACK); #ifdef NEW_RENDERER int i; CEntity *e; EntityInfo ei; if(gbNewRenderer){ gSortedVehiclesAndPeds.Clear(); // not the real thing for(i = 0; i < ms_nNoOfVisibleVehicles; i++){ e = ms_aVisibleVehiclePtrs[i]; if(e->IsVehicle() && PutIntoSortedVehicleList((CVehicle*)e)){ ei.ent = e; ei.sort = (ms_vecCameraPosition - e->GetPosition()).MagnitudeSqr(); gSortedVehiclesAndPeds.InsertSorted(ei); } } } #endif for(node = gSortedVehiclesAndPeds.tail.prev; node != &gSortedVehiclesAndPeds.head; node = node->prev){ CVehicle *v = (CVehicle*)node->item.ent; RenderOneNonRoad(v); } } #ifdef NEW_RENDERER #ifndef LIBRW #error "Need librw for EXTENDED_PIPELINES" #endif #include "WaterLevel.h" enum { // blend passes PASS_NOZ, // no z-write PASS_ADD, // additive PASS_BLEND // normal blend }; static RwRGBAReal black; #ifdef RW_D3D9 struct BuildingInst { rw::RawMatrix combinedMat; rw::d3d9::InstanceDataHeader *instHeader; uint8 fadeAlpha; bool lighting; }; static BuildingInst blendInsts[3][2000]; static int numBlendInsts[3]; static void SetStencilState(int state) { switch(state){ // disable stencil case 0: rw::d3d::setRenderState(D3DRS_STENCILENABLE, FALSE); break; // test against stencil case 1: rw::d3d::setRenderState(D3DRS_STENCILENABLE, TRUE); rw::d3d::setRenderState(D3DRS_STENCILFUNC, D3DCMP_NOTEQUAL); rw::d3d::setRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); rw::d3d::setRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP); rw::d3d::setRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); rw::d3d::setRenderState(D3DRS_STENCILMASK, 0xFF); rw::d3d::setRenderState(D3DRS_STENCILREF, 0xFF); break; // write to stencil case 2: rw::d3d::setRenderState(D3DRS_STENCILENABLE, TRUE); rw::d3d::setRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS); rw::d3d::setRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE); rw::d3d::setRenderState(D3DRS_STENCILREF, 0xFF); break; } } static void SetMatrix(BuildingInst *building, rw::Matrix *worldMat) { using namespace rw; RawMatrix world, worldview; Camera *cam = engine->currentCamera; convMatrix(&world, worldMat); RawMatrix::mult(&worldview, &world, &cam->devView); RawMatrix::mult(&building->combinedMat, &worldview, &cam->devProj); } static bool IsTextureTransparent(RwTexture *tex) { if(tex == nil || tex->raster == nil) return false; return PLUGINOFFSET(rw::d3d::D3dRaster, tex->raster, rw::d3d::nativeRasterOffset)->hasAlpha; } // Render all opaque meshes and put atomics that needs blending // into the deferred list. static void AtomicFirstPass(RpAtomic *atomic, int pass) { using namespace rw; using namespace rw::d3d; using namespace rw::d3d9; BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]]; atomic->getPipeline()->instance(atomic); building->instHeader = (d3d9::InstanceDataHeader*)atomic->geometry->instData; assert(building->instHeader != nil); assert(building->instHeader->platform == PLATFORM_D3D9); building->fadeAlpha = 255; building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT); bool setupDone = false; bool defer = false; SetMatrix(building, atomic->getFrame()->getLTM()); InstanceData *inst = building->instHeader->inst; for(rw::uint32 i = 0; i < building->instHeader->numMeshes; i++, inst++){ Material *m = inst->material; if(inst->vertexAlpha || m->color.alpha != 255 || IsTextureTransparent(m->texture)){ defer = true; continue; } // alright we're rendering this atomic if(!setupDone){ setStreamSource(0, building->instHeader->vertexStream[0].vertexBuffer, 0, building->instHeader->vertexStream[0].stride); setIndices(building->instHeader->indexBuffer); setVertexDeclaration(building->instHeader->vertexDeclaration); setVertexShader(default_amb_VS); d3ddevice->SetVertexShaderConstantF(VSLOC_combined, (float*)&building->combinedMat, 4); if(building->lighting) setAmbient(pAmbient->color); else setAmbient(black); setupDone = true; } setMaterial(m->color, m->surfaceProps); if(m->texture){ d3d::setTexture(0, m->texture); setPixelShader(default_tex_PS); }else setPixelShader(default_PS); drawInst(building->instHeader, inst); } if(defer) numBlendInsts[pass]++; } static void AtomicFullyTransparent(RpAtomic *atomic, int pass, int fadeAlpha) { using namespace rw; using namespace rw::d3d; using namespace rw::d3d9; BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]]; atomic->getPipeline()->instance(atomic); building->instHeader = (d3d9::InstanceDataHeader*)atomic->geometry->instData; assert(building->instHeader != nil); assert(building->instHeader->platform == PLATFORM_D3D9); building->fadeAlpha = fadeAlpha; building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT); SetMatrix(building, atomic->getFrame()->getLTM()); numBlendInsts[pass]++; } static void RenderBlendPass(int pass) { using namespace rw; using namespace rw::d3d; using namespace rw::d3d9; setVertexShader(default_amb_VS); int i; for(i = 0; i < numBlendInsts[pass]; i++){ BuildingInst *building = &blendInsts[pass][i]; setStreamSource(0, building->instHeader->vertexStream[0].vertexBuffer, 0, building->instHeader->vertexStream[0].stride); setIndices(building->instHeader->indexBuffer); setVertexDeclaration(building->instHeader->vertexDeclaration); d3ddevice->SetVertexShaderConstantF(VSLOC_combined, (float*)&building->combinedMat, 4); if(building->lighting) setAmbient(pAmbient->color); else setAmbient(black); InstanceData *inst = building->instHeader->inst; for(rw::uint32 j = 0; j < building->instHeader->numMeshes; j++, inst++){ Material *m = inst->material; if(!inst->vertexAlpha && m->color.alpha == 255 && !IsTextureTransparent(m->texture) && building->fadeAlpha == 255) continue; // already done this one rw::RGBA color = m->color; color.alpha = (color.alpha * building->fadeAlpha)/255; setMaterial(color, m->surfaceProps); if(m->texture){ d3d::setTexture(0, m->texture); setPixelShader(default_tex_PS); }else setPixelShader(default_PS); drawInst(building->instHeader, inst); } } } #endif #ifdef RW_GL3 struct BuildingInst { rw::Matrix matrix; rw::gl3::InstanceDataHeader *instHeader; uint8 fadeAlpha; bool lighting; }; static BuildingInst blendInsts[3][2000]; static int numBlendInsts[3]; static void SetStencilState(int state) { switch(state){ // disable stencil case 0: glDisable(GL_STENCIL_TEST); break; // test against stencil case 1: glEnable(GL_STENCIL_TEST); glStencilFunc(GL_NOTEQUAL, 0xFF, 0xFF); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glStencilMask(0xFF); break; // write to stencil case 2: glEnable(GL_STENCIL_TEST); glStencilFunc(GL_ALWAYS, 0xFF, 0xFF); glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); break; } } static bool IsTextureTransparent(RwTexture *tex) { if(tex == nil || tex->raster == nil) return false; return PLUGINOFFSET(rw::gl3::Gl3Raster, tex->raster, rw::gl3::nativeRasterOffset)->hasAlpha; } // Render all opaque meshes and put atomics that needs blending // into the deferred list. static void AtomicFirstPass(RpAtomic *atomic, int pass) { using namespace rw; using namespace rw::gl3; BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]]; atomic->getPipeline()->instance(atomic); building->instHeader = (gl3::InstanceDataHeader*)atomic->geometry->instData; assert(building->instHeader != nil); assert(building->instHeader->platform == PLATFORM_GL3); building->fadeAlpha = 255; building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT); WorldLights lights; lights.numAmbients = 1; lights.numDirectionals = 0; lights.numLocals = 0; if(building->lighting) lights.ambient = pAmbient->color; else lights.ambient = black; bool setupDone = false; bool defer = false; building->matrix = *atomic->getFrame()->getLTM(); InstanceData *inst = building->instHeader->inst; for(rw::uint32 i = 0; i < building->instHeader->numMeshes; i++, inst++){ Material *m = inst->material; if(inst->vertexAlpha || m->color.alpha != 255 || IsTextureTransparent(m->texture)){ defer = true; continue; } // alright we're rendering this atomic if(!setupDone){ defaultShader->use(); setWorldMatrix(&building->matrix); #ifdef RW_GL_USE_VAOS glBindVertexArray(building->instHeader->vao); #else glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, building->instHeader->ibo); glBindBuffer(GL_ARRAY_BUFFER, building->instHeader->vbo); setAttribPointers(building->instHeader->attribDesc, building->instHeader->numAttribs); #endif setLights(&lights); setupDone = true; } setMaterial(m->color, m->surfaceProps); setTexture(0, m->texture); drawInst(building->instHeader, inst); } #ifndef RW_GL_USE_VAOS disableAttribPointers(building->instHeader->attribDesc, building->instHeader->numAttribs); #endif if(defer) numBlendInsts[pass]++; } static void AtomicFullyTransparent(RpAtomic *atomic, int pass, int fadeAlpha) { using namespace rw; using namespace rw::gl3; BuildingInst *building = &blendInsts[pass][numBlendInsts[pass]]; atomic->getPipeline()->instance(atomic); building->instHeader = (gl3::InstanceDataHeader*)atomic->geometry->instData; assert(building->instHeader != nil); assert(building->instHeader->platform == PLATFORM_GL3); building->fadeAlpha = fadeAlpha; building->lighting = !!(atomic->geometry->flags & rw::Geometry::LIGHT); building->matrix = *atomic->getFrame()->getLTM(); numBlendInsts[pass]++; } static void RenderBlendPass(int pass) { using namespace rw; using namespace rw::gl3; defaultShader->use(); WorldLights lights; lights.numAmbients = 1; lights.numDirectionals = 0; lights.numLocals = 0; int i; for(i = 0; i < numBlendInsts[pass]; i++){ BuildingInst *building = &blendInsts[pass][i]; #ifdef RW_GL_USE_VAOS glBindVertexArray(building->instHeader->vao); #else glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, building->instHeader->ibo); glBindBuffer(GL_ARRAY_BUFFER, building->instHeader->vbo); setAttribPointers(building->instHeader->attribDesc, building->instHeader->numAttribs); #endif setWorldMatrix(&building->matrix); if(building->lighting) lights.ambient = pAmbient->color; else lights.ambient = black; setLights(&lights); InstanceData *inst = building->instHeader->inst; for(rw::uint32 j = 0; j < building->instHeader->numMeshes; j++, inst++){ Material *m = inst->material; if(!inst->vertexAlpha && m->color.alpha == 255 && !IsTextureTransparent(m->texture) && building->fadeAlpha == 255) continue; // already done this one rw::RGBA color = m->color; color.alpha = (color.alpha * building->fadeAlpha)/255; setMaterial(color, m->surfaceProps); setTexture(0, m->texture); drawInst(building->instHeader, inst); } #ifndef RW_GL_USE_VAOS disableAttribPointers(building->instHeader->attribDesc, building->instHeader->numAttribs); #endif } } #endif void CRenderer::RenderOneBuilding(CEntity *ent, float camdist) { if(ent->m_rwObject == nil) return; assert(RwObjectGetType(ent->m_rwObject) == rpATOMIC); RpAtomic *atomic = (RpAtomic*)ent->m_rwObject; CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(ent->GetModelIndex()); ent->bImBeingRendered = true; // TODO: this seems wrong, but do we even need it? int pass = PASS_BLEND; if(mi->m_additive) // very questionable pass = PASS_ADD; if(mi->m_noZwrite) pass = PASS_NOZ; if(ent->bDistanceFade){ RpAtomic *lodatm; float fadefactor; uint32 alpha; lodatm = mi->GetAtomicFromDistance(camdist - FADE_DISTANCE); fadefactor = (mi->GetLargestLodDistance() - (camdist - FADE_DISTANCE))/FADE_DISTANCE; if(fadefactor > 1.0f) fadefactor = 1.0f; alpha = mi->m_alpha * fadefactor; if(alpha == 255) AtomicFirstPass(atomic, pass); else{ // not quite sure what this is about, do we have to do that? RpGeometry *geo = RpAtomicGetGeometry(lodatm); if(geo != RpAtomicGetGeometry(atomic)) RpAtomicSetGeometry(atomic, geo, rpATOMICSAMEBOUNDINGSPHERE); AtomicFullyTransparent(atomic, pass, alpha); } }else AtomicFirstPass(atomic, pass); ent->bImBeingRendered = false; // TODO: this seems wrong, but do we even need it? } void CRenderer::RenderWorld(int pass) { int i; CEntity *e; CLink *node; RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); SetCullMode(rwCULLMODECULLBACK); DeActivateDirectional(); SetAmbientColours(); // Temporary...have to figure out sorting better switch(pass){ case 0: // Roads RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); for(i = 0; i < ms_nNoOfVisibleBuildings; i++){ e = ms_aVisibleBuildingPtrs[i]; if(e->bIsBIGBuilding || IsRoad(e)) RenderOneBuilding(e); } for(node = CVisibilityPlugins::m_alphaBuildingList.tail.prev; node != &CVisibilityPlugins::m_alphaBuildingList.head; node = node->prev){ e = node->item.entity; if(e->bIsBIGBuilding || IsRoad(e)) RenderOneBuilding(e, node->item.sort); } break; case 1: // Opaque RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); for(i = 0; i < ms_nNoOfVisibleBuildings; i++){ e = ms_aVisibleBuildingPtrs[i]; if(!(e->bIsBIGBuilding || IsRoad(e))) RenderOneBuilding(e); } for(node = CVisibilityPlugins::m_alphaBuildingList.tail.prev; node != &CVisibilityPlugins::m_alphaBuildingList.head; node = node->prev){ e = node->item.entity; if(!(e->bIsBIGBuilding || IsRoad(e))) RenderOneBuilding(e, node->item.sort); } // Now we have iterated through all visible buildings (unsorted and sorted) // and the transparency list is done. RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE); RenderBlendPass(PASS_NOZ); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); break; case 2: // Transparent RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RenderBlendPass(PASS_ADD); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RenderBlendPass(PASS_BLEND); break; } } void CRenderer::RenderVehiclesAndPeds(void) { int i; CEntity *e; EntityInfo ei; CLink *node; RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); //CVisibilityPlugins::InitAlphaEntityList(); // not safe yet // not the real thing for(i = 0; i < ms_nNoOfVisibleVehicles; i++){ e = ms_aVisibleVehiclePtrs[i]; if(e->IsVehicle() && PutIntoSortedVehicleList((CVehicle*)e)) continue; // boats handled elsewhere if(e->IsPed()) RenderOneNonRoad(e); else{ ei.ent = e; ei.sort = (ms_vecCameraPosition - e->GetPosition()).MagnitudeSqr(); gSortedVehiclesAndPeds.InsertSorted(ei); } } for(node = gSortedVehiclesAndPeds.tail.prev; node != &gSortedVehiclesAndPeds.head; node = node->prev) RenderOneNonRoad(node->item.ent); // RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); } void CRenderer::RenderTransparentWater(void) { int i; CEntity *e; RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDZERO); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); SetStencilState(2); for(i = 0; i < ms_nNoOfVisibleVehicles; i++){ e = ms_aVisibleVehiclePtrs[i]; if(e->IsVehicle() && ((CVehicle*)e)->IsBoat()) ((CBoat*)e)->RenderWaterOutPolys(); } RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE); SetStencilState(1); CWaterLevel::RenderTransparentWater(); SetStencilState(0); } void CRenderer::ClearForFrame(void) { ms_nNoOfVisibleEntities = 0; ms_nNoOfVisibleVehicles = 0; ms_nNoOfVisibleBuildings = 0; ms_nNoOfInVisibleEntities = 0; gSortedVehiclesAndPeds.Clear(); numBlendInsts[PASS_NOZ] = 0; numBlendInsts[PASS_ADD] = 0; numBlendInsts[PASS_BLEND] = 0; } #endif void CRenderer::RenderFadingInEntities(void) { RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)TRUE); RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); SetCullMode(rwCULLMODECULLBACK); DeActivateDirectional(); SetAmbientColours(); CVisibilityPlugins::RenderFadingEntities(); } void CRenderer::RenderFadingInUnderwaterEntities(void) { DeActivateDirectional(); SetAmbientColours(); CVisibilityPlugins::RenderFadingUnderwaterEntities(); } void CRenderer::RenderCollisionLines(void) { int i; // game doesn't draw fading in entities // this should probably be fixed for(i = 0; i < ms_nNoOfVisibleEntities; i++){ CEntity *e = ms_aVisibleEntityPtrs[i]; if(Abs(e->GetPosition().x - ms_vecCameraPosition.x) < 100.0f && Abs(e->GetPosition().y - ms_vecCameraPosition.y) < 100.0f) CCollision::DrawColModel(e->GetMatrix(), *e->GetColModel()); } } enum Visbility { VIS_INVISIBLE, VIS_VISIBLE, VIS_OFFSCREEN, VIS_STREAMME }; // Time Objects can be time culled if // other == -1 || CModelInfo::GetModelInfo(other)->GetRwObject() // i.e. we have to draw even at the wrong time if // other != -1 && CModelInfo::GetModelInfo(other)->GetRwObject() == nil #define OTHERUNAVAILABLE (other != -1 && CModelInfo::GetModelInfo(other)->GetRwObject() == nil) #define CANTIMECULL (!OTHERUNAVAILABLE) int32 CRenderer::SetupEntityVisibility(CEntity *ent) { CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(ent->m_modelIndex); CTimeModelInfo *ti; int32 other; float dist; bool request = true; if(mi->GetModelType() == MITYPE_TIME){ ti = (CTimeModelInfo*)mi; other = ti->GetOtherTimeModel(); if(CClock::GetIsTimeInRange(ti->GetTimeOn(), ti->GetTimeOff())){ // don't fade in, or between time objects if(CANTIMECULL) ti->m_alpha = 255; }else{ // Hide if possible if(CANTIMECULL) return VIS_INVISIBLE; // can't cull, so we'll try to draw this one, but don't request // it since what we really want is the other one. request = false; } }else{ if(mi->GetModelType() != MITYPE_SIMPLE && mi->GetModelType() != MITYPE_WEAPON){ if(FindPlayerVehicle() == ent && TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON && !(FindPlayerVehicle()->IsBike() && ((CBike*)FindPlayerVehicle())->bWheelieCam)){ // Player's vehicle in first person mode CVehicle *veh = (CVehicle*)ent; int model = veh->GetModelIndex(); int direction = TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking; if(direction == LOOKING_FORWARD || ent->GetModelIndex() == MI_RHINO || ent->GetModelIndex() == MI_COACH || TheCamera.m_bInATunnelAndABigVehicle || direction == LOOKING_BEHIND && veh->pHandling->Flags & HANDLING_UNKNOWN){ ent->bNoBrightHeadLights = true; return VIS_OFFSCREEN; } if(direction != LOOKING_BEHIND || !veh->IsBoat() || model == MI_REEFER || model == MI_TROPIC || model == MI_PREDATOR || model == MI_SKIMMER){ m_pFirstPersonVehicle = (CVehicle*)ent; ent->bNoBrightHeadLights = false; return VIS_OFFSCREEN; } } // All sorts of Clumps if(ent->m_rwObject == nil || !ent->bIsVisible) return VIS_INVISIBLE; if(!ent->GetIsOnScreen() || ent->IsEntityOccluded()) return VIS_OFFSCREEN; if(ent->bDrawLast){ dist = (ent->GetPosition() - ms_vecCameraPosition).Magnitude(); CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); ent->bDistanceFade = false; return VIS_INVISIBLE; } return VIS_VISIBLE; } if(ent->bDontStream){ if(ent->m_rwObject == nil || !ent->bIsVisible) return VIS_INVISIBLE; if(!ent->GetIsOnScreen() || ent->IsEntityOccluded()) return VIS_OFFSCREEN; if(ent->bDrawLast){ dist = (ent->GetPosition() - ms_vecCameraPosition).Magnitude(); CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); ent->bDistanceFade = false; return VIS_INVISIBLE; } return VIS_VISIBLE; } } // Simple ModelInfo if(!IsAreaVisible(ent->m_area)) return VIS_INVISIBLE; dist = (ent->GetPosition() - ms_vecCameraPosition).Magnitude(); #ifndef FIX_BUGS // Whatever this is supposed to do, it breaks fading for objects // whose draw dist is > LOD_DISTANCE-FADE_DISTANCE, i.e. 280 // because decreasing dist here makes the object visible above LOD_DISTANCE // before fading normally once below LOD_DISTANCE. // aha! this must be a workaround for the fact that we're not taking // the LOD multiplier into account here anywhere if(LOD_DISTANCE < dist && dist < mi->GetLargestLodDistance() + FADE_DISTANCE) dist += mi->GetLargestLodDistance() - LOD_DISTANCE; #endif if(ent->IsObject() && ent->bRenderDamaged) mi->m_isDamaged = true; RpAtomic *a = mi->GetAtomicFromDistance(dist); if(a){ mi->m_isDamaged = false; if(ent->m_rwObject == nil) ent->CreateRwObject(); assert(ent->m_rwObject); RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject; // Make sure our atomic uses the right geometry and not // that of an atomic for another draw distance. if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj)) RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?) mi->IncreaseAlpha(); if(ent->m_rwObject == nil || !ent->bIsVisible) return VIS_INVISIBLE; if(!ent->GetIsOnScreen() || ent->IsEntityOccluded()){ mi->m_alpha = 255; return VIS_OFFSCREEN; } if(mi->m_alpha != 255){ CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); ent->bDistanceFade = true; return VIS_INVISIBLE; } if(mi->m_drawLast || ent->bDrawLast){ if(CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist)){ ent->bDistanceFade = false; return VIS_INVISIBLE; } } return VIS_VISIBLE; } // Object is not loaded, figure out what to do if(mi->m_noFade){ mi->m_isDamaged = false; // request model if(dist - STREAM_DISTANCE < mi->GetLargestLodDistance() && request) return VIS_STREAMME; return VIS_INVISIBLE; } // We might be fading a = mi->GetAtomicFromDistance(dist - FADE_DISTANCE); mi->m_isDamaged = false; if(a == nil){ // request model if(dist - FADE_DISTANCE - STREAM_DISTANCE < mi->GetLargestLodDistance() && request) return VIS_STREAMME; return VIS_INVISIBLE; } if(ent->m_rwObject == nil) ent->CreateRwObject(); assert(ent->m_rwObject); RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject; if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj)) RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?) mi->IncreaseAlpha(); if(ent->m_rwObject == nil || !ent->bIsVisible) return VIS_INVISIBLE; if(!ent->GetIsOnScreen() || ent->IsEntityOccluded()){ mi->m_alpha = 255; return VIS_OFFSCREEN; }else{ CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); ent->bDistanceFade = true; return VIS_OFFSCREEN; // Why this? } } int32 CRenderer::SetupBigBuildingVisibility(CEntity *ent) { CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(ent->m_modelIndex); CTimeModelInfo *ti; int32 other; if(!IsAreaVisible(ent->m_area)) return VIS_INVISIBLE; bool request = true; if(mi->GetModelType() == MITYPE_TIME){ ti = (CTimeModelInfo*)mi; other = ti->GetOtherTimeModel(); if(CClock::GetIsTimeInRange(ti->GetTimeOn(), ti->GetTimeOff())){ // don't fade in, or between time objects if(CANTIMECULL) ti->m_alpha = 255; }else{ // Hide if possible if(CANTIMECULL){ ent->DeleteRwObject(); return VIS_INVISIBLE; } // can't cull, so we'll try to draw this one, but don't request // it since what we really want is the other one. request = false; } }else if(mi->GetModelType() == MITYPE_VEHICLE) return ent->IsVisible() ? VIS_VISIBLE : VIS_INVISIBLE; float dist = (ms_vecCameraPosition-ent->GetPosition()).Magnitude(); CSimpleModelInfo *nonLOD = mi->GetRelatedModel(); // Find out whether to draw below near distance. // This is only the case if there is a non-LOD which is either not // loaded or not completely faded in yet. if(dist < mi->GetNearDistance() && dist < LOD_DISTANCE){ // No non-LOD or non-LOD is completely visible. if(nonLOD == nil || nonLOD->GetRwObject() && nonLOD->m_alpha == 255) return VIS_INVISIBLE; // But if it is a time object, we'd rather draw the wrong // non-LOD than the right LOD. if(nonLOD->GetModelType() == MITYPE_TIME){ ti = (CTimeModelInfo*)nonLOD; other = ti->GetOtherTimeModel(); if(other != -1 && CModelInfo::GetModelInfo(other)->GetRwObject()) return VIS_INVISIBLE; } } RpAtomic *a = mi->GetFirstAtomicFromDistance(dist); if(a){ if(ent->m_rwObject == nil) ent->CreateRwObject(); assert(ent->m_rwObject); RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject; // Make sure our atomic uses the right geometry and not // that of an atomic for another draw distance. if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj)) RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?) mi->IncreaseAlpha(); if(!ent->IsVisibleComplex() || ent->IsEntityOccluded()){ mi->m_alpha = 255; return VIS_INVISIBLE; } if(mi->m_alpha != 255){ CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); ent->bDistanceFade = true; return VIS_INVISIBLE; } if(mi->m_drawLast){ CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); ent->bDistanceFade = false; return VIS_INVISIBLE; } return VIS_VISIBLE; } if(mi->m_noFade){ ent->DeleteRwObject(); return VIS_INVISIBLE; } // get faded atomic a = mi->GetFirstAtomicFromDistance(dist - FADE_DISTANCE); if(a == nil){ if(ent->bStreamBIGBuilding && dist-STREAM_DISTANCE < mi->GetLodDistance(0) && request){ return ent->GetIsOnScreen() ? VIS_STREAMME : VIS_INVISIBLE; }else{ ent->DeleteRwObject(); return VIS_INVISIBLE; } } // Fade... if(ent->m_rwObject == nil) ent->CreateRwObject(); assert(ent->m_rwObject); RpAtomic *rwobj = (RpAtomic*)ent->m_rwObject; if(RpAtomicGetGeometry(a) != RpAtomicGetGeometry(rwobj)) RpAtomicSetGeometry(rwobj, RpAtomicGetGeometry(a), rpATOMICSAMEBOUNDINGSPHERE); // originally 5 (mistake?) mi->IncreaseAlpha(); if(!ent->IsVisibleComplex() || ent->IsEntityOccluded()){ mi->m_alpha = 255; return VIS_INVISIBLE; } CVisibilityPlugins::InsertEntityIntoSortedList(ent, dist); ent->bDistanceFade = true; return VIS_INVISIBLE; } void CRenderer::ConstructRenderList(void) { COcclusion::ProcessBeforeRendering(); #ifdef NEW_RENDERER if(!gbNewRenderer) #endif { ms_nNoOfVisibleEntities = 0; ms_nNoOfInVisibleEntities = 0; } ms_vecCameraPosition = TheCamera.GetPosition(); // TODO: blocked ranges, but unused ScanWorld(); } void LimitFrustumVector(CVector &vec1, const CVector &vec2, float l) { float f; f = (l - vec2.z) / (vec1.z - vec2.z); vec1.x = f*(vec1.x - vec2.x) + vec2.x; vec1.y = f*(vec1.y - vec2.y) + vec2.y; vec1.z = f*(vec1.z - vec2.z) + vec2.z; } enum Corners { CORNER_CAM = 0, CORNER_FAR_TOPLEFT, CORNER_FAR_TOPRIGHT, CORNER_FAR_BOTRIGHT, CORNER_FAR_BOTLEFT, CORNER_LOD_LEFT, CORNER_LOD_RIGHT, CORNER_PRIO_LEFT, CORNER_PRIO_RIGHT, }; void CRenderer::ScanWorld(void) { float f = RwCameraGetFarClipPlane(TheCamera.m_pRwCamera); RwV2d vw = *RwCameraGetViewWindow(TheCamera.m_pRwCamera); CVector vectors[9]; RwMatrix *cammatrix; RwV2d poly[3]; memset(vectors, 0, sizeof(vectors)); vectors[CORNER_FAR_TOPLEFT].x = -vw.x * f; vectors[CORNER_FAR_TOPLEFT].y = vw.y * f; vectors[CORNER_FAR_TOPLEFT].z = f; vectors[CORNER_FAR_TOPRIGHT].x = vw.x * f; vectors[CORNER_FAR_TOPRIGHT].y = vw.y * f; vectors[CORNER_FAR_TOPRIGHT].z = f; vectors[CORNER_FAR_BOTRIGHT].x = vw.x * f; vectors[CORNER_FAR_BOTRIGHT].y = -vw.y * f; vectors[CORNER_FAR_BOTRIGHT].z = f; vectors[CORNER_FAR_BOTLEFT].x = -vw.x * f; vectors[CORNER_FAR_BOTLEFT].y = -vw.y * f; vectors[CORNER_FAR_BOTLEFT].z = f; cammatrix = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera)); m_pFirstPersonVehicle = nil; CVisibilityPlugins::InitAlphaEntityList(); CWorld::AdvanceCurrentScanCode(); // unused static CVector prevPos; static CVector prevFwd; static bool smallMovement; smallMovement = (TheCamera.GetPosition() - prevPos).MagnitudeSqr() < SQR(4.0f) && DotProduct(TheCamera.GetForward(), prevFwd) > 0.98f; prevPos = TheCamera.GetPosition(); prevFwd = TheCamera.GetForward(); if(cammatrix->at.z > 0.0f){ // looking up, bottom corners are further away vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_BOTLEFT] * LOD_DISTANCE/f; vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_BOTRIGHT] * LOD_DISTANCE/f; }else{ // looking down, top corners are further away vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_TOPLEFT] * LOD_DISTANCE/f; vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_TOPRIGHT] * LOD_DISTANCE/f; } vectors[CORNER_PRIO_LEFT].x = vectors[CORNER_LOD_LEFT].x * 0.2f; vectors[CORNER_PRIO_LEFT].y = vectors[CORNER_LOD_LEFT].y * 0.2f; vectors[CORNER_PRIO_LEFT].z = vectors[CORNER_LOD_LEFT].z; vectors[CORNER_PRIO_RIGHT].x = vectors[CORNER_LOD_RIGHT].x * 0.2f; vectors[CORNER_PRIO_RIGHT].y = vectors[CORNER_LOD_RIGHT].y * 0.2f; vectors[CORNER_PRIO_RIGHT].z = vectors[CORNER_LOD_RIGHT].z; RwV3dTransformPoints((RwV3d*)vectors, (RwV3d*)vectors, 9, cammatrix); m_loadingPriority = false; if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || #ifdef FIX_BUGS TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_GTACLASSIC || #endif TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED){ CRect rect; int x1, x2, y1, y2; LimitFrustumVector(vectors[CORNER_FAR_TOPLEFT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_TOPLEFT]); LimitFrustumVector(vectors[CORNER_FAR_TOPRIGHT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_TOPRIGHT]); LimitFrustumVector(vectors[CORNER_FAR_BOTRIGHT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_BOTRIGHT]); LimitFrustumVector(vectors[CORNER_FAR_BOTLEFT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_BOTLEFT]); x1 = CWorld::GetSectorIndexX(rect.left); if(x1 < 0) x1 = 0; x2 = CWorld::GetSectorIndexX(rect.right); if(x2 >= NUMSECTORS_X-1) x2 = NUMSECTORS_X-1; y1 = CWorld::GetSectorIndexY(rect.top); if(y1 < 0) y1 = 0; y2 = CWorld::GetSectorIndexY(rect.bottom); if(y2 >= NUMSECTORS_Y-1) y2 = NUMSECTORS_Y-1; for(; x1 <= x2; x1++) for(int y = y1; y <= y2; y++) ScanSectorList(CWorld::GetSector(x1, y)->m_lists); }else{ #ifdef GTA_TRAIN CVehicle *train = FindPlayerTrain(); if(train && train->GetPosition().z < 0.0f){ poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x); poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y); poly[1].x = CWorld::GetSectorX(vectors[CORNER_LOD_LEFT].x); poly[1].y = CWorld::GetSectorY(vectors[CORNER_LOD_LEFT].y); poly[2].x = CWorld::GetSectorX(vectors[CORNER_LOD_RIGHT].x); poly[2].y = CWorld::GetSectorY(vectors[CORNER_LOD_RIGHT].y); ScanSectorPoly(poly, 3, ScanSectorList_Subway); }else #endif { if(f > LOD_DISTANCE){ // priority poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x); poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y); poly[1].x = CWorld::GetSectorX(vectors[CORNER_PRIO_LEFT].x); poly[1].y = CWorld::GetSectorY(vectors[CORNER_PRIO_LEFT].y); poly[2].x = CWorld::GetSectorX(vectors[CORNER_PRIO_RIGHT].x); poly[2].y = CWorld::GetSectorY(vectors[CORNER_PRIO_RIGHT].y); ScanSectorPoly(poly, 3, ScanSectorList_Priority); // below LOD poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x); poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y); poly[1].x = CWorld::GetSectorX(vectors[CORNER_LOD_LEFT].x); poly[1].y = CWorld::GetSectorY(vectors[CORNER_LOD_LEFT].y); poly[2].x = CWorld::GetSectorX(vectors[CORNER_LOD_RIGHT].x); poly[2].y = CWorld::GetSectorY(vectors[CORNER_LOD_RIGHT].y); ScanSectorPoly(poly, 3, ScanSectorList); }else{ poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x); poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y); poly[1].x = CWorld::GetSectorX(vectors[CORNER_FAR_TOPLEFT].x); poly[1].y = CWorld::GetSectorY(vectors[CORNER_FAR_TOPLEFT].y); poly[2].x = CWorld::GetSectorX(vectors[CORNER_FAR_TOPRIGHT].x); poly[2].y = CWorld::GetSectorY(vectors[CORNER_FAR_TOPRIGHT].y); ScanSectorPoly(poly, 3, ScanSectorList); } ScanBigBuildingList(CWorld::GetBigBuildingList(CGame::currLevel)); ScanBigBuildingList(CWorld::GetBigBuildingList(LEVEL_GENERIC)); } } } void CRenderer::RequestObjectsInFrustum(void) { float f = RwCameraGetFarClipPlane(TheCamera.m_pRwCamera); RwV2d vw = *RwCameraGetViewWindow(TheCamera.m_pRwCamera); CVector vectors[9]; RwMatrix *cammatrix; RwV2d poly[3]; memset(vectors, 0, sizeof(vectors)); vectors[CORNER_FAR_TOPLEFT].x = -vw.x * f; vectors[CORNER_FAR_TOPLEFT].y = vw.y * f; vectors[CORNER_FAR_TOPLEFT].z = f; vectors[CORNER_FAR_TOPRIGHT].x = vw.x * f; vectors[CORNER_FAR_TOPRIGHT].y = vw.y * f; vectors[CORNER_FAR_TOPRIGHT].z = f; vectors[CORNER_FAR_BOTRIGHT].x = vw.x * f; vectors[CORNER_FAR_BOTRIGHT].y = -vw.y * f; vectors[CORNER_FAR_BOTRIGHT].z = f; vectors[CORNER_FAR_BOTLEFT].x = -vw.x * f; vectors[CORNER_FAR_BOTLEFT].y = -vw.y * f; vectors[CORNER_FAR_BOTLEFT].z = f; cammatrix = RwFrameGetMatrix(RwCameraGetFrame(TheCamera.m_pRwCamera)); CWorld::AdvanceCurrentScanCode(); ms_vecCameraPosition = TheCamera.GetPosition(); if(cammatrix->at.z > 0.0f){ // looking up, bottom corners are further away vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_BOTLEFT] * LOD_DISTANCE/f; vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_BOTRIGHT] * LOD_DISTANCE/f; }else{ // looking down, top corners are further away vectors[CORNER_LOD_LEFT] = vectors[CORNER_FAR_TOPLEFT] * LOD_DISTANCE/f; vectors[CORNER_LOD_RIGHT] = vectors[CORNER_FAR_TOPRIGHT] * LOD_DISTANCE/f; } vectors[CORNER_PRIO_LEFT].x = vectors[CORNER_LOD_LEFT].x * 0.2f; vectors[CORNER_PRIO_LEFT].y = vectors[CORNER_LOD_LEFT].y * 0.2f; vectors[CORNER_PRIO_LEFT].z = vectors[CORNER_LOD_LEFT].z; vectors[CORNER_PRIO_RIGHT].x = vectors[CORNER_LOD_RIGHT].x * 0.2f; vectors[CORNER_PRIO_RIGHT].y = vectors[CORNER_LOD_RIGHT].y * 0.2f; vectors[CORNER_PRIO_RIGHT].z = vectors[CORNER_LOD_RIGHT].z; RwV3dTransformPoints((RwV3d*)vectors, (RwV3d*)vectors, 9, cammatrix); if(TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN || #ifdef FIX_BUGS TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_GTACLASSIC || #endif TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOP_DOWN_PED){ CRect rect; int x1, x2, y1, y2; LimitFrustumVector(vectors[CORNER_FAR_TOPLEFT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_TOPLEFT]); LimitFrustumVector(vectors[CORNER_FAR_TOPRIGHT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_TOPRIGHT]); LimitFrustumVector(vectors[CORNER_FAR_BOTRIGHT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_BOTRIGHT]); LimitFrustumVector(vectors[CORNER_FAR_BOTLEFT], vectors[CORNER_CAM], -100.0f); rect.ContainPoint(vectors[CORNER_FAR_BOTLEFT]); x1 = CWorld::GetSectorIndexX(rect.left); if(x1 < 0) x1 = 0; x2 = CWorld::GetSectorIndexX(rect.right); if(x2 >= NUMSECTORS_X-1) x2 = NUMSECTORS_X-1; y1 = CWorld::GetSectorIndexY(rect.top); if(y1 < 0) y1 = 0; y2 = CWorld::GetSectorIndexY(rect.bottom); if(y2 >= NUMSECTORS_Y-1) y2 = NUMSECTORS_Y-1; for(; x1 <= x2; x1++) for(int y = y1; y <= y2; y++) ScanSectorList_RequestModels(CWorld::GetSector(x1, y)->m_lists); }else{ poly[0].x = CWorld::GetSectorX(vectors[CORNER_CAM].x); poly[0].y = CWorld::GetSectorY(vectors[CORNER_CAM].y); poly[1].x = CWorld::GetSectorX(vectors[CORNER_LOD_LEFT].x); poly[1].y = CWorld::GetSectorY(vectors[CORNER_LOD_LEFT].y); poly[2].x = CWorld::GetSectorX(vectors[CORNER_LOD_RIGHT].x); poly[2].y = CWorld::GetSectorY(vectors[CORNER_LOD_RIGHT].y); ScanSectorPoly(poly, 3, ScanSectorList_RequestModels); } } float CalcNewDelta(RwV2d *a, RwV2d *b) { return (b->x - a->x) / (b->y - a->y); } #ifdef FIX_BUGS #define TOINT(x) ((int)Floor(x)) #else #define TOINT(x) ((int)(x)) #endif void CRenderer::ScanSectorPoly(RwV2d *poly, int32 numVertices, void (*scanfunc)(CPtrList *)) { float miny, maxy; int y, yend; int x, xstart, xend; int i; int a1, a2, b1, b2; float deltaA, deltaB; float xA, xB; miny = poly[0].y; maxy = poly[0].y; a2 = 0; xstart = 9999; xend = -9999; for(i = 1; i < numVertices; i++){ if(poly[i].y > maxy) maxy = poly[i].y; if(poly[i].y < miny){ miny = poly[i].y; a2 = i; } } y = TOINT(miny); yend = TOINT(maxy); // Go left in poly to find first edge b b2 = a2; for(i = 0; i < numVertices; i++){ b1 = b2--; if(b2 < 0) b2 = numVertices-1; if(poly[b1].x < xstart) xstart = TOINT(poly[b1].x); if(TOINT(poly[b1].y) != TOINT(poly[b2].y)) break; } // Go right to find first edge a for(i = 0; i < numVertices; i++){ a1 = a2++; if(a2 == numVertices) a2 = 0; if(poly[a1].x > xend) xend = TOINT(poly[a1].x); if(TOINT(poly[a1].y) != TOINT(poly[a2].y)) break; } // prestep x1 and x2 to next integer y deltaA = CalcNewDelta(&poly[a1], &poly[a2]); xA = deltaA * (Ceil(poly[a1].y) - poly[a1].y) + poly[a1].x; deltaB = CalcNewDelta(&poly[b1], &poly[b2]); xB = deltaB * (Ceil(poly[b1].y) - poly[b1].y) + poly[b1].x; if(y != yend){ if(deltaB < 0.0f && TOINT(xB) < xstart) xstart = TOINT(xB); if(deltaA >= 0.0f && TOINT(xA) > xend) xend = TOINT(xA); } while(y <= yend && y < NUMSECTORS_Y){ // scan one x-line if(y >= 0 && xstart < NUMSECTORS_X) for(x = xstart; x <= xend && x != NUMSECTORS_X; x++) if(x >= 0) scanfunc(CWorld::GetSector(x, y)->m_lists); // advance one scan line y++; xA += deltaA; xB += deltaB; // update left side if(y == TOINT(poly[b2].y)){ // reached end of edge if(y == yend){ if(deltaB < 0.0f){ do{ xstart = TOINT(poly[b2--].x); if(b2 < 0) b2 = numVertices-1; }while(xstart > TOINT(poly[b2].x)); }else xstart = TOINT(xB - deltaB); }else{ // switch edges if(deltaB < 0.0f) xstart = TOINT(poly[b2].x); else xstart = TOINT(xB - deltaB); do{ b1 = b2--; if(b2 < 0) b2 = numVertices-1; if(TOINT(poly[b1].x) < xstart) xstart = TOINT(poly[b1].x); }while(y == TOINT(poly[b2].y)); deltaB = CalcNewDelta(&poly[b1], &poly[b2]); xB = deltaB * (Ceil(poly[b1].y) - poly[b1].y) + poly[b1].x; if(deltaB < 0.0f && TOINT(xB) < xstart) xstart = TOINT(xB); } }else{ if(deltaB < 0.0f) xstart = TOINT(xB); else xstart = TOINT(xB - deltaB); } // update right side if(y == TOINT(poly[a2].y)){ // reached end of edge if(y == yend){ if(deltaA < 0.0f) xend = TOINT(xA - deltaA); else{ do{ xend = TOINT(poly[a2++].x); if(a2 == numVertices) a2 = 0; }while(xend < TOINT(poly[a2].x)); } }else{ // switch edges if(deltaA < 0.0f) xend = TOINT(xA - deltaA); else xend = TOINT(poly[a2].x); do{ a1 = a2++; if(a2 == numVertices) a2 = 0; if(TOINT(poly[a1].x) > xend) xend = TOINT(poly[a1].x); }while(y == TOINT(poly[a2].y)); deltaA = CalcNewDelta(&poly[a1], &poly[a2]); xA = deltaA * (Ceil(poly[a1].y) - poly[a1].y) + poly[a1].x; if(deltaA >= 0.0f && TOINT(xA) > xend) xend = TOINT(xA); } }else{ if(deltaA < 0.0f) xend = TOINT(xA - deltaA); else xend = TOINT(xA); } } } void CRenderer::InsertEntityIntoList(CEntity *ent) { #ifdef NEW_RENDERER // TODO: there are more flags being checked here if(gbNewRenderer && (ent->IsVehicle() || ent->IsPed())) ms_aVisibleVehiclePtrs[ms_nNoOfVisibleVehicles++] = ent; else if(gbNewRenderer && ent->IsBuilding()) ms_aVisibleBuildingPtrs[ms_nNoOfVisibleBuildings++] = ent; else #endif ms_aVisibleEntityPtrs[ms_nNoOfVisibleEntities++] = ent; } void CRenderer::ScanBigBuildingList(CPtrList &list) { CPtrNode *node; CEntity *ent; int vis; int f = CTimer::GetFrameCounter() & 3; for(node = list.first; node; node = node->next){ ent = (CEntity*)node->item; if(ent->bOffscreen || (ent->m_randomSeed&3) != f){ ent->bOffscreen = true; vis = SetupBigBuildingVisibility(ent); }else vis = VIS_VISIBLE; switch(vis){ case VIS_VISIBLE: InsertEntityIntoList(ent); ent->bOffscreen = false; break; case VIS_STREAMME: if(!CStreaming::ms_disableStreaming) CStreaming::RequestModel(ent->GetModelIndex(), 0); break; } } } void CRenderer::ScanSectorList(CPtrList *lists) { CPtrNode *node; CPtrList *list; CEntity *ent; int i; float dx, dy; for(i = 0; i < NUMSECTORENTITYLISTS; i++){ list = &lists[i]; for(node = list->first; node; node = node->next){ ent = (CEntity*)node->item; if(ent->m_scanCode == CWorld::GetCurrentScanCode()) continue; // already seen ent->m_scanCode = CWorld::GetCurrentScanCode(); ent->bOffscreen = false; switch(SetupEntityVisibility(ent)){ case VIS_VISIBLE: InsertEntityIntoList(ent); break; case VIS_INVISIBLE: if(!IsGlass(ent->GetModelIndex())) break; // fall through case VIS_OFFSCREEN: ent->bOffscreen = true; dx = ms_vecCameraPosition.x - ent->GetPosition().x; dy = ms_vecCameraPosition.y - ent->GetPosition().y; if(dx > -30.0f && dx < 30.0f && dy > -30.0f && dy < 30.0f && ms_nNoOfInVisibleEntities < NUMINVISIBLEENTITIES - 1) ms_aInVisibleEntityPtrs[ms_nNoOfInVisibleEntities++] = ent; break; case VIS_STREAMME: if(!CStreaming::ms_disableStreaming) if(!m_loadingPriority || CStreaming::ms_numModelsRequested < 10) CStreaming::RequestModel(ent->GetModelIndex(), 0); break; } } } } void CRenderer::ScanSectorList_Priority(CPtrList *lists) { CPtrNode *node; CPtrList *list; CEntity *ent; int i; float dx, dy; for(i = 0; i < NUMSECTORENTITYLISTS; i++){ list = &lists[i]; for(node = list->first; node; node = node->next){ ent = (CEntity*)node->item; if(ent->m_scanCode == CWorld::GetCurrentScanCode()) continue; // already seen ent->m_scanCode = CWorld::GetCurrentScanCode(); ent->bOffscreen = false; switch(SetupEntityVisibility(ent)){ case VIS_VISIBLE: InsertEntityIntoList(ent); break; case VIS_INVISIBLE: if(!IsGlass(ent->GetModelIndex())) break; // fall through case VIS_OFFSCREEN: ent->bOffscreen = true; dx = ms_vecCameraPosition.x - ent->GetPosition().x; dy = ms_vecCameraPosition.y - ent->GetPosition().y; if(dx > -30.0f && dx < 30.0f && dy > -30.0f && dy < 30.0f && ms_nNoOfInVisibleEntities < NUMINVISIBLEENTITIES - 1) ms_aInVisibleEntityPtrs[ms_nNoOfInVisibleEntities++] = ent; break; case VIS_STREAMME: if(!CStreaming::ms_disableStreaming){ CStreaming::RequestModel(ent->GetModelIndex(), 0); if(CStreaming::ms_aInfoForModel[ent->GetModelIndex()].m_loadState != STREAMSTATE_LOADED) m_loadingPriority = true; } break; } } } } #ifdef GTA_TRAIN void CRenderer::ScanSectorList_Subway(CPtrList *lists) { CPtrNode *node; CPtrList *list; CEntity *ent; int i; float dx, dy; for(i = 0; i < NUMSECTORENTITYLISTS; i++){ list = &lists[i]; for(node = list->first; node; node = node->next){ ent = (CEntity*)node->item; if(ent->m_scanCode == CWorld::GetCurrentScanCode()) continue; // already seen ent->m_scanCode = CWorld::GetCurrentScanCode(); ent->bOffscreen = false; switch(SetupEntityVisibility(ent)){ case VIS_VISIBLE: InsertEntityIntoList(ent); break; case VIS_OFFSCREEN: ent->bOffscreen = true; dx = ms_vecCameraPosition.x - ent->GetPosition().x; dy = ms_vecCameraPosition.y - ent->GetPosition().y; if(dx > -30.0f && dx < 30.0f && dy > -30.0f && dy < 30.0f && ms_nNoOfInVisibleEntities < NUMINVISIBLEENTITIES - 1) ms_aInVisibleEntityPtrs[ms_nNoOfInVisibleEntities++] = ent; break; } } } } #endif void CRenderer::ScanSectorList_RequestModels(CPtrList *lists) { CPtrNode *node; CPtrList *list; CEntity *ent; int i; for(i = 0; i < NUMSECTORENTITYLISTS; i++){ list = &lists[i]; for(node = list->first; node; node = node->next){ ent = (CEntity*)node->item; if(ent->m_scanCode == CWorld::GetCurrentScanCode()) continue; // already seen ent->m_scanCode = CWorld::GetCurrentScanCode(); if(ShouldModelBeStreamed(ent, ms_vecCameraPosition)) CStreaming::RequestModel(ent->GetModelIndex(), 0); } } } // Put big buildings in front // This seems pointless because the sector lists shouldn't have big buildings in the first place void CRenderer::SortBIGBuildings(void) { int x, y; for(y = 0; y < NUMSECTORS_Y; y++) for(x = 0; x < NUMSECTORS_X; x++){ SortBIGBuildingsForSectorList(&CWorld::GetSector(x, y)->m_lists[ENTITYLIST_BUILDINGS]); SortBIGBuildingsForSectorList(&CWorld::GetSector(x, y)->m_lists[ENTITYLIST_BUILDINGS_OVERLAP]); } } void CRenderer::SortBIGBuildingsForSectorList(CPtrList *list) { CPtrNode *node; CEntity *ent; for(node = list->first; node; node = node->next){ ent = (CEntity*)node->item; if(ent->bIsBIGBuilding){ list->RemoveNode(node); list->InsertNode(node); } } } bool CRenderer::ShouldModelBeStreamed(CEntity *ent, const CVector &campos) { if(!IsAreaVisible(ent->m_area)) return false; CTimeModelInfo *mi = (CTimeModelInfo *)CModelInfo::GetModelInfo(ent->GetModelIndex()); if(mi->GetModelType() == MITYPE_TIME) if(!CClock::GetIsTimeInRange(mi->GetTimeOn(), mi->GetTimeOff())) return false; float dist = (ent->GetPosition() - campos).Magnitude(); if(mi->m_noFade) return dist - STREAM_DISTANCE < mi->GetLargestLodDistance(); else return dist - FADE_DISTANCE - STREAM_DISTANCE < mi->GetLargestLodDistance(); } void CRenderer::RemoveVehiclePedLights(CEntity *ent, bool reset) { if(!ent->bRenderScorched){ CPointLights::RemoveLightsAffectingObject(); if(reset) ReSetAmbientAndDirectionalColours(); } SetAmbientColours(); DeActivateDirectional(); }