diff --git a/src/Layers/xrRender/r__dsgraph_build.cpp b/src/Layers/xrRender/r__dsgraph_build.cpp index 67d24fe9d55..37ea3f73dc3 100644 --- a/src/Layers/xrRender/r__dsgraph_build.cpp +++ b/src/Layers/xrRender/r__dsgraph_build.cpp @@ -830,7 +830,9 @@ void R_dsgraph_structure::build_subspace() for (u32 o_it = 0; o_it < lstRenderables.size(); o_it++) { ISpatial* spatial = lstRenderables[o_it]; - if (o.is_main_pass) + // Re-detect the sector only when it was invalidated by a move (spatial_move). Static objects keep + // their cached sector and skip the 2 ray_query casts entirely - this is the bulk of per-frame rays. + if (o.is_main_pass && (spatial->GetSpatialData().type & STYPEFLAG_INVALIDSECTOR)) { const auto& entity_pos = spatial->spatial_sector_point(); const auto sector_id = detect_sector(entity_pos); diff --git a/src/Layers/xrRender_R2/r2_R_calculate.cpp b/src/Layers/xrRender_R2/r2_R_calculate.cpp index 86be1c54176..355106aea90 100644 --- a/src/Layers/xrRender_R2/r2_R_calculate.cpp +++ b/src/Layers/xrRender_R2/r2_R_calculate.cpp @@ -114,8 +114,12 @@ void CRender::Calculate() g_pGamePersistent->SpatialSpace.q_sphere(spatial_lights, 0, STYPE_LIGHTSOURCE, Device.vCameraPosition, EPS_L); for (auto spatial : spatial_lights) { - const auto& entity_pos = spatial->spatial_sector_point(); - spatial->spatial_updatesector(dsgraph_main.detect_sector(entity_pos)); + if (spatial->GetSpatialData().type & STYPEFLAG_INVALIDSECTOR) + { + const auto& entity_pos = spatial->spatial_sector_point(); + spatial->spatial_updatesector(dsgraph_main.detect_sector(entity_pos)); + } + const auto sector_id = spatial->GetSpatialData().sector_id; if (sector_id == IRender_Sector::INVALID_SECTOR_ID) continue; // disassociated from S/P structure diff --git a/src/xrCDB/ISpatial.cpp b/src/xrCDB/ISpatial.cpp index 2526180b7c1..fe82fcc4108 100644 --- a/src/xrCDB/ISpatial.cpp +++ b/src/xrCDB/ISpatial.cpp @@ -100,8 +100,13 @@ void SpatialBase::spatial_move() ZoneScoped; if (spatial.node_ptr) { - //*** somehow it was determined that object has been moved - spatial.type |= STYPEFLAG_INVALIDSECTOR; + //*** invalidate the cached sector only on a meaningful move, so stationary/jittering + //*** objects don't force a per-frame sector raycast (see r__dsgraph_build sector detection) + //*** adopted from xray-monolith + if (last_sector_point.distance_to_sqr(spatial_sector_point()) > 1.f) + { + spatial.type |= STYPEFLAG_INVALIDSECTOR; + } //*** check if we are supposed to correct it's spatial location if (spatial_inside()) @@ -119,6 +124,7 @@ void SpatialBase::spatial_move() void SpatialBase::spatial_updatesector_internal(IRender_Sector::sector_id_t sector_id) { ZoneScoped; + last_sector_point = spatial_sector_point(); spatial.type &= ~STYPEFLAG_INVALIDSECTOR; if (sector_id != IRender_Sector::INVALID_SECTOR_ID) spatial.sector_id = sector_id; diff --git a/src/xrCDB/ISpatial.h b/src/xrCDB/ISpatial.h index 30ee17a46bf..7c045b0c171 100644 --- a/src/xrCDB/ISpatial.h +++ b/src/xrCDB/ISpatial.h @@ -137,6 +137,8 @@ class XRCDB_API XR_NOVTABLE SpatialBase : public virtual ISpatial SpatialData spatial; private: + // Point at which the sector was last (re)detected. + Fvector last_sector_point{}; void spatial_updatesector_internal(IRender_Sector::sector_id_t sector_id); public: