use ifc_lite_geometry::InstanceMeta;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MeshTextureData {
#[serde(default)]
pub texture_id: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rgba: Option<std::sync::Arc<Vec<u8>>>,
#[serde(default)]
pub width: u32,
#[serde(default)]
pub height: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
pub repeat_s: bool,
pub repeat_t: bool,
}
impl MeshTextureData {
pub fn from_attachment(att: &ifc_lite_geometry::TextureAttachment) -> Self {
match &att.source {
ifc_lite_geometry::TextureSource::Decoded(tex) => Self {
texture_id: att.texture_id,
rgba: Some(tex.rgba.clone()),
width: tex.width,
height: tex.height,
url: None,
repeat_s: tex.repeat_s,
repeat_t: tex.repeat_t,
},
ifc_lite_geometry::TextureSource::Image(img) => Self {
texture_id: att.texture_id,
rgba: None,
width: 0,
height: 0,
url: Some(img.url.clone()),
repeat_s: img.repeat_s,
repeat_t: img.repeat_t,
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MeshData {
pub express_id: u32,
pub ifc_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub global_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub presentation_layer: Option<String>,
pub positions: Vec<f32>,
pub normals: Vec<f32>,
pub indices: Vec<u32>,
pub color: [f32; 4],
#[serde(skip_serializing_if = "Option::is_none")]
pub material_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub geometry_item_id: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub properties: Option<BTreeMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub uvs: Option<Vec<f32>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub texture: Option<MeshTextureData>,
#[serde(default, skip_serializing_if = "geometry_class_is_occurrence")]
pub geometry_class: u8,
#[serde(default, skip_serializing_if = "origin_is_zero")]
pub origin: [f64; 3],
#[serde(skip)]
pub instance: Option<InstanceMeta>,
#[serde(skip)]
pub local_bounds: Option<[f32; 6]>,
#[serde(skip)]
pub local_to_world: Option<[f64; 16]>,
}
fn geometry_class_is_occurrence(class: &u8) -> bool {
*class == 0
}
fn origin_is_zero(origin: &[f64; 3]) -> bool {
origin[0] == 0.0 && origin[1] == 0.0 && origin[2] == 0.0
}
impl MeshData {
pub fn new(
express_id: u32,
ifc_type: String,
positions: Vec<f32>,
normals: Vec<f32>,
indices: Vec<u32>,
color: [f32; 4],
) -> Self {
Self {
express_id,
ifc_type,
global_id: None,
name: None,
presentation_layer: None,
positions,
normals,
indices,
color,
material_name: None,
geometry_item_id: None,
properties: None,
uvs: None,
texture: None,
geometry_class: 0,
origin: [0.0; 3],
instance: None,
local_bounds: None,
local_to_world: None,
}
}
pub fn with_instance(mut self, instance: Option<InstanceMeta>) -> Self {
self.instance = instance;
self
}
pub fn with_local_bounds(mut self, local_bounds: Option<[f32; 6]>) -> Self {
self.local_bounds = local_bounds;
self
}
pub fn with_local_to_world(mut self, local_to_world: Option<[f64; 16]>) -> Self {
self.local_to_world = local_to_world;
self
}
pub fn with_geometry_class(mut self, geometry_class: u8) -> Self {
self.geometry_class = geometry_class;
self
}
pub fn with_origin(mut self, origin: [f64; 3]) -> Self {
self.origin = origin;
self
}
pub fn with_element_metadata(
mut self,
global_id: Option<String>,
name: Option<String>,
presentation_layer: Option<String>,
) -> Self {
self.global_id = global_id;
self.name = name;
self.presentation_layer = presentation_layer;
self
}
pub fn with_style_metadata(
mut self,
material_name: Option<String>,
geometry_item_id: Option<u32>,
) -> Self {
self.material_name = material_name;
self.geometry_item_id = geometry_item_id;
self
}
pub fn with_properties(mut self, properties: Option<BTreeMap<String, String>>) -> Self {
self.properties = properties;
self
}
pub fn vertex_count(&self) -> usize {
self.positions.len() / 3
}
pub fn triangle_count(&self) -> usize {
self.indices.len() / 3
}
pub fn is_empty(&self) -> bool {
self.positions.is_empty() || self.indices.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct RawInstanceOccurrence {
pub express_id: u32,
pub ifc_type: String,
pub global_id: Option<String>,
pub name: Option<String>,
pub presentation_layer: Option<String>,
pub color: [f32; 4],
pub rep_identity: u128,
pub world_transform: [f64; 16],
}
#[derive(Debug, Clone)]
pub struct InstanceRecord {
pub express_id: u32,
pub ifc_type: String,
pub global_id: Option<String>,
pub name: Option<String>,
pub presentation_layer: Option<String>,
pub color: [f32; 4],
pub template_express_id: u32,
pub rep_identity: u128,
pub transform: [f32; 16],
}