fix(xref): tessellate xref'd solids so they render (#203)

XREFs are merged into the document on the UI thread AFTER the background
worker already built the mesh caches, so those caches contained none of
the xref'd geometry. The wire pass rebuilds from the document each frame
(bump_geometry covers it), but 3D-solid meshes are only produced by
populate_meshes_from_document, which never re-ran after the merge — so
xref'd solids (walls, floors, roofs) were loaded but never tessellated
and never drawn. Re-run it once a xref actually resolves.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-07-02 10:20:24 +03:00
commit a763144622

View file

@ -286,6 +286,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
// Auto-resolve XREFs relative to the opened file's directory. // Auto-resolve XREFs relative to the opened file's directory.
let mut xref_ms = 0u32; let mut xref_ms = 0u32;
let mut xref_merged = false;
if let Some(base_dir) = path.parent() { if let Some(base_dir) = path.parent() {
// xref content arrives un-purged: parser-garbage entities // xref content arrives un-purged: parser-garbage entities
// inside the referenced file can trigger infinite loops in // inside the referenced file can trigger infinite loops in
@ -304,6 +305,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
for info in &xrefs { for info in &xrefs {
match info.status { match info.status {
crate::io::xref::XrefStatus::Loaded => { crate::io::xref::XrefStatus::Loaded => {
xref_merged = true;
self.command_line self.command_line
.push_output(&format!("XREF Loaded \"{}\"", info.name)); .push_output(&format!("XREF Loaded \"{}\"", info.name));
} }
@ -344,6 +346,15 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
self.tabs[i].scene.block_meshes = caches.block_meshes; self.tabs[i].scene.block_meshes = caches.block_meshes;
// Invalidate the wire cache so the new document is tessellated. // Invalidate the wire cache so the new document is tessellated.
self.tabs[i].scene.bump_geometry(); self.tabs[i].scene.bump_geometry();
// XREFs are merged into the document AFTER the background worker
// built the mesh caches above, so those caches contain none of
// the xref'd geometry. The wire pass rebuilds from the document
// each frame (bump_geometry covers it), but 3D-solid meshes are
// only tessellated by `populate_meshes_from_document` — re-run it
// so xref'd solids (walls, floors, roofs) actually render. (#203)
if xref_merged {
self.tabs[i].scene.populate_meshes_from_document();
}
self.tabs[i].scene.selected = rustc_hash::FxHashSet::default(); self.tabs[i].scene.selected = rustc_hash::FxHashSet::default();
self.tabs[i].scene.preview_wires = vec![]; self.tabs[i].scene.preview_wires = vec![];
self.tabs[i].scene.current_layout = "Model".to_string(); self.tabs[i].scene.current_layout = "Model".to_string();