2269 lines
73 KiB
C++
2269 lines
73 KiB
C++
// Title : PostScan.cpp
|
|
// Author : John Whyte
|
|
// Started : 4/8/2009
|
|
|
|
// header stuff
|
|
#include "renderer/PostScan.h"
|
|
|
|
// Framework headers
|
|
#include "fwanimation/directorcomponentmotiontree.h"
|
|
#include "fwrenderer/RenderListBuilder.h"
|
|
#include "fwrenderer/renderlistgroup.h"
|
|
#include "fwscene/scan/Scan.h"
|
|
#include "fwscene/scan/ScanDebug.h"
|
|
#include "fwscene/world/WorldRepMulti.h"
|
|
#include "streaming/streamingvisualize.h"
|
|
|
|
// Rage headers
|
|
#include "profile/element.h"
|
|
#include "profile/group.h"
|
|
#include "profile/page.h"
|
|
#include "profile/timebars.h"
|
|
#include "system/membarrier.h"
|
|
#include "system/spinlockedobject.h"
|
|
|
|
// game includes
|
|
#include "camera/camInterface.h"
|
|
#include "camera/base/BaseCamera.h"
|
|
#include "camera/debug/DebugDirector.h"
|
|
#include "camera/viewports/ViewportManager.h"
|
|
|
|
#include "control/replay/replay.h"
|
|
#include "cutscene/CutSceneManagerNew.h"
|
|
#include "debug/Rendering/DebugLighting.h"
|
|
#include "debug/Rendering/DebugLights.h"
|
|
#include "objects/objectpopulation.h"
|
|
#include "objects/DummyObject.h"
|
|
#include "modelinfo/MloModelInfo.h"
|
|
#include "modelinfo/TimeModelInfo.h"
|
|
#include "peds/ped.h"
|
|
#include "renderer/Mirrors.h"
|
|
#include "renderer/RenderPhases/RenderPhaseParaboloidShadows.h"
|
|
#include "renderer/RenderPhases/RenderPhaseWaterReflection.h"
|
|
#include "renderer/RenderPhases/RenderPhaseReflection.h"
|
|
#include "renderer/RenderPhases/RenderPhaseCascadeShadows.h"
|
|
#include "renderer/Renderphases/RenderPhaseMirrorReflection.h"
|
|
#include "renderer/shadows/shadows.h"
|
|
#include "renderer/Lights/LightEntity.h"
|
|
#include "renderer/Lights/LODLights.h"
|
|
#include "renderer/renderListGroup.h"
|
|
#include "scene/lod/LodMgr.h"
|
|
#include "scene/portals/Portal.h"
|
|
#include "scene/portals/PortalInst.h"
|
|
#include "scene/streamer/SceneStreamerList.h"
|
|
#include "scene/streamer/SceneStreamerMgr.h"
|
|
#include "scene/streamer/StreamVolume.h"
|
|
#include "scene/texLod.h"
|
|
#include "scene/world/GameScan.h"
|
|
#include "scene/world/GameWorld.h"
|
|
#include "scene/AnimatedBuilding.h"
|
|
#include "scene/DynamicEntity.h"
|
|
#include "streaming/streaming.h"
|
|
#include "streaming/streamingdebug.h"
|
|
#include "streaming/streamingrequestlist.h"
|
|
#include "timecycle/TimeCycleAsyncQueryMgr.h"
|
|
#include "vfx/misc/DistantLights.h"
|
|
#include "control/replay/ReplayLightingManager.h"
|
|
|
|
#if __BANK
|
|
#include "debug/GtaPicker.h"
|
|
#include "debug/TiledScreenCapture.h"
|
|
#include "scene/debug/PostScanDebug.h"
|
|
#include "scene/lod/LodDebug.h"
|
|
#include "scene/world/GameScan.h"
|
|
#include "fwscene/world/InteriorSceneGraphNode.h"
|
|
#include "fwscene/world/SceneGraphVisitor.h"
|
|
#endif //__BANK
|
|
|
|
#define PRERENDER_VEHICLE_SPHERE_RADIUS (50)
|
|
#define PRERENDER_PED_AND_OBJECT_SPHERE_RADIUS (20)
|
|
#define ALPHA_CULL_THRESHOLD (50)
|
|
|
|
#if __DEV
|
|
#define INVALID_PVS_ENTRY_PATTERN 0xDF
|
|
#define INVALID_PVS_ENTRY_POINTER_VALUE 0xDFDFDFDF
|
|
#endif
|
|
|
|
#define __CHECK_FOR_DUPLICATES 0 //__DEV
|
|
|
|
RENDER_OPTIMISATIONS()
|
|
SCENE_OPTIMISATIONS()
|
|
|
|
// init
|
|
CPostScan gPostScan;
|
|
CPostScanExteriorHdCull g_PostScanCull;
|
|
|
|
static fwSearch ms_PreRenderVehicleSearch(150);
|
|
static fwSearch ms_PreRenderPedAndObjectSearch(1024);
|
|
|
|
#define MAX_ALPHA_OVERRIDES (256)
|
|
|
|
struct alphaOverride {
|
|
RegdEnt entity;
|
|
int alpha;
|
|
|
|
#if __BANK
|
|
atString owner;
|
|
#endif
|
|
};
|
|
static alphaOverride ms_alphaOverrides[MAX_ALPHA_OVERRIDES];
|
|
static bool ms_hasAnyAlphaOverride = false;
|
|
static int ms_lastAlphaOverride = 0;
|
|
|
|
struct entitySortBias {
|
|
RegdEnt entity;
|
|
float bias;
|
|
};
|
|
static atArray<entitySortBias> ms_sortBiases;
|
|
|
|
bool BuildRenderListsFromPVS_Dependency(const sysDependency& dependency);
|
|
bool SortPVSAsync_Dependency(const sysDependency& dependency);
|
|
|
|
sysDependency g_BuildRenderListsFromPVSDependency;
|
|
sysDependency g_SortPVSDependency;
|
|
u32 g_BuildRenderListsFromPVSDependencyRunning;
|
|
u32 g_SortPVSDependencyRunning;
|
|
|
|
bool CPostScan::ms_bAsyncSearchStarted = false;
|
|
bool CPostScan::ms_bAllowLateLightEntities = false;
|
|
spdSphere CPostScan::ms_LateLightsNearCameraSphere;
|
|
atArray< CEntity*> CPostScan::ms_lateLights;
|
|
|
|
void ForceUpLinkedInteriors(fwInteriorLocation intLoc, CInteriorInst *pIntInst, const spdTransposedPlaneSet8 &frustum, atArray<fwInteriorLocation> &visitedLocs);
|
|
|
|
//
|
|
// name: CPostScan::CPostScan
|
|
// description: CPostScan constructor
|
|
//
|
|
CPostScan::CPostScan()
|
|
{
|
|
fwRenderListBuilder::SetFlushPrototypesCallback(CDrawListPrototypeManager::Flush);
|
|
fwRenderListBuilder::EnableScissoringExterior(true);
|
|
|
|
m_nonVisiblePreRenderList.Init(1024);
|
|
|
|
ms_lateLights.Reset();
|
|
|
|
#if !__FINAL
|
|
m_EntityCount = 0;
|
|
#endif
|
|
|
|
}
|
|
|
|
//
|
|
// name: CPostScan::Init
|
|
// description: Initialise CPostScan structures
|
|
//
|
|
void CPostScan::Init(unsigned /*initMode*/)
|
|
{
|
|
// need to allocate this once code has all moved out of renderer.cpp
|
|
//gRenderListGroup.Init();
|
|
|
|
#if __PPU
|
|
// We're using a 64-bit DMA on PS3, so this pointer needs to be aligned accordingly.
|
|
CompileTimeAssert((OffsetOf(CPostScan, m_ResultBlocks) & 0x7) == 0);
|
|
#endif // __PPU
|
|
sysMemSet( ms_alphaOverrides, 0, sizeof(alphaOverride) * MAX_ALPHA_OVERRIDES );
|
|
ms_hasAnyAlphaOverride = false;
|
|
ms_lastAlphaOverride = 0;
|
|
//@@: location CPOSTSCAN_INIT
|
|
g_SortPVSDependency.Init( SortPVSAsync_Dependency, 0, sysDepFlag::INPUT0 );
|
|
g_SortPVSDependency.m_Priority = sysDependency::kPriorityCritical;
|
|
|
|
g_BuildRenderListsFromPVSDependency.Init( BuildRenderListsFromPVS_Dependency, 0, 0 );
|
|
g_BuildRenderListsFromPVSDependency.m_Priority = sysDependency::kPriorityLow;
|
|
|
|
g_PostScanCull.Init();
|
|
}
|
|
|
|
void CPostScan::Shutdown(unsigned /*initMode*/)
|
|
{
|
|
}
|
|
namespace PostScanHelperStats
|
|
{
|
|
PF_PAGE(PostScanHelperPage,"PostScanHelper");
|
|
|
|
PF_GROUP(PostScanHelper);
|
|
PF_LINK(PostScanHelperPage, PostScanHelper);
|
|
PF_TIMER(BuildRenderLists, PostScanHelper);
|
|
PF_TIMER(SortRenderLists, PostScanHelper);
|
|
}
|
|
using namespace PostScanHelperStats;
|
|
|
|
//
|
|
// name: CPostScan::Reset
|
|
// description: Reset the ptrs to the start of the buffers ready for new frame
|
|
//
|
|
void CPostScan::Reset()
|
|
{
|
|
#if 0
|
|
sysMemSet( m_PVS, INVALID_PVS_ENTRY_PATTERN, sizeof(CGtaPvsEntry) * MAX_PVS_SIZE );
|
|
sysMemSet( m_ResultBlocks, INVALID_PVS_ENTRY_PATTERN, sizeof(m_ResultBlocks) );
|
|
#endif
|
|
m_pCurrPVSEntry = &m_PVS[0];
|
|
m_NextResultBlock = 0;
|
|
m_ResultBlockCount = 0;
|
|
}
|
|
|
|
void CPostScan::BuildRenderListsNew()
|
|
{
|
|
//Performing the LOD Lights Bucket Visibility here as
|
|
//we would have the updated scan results flag by this point
|
|
PF_PUSH_TIMEBAR("LOD/Distant LOD Lights Bucket Visibility");
|
|
CLODLightManager::BuildDistLODLightsVisibility(gVpMan.GetGameViewport());
|
|
CLODLightManager::BuildLODLightsBucketVisibility(gVpMan.GetGameViewport());
|
|
PF_POP_TIMEBAR();
|
|
|
|
ProcessPVSForRenderPhase();
|
|
|
|
// handle all of the pre-rendering of entities which require it...
|
|
CRenderer::PreRenderEntities();
|
|
|
|
#if __BANK
|
|
CPostScanDebug::ProcessAfterAllPreRendering();
|
|
DebugLights::UpdateLights();
|
|
#endif
|
|
}
|
|
|
|
#if SCL_LOGGING
|
|
bool g_forceWait = false;
|
|
#endif
|
|
|
|
__forceinline void StartPreRenderForEntity(CEntity * entity, const bool bIsVisibleInMainViewport = true)
|
|
{
|
|
if ( entity &&
|
|
entity->GetIsTypeLight() == false &&
|
|
entity->IsBaseFlagSet( fwEntity::HAS_PRERENDER ) )
|
|
{
|
|
Assert(!entity->GetPreRenderedThisFrame());
|
|
|
|
const bool preRenderLater = !entity->GetDrawable() || (entity->GetAttachParent() != NULL);
|
|
|
|
if ( preRenderLater )
|
|
gRenderListGroup.GetPreRenderList().AddEntity( entity );
|
|
else
|
|
{
|
|
#if __BANK
|
|
CPostScanDebug::ProcessPreRenderEntity(entity);
|
|
#endif
|
|
|
|
const ePrerenderStatus preRenderStatus = entity->PreRender(bIsVisibleInMainViewport);
|
|
if ( preRenderStatus != PRERENDER_DONE )
|
|
gRenderListGroup.GetPreRender2List().AddEntity( entity );
|
|
|
|
gRenderListGroup.GetAlreadyPreRenderedList().AddEntity( entity );
|
|
|
|
ASSERT_ONLY( entity->SetPreRenderedThisFrame(true); )
|
|
BANK_ONLY( CPostScanDebug::GetNumModelsPreRenderedRef() += 1; )
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CPostScan::RejectTimedEntity(CEntity* RESTRICT pEntity, const bool visibleInGbuf)
|
|
{
|
|
#if __BANK
|
|
if ( gStreamingRequestList.IsRecording() && !gStreamingRequestList.IsSkipTimedEntities() )
|
|
{
|
|
return false;
|
|
}
|
|
#endif // !__FINAL
|
|
|
|
CBaseModelInfo* pModelInfo = pEntity->GetBaseModelInfo();
|
|
Assert(pModelInfo->GetModelType()==MI_TYPE_TIME);
|
|
|
|
CTimeInfo* pTimeInfo = pModelInfo->GetTimeInfo();
|
|
Assert(pTimeInfo);
|
|
//u32 otherModel = pTimeInfo->GetOtherTimeModel();
|
|
|
|
int game_hour = CClock::GetHour();
|
|
int game_minute = CClock::GetMinute();
|
|
|
|
if (!pTimeInfo->OnlySwapWhenOffScreen())
|
|
{
|
|
// allowed to switch on when visible so are probably lights so add some noise to
|
|
// the time so all the buildings don't come on together
|
|
const u32 noiseFactor = (( pEntity->GetLodData().IsOrphanHd() ? 0 : g_LodMgr.GetHierarchyNoiseFactor(pEntity) ) >> 2) & 31;
|
|
|
|
game_minute += noiseFactor;
|
|
|
|
while(game_minute > 60)
|
|
{
|
|
game_minute -= 60;
|
|
game_hour++;
|
|
}
|
|
if(game_hour > 23)
|
|
{
|
|
game_hour = 0;
|
|
}
|
|
}
|
|
|
|
if ( !visibleInGbuf || !pTimeInfo->OnlySwapWhenOffScreen() )
|
|
{
|
|
const bool isOn = pTimeInfo->IsOn(game_hour);
|
|
pEntity->SetTimedEntityDelayedState( isOn );
|
|
return isOn == false;
|
|
}
|
|
else
|
|
{
|
|
return pEntity->GetTimedEntityDelayedState() == false;
|
|
}
|
|
|
|
}
|
|
|
|
void CPostScan::ProcessResultBlock(const int blockIndex, const u32 viewportBits, const u32 lastVisibleFrame)
|
|
{
|
|
fwScanResultBlock& resultBlock = m_ResultBlocks[blockIndex];
|
|
|
|
CGtaPvsEntry* entry = (CGtaPvsEntry*) resultBlock.m_PvsEntry;
|
|
CGtaPvsEntry* lastEntry = entry + resultBlock.m_EntryCount;
|
|
|
|
Assertf(entry != (CGtaPvsEntry*)INVALID_PVS_ENTRY_POINTER_VALUE, "Entry %d isn't valid yet (at %p)", blockIndex, entry);
|
|
|
|
CStreamingLists &lists = g_SceneStreamerMgr.GetStreamingLists();
|
|
const u32 streamMode = lists.GetStreamMode();
|
|
|
|
for (; entry != lastEntry; entry++)
|
|
{
|
|
CEntity* entity = reinterpret_cast<CEntity*>( entry->GetEntity() );
|
|
|
|
#if __BANK
|
|
// trace selected entity
|
|
if (CPostScanDebug::ms_breakIfEntityIsInPVS && entity == g_PickerManager.GetSelectedEntity())
|
|
{
|
|
__debugbreak();
|
|
}
|
|
if (CPostScanDebug::ms_renderVisFlagsForSelectedEntity && g_PickerManager.GetSelectedEntity())
|
|
{
|
|
fwEntity *pSelectedEntity = g_PickerManager.GetSelectedEntity();
|
|
if (pSelectedEntity)
|
|
{
|
|
if (entity == pSelectedEntity)
|
|
{
|
|
int index = TIME.GetFrameCount() & 1;
|
|
CPostScanDebug::ms_pvsEntryForSelectedEntity[index] = *entry;
|
|
CPostScanDebug::ms_pvsEntryEntityRenderPhaseVisibilityMask[index] = entry->GetEntity()->GetRenderPhaseVisibilityMask().GetAllFlags();
|
|
}
|
|
}
|
|
}
|
|
#endif // __BANK
|
|
|
|
const fwVisibilityFlags& visFlags = entry->GetVisFlags();
|
|
CEntityDrawHandler* drawHandler = (CEntityDrawHandler*) entity->GetDrawHandlerPtr();
|
|
PrefetchDC(drawHandler);
|
|
|
|
const bool isVisibleInGBufOrMirror = (visFlags & viewportBits) != 0;
|
|
|
|
// Prefetch the entity that is two entries ahead, since some entities get rejected
|
|
// fairly quickly by being a timed entity, etc.
|
|
const CGtaPvsEntry* pNextEntryToPrefetch = entry + 2;
|
|
if(Likely(pNextEntryToPrefetch < lastEntry))
|
|
{
|
|
PrefetchDC(pNextEntryToPrefetch->GetEntity());
|
|
}
|
|
|
|
// Prefetch the next cache line of PVS entries.
|
|
PrefetchDC((char*)entry+128);
|
|
|
|
Assertf(entity != (CEntity*)INVALID_PVS_ENTRY_POINTER_VALUE, "Invalid entity in block %d at %p", blockIndex, entry);
|
|
if(Unlikely(!entity))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// If the entity is a timed entity, and isn't supposed to be visible at this time of day,
|
|
// then invalidate the visibility entry and continue.
|
|
if (entity->IsTimedEntity() && this->RejectTimedEntity(entity, isVisibleInGBufOrMirror))
|
|
{
|
|
entry->Invalidate();
|
|
|
|
entity->ReleaseImportantSlodAssets();
|
|
|
|
// rare special case - timed emissive objects with lights attached should destroy their draw handler
|
|
// when rejected, to avoid the light drawing before emissive model is loaded - B* 1337696 (IanK)
|
|
if (entity->GetDrawHandlerPtr()!=NULL && entity->m_nFlags.bLightObject)
|
|
{
|
|
entity->DeleteDrawable();
|
|
}
|
|
continue;
|
|
}
|
|
|
|
lists.ProcessPvsEntry(entry, entity, visFlags, isVisibleInGBufOrMirror, drawHandler, lastVisibleFrame, streamMode);
|
|
|
|
if(isVisibleInGBufOrMirror)
|
|
{
|
|
if(entity->GetIsDynamic())
|
|
{
|
|
static_cast<CDynamicEntity*>(entity)->SetIsVisibleInSomeViewportThisFrame(true);
|
|
}
|
|
|
|
StartPreRenderForEntity(entity);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void CPostScan::CleanAndSortPreRenderLists()
|
|
{
|
|
PF_AUTO_PUSH_TIMEBAR("Clean and Sort PreRender Lists");
|
|
|
|
fwEntityPreRenderList& alreadyPreRenderedList = gRenderListGroup.GetAlreadyPreRenderedList();
|
|
fwEntityPreRenderList& preRenderList = gRenderListGroup.GetPreRenderList();
|
|
|
|
alreadyPreRenderedList.SortAndRemoveDuplicates();
|
|
preRenderList.SortAndRemoveDuplicates();
|
|
m_nonVisiblePreRenderList.SortAndRemoveDuplicates();
|
|
|
|
preRenderList.RemoveElements( alreadyPreRenderedList );
|
|
|
|
// Now that we know the set of visible stuff that has been (or will be) pre-rendered, we can
|
|
// remove visible entities from the non-visible list, so that only non-visible entities remain.
|
|
m_nonVisiblePreRenderList.RemoveElements( preRenderList );
|
|
m_nonVisiblePreRenderList.RemoveElements( alreadyPreRenderedList );
|
|
}
|
|
|
|
void CPostScan::PreRenderNonVisibleEntities()
|
|
{
|
|
PF_AUTO_PUSH_TIMEBAR("PreRender Non-Visible Entities");
|
|
|
|
const int numEntitiesInNonVisiblePreRenderList = m_nonVisiblePreRenderList.GetCount();
|
|
for(int i = 0; i < numEntitiesInNonVisiblePreRenderList; i++)
|
|
{
|
|
CEntity* pEntity = static_cast<CEntity*>( m_nonVisiblePreRenderList.GetEntity(i) );
|
|
Assert( pEntity );
|
|
if(pEntity->GetIsVisible())
|
|
{
|
|
StartPreRenderForEntity(pEntity, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool PreRenderSearchCBVehicle(fwEntity* entity, void* spherePtr)
|
|
{
|
|
CVehicle* pEntity = static_cast<CVehicle*>(entity);
|
|
Assert(pEntity->GetIsDynamic() && pEntity->IsBaseFlagSet(fwEntity::HAS_PRERENDER));
|
|
|
|
// this entity has already been PreRendered, so we can avoid the checks below
|
|
if(pEntity->GetIsVisibleInSomeViewportThisFrame())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// this entity needs to be PreRendered (most likely because its lights are on)
|
|
if(pEntity->ShouldPreRenderBeyondSmallRadius())
|
|
{
|
|
gPostScan.AddToNonVisiblePreRenderList(pEntity);
|
|
return true;
|
|
}
|
|
|
|
// if this entity is within the smaller radius, then we need to PreRender it
|
|
const spdSphere& nearCameraSphere = *(static_cast<const spdSphere*>(spherePtr));
|
|
spdSphere entitySphere = pEntity->GetBoundSphere();
|
|
if(nearCameraSphere.IntersectsSphere(entitySphere))
|
|
{
|
|
gPostScan.AddToNonVisiblePreRenderList(pEntity);
|
|
return true;
|
|
}
|
|
|
|
// if we hit this point, then the entity does not need PreRendering
|
|
return true;
|
|
}
|
|
|
|
bool PreRenderSearchCBNonVehicle(fwEntity* entity, void*)
|
|
{
|
|
CEntity* pEntity = static_cast<CEntity*>(entity);
|
|
|
|
// The only types we should be processing here are Peds, Vehicles, and Objects, all of
|
|
// which explicitly set the HAS_PRERENDER flag. StartPreRenderForEntity() has a real
|
|
// check for HAS_PRERENDER anyways, so it won't call PreRender() on anything without
|
|
// HAS_PRERENDER set.
|
|
Assert(pEntity->GetIsDynamic() && pEntity->IsBaseFlagSet(fwEntity::HAS_PRERENDER));
|
|
|
|
gPostScan.AddToNonVisiblePreRenderList(pEntity);
|
|
return true;
|
|
}
|
|
|
|
void CPostScan::ProcessPvsWhileScanning()
|
|
{
|
|
int availableEntries = 0;
|
|
int currentEntryIndex = 0;
|
|
const u32 viewportBits = CRenderer::GetSceneToGBufferListBits() | CRenderer::GetMirrorListBits();
|
|
const u32 lastVisibleFrame = TIME.GetFrameCount();
|
|
|
|
CDynamicEntity::NextFrameForVisibility();
|
|
fwAnimDirectorComponentMotionTree::SetLockedGlobal(true, fwAnimDirectorComponent::kPhasePreRender);
|
|
|
|
#if SCL_LOGGING
|
|
sysTimer logTimer;
|
|
const float waitTime = 30.0f * 33.33333f; // 20 frames, probably less as we're not running at 30
|
|
int runTwice = 0;
|
|
#endif
|
|
#if __BANK
|
|
CPostScanDebug::ms_pvsEntryForSelectedEntity[TIME.GetFrameCount() & 1].SetEntity(NULL);
|
|
#endif
|
|
// Process the PVS entries found by visibility scan jobs that have
|
|
// already completed. If we run out of PVS entries, but scan is not yet
|
|
// finished, wait for scan to produce more PVS entries and process them.
|
|
PF_PUSH_TIMEBAR("Process PVS Entries");
|
|
|
|
// Are we moving fast? Then switch to a different stream mode.
|
|
// Completely arbitrary check to see if we're moving fast.
|
|
CStreamingLists &lists = g_SceneStreamerMgr.GetStreamingLists();
|
|
u32 streamMode = 0;
|
|
Vector3 dir = CGameWorld::FindFollowPlayerSpeed();
|
|
if (dir.Mag2() > 15.0f * 15.0f)
|
|
{
|
|
if (*lists.GetInteriorDriveModePtr())
|
|
{
|
|
streamMode |= CSceneStreamerList::DRIVING_INTERIOR;
|
|
}
|
|
|
|
if (*lists.GetDriveModePtr())
|
|
{
|
|
streamMode |= CSceneStreamerList::DRIVING_LOD;
|
|
}
|
|
}
|
|
lists.SetStreamMode(streamMode);
|
|
|
|
bool asyncWorkCompleted = false;
|
|
while (true)
|
|
{
|
|
availableEntries = sysInterlockedRead( &m_ResultBlockCount );
|
|
sysMemReadBarrier();
|
|
if ( SCL_LOGGING_ONLY(g_forceWait ||) currentEntryIndex >= availableEntries )
|
|
{
|
|
extern u32 g_scanTasksPending;
|
|
#if __WIN32PC
|
|
extern sysIpcEvent g_scanTasksReady;
|
|
#endif
|
|
|
|
if ( SCL_LOGGING_ONLY(g_forceWait ||) sysInterlockedRead(&g_scanTasksPending) > 0 )
|
|
{
|
|
#if SCL_LOGGING
|
|
if (g_forceWait)
|
|
{
|
|
sysIpcSleep(u32(waitTime) + 1);
|
|
continue;
|
|
}
|
|
#endif
|
|
PF_PUSH_TIMEBAR("Wait for more PVS Entries2");
|
|
do
|
|
{
|
|
#if __WIN32PC
|
|
WaitForSingleObject(g_scanTasksReady, 1);
|
|
#else
|
|
sysIpcSleep(0);
|
|
#endif
|
|
|
|
// Re-read the entries counter.
|
|
availableEntries = sysInterlockedRead( &m_ResultBlockCount );
|
|
} while ( sysInterlockedRead(&g_scanTasksPending) > 0 && currentEntryIndex >= availableEntries );
|
|
PF_POP_TIMEBAR();
|
|
|
|
continue;
|
|
}
|
|
else if (!asyncWorkCompleted)
|
|
{
|
|
// Must re-check m_ResultBlockCount after we see
|
|
// g_scanTasksPending go to zero, otherwise there is a race
|
|
// condition where we can miss calling pre-render on some
|
|
// entities.
|
|
asyncWorkCompleted = true;
|
|
// Read barrier here to ensure that even with out-of-order
|
|
// speculative execution, m_ResultBlockCount is read after
|
|
// g_scanTasksPending.
|
|
sysMemReadBarrier();
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// We are over the available entries count because
|
|
// there are no more jobs to process - exit the loop.
|
|
break;
|
|
}
|
|
}
|
|
|
|
ProcessResultBlock(currentEntryIndex++, viewportBits, lastVisibleFrame);
|
|
}
|
|
PF_POP_TIMEBAR();
|
|
|
|
// Scan tasks aren't checking for mem overwrite, so this is fatal.
|
|
FatalAssert(GetPVSEnd()-GetPVSBase() < MAX_PVS_SIZE);
|
|
|
|
#if __BANK
|
|
TiledScreenCapture::SetCurrentPVSSize((int)(GetPVSEnd()-GetPVSBase()));
|
|
#endif // __BANK
|
|
|
|
// Kick off the PVS sort job as soon as possible to give it the most time to finish.
|
|
StartSortPVSJob();
|
|
|
|
PF_PUSH_TIMEBAR("Gather Entities from Search and Always PreRender");
|
|
m_nonVisiblePreRenderList.Clear();
|
|
|
|
fwPtrNodeSingleLink* pNode = m_alwaysPreRenderList.GetHeadPtr();
|
|
while (pNode)
|
|
{
|
|
CEntity* pEntity = (CEntity*) pNode->GetPtr();
|
|
if (pEntity)
|
|
{
|
|
AddToNonVisiblePreRenderList(pEntity);
|
|
}
|
|
pNode = (fwPtrNodeSingleLink*) pNode->GetNextPtr();
|
|
|
|
}
|
|
|
|
if ( g_scanDebugFlags & SCAN_DEBUG_ENABLE_PRERENDER_SPHERE )
|
|
{
|
|
|
|
EndAsyncSearchForAdditionalPreRenderEntities();
|
|
spdSphere nearCameraSphere(VECTOR3_TO_VEC3V(camInterface::GetPos()), ScalarV(20.0f));
|
|
ms_PreRenderVehicleSearch.ExecuteCallbackOnResult(PreRenderSearchCBVehicle, &nearCameraSphere);
|
|
ms_PreRenderPedAndObjectSearch.ExecuteCallbackOnResult(PreRenderSearchCBNonVehicle, NULL);
|
|
}
|
|
PF_POP_TIMEBAR();
|
|
|
|
// Add peds that update animation but are not visible in viewport
|
|
CPed::Pool* pedPool = CPed::GetPool();
|
|
for(s32 i = 0; i < pedPool->GetSize(); i++)
|
|
{
|
|
CPed* pPed = pedPool->GetSlot(i);
|
|
if(pPed && pPed->GetPedAiLod().ShouldUpdateAnimsThisFrame() && !pPed->GetIsVisibleInSomeViewportThisFrame() )
|
|
{
|
|
gPostScan.AddToNonVisiblePreRenderList(pPed);
|
|
}
|
|
}
|
|
|
|
CleanAndSortPreRenderLists();
|
|
|
|
PreRenderNonVisibleEntities();
|
|
|
|
// We can only access the Scan results from the main thread, so cache the result
|
|
// here. The BuildRenderListsFromPVS dependency job will need it.
|
|
m_cameraInInterior = fwScan::GetScanResults().m_startingSceneNodeInInterior;
|
|
|
|
#if __ASSERT
|
|
PF_PUSH_TIMEBAR("Clear PreRendered flag");
|
|
fwEntityPreRenderList& alreadyPreRenderedList = gRenderListGroup.GetAlreadyPreRenderedList();
|
|
int entityCount = alreadyPreRenderedList.GetCount();
|
|
for(int i = 0; i < entityCount; i++)
|
|
{
|
|
CEntity *pEntity = (CEntity*)alreadyPreRenderedList.GetEntity(i);
|
|
if(pEntity)
|
|
pEntity->SetPreRenderedThisFrame(false);
|
|
}
|
|
PF_POP_TIMEBAR();
|
|
#endif
|
|
}
|
|
|
|
void CPostScan::ProcessPVSForRenderPhase()
|
|
{
|
|
PF_AUTO_PUSH_TIMEBAR_BUDGETED("Process PVS For RP", 1.0f);
|
|
|
|
ms_lateLights.ResetCount();
|
|
ms_bAllowLateLightEntities = true;
|
|
ms_LateLightsNearCameraSphere = spdSphere(VECTOR3_TO_VEC3V(camInterface::GetPos()), ScalarV(40.0f));
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// process scene streaming based on PVS contents
|
|
PF_PUSH_TIMEBAR("Scene streaming");
|
|
{
|
|
g_SceneStreamerMgr.Process();
|
|
CStreamVolumeMgr::UpdateVolumesLoadedState(); // TODO: move this logic into the scene streamer update
|
|
// TODO: have scene streaming write back a "loaded" flag into the pvs results?
|
|
}
|
|
PF_POP_TIMEBAR();
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
PF_PUSH_TIMEBAR("Finish PreRender");
|
|
{
|
|
FinishPreRender();
|
|
}
|
|
PF_POP_TIMEBAR();
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// update alpha values etc
|
|
PF_PUSH_TIMEBAR("Process Post Visibility");
|
|
{
|
|
ProcessPostVisibility();
|
|
}
|
|
PF_POP_TIMEBAR();
|
|
|
|
#if __BANK
|
|
PF_PUSH_TIMEBAR("Process Debug");
|
|
CPostScanDebug::ProcessDebug2(GetPVSBase(), GetPVSEnd()); // TODO look at reducing
|
|
PF_POP_TIMEBAR();
|
|
#endif // __BANK
|
|
|
|
// Remove sort biases for deleted entities
|
|
for(int i = ms_sortBiases.GetCount() - 1; i >=0; i--)
|
|
{
|
|
if(!ms_sortBiases[i].entity.Get())
|
|
{
|
|
ms_sortBiases.Delete(i);
|
|
}
|
|
}
|
|
|
|
g_BuildRenderListsFromPVSDependencyRunning = 1;
|
|
sysDependencyScheduler::Insert( &g_BuildRenderListsFromPVSDependency );
|
|
|
|
PF_PUSH_TIMEBAR_BUDGETED("Process Visible Lights", 0.4f);
|
|
//
|
|
// convert light entities into light sources for rendering
|
|
//
|
|
typedef atArray< std::pair< CEntity*, fwVisibilityFlags > >::iterator LightsIterator;
|
|
for (LightsIterator it=m_lights.begin(); it!=m_lights.end(); ++it)
|
|
{
|
|
reinterpret_cast< CLightEntity* >( it->first )->ProcessVisibleLight( it->second );
|
|
}
|
|
|
|
fwVisibilityFlags forcedVisibleFlags;
|
|
forcedVisibleFlags.Clear();
|
|
forcedVisibleFlags.SetVisBit(g_SceneToGBufferPhaseNew->GetEntityListIndex());
|
|
forcedVisibleFlags.SetVisBit(CMirrors::GetMirrorRenderPhase()->GetEntityListIndex());
|
|
|
|
for(u32 i=0; i< ms_lateLights.GetCount(); i++)
|
|
{
|
|
reinterpret_cast< CLightEntity* >(ms_lateLights[i])->ProcessVisibleLight(forcedVisibleFlags);
|
|
}
|
|
|
|
PF_POP_TIMEBAR();
|
|
|
|
PF_PUSH_TIMEBAR("Process LOD Light Visibility");
|
|
CLODLights::ProcessVisibility();
|
|
PF_POP_TIMEBAR();
|
|
|
|
BANK_ONLY(if(DebugLighting::m_DistantLightsProcessVisibilityAsync))
|
|
{
|
|
PF_PUSH_TIMEBAR("Process Distant Lights Visibility");
|
|
g_distantLights.ProcessVisibility();
|
|
PF_POP_TIMEBAR();
|
|
|
|
}
|
|
#if GTA_REPLAY
|
|
//Not sure if this is the best place to call this but needs to be somewhere so the
|
|
//drawlist gets setup correctly.
|
|
ReplayLightingManager::AddLightsToScene();
|
|
#endif
|
|
}
|
|
|
|
static int GetAttachmentDepth(const fwEntity* entity)
|
|
{
|
|
const fwEntity* current = entity;
|
|
int depth = 0;
|
|
|
|
while ( current->GetAttachParent() )
|
|
{
|
|
++depth;
|
|
current = current->GetAttachParent();
|
|
}
|
|
|
|
return depth;
|
|
}
|
|
|
|
static bool AttachmentDepthCompare(const fwEntity* a, fwEntity* const b) {
|
|
return GetAttachmentDepth(a) < GetAttachmentDepth(b);
|
|
}
|
|
|
|
static void SortByAttachmentDepth(fwEntityPreRenderList& list) {
|
|
std::sort( list.begin(), list.end(), AttachmentDepthCompare );
|
|
}
|
|
|
|
void CPostScan::FinishPreRender()
|
|
{
|
|
fwEntityPreRenderList& preRenderList = gRenderListGroup.GetPreRenderList();
|
|
|
|
PF_PUSH_TIMEBAR("SortByAttachmentDepth");
|
|
SortByAttachmentDepth( preRenderList );
|
|
PF_POP_TIMEBAR();
|
|
|
|
fwAnimDirectorComponentMotionTree::SetLockedGlobal(true, fwAnimDirectorComponent::kPhasePreRender);
|
|
|
|
for (int i = 0; i < preRenderList.GetCount(); ++i)
|
|
{
|
|
CEntity* pEntity = static_cast< CEntity* >( preRenderList.GetEntity(i) );
|
|
Assert( pEntity );
|
|
|
|
#if __BANK
|
|
CPostScanDebug::ProcessPreRenderEntity( pEntity );
|
|
#endif
|
|
// Visibility scanning is completely done by now, so GetIsVisibleInSomeViewportThisFrame()
|
|
// should return the correct visibility for the main viewport this frame - this method is
|
|
// only available on dynamic entities, however.
|
|
const bool bIsEntityVisibleInMainViewport = pEntity->GetIsDynamic() ? static_cast<CDynamicEntity*>(pEntity)->GetIsVisibleInSomeViewportThisFrame() : true;
|
|
if ( pEntity->PreRender(bIsEntityVisibleInMainViewport) != PRERENDER_DONE )
|
|
gRenderListGroup.GetPreRender2List().AddEntity( pEntity );
|
|
|
|
BANK_ONLY( CPostScanDebug::GetNumModelsPreRenderedRef() += 1; )
|
|
}
|
|
|
|
// Some DummyObjects may need to get their ambient scales updated this frame - this call
|
|
// will queue them up for async processing
|
|
CObjectPopulation::SubmitDummyObjectsForAmbientScaleCalc();
|
|
|
|
CTimeCycleAsyncQueryMgr::StartAsyncProcessing();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION: ProcessPostVisibility
|
|
// PURPOSE: runs over pvs and performs a variety of tasks including alpha
|
|
// update, marking of important dummy objects for promotion, etc
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CPostScan::ProcessPostVisibility()
|
|
{
|
|
#if __BANK
|
|
// don't update alphas if render is locked
|
|
extern bool g_render_lock;
|
|
if (g_render_lock)
|
|
return;
|
|
#endif //__BANK
|
|
|
|
WaitForSortPVSDependency();
|
|
CGtaPvsEntry* pStart = GetPVSBase();
|
|
CGtaPvsEntry* pStop = GetPVSEnd();
|
|
|
|
#if __DEV
|
|
if(CPostScanDebug::ShouldVerifyPvsSort())
|
|
{
|
|
PF_PUSH_TIMEBAR("Verify PVS Sort by LOD");
|
|
CGtaPvsEntry* RESTRICT pPvsEntry = (CGtaPvsEntry* RESTRICT) pStart;
|
|
while (pPvsEntry != pStop)
|
|
{
|
|
CGtaPvsEntry* pNextEntry = pPvsEntry + 1;
|
|
if(pNextEntry != pStop)
|
|
{
|
|
// Assert that the PVS entries are sorted in ascending LOD sort value.
|
|
Assertf(pPvsEntry->GetVisFlags().GetLodSortVal() <= pNextEntry->GetVisFlags().GetLodSortVal(),
|
|
"CPostScan::ProcessPostVisibility - PVS is not sorted in the correct LOD order!");
|
|
}
|
|
pPvsEntry = pNextEntry;
|
|
}
|
|
PF_POP_TIMEBAR();
|
|
}
|
|
#endif
|
|
|
|
#if STREAMING_VISUALIZE
|
|
if (strStreamingVisualize::IsInstantiated())
|
|
{
|
|
CGtaPvsEntry* RESTRICT pPvsEntry = (CGtaPvsEntry* RESTRICT) pStart;
|
|
while (pPvsEntry != pStop)
|
|
{
|
|
CEntity* RESTRICT pEntity = (CEntity* RESTRICT) pPvsEntry->GetEntity();
|
|
fwVisibilityFlags& visFlags = pPvsEntry->GetVisFlags();
|
|
if (pEntity && CStreamingLists::IsRequired(pEntity) && ((visFlags.GetOptionFlags() & VIS_FLAG_ONLY_STREAM_ENTITY) == 0) && visFlags.GetAlpha() && !pEntity->GetDrawHandlerPtr() )
|
|
{
|
|
STRVIS_MARK_MISSING_LOD(pEntity, false);
|
|
}
|
|
|
|
++pPvsEntry;
|
|
}
|
|
}
|
|
#endif // STREAMING_VISUALIZE
|
|
|
|
#if __BANK
|
|
PF_PUSH_TIMEBAR("Post Scan Debug 1");
|
|
CPostScanDebug::ProcessDebug1(pStart, pStop);
|
|
PF_POP_TIMEBAR(); // Post Scan Debug 1
|
|
#endif
|
|
|
|
CVehicle::ProcessPostVisibility();
|
|
|
|
PF_PUSH_TIMEBAR("Post Sort PVS Loop");
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
const bool bSlodMode = g_LodMgr.IsSlodModeActive();
|
|
const bool bAllowTimeBaseFading = g_LodMgr.AllowTimeBasedFadingThisFrame();
|
|
const u32 gbufPhaseFlag = 1 << g_SceneToGBufferPhaseNew->GetEntityListIndex();
|
|
const u32 mirrorPhaseFlag = 1 << g_MirrorReflectionPhase->GetEntityListIndex();
|
|
const bool bDrivingFast = g_ContinuityMgr.HighSpeedDriving();
|
|
const bool bSniping = g_ContinuityMgr.IsUsingSniperOrTelescope();
|
|
|
|
#if !__FINAL
|
|
const camBaseCamera* renderedDebugCamera = camInterface::GetDebugDirector().GetRenderedCamera();
|
|
const bool bUsingDebugCam = (renderedDebugCamera && camInterface::IsDominantRenderedCamera(*renderedDebugCamera));
|
|
#else // !__FINAL
|
|
const bool bUsingDebugCam = false;
|
|
#endif // !__FINAL
|
|
const bool bSrlPlayback = gStreamingRequestList.IsPlaybackActive() && !bUsingDebugCam && gStreamingRequestList.IsLongJumpMode();
|
|
const CViewport* pGameViewport = gVpMan.GetGameViewport();
|
|
|
|
m_lights.ResetCount();
|
|
|
|
#if __BANK
|
|
const bool bDoAlphaUpdate = CPostScanDebug::DoAlphaUpdate();
|
|
bool forceLodForCSS = CPostScanDebug::ms_enableForceLodInCCS;
|
|
#endif //__BANK
|
|
|
|
const s32 gCCSIndex = g_CascadeShadowsNew->GetEntityListIndex();
|
|
const s32 gCCSPhaseMask = 1 << gCCSIndex;
|
|
|
|
bool bAllowSuppressingExternalLod = true;
|
|
#if GTA_REPLAY
|
|
if(CReplayMgr::IsEditModeActive())
|
|
{
|
|
const camBaseCamera* renderedCamera = camInterface::GetDominantRenderedCamera();
|
|
if(renderedCamera && !renderedCamera->GetAttachParent())
|
|
{
|
|
CInteriorInst* pIntInst = CPortalVisTracker::GetPrimaryInteriorInst();
|
|
s32 roomIdx = CPortalVisTracker::GetPrimaryRoomIdx();
|
|
|
|
if (!pIntInst || roomIdx == INTLOC_INVALID_INDEX)
|
|
bAllowSuppressingExternalLod = false;
|
|
}
|
|
}
|
|
#endif //GTA_REPLAY
|
|
|
|
|
|
CGtaPvsEntry* RESTRICT pPvsEntry = (CGtaPvsEntry* RESTRICT) pStart;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// alpha update pass part #1 - assign fade-up values to everything and update parent LODs
|
|
PF_PUSH_TIMEBAR("Alpha Part 1");
|
|
BANK_ONLY( if (bDoAlphaUpdate) )
|
|
{
|
|
while (pPvsEntry != pStop)
|
|
{
|
|
CGtaPvsEntry* pNextEntry = pPvsEntry + 1;
|
|
PrefetchDC((char*)pPvsEntry+128);
|
|
if(Likely(pNextEntry < pStop))
|
|
{
|
|
PrefetchDC(pNextEntry->GetEntity());
|
|
}
|
|
|
|
CEntity* RESTRICT pEntity = (CEntity* RESTRICT) pPvsEntry->GetEntity();
|
|
// Because the PVS partition/sort by lod now eliminates the "holes" (invalidated entries)
|
|
// in the PVS, we can simply assert on the entity instead of taking the branch (and possible
|
|
// penalty) to check it.
|
|
Assert(pEntity);
|
|
|
|
fwVisibilityFlags& visFlags = pPvsEntry->GetVisFlags();
|
|
if(visFlags.GetOptionFlags() & VIS_FLAG_ONLY_STREAM_ENTITY)
|
|
{
|
|
pPvsEntry = pNextEntry;
|
|
continue;
|
|
}
|
|
|
|
fwLodData& lodData = pEntity->GetLodData();
|
|
const bool bVisibleInGbuf = ((visFlags & gbufPhaseFlag) != 0);
|
|
const bool bVisibleInMirrors = ((visFlags & mirrorPhaseFlag) != 0);
|
|
|
|
// Performance optimization - Use existence of draw handler as a proxy for whether or not the model info
|
|
// has loaded. Otherwise we cache miss on the model info, which is very expensive to do for each entity
|
|
// in this loop.
|
|
const bool bIsLoaded = pEntity->GetDrawHandlerPtr() ? true : pEntity->GetBaseModelInfo()->GetHasLoaded();
|
|
const bool bResetAlpha = lodData.GetResetAlpha();
|
|
const bool bIsResetDisabled = lodData.IsResetDisabled();
|
|
|
|
Assert(bIsLoaded == pEntity->GetBaseModelInfo()->GetHasLoaded());
|
|
|
|
// Common case is that we're not in SLod mode - check for that to avoid branch mis-prediction.
|
|
if (!bSlodMode)
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// A: reset child alpha (as list is sorted by lod level)
|
|
pEntity->ResetChildAlpha();
|
|
|
|
// A2 - re-examine to see how much of this is really necessary
|
|
if (bAllowTimeBaseFading && bVisibleInGbuf && bResetAlpha && !bIsResetDisabled)
|
|
{
|
|
pEntity->SetAlpha(LODTYPES_ALPHA_INVISIBLE); // entity has had drawable created, so force alpha down to 0
|
|
}
|
|
|
|
// Moved the flag setting here to avoid a LHS
|
|
lodData.SetVisible( bVisibleInGbuf || bVisibleInMirrors);
|
|
lodData.SetLoaded( bIsLoaded );
|
|
// B: calculate new alpha value
|
|
lodData.SetResetAlpha(false); // clear reset flag
|
|
lodData.SetChildDrawing(false); // water reflection code needs to know if something has been faded down relative to its children :/
|
|
u32 alpha = g_LodMgr.GetAlphaPt1_FadeUp( lodData, visFlags.GetAlpha() );
|
|
lodData.SetAlpha(alpha);
|
|
lodData.SetForceOnInCascadeShadows(false);
|
|
|
|
// C: update parent's child alpha value
|
|
CEntity* pLod = (CEntity*) pEntity->GetLod();
|
|
if (pLod)
|
|
{
|
|
pLod->UpdateChildAlpha( alpha );
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
}
|
|
else
|
|
{
|
|
// Moved the flag setting here to avoid a LHS in the other section of this if statement
|
|
lodData.SetVisible( bVisibleInGbuf );
|
|
lodData.SetLoaded( bIsLoaded );
|
|
g_LodMgr.UpdateAlpha_SlodMode( lodData );
|
|
}
|
|
pPvsEntry++;
|
|
}
|
|
}
|
|
PF_POP_TIMEBAR(); // "Alpha Part 1"
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
PF_PUSH_TIMEBAR("Alpha Part 2 + Misc");
|
|
const bool bPerformAlphaUpdateSecondPart = (!bSlodMode BANK_ONLY(&& bDoAlphaUpdate));
|
|
|
|
pPvsEntry = (CGtaPvsEntry* RESTRICT) pStart;
|
|
while (pPvsEntry != pStop)
|
|
{
|
|
CGtaPvsEntry* pNextEntry = pPvsEntry + 1;
|
|
PrefetchDC((char*)pPvsEntry+128);
|
|
if(Likely(pNextEntry < pStop))
|
|
{
|
|
PrefetchDC(pNextEntry->GetEntity());
|
|
}
|
|
|
|
CEntity* RESTRICT pEntity = (CEntity* RESTRICT) pPvsEntry->GetEntity();
|
|
// See comment in above loop - holes have been eliminated, and previous loop doesn't
|
|
// create any additional PVS holes by invalidating entries, so simply assert on the entity.
|
|
Assert(pEntity);
|
|
|
|
fwVisibilityFlags& visFlags = pPvsEntry->GetVisFlags();
|
|
if(visFlags.GetOptionFlags() & VIS_FLAG_ONLY_STREAM_ENTITY)
|
|
{
|
|
pEntity->SetAlpha( visFlags.GetAlpha() );
|
|
pPvsEntry->Invalidate();
|
|
|
|
pPvsEntry = pNextEntry;
|
|
continue;
|
|
}
|
|
|
|
fwLodData& lodData = pEntity->GetLodData();
|
|
bool bisLoaded = lodData.IsLoaded();
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// alpha update pass #2
|
|
if ( Likely(bPerformAlphaUpdateSecondPart) )
|
|
{
|
|
// Prefetch the entity's transform, as it will be needed later when we call UpdateAlphaPt2_FadeDownRelativeToChildren().
|
|
PrefetchDC(pEntity->GetTransformPtr());
|
|
|
|
CEntity* pLod = NULL;
|
|
const u32 numChildren = lodData.GetNumChildren();
|
|
const bool allChildrenAttached = lodData.AllChildrenAttached();
|
|
const bool bMayForceUpGrandparent = (bSniping || bDrivingFast) && lodData.IsLoddedHd();
|
|
|
|
const bool bUseRelatedDummyLod = (visFlags.GetOptionFlags()&VIS_FLAG_ENTITY_NEVER_DUMMY)!=0 &&
|
|
visFlags.GetEntityType()==ENTITY_TYPE_OBJECT &&
|
|
static_cast<CObject*>(pEntity)->GetRelatedDummy();
|
|
// Common case is not an object with related dummy - check for that to avoid branch mis-prediction.
|
|
if ( !bUseRelatedDummyLod )
|
|
{
|
|
pLod = (CEntity*) pEntity->GetLod();
|
|
}
|
|
else
|
|
{
|
|
// this is disgusting, but special behaviour for a type of object only used a couple of times in the entire map.
|
|
// that is - windmills, pumpjacks, ferris wheel etc. normally dynamic objects don't have lods, but in these
|
|
// cases (animated objects, fragments) we can't render the dummy object ever (because it cannot animate)
|
|
pLod = (CEntity*) static_cast<CObject*>(pEntity)->GetRelatedDummy()->GetLod();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// A: pick up alpha value from parent's child alpha
|
|
if ( pLod && pLod->GetChildAlpha() && visFlags.GetAlpha()!=LODTYPES_ALPHA_INVISIBLE)
|
|
{
|
|
pEntity->SetAlpha( pLod->GetChildAlpha() );
|
|
}
|
|
|
|
// B: fade down relative to children
|
|
bool bShouldBeFullyVisible = ( visFlags.GetAlpha()==LODTYPES_ALPHA_VISIBLE );
|
|
if ( numChildren && (bSrlPlayback || allChildrenAttached) ) // SRLs don't do full IMAP load, to avoid building pool problems etc
|
|
{
|
|
// Don't calculate this stuff if above isn't true.
|
|
const bool bAvoidFadeRelatedToChildren = (visFlags.GetOptionFlags() & VIS_FLAG_RENDERED_THROUGH_LODS_ONLY_PORTAL) && lodData.IsLod();
|
|
if(!bAvoidFadeRelatedToChildren)
|
|
{
|
|
g_LodMgr.UpdateAlphaPt2_FadeDownRelativeToChildren(pEntity, bShouldBeFullyVisible);
|
|
}
|
|
}
|
|
|
|
// C: force LOD to draw if required
|
|
if ( lodData.IsVisible() && pLod && lodData.GetAlpha() && !lodData.IsParentOfInterior() )
|
|
{
|
|
// Don't calculate this stuff if above isn't true.
|
|
const bool bForceUpLodBecauseNotLoaded = ( !bisLoaded );
|
|
const bool bForceUpLodBecauseFading = ( bShouldBeFullyVisible && lodData.IsFading() && g_LodMgr.AllowedToForceDrawParent(lodData) );
|
|
if(bForceUpLodBecauseFading || bForceUpLodBecauseNotLoaded)
|
|
{
|
|
pLod->SetAlpha( LODTYPES_ALPHA_VISIBLE );
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// for HD models only - if driving fast and forcing up the LOD has
|
|
// failed (ie it hasn't streamed in yet) then we allow forcing up of SLOD1
|
|
if (bMayForceUpGrandparent && bForceUpLodBecauseNotLoaded && !pLod->GetLodData().IsLoaded() && pLod->GetLodData().HasLod())
|
|
{
|
|
pLod->GetLod()->SetAlpha( LODTYPES_ALPHA_VISIBLE );
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
}
|
|
}
|
|
|
|
if (BANK_ONLY(forceLodForCSS &&) ( lodData.IsLoddedHd() || lodData.IsLod() ) && !bisLoaded && pLod && lodData.GetAlpha() && !lodData.IsVisible() && (visFlags.GetPhaseVisFlags() & gCCSPhaseMask))
|
|
{
|
|
pLod->GetLodData().SetForceOnInCascadeShadows(true);
|
|
}
|
|
|
|
// re-examine to see how much of this is really necessary
|
|
if ( bisLoaded )
|
|
{
|
|
lodData.SetResetDisabled(false);
|
|
}
|
|
// else
|
|
// {
|
|
// lodData.SetAlpha(LODTYPES_ALPHA_INVISIBLE);
|
|
// }
|
|
|
|
// if interior shell not ready, then force draw the LOD if available
|
|
if ( visFlags.GetEntityType() == ENTITY_TYPE_MLO )
|
|
{
|
|
fwLodData& lodData = pEntity->GetLodData();
|
|
const u32 childDepth = lodData.GetChildLodType();
|
|
|
|
CInteriorInst* pIntInst = static_cast<CInteriorInst*>(pEntity);
|
|
if (pPvsEntry->GetDist() < ((float(pIntInst->GetLodDistance()) * g_LodScale.GetPerLodLevelScale(childDepth)) + LODTYPES_FADE_DIST + 5.0f)) // add fading distance and a bit
|
|
{
|
|
if (pLod)
|
|
{
|
|
if (pIntInst->SetAlphaForRoomZeroModels(pIntInst->GetAlpha()))
|
|
{
|
|
pLod->SetAlpha(LODTYPES_ALPHA_INVISIBLE);
|
|
} else {
|
|
pLod->SetAlpha(LODTYPES_ALPHA_VISIBLE);
|
|
}
|
|
}
|
|
pIntInst->CapAlphaForContentsEntities();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// never render dummy objects which are set to never dummy
|
|
if (visFlags.GetEntityType()==ENTITY_TYPE_DUMMY_OBJECT && pLod && (visFlags.GetOptionFlags()&VIS_FLAG_ENTITY_NEVER_DUMMY)!=0)
|
|
{
|
|
// special case - we never want to see these dummy objects!
|
|
pEntity->SetAlpha( LODTYPES_ALPHA_INVISIBLE );
|
|
pLod->SetAlpha( LODTYPES_ALPHA_VISIBLE );
|
|
}
|
|
|
|
} /* end of alpha update pt 2 */
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// misc + extract important objects / dummy objects
|
|
switch ( visFlags.GetEntityType() )
|
|
{
|
|
case ENTITY_TYPE_DUMMY_OBJECT:
|
|
if ( pPvsEntry->GetDist() < OBJECT_POPULATION_RESET_RANGE )
|
|
{
|
|
CObjectPopulation::InsertSortedDummyClose(pEntity, pPvsEntry->GetDist());
|
|
pEntity->m_nFlags.bFoundInPvs = true;
|
|
}
|
|
break;
|
|
case ENTITY_TYPE_OBJECT:
|
|
if ( pPvsEntry->GetDist() < OBJECT_POPULATION_RESET_RANGE )
|
|
{
|
|
pEntity->m_nFlags.bFoundInPvs = true;
|
|
}
|
|
break;
|
|
case ENTITY_TYPE_PORTAL:
|
|
//
|
|
// update scanned portals
|
|
//
|
|
{
|
|
if ( pGameViewport )
|
|
{
|
|
CRenderer::UpdateScannedVisiblePortal( reinterpret_cast<CPortalInst*>(pEntity) );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ENTITY_TYPE_LIGHT:
|
|
//
|
|
// push light entities into a separate list for conversion into light sources for rendering
|
|
//
|
|
{
|
|
//let's not add the light source to the list if it's not visible in the main viewport and its beyond a certain cutoff distance
|
|
if(lodData.IsVisible() || pPvsEntry->GetDist() < Lights::GetNonGBufLightCutoffDistance())
|
|
{
|
|
m_lights.Grow() = std::make_pair( pEntity, visFlags );
|
|
}
|
|
pPvsEntry->Invalidate();
|
|
}
|
|
break;
|
|
|
|
case ENTITY_TYPE_ANIMATED_BUILDING:
|
|
//
|
|
// force large nearby animated buildings to animate even if off screen
|
|
//
|
|
{
|
|
if ( !lodData.IsVisible()
|
|
&& (visFlags.GetOptionFlags() & VIS_FLAG_ONLY_STREAM_ENTITY)==0
|
|
&& pPvsEntry->GetDist()<=CAnimatedBuilding::FORCEANIM_DIST_MAX
|
|
&& pEntity->GetBaseModelInfo()->GetBoundingSphereRadius()>=CAnimatedBuilding::FORCEANIM_SIZE_MIN )
|
|
{
|
|
reinterpret_cast< CDynamicEntity* >( pEntity )->m_nDEflags.bForcePrePhysicsAnimUpdate = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// make sure no one tries to render an entity which is not loaded.
|
|
if (!bisLoaded)
|
|
{
|
|
pPvsEntry->Invalidate();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
pPvsEntry = pNextEntry;
|
|
}
|
|
PF_POP_TIMEBAR(); // "Alpha Part 2 + Misc"
|
|
|
|
PF_POP_TIMEBAR(); // "Post Sort PVS Loop"
|
|
|
|
// suppress the interior LOD of the MLO the player is actually in
|
|
CInteriorInst* pPlayerIntInst = NULL;
|
|
CPed* pPlayer = CPedFactory::GetFactory()->GetLocalPlayer();
|
|
if (pPlayer && pPlayer->GetIsInInterior() && bAllowSuppressingExternalLod)
|
|
{
|
|
CInteriorInst* pPlayerIntInst = pPlayer->GetPortalTracker()->GetInteriorInst();
|
|
|
|
if (pPlayerIntInst)
|
|
{
|
|
if (pPlayerIntInst->SetAlphaForRoomZeroModels(LODTYPES_ALPHA_VISIBLE))
|
|
{
|
|
pPlayerIntInst->SuppressInternalLOD();
|
|
}
|
|
|
|
if (!pPlayerIntInst->GetGroupId())
|
|
{
|
|
pPlayerIntInst->SuppressExternalLOD();
|
|
}
|
|
}
|
|
}
|
|
|
|
CInteriorInst* pPrimaryIntInst = CPortalVisTracker::GetPrimaryInteriorInst();
|
|
if (pPrimaryIntInst)
|
|
{
|
|
if ((pPrimaryIntInst != pPlayerIntInst))
|
|
{
|
|
if (pPrimaryIntInst->SetAlphaForRoomZeroModels(LODTYPES_ALPHA_VISIBLE))
|
|
{
|
|
pPrimaryIntInst->SuppressInternalLOD();
|
|
}
|
|
}
|
|
|
|
// If the exterior is not visible in the GBuffer
|
|
if (!fwScan::GetScanResults().GetIsExteriorVisibleInGbuf())
|
|
{
|
|
// Look through any linked portals attached to the room and suppress their LODs
|
|
// Note we do this for 2 link interiors
|
|
spdTransposedPlaneSet8 frustum;
|
|
frustum.Set(g_SceneToGBufferPhaseNew->GetViewport()->GetGrcViewport());
|
|
|
|
const int maxVisitedLocations = 0;
|
|
atArray<fwInteriorLocation> visitedLocs(0, maxVisitedLocations);
|
|
ForceUpLinkedInteriors(CPortalVisTracker::GetInteriorLocation(), pPrimaryIntInst, frustum, visitedLocs);
|
|
}
|
|
}
|
|
|
|
ms_bAllowLateLightEntities = false;
|
|
|
|
// Apply scripted alpha override
|
|
if( ms_hasAnyAlphaOverride )
|
|
{
|
|
int lastAlphaOverride = 0;
|
|
bool hasAnyAlphaOverride = false;
|
|
for(int i=0;i<ms_lastAlphaOverride;i++)
|
|
{
|
|
alphaOverride& alphaO = ms_alphaOverrides[i];
|
|
|
|
if( alphaO.entity )
|
|
{
|
|
lastAlphaOverride = i;
|
|
hasAnyAlphaOverride = true;
|
|
alphaO.entity->SetAlpha((u8)alphaO.alpha&0xff);
|
|
}
|
|
}
|
|
|
|
ms_hasAnyAlphaOverride = hasAnyAlphaOverride;
|
|
ms_lastAlphaOverride = lastAlphaOverride + 1;
|
|
}
|
|
|
|
|
|
// appalling multiplayer apartment hack - cull exterior HD stuff inside the specified sphere from gbuffer
|
|
// this was done because we have these multiplayer apartments you can buy, but they don't properly fit inside the buildings
|
|
// they are located in. so script hide some of the models. burn this code upon reading
|
|
g_PostScanCull.Process(pStart, pStop);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION: BuildRenderListsFromPVS
|
|
// PURPOSE: process the PVS from start to finish and place each entry in the appropriate renderlists.
|
|
// Also adds any entries to the pre-render list which need to be
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CPostScan::BuildRenderListsFromPVS()
|
|
{
|
|
CGtaPvsEntry* pStart = GetPVSBase();
|
|
CGtaPvsEntry* pStop = GetPVSEnd();
|
|
u32 canRenderAlpha = CRenderer::GetCanRenderAlphaFlags();
|
|
u32 canRenderWater = CRenderer::GetCanRenderWaterFlags();
|
|
u32 canRenderCutout = CRenderer::GetCanRenderScreenDoorFlags();
|
|
u32 canRenderDecal = CRenderer::GetCanRenderDecalFlags();
|
|
|
|
#if __BANK
|
|
if (Unlikely(CPostScanDebug::GetOverrideGBufBitsEnabled()))
|
|
{
|
|
canRenderWater = 0;
|
|
canRenderDecal = 0;
|
|
}
|
|
#endif // __BANK
|
|
|
|
// Process sort biases set by script
|
|
if(ms_sortBiases.GetCount())
|
|
{
|
|
int numBiases = ms_sortBiases.GetCount();
|
|
CGtaPvsEntry* RESTRICT pPvsEntry = (CGtaPvsEntry* RESTRICT ) pStart;
|
|
while(pPvsEntry != pStop)
|
|
{
|
|
CEntity* RESTRICT pEntity = (CEntity* RESTRICT) pPvsEntry->GetEntity();
|
|
if(pEntity)
|
|
{
|
|
for(int i = 0; i < numBiases; i++)
|
|
{
|
|
if(ms_sortBiases[i].entity == pEntity)
|
|
{
|
|
pPvsEntry->AddToDist(ms_sortBiases[i].bias);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pPvsEntry++;
|
|
}
|
|
}
|
|
|
|
CompileTimeAssert(VIS_FLAG_OPTION_INTERIOR == BIT(0)); // This function relies on VIS_FLAG_OPTION_INTERIOR being bit 0, if it has moved the code below will need to be updated
|
|
u32 cameraInInterior = m_cameraInInterior;
|
|
const float lodTypesMaxLodDist = (float)LODTYPES_MAX_LODDIST;
|
|
|
|
s32 gBuffIndex = g_SceneToGBufferPhaseNew->GetEntityListIndex();
|
|
s32 gBuffPhaseMask = 1 << gBuffIndex;
|
|
u32 gbuffCameraInInterior = (cameraInInterior & gBuffPhaseMask) >> gBuffIndex;
|
|
|
|
s32 reflectionPhaseIndex = g_ReflectionMapPhase->GetEntityListIndex();
|
|
s32 reflectionPhaseMask = 1 << reflectionPhaseIndex;
|
|
u32 reflectionCameraInInterior = (cameraInInterior & reflectionPhaseMask) >> reflectionPhaseIndex;
|
|
|
|
s32 gCCSIndex = g_CascadeShadowsNew->GetEntityListIndex();
|
|
|
|
atFixedArray<int, fwVisibilityFlags::MAX_PHASE_VISIBILITY_BITS> renderPhaseMask;
|
|
atFixedArray<int, fwVisibilityFlags::MAX_PHASE_VISIBILITY_BITS> renderPhaseListIndex;
|
|
for(u32 i=0; i<fwVisibilityFlags::MAX_PHASE_VISIBILITY_BITS; i++)
|
|
{
|
|
fwRenderListDesc& rlDesc = gRenderListGroup.GetRenderListForPhase(i);
|
|
CRenderPhase *renderPhase = (CRenderPhase *) rlDesc.GetRenderPhaseNew();
|
|
|
|
// the streaming cull shapes aren't backed by a real render phase
|
|
if ( !renderPhase )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
renderPhaseListIndex.Push(i);
|
|
renderPhaseMask.Push(1<<i);
|
|
}
|
|
|
|
u32 phaseCount = renderPhaseListIndex.GetCount();
|
|
|
|
u32 flagMask = fwEntity::HAS_ALPHA | fwEntity::HAS_DISPL_ALPHA | fwEntity::HAS_WATER | fwEntity::HAS_DECAL | fwEntity::HAS_CUTOUT;
|
|
|
|
#if __PS3
|
|
#define NUM_ENTITIES_TO_PROCESS_BEFORE_YIELDING (50)
|
|
int yieldCounter = 0;
|
|
#endif
|
|
|
|
#if __CHECK_FOR_DUPLICATES
|
|
rage::atMap<CEntity*, CGtaPvsEntry*> duplicateMap;
|
|
#endif
|
|
|
|
#if !__FINAL
|
|
m_EntityCount = 0;
|
|
#endif //!__FINAL
|
|
|
|
CGtaPvsEntry* RESTRICT pPvsEntry = (CGtaPvsEntry* RESTRICT ) pStart;
|
|
while(pPvsEntry != pStop)
|
|
{
|
|
|
|
#if __PS3
|
|
if(yieldCounter == NUM_ENTITIES_TO_PROCESS_BEFORE_YIELDING)
|
|
{
|
|
yieldCounter = 0;
|
|
sysIpcYield(0);
|
|
}
|
|
#endif
|
|
|
|
CGtaPvsEntry* pNextEntry = pPvsEntry + 1;
|
|
PrefetchDC((char*)pPvsEntry + 128);
|
|
if(Likely(pNextEntry < pStop))
|
|
{
|
|
PrefetchDC(pNextEntry->GetEntity());
|
|
}
|
|
|
|
CEntity* RESTRICT pEntity = (CEntity* RESTRICT) pPvsEntry->GetEntity();
|
|
|
|
if(pEntity)
|
|
{
|
|
#if __PS3
|
|
yieldCounter++;
|
|
#endif
|
|
|
|
#if !__FINAL
|
|
m_EntityCount++;
|
|
#endif //!__FINAL
|
|
|
|
#if __BANK
|
|
// trace selected entity
|
|
if (CPostScanDebug::ms_breakOnSelectedEntityInBuildRenderListFromPVS && pEntity == g_PickerManager.GetSelectedEntity())
|
|
{
|
|
__debugbreak();
|
|
}
|
|
#endif // __BANK
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// required info:
|
|
// 1. is alpha less than threshold value (from entity / loddata)
|
|
// 2. is alpha less than 255 (from entity / loddata)
|
|
// 3. is it a tree (from entity / loddata)
|
|
// 4. base flags (from entity)
|
|
// 5. vis flags (from pvs entry)
|
|
// 6. entity ptr (from pvs entry)
|
|
// 7. distance (from pvs entry)
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
fwVisibilityFlags& visFlags = pPvsEntry->GetVisFlags();
|
|
|
|
Assertf( (visFlags.GetOptionFlags() & VIS_FLAG_ONLY_STREAM_ENTITY)==0, "%s is flagged as streaming only, so shouldn't be getting through to build render lists", pEntity->GetModelName() );
|
|
|
|
u32 optionFlags = visFlags.GetOptionFlags();
|
|
const float fDistanceToCamera = pPvsEntry->GetDist();
|
|
const u32 perPhaseRenderFlags = visFlags.GetPhaseVisFlags();
|
|
const u32 subphaseVisFlags = visFlags.GetSubphaseVisFlags();
|
|
const bool pedInVehicle = (optionFlags & VIS_FLAG_ENTITY_IS_PED_INSIDE_VEHICLE) != 0;
|
|
const bool hdTexCapable = (optionFlags & VIS_FLAG_ENTITY_IS_HD_TEX_CAPABLE) != 0;
|
|
|
|
u16 baseCustomFlags = pedInVehicle ? (fwEntityList::CUSTOM_FLAGS_PED_IN_VEHICLE) : 0;
|
|
baseCustomFlags |= hdTexCapable ? (fwEntityList::CUSTOM_FLAGS_HD_TEX_CAPABLE) : 0;
|
|
|
|
u16 inInterior = optionFlags & VIS_FLAG_OPTION_INTERIOR;
|
|
|
|
// Flipping the lodSortValue here so it works with an ascending sort
|
|
const u16 hdLodSortValue = (u16)fwLodData::GetSortVal(LODTYPES_DEPTH_HD);
|
|
u16 flippedCustomLodSortValue = hdLodSortValue - (u16)visFlags.GetLodSortVal();
|
|
Assertf(flippedCustomLodSortValue <= hdLodSortValue, "visFlags.GetLodSortVal() returned a bad value %d", visFlags.GetLodSortVal());
|
|
|
|
const bool bIsPed = pEntity->GetIsTypePed();
|
|
const bool bIsVehicle = pEntity->GetIsTypeVehicle();
|
|
// We have additional special checks for peds & vehicles that may cause them to be excluded from these passes
|
|
bool bShouldDoAlpha = true;
|
|
bool bShouldDoDecal = true;
|
|
bool bShouldDoCutout = true;
|
|
if(bIsPed)
|
|
{
|
|
CPed* pPed = pPed = static_cast<CPed*>(pEntity);
|
|
u8 pedLodIndex = pedLodIndex = pPed->GetModelLodIndex();
|
|
if((pedLodIndex == 3) && !pPed->GetLodBucketInfoExists(pedLodIndex) && pPed->GetLodBucketInfoExists(pedLodIndex-1))
|
|
{
|
|
// If the ped is supposed to be in SLOD mode (lod == 3), and the info
|
|
// doesn't exist for SLOD, but does exist for 2, this is because we sometimes
|
|
// don't have a SLOD model, and will simply be rendered at lod = 2, use that index instead.
|
|
pedLodIndex--;
|
|
}
|
|
|
|
Assert(pPed->GetDrawHandlerPtr());
|
|
const CPedPropData& pedPropData = pPed->GetPedDrawHandler().GetPropData();
|
|
|
|
// 1 - A Ped with a prop that has Alpha/Decal/Cutout,
|
|
// ------ OR ---
|
|
// 2 - A Ped and LOD bucket mask info does not exist.
|
|
// 3 - A Ped and LOD bucket mask info does exist, and it says the ped has alpha/decal/cutout at the specified LOD level.
|
|
bShouldDoAlpha = pedPropData.GetIsCurrPropsAlpha() || !pPed->GetLodBucketInfoExists(pedLodIndex) || pPed->GetLodHasAlpha(pedLodIndex);
|
|
bShouldDoDecal = pedPropData.GetIsCurrPropsDecal() || !pPed->GetLodBucketInfoExists(pedLodIndex) || pPed->GetLodHasDecal(pedLodIndex);
|
|
bShouldDoCutout = pedPropData.GetIsCurrPropsCutout() || !pPed->GetLodBucketInfoExists(pedLodIndex) || pPed->GetLodHasCutout(pedLodIndex);
|
|
}
|
|
else if(bIsVehicle)
|
|
{
|
|
CVehicle* pVehicle = static_cast<CVehicle*>(pEntity);
|
|
CVehicleModelInfo *pVMI = pVehicle->GetVehicleModelInfo();
|
|
bShouldDoAlpha = !pVehicle->GetIsLandVehicle() || (fDistanceToCamera < pVMI->GetVehicleLodDistance(VLT_LOD2));
|
|
}
|
|
|
|
#if __CHECK_FOR_DUPLICATES
|
|
if (pEntity->GetIsDynamic() && static_cast<CDynamicEntity*>(pEntity)->IsStraddlingPortal())
|
|
{
|
|
CGtaPvsEntry **pDup = duplicateMap.Access(pEntity);
|
|
if (pDup)
|
|
{
|
|
Assertf((*pDup)->GetEntity() != pEntity, "Duplicate Entries in PVS");
|
|
}
|
|
else
|
|
{
|
|
duplicateMap.Insert(pEntity, pPvsEntry);
|
|
}
|
|
}
|
|
#endif
|
|
// for each phase, determine if entity should be added to associated renderList and if so, tagged for which pass
|
|
for(u32 i=0; i<phaseCount; i++)
|
|
{
|
|
if (perPhaseRenderFlags & renderPhaseMask[i])
|
|
{
|
|
u16 customFlags = baseCustomFlags;
|
|
u16 alphaCustomFlags = baseCustomFlags;
|
|
float customDistanceToCamera = fDistanceToCamera;
|
|
|
|
fwRenderListDesc& rlDesc = gRenderListGroup.GetRenderListForPhase(renderPhaseListIndex[i]);
|
|
CRenderPhase *renderPhase = (CRenderPhase *) rlDesc.GetRenderPhaseNew();
|
|
bool bRenderingShadows = CDrawListMgr::IsShadowDrawList(renderPhase->GetDrawListType());
|
|
bool bParabReflectionPhase = (renderPhase->GetDrawListType() == DL_RENDERPHASE_REFLECTION_MAP);
|
|
|
|
if (renderPhase->GetDrawListType() == DL_RENDERPHASE_GBUF)
|
|
{
|
|
#if __BANK
|
|
if (CPostScanDebug::ms_enableSortingAndExteriorScreenQuadScissoring)
|
|
#endif
|
|
{
|
|
// interiorSortGBuff is 1 if 'the entity and the camera are in an interior' or 'the entity and the camera are in the in exterior'
|
|
u16 interiorSortGBuff = u16(~(inInterior ^ gbuffCameraInInterior)) & 0x1;
|
|
|
|
u32 entityType = visFlags.GetEntityType();
|
|
const bool dontScissorDynamicEntities = BANK_SWITCH(CPostScanDebug::ms_dontScissorDynamicEntities, true);
|
|
u16 notDynamicEntity = dontScissorDynamicEntities ? u16(entityType < ENTITY_TYPE_ANIMATED_BUILDING || entityType > ENTITY_TYPE_OBJECT) : 1;
|
|
|
|
customFlags |= interiorSortGBuff << fwEntityList::CUSTOM_FLAGS_INTERIOR_SORT_BIT;
|
|
customFlags |= ((~inInterior & gbuffCameraInInterior) & notDynamicEntity) << fwEntityList::CUSTOM_FLAGS_USE_EXTERIOR_SCREEN_QUAD_BIT;
|
|
}
|
|
}
|
|
else if (bParabReflectionPhase)
|
|
{
|
|
// interiorSortReflection is 1 if 'the entity and the camera are in an interior' or 'the entity and the camera are in the in exterior'
|
|
u16 interiorSortReflection = u16(~(inInterior ^ reflectionCameraInInterior)) & 0x1;
|
|
customFlags |= interiorSortReflection << fwEntityList::CUSTOM_FLAGS_INTERIOR_SORT_BIT;
|
|
}
|
|
else if (renderPhase->GetDrawListType() == DL_RENDERPHASE_MIRROR_REFLECTION)
|
|
{
|
|
u16 interiorSortReflection = u16(inInterior) & 0x1;
|
|
customFlags |= interiorSortReflection << fwEntityList::CUSTOM_FLAGS_INTERIOR_SORT_BIT;
|
|
alphaCustomFlags = customFlags; // mirror reflection needs custom flags in alpha passes too
|
|
}
|
|
#if WATER_REFLECTION_PRE_REFLECTED
|
|
else if (renderPhase->GetDrawListType() == DL_RENDERPHASE_WATER_REFLECTION)
|
|
{
|
|
if (Unlikely(pEntity->GetRenderPhaseVisibilityMask().IsFlagSet(VIS_PHASE_MASK_WATER_REFLECTION_PROXY_PRE_REFLECTED)))
|
|
{
|
|
customFlags |= fwEntityList::CUSTOM_FLAGS_WATER_REFLECTION_PRE_REFLECTED;
|
|
}
|
|
|
|
customFlags |= fwEntityList::CUSTOM_FLAGS_WATER_REFLECTION; // this forces the FlagsChanged function to get called before anything else
|
|
alphaCustomFlags = customFlags; // water reflection needs custom flags in alpha passes too
|
|
}
|
|
#endif // WATER_REFLECTION_PRE_REFLECTED
|
|
|
|
// See if a lod mask is set
|
|
if (renderPhase->GetLodMask() != fwRenderPhase::LODMASK_NONE)
|
|
{
|
|
if (bParabReflectionPhase)
|
|
{
|
|
customFlags |= (fwEntityList::CUSTOM_FLAGS_LOD_SORT_ENABLED | (flippedCustomLodSortValue << fwEntityList::CUSTOM_FLAGS_LOD_SORT_VAL_BIT0));
|
|
#if __ASSERT
|
|
u32 invalidBits = ~u32( fwEntityList::CUSTOM_FLAGS_INTERIOR_SORT |
|
|
fwEntityList::CUSTOM_FLAGS_LOD_SORT_ENABLED |
|
|
fwEntityList::CUSTOM_FLAGS_LOD_SORT_VAL_MASK |
|
|
fwEntityList::CUSTOM_FLAGS_PED_IN_VEHICLE |
|
|
fwEntityList::CUSTOM_FLAGS_HD_TEX_CAPABLE );
|
|
Assertf((customFlags & invalidBits) == 0, "Invalid bits set 0x%x", customFlags);
|
|
#endif
|
|
const u32 SLod3SortValue = fwLodData::GetSortVal(LODTYPES_DEPTH_SLOD3);
|
|
if (visFlags.GetLodSortVal() == SLod3SortValue)
|
|
{
|
|
customDistanceToCamera = lodTypesMaxLodDist - customDistanceToCamera;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// cull entities with low alpha values
|
|
//
|
|
bool bWaterReflectionPhase = (renderPhase->GetDrawListType() == DL_RENDERPHASE_WATER_REFLECTION);
|
|
bool bForceCascadeShadowPhase = renderPhase->GetEntityListIndex() == gCCSIndex && pEntity->GetLodData().GetForceOnInCascadeShadows();
|
|
#if __BANK
|
|
if (Unlikely(CPostScanDebug::GetOverrideGBufBitsEnabled()))
|
|
{
|
|
if (CPostScanDebug::GetOverrideGBufVisibilityType() == VIS_PHASE_WATER_REFLECTION)
|
|
{
|
|
if (renderPhase->GetDrawListType() == DL_RENDERPHASE_GBUF ||
|
|
renderPhase->GetDrawListType() == DL_RENDERPHASE_DRAWSCENE ||
|
|
renderPhase->GetDrawListType() == DL_RENDERPHASE_DEBUG ||
|
|
renderPhase->GetDrawListType() == DL_RENDERPHASE_DEBUG_OVERLAY)
|
|
{
|
|
bWaterReflectionPhase = true; // use water reflection alpha
|
|
}
|
|
}
|
|
}
|
|
#endif // __BANK
|
|
u8 alpha = pEntity->GetAlpha();
|
|
|
|
if (bWaterReflectionPhase && CRenderPhaseWaterReflectionInterface::ShouldEntityForceUpAlpha(pEntity))
|
|
{
|
|
alpha = LODTYPES_ALPHA_VISIBLE;
|
|
}
|
|
else if (alpha < ALPHA_CULL_THRESHOLD)
|
|
{
|
|
if (!bParabReflectionPhase && !bForceCascadeShadowPhase
|
|
BANK_ONLY(&& !(bWaterReflectionPhase && !CRenderPhaseWaterReflectionInterface::CanAlphaCullInPostScan())))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// if tree type object then add to tree pass and skip to next object (don't process in any other passes)
|
|
if (pEntity->GetLodData().IsTree())
|
|
{
|
|
rlDesc.AddEntity(pEntity, subphaseVisFlags, customDistanceToCamera, RPASS_TREE, customFlags);
|
|
pEntity->SetAddedToDrawListThisFrame(renderPhase->GetDrawListType());
|
|
continue;
|
|
}
|
|
|
|
// if fading then add to fading pass and skip to next object (don't process in any other passes)
|
|
if (!bParabReflectionPhase && alpha<LODTYPES_ALPHA_VISIBLE && !bForceCascadeShadowPhase)
|
|
{
|
|
rlDesc.AddEntity(pEntity, subphaseVisFlags, customDistanceToCamera, RPASS_FADING, customFlags);
|
|
pEntity->SetAddedToDrawListThisFrame(renderPhase->GetDrawListType());
|
|
continue;
|
|
}
|
|
|
|
// determine if object is placed in either the LOD pass or the VISIBLE pass
|
|
if(pEntity->IsBaseFlagSet(fwEntity::HAS_OPAQUE))
|
|
{
|
|
if (pEntity->GetLodData().HasChildren() && !bParabReflectionPhase)
|
|
{
|
|
rlDesc.AddEntity(pEntity, subphaseVisFlags, customDistanceToCamera, RPASS_LOD, customFlags);
|
|
pEntity->SetAddedToDrawListThisFrame(renderPhase->GetDrawListType());
|
|
}
|
|
else
|
|
{
|
|
if( !bRenderingShadows && pEntity->IsBaseFlagSet(fwEntity::FORCE_ALPHA) )
|
|
{
|
|
rlDesc.AddEntity(pEntity, subphaseVisFlags, customDistanceToCamera, RPASS_FADING, customFlags);
|
|
pEntity->SetAddedToDrawListThisFrame(renderPhase->GetDrawListType());
|
|
}
|
|
else
|
|
{
|
|
rlDesc.AddEntity(pEntity, subphaseVisFlags, customDistanceToCamera, RPASS_VISIBLE, customFlags);
|
|
pEntity->SetAddedToDrawListThisFrame(renderPhase->GetDrawListType());
|
|
}
|
|
}
|
|
}
|
|
|
|
if(pEntity->GetBaseFlags() & flagMask)
|
|
{
|
|
u32 phaseMask = renderPhaseMask[i];
|
|
|
|
// add object to other lists if the object is flagged as containing any of the appropriate type of rendering material
|
|
if (pEntity->IsBaseFlagSet(fwEntity::HAS_ALPHA) && (phaseMask&canRenderAlpha) && bShouldDoAlpha)
|
|
{
|
|
if (bIsPed)
|
|
{
|
|
rlDesc.AddEntity(pEntity, pEntity->GetBaseFlags() | fwEntity::DRAW_FIRST_SORTED, pEntity->GetRenderFlags(), subphaseVisFlags, customDistanceToCamera, RPASS_ALPHA, baseCustomFlags);
|
|
pEntity->SetAddedToDrawListThisFrame(renderPhase->GetDrawListType());
|
|
}
|
|
else
|
|
{
|
|
rlDesc.AddEntity(pEntity, subphaseVisFlags, customDistanceToCamera, RPASS_ALPHA, alphaCustomFlags);
|
|
pEntity->SetAddedToDrawListThisFrame(renderPhase->GetDrawListType());
|
|
}
|
|
}
|
|
|
|
if (pEntity->IsBaseFlagSet(fwEntity::HAS_DISPL_ALPHA) && (phaseMask&canRenderAlpha))
|
|
{
|
|
rlDesc.AddEntity(pEntity, subphaseVisFlags, customDistanceToCamera, RPASS_ALPHA, alphaCustomFlags);
|
|
pEntity->SetAddedToDrawListThisFrame(renderPhase->GetDrawListType());
|
|
}
|
|
|
|
if (pEntity->IsBaseFlagSet(fwEntity::HAS_WATER) && (phaseMask&canRenderWater))
|
|
{
|
|
rlDesc.AddEntity(pEntity, subphaseVisFlags, customDistanceToCamera, RPASS_WATER, baseCustomFlags);
|
|
pEntity->SetAddedToDrawListThisFrame(renderPhase->GetDrawListType());
|
|
}
|
|
|
|
if(pEntity->IsBaseFlagSet(fwEntity::HAS_DECAL) && (phaseMask&canRenderDecal) && bShouldDoDecal)
|
|
{
|
|
rlDesc.AddEntity(pEntity, subphaseVisFlags, customDistanceToCamera, RPASS_DECAL, customFlags);
|
|
pEntity->SetAddedToDrawListThisFrame(renderPhase->GetDrawListType());
|
|
}
|
|
|
|
if (pEntity->IsBaseFlagSet(fwEntity::HAS_CUTOUT) && (phaseMask&canRenderCutout) && bShouldDoCutout)
|
|
{
|
|
rlDesc.AddEntity(pEntity, subphaseVisFlags, customDistanceToCamera, RPASS_CUTOUT, customFlags);
|
|
pEntity->SetAddedToDrawListThisFrame(renderPhase->GetDrawListType());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pPvsEntry++;
|
|
}
|
|
}
|
|
|
|
void CPostScan::StartAsyncSearchForAdditionalPreRenderEntities()
|
|
{
|
|
PF_AUTO_PUSH_TIMEBAR("Start PreRender Searches");
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// sphere around the player
|
|
if ( g_scanDebugFlags & SCAN_DEBUG_ENABLE_PRERENDER_SPHERE )
|
|
{
|
|
Assertf(!ms_bAsyncSearchStarted, "Async search for additional PreRender entities already started");
|
|
Vec3V camPos = VECTOR3_TO_VEC3V( camInterface::GetPos() );
|
|
|
|
// Vehicles
|
|
{
|
|
const spdSphere preRenderSphere( camPos, ScalarV(PRERENDER_VEHICLE_SPHERE_RADIUS) );
|
|
fwIsSphereIntersecting testSphere( preRenderSphere );
|
|
|
|
ms_PreRenderVehicleSearch.Start
|
|
(
|
|
fwWorldRepMulti::GetSceneGraphRoot(),
|
|
&testSphere,
|
|
ENTITY_TYPE_MASK_VEHICLE,
|
|
SEARCH_LOCATION_EXTERIORS | SEARCH_LOCATION_INTERIORS,
|
|
SEARCH_LODTYPE_HIGHDETAIL,
|
|
SEARCH_OPTION_DYNAMICS_ONLY,
|
|
WORLDREP_SEARCHMODULE_DEFAULT,
|
|
sysDependency::kPriorityHigh
|
|
);
|
|
}
|
|
|
|
// Peds & Objects
|
|
{
|
|
const spdSphere preRenderSphere( camPos, ScalarV(PRERENDER_PED_AND_OBJECT_SPHERE_RADIUS) );
|
|
fwIsSphereIntersecting testSphere( preRenderSphere );
|
|
|
|
ms_PreRenderPedAndObjectSearch.Start
|
|
(
|
|
fwWorldRepMulti::GetSceneGraphRoot(),
|
|
&testSphere,
|
|
ENTITY_TYPE_MASK_PED | ENTITY_TYPE_MASK_OBJECT,
|
|
SEARCH_LOCATION_EXTERIORS | SEARCH_LOCATION_INTERIORS,
|
|
SEARCH_LODTYPE_HIGHDETAIL,
|
|
SEARCH_OPTION_DYNAMICS_ONLY,
|
|
WORLDREP_SEARCHMODULE_DEFAULT,
|
|
sysDependency::kPriorityHigh
|
|
);
|
|
}
|
|
|
|
ms_bAsyncSearchStarted = true;
|
|
}
|
|
}
|
|
|
|
void CPostScan::EndAsyncSearchForAdditionalPreRenderEntities()
|
|
{
|
|
if ( g_scanDebugFlags & SCAN_DEBUG_ENABLE_PRERENDER_SPHERE )
|
|
{
|
|
ms_PreRenderVehicleSearch.Finalize();
|
|
ms_PreRenderPedAndObjectSearch.Finalize();
|
|
ms_bAsyncSearchStarted = false;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION: AddToAlwaysPreRenderList
|
|
// PURPOSE: script may require that certain entities always be prerendered regardless of whether
|
|
// they are visible or not. sets a flag in the entity and pushes it onto a linked list.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CPostScan::AddToAlwaysPreRenderList(CEntity* pEntity)
|
|
{
|
|
if ( pEntity && pEntity->IsBaseFlagSet(fwEntity::HAS_PRERENDER) && !pEntity->GetAlwaysPreRender() )
|
|
{
|
|
pEntity->SetAlwaysPreRender(true);
|
|
m_alwaysPreRenderList.Add(pEntity);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION: RemoveFromAlwaysPreRenderList
|
|
// PURPOSE: script may require that certain entities always be prerendered regardless of whether
|
|
// they are visible or not. clears a flag in the entity and removes it from a linked list.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CPostScan::RemoveFromAlwaysPreRenderList(CEntity* pEntity)
|
|
{
|
|
if ( pEntity && Verifyf( pEntity->GetAlwaysPreRender(), "Trying to remove entity %s from \"always pre render\" list but it is not currently inserted", pEntity->GetModelName() ) )
|
|
{
|
|
pEntity->SetAlwaysPreRender(false);
|
|
m_alwaysPreRenderList.Remove(pEntity);
|
|
}
|
|
}
|
|
|
|
void CPostScan::AddToNonVisiblePreRenderList(CEntity* pEntity)
|
|
{
|
|
Assert(pEntity);
|
|
|
|
m_nonVisiblePreRenderList.AddEntity(pEntity);
|
|
}
|
|
|
|
void CPostScan::StartSortPVSJob()
|
|
{
|
|
Assert(!g_SortPVSDependencyRunning);
|
|
|
|
if(GetPVSBase() == GetPVSEnd())
|
|
{
|
|
return;
|
|
}
|
|
|
|
int minSortVal = MAX_INT32;
|
|
int maxSortVal = MIN_INT32;
|
|
for(int i = 0; i < LODTYPES_DEPTH_TOTAL; i++)
|
|
{
|
|
int sortVal = fwLodData::GetSortVal(i);
|
|
minSortVal = sortVal < minSortVal ? sortVal : minSortVal;
|
|
maxSortVal = sortVal > maxSortVal ? sortVal : maxSortVal;
|
|
}
|
|
|
|
g_SortPVSDependency.m_Params[0].m_AsPtr = GetPVSBase();
|
|
g_SortPVSDependency.m_Params[1].m_AsPtr = &m_pCurrPVSEntry;
|
|
g_SortPVSDependency.m_Params[2].m_AsPtr = &g_SortPVSDependencyRunning;
|
|
#if __BANK
|
|
g_SortPVSDependency.m_Params[3].m_AsBool = CPostScanDebug::ShouldPartitionPvsInsteadOfSorting();
|
|
#else
|
|
g_SortPVSDependency.m_Params[3].m_AsBool = true;
|
|
#endif
|
|
g_SortPVSDependency.m_Params[4].m_AsInt = minSortVal;
|
|
g_SortPVSDependency.m_Params[5].m_AsInt = maxSortVal;
|
|
g_SortPVSDependency.m_Params[6].m_AsPtr = GetPVSBase();
|
|
g_SortPVSDependency.m_DataSizes[0] = ptrdiff_t_to_int((u8*)GetPVSEnd() - (u8*)GetPVSBase());
|
|
|
|
g_SortPVSDependencyRunning = 1;
|
|
|
|
sysDependencyScheduler::Insert( &g_SortPVSDependency );
|
|
}
|
|
|
|
void CPostScan::WaitForSortPVSDependency()
|
|
{
|
|
PF_PUSH_TIMEBAR("Wait For Sort PVS Dependency");
|
|
while(g_SortPVSDependencyRunning)
|
|
{
|
|
sysIpcYield(PRIO_NORMAL);
|
|
}
|
|
PF_POP_TIMEBAR();
|
|
}
|
|
|
|
void CPostScan::WaitForPostScanHelper()
|
|
{
|
|
PF_PUSH_TIMEBAR("Wait for BuildRenderListsFromPVS Dependency");
|
|
while(g_BuildRenderListsFromPVSDependencyRunning)
|
|
{
|
|
sysIpcYield(PRIO_NORMAL);
|
|
}
|
|
PF_POP_TIMEBAR();
|
|
}
|
|
|
|
static bool sm_HasAlphaOverrideInfoBeenDumped = false;
|
|
|
|
bool CPostScan::AddAlphaOverride(CEntity *pEntity, int alpha BANK_PARAM(const atString& ownerString) )
|
|
{
|
|
int freeIdx = -1;
|
|
|
|
for(int i=0;i<MAX_ALPHA_OVERRIDES;i++)
|
|
{
|
|
if( ms_alphaOverrides[i].entity == pEntity )
|
|
{
|
|
ms_alphaOverrides[i].alpha = alpha;
|
|
return true;
|
|
}
|
|
else if( ms_alphaOverrides[i].entity == NULL )
|
|
{
|
|
freeIdx = i;
|
|
}
|
|
}
|
|
|
|
if( freeIdx != -1 )
|
|
{
|
|
ms_alphaOverrides[freeIdx].entity = pEntity;
|
|
ms_alphaOverrides[freeIdx].alpha = alpha;
|
|
|
|
#if __BANK
|
|
ms_alphaOverrides[freeIdx].owner = ownerString;
|
|
#endif
|
|
|
|
ms_hasAnyAlphaOverride = true;
|
|
ms_lastAlphaOverride = Max(ms_lastAlphaOverride,freeIdx+1);
|
|
}
|
|
#if __BANK
|
|
else if(!sm_HasAlphaOverrideInfoBeenDumped)
|
|
{
|
|
sm_HasAlphaOverrideInfoBeenDumped = true;
|
|
gnetDebug1("Failed to find free index for alpha override. Dumping array:");
|
|
for(int i=0;i<MAX_ALPHA_OVERRIDES;i++)
|
|
{
|
|
gnetDebug1("Entity: %s with alpha: %d", ms_alphaOverrides[i].entity ? ms_alphaOverrides[i].entity->GetLogName() : "Empty", ms_alphaOverrides[i].alpha);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return freeIdx != -1;
|
|
}
|
|
|
|
|
|
bool CPostScan::IsOnAlphaOverrideList(CEntity* pEntity)
|
|
{
|
|
for(int i=0;i<MAX_ALPHA_OVERRIDES;i++)
|
|
{
|
|
if( ms_alphaOverrides[i].entity == pEntity )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#if __BANK
|
|
|
|
const char* CPostScan::GetAlphaOverrideOwner(CEntity* pEntity)
|
|
{
|
|
for(int i=0;i<MAX_ALPHA_OVERRIDES;i++)
|
|
{
|
|
if( ms_alphaOverrides[i].entity == pEntity )
|
|
{
|
|
return ms_alphaOverrides[i].owner.c_str();
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
int CPostScan::GetAlphaOverride(CEntity *pEntity)
|
|
{
|
|
for(int i=0;i<MAX_ALPHA_OVERRIDES;i++)
|
|
{
|
|
if( ms_alphaOverrides[i].entity == pEntity )
|
|
{
|
|
return ms_alphaOverrides[i].alpha;
|
|
}
|
|
}
|
|
|
|
return pEntity->GetAlpha();
|
|
}
|
|
|
|
bool CPostScan::GetAlphaOverride(const CEntity *pEntity, int& alpha)
|
|
{
|
|
alpha = 0;
|
|
for(int i=0;i<MAX_ALPHA_OVERRIDES;i++)
|
|
{
|
|
if( ms_alphaOverrides[i].entity == pEntity )
|
|
{
|
|
alpha = ms_alphaOverrides[i].alpha;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CPostScan::RemoveAlphaOverride(CEntity *pEntity)
|
|
{
|
|
for(int i=0;i<MAX_ALPHA_OVERRIDES;i++)
|
|
{
|
|
if( ms_alphaOverrides[i].entity == pEntity )
|
|
{
|
|
ms_alphaOverrides[i].entity = NULL;
|
|
ms_alphaOverrides[i].alpha = 0;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPostScan::AddEntitySortBias(CEntity *pEntity, float bias)
|
|
{
|
|
int count = ms_sortBiases.GetCount();
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
if(ms_sortBiases[i].entity == pEntity)
|
|
{
|
|
ms_sortBiases[i].bias = bias;
|
|
return;
|
|
}
|
|
}
|
|
|
|
entitySortBias& newBias = ms_sortBiases.Grow();
|
|
newBias.entity = pEntity;
|
|
newBias.bias = bias;
|
|
}
|
|
|
|
void CPostScan::RemoveEntitySortBias(CEntity *pEntity)
|
|
{
|
|
for(int i = 0; i < ms_sortBiases.GetCount(); i++)
|
|
{
|
|
if(ms_sortBiases[i].entity == pEntity)
|
|
{
|
|
ms_sortBiases.DeleteFast(i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPostScan::PossiblyAddToLateLightList(CEntity* pEntity)
|
|
{
|
|
if (ms_bAllowLateLightEntities && pEntity &&
|
|
(CutSceneManager::GetInstance()->IsCutscenePlayingBack() || gStreamingRequestList.IsPlaybackActive()))
|
|
{
|
|
if(ms_LateLightsNearCameraSphere.IntersectsSphere(pEntity->GetBoundSphere()))
|
|
{
|
|
ms_lateLights.PushAndGrow(pEntity, 64);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool BuildRenderListsFromPVS_Dependency(const sysDependency& )
|
|
{
|
|
CSystem::SetThisThreadId(SYS_THREAD_POSTSCAN);
|
|
|
|
PIXBegin(0, "PostScanHelper");
|
|
gRenderListGroup.SetThreadAccessMask(SYS_THREAD_POSTSCAN);
|
|
|
|
{
|
|
PF_FUNC(BuildRenderLists);
|
|
gPostScan.BuildRenderListsFromPVS();
|
|
}
|
|
|
|
{
|
|
PF_FUNC(SortRenderLists);
|
|
|
|
// Sorting will be done asynchronously - this loop will set up all of the job data
|
|
atFixedArray<int, MAX_NUM_RENDER_PHASES> renderListsToSort;
|
|
int count = RENDERPHASEMGR.GetRenderPhaseCount();
|
|
for(int i=0;i<count;i++)
|
|
{
|
|
fwRenderPhase &renderPhase = RENDERPHASEMGR.GetRenderPhase(i);
|
|
|
|
if (renderPhase.HasEntityListIndex())
|
|
{
|
|
// make sure we only sort each list once
|
|
int EntityListIndex = renderPhase.GetEntityListIndex();
|
|
if(renderListsToSort.Find(EntityListIndex) == -1)
|
|
{
|
|
renderListsToSort.Push(EntityListIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
for(int i = 0; i < renderListsToSort.GetCount(); i++)
|
|
{
|
|
gRenderListGroup.GetRenderListForPhase(renderListsToSort[i]).PostRenderListBuild();
|
|
}
|
|
|
|
#if SORT_RENDERLISTS_ON_SPU
|
|
// This will kick off the sorting job - we block wait for the results near the end of CGame::Update()
|
|
gRenderListGroup.StartSortEntityListJob();
|
|
#endif //SORT_RENDERLISTS_ON_SPU
|
|
}
|
|
|
|
gRenderListGroup.SetThreadAccessMask(sysThreadType::THREAD_TYPE_UPDATE);
|
|
CSystem::ClearThisThreadId(SYS_THREAD_POSTSCAN);
|
|
|
|
g_BuildRenderListsFromPVSDependencyRunning = 0;
|
|
|
|
PIXEnd();
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION: Process
|
|
// PURPOSE: strip out specific exterior models from gbuf. assumed PVS is sorted by lod level.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CPostScanExteriorHdCull::Process(CGtaPvsEntry* pStart, CGtaPvsEntry* pStop)
|
|
{
|
|
if ( Unlikely(IsActive()) )
|
|
{
|
|
const u32 hdSortVal = fwLodData::GetSortVal(LODTYPES_DEPTH_HD);
|
|
const u32 gbufIndex = g_SceneToGBufferPhaseNew->GetEntityListIndex();
|
|
const u32 mirrorReflectionIndex = g_MirrorReflectionPhase->GetEntityListIndex();
|
|
const u32 shadowIndex = g_CascadeShadowsNew->GetEntityListIndex();
|
|
bool bStillProcessingHd = true;
|
|
|
|
CGtaPvsEntry* pPvsEntry = (pStop - 1);
|
|
while (bStillProcessingHd && pPvsEntry>=pStart)
|
|
{
|
|
if ( pPvsEntry && pPvsEntry->GetEntity()
|
|
&& pPvsEntry->GetVisFlags().GetLodSortVal()==hdSortVal
|
|
&& (pPvsEntry->GetVisFlags().GetOptionFlags()&VIS_FLAG_OPTION_INTERIOR)==0 )
|
|
{
|
|
fwVisibilityFlags& visFlags = pPvsEntry->GetVisFlags();
|
|
|
|
CEntity* pEntity = pPvsEntry->GetEntity();
|
|
if (ShouldCullEntity(pEntity))
|
|
{
|
|
visFlags.ClearVisBit(gbufIndex);
|
|
visFlags.ClearVisBit(mirrorReflectionIndex);
|
|
}
|
|
|
|
if (ShouldShadowCullEntity(pEntity))
|
|
{
|
|
visFlags.ClearVisBit(shadowIndex);
|
|
}
|
|
|
|
}
|
|
|
|
if (pPvsEntry->GetEntity())
|
|
{
|
|
bStillProcessingHd = ( pPvsEntry->GetVisFlags().GetLodSortVal()==hdSortVal );
|
|
}
|
|
pPvsEntry--;
|
|
}
|
|
|
|
Init();
|
|
}
|
|
}
|
|
|
|
|
|
bool CPostScanExteriorHdCull::ShouldCullEntity(CEntity* pEntity)
|
|
{
|
|
if (pEntity)
|
|
{
|
|
// cull by sphere
|
|
if (m_bCullSphereEnabled)
|
|
{
|
|
const spdAABB entityBox = pEntity->GetBoundBox();
|
|
if (entityBox.IntersectsSphere(m_cullSphere))
|
|
return true;
|
|
}
|
|
// cull by model name
|
|
if (m_cullList.GetCount()>0)
|
|
{
|
|
CBaseModelInfo* pModelInfo = pEntity->GetBaseModelInfo();
|
|
if (pModelInfo)
|
|
{
|
|
const u32 modelNameHash = pModelInfo->GetModelNameHash();
|
|
for (s32 i=0; i<m_cullList.GetCount(); i++)
|
|
{
|
|
if (m_cullList[i]==modelNameHash)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CPostScanExteriorHdCull::ShouldShadowCullEntity(CEntity* pEntity)
|
|
{
|
|
if (pEntity)
|
|
{
|
|
// shadow cull by model name
|
|
if (m_shadowCullList.GetCount()>0)
|
|
{
|
|
CBaseModelInfo* pModelInfo = pEntity->GetBaseModelInfo();
|
|
if (pModelInfo)
|
|
{
|
|
const u32 modelNameHash = pModelInfo->GetModelNameHash();
|
|
for (s32 i=0; i<m_shadowCullList.GetCount(); i++)
|
|
{
|
|
if (m_shadowCullList[i]==modelNameHash)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#define CARRIER_GROUP_ID (100)
|
|
|
|
void ForceUpLinkedInteriors(fwInteriorLocation intLoc, CInteriorInst *pIntInst, const spdTransposedPlaneSet8 &frustum, atArray<fwInteriorLocation> &visitedLocs)
|
|
{
|
|
if (!pIntInst)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// for not-tunnel linked interiors, or the carrier (because it is so _special_) - bug 2016877
|
|
bool bGroupTest = pIntInst->GetProxy()->GetGroupId() == 0 || pIntInst->GetProxy()->GetGroupId() == CARRIER_GROUP_ID;
|
|
|
|
if (bGroupTest && pIntInst->GetProxy()->GetCurrentState() >= CInteriorProxy::PS_FULL)
|
|
{
|
|
s32 roomIndex = intLoc.GetRoomIndex();
|
|
for(u32 i = 0; i < pIntInst->GetNumPortalsInRoom(roomIndex); ++i)
|
|
{
|
|
if (pIntInst->IsMirrorPortal(roomIndex, i))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pIntInst->IsLinkPortal(roomIndex, i))
|
|
{
|
|
s32 portalIdx = pIntInst->GetPortalIdxInRoom(roomIndex, i);
|
|
const fwPortalCorners &portalCorners = pIntInst->GetPortal(portalIdx);
|
|
spdAABB aabb;
|
|
portalCorners.CalcBoundingBox(aabb);
|
|
|
|
if (frustum.IntersectsAABB(aabb))
|
|
{
|
|
CPortalInst* pPortalInst = pIntInst->GetMatchingPortalInst(roomIndex,i);
|
|
if (Verifyf(pPortalInst, "Failed to get pPortalInst"))
|
|
{
|
|
// update using info from the linked portal
|
|
CInteriorInst* pDestIntInst = NULL;
|
|
s32 destPortalIdx = -1;
|
|
|
|
// get data about linked interior through this portal
|
|
pPortalInst->GetDestinationData(pIntInst, pDestIntInst, destPortalIdx);
|
|
if (pDestIntInst)
|
|
{
|
|
if (pDestIntInst->SetAlphaForRoomZeroModels(LODTYPES_ALPHA_VISIBLE))
|
|
{
|
|
pDestIntInst->SuppressInternalLOD();
|
|
|
|
fwInteriorLocation linkLoc;
|
|
s32 destRoomIdx = pDestIntInst->GetDestThruPortalInRoom(0, destPortalIdx);
|
|
|
|
|
|
if (destRoomIdx > 0)
|
|
{
|
|
linkLoc.SetInteriorProxyIndex(pDestIntInst->GetProxy()->GetInteriorProxyPoolIdx());
|
|
linkLoc.SetRoomIndex(destRoomIdx);
|
|
bool alreadyVisited = false;
|
|
for (int j = 0; j < visitedLocs.GetCount(); ++j)
|
|
{
|
|
if (linkLoc.GetAsUint32() == visitedLocs[j].GetAsUint32())
|
|
{
|
|
alreadyVisited = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!alreadyVisited)
|
|
{
|
|
if (visitedLocs.GetCount() < visitedLocs.GetCapacity())
|
|
{
|
|
visitedLocs.Append() = intLoc;
|
|
ForceUpLinkedInteriors(linkLoc, pDestIntInst, frustum, visitedLocs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|