@@ -104,23 +104,52 @@ struct Occluder : compo::RestrictedSubsystem<Occluder<V>, OccluderManifest<V>>
104104 u32 current_cluster{0 };
105105 generation_idx_t current_bsp_id{};
106106
107- for (auto & ent : p.select (ObjectBsp))
107+ /* Structure BSP switching: the active section changes only when the
108+ * camera crosses a bsp-switch trigger volume whose source is the
109+ * active section — flying into another section's space without
110+ * crossing one keeps that section hidden, exactly like the original
111+ * engine. Everything below (cluster lookup, PVS, culling) is
112+ * restricted to the active section, so inactive sections cost
113+ * nothing per frame. */
114+ for (auto const & sw : bsp_cache->bsp_switches )
108115 {
109- auto ref = p.template ref <Proxy>(ent);
110- BspReference& bsp_ref = ref.template get <BspReference>();
116+ if (sw.source != bsp_cache->active_section )
117+ continue ;
118+ if (!sw.volume ->contains (camera_pos))
119+ continue ;
120+ cDebug (
121+ " BSP switch: section {} → {} ('{}')" ,
122+ sw.source ,
123+ sw.destination ,
124+ sw.volume ->name .str ());
125+ bsp_cache->active_section = sw.destination ;
126+ break ;
127+ }
128+
129+ BSPItem const * active_bsp{nullptr };
130+ generation_idx_t active_bsp_id{};
131+ for (auto & [id, item] : bsp_cache->m_cache )
132+ if (item.valid () && item.section_idx == bsp_cache->active_section )
133+ {
134+ active_bsp = &item;
135+ active_bsp_id = {id, bsp_cache->generation };
136+ break ;
137+ }
111138
112- BSPItem const & bsp = bsp_cache->find (bsp_ref.bsp )->second ;
113- for (auto const & cluster : bsp.clusters )
139+ if (active_bsp)
140+ {
141+ for (auto const & cluster : active_bsp->clusters )
114142 for (auto const & sub : cluster.sub )
115143 portal_colors[sub.debug_color_idx ] = Vecf3 (1 , 0 , 0 );
116144
117- if (auto cluster = bsp.find_cluster (camera_pos); cluster.has_value ())
145+ if (auto cluster = active_bsp->find_cluster (camera_pos);
146+ cluster.has_value ())
118147 {
119148 auto [cluster_, sub_] = cluster.value ();
120- current_bsp = &bsp ;
149+ current_bsp = active_bsp ;
121150 current_cluster = cluster_;
122- current_bsp_id = bsp_ref. bsp ;
123- auto const & subs = bsp. clusters .at (cluster_).sub ;
151+ current_bsp_id = active_bsp_id ;
152+ auto const & subs = active_bsp-> clusters .at (cluster_).sub ;
124153 if (sub_ < subs.size ())
125154 portal_colors[subs.at (sub_).debug_color_idx ] =
126155 Vecf3 (0 , 1 , 0 );
@@ -132,24 +161,30 @@ struct Occluder : compo::RestrictedSubsystem<Occluder<V>, OccluderManifest<V>>
132161 /* When the camera enters a new cluster, recompute the portal-traversal
133162 * visible set. When between clusters, keep the last valid set so
134163 * culling doesn't snap to all-visible at cluster boundaries. */
135- bool cluster_changed = (current_bsp != nullptr ) != last_found ||
164+ bool section_changed = active_bsp != pvs_bsp;
165+ bool cluster_changed = section_changed ||
166+ (current_bsp != nullptr ) != last_found ||
136167 current_cluster != last_cluster;
137168 last_found = current_bsp != nullptr ;
138169 last_cluster = current_cluster;
139170
171+ /* The active section is culled even while the camera is outside its
172+ * clusters; other sections stay hidden wholesale. */
173+ pvs_bsp = active_bsp;
174+ pvs_bsp_id = active_bsp_id;
175+ if (section_changed)
176+ pvs_visible.clear (); /* stale per-cluster bits of old section */
177+
140178 if (current_bsp)
141179 {
142- pvs_bsp = current_bsp;
143180 pvs_cluster = current_cluster;
144- pvs_bsp_id = current_bsp_id;
145181 pvs_visible = pvs_bsp->portal_visible_set (
146182 pvs_cluster, camera_pos, camera_mvp);
147- } else if (pvs_bsp)
148- {
149- /* Camera outside all clusters (noclip outside the map shell):
150- * keep the last valid visible set. Snapping to all-visible here
151- * is what used to flash far-off geometry into view. */
152183 }
184+ /* else: camera outside the active section's clusters (noclip through
185+ * rock) — keep the last valid set; empty set = all-visible within the
186+ * active section. Snapping to all-visible across sections is what
187+ * used to flash far-off geometry into view. */
153188
154189 rendering->current_bsp_cluster = pvs_cluster;
155190
0 commit comments