1use ifc_lite_geometry::Mesh;
10use wasm_bindgen::prelude::*;
11
12#[wasm_bindgen]
14pub struct MeshDataJs {
15 express_id: u32,
16 ifc_type: String, positions: Vec<f32>,
18 normals: Vec<f32>,
19 indices: Vec<u32>,
20 color: [f32; 4], }
22
23#[wasm_bindgen]
24impl MeshDataJs {
25 #[wasm_bindgen(getter, js_name = expressId)]
27 pub fn express_id(&self) -> u32 {
28 self.express_id
29 }
30
31 #[wasm_bindgen(getter, js_name = ifcType)]
33 pub fn ifc_type(&self) -> String {
34 self.ifc_type.clone()
35 }
36
37 #[wasm_bindgen(getter)]
39 pub fn positions(&self) -> js_sys::Float32Array {
40 js_sys::Float32Array::from(&self.positions[..])
41 }
42
43 #[wasm_bindgen(getter)]
45 pub fn normals(&self) -> js_sys::Float32Array {
46 js_sys::Float32Array::from(&self.normals[..])
47 }
48
49 #[wasm_bindgen(getter)]
51 pub fn indices(&self) -> js_sys::Uint32Array {
52 js_sys::Uint32Array::from(&self.indices[..])
53 }
54
55 #[wasm_bindgen(getter)]
57 pub fn color(&self) -> Vec<f32> {
58 self.color.to_vec()
59 }
60
61 #[wasm_bindgen(getter, js_name = vertexCount)]
63 pub fn vertex_count(&self) -> usize {
64 self.positions.len() / 3
65 }
66
67 #[wasm_bindgen(getter, js_name = triangleCount)]
69 pub fn triangle_count(&self) -> usize {
70 self.indices.len() / 3
71 }
72}
73
74impl MeshDataJs {
75 pub fn new(express_id: u32, ifc_type: String, mesh: Mesh, color: [f32; 4]) -> Self {
77 Self {
78 express_id,
79 ifc_type,
80 positions: mesh.positions,
81 normals: mesh.normals,
82 indices: mesh.indices,
83 color,
84 }
85 }
86}
87
88#[wasm_bindgen]
90pub struct MeshCollection {
91 meshes: Vec<MeshDataJs>,
92}
93
94#[wasm_bindgen]
95impl MeshCollection {
96 #[wasm_bindgen(getter)]
98 pub fn length(&self) -> usize {
99 self.meshes.len()
100 }
101
102 #[wasm_bindgen]
104 pub fn get(&self, index: usize) -> Option<MeshDataJs> {
105 self.meshes.get(index).map(|m| MeshDataJs {
106 express_id: m.express_id,
107 ifc_type: m.ifc_type.clone(),
108 positions: m.positions.clone(),
109 normals: m.normals.clone(),
110 indices: m.indices.clone(),
111 color: m.color,
112 })
113 }
114
115 #[wasm_bindgen(getter, js_name = totalVertices)]
117 pub fn total_vertices(&self) -> usize {
118 self.meshes.iter().map(|m| m.positions.len() / 3).sum()
119 }
120
121 #[wasm_bindgen(getter, js_name = totalTriangles)]
123 pub fn total_triangles(&self) -> usize {
124 self.meshes.iter().map(|m| m.indices.len() / 3).sum()
125 }
126}
127
128impl MeshCollection {
129 pub fn new() -> Self {
131 Self { meshes: Vec::new() }
132 }
133
134 pub fn with_capacity(capacity: usize) -> Self {
136 Self {
137 meshes: Vec::with_capacity(capacity),
138 }
139 }
140
141 #[inline]
143 pub fn add(&mut self, mesh: MeshDataJs) {
144 self.meshes.push(mesh);
145 }
146
147 pub fn from_vec(meshes: Vec<MeshDataJs>) -> Self {
149 Self { meshes }
150 }
151
152 pub fn len(&self) -> usize {
154 self.meshes.len()
155 }
156
157 pub fn is_empty(&self) -> bool {
159 self.meshes.is_empty()
160 }
161
162 pub fn apply_rtc_offset(&mut self, x: f64, y: f64, z: f64) {
164 for mesh in &mut self.meshes {
165 for chunk in mesh.positions.chunks_exact_mut(3) {
166 chunk[0] = (chunk[0] as f64 - x) as f32;
167 chunk[1] = (chunk[1] as f64 - y) as f32;
168 chunk[2] = (chunk[2] as f64 - z) as f32;
169 }
170 }
171 }
172}
173
174impl Clone for MeshCollection {
175 fn clone(&self) -> Self {
176 Self {
177 meshes: self
178 .meshes
179 .iter()
180 .map(|m| MeshDataJs {
181 express_id: m.express_id,
182 ifc_type: m.ifc_type.clone(),
183 positions: m.positions.clone(),
184 normals: m.normals.clone(),
185 indices: m.indices.clone(),
186 color: m.color,
187 })
188 .collect(),
189 }
190 }
191}
192
193impl Default for MeshCollection {
194 fn default() -> Self {
195 Self::new()
196 }
197}
198
199#[wasm_bindgen]
201pub struct ZeroCopyMesh {
202 mesh: Mesh,
203}
204
205#[wasm_bindgen]
206impl ZeroCopyMesh {
207 #[wasm_bindgen(constructor)]
209 pub fn new() -> Self {
210 Self { mesh: Mesh::new() }
211 }
212
213 #[wasm_bindgen(getter)]
216 pub fn positions_ptr(&self) -> *const f32 {
217 self.mesh.positions.as_ptr()
218 }
219
220 #[wasm_bindgen(getter)]
222 pub fn positions_len(&self) -> usize {
223 self.mesh.positions.len()
224 }
225
226 #[wasm_bindgen(getter)]
228 pub fn normals_ptr(&self) -> *const f32 {
229 self.mesh.normals.as_ptr()
230 }
231
232 #[wasm_bindgen(getter)]
234 pub fn normals_len(&self) -> usize {
235 self.mesh.normals.len()
236 }
237
238 #[wasm_bindgen(getter)]
240 pub fn indices_ptr(&self) -> *const u32 {
241 self.mesh.indices.as_ptr()
242 }
243
244 #[wasm_bindgen(getter)]
246 pub fn indices_len(&self) -> usize {
247 self.mesh.indices.len()
248 }
249
250 #[wasm_bindgen(getter)]
252 pub fn vertex_count(&self) -> usize {
253 self.mesh.vertex_count()
254 }
255
256 #[wasm_bindgen(getter)]
258 pub fn triangle_count(&self) -> usize {
259 self.mesh.triangle_count()
260 }
261
262 #[wasm_bindgen(getter)]
264 pub fn is_empty(&self) -> bool {
265 self.mesh.is_empty()
266 }
267
268 #[wasm_bindgen]
270 pub fn bounds_min(&self) -> Vec<f32> {
271 let (min, _) = self.mesh.bounds();
272 vec![min.x, min.y, min.z]
273 }
274
275 #[wasm_bindgen]
277 pub fn bounds_max(&self) -> Vec<f32> {
278 let (_, max) = self.mesh.bounds();
279 vec![max.x, max.y, max.z]
280 }
281}
282
283impl From<Mesh> for ZeroCopyMesh {
284 fn from(mesh: Mesh) -> Self {
285 Self { mesh }
286 }
287}
288
289impl Default for ZeroCopyMesh {
290 fn default() -> Self {
291 Self::new()
292 }
293}
294
295#[wasm_bindgen]
297pub struct InstanceData {
298 express_id: u32,
299 transform: Vec<f32>, color: [f32; 4], }
302
303#[wasm_bindgen]
304impl InstanceData {
305 #[wasm_bindgen(getter, js_name = expressId)]
306 pub fn express_id(&self) -> u32 {
307 self.express_id
308 }
309
310 #[wasm_bindgen(getter)]
311 pub fn transform(&self) -> js_sys::Float32Array {
312 js_sys::Float32Array::from(&self.transform[..])
313 }
314
315 #[wasm_bindgen(getter)]
316 pub fn color(&self) -> Vec<f32> {
317 self.color.to_vec()
318 }
319}
320
321impl InstanceData {
322 pub fn new(express_id: u32, transform: Vec<f32>, color: [f32; 4]) -> Self {
323 Self {
324 express_id,
325 transform,
326 color,
327 }
328 }
329}
330
331#[wasm_bindgen]
333pub struct InstancedGeometry {
334 geometry_id: u64,
335 positions: Vec<f32>,
336 normals: Vec<f32>,
337 indices: Vec<u32>,
338 instances: Vec<InstanceData>,
339}
340
341#[wasm_bindgen]
342impl InstancedGeometry {
343 #[wasm_bindgen(getter, js_name = geometryId)]
344 pub fn geometry_id(&self) -> u64 {
345 self.geometry_id
346 }
347
348 #[wasm_bindgen(getter)]
349 pub fn positions(&self) -> js_sys::Float32Array {
350 js_sys::Float32Array::from(&self.positions[..])
351 }
352
353 #[wasm_bindgen(getter)]
354 pub fn normals(&self) -> js_sys::Float32Array {
355 js_sys::Float32Array::from(&self.normals[..])
356 }
357
358 #[wasm_bindgen(getter)]
359 pub fn indices(&self) -> js_sys::Uint32Array {
360 js_sys::Uint32Array::from(&self.indices[..])
361 }
362
363 #[wasm_bindgen(getter)]
364 pub fn instance_count(&self) -> usize {
365 self.instances.len()
366 }
367
368 #[wasm_bindgen]
369 pub fn get_instance(&self, index: usize) -> Option<InstanceData> {
370 self.instances.get(index).map(|inst| InstanceData {
371 express_id: inst.express_id,
372 transform: inst.transform.clone(),
373 color: inst.color,
374 })
375 }
376}
377
378impl InstancedGeometry {
379 pub fn new(
380 geometry_id: u64,
381 positions: Vec<f32>,
382 normals: Vec<f32>,
383 indices: Vec<u32>,
384 ) -> Self {
385 Self {
386 geometry_id,
387 positions,
388 normals,
389 indices,
390 instances: Vec::new(),
391 }
392 }
393
394 pub fn add_instance(&mut self, instance: InstanceData) {
395 self.instances.push(instance);
396 }
397}
398
399#[wasm_bindgen]
401pub struct InstancedMeshCollection {
402 geometries: Vec<InstancedGeometry>,
403}
404
405#[wasm_bindgen]
406impl InstancedMeshCollection {
407 #[wasm_bindgen(getter)]
408 pub fn length(&self) -> usize {
409 self.geometries.len()
410 }
411
412 #[wasm_bindgen]
413 pub fn get(&self, index: usize) -> Option<InstancedGeometry> {
414 self.geometries.get(index).map(|g| InstancedGeometry {
415 geometry_id: g.geometry_id,
416 positions: g.positions.clone(),
417 normals: g.normals.clone(),
418 indices: g.indices.clone(),
419 instances: g
420 .instances
421 .iter()
422 .map(|inst| InstanceData {
423 express_id: inst.express_id,
424 transform: inst.transform.clone(),
425 color: inst.color,
426 })
427 .collect(),
428 })
429 }
430
431 #[wasm_bindgen(getter, js_name = totalGeometries)]
432 pub fn total_geometries(&self) -> usize {
433 self.geometries.len()
434 }
435
436 #[wasm_bindgen(getter, js_name = totalInstances)]
437 pub fn total_instances(&self) -> usize {
438 self.geometries.iter().map(|g| g.instances.len()).sum()
439 }
440}
441
442impl InstancedMeshCollection {
443 pub fn new() -> Self {
444 Self {
445 geometries: Vec::new(),
446 }
447 }
448
449 pub fn add(&mut self, geometry: InstancedGeometry) {
450 self.geometries.push(geometry);
451 }
452}
453
454impl Default for InstancedMeshCollection {
455 fn default() -> Self {
456 Self::new()
457 }
458}
459
460#[wasm_bindgen]
462pub fn get_memory() -> JsValue {
463 wasm_bindgen::memory()
464}
465
466#[cfg(test)]
467mod tests {
468 use super::*;
469
470 #[test]
471 fn test_zero_copy_mesh_creation() {
472 let mesh = ZeroCopyMesh::new();
473 assert!(mesh.is_empty());
474 assert_eq!(mesh.vertex_count(), 0);
475 assert_eq!(mesh.triangle_count(), 0);
476 }
477
478 #[test]
479 fn test_zero_copy_mesh_pointers() {
480 let mesh = ZeroCopyMesh::new();
481
482 assert!(!mesh.positions_ptr().is_null());
484 assert!(!mesh.normals_ptr().is_null());
485 assert!(!mesh.indices_ptr().is_null());
486 }
487}