fix(viewport): include model-block hatches in paper layouts

Paper-space content viewports rendered the model's wires + meshes but
no hatches. `synced_hatch_models` only kept entries owned by the active
layout's block, so flipping to a paper layout dropped every hatch
created in model space from the arc that `viewport_data_for` uploads
to the per-vp GPU pipeline.

`populate_hatches_from_document` already bakes the right per-block
world_offset into each `world_origin` (model → `self.world_offset`,
paper → 0), so adding both buckets back is safe: projecting a paper-
block hatch through a model viewport's camera lands it outside the
frustum and the per-vp scissor / LOD culls it (and vice versa). No
double-rendering.

`wipeout_models` had the same shape of bug — its `world_offset` was
picked from the active layout instead of the wipeout's owner. Now it
chooses per-entity, so model wipeouts also show up in paper viewports.
This commit is contained in:
Hakan Seven committed 2026-05-28 20:59:12 +03:00
1 parent 74a685d0e8
commit 4927804b77
1 file changed
+31 -9
+31 -9
View File
@@ -2822,6 +2822,15 @@ impl Scene {
// skip flag in compute_hatch_lod handles frustum + sub-pixel
// culling at draw time, which keeps the GPU upload set stable
// across pan/zoom.
//
// We INCLUDE hatches from blocks other than `current_layout`'s
// own block (specifically: paper-layout content viewports want
// model-block hatches). Every hatch's `world_origin` is already
// baked into the correct block coord-space at
// `populate_hatches_from_document` time (offset for model, 0 for
// paper), so projecting them through a camera built for the
// wrong block lands them outside the frustum and the per-vp
// scissor / LOD culls them out — no double-rendering.
let mut models: Vec<HatchModel> = self
.hatches
.iter()
@@ -2830,9 +2839,19 @@ impl Scene {
return true;
};
let c = entity.common();
!c.invisible
&& !layer_hidden(&c.layer)
&& self.belongs_to_visible_block(handle, c.owner_handle, layout_block)
if c.invisible || layer_hidden(&c.layer) {
return false;
}
// Reject block-defn-only hatches (entities owned by a
// BLOCK record that's neither model nor a paper layout
// block) — they're tessellated separately via Insert
// explosion and only the laid-out copies should appear.
self.belongs_to_visible_block(handle, c.owner_handle, layout_block)
|| self.belongs_to_visible_block(
handle,
c.owner_handle,
self.model_space_block_handle(),
)
})
.map(|(&handle, model)| {
let entity = self.document.get_entity(handle);
@@ -2959,12 +2978,7 @@ impl Scene {
} else {
self.bg_color
};
// Paper-space entities are already in small coordinates — don't shift them.
let world_offset = if is_paper {
[0.0; 3]
} else {
self.world_offset
};
let model_block = self.model_space_block_handle();
// No per-frame view-cull here: GPU wipeout buffer upload is
// gated on geometry_epoch only (see render.rs), so any cull at
// build time would freeze the visible subset at the geometry
@@ -2988,6 +3002,14 @@ impl Scene {
{
continue;
}
// Per-entity world_offset selection so paper-layout content
// viewports still see model-block wipeouts at the right local
// coordinates (same rationale as hatches).
let world_offset = if wo.common.owner_handle == model_block {
self.world_offset
} else {
[0.0; 3]
};
let boundary = Self::wipeout_boundary_2d(wo, world_offset);
if boundary.len() >= 3 {
let mut fill_color = bg_color;