mod grid;
mod linear;
mod mesh_world;
mod parsers;
use super::GeometryRouter;
use crate::{Mesh, Result, SubMeshCollection};
use ifc_lite_core::{DecodedEntity, EntityDecoder, IfcType};
use nalgebra::Matrix4;
static LOCAL_FRAME_OVERRIDE: std::sync::atomic::AtomicI8 = std::sync::atomic::AtomicI8::new(-1);
pub fn local_frame_set_enabled_override(v: Option<bool>) {
LOCAL_FRAME_OVERRIDE.store(
match v {
None => -1,
Some(false) => 0,
Some(true) => 1,
},
std::sync::atomic::Ordering::Relaxed,
);
}
pub(crate) fn local_frame_enabled() -> bool {
match LOCAL_FRAME_OVERRIDE.load(std::sync::atomic::Ordering::Relaxed) {
0 => return false,
1 => return true,
_ => {}
}
static ENABLED: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
*ENABLED.get_or_init(|| {
if cfg!(target_arch = "wasm32") {
true
} else {
std::env::var("IFC_LITE_LOCAL_FRAME").is_ok()
}
})
}
#[inline]
pub(crate) fn instancing_enabled() -> bool {
true
}
#[inline]
fn mat4_to_col_array(m: &Matrix4<f64>) -> [f64; 16] {
*m.as_slice()
.first_chunk::<16>()
.expect("Matrix4<f64> as_slice is exactly 16 elements")
}
pub(crate) fn mat4_to_row_major(m: &Matrix4<f64>) -> [f64; 16] {
[
m[(0, 0)], m[(0, 1)], m[(0, 2)], m[(0, 3)],
m[(1, 0)], m[(1, 1)], m[(1, 2)], m[(1, 3)],
m[(2, 0)], m[(2, 1)], m[(2, 2)], m[(2, 3)],
m[(3, 0)], m[(3, 1)], m[(3, 2)], m[(3, 3)],
]
}
impl GeometryRouter {
pub(super) fn apply_placement(
&self,
element: &DecodedEntity,
decoder: &mut EntityDecoder,
mesh: &mut Mesh,
) -> Result<()> {
let placement_attr = match element.get(5) {
Some(attr) if !attr.is_null() => attr,
_ => {
self.transform_mesh_world(mesh, &Matrix4::identity());
return Ok(());
}
};
let placement = match decoder.resolve_ref(placement_attr)? {
Some(p) => p,
None => {
self.transform_mesh_world(mesh, &Matrix4::identity());
return Ok(());
}
};
let mut transform = self.get_placement_transform(&placement, decoder)?;
self.scale_transform(&mut transform);
if let Some(im) = mesh.instance_meta.as_mut() {
im.transform = mat4_to_row_major(&transform);
}
self.transform_mesh_world(mesh, &transform);
Ok(())
}
pub(super) fn apply_submesh_placement(
&self,
sub_meshes: &mut SubMeshCollection,
element: &DecodedEntity,
decoder: &mut EntityDecoder,
) -> Result<()> {
if let Some(placement_attr) = element.get(5) {
if !placement_attr.is_null() {
if let Some(placement) = decoder.resolve_ref(placement_attr)? {
let mut transform = self.get_placement_transform(&placement, decoder)?;
self.scale_transform(&mut transform);
if instancing_enabled() {
let row_major = mat4_to_row_major(&transform);
for sub in &mut sub_meshes.sub_meshes {
if let Some(im) = sub.mesh.instance_meta.as_mut() {
im.transform = row_major;
}
}
}
for sub in &mut sub_meshes.sub_meshes {
self.transform_mesh_world(&mut sub.mesh, &transform);
}
return Ok(());
}
}
}
for sub in &mut sub_meshes.sub_meshes {
self.transform_mesh_world(&mut sub.mesh, &Matrix4::identity());
}
Ok(())
}
pub(super) fn get_placement_transform_from_element(
&self,
element: &DecodedEntity,
decoder: &mut EntityDecoder,
) -> Result<Matrix4<f64>> {
let placement_attr = match element.get(5) {
Some(attr) if !attr.is_null() => attr,
_ => return Ok(Matrix4::identity()), };
let placement = match decoder.resolve_ref(placement_attr)? {
Some(p) => p,
None => return Ok(Matrix4::identity()),
};
self.get_placement_transform(&placement, decoder)
}
pub(super) fn get_placement_transform(
&self,
placement: &DecodedEntity,
decoder: &mut EntityDecoder,
) -> Result<Matrix4<f64>> {
self.get_placement_transform_with_depth(placement, decoder, 0)
}
const MAX_PLACEMENT_DEPTH: usize = 32;
pub(super) fn get_placement_transform_with_depth(
&self,
placement: &DecodedEntity,
decoder: &mut EntityDecoder,
depth: usize,
) -> Result<Matrix4<f64>> {
if depth > Self::MAX_PLACEMENT_DEPTH {
return Ok(Matrix4::identity());
}
if let Some(m) = decoder.get_placement_transform_cached(placement.id) {
return Ok(Matrix4::from_column_slice(&m));
}
if placement.ifc_type == IfcType::IfcLinearPlacement {
let result = self.resolve_linear_placement_with_depth(placement, decoder, depth)?;
decoder.cache_placement_transform(placement.id, mat4_to_col_array(&result));
return Ok(result);
}
if placement.ifc_type == IfcType::IfcGridPlacement {
let result = self.resolve_grid_placement_with_depth(placement, decoder, depth)?;
decoder.cache_placement_transform(placement.id, mat4_to_col_array(&result));
return Ok(result);
}
if placement.ifc_type != IfcType::IfcLocalPlacement {
return Ok(Matrix4::identity());
}
let parent_transform = if let Some(parent_attr) = placement.get(0) {
if !parent_attr.is_null() {
if let Some(parent) = decoder.resolve_ref(parent_attr)? {
self.get_placement_transform_with_depth(&parent, decoder, depth + 1)?
} else {
Matrix4::identity()
}
} else {
Matrix4::identity()
}
} else {
Matrix4::identity()
};
let local_transform = if let Some(rel_attr) = placement.get(1) {
if !rel_attr.is_null() {
if let Some(rel) = decoder.resolve_ref(rel_attr)? {
if rel.ifc_type == IfcType::IfcAxis2Placement3D {
self.parse_axis2_placement_3d(&rel, decoder)?
} else {
Matrix4::identity()
}
} else {
Matrix4::identity()
}
} else {
Matrix4::identity()
}
} else {
Matrix4::identity()
};
let result = parent_transform * local_transform;
decoder.cache_placement_transform(placement.id, mat4_to_col_array(&result));
Ok(result)
}
}