From c1e344ca94e4503335abd0ddb8cb5e54a6df1f62 Mon Sep 17 00:00:00 2001 From: Justin Marshall Date: Fri, 8 May 2026 00:45:48 -0700 Subject: [PATCH] DXR now supports portal culling. --- neo/opengl/gl_d3d12raylight.cpp | 161 +++- neo/opengl/gl_d3d12shim.cpp | 157 +++- neo/opengl/opengl.h | 23 + neo/prey/game/Game_local.h | 2 + neo/renderer/Material.cpp | 14 + neo/renderer/Material.h | 18 +- neo/renderer/Model.cpp | 15 + neo/renderer/RenderWorld.cpp | 14 + neo/renderer/RenderWorld_load.cpp | 3 +- neo/renderer/RenderWorld_local.h | 3 + neo/renderer/RenderWorld_portals.cpp | 1068 +++++++++++++++++++------- neo/renderer/tr_light.cpp | 9 + 12 files changed, 1212 insertions(+), 275 deletions(-) diff --git a/neo/opengl/gl_d3d12raylight.cpp b/neo/opengl/gl_d3d12raylight.cpp index 1580e20d..aa7ea04c 100644 --- a/neo/opengl/gl_d3d12raylight.cpp +++ b/neo/opengl/gl_d3d12raylight.cpp @@ -1275,7 +1275,7 @@ static inline void glRaytracingBuildInstanceDesc( (meshMaterialFlags | instanceMaterialFlags) & GL_RAYTRACING_INSTANCE_MATERIAL_MASK; const uint32_t materialBits = combinedMaterialFlags << GL_RAYTRACING_INSTANCE_MATERIAL_SHIFT; outDesc->InstanceID = userInstanceId | materialBits; - outDesc->InstanceMask = (UINT8)(inst.descCpu.mask ? inst.descCpu.mask : 0xFF); + outDesc->InstanceMask = (UINT8)(inst.descCpu.mask & 0xFFu); outDesc->InstanceContributionToHitGroupIndex = 0; outDesc->Flags = D3D12_RAYTRACING_INSTANCE_FLAG_NONE; outDesc->AccelerationStructure = blasGpuVA; @@ -2066,6 +2066,12 @@ void glRaytracingDeleteMesh(glRaytracingMeshHandle_t meshHandle) glRaytracingMarkAllWorldsNeedRebuild(); } +static inline uint32_t glRaytracingNormalizeVisibleInstanceMask(uint32_t mask) +{ + mask &= 0xFFu; + return mask ? mask : 0xFFu; +} + glRaytracingInstanceHandle_t glRaytracingCreateInstanceInScene(glRaytracingSceneHandle_t worldHandle, const glRaytracingInstanceDesc_t* desc) { std::lock_guard lock(g_glRaytracingMutex); @@ -2084,6 +2090,7 @@ glRaytracingInstanceHandle_t glRaytracingCreateInstanceInScene(glRaytracingScene inst.handle = world->nextInstanceHandle++; inst.alive = 1; inst.descCpu = *desc; + inst.descCpu.mask = glRaytracingNormalizeVisibleInstanceMask(inst.descCpu.mask); inst.dirty = 1; world->instances.push_back(inst); @@ -2113,8 +2120,18 @@ int glRaytracingUpdateInstanceInScene(glRaytracingSceneHandle_t worldHandle, glR return 0; const uint32_t oldMeshHandle = inst->descCpu.meshHandle; + const uint32_t oldMask = ((uint32_t)inst->descCpu.mask) & 0xFFu; + const int wasHidden = (oldMask == 0u); - inst->descCpu = *desc; + glRaytracingInstanceDesc_t newDesc = *desc; + + // If this instance is hidden, keep it hidden even if the caller's + // transform-update helper sends mask = 0xFF again. + newDesc.mask = wasHidden + ? 0u + : glRaytracingNormalizeVisibleInstanceMask(newDesc.mask); + + inst->descCpu = newDesc; inst->dirty = 1; if (oldMeshHandle != desc->meshHandle) @@ -4494,7 +4511,7 @@ void RayGen() // then reuse them for every stochastic GI sample. float cavity = ComputeCavity(pixel, worldPos, N); float ao = ComputeAmbientOcclusion(worldPos, N, pixel); - float skyVis = ComputeSkyVisibility(worldPos, N, pixel); + float skyVis = 0; // ComputeSkyVisibility(worldPos, N, pixel); // jmarshall - fix me later float ambientSkyVis = TraceStraightUpToSky(worldPos, N); float microShadow = lerp(0.75, 1.0, cavity); @@ -6734,3 +6751,141 @@ uint32_t glRaytracingLightingGetLightCount(void) return (uint32_t)g_glRaytracingLighting.cpuLights.size(); } +int glRaytracingSetInstanceVisibilityUnlocked( + glRaytracingRenderWorld_t* world, + glRaytracingInstanceHandle_t instanceHandle, + int visible) +{ + if (!world || !world->alive) + return 0; + + glRaytracingInstanceRecord_t* inst = + glRaytracingFindInstance(world, instanceHandle); + + if (!inst || !inst->alive) + return 0; + + const uint32_t newMask = visible ? 0xFFu : 0u; + + if ((((uint32_t)inst->descCpu.mask) & 0xFFu) == newMask) + return 1; + + inst->descCpu.mask = newMask; + inst->dirty = 1; + + glRaytracingMarkWorldNeedsUpdate(world); + return 1; +} + +static void glRaytracingSetAllInstancesVisibleUnlocked( + glRaytracingRenderWorld_t* world, + int visible) +{ + if (!world || !world->alive) + return; + + const uint32_t newMask = visible ? 0xFFu : 0u; + int changed = 0; + + for (size_t i = 0; i < world->instances.size(); ++i) + { + glRaytracingInstanceRecord_t& inst = world->instances[i]; + + if (!inst.alive) + continue; + + const int wasVisible = inst.descCpu.mask != 0; + if (wasVisible == (visible != 0)) + continue; + + inst.descCpu.mask = newMask; + inst.dirty = 1; + changed = 1; + } + + if (changed) + glRaytracingMarkWorldNeedsUpdate(world); +} + +int glRaytracingSetInstanceVisibilityInScene( + glRaytracingSceneHandle_t sceneHandle, + glRaytracingInstanceHandle_t instanceHandle, + int visible) +{ + std::lock_guard lock(g_glRaytracingMutex); + + if (!g_glRaytracingScene.initialized) + return 0; + + glRaytracingRenderWorld_t* world = glRaytracingFindWorld(sceneHandle); + return glRaytracingSetInstanceVisibilityUnlocked( + world, + instanceHandle, + visible ? 1 : 0); +} + +int glRaytracingGetInstanceVisibilityInScene( + glRaytracingSceneHandle_t sceneHandle, + glRaytracingInstanceHandle_t instanceHandle) +{ + std::lock_guard lock(g_glRaytracingMutex); + + if (!g_glRaytracingScene.initialized) + return 0; + + glRaytracingRenderWorld_t* world = glRaytracingFindWorld(sceneHandle); + if (!world) + return 0; + + glRaytracingInstanceRecord_t* inst = + glRaytracingFindInstance(world, instanceHandle); + + if (!inst || !inst->alive) + return 0; + + return inst->descCpu.mask != 0 ? 1 : 0; +} + +void glRaytracingHideAllInstancesInScene(glRaytracingSceneHandle_t sceneHandle) +{ + std::lock_guard lock(g_glRaytracingMutex); + + if (!g_glRaytracingScene.initialized) + return; + + glRaytracingRenderWorld_t* world = glRaytracingFindWorld(sceneHandle); + glRaytracingSetAllInstancesVisibleUnlocked(world, 0); +} + +void glRaytracingShowAllInstancesInScene(glRaytracingSceneHandle_t sceneHandle) +{ + std::lock_guard lock(g_glRaytracingMutex); + + if (!g_glRaytracingScene.initialized) + return; + + glRaytracingRenderWorld_t* world = glRaytracingFindWorld(sceneHandle); + glRaytracingSetAllInstancesVisibleUnlocked(world, 1); +} + +void glRaytracingHideAllInstances(void) +{ + std::lock_guard lock(g_glRaytracingMutex); + + if (!g_glRaytracingScene.initialized) + return; + + for (int i = 0; i < GL_RAYTRACING_MAX_RENDER_WORLDS; ++i) + glRaytracingSetAllInstancesVisibleUnlocked(&g_glRaytracingScene.worlds[i], 0); +} + +void glRaytracingShowAllInstances(void) +{ + std::lock_guard lock(g_glRaytracingMutex); + + if (!g_glRaytracingScene.initialized) + return; + + for (int i = 0; i < GL_RAYTRACING_MAX_RENDER_WORLDS; ++i) + glRaytracingSetAllInstancesVisibleUnlocked(&g_glRaytracingScene.worlds[i], 1); +} \ No newline at end of file diff --git a/neo/opengl/gl_d3d12shim.cpp b/neo/opengl/gl_d3d12shim.cpp index 562386dc..cc3a523d 100644 --- a/neo/opengl/gl_d3d12shim.cpp +++ b/neo/opengl/gl_d3d12shim.cpp @@ -242,6 +242,32 @@ void glRaytracingLightingSetSpecularInput(ID3D12Resource* texture, DXGI_FORMAT f void glRaytracingSetMeshMaterialFlags(glRaytracingMeshHandle_t meshHandle, uint32_t materialFlags); void glRaytracingSetMeshGlass(glRaytracingMeshHandle_t meshHandle, int isGlass); uint32_t glRaytracingGetMeshMaterialFlags(glRaytracingMeshHandle_t meshHandle); + +// Fast TLAS instance visibility controls are implemented in gl_raytracing.cpp. +// They flip the D3D12_RAYTRACING_INSTANCE_DESC InstanceMask via the existing +// TLAS update path, so hiding/showing a model does not rebuild or recreate BLAS. +int glRaytracingSetInstanceVisibilityInScene(glRaytracingSceneHandle_t sceneHandle, glRaytracingInstanceHandle_t instanceHandle, int visible); +int glRaytracingGetInstanceVisibilityInScene(glRaytracingSceneHandle_t sceneHandle, glRaytracingInstanceHandle_t instanceHandle); +void glRaytracingHideAllInstancesInScene(glRaytracingSceneHandle_t sceneHandle); +void glRaytracingShowAllInstancesInScene(glRaytracingSceneHandle_t sceneHandle); +void glRaytracingHideAllInstances(void); +void glRaytracingShowAllInstances(void); + +// Shim/public helpers for the app's top-level acceleration-structure handles. +int glSetTopLevelAccelStructureVisible(glRaytracingSceneHandle_t scene, uint32_t topLevelHandle, int visible); +void glHideTopLevelAccelStructure(glRaytracingSceneHandle_t scene, uint32_t topLevelHandle); +void glShowTopLevelAccelStructure(glRaytracingSceneHandle_t scene, uint32_t topLevelHandle); +int glIsTopLevelAccelStructureVisible(glRaytracingSceneHandle_t scene, uint32_t topLevelHandle); +void glHideAllTopLevelAccelStructures(glRaytracingSceneHandle_t scene); +void glShowAllTopLevelAccelStructures(glRaytracingSceneHandle_t scene); + +// Backward-compatible aliases matching the existing Aceel typo in this file. +int glSetTopLevelAceelStructureVisible(glRaytracingSceneHandle_t scene, uint32_t topLevelHandle, int visible); +void glHideTopLevelAceelStructure(glRaytracingSceneHandle_t scene, uint32_t topLevelHandle); +void glShowTopLevelAceelStructure(glRaytracingSceneHandle_t scene, uint32_t topLevelHandle); +int glIsTopLevelAceelStructureVisible(glRaytracingSceneHandle_t scene, uint32_t topLevelHandle); +void glHideAllTopLevelAceelStructures(glRaytracingSceneHandle_t scene); +void glShowAllTopLevelAceelStructures(glRaytracingSceneHandle_t scene); void QD3D12_SetPathTracingQuality(uint32_t samplesPerPixel, uint32_t maxBounces); void QD3D12_SetPathTracingFallbackSamples(uint32_t samplesPerPixel); void QD3D12_SetCameraInfo( @@ -11976,6 +12002,18 @@ PROC WINAPI qd3d12_wglGetProcAddress(LPCSTR name) { { "QD3D12_ResolveGBufferNow", (PROC)QD3D12_ResolveGBufferNow }, { "QD3D12_SetPathTracingQuality", (PROC)QD3D12_SetPathTracingQuality }, { "QD3D12_SetPathTracingFallbackSamples", (PROC)QD3D12_SetPathTracingFallbackSamples }, + { "glSetTopLevelAccelStructureVisible", (PROC)glSetTopLevelAccelStructureVisible }, + { "glHideTopLevelAccelStructure", (PROC)glHideTopLevelAccelStructure }, + { "glShowTopLevelAccelStructure", (PROC)glShowTopLevelAccelStructure }, + { "glIsTopLevelAccelStructureVisible", (PROC)glIsTopLevelAccelStructureVisible }, + { "glHideAllTopLevelAccelStructures", (PROC)glHideAllTopLevelAccelStructures }, + { "glShowAllTopLevelAccelStructures", (PROC)glShowAllTopLevelAccelStructures }, + { "glSetTopLevelAceelStructureVisible", (PROC)glSetTopLevelAceelStructureVisible }, + { "glHideTopLevelAceelStructure", (PROC)glHideTopLevelAceelStructure }, + { "glShowTopLevelAceelStructure", (PROC)glShowTopLevelAceelStructure }, + { "glIsTopLevelAceelStructureVisible", (PROC)glIsTopLevelAceelStructureVisible }, + { "glHideAllTopLevelAceelStructures", (PROC)glHideAllTopLevelAceelStructures }, + { "glShowAllTopLevelAceelStructures", (PROC)glShowAllTopLevelAceelStructures }, { "glRaytracingLightingSetVolumetricScattering", (PROC)glRaytracingLightingSetVolumetricScattering }, { "glGenProgramsARB", (PROC)glGenProgramsARB }, { "glDeleteProgramsARB", (PROC)glDeleteProgramsARB }, @@ -14257,6 +14295,108 @@ void glUpdateBottomAccelStructure(bool opaque, uint32_t& meshHandle) } } + +int glSetTopLevelAccelStructureVisible( + glRaytracingSceneHandle_t scene, + uint32_t topLevelHandle, + int visible) +{ + if (scene == 0 || topLevelHandle == 0) + return 0; + + return glRaytracingSetInstanceVisibilityInScene( + scene, + (glRaytracingInstanceHandle_t)topLevelHandle, + visible ? 1 : 0); +} + +void glHideTopLevelAccelStructure( + glRaytracingSceneHandle_t scene, + uint32_t topLevelHandle) +{ + (void)glSetTopLevelAccelStructureVisible(scene, topLevelHandle, 0); +} + +void glShowTopLevelAccelStructure( + glRaytracingSceneHandle_t scene, + uint32_t topLevelHandle) +{ + (void)glSetTopLevelAccelStructureVisible(scene, topLevelHandle, 1); +} + +int glIsTopLevelAccelStructureVisible( + glRaytracingSceneHandle_t scene, + uint32_t topLevelHandle) +{ + if (scene == 0 || topLevelHandle == 0) + return 0; + + return glRaytracingGetInstanceVisibilityInScene( + scene, + (glRaytracingInstanceHandle_t)topLevelHandle) ? 1 : 0; +} + +void glHideAllTopLevelAccelStructures(glRaytracingSceneHandle_t scene) +{ + if (scene == 0) + { + glRaytracingHideAllInstances(); + return; + } + + glRaytracingHideAllInstancesInScene(scene); +} + +void glShowAllTopLevelAccelStructures(glRaytracingSceneHandle_t scene) +{ + if (scene == 0) + { + glRaytracingShowAllInstances(); + return; + } + + glRaytracingShowAllInstancesInScene(scene); +} + +int glSetTopLevelAceelStructureVisible( + glRaytracingSceneHandle_t scene, + uint32_t topLevelHandle, + int visible) +{ + return glSetTopLevelAccelStructureVisible(scene, topLevelHandle, visible); +} + +void glHideTopLevelAceelStructure( + glRaytracingSceneHandle_t scene, + uint32_t topLevelHandle) +{ + glHideTopLevelAccelStructure(scene, topLevelHandle); +} + +void glShowTopLevelAceelStructure( + glRaytracingSceneHandle_t scene, + uint32_t topLevelHandle) +{ + glShowTopLevelAccelStructure(scene, topLevelHandle); +} + +int glIsTopLevelAceelStructureVisible( + glRaytracingSceneHandle_t scene, + uint32_t topLevelHandle) +{ + return glIsTopLevelAccelStructureVisible(scene, topLevelHandle); +} + +void glHideAllTopLevelAceelStructures(glRaytracingSceneHandle_t scene) +{ + glHideAllTopLevelAccelStructures(scene); +} + +void glShowAllTopLevelAceelStructures(glRaytracingSceneHandle_t scene) +{ + glShowAllTopLevelAccelStructures(scene); +} + void glUpdateTopLevelAceelStructure( glRaytracingSceneHandle_t scene, uint32_t mesh, @@ -14271,7 +14411,10 @@ void glUpdateTopLevelAceelStructure( const uint32_t materialFlags = QD3D12_GetRaytracingMeshMaterialFlags(mesh); instDesc.instanceID = QD3D12_EncodeRaytracingInstanceId(0u, materialFlags); - instDesc.mask = 0xFF; + + // Default for newly-created instances. Existing hidden instances preserve + // their current mask below instead of being forced visible by this update path. + instDesc.mask = 0xFFu; if (transform == NULL) { @@ -14288,18 +14431,24 @@ void glUpdateTopLevelAceelStructure( if (topLevelHandle == 0) { + instDesc.mask = 0xFFu; topLevelHandle = glRaytracingCreateInstanceInScene(scene, &instDesc); return; } + // Preserve the current hide/show state across transform/material updates. + const int wasVisible = glRaytracingGetInstanceVisibilityInScene( + scene, + (glRaytracingInstanceHandle_t)topLevelHandle); + instDesc.mask = wasVisible ? 0xFFu : 0u; + if (!glRaytracingUpdateInstanceInScene( scene, (glRaytracingInstanceHandle_t)topLevelHandle, &instDesc)) { - // The saved handle can become stale if the scene was cleared/deleted, or if - // the caller accidentally reuses a handle from another render world. Create - // a new per-scene instance and return that handle to the caller. + // Stale handle path: create a fresh visible instance. + instDesc.mask = 0xFFu; topLevelHandle = glRaytracingCreateInstanceInScene(scene, &instDesc); } } \ No newline at end of file diff --git a/neo/opengl/opengl.h b/neo/opengl/opengl.h index 770aea4e..9bc0b195 100644 --- a/neo/opengl/opengl.h +++ b/neo/opengl/opengl.h @@ -2300,3 +2300,26 @@ void APIENTRY glTextureGlowMap(GLuint texture, GLboolean isGlowMap); // alias void APIENTRY glBindGlowMapTexture(GLuint texture); // 0 disables explicit glow map void APIENTRY glGlowMapTexture(GLuint texture); // alias void APIENTRY glGlowMapStrengthf(GLfloat strength); // default 1.0; >1 allows overbright emission + +int APIENTRY glSetTopLevelAccelStructureVisible( + glRaytracingSceneHandle_t scene, + uint32_t topLevelHandle, + int visible); + +void APIENTRY glHideTopLevelAccelStructure( + glRaytracingSceneHandle_t scene, + uint32_t topLevelHandle); + +void APIENTRY glShowTopLevelAccelStructure( + glRaytracingSceneHandle_t scene, + uint32_t topLevelHandle); + +int APIENTRY glIsTopLevelAccelStructureVisible( + glRaytracingSceneHandle_t scene, + uint32_t topLevelHandle); + +void APIENTRY glHideAllTopLevelAccelStructures( + glRaytracingSceneHandle_t scene); + +void APIENTRY glShowAllTopLevelAccelStructures( + glRaytracingSceneHandle_t scene); \ No newline at end of file diff --git a/neo/prey/game/Game_local.h b/neo/prey/game/Game_local.h index 1fdf3449..90387ec3 100644 --- a/neo/prey/game/Game_local.h +++ b/neo/prey/game/Game_local.h @@ -674,6 +674,8 @@ public: protected: idMapFile * additionalMapFile; virtual void SpawnAppendedMapEntities() {} +#else + bool DeathwalkMapLoaded() const { return false; } #endif // HUMANHEAD END diff --git a/neo/renderer/Material.cpp b/neo/renderer/Material.cpp index 5b82e260..f0048050 100644 --- a/neo/renderer/Material.cpp +++ b/neo/renderer/Material.cpp @@ -1871,6 +1871,20 @@ idImage* idMaterial::GetDiffuseImage(void) const { return GetEditorImage(); } +/* +=================== +idMaterial::IsLitMaterial +=================== +*/ +bool idMaterial::IsLitMaterial(void) const { + for (int i = 0; i < numStages; i++) { + if (stages[i].lighting == SL_DIFFUSE && stages[i].texture.image) { + return true; + } + } + return false; +} + /* =================== idMaterial::IsSky diff --git a/neo/renderer/Material.h b/neo/renderer/Material.h index e2638a70..ae2f5837 100644 --- a/neo/renderer/Material.h +++ b/neo/renderer/Material.h @@ -504,13 +504,25 @@ public: // returns true if the material will generate interactions with fog/blend lights // All non-translucent surfaces receive fog unless they are explicitly noFog bool ReceivesFog(void) const { return (IsDrawn() && !noFog && coverage != MC_TRANSLUCENT); } - // jmarshall +// jmarshall + // Returns the diffuse/albedo image used by this material stage. idImage* GetDiffuseImage(void) const; + + // Returns the bump/normal map image used by this material stage. idImage* GetBumpImage(void) const; + + // Returns the specular map image used by this material stage. idImage* GetSpecImage(void) const; + + // Returns the glow/emissive map image used by this material stage. idImage* GetGlowImage(void) const; - bool IsSky(void) const; - // jmarshall end + + // Returns true if this material participates in dynamic lighting. + bool IsLitMaterial(void) const; + + // Returns true if this material is rendered as a sky surface. + bool IsSky(void) const; +// jmarshall end // returns true if the material will generate interactions with normal lights // Many special effect surfaces don't have any bump/diffuse/specular // stages, and don't interact with lights at all diff --git a/neo/renderer/Model.cpp b/neo/renderer/Model.cpp index a6cf3a00..0eb4d756 100644 --- a/neo/renderer/Model.cpp +++ b/neo/renderer/Model.cpp @@ -569,6 +569,13 @@ void idRenderModelStatic::UpdateDXR(uint32_t& dxrBottomAcel, int onlySurface) modelSurface_t* surf = &surfaces[i]; + if (onlySurface == -1) + { + if (!surf->shader->IsLitMaterial()) { + continue; + } + } + numDXRVerts += surf->geometry->numVerts; numDXRIndexes += surf->geometry->numIndexes; } @@ -584,6 +591,14 @@ void idRenderModelStatic::UpdateDXR(uint32_t& dxrBottomAcel, int onlySurface) continue; modelSurface_t* surf = &surfaces[i]; + + if (onlySurface == -1) + { + if (!surf->shader->IsLitMaterial()) { + continue; + } + } + for (int d = 0; d < surf->geometry->numIndexes; ++d) { indices[indexId++] = vertexId + surf->geometry->indexes[d]; diff --git a/neo/renderer/RenderWorld.cpp b/neo/renderer/RenderWorld.cpp index 731bd9ee..7a6482dd 100644 --- a/neo/renderer/RenderWorld.cpp +++ b/neo/renderer/RenderWorld.cpp @@ -1634,6 +1634,20 @@ void idRenderWorldLocal::PushVolumeIntoTree_r( idRenderEntityLocal *def, idRende } } +/* +============== +GetDXRModelForSurf +============== +*/ +dxrWorldModel_t* idRenderWorldLocal::GetDXRModelForSurf(srfTriangles_t* tri) { + for (int i = 0; i < worldDXRmodels.Num(); i++) { + if (worldDXRmodels[i].tri == tri) + return &worldDXRmodels[i]; + } + + return NULL; +} + /* ============== PushVolumeIntoTree diff --git a/neo/renderer/RenderWorld_load.cpp b/neo/renderer/RenderWorld_load.cpp index 0a758681..a6467e1f 100644 --- a/neo/renderer/RenderWorld_load.cpp +++ b/neo/renderer/RenderWorld_load.cpp @@ -182,10 +182,11 @@ idRenderModel *idRenderWorldLocal::ParseModel( idLexer *src ) { // add the completed surface to the model model->AddSurface( surf ); - if (!surf.shader->IsSky()) + if (!surf.shader->IsSky() && surf.shader->IsLitMaterial() && surf.shader->SurfaceCastsShadow()) { idRenderModelStatic* modelStatic = (idRenderModelStatic*)model; dxrWorldModel_t dxrModel; + dxrModel.tri = tri; modelStatic->UpdateDXR(dxrModel.dxrBottomAcel, model->NumSurfaces() - 1); glUpdateTopLevelAceelStructure(dxrWorldId, dxrModel.dxrBottomAcel, NULL, dxrModel.topAccelStruct); worldDXRmodels.Append(dxrModel); diff --git a/neo/renderer/RenderWorld_local.h b/neo/renderer/RenderWorld_local.h index d046f9fc..28d9585e 100644 --- a/neo/renderer/RenderWorld_local.h +++ b/neo/renderer/RenderWorld_local.h @@ -75,6 +75,7 @@ typedef struct { } areaNode_t; struct dxrWorldModel_t { + srfTriangles_t* tri; uint32_t dxrBottomAcel = 0; uint32_t topAccelStruct = 0; }; @@ -250,6 +251,8 @@ public: void PushVolumeIntoTree( idRenderEntityLocal *def, idRenderLightLocal *light, int numPoints, const idVec3 (*points) ); + dxrWorldModel_t* GetDXRModelForSurf(srfTriangles_t* tri); + //------------------------------- // tr_light.c void CreateLightDefInteractions( idRenderLightLocal *ldef ); diff --git a/neo/renderer/RenderWorld_portals.cpp b/neo/renderer/RenderWorld_portals.cpp index db85354a..e072daa3 100644 --- a/neo/renderer/RenderWorld_portals.cpp +++ b/neo/renderer/RenderWorld_portals.cpp @@ -2,9 +2,9 @@ =========================================================================== IceTech GPL Source Code -Copyright (C) 2026 Justin Marshall +Copyright (C) 2026 Justin Marshall -This file is part of the IceTech GPL Source Code (?IceTech Source Code?). +This file is part of the IceTech GPL Source Code (?IceTech Source Code?). IceTech Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -43,29 +43,549 @@ in the portal areas that can be seen from the current viewpoint. // if we hit this many planes, we will just stop cropping the // view down, which is still correct, just conservative -const int MAX_PORTAL_PLANES = 20; +const int MAX_PORTAL_PLANES = 20; typedef struct portalStack_s { - portal_t *p; - const struct portalStack_s *next; + portal_t* p; + const struct portalStack_s* next; idScreenRect rect; int numPortalPlanes; - idPlane portalPlanes[MAX_PORTAL_PLANES+1]; + idPlane portalPlanes[MAX_PORTAL_PLANES + 1]; // positive side is outside the visible frustum } portalStack_t; //==================================================================== +// DXR world visibility is deliberately driven by its own portal-shape flood. +// +// Raster visibility uses FlowViewThroughPortals() with the camera frustum as the +// initial portal stack, which is correct for viewEntitys/viewLights but too +// aggressive for ray tracing: reflections, GI, glossy rays, and shadow rays can +// need static world geometry that is outside the camera FOV. +// +// A plain connectedAreas flood is too broad because it ignores portal shape and +// line-of-sight through portals, often making the whole connected map visible. +// +// The DXR path below uses the middle ground: +// * hide all static world TLAS instances at the start of the view +// * flood through portals from the view origin with ZERO initial FOV planes +// * still clip recursively by the actual portal windings / portal planes +// * show the static world TLAS ranges for areas reached by that flood + +static idRenderWorldLocal* s_dxrVisibilityWorld = NULL; +static int* s_dxrWorldModelFirstForArea = NULL; +static int* s_dxrWorldModelCountForArea = NULL; +static bool* s_dxrWorldModelShownForArea = NULL; +static bool s_dxrUseAllLocalModelDXRIndexing = false; + +static int DXR_AreaNumForWorldModelName(const char* name) { + if (!name) { + return -1; + } + + // Static proc world models are normally named _area0, _area1, ... by AddWorldModelEntities(). + if (name[0] != '_' || name[1] != 'a' || name[2] != 'r' || name[3] != 'e' || name[4] != 'a') { + return -1; + } + + const char* p = name + 5; + if (*p < '0' || *p > '9') { + return -1; + } + + int areaNum = 0; + for (; *p; p++) { + if (*p < '0' || *p > '9') { + return -1; + } + areaNum = areaNum * 10 + (*p - '0'); + } + + return areaNum; +} + +static int DXR_AreaNumForWorldModel(const idRenderModel* model) { + if (!model) { + return -1; + } + return DXR_AreaNumForWorldModelName(model->Name()); +} + +static bool DXR_SurfaceCreatesWorldTopAccelStruct(const modelSurface_t* surf) { + if (!surf || !surf->shader) { + return false; + } + + // Must match the ParseModel() creation filter exactly: + // if ( !surf.shader->IsSky() && surf.shader->IsLitMaterial() ) + return !surf->shader->IsSky() && surf->shader->IsLitMaterial(); +} + +static bool DXR_ModelContributesToWorldDXRList(idRenderWorldLocal* world, const idRenderModel* model) { + if (!world || !model) { + return false; + } + + if (s_dxrUseAllLocalModelDXRIndexing) { + return true; + } + + const int areaNum = DXR_AreaNumForWorldModel(model); + return areaNum >= 0 && areaNum < world->numPortalAreas; +} + +static void DXR_SetWorldTopAccelStructVisible(idRenderWorldLocal* world, uint32_t topAccelStruct, bool visible) { + if (!world || !topAccelStruct) { + return; + } + + glSetTopLevelAccelStructureVisible(world->dxrWorldId, topAccelStruct, visible ? 1 : 0); +} + +static void DXR_HideAllWorldTopAccelStructs(idRenderWorldLocal* world) { + if (!world) { + return; + } + + for (int i = 0; i < world->worldDXRmodels.Num(); i++) { + DXR_SetWorldTopAccelStructVisible(world, world->worldDXRmodels[i].topAccelStruct, false); + } +} + +static void DXR_ShowAllWorldTopAccelStructs(idRenderWorldLocal* world) { + if (!world) { + return; + } + + for (int i = 0; i < world->worldDXRmodels.Num(); i++) { + DXR_SetWorldTopAccelStructVisible(world, world->worldDXRmodels[i].topAccelStruct, true); + } +} + +static int DXR_CountFilteredSurfacesForModel(const idRenderModel* model) { + if (!model) { + return 0; + } + + int count = 0; + for (int surfaceNum = 0; surfaceNum < model->NumSurfaces(); surfaceNum++) { + const modelSurface_t* surf = model->Surface(surfaceNum); + if (DXR_SurfaceCreatesWorldTopAccelStruct(surf)) { + count++; + } + } + + return count; +} + +static void DXR_SelectWorldDXRIndexingMode(idRenderWorldLocal* world) { + s_dxrUseAllLocalModelDXRIndexing = false; + + if (!world) { + return; + } + + int areaModelSurfaceCount = 0; + int allLocalModelSurfaceCount = 0; + + for (int modelIndex = 0; modelIndex < world->localModels.Num(); modelIndex++) { + idRenderModel* model = world->localModels[modelIndex]; + if (!model) { + continue; + } + + const int modelSurfaceCount = DXR_CountFilteredSurfacesForModel(model); + allLocalModelSurfaceCount += modelSurfaceCount; + + const int areaNum = DXR_AreaNumForWorldModel(model); + if (areaNum >= 0 && areaNum < world->numPortalAreas) { + areaModelSurfaceCount += modelSurfaceCount; + } + } + + // The original Doom 3 proc-world path normally creates worldDXRmodels only + // from _areaN models. If the count does not match but all local lit surfaces + // do match, use all-local indexing as a compatibility fallback for builds + // where the DXR parser added more model types to worldDXRmodels. + if (world->worldDXRmodels.Num() != areaModelSurfaceCount && + world->worldDXRmodels.Num() == allLocalModelSurfaceCount) { + s_dxrUseAllLocalModelDXRIndexing = true; + } +} + +static void DXR_BuildWorldTopAccelStructAreaMap(idRenderWorldLocal* world) { + s_dxrVisibilityWorld = world; + s_dxrWorldModelFirstForArea = NULL; + s_dxrWorldModelCountForArea = NULL; + s_dxrWorldModelShownForArea = NULL; + s_dxrUseAllLocalModelDXRIndexing = false; + + if (!world || world->numPortalAreas <= 0) { + return; + } + + DXR_SelectWorldDXRIndexingMode(world); + + s_dxrWorldModelFirstForArea = (int*)R_FrameAlloc(world->numPortalAreas * sizeof(s_dxrWorldModelFirstForArea[0])); + s_dxrWorldModelCountForArea = (int*)R_FrameAlloc(world->numPortalAreas * sizeof(s_dxrWorldModelCountForArea[0])); + s_dxrWorldModelShownForArea = (bool*)R_FrameAlloc(world->numPortalAreas * sizeof(s_dxrWorldModelShownForArea[0])); + + for (int i = 0; i < world->numPortalAreas; i++) { + s_dxrWorldModelFirstForArea[i] = -1; + s_dxrWorldModelCountForArea[i] = 0; + s_dxrWorldModelShownForArea[i] = false; + } + + int dxrIndex = 0; + for (int modelIndex = 0; modelIndex < world->localModels.Num(); modelIndex++) { + idRenderModel* model = world->localModels[modelIndex]; + if (!model) { + continue; + } + + const int areaNum = DXR_AreaNumForWorldModel(model); + const bool validAreaModel = areaNum >= 0 && areaNum < world->numPortalAreas; + const bool contributesToDXRList = DXR_ModelContributesToWorldDXRList(world, model); + const int firstForThisModel = dxrIndex; + int countForThisArea = 0; + + for (int surfaceNum = 0; surfaceNum < model->NumSurfaces(); surfaceNum++) { + const modelSurface_t* surf = model->Surface(surfaceNum); + if (!DXR_SurfaceCreatesWorldTopAccelStruct(surf)) { + continue; + } + + if (contributesToDXRList) { + if (validAreaModel && dxrIndex < world->worldDXRmodels.Num()) { + countForThisArea++; + } + dxrIndex++; + } + } + + if (validAreaModel && countForThisArea > 0) { + s_dxrWorldModelFirstForArea[areaNum] = firstForThisModel; + s_dxrWorldModelCountForArea[areaNum] = countForThisArea; + } + } + + // Conservative fallback for maps/builds where the DXR list is one entry per + // portal area but the _areaN surface mapping above could not identify a range. + if (world->worldDXRmodels.Num() == world->numPortalAreas) { + for (int areaNum = 0; areaNum < world->numPortalAreas; areaNum++) { + if (s_dxrWorldModelFirstForArea[areaNum] < 0) { + s_dxrWorldModelFirstForArea[areaNum] = areaNum; + s_dxrWorldModelCountForArea[areaNum] = 1; + } + } + } +} + +static void DXR_BeginWorldTopAccelStructVisibilityPass(idRenderWorldLocal* world) { + DXR_BuildWorldTopAccelStructAreaMap(world); + DXR_HideAllWorldTopAccelStructs(world); +} + +static void DXR_ShowWorldTopAccelStructsForArea(idRenderWorldLocal* world, int areaNum) { + if (!world || world != s_dxrVisibilityWorld || + !s_dxrWorldModelFirstForArea || !s_dxrWorldModelCountForArea || !s_dxrWorldModelShownForArea) { + return; + } + if (areaNum < 0 || areaNum >= world->numPortalAreas) { + return; + } + if (s_dxrWorldModelShownForArea[areaNum]) { + return; + } + + const int first = s_dxrWorldModelFirstForArea[areaNum]; + const int count = s_dxrWorldModelCountForArea[areaNum]; + if (first < 0 || count <= 0) { + return; + } + + s_dxrWorldModelShownForArea[areaNum] = true; + + const int end = first + count; + for (int i = first; i < end && i < world->worldDXRmodels.Num(); i++) { + DXR_SetWorldTopAccelStructVisible(world, world->worldDXRmodels[i].topAccelStruct, true); + } +} + +static int DXR_FirstWorldDXRIndexForModel(idRenderWorldLocal* world, const idRenderModel* wantedModel) { + if (!world || !wantedModel) { + return -1; + } + + int dxrIndex = 0; + + for (int modelIndex = 0; modelIndex < world->localModels.Num(); modelIndex++) { + const idRenderModel* model = world->localModels[modelIndex]; + if (!model) { + continue; + } + + const bool contributesToDXRList = DXR_ModelContributesToWorldDXRList(world, model); + const bool isWantedModel = (model == wantedModel); + int firstForModel = -1; + + for (int surfaceNum = 0; surfaceNum < model->NumSurfaces(); surfaceNum++) { + const modelSurface_t* surf = model->Surface(surfaceNum); + if (!DXR_SurfaceCreatesWorldTopAccelStruct(surf)) { + continue; + } + + if (!contributesToDXRList) { + continue; + } + + if (isWantedModel && firstForModel < 0) { + firstForModel = dxrIndex; + } + + dxrIndex++; + } + + if (isWantedModel) { + return firstForModel; + } + } + + return -1; +} + +static int DXR_WorldDXRCountForModel(idRenderWorldLocal* world, const idRenderModel* model) { + if (!world || !model || !DXR_ModelContributesToWorldDXRList(world, model)) { + return 0; + } + + return DXR_CountFilteredSurfacesForModel(model); +} + +static void DXR_ShowWorldTopAccelStructsForModel(idRenderWorldLocal* world, const idRenderModel* model) { + if (!world || !model) { + return; + } + + const int first = DXR_FirstWorldDXRIndexForModel(world, model); + const int count = DXR_WorldDXRCountForModel(world, model); + + if (first < 0 || count <= 0) { + return; + } + + const int end = first + count; + for (int i = first; i < end && i < world->worldDXRmodels.Num(); i++) { + DXR_SetWorldTopAccelStructVisible(world, world->worldDXRmodels[i].topAccelStruct, true); + } +} + +static void DXR_ShowWorldTopAccelStructsReferencedByArea(idRenderWorldLocal* world, int areaNum) { + if (!world) { + return; + } + + if (areaNum < 0 || areaNum >= world->numPortalAreas) { + return; + } + + portalArea_t* area = &world->portalAreas[areaNum]; + + for (areaReference_t* ref = area->entityRefs.areaNext; ref != &area->entityRefs; ref = ref->areaNext) { + idRenderEntityLocal* entity = ref->entity; + if (!entity) { + continue; + } + + idRenderModel* model = entity->parms.hModel; + if (!model) { + continue; + } + + DXR_ShowWorldTopAccelStructsForModel(world, model); + } +} + +static void DXR_ShowWorldTopAccelStructsForAreaRobust(idRenderWorldLocal* world, int areaNum) { + if (!world) { + return; + } + + if (areaNum < 0 || areaNum >= world->numPortalAreas) { + return; + } + + // For DXR this means "area already processed this view". The fast area + // range path and entity-ref fallback are both attempted before the flag is set. + if (s_dxrWorldModelShownForArea && s_dxrWorldModelShownForArea[areaNum]) { + return; + } + + // Fast _areaN range path. + DXR_ShowWorldTopAccelStructsForArea(world, areaNum); + + // Correctness fallback path using the actual render models referenced by this area. + DXR_ShowWorldTopAccelStructsReferencedByArea(world, areaNum); + + if (s_dxrWorldModelShownForArea) { + s_dxrWorldModelShownForArea[areaNum] = true; + } +} + +static void DXR_FloodWorldTopAccelStructVisibilityThroughArea_r(idRenderWorldLocal* world, + const idVec3 origin, int areaNum, const portalStack_t* ps) { + portal_t* p; + float d; + portalArea_t* area; + const portalStack_t* check; + portalStack_t newStack; + int i, j; + idVec3 v1, v2; + int addPlanes; + idFixedWinding w; + + if (!world) { + return; + } + if (areaNum < 0 || areaNum >= world->numPortalAreas) { + return; + } + + area = &world->portalAreas[areaNum]; + + // This area is visible to DXR from the view origin through the current portal chain. + DXR_ShowWorldTopAccelStructsForAreaRobust(world, areaNum); + + for (p = area->portals; p; p = p->next) { + // Closed doors / blocked portals should still stop DXR visibility. + if (p->doublePortal->blockingBits & PS_BLOCK_VIEW) { + continue; + } + + // Geometric portal facing test, not camera-FOV culling. + d = p->plane.Distance(origin); + if (d < -0.1f) { + continue; + } + + // Avoid infinite portal recursion. + for (check = ps; check; check = check->next) { + if (check->p == p) { + break; + } + } + if (check) { + continue; + } + + // If very close to the portal plane, avoid numerical clipping issues. + if (d < 1.0f) { + newStack = *ps; + newStack.p = p; + newStack.next = ps; + DXR_FloodWorldTopAccelStructVisibilityThroughArea_r(world, origin, p->intoArea, &newStack); + continue; + } + + // Clip this portal against the current portal visibility cone. + w = *p->w; + for (j = 0; j < ps->numPortalPlanes; j++) { + if (!w.ClipInPlace(-ps->portalPlanes[j], 0)) { + break; + } + } + if (!w.GetNumPoints()) { + continue; + } + + // Do NOT call PortalIsFoggedOut() here. Fog is a raster visibility + // optimization; it should not hide TLAS geometry needed by rays. + + newStack.p = p; + newStack.next = ps; + + addPlanes = w.GetNumPoints(); + if (addPlanes > MAX_PORTAL_PLANES) { + addPlanes = MAX_PORTAL_PLANES; + } + + newStack.numPortalPlanes = 0; + for (i = 0; i < addPlanes; i++) { + j = i + 1; + if (j == w.GetNumPoints()) { + j = 0; + } + + v1 = origin - w[i].ToVec3(); + v2 = origin - w[j].ToVec3(); + + newStack.portalPlanes[newStack.numPortalPlanes].Normal().Cross(v2, v1); + + if (newStack.portalPlanes[newStack.numPortalPlanes].Normalize() < 0.01f) { + continue; + } + + newStack.portalPlanes[newStack.numPortalPlanes].FitThroughPoint(origin); + newStack.numPortalPlanes++; + } + + // The last stack plane is the portal plane, matching the raster flood. + newStack.portalPlanes[newStack.numPortalPlanes] = p->plane; + newStack.numPortalPlanes++; + + DXR_FloodWorldTopAccelStructVisibilityThroughArea_r(world, origin, p->intoArea, &newStack); + } +} + +static void DXR_FlowWorldTopAccelStructVisibilityThroughPortals(idRenderWorldLocal* world) { + portalStack_t ps; + + if (!world || !tr.viewDef) { + return; + } + + // If portal culling is disabled, match raster debug behavior and show all. + if (!r_usePortals.GetBool()) { + DXR_ShowAllWorldTopAccelStructs(world); + return; + } + + // If outside the world, match original FlowViewThroughPortals behavior and show all. + if (tr.viewDef->areaNum < 0) { + DXR_ShowAllWorldTopAccelStructs(world); + return; + } + + // Honor r_singleArea debug mode. + if (r_singleArea.GetBool()) { + DXR_ShowWorldTopAccelStructsForAreaRobust(world, tr.viewDef->areaNum); + return; + } + + memset(&ps, 0, sizeof(ps)); + ps.next = NULL; + ps.p = NULL; + + // Critical difference from raster: + // zero initial planes means "do not camera-FOV cull DXR". + // The recursive portal cone still prevents showing the whole connected map. + ps.numPortalPlanes = 0; + ps.rect = tr.viewDef->scissor; + + DXR_FloodWorldTopAccelStructVisibilityThroughArea_r(world, + tr.viewDef->renderView.vieworg, tr.viewDef->areaNum, &ps); +} + /* =================== idRenderWorldLocal::ScreenRectForWinding =================== */ -idScreenRect idRenderWorldLocal::ScreenRectFromWinding( const idWinding *w, viewEntity_t *space ) { +idScreenRect idRenderWorldLocal::ScreenRectFromWinding(const idWinding* w, viewEntity_t* space) { idScreenRect r; int i; idVec3 v; @@ -73,14 +593,14 @@ idScreenRect idRenderWorldLocal::ScreenRectFromWinding( const idWinding *w, view float windowX, windowY; r.Clear(); - for ( i = 0 ; i < w->GetNumPoints() ; i++ ) { - R_LocalPointToGlobal( space->modelMatrix, (*w)[i].ToVec3(), v ); - R_GlobalToNormalizedDeviceCoordinates( v, ndc ); + for (i = 0; i < w->GetNumPoints(); i++) { + R_LocalPointToGlobal(space->modelMatrix, (*w)[i].ToVec3(), v); + R_GlobalToNormalizedDeviceCoordinates(v, ndc); - windowX = 0.5f * ( 1.0f + ndc[0] ) * ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 ); - windowY = 0.5f * ( 1.0f + ndc[1] ) * ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 ); + windowX = 0.5f * (1.0f + ndc[0]) * (tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1); + windowY = 0.5f * (1.0f + ndc[1]) * (tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1); - r.AddPoint( windowX, windowY ); + r.AddPoint(windowX, windowY); } r.Expand(); @@ -93,35 +613,36 @@ idScreenRect idRenderWorldLocal::ScreenRectFromWinding( const idWinding *w, view PortalIsFoggedOut =================== */ -bool idRenderWorldLocal::PortalIsFoggedOut( const portal_t *p ) { - idRenderLightLocal *ldef; - const idWinding *w; +bool idRenderWorldLocal::PortalIsFoggedOut(const portal_t* p) { + idRenderLightLocal* ldef; + const idWinding* w; int i; idPlane forward; ldef = p->doublePortal->fogLight; - if ( !ldef ) { + if (!ldef) { return false; } // find the current density of the fog - const idMaterial *lightShader = ldef->lightShader; - int size = sizeof( float ) *lightShader->GetNumRegisters(); - float *regs =(float *)_alloca( size ); + const idMaterial* lightShader = ldef->lightShader; + int size = sizeof(float) * lightShader->GetNumRegisters(); + float* regs = (float*)_alloca(size); - lightShader->EvaluateRegisters( regs, ldef->parms.shaderParms, tr.viewDef, ldef->parms.referenceSound ); + lightShader->EvaluateRegisters(regs, ldef->parms.shaderParms, tr.viewDef, ldef->parms.referenceSound); - const shaderStage_t *stage = lightShader->GetStage(0); + const shaderStage_t* stage = lightShader->GetStage(0); - float alpha = regs[ stage->color.registers[3] ]; + float alpha = regs[stage->color.registers[3]]; // if they left the default value on, set a fog distance of 500 float a; - if ( alpha <= 1.0f ) { + if (alpha <= 1.0f) { a = -0.5f / DEFAULT_FOG_DISTANCE; - } else { + } + else { // otherwise, distance = alpha color a = -0.5f / alpha; } @@ -132,11 +653,11 @@ bool idRenderWorldLocal::PortalIsFoggedOut( const portal_t *p ) { forward[3] = a * tr.viewDef->worldSpace.modelViewMatrix[14]; w = p->w; - for ( i = 0 ; i < w->GetNumPoints() ; i++ ) { + for (i = 0; i < w->GetNumPoints(); i++) { float d; - d = forward.Distance( (*w)[i].ToVec3() ); - if ( d < 0.5f ) { + d = forward.Distance((*w)[i].ToVec3()); + if (d < 0.5f) { return false; // a point not clipped off } } @@ -149,78 +670,79 @@ bool idRenderWorldLocal::PortalIsFoggedOut( const portal_t *p ) { FloodViewThroughArea_r =================== */ -void idRenderWorldLocal::FloodViewThroughArea_r( const idVec3 origin, int areaNum, - const struct portalStack_s *ps ) { - portal_t* p; +void idRenderWorldLocal::FloodViewThroughArea_r(const idVec3 origin, int areaNum, + const struct portalStack_s* ps) { + portal_t* p; float d; - portalArea_t * area; - const portalStack_t *check; + portalArea_t* area; + const portalStack_t* check; portalStack_t newStack; int i, j; idVec3 v1, v2; int addPlanes; idFixedWinding w; // we won't overflow because MAX_PORTAL_PLANES = 20 - area = &portalAreas[ areaNum ]; + area = &portalAreas[areaNum]; // cull models and lights to the current collection of planes - AddAreaRefs( areaNum, ps ); + AddAreaRefs(areaNum, ps); - if ( areaScreenRect[areaNum].IsEmpty() ) { + if (areaScreenRect[areaNum].IsEmpty()) { areaScreenRect[areaNum] = ps->rect; - } else { - areaScreenRect[areaNum].Union( ps->rect ); + } + else { + areaScreenRect[areaNum].Union(ps->rect); } // go through all the portals - for ( p = area->portals; p; p = p->next ) { + for (p = area->portals; p; p = p->next) { // an enclosing door may have sealed the portal off - if ( p->doublePortal->blockingBits & PS_BLOCK_VIEW ) { + if (p->doublePortal->blockingBits & PS_BLOCK_VIEW) { continue; } // make sure this portal is facing away from the view - d = p->plane.Distance( origin ); - if ( d < -0.1f ) { + d = p->plane.Distance(origin); + if (d < -0.1f) { continue; } // make sure the portal isn't in our stack trace, // which would cause an infinite loop - for ( check = ps; check; check = check->next ) { - if ( check->p == p ) { + for (check = ps; check; check = check->next) { + if (check->p == p) { break; // don't recursively enter a stack } } - if ( check ) { + if (check) { continue; // already in stack } // if we are very close to the portal surface, don't bother clipping // it, which tends to give epsilon problems that make the area vanish - if ( d < 1.0f ) { + if (d < 1.0f) { // go through this portal newStack = *ps; newStack.p = p; newStack.next = ps; - FloodViewThroughArea_r( origin, p->intoArea, &newStack ); + FloodViewThroughArea_r(origin, p->intoArea, &newStack); continue; } // clip the portal winding to all of the planes w = *p->w; - for ( j = 0; j < ps->numPortalPlanes; j++ ) { - if ( !w.ClipInPlace( -ps->portalPlanes[j], 0 ) ) { + for (j = 0; j < ps->numPortalPlanes; j++) { + if (!w.ClipInPlace(-ps->portalPlanes[j], 0)) { break; } } - if ( !w.GetNumPoints() ) { + if (!w.GetNumPoints()) { continue; // portal not visible } // see if it is fogged out - if ( PortalIsFoggedOut( p ) ) { + if (PortalIsFoggedOut(p)) { continue; } @@ -230,36 +752,36 @@ void idRenderWorldLocal::FloodViewThroughArea_r( const idVec3 origin, int areaNu // find the screen pixel bounding box of the remaining portal // so we can scissor things outside it - newStack.rect = ScreenRectFromWinding( &w, &tr.identitySpace ); - + newStack.rect = ScreenRectFromWinding(&w, &tr.identitySpace); + // slop might have spread it a pixel outside, so trim it back - newStack.rect.Intersect( ps->rect ); + newStack.rect.Intersect(ps->rect); // generate a set of clipping planes that will further restrict // the visible view beyond just the scissor rect addPlanes = w.GetNumPoints(); - if ( addPlanes > MAX_PORTAL_PLANES ) { + if (addPlanes > MAX_PORTAL_PLANES) { addPlanes = MAX_PORTAL_PLANES; } newStack.numPortalPlanes = 0; - for ( i = 0; i < addPlanes; i++ ) { - j = i+1; - if ( j == w.GetNumPoints() ) { + for (i = 0; i < addPlanes; i++) { + j = i + 1; + if (j == w.GetNumPoints()) { j = 0; } v1 = origin - w[i].ToVec3(); v2 = origin - w[j].ToVec3(); - newStack.portalPlanes[newStack.numPortalPlanes].Normal().Cross( v2, v1 ); + newStack.portalPlanes[newStack.numPortalPlanes].Normal().Cross(v2, v1); // if it is degenerate, skip the plane - if ( newStack.portalPlanes[newStack.numPortalPlanes].Normalize() < 0.01f ) { + if (newStack.portalPlanes[newStack.numPortalPlanes].Normalize() < 0.01f) { continue; } - newStack.portalPlanes[newStack.numPortalPlanes].FitThroughPoint( origin ); + newStack.portalPlanes[newStack.numPortalPlanes].FitThroughPoint(origin); newStack.numPortalPlanes++; } @@ -268,7 +790,7 @@ void idRenderWorldLocal::FloodViewThroughArea_r( const idVec3 origin, int areaNu newStack.portalPlanes[newStack.numPortalPlanes] = p->plane; newStack.numPortalPlanes++; - FloodViewThroughArea_r( origin, p->intoArea, &newStack ); + FloodViewThroughArea_r(origin, p->intoArea, &newStack); } } @@ -282,38 +804,39 @@ sides facing in) that should contain the origin, such as a view frustum or a poi Zero planes assumes an unbounded volume. ======================= */ -void idRenderWorldLocal::FlowViewThroughPortals( const idVec3 origin, int numPlanes, const idPlane *planes ) { +void idRenderWorldLocal::FlowViewThroughPortals(const idVec3 origin, int numPlanes, const idPlane* planes) { portalStack_t ps; int i; ps.next = NULL; ps.p = NULL; - for ( i = 0 ; i < numPlanes ; i++ ) { + for (i = 0; i < numPlanes; i++) { ps.portalPlanes[i] = planes[i]; } ps.numPortalPlanes = numPlanes; ps.rect = tr.viewDef->scissor; - if ( tr.viewDef->areaNum < 0 ){ + if (tr.viewDef->areaNum < 0) { - for ( i = 0; i < numPortalAreas; i++ ) { + for (i = 0; i < numPortalAreas; i++) { areaScreenRect[i] = tr.viewDef->scissor; } // if outside the world, mark everything - for ( i = 0 ; i < numPortalAreas ; i++ ) { - AddAreaRefs( i, &ps ); + for (i = 0; i < numPortalAreas; i++) { + AddAreaRefs(i, &ps); } - } else { + } + else { - for ( i = 0; i < numPortalAreas; i++ ) { + for (i = 0; i < numPortalAreas; i++) { areaScreenRect[i].Clear(); } // flood out through portals, setting area viewCount - FloodViewThroughArea_r( origin, tr.viewDef->areaNum, &ps ); + FloodViewThroughArea_r(origin, tr.viewDef->areaNum, &ps); } } @@ -325,72 +848,72 @@ void idRenderWorldLocal::FlowViewThroughPortals( const idVec3 origin, int numPla FloodLightThroughArea_r =================== */ -void idRenderWorldLocal::FloodLightThroughArea_r( idRenderLightLocal *light, int areaNum, - const struct portalStack_s *ps ) { - portal_t* p; +void idRenderWorldLocal::FloodLightThroughArea_r(idRenderLightLocal* light, int areaNum, + const struct portalStack_s* ps) { + portal_t* p; float d; - portalArea_t * area; - const portalStack_t *check, *firstPortalStack; + portalArea_t* area; + const portalStack_t* check, * firstPortalStack; portalStack_t newStack; int i, j; idVec3 v1, v2; int addPlanes; idFixedWinding w; // we won't overflow because MAX_PORTAL_PLANES = 20 - area = &portalAreas[ areaNum ]; + area = &portalAreas[areaNum]; // add an areaRef - AddLightRefToArea( light, area ); + AddLightRefToArea(light, area); // go through all the portals - for ( p = area->portals; p; p = p->next ) { + for (p = area->portals; p; p = p->next) { // make sure this portal is facing away from the view - d = p->plane.Distance( light->globalLightOrigin ); - if ( d < -0.1f ) { + d = p->plane.Distance(light->globalLightOrigin); + if (d < -0.1f) { continue; } // make sure the portal isn't in our stack trace, // which would cause an infinite loop - for ( check = ps; check; check = check->next ) { + for (check = ps; check; check = check->next) { firstPortalStack = check; - if ( check->p == p ) { + if (check->p == p) { break; // don't recursively enter a stack } } - if ( check ) { + if (check) { continue; // already in stack } // if we are very close to the portal surface, don't bother clipping // it, which tends to give epsilon problems that make the area vanish - if ( d < 1.0f ) { + if (d < 1.0f) { // go through this portal newStack = *ps; newStack.p = p; newStack.next = ps; - FloodLightThroughArea_r( light, p->intoArea, &newStack ); + FloodLightThroughArea_r(light, p->intoArea, &newStack); continue; } // clip the portal winding to all of the planes w = *p->w; - for ( j = 0; j < ps->numPortalPlanes; j++ ) { - if ( !w.ClipInPlace( -ps->portalPlanes[j], 0 ) ) { + for (j = 0; j < ps->numPortalPlanes; j++) { + if (!w.ClipInPlace(-ps->portalPlanes[j], 0)) { break; } } - if ( !w.GetNumPoints() ) { + if (!w.GetNumPoints()) { continue; // portal not visible } // also always clip to the original light planes, because they aren't // necessarily extending to infinitiy like a view frustum - for ( j = 0; j < firstPortalStack->numPortalPlanes; j++ ) { - if ( !w.ClipInPlace( -firstPortalStack->portalPlanes[j], 0 ) ) { + for (j = 0; j < firstPortalStack->numPortalPlanes; j++) { + if (!w.ClipInPlace(-firstPortalStack->portalPlanes[j], 0)) { break; } } - if ( !w.GetNumPoints() ) { + if (!w.GetNumPoints()) { continue; // portal not visible } @@ -402,32 +925,32 @@ void idRenderWorldLocal::FloodLightThroughArea_r( idRenderLightLocal *light, int // the visible view beyond just the scissor rect addPlanes = w.GetNumPoints(); - if ( addPlanes > MAX_PORTAL_PLANES ) { + if (addPlanes > MAX_PORTAL_PLANES) { addPlanes = MAX_PORTAL_PLANES; } newStack.numPortalPlanes = 0; - for ( i = 0; i < addPlanes; i++ ) { - j = i+1; - if ( j == w.GetNumPoints() ) { + for (i = 0; i < addPlanes; i++) { + j = i + 1; + if (j == w.GetNumPoints()) { j = 0; } v1 = light->globalLightOrigin - w[i].ToVec3(); v2 = light->globalLightOrigin - w[j].ToVec3(); - newStack.portalPlanes[newStack.numPortalPlanes].Normal().Cross( v2, v1 ); + newStack.portalPlanes[newStack.numPortalPlanes].Normal().Cross(v2, v1); // if it is degenerate, skip the plane - if ( newStack.portalPlanes[newStack.numPortalPlanes].Normalize() < 0.01f ) { + if (newStack.portalPlanes[newStack.numPortalPlanes].Normalize() < 0.01f) { continue; } - newStack.portalPlanes[newStack.numPortalPlanes].FitThroughPoint( light->globalLightOrigin ); + newStack.portalPlanes[newStack.numPortalPlanes].FitThroughPoint(light->globalLightOrigin); newStack.numPortalPlanes++; } - FloodLightThroughArea_r( light, p->intoArea, &newStack ); + FloodLightThroughArea_r(light, p->intoArea, &newStack); } } @@ -441,25 +964,25 @@ This can only be used for shadow casting lights that have a generated prelight, because shadows are cast from back side which may not be in visible areas. ======================= */ -void idRenderWorldLocal::FlowLightThroughPortals( idRenderLightLocal *light ) { +void idRenderWorldLocal::FlowLightThroughPortals(idRenderLightLocal* light) { portalStack_t ps; int i; const idVec3 origin = light->globalLightOrigin; // if the light origin areaNum is not in a valid area, // the light won't have any area refs - if ( light->areaNum == -1 ) { + if (light->areaNum == -1) { return; } - memset( &ps, 0, sizeof( ps ) ); + memset(&ps, 0, sizeof(ps)); ps.numPortalPlanes = 6; - for ( i = 0 ; i < 6 ; i++ ) { + for (i = 0; i < 6; i++) { ps.portalPlanes[i] = light->frustum[i]; } - FloodLightThroughArea_r( light, light->areaNum, &ps ); + FloodLightThroughArea_r(light, light->areaNum, &ps); } //====================================================================================================== @@ -469,43 +992,43 @@ void idRenderWorldLocal::FlowLightThroughPortals( idRenderLightLocal *light ) { idRenderWorldLocal::FloodFrustumAreas_r =================== */ -areaNumRef_t *idRenderWorldLocal::FloodFrustumAreas_r( const idFrustum &frustum, const int areaNum, const idBounds &bounds, areaNumRef_t *areas ) { - portal_t *p; - portalArea_t *portalArea; +areaNumRef_t* idRenderWorldLocal::FloodFrustumAreas_r(const idFrustum& frustum, const int areaNum, const idBounds& bounds, areaNumRef_t* areas) { + portal_t* p; + portalArea_t* portalArea; idBounds newBounds; - areaNumRef_t *a; + areaNumRef_t* a; - portalArea = &portalAreas[ areaNum ]; + portalArea = &portalAreas[areaNum]; // go through all the portals - for ( p = portalArea->portals; p; p = p->next ) { + for (p = portalArea->portals; p; p = p->next) { // check if we already visited the area the portal leads to - for ( a = areas; a; a = a->next ) { - if ( a->areaNum == p->intoArea ) { + for (a = areas; a; a = a->next) { + if (a->areaNum == p->intoArea) { break; } } - if ( a ) { + if (a) { continue; } // the frustum origin must be at the front of the portal plane - if ( p->plane.Side( frustum.GetOrigin(), 0.1f ) == SIDE_BACK ) { + if (p->plane.Side(frustum.GetOrigin(), 0.1f) == SIDE_BACK) { continue; } // the frustum must cross the portal plane - if ( frustum.PlaneSide( p->plane, 0.0f ) != PLANESIDE_CROSS ) { + if (frustum.PlaneSide(p->plane, 0.0f) != PLANESIDE_CROSS) { continue; } // get the bounds for the portal winding projected in the frustum - frustum.ProjectionBounds( *p->w, newBounds ); + frustum.ProjectionBounds(*p->w, newBounds); - newBounds.IntersectSelf( bounds ); + newBounds.IntersectSelf(bounds); - if ( newBounds[0][0] > newBounds[1][0] || newBounds[0][1] > newBounds[1][1] || newBounds[0][2] > newBounds[1][2] ) { + if (newBounds[0][0] > newBounds[1][0] || newBounds[0][1] > newBounds[1][1] || newBounds[0][2] > newBounds[1][2]) { continue; } @@ -516,7 +1039,7 @@ areaNumRef_t *idRenderWorldLocal::FloodFrustumAreas_r( const idFrustum &frustum, a->next = areas; areas = a; - areas = FloodFrustumAreas_r( frustum, p->intoArea, newBounds, areas ); + areas = FloodFrustumAreas_r(frustum, p->intoArea, newBounds, areas); } return areas; @@ -530,16 +1053,16 @@ idRenderWorldLocal::FloodFrustumAreas All portals are assumed to be open. =================== */ -areaNumRef_t *idRenderWorldLocal::FloodFrustumAreas( const idFrustum &frustum, areaNumRef_t *areas ) { +areaNumRef_t* idRenderWorldLocal::FloodFrustumAreas(const idFrustum& frustum, areaNumRef_t* areas) { idBounds bounds; - areaNumRef_t *a; + areaNumRef_t* a; // bounds that cover the whole frustum - bounds[0].Set( frustum.GetNearDistance(), -1.0f, -1.0f ); - bounds[1].Set( frustum.GetFarDistance(), 1.0f, 1.0f ); + bounds[0].Set(frustum.GetNearDistance(), -1.0f, -1.0f); + bounds[1].Set(frustum.GetFarDistance(), 1.0f, 1.0f); - for ( a = areas; a; a = a->next ) { - areas = FloodFrustumAreas_r( frustum, a->areaNum, bounds, areas ); + for (a = areas; a; a = a->next) { + areas = FloodFrustumAreas_r(frustum, a->areaNum, bounds, areas); } return areas; @@ -561,9 +1084,9 @@ CullEntityByPortals Return true if the entity reference bounds do not intersect the current portal chain. ================ */ -bool idRenderWorldLocal::CullEntityByPortals( const idRenderEntityLocal *entity, const portalStack_t *ps ) { +bool idRenderWorldLocal::CullEntityByPortals(const idRenderEntityLocal* entity, const portalStack_t* ps) { - if ( !r_useEntityCulling.GetBool() ) { + if (!r_useEntityCulling.GetBool()) { return false; } @@ -572,8 +1095,8 @@ bool idRenderWorldLocal::CullEntityByPortals( const idRenderEntityLocal *entity, // because we want to do all touching of the model after // we have determined all the lights that may effect it, // which optimizes cache usage - if ( R_CullLocalBox( entity->referenceBounds, entity->modelMatrix, - ps->numPortalPlanes, ps->portalPlanes ) ) { + if (R_CullLocalBox(entity->referenceBounds, entity->modelMatrix, + ps->numPortalPlanes, ps->portalPlanes)) { return true; } @@ -585,52 +1108,52 @@ bool idRenderWorldLocal::CullEntityByPortals( const idRenderEntityLocal *entity, AddAreaEntityRefs Any models that are visible through the current portalStack will -have their scissor +have their scissor =================== */ -void idRenderWorldLocal::AddAreaEntityRefs( int areaNum, const portalStack_t *ps ) { - areaReference_t *ref; - idRenderEntityLocal *entity; - portalArea_t *area; - viewEntity_t *vEnt; +void idRenderWorldLocal::AddAreaEntityRefs(int areaNum, const portalStack_t* ps) { + areaReference_t* ref; + idRenderEntityLocal* entity; + portalArea_t* area; + viewEntity_t* vEnt; idBounds b; - area = &portalAreas[ areaNum ]; + area = &portalAreas[areaNum]; - for ( ref = area->entityRefs.areaNext ; ref != &area->entityRefs ; ref = ref->areaNext ) { + for (ref = area->entityRefs.areaNext; ref != &area->entityRefs; ref = ref->areaNext) { entity = ref->entity; // debug tool to allow viewing of only one entity at a time - if ( r_singleEntity.GetInteger() >= 0 && r_singleEntity.GetInteger() != entity->index ) { + if (r_singleEntity.GetInteger() >= 0 && r_singleEntity.GetInteger() != entity->index) { continue; } // remove decals that are completely faded away - R_FreeEntityDefFadedDecals( entity, tr.viewDef->renderView.time ); + R_FreeEntityDefFadedDecals(entity, tr.viewDef->renderView.time); // check for completely suppressing the model - if ( !r_skipSuppress.GetBool() ) { - if ( entity->parms.suppressSurfaceInViewID - && entity->parms.suppressSurfaceInViewID == tr.viewDef->renderView.viewID ) { + if (!r_skipSuppress.GetBool()) { + if (entity->parms.suppressSurfaceInViewID + && entity->parms.suppressSurfaceInViewID == tr.viewDef->renderView.viewID) { continue; } - if ( entity->parms.allowSurfaceInViewID - && entity->parms.allowSurfaceInViewID != tr.viewDef->renderView.viewID ) { + if (entity->parms.allowSurfaceInViewID + && entity->parms.allowSurfaceInViewID != tr.viewDef->renderView.viewID) { continue; } } // cull reference bounds - if ( CullEntityByPortals( entity, ps ) ) { + if (CullEntityByPortals(entity, ps)) { // we are culled out through this portal chain, but it might // still be visible through others continue; } - vEnt = R_SetEntityDefViewEntity( entity ); + vEnt = R_SetEntityDefViewEntity(entity); // possibly expand the scissor rect - vEnt->scissorRect.Union( ps->rect ); + vEnt->scissorRect.Union(ps->rect); } } @@ -642,45 +1165,45 @@ Return true if the light frustum does not intersect the current portal chain. The last stack plane is not used because lights are not near clipped. ================ */ -bool idRenderWorldLocal::CullLightByPortals( const idRenderLightLocal *light, const portalStack_t *ps ) { +bool idRenderWorldLocal::CullLightByPortals(const idRenderLightLocal* light, const portalStack_t* ps) { int i, j; - const srfTriangles_t *tri; + const srfTriangles_t* tri; float d; idFixedWinding w; // we won't overflow because MAX_PORTAL_PLANES = 20 - if ( r_useLightCulling.GetInteger() == 0 ) { + if (r_useLightCulling.GetInteger() == 0) { return false; } - if ( r_useLightCulling.GetInteger() >= 2 ) { + if (r_useLightCulling.GetInteger() >= 2) { // exact clip of light faces against all planes - for ( i = 0; i < 6; i++ ) { + for (i = 0; i < 6; i++) { // the light frustum planes face out from the light, // so the planes that have the view origin on the negative // side will be the "back" faces of the light, which must have // some fragment inside the portalStack to be visible - if ( light->frustum[i].Distance( tr.viewDef->renderView.vieworg ) >= 0 ) { + if (light->frustum[i].Distance(tr.viewDef->renderView.vieworg) >= 0) { continue; } // get the exact winding for this side - const idWinding *ow = light->frustumWindings[i]; + const idWinding* ow = light->frustumWindings[i]; // projected lights may have one of the frustums degenerated - if ( !ow ) { + if (!ow) { continue; } w = *ow; // now check the winding against each of the portalStack planes - for ( j = 0; j < ps->numPortalPlanes - 1; j++ ) { - if ( !w.ClipInPlace( -ps->portalPlanes[j] ) ) { + for (j = 0; j < ps->numPortalPlanes - 1; j++) { + if (!w.ClipInPlace(-ps->portalPlanes[j])) { break; } } - if ( w.GetNumPoints() ) { + if (w.GetNumPoints()) { // part of the winding is visible through the portalStack, // so the light is not culled return false; @@ -689,20 +1212,21 @@ bool idRenderWorldLocal::CullLightByPortals( const idRenderLightLocal *light, co // none of the light surfaces were visible return true; - } else { + } + else { // simple point check against each plane tri = light->frustumTris; // check against frustum planes - for ( i = 0; i < ps->numPortalPlanes - 1; i++ ) { - for ( j = 0; j < tri->numVerts; j++ ) { - d = ps->portalPlanes[i].Distance( tri->verts[j].xyz ); - if ( d < 0.0f ) { + for (i = 0; i < ps->numPortalPlanes - 1; i++) { + for (j = 0; j < tri->numVerts; j++) { + d = ps->portalPlanes[i].Distance(tri->verts[j].xyz); + if (d < 0.0f) { break; // point is inside this plane } } - if ( j == tri->numVerts ) { + if (j == tri->numVerts) { // all points were outside one of the planes tr.pc.c_box_cull_out++; return true; @@ -720,41 +1244,41 @@ AddAreaLightRefs This is the only point where lights get added to the viewLights list =================== */ -void idRenderWorldLocal::AddAreaLightRefs( int areaNum, const portalStack_t *ps ) { - areaReference_t *lref; - portalArea_t *area; - idRenderLightLocal *light; - viewLight_t *vLight; +void idRenderWorldLocal::AddAreaLightRefs(int areaNum, const portalStack_t* ps) { + areaReference_t* lref; + portalArea_t* area; + idRenderLightLocal* light; + viewLight_t* vLight; - area = &portalAreas[ areaNum ]; + area = &portalAreas[areaNum]; - for ( lref = area->lightRefs.areaNext ; lref != &area->lightRefs ; lref = lref->areaNext ) { + for (lref = area->lightRefs.areaNext; lref != &area->lightRefs; lref = lref->areaNext) { light = lref->light; // debug tool to allow viewing of only one light at a time - if ( r_singleLight.GetInteger() >= 0 && r_singleLight.GetInteger() != light->index ) { + if (r_singleLight.GetInteger() >= 0 && r_singleLight.GetInteger() != light->index) { continue; } // check for being closed off behind a door // a light that doesn't cast shadows will still light even if it is behind a door - if ( r_useLightCulling.GetInteger() >= 3 && - !light->parms.noShadows && light->lightShader->LightCastsShadows() - && light->areaNum != -1 && !tr.viewDef->connectedAreas[ light->areaNum ] ) { + if (r_useLightCulling.GetInteger() >= 3 && + !light->parms.noShadows && light->lightShader->LightCastsShadows() + && light->areaNum != -1 && !tr.viewDef->connectedAreas[light->areaNum]) { continue; } // cull frustum - if ( CullLightByPortals( light, ps ) ) { + if (CullLightByPortals(light, ps)) { // we are culled out through this portal chain, but it might // still be visible through others continue; } - vLight = R_SetLightDefViewLight( light ); + vLight = R_SetLightDefViewLight(light); // expand the scissor rect - vLight->scissorRect.Union( ps->rect ); + vLight->scissorRect.Union(ps->rect); } } @@ -766,14 +1290,19 @@ This may be entered multiple times with different planes if more than one portal sees into the area =================== */ -void idRenderWorldLocal::AddAreaRefs( int areaNum, const portalStack_t *ps ) { +void idRenderWorldLocal::AddAreaRefs(int areaNum, const portalStack_t* ps) { // mark the viewCount, so r_showPortals can display the // considered portals - portalAreas[ areaNum ].viewCount = tr.viewCount; + portalAreas[areaNum].viewCount = tr.viewCount; + + // Do not drive DXR visibility from here. + // This function is camera-FOV portal visibility for raster only. + // DXR static world visibility is handled once in FindViewLightsAndEntities() + // with a separate no-initial-FOV portal-shape flood. // add the models and lights, using more precise culling to the planes - AddAreaEntityRefs( areaNum, ps ); - AddAreaLightRefs( areaNum, ps ); + AddAreaEntityRefs(areaNum, ps); + AddAreaLightRefs(areaNum, ps); } /* @@ -781,21 +1310,21 @@ void idRenderWorldLocal::AddAreaRefs( int areaNum, const portalStack_t *ps ) { BuildConnectedAreas_r =================== */ -void idRenderWorldLocal::BuildConnectedAreas_r( int areaNum ) { - portalArea_t *area; - portal_t *portal; +void idRenderWorldLocal::BuildConnectedAreas_r(int areaNum) { + portalArea_t* area; + portal_t* portal; - if ( tr.viewDef->connectedAreas[areaNum] ) { + if (tr.viewDef->connectedAreas[areaNum]) { return; } tr.viewDef->connectedAreas[areaNum] = true; // flood through all non-blocked portals - area = &portalAreas[ areaNum ]; - for ( portal = area->portals ; portal ; portal = portal->next ) { - if ( !(portal->doublePortal->blockingBits & PS_BLOCK_VIEW) ) { - BuildConnectedAreas_r( portal->intoArea ); + area = &portalAreas[areaNum]; + for (portal = area->portals; portal; portal = portal->next) { + if (!(portal->doublePortal->blockingBits & PS_BLOCK_VIEW)) { + BuildConnectedAreas_r(portal->intoArea); } } } @@ -807,23 +1336,23 @@ BuildConnectedAreas This is only valid for a given view, not all views in a frame =================== */ -void idRenderWorldLocal::BuildConnectedAreas( void ) { +void idRenderWorldLocal::BuildConnectedAreas(void) { int i; - tr.viewDef->connectedAreas = (bool *)R_FrameAlloc( numPortalAreas - * sizeof( tr.viewDef->connectedAreas[0] ) ); + tr.viewDef->connectedAreas = (bool*)R_FrameAlloc(numPortalAreas + * sizeof(tr.viewDef->connectedAreas[0])); // if we are outside the world, we can see all areas - if ( tr.viewDef->areaNum == -1 ) { - for ( i = 0 ; i < numPortalAreas ; i++ ) { + if (tr.viewDef->areaNum == -1) { + for (i = 0; i < numPortalAreas; i++) { tr.viewDef->connectedAreas[i] = true; } return; } // start with none visible, and flood fill from the current area - memset( tr.viewDef->connectedAreas, 0, numPortalAreas * sizeof( tr.viewDef->connectedAreas[0] ) ); - BuildConnectedAreas_r( tr.viewDef->areaNum ); + memset(tr.viewDef->connectedAreas, 0, numPortalAreas * sizeof(tr.viewDef->connectedAreas[0])); + BuildConnectedAreas_r(tr.viewDef->areaNum); } /* @@ -837,54 +1366,64 @@ The scissorRects on the viewEntitys and viewLights may be empty if they were considered, but not actually visible. ============= */ -void idRenderWorldLocal::FindViewLightsAndEntities( void ) { +void idRenderWorldLocal::FindViewLightsAndEntities(void) { // clear the visible lightDef and entityDef lists tr.viewDef->viewLights = NULL; tr.viewDef->viewEntitys = NULL; // find the area to start the portal flooding in - if ( !r_usePortals.GetBool() ) { + if (!r_usePortals.GetBool()) { // debug tool to force no portal culling tr.viewDef->areaNum = -1; - } else { - tr.viewDef->areaNum = PointInArea( tr.viewDef->initialViewAreaOrigin ); + } + else { + tr.viewDef->areaNum = PointInArea(tr.viewDef->initialViewAreaOrigin); } // determine all possible connected areas for // light-behind-door culling BuildConnectedAreas(); + // Reset DXR static world visibility for this view, then re-show static + // world geometry with a DXR-specific portal flood. This flood starts with + // zero camera-FOV planes, so rays can hit geometry outside the camera FOV, + // but it still clips through actual portal windings so it does not make the + // whole connected map visible. + DXR_BeginWorldTopAccelStructVisibilityPass(this); + //DXR_FlowWorldTopAccelStructVisibilityThroughPortals(this); + // bump the view count, invalidating all // visible areas tr.viewCount++; // flow through all the portals and add models / lights - if ( r_singleArea.GetBool() ) { + if (r_singleArea.GetBool()) { // if debugging, only mark this area // if we are outside the world, don't draw anything - if ( tr.viewDef->areaNum >= 0 ) { + if (tr.viewDef->areaNum >= 0) { portalStack_t ps; int i; static int lastPrintedAreaNum; - if ( tr.viewDef->areaNum != lastPrintedAreaNum ) { + if (tr.viewDef->areaNum != lastPrintedAreaNum) { lastPrintedAreaNum = tr.viewDef->areaNum; - common->Printf( "entering portal area %i\n", tr.viewDef->areaNum ); + common->Printf("entering portal area %i\n", tr.viewDef->areaNum); } - for ( i = 0 ; i < 5 ; i++ ) { + for (i = 0; i < 5; i++) { ps.portalPlanes[i] = tr.viewDef->frustum[i]; } ps.numPortalPlanes = 5; ps.rect = tr.viewDef->scissor; - AddAreaRefs( tr.viewDef->areaNum, &ps ); + AddAreaRefs(tr.viewDef->areaNum, &ps); } - } else { + } + else { // note that the center of projection for flowing through portals may // be a different point than initialViewAreaOrigin for subviews that // may have the viewOrigin in a solid/invalid area - FlowViewThroughPortals( tr.viewDef->renderView.vieworg, 5, tr.viewDef->frustum ); + FlowViewThroughPortals(tr.viewDef->renderView.vieworg, 5, tr.viewDef->frustum); } } @@ -893,7 +1432,7 @@ void idRenderWorldLocal::FindViewLightsAndEntities( void ) { NumPortals ============== */ -int idRenderWorldLocal::NumPortals( void ) const { +int idRenderWorldLocal::NumPortals(void) const { return numInterAreaPortals; } @@ -905,21 +1444,21 @@ Game code uses this to identify which portals are inside doors. Returns 0 if no portal contacts the bounds ============== */ -qhandle_t idRenderWorldLocal::FindPortal( const idBounds &b ) const { +qhandle_t idRenderWorldLocal::FindPortal(const idBounds& b) const { int i, j; idBounds wb; - doublePortal_t *portal; - idWinding *w; + doublePortal_t* portal; + idWinding* w; - for ( i = 0 ; i < numInterAreaPortals ; i++ ) { + for (i = 0; i < numInterAreaPortals; i++) { portal = &doublePortals[i]; w = portal->portals[0]->w; wb.Clear(); - for ( j = 0 ; j < w->GetNumPoints() ; j++ ) { - wb.AddPoint( (*w)[j].ToVec3() ); + for (j = 0; j < w->GetNumPoints(); j++) { + wb.AddPoint((*w)[j].ToVec3()); } - if ( wb.IntersectsBounds( b ) ) { + if (wb.IntersectsBounds(b)) { return i + 1; } } @@ -932,15 +1471,15 @@ qhandle_t idRenderWorldLocal::FindPortal( const idBounds &b ) const { FloodConnectedAreas ============= */ -void idRenderWorldLocal::FloodConnectedAreas( portalArea_t *area, int portalAttributeIndex ) { - if ( area->connectedAreaNum[portalAttributeIndex] == connectedAreaNum ) { +void idRenderWorldLocal::FloodConnectedAreas(portalArea_t* area, int portalAttributeIndex) { + if (area->connectedAreaNum[portalAttributeIndex] == connectedAreaNum) { return; } area->connectedAreaNum[portalAttributeIndex] = connectedAreaNum; - for ( portal_t *p = area->portals ; p ; p = p->next ) { - if ( !(p->doublePortal->blockingBits & (1<intoArea], portalAttributeIndex ); + for (portal_t* p = area->portals; p; p = p->next) { + if (!(p->doublePortal->blockingBits & (1 << portalAttributeIndex))) { + FloodConnectedAreas(&portalAreas[p->intoArea], portalAttributeIndex); } } } @@ -951,24 +1490,24 @@ AreasAreConnected ============== */ -bool idRenderWorldLocal::AreasAreConnected( int areaNum1, int areaNum2, portalConnection_t connection ) { - if ( areaNum1 == -1 || areaNum2 == -1 ) { +bool idRenderWorldLocal::AreasAreConnected(int areaNum1, int areaNum2, portalConnection_t connection) { + if (areaNum1 == -1 || areaNum2 == -1) { return false; } - if ( areaNum1 > numPortalAreas || areaNum2 > numPortalAreas || areaNum1 < 0 || areaNum2 < 0 ) { - common->Error( "idRenderWorldLocal::AreAreasConnected: bad parms: %i, %i", areaNum1, areaNum2 ); + if (areaNum1 > numPortalAreas || areaNum2 > numPortalAreas || areaNum1 < 0 || areaNum2 < 0) { + common->Error("idRenderWorldLocal::AreAreasConnected: bad parms: %i, %i", areaNum1, areaNum2); } int attribute = 0; int intConnection = (int)connection; - while ( intConnection > 1 ) { + while (intConnection > 1) { attribute++; intConnection >>= 1; } - if ( attribute >= NUM_PORTAL_ATTRIBUTES || ( 1 << attribute ) != (int)connection ) { - common->Error( "idRenderWorldLocal::AreasAreConnected: bad connection number: %i\n", (int)connection ); + if (attribute >= NUM_PORTAL_ATTRIBUTES || (1 << attribute) != (int)connection) { + common->Error("idRenderWorldLocal::AreasAreConnected: bad connection number: %i\n", (int)connection); } return portalAreas[areaNum1].connectedAreaNum[attribute] == portalAreas[areaNum2].connectedAreaNum[attribute]; @@ -982,34 +1521,34 @@ SetPortalState doors explicitly close off portals when shut ============== */ -void idRenderWorldLocal::SetPortalState( qhandle_t portal, int blockTypes ) { - if ( portal == 0 ) { +void idRenderWorldLocal::SetPortalState(qhandle_t portal, int blockTypes) { + if (portal == 0) { return; } - if ( portal < 1 || portal > numInterAreaPortals ) { - common->Error( "SetPortalState: bad portal number %i", portal ); + if (portal < 1 || portal > numInterAreaPortals) { + common->Error("SetPortalState: bad portal number %i", portal); } - int old = doublePortals[portal-1].blockingBits; - if ( old == blockTypes ) { + int old = doublePortals[portal - 1].blockingBits; + if (old == blockTypes) { return; } - doublePortals[portal-1].blockingBits = blockTypes; + doublePortals[portal - 1].blockingBits = blockTypes; // leave the connectedAreaGroup the same on one side, // then flood fill from the other side with a new number for each changed attribute - for ( int i = 0 ; i < NUM_PORTAL_ATTRIBUTES ; i++ ) { - if ( ( old ^ blockTypes ) & ( 1 << i ) ) { + for (int i = 0; i < NUM_PORTAL_ATTRIBUTES; i++) { + if ((old ^ blockTypes) & (1 << i)) { connectedAreaNum++; - FloodConnectedAreas( &portalAreas[doublePortals[portal-1].portals[1]->intoArea], i ); + FloodConnectedAreas(&portalAreas[doublePortals[portal - 1].portals[1]->intoArea], i); } } - if ( session->writeDemo ) { - session->writeDemo->WriteInt( DS_RENDER ); - session->writeDemo->WriteInt( DC_SET_PORTAL_STATE ); - session->writeDemo->WriteInt( portal ); - session->writeDemo->WriteInt( blockTypes ); + if (session->writeDemo) { + session->writeDemo->WriteInt(DS_RENDER); + session->writeDemo->WriteInt(DC_SET_PORTAL_STATE); + session->writeDemo->WriteInt(portal); + session->writeDemo->WriteInt(blockTypes); } } @@ -1018,16 +1557,16 @@ void idRenderWorldLocal::SetPortalState( qhandle_t portal, int blockTypes ) { GetPortalState ============== */ -int idRenderWorldLocal::GetPortalState( qhandle_t portal ) { - if ( portal == 0 ) { +int idRenderWorldLocal::GetPortalState(qhandle_t portal) { + if (portal == 0) { return 0; } - if ( portal < 1 || portal > numInterAreaPortals ) { - common->Error( "GetPortalState: bad portal number %i", portal ); + if (portal < 1 || portal > numInterAreaPortals) { + common->Error("GetPortalState: bad portal number %i", portal); } - return doublePortals[portal-1].blockingBits; + return doublePortals[portal - 1].blockingBits; } /* @@ -1039,33 +1578,34 @@ Debugging tool, won't work correctly with SMP or when mirrors are present */ void idRenderWorldLocal::ShowPortals() { int i, j; - portalArea_t *area; - portal_t *p; - idWinding *w; + portalArea_t* area; + portal_t* p; + idWinding* w; // flood out through portals, setting area viewCount - for ( i = 0 ; i < numPortalAreas ; i++ ) { + for (i = 0; i < numPortalAreas; i++) { area = &portalAreas[i]; - if ( area->viewCount != tr.viewCount ) { + if (area->viewCount != tr.viewCount) { continue; } - for ( p = area->portals ; p ; p = p->next ) { + for (p = area->portals; p; p = p->next) { w = p->w; - if ( !w ) { + if (!w) { continue; } - if ( portalAreas[ p->intoArea ].viewCount != tr.viewCount ) { + if (portalAreas[p->intoArea].viewCount != tr.viewCount) { // red = can't see - glColor3f( 1, 0, 0 ); - } else { + glColor3f(1, 0, 0); + } + else { // green = see through - glColor3f( 0, 1, 0 ); + glColor3f(0, 1, 0); } - glBegin( GL_LINE_LOOP ); - for ( j = 0 ; j < w->GetNumPoints() ; j++ ) { - glVertex3fv( (*w)[j].ToFloatPtr() ); + glBegin(GL_LINE_LOOP); + for (j = 0; j < w->GetNumPoints(); j++) { + glVertex3fv((*w)[j].ToFloatPtr()); } glEnd(); } diff --git a/neo/renderer/tr_light.cpp b/neo/renderer/tr_light.cpp index 7f55f3e9..726c4a15 100644 --- a/neo/renderer/tr_light.cpp +++ b/neo/renderer/tr_light.cpp @@ -1341,6 +1341,15 @@ static void R_AddAmbientDrawsurfs( viewEntity_t *vEntity ) { continue; } + if (tr.viewDef->renderWorld) + { + dxrWorldModel_t* dxrWorldModel = tr.viewDef->renderWorld->GetDXRModelForSurf((srfTriangles_t*)tri); + if (dxrWorldModel) + { + glShowTopLevelAccelStructure(tr.viewDef->renderWorld->dxrWorldId, dxrWorldModel->topAccelStruct); + } + } + // debugging tool to make sure we are have the correct pre-calculated bounds if ( r_checkBounds.GetBool() ) { int j, k;