ifc_lite_geometry/instancing/
dont_bake.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! #1623 Phase 2 don't-bake finalize helpers.
6//!
7//! Split out of `collate.rs` to keep that module under its size ratchet; the
8//! logic is unchanged, so instanced-occurrence placement (and every
9//! reconstructed byte) is identical. These three functions let the browser
10//! don't-bake path record and, if needed, recover a world placement WITHOUT
11//! ever materializing the occurrence's own baked vertices — see each fn doc.
12
13use super::collate::{compose_world, mat4_to_row_major_f32, to_post_rtc};
14use crate::mesh::{InstanceMeta, Mesh};
15use nalgebra::Matrix4;
16
17/// Compose an occurrence's full PRE-RTC world transform `transform·local·canonical`
18/// as a row-major `[f64; 16]`. Public so the processing crate's don't-bake finalize
19/// (#1623 Phase 2) can record the SAME world placement `collate_refs` computes for a
20/// baked occurrence — without materializing the occurrence's vertices.
21pub fn compose_instance_world_row_major(meta: &InstanceMeta) -> [f64; 16] {
22    let m = compose_world(meta);
23    let mut out = [0.0f64; 16];
24    for r in 0..4 {
25        for c in 0..4 {
26            out[r * 4 + c] = m[(r, c)];
27        }
28    }
29    out
30}
31
32/// Template-relative instance transform `rel = post_rtc(M_k) · post_rtc(M_ref)⁻¹`
33/// as a row-major `[f32; 16]`, or `None` when `M_ref` is singular. `m_k` / `m_ref`
34/// are PRE-RTC row-major world transforms (see [`compose_instance_world_row_major`]);
35/// `rtc` is the model offset. This is EXACTLY `collate_refs`' per-occurrence `rel`,
36/// exposed for the don't-bake finalize where the occurrence carries no geometry to
37/// group — the template's baked world geometry placed by `rel` reproduces the
38/// occurrence's world geometry (bounded by `verify_recomposition`). #1623 Phase 2.
39pub fn instance_rel_row_major_f32(
40    m_k: &[f64; 16],
41    m_ref: &[f64; 16],
42    rtc: [f64; 3],
43) -> Option<[f32; 16]> {
44    let mk = to_post_rtc(Matrix4::from_row_slice(m_k), rtc);
45    let mref = to_post_rtc(Matrix4::from_row_slice(m_ref), rtc);
46    let mref_inv = mref.try_inverse()?;
47    Some(mat4_to_row_major_f32(&(mk * mref_inv)))
48}
49
50/// Bake a SOURCE-coords `Mesh` at a PRE-RTC row-major world transform into absolute
51/// POST-RTC world geometry `(positions, normals, indices)` — the #1623 Phase 2
52/// finalize fallback for a don't-bake instance whose template occurrence never
53/// materialized (an orphan; effectively unreachable for the eligible single-solid
54/// type-instanced set, but kept so geometry is NEVER silently lost). The affine part
55/// transforms positions; the inverse-transpose of the linear part transforms normals
56/// (renormalized). Geometrically equal to the baked flat occurrence (same triangles);
57/// the registry source is pre-weld, so vertices are unwelded — that changes only the
58/// vertex count, not the rendered surface.
59pub fn bake_source_at_world(
60    source: &Mesh,
61    world_row_major: &[f64; 16],
62    rtc: [f64; 3],
63) -> (Vec<f32>, Vec<f32>, Vec<u32>) {
64    let m = to_post_rtc(Matrix4::from_row_slice(world_row_major), rtc);
65    let vcount = source.positions.len() / 3;
66    let mut positions = Vec::with_capacity(source.positions.len());
67    for v in 0..vcount {
68        let p = m * nalgebra::Vector4::new(
69            source.positions[v * 3] as f64,
70            source.positions[v * 3 + 1] as f64,
71            source.positions[v * 3 + 2] as f64,
72            1.0,
73        );
74        positions.push((p.x / p.w) as f32);
75        positions.push((p.y / p.w) as f32);
76        positions.push((p.z / p.w) as f32);
77    }
78    let linear = m.fixed_view::<3, 3>(0, 0).into_owned();
79    let nmat = linear
80        .try_inverse()
81        .map(|inv| inv.transpose())
82        .unwrap_or(linear);
83    let ncount = source.normals.len() / 3;
84    let mut normals = Vec::with_capacity(source.normals.len());
85    for v in 0..ncount {
86        let nv = nmat
87            * nalgebra::Vector3::new(
88                source.normals[v * 3] as f64,
89                source.normals[v * 3 + 1] as f64,
90                source.normals[v * 3 + 2] as f64,
91            );
92        let nv = nv.try_normalize(0.0).unwrap_or(nv);
93        normals.push(nv.x as f32);
94        normals.push(nv.y as f32);
95        normals.push(nv.z as f32);
96    }
97    (positions, normals, source.indices.clone())
98}