ifc_lite_server/services/
parquet_vertex_columns.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//! One shape's vertex columns, in the Y-up wire frame.
6//!
7//! The Z-up to Y-up swap is applied here, once, server-side, so no client
8//! repeats it per vertex (IFC is Z-up, WebGL is Y-up). Separate from
9//! `parquet_mesh_tables.rs` because it is per-SHAPE work: it knows nothing
10//! about which mesh rows point at the block it produces.
11
12use crate::services::axis::zup_to_yup;
13use crate::types::MeshData;
14
15/// One shape's six Y-up vertex columns in `vertex_schema()` order,
16/// `(x, y, z, nx, ny, nz)`. Named because every slot has the same type and the
17/// positional pairing is what keeps positions out of the normal columns.
18pub(super) type VertexColumns = (Vec<f32>, Vec<f32>, Vec<f32>, Vec<f32>, Vec<f32>, Vec<f32>);
19
20pub(super) fn shape_vertices(mesh: &MeshData) -> VertexColumns {
21    let vert_count = mesh.positions.len() / 3;
22    let mut px = Vec::with_capacity(vert_count);
23    let mut py = Vec::with_capacity(vert_count);
24    let mut pz = Vec::with_capacity(vert_count);
25    let mut nx = Vec::with_capacity(vert_count);
26    let mut ny = Vec::with_capacity(vert_count);
27    let mut nz = Vec::with_capacity(vert_count);
28
29    // Some IFC pipelines (e.g. advanced_brep) yield meshes with positions but
30    // no normals. The schema requires non-null normal columns, so pad with
31    // zeros and let the client recompute them from positions.
32    let has_normals = mesh.normals.len() == mesh.positions.len();
33    if !has_normals && !mesh.normals.is_empty() {
34        tracing::warn!(
35            express_id = mesh.express_id,
36            ifc_type = %mesh.ifc_type,
37            positions = mesh.positions.len(),
38            normals = mesh.normals.len(),
39            "Mesh normals length mismatch; emitting zero normals"
40        );
41    }
42
43    for i in 0..vert_count {
44        let (x, y, z) = zup_to_yup(
45            mesh.positions[i * 3],
46            mesh.positions[i * 3 + 1],
47            mesh.positions[i * 3 + 2],
48        );
49        px.push(x);
50        py.push(y);
51        pz.push(z);
52
53        if has_normals {
54            let (x, y, z) = zup_to_yup(
55                mesh.normals[i * 3],
56                mesh.normals[i * 3 + 1],
57                mesh.normals[i * 3 + 2],
58            );
59            nx.push(x);
60            ny.push(y);
61            nz.push(z);
62        } else {
63            nx.push(0.0);
64            ny.push(0.0);
65            nz.push(0.0);
66        }
67    }
68    (px, py, pz, nx, ny, nz)
69}