fix(render): adapt mesh + hatch load colours to background

`adapt_to_bg` (pure black → white on dark bg, pure white → black on
light bg) was only running through `render_style`, which the wire and
per-frame hatch paths already use. The mesh population paths and the
file-load hatch path went straight through `aci_to_rgba`, so:

- ACIS solids / regions / bodies appeared in their raw ACI colour
  even when that colour was pure white on a white paper bg.
- The initial hatch upload used the unadapted colour for the first
  frame after load (`synced_hatch_models` then re-ran adaptation per
  frame, so the issue cleared after one redraw — but visible).

Now every mesh / hatch creation site funnels through
`render_style_for` + `adapt_to_bg`:

- `build_derived_caches` (load): uses the default model bg as the
  adaptation target.
- `populate_meshes_from_document`: uses `self.render_style` so the
  per-frame paper / model bg selection applies.
- New `Scene::recolor_meshes` rewrites every cached mesh's colour
  field through `render_style`; called from `BACKGROUND` (after the
  user changes bg) and `set_current_layout` (Model ↔ paper bg
  switch) so ACIS mesh colour tracks the live bg without re-
  tessellating ACIS geometry.

Wipeout fills stay bg-coloured by design (that's their job). Raster
images skip adaptation — pixel data is not entity colour.
This commit is contained in:
Hakan Seven committed 2026-05-28 21:15:28 +03:00
1 parent e9e3bbbca1
commit 8a510c563c
2 files changed
+51 -13

No files matched your search

+7 -1
View File
@@ -54,7 +54,12 @@ impl OpenCADStudio {
// Wire colour adaptation (`adapt_to_bg`) reads the bg
// at tessellation time, so the cached wires need to
// refresh — otherwise a light→dark bg flip leaves
// black lines invisible against the new bg.
// black lines invisible against the new bg. Meshes
// bake colour into per-vertex GPU buffers at upload
// time; `recolor_meshes` rewrites the CPU side so
// the next epoch-driven re-upload picks up the new
// colour.
self.tabs[i].scene.recolor_meshes();
self.tabs[i].scene.bump_geometry();
self.command_line
.push_output("Background reset to default.");
@@ -69,6 +74,7 @@ impl OpenCADStudio {
self.tabs[i].bg_color = Some([r, g, b, 1.0]);
self.tabs[i].scene.bg_color = [r, g, b, 1.0];
}
self.tabs[i].scene.recolor_meshes();
self.tabs[i].scene.bump_geometry();
self.command_line.push_output(&format!(
"Background: rgb({}, {}, {})",
+44 -12
View File
@@ -168,6 +168,12 @@ pub fn build_derived_caches(doc: &CadDocument) -> DerivedCaches {
}
}
// Default bg adaptation target at load: the model background (paper
// bg is only relevant after the user enters a paper layout, and
// `synced_hatch_models` re-runs `render_style` per-frame anyway so
// the per-layout adaptation kicks in later regardless).
const LOAD_BG: [f32; 4] = [0.11, 0.11, 0.11, 1.0];
// hatches
let hatches: HashMap<Handle, HatchModel> = hatch_handles
.par_iter()
@@ -179,15 +185,11 @@ pub fn build_derived_caches(doc: &CadDocument) -> DerivedCaches {
} else {
[0.0; 3]
};
let (raw, ..) = render::render_style_for(doc, e);
let color = render::adapt_to_bg(raw, LOAD_BG);
let model = match e {
EntityType::Hatch(dxf) => {
let color = tess_util::aci_to_rgba(&dxf.common.color);
Scene::hatch_model_from_dxf(dxf, color, offset)
}
EntityType::Solid(solid) => {
let color = tess_util::aci_to_rgba(&solid.common.color);
Some(Scene::solid_hatch_model(solid, color, offset))
}
EntityType::Hatch(dxf) => Scene::hatch_model_from_dxf(dxf, color, offset),
EntityType::Solid(solid) => Some(Scene::solid_hatch_model(solid, color, offset)),
_ => None,
};
model.map(|m| (handle, m))
@@ -214,7 +216,8 @@ pub fn build_derived_caches(doc: &CadDocument) -> DerivedCaches {
.par_iter()
.filter_map(|&handle| {
let e = doc.get_entity(handle)?;
let color = tess_util::aci_to_rgba(&e.common().color);
let (raw, ..) = render::render_style_for(doc, e);
let color = render::adapt_to_bg(raw, LOAD_BG);
crate::entities::solid3d::tessellate_volume(e, color, facet_res)
.map(|m| (handle, offset_mesh_lod_set(m, world_offset)))
})
@@ -820,13 +823,40 @@ impl Scene {
self.geometry_epoch = GEOMETRY_EPOCH.fetch_add(1, Ordering::Relaxed);
}
/// Re-evaluate every cached mesh's color through `render_style` so a
/// `BACKGROUND` change picks up the new `adapt_to_bg` result without
/// re-tessellating ACIS geometry. Caller must bump `geometry_epoch`
/// afterwards so the GPU re-uploads the now-updated colour data.
pub fn recolor_meshes(&mut self) {
// Cache colour lookups by handle to avoid borrowing the document
// re-entrantly through `render_style` inside a `&mut self` loop.
let colors: HashMap<Handle, [f32; 4]> = self
.meshes
.keys()
.filter_map(|&h| {
self.document
.get_entity(h)
.map(|e| (h, self.render_style(e).0))
})
.collect();
for (h, set) in self.meshes.iter_mut() {
if let Some(&c) = colors.get(h) {
for lod in &mut set.lods {
lod.color = c;
}
}
}
}
/// Switch the active layout. Bumps `geometry_epoch` so the wire cache
/// re-tessellates — `render_style`'s `adapt_to_bg` picks the model or
/// paper background depending on `current_layout`, so cached wires
/// from the previous layout would be coloured against the wrong bg.
/// Also runs `recolor_meshes` so ACIS mesh colour tracks the new bg.
pub fn set_current_layout(&mut self, name: String) {
if self.current_layout != name {
self.current_layout = name;
self.recolor_meshes();
self.bump_geometry();
}
}
@@ -3502,14 +3532,16 @@ impl Scene {
/// `Solid3D` entity is represented in the mesh cache.
pub fn populate_meshes_from_document(&mut self) {
self.meshes.clear();
// Collect all ACIS-bearing entities with their color resolved now,
// so the parallel phase only sees owned data.
// Resolve color through `render_style` so the same bg adaptation
// wires use kicks in (pure black on dark bg → white, pure white
// on light bg → black). Without this, ACIS meshes ignore
// `adapt_to_bg` and stay invisible against matching bg colours.
let entries: Vec<(Handle, EntityType, [f32; 4])> = self
.document
.entities()
.filter_map(|e| match e {
EntityType::Solid3D(_) | EntityType::Region(_) | EntityType::Body(_) => {
let color = tess_util::aci_to_rgba(&e.common().color);
let color = self.render_style(e).0;
Some((e.common().handle, e.clone(), color))
}
_ => None,