1use wasm_bindgen::prelude::*;
37
38#[wasm_bindgen]
40#[derive(Debug, Clone)]
41pub struct GpuMeshMetadata {
42 express_id: u32,
44 ifc_type_idx: u16,
46 vertex_offset: u32,
48 vertex_count: u32,
50 index_offset: u32,
52 index_count: u32,
54 color: [f32; 4],
56}
57
58#[wasm_bindgen]
59impl GpuMeshMetadata {
60 #[wasm_bindgen(getter, js_name = expressId)]
61 pub fn express_id(&self) -> u32 {
62 self.express_id
63 }
64
65 #[wasm_bindgen(getter, js_name = ifcTypeIdx)]
66 pub fn ifc_type_idx(&self) -> u16 {
67 self.ifc_type_idx
68 }
69
70 #[wasm_bindgen(getter, js_name = vertexOffset)]
71 pub fn vertex_offset(&self) -> u32 {
72 self.vertex_offset
73 }
74
75 #[wasm_bindgen(getter, js_name = vertexCount)]
76 pub fn vertex_count(&self) -> u32 {
77 self.vertex_count
78 }
79
80 #[wasm_bindgen(getter, js_name = indexOffset)]
81 pub fn index_offset(&self) -> u32 {
82 self.index_offset
83 }
84
85 #[wasm_bindgen(getter, js_name = indexCount)]
86 pub fn index_count(&self) -> u32 {
87 self.index_count
88 }
89
90 #[wasm_bindgen(getter)]
91 pub fn color(&self) -> Vec<f32> {
92 self.color.to_vec()
93 }
94}
95
96#[wasm_bindgen]
105pub struct GpuGeometry {
106 vertex_data: Vec<f32>,
109
110 indices: Vec<u32>,
112
113 mesh_metadata: Vec<GpuMeshMetadata>,
115
116 ifc_type_names: Vec<String>,
118
119 rtc_offset_x: f64,
122 rtc_offset_y: f64,
123 rtc_offset_z: f64,
124}
125
126#[wasm_bindgen]
127impl GpuGeometry {
128 #[wasm_bindgen(constructor)]
130 pub fn new() -> Self {
131 Self {
132 vertex_data: Vec::new(),
133 indices: Vec::new(),
134 mesh_metadata: Vec::new(),
135 ifc_type_names: Vec::new(),
136 rtc_offset_x: 0.0,
137 rtc_offset_y: 0.0,
138 rtc_offset_z: 0.0,
139 }
140 }
141
142 pub fn set_rtc_offset(&mut self, x: f64, y: f64, z: f64) {
144 self.rtc_offset_x = x;
145 self.rtc_offset_y = y;
146 self.rtc_offset_z = z;
147 }
148
149 #[wasm_bindgen(getter, js_name = rtcOffsetX)]
151 pub fn rtc_offset_x(&self) -> f64 {
152 self.rtc_offset_x
153 }
154
155 #[wasm_bindgen(getter, js_name = rtcOffsetY)]
157 pub fn rtc_offset_y(&self) -> f64 {
158 self.rtc_offset_y
159 }
160
161 #[wasm_bindgen(getter, js_name = rtcOffsetZ)]
163 pub fn rtc_offset_z(&self) -> f64 {
164 self.rtc_offset_z
165 }
166
167 #[wasm_bindgen(getter, js_name = hasRtcOffset)]
169 pub fn has_rtc_offset(&self) -> bool {
170 self.rtc_offset_x != 0.0 || self.rtc_offset_y != 0.0 || self.rtc_offset_z != 0.0
171 }
172
173 #[wasm_bindgen(getter, js_name = vertexDataPtr)]
178 pub fn vertex_data_ptr(&self) -> *const f32 {
179 self.vertex_data.as_ptr()
180 }
181
182 #[wasm_bindgen(getter, js_name = vertexDataLen)]
184 pub fn vertex_data_len(&self) -> usize {
185 self.vertex_data.len()
186 }
187
188 #[wasm_bindgen(getter, js_name = vertexDataByteLength)]
190 pub fn vertex_data_byte_length(&self) -> usize {
191 self.vertex_data.len() * 4 }
193
194 #[wasm_bindgen(getter, js_name = indicesPtr)]
196 pub fn indices_ptr(&self) -> *const u32 {
197 self.indices.as_ptr()
198 }
199
200 #[wasm_bindgen(getter, js_name = indicesLen)]
202 pub fn indices_len(&self) -> usize {
203 self.indices.len()
204 }
205
206 #[wasm_bindgen(getter, js_name = indicesByteLength)]
208 pub fn indices_byte_length(&self) -> usize {
209 self.indices.len() * 4 }
211
212 #[wasm_bindgen(getter, js_name = meshCount)]
214 pub fn mesh_count(&self) -> usize {
215 self.mesh_metadata.len()
216 }
217
218 #[wasm_bindgen(getter, js_name = totalVertexCount)]
220 pub fn total_vertex_count(&self) -> usize {
221 self.vertex_data.len() / 6 }
223
224 #[wasm_bindgen(getter, js_name = totalTriangleCount)]
226 pub fn total_triangle_count(&self) -> usize {
227 self.indices.len() / 3
228 }
229
230 #[wasm_bindgen(js_name = getMeshMetadata)]
232 pub fn get_mesh_metadata(&self, index: usize) -> Option<GpuMeshMetadata> {
233 self.mesh_metadata.get(index).cloned()
234 }
235
236 #[wasm_bindgen(js_name = getIfcTypeName)]
238 pub fn get_ifc_type_name(&self, index: u16) -> Option<String> {
239 self.ifc_type_names.get(index as usize).cloned()
240 }
241
242 #[wasm_bindgen(getter, js_name = isEmpty)]
244 pub fn is_empty(&self) -> bool {
245 self.vertex_data.is_empty()
246 }
247}
248
249impl GpuGeometry {
250 pub fn with_capacity(vertex_capacity: usize, index_capacity: usize) -> Self {
252 Self {
253 vertex_data: Vec::with_capacity(vertex_capacity),
254 indices: Vec::with_capacity(index_capacity),
255 mesh_metadata: Vec::with_capacity(256),
256 ifc_type_names: Vec::with_capacity(64),
257 rtc_offset_x: 0.0,
258 rtc_offset_y: 0.0,
259 rtc_offset_z: 0.0,
260 }
261 }
262
263 pub fn add_mesh(
265 &mut self,
266 express_id: u32,
267 ifc_type: &str,
268 positions: &[f32],
269 normals: &[f32],
270 indices: &[u32],
271 color: [f32; 4],
272 ) {
273 let vertex_count = positions.len() / 3;
274 if vertex_count == 0 {
275 return;
276 }
277
278 let ifc_type_idx = self.get_or_add_ifc_type(ifc_type);
280
281 let vertex_offset = (self.vertex_data.len() / 6) as u32;
283 let index_offset = self.indices.len() as u32;
284
285 self.vertex_data.reserve(vertex_count * 6);
288
289 for i in 0..vertex_count {
290 let pi = i * 3;
291
292 let px = positions[pi];
294 let py = positions[pi + 2]; let pz = -positions[pi + 1]; let nx = normals[pi];
299 let ny = normals[pi + 2]; let nz = -normals[pi + 1]; self.vertex_data.push(px);
303 self.vertex_data.push(py);
304 self.vertex_data.push(pz);
305 self.vertex_data.push(nx);
306 self.vertex_data.push(ny);
307 self.vertex_data.push(nz);
308 }
309
310 self.indices.reserve(indices.len());
312 for &idx in indices {
313 self.indices.push(idx + vertex_offset);
314 }
315
316 self.mesh_metadata.push(GpuMeshMetadata {
318 express_id,
319 ifc_type_idx,
320 vertex_offset,
321 vertex_count: vertex_count as u32,
322 index_offset,
323 index_count: indices.len() as u32,
324 color,
325 });
326 }
327
328 fn get_or_add_ifc_type(&mut self, ifc_type: &str) -> u16 {
330 for (i, name) in self.ifc_type_names.iter().enumerate() {
332 if name == ifc_type {
333 return i as u16;
334 }
335 }
336
337 let idx = self.ifc_type_names.len() as u16;
339 self.ifc_type_names.push(ifc_type.to_string());
340 idx
341 }
342
343 pub fn clear(&mut self) {
345 self.vertex_data.clear();
346 self.indices.clear();
347 self.mesh_metadata.clear();
348 }
350}
351
352impl Default for GpuGeometry {
353 fn default() -> Self {
354 Self::new()
355 }
356}
357
358#[wasm_bindgen]
365pub struct GpuInstancedGeometry {
366 geometry_id: u64,
368
369 vertex_data: Vec<f32>,
371
372 indices: Vec<u32>,
374
375 instance_data: Vec<f32>,
377
378 instance_express_ids: Vec<u32>,
380}
381
382#[wasm_bindgen]
383impl GpuInstancedGeometry {
384 #[wasm_bindgen(constructor)]
386 pub fn new(geometry_id: u64) -> Self {
387 Self {
388 geometry_id,
389 vertex_data: Vec::new(),
390 indices: Vec::new(),
391 instance_data: Vec::new(),
392 instance_express_ids: Vec::new(),
393 }
394 }
395
396 #[wasm_bindgen(getter, js_name = geometryId)]
397 pub fn geometry_id(&self) -> u64 {
398 self.geometry_id
399 }
400
401 #[wasm_bindgen(getter, js_name = vertexDataPtr)]
403 pub fn vertex_data_ptr(&self) -> *const f32 {
404 self.vertex_data.as_ptr()
405 }
406
407 #[wasm_bindgen(getter, js_name = vertexDataLen)]
408 pub fn vertex_data_len(&self) -> usize {
409 self.vertex_data.len()
410 }
411
412 #[wasm_bindgen(getter, js_name = vertexDataByteLength)]
413 pub fn vertex_data_byte_length(&self) -> usize {
414 self.vertex_data.len() * 4
415 }
416
417 #[wasm_bindgen(getter, js_name = indicesPtr)]
419 pub fn indices_ptr(&self) -> *const u32 {
420 self.indices.as_ptr()
421 }
422
423 #[wasm_bindgen(getter, js_name = indicesLen)]
424 pub fn indices_len(&self) -> usize {
425 self.indices.len()
426 }
427
428 #[wasm_bindgen(getter, js_name = indicesByteLength)]
429 pub fn indices_byte_length(&self) -> usize {
430 self.indices.len() * 4
431 }
432
433 #[wasm_bindgen(getter, js_name = instanceDataPtr)]
435 pub fn instance_data_ptr(&self) -> *const f32 {
436 self.instance_data.as_ptr()
437 }
438
439 #[wasm_bindgen(getter, js_name = instanceDataLen)]
440 pub fn instance_data_len(&self) -> usize {
441 self.instance_data.len()
442 }
443
444 #[wasm_bindgen(getter, js_name = instanceDataByteLength)]
445 pub fn instance_data_byte_length(&self) -> usize {
446 self.instance_data.len() * 4
447 }
448
449 #[wasm_bindgen(getter, js_name = instanceExpressIdsPtr)]
451 pub fn instance_express_ids_ptr(&self) -> *const u32 {
452 self.instance_express_ids.as_ptr()
453 }
454
455 #[wasm_bindgen(getter, js_name = instanceCount)]
456 pub fn instance_count(&self) -> usize {
457 self.instance_express_ids.len()
458 }
459
460 #[wasm_bindgen(getter, js_name = vertexCount)]
461 pub fn vertex_count(&self) -> usize {
462 self.vertex_data.len() / 6
463 }
464
465 #[wasm_bindgen(getter, js_name = triangleCount)]
466 pub fn triangle_count(&self) -> usize {
467 self.indices.len() / 3
468 }
469}
470
471impl GpuInstancedGeometry {
472 pub fn set_geometry(&mut self, positions: &[f32], normals: &[f32], indices: &[u32]) {
474 let vertex_count = positions.len() / 3;
475
476 self.vertex_data.clear();
478 self.vertex_data.reserve(vertex_count * 6);
479 self.indices.clear();
480 self.indices.reserve(indices.len());
481
482 for i in 0..vertex_count {
484 let pi = i * 3;
485
486 self.vertex_data.push(positions[pi]);
488 self.vertex_data.push(positions[pi + 2]); self.vertex_data.push(-positions[pi + 1]); self.vertex_data.push(normals[pi]);
493 self.vertex_data.push(normals[pi + 2]); self.vertex_data.push(-normals[pi + 1]); }
496
497 self.indices.extend_from_slice(indices);
499 }
500
501 pub fn add_instance(&mut self, express_id: u32, transform: &[f32; 16], color: [f32; 4]) {
503 self.instance_data.extend_from_slice(transform);
505
506 self.instance_data.extend_from_slice(&color);
508
509 self.instance_express_ids.push(express_id);
511 }
512}
513
514#[wasm_bindgen]
516pub struct GpuInstancedGeometryCollection {
517 geometries: Vec<GpuInstancedGeometry>,
518}
519
520#[wasm_bindgen]
521impl GpuInstancedGeometryCollection {
522 #[wasm_bindgen(constructor)]
523 pub fn new() -> Self {
524 Self {
525 geometries: Vec::new(),
526 }
527 }
528
529 #[wasm_bindgen(getter)]
530 pub fn length(&self) -> usize {
531 self.geometries.len()
532 }
533
534 #[wasm_bindgen]
535 pub fn get(&self, index: usize) -> Option<GpuInstancedGeometry> {
536 self.geometries.get(index).map(|g| GpuInstancedGeometry {
537 geometry_id: g.geometry_id,
538 vertex_data: g.vertex_data.clone(),
539 indices: g.indices.clone(),
540 instance_data: g.instance_data.clone(),
541 instance_express_ids: g.instance_express_ids.clone(),
542 })
543 }
544
545 #[wasm_bindgen(js_name = getRef)]
548 pub fn get_ref(&self, index: usize) -> Option<GpuInstancedGeometryRef> {
549 if index < self.geometries.len() {
550 Some(GpuInstancedGeometryRef {
551 collection_ptr: self as *const GpuInstancedGeometryCollection,
552 index,
553 })
554 } else {
555 None
556 }
557 }
558}
559
560impl GpuInstancedGeometryCollection {
561 pub fn add(&mut self, geometry: GpuInstancedGeometry) {
562 self.geometries.push(geometry);
563 }
564
565 pub fn get_mut(&mut self, index: usize) -> Option<&mut GpuInstancedGeometry> {
566 self.geometries.get_mut(index)
567 }
568}
569
570impl Default for GpuInstancedGeometryCollection {
571 fn default() -> Self {
572 Self::new()
573 }
574}
575
576#[wasm_bindgen]
579pub struct GpuInstancedGeometryRef {
580 collection_ptr: *const GpuInstancedGeometryCollection,
581 index: usize,
582}
583
584#[wasm_bindgen]
585impl GpuInstancedGeometryRef {
586 fn get_geometry(&self) -> Option<&GpuInstancedGeometry> {
587 unsafe {
588 let collection = &*self.collection_ptr;
589 collection.geometries.get(self.index)
590 }
591 }
592
593 #[wasm_bindgen(getter, js_name = geometryId)]
594 pub fn geometry_id(&self) -> u64 {
595 self.get_geometry().map(|g| g.geometry_id).unwrap_or(0)
596 }
597
598 #[wasm_bindgen(getter, js_name = vertexDataPtr)]
599 pub fn vertex_data_ptr(&self) -> *const f32 {
600 self.get_geometry()
601 .map(|g| g.vertex_data.as_ptr())
602 .unwrap_or(std::ptr::null())
603 }
604
605 #[wasm_bindgen(getter, js_name = vertexDataLen)]
606 pub fn vertex_data_len(&self) -> usize {
607 self.get_geometry().map(|g| g.vertex_data.len()).unwrap_or(0)
608 }
609
610 #[wasm_bindgen(getter, js_name = vertexDataByteLength)]
611 pub fn vertex_data_byte_length(&self) -> usize {
612 self.vertex_data_len() * 4
613 }
614
615 #[wasm_bindgen(getter, js_name = indicesPtr)]
616 pub fn indices_ptr(&self) -> *const u32 {
617 self.get_geometry()
618 .map(|g| g.indices.as_ptr())
619 .unwrap_or(std::ptr::null())
620 }
621
622 #[wasm_bindgen(getter, js_name = indicesLen)]
623 pub fn indices_len(&self) -> usize {
624 self.get_geometry().map(|g| g.indices.len()).unwrap_or(0)
625 }
626
627 #[wasm_bindgen(getter, js_name = indicesByteLength)]
628 pub fn indices_byte_length(&self) -> usize {
629 self.indices_len() * 4
630 }
631
632 #[wasm_bindgen(getter, js_name = instanceDataPtr)]
633 pub fn instance_data_ptr(&self) -> *const f32 {
634 self.get_geometry()
635 .map(|g| g.instance_data.as_ptr())
636 .unwrap_or(std::ptr::null())
637 }
638
639 #[wasm_bindgen(getter, js_name = instanceDataLen)]
640 pub fn instance_data_len(&self) -> usize {
641 self.get_geometry()
642 .map(|g| g.instance_data.len())
643 .unwrap_or(0)
644 }
645
646 #[wasm_bindgen(getter, js_name = instanceDataByteLength)]
647 pub fn instance_data_byte_length(&self) -> usize {
648 self.instance_data_len() * 4
649 }
650
651 #[wasm_bindgen(getter, js_name = instanceExpressIdsPtr)]
652 pub fn instance_express_ids_ptr(&self) -> *const u32 {
653 self.get_geometry()
654 .map(|g| g.instance_express_ids.as_ptr())
655 .unwrap_or(std::ptr::null())
656 }
657
658 #[wasm_bindgen(getter, js_name = instanceCount)]
659 pub fn instance_count(&self) -> usize {
660 self.get_geometry()
661 .map(|g| g.instance_express_ids.len())
662 .unwrap_or(0)
663 }
664}
665
666#[cfg(test)]
667mod tests {
668 use super::*;
669
670 #[test]
671 fn test_gpu_geometry_creation() {
672 let geom = GpuGeometry::new();
673 assert!(geom.is_empty());
674 assert_eq!(geom.mesh_count(), 0);
675 }
676
677 #[test]
678 fn test_gpu_geometry_add_mesh() {
679 let mut geom = GpuGeometry::new();
680
681 let positions = vec![0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.5, 0.0, 1.0];
683 let normals = vec![0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0];
684 let indices = vec![0, 1, 2];
685 let color = [1.0, 0.0, 0.0, 1.0];
686
687 geom.add_mesh(123, "IfcWall", &positions, &normals, &indices, color);
688
689 assert!(!geom.is_empty());
690 assert_eq!(geom.mesh_count(), 1);
691 assert_eq!(geom.total_vertex_count(), 3);
692 assert_eq!(geom.total_triangle_count(), 1);
693
694 let meta = geom.get_mesh_metadata(0).unwrap();
696 assert_eq!(meta.express_id, 123);
697 assert_eq!(meta.vertex_count, 3);
698 assert_eq!(meta.index_count, 3);
699 }
700
701 #[test]
702 fn test_coordinate_conversion() {
703 let mut geom = GpuGeometry::new();
704
705 let positions = vec![1.0, 2.0, 3.0];
707 let normals = vec![0.0, 0.0, 1.0]; let indices = vec![0];
709 let color = [1.0, 1.0, 1.0, 1.0];
710
711 geom.add_mesh(1, "Test", &positions, &normals, &indices, color);
712
713 assert_eq!(geom.vertex_data[0], 1.0); assert_eq!(geom.vertex_data[1], 3.0); assert_eq!(geom.vertex_data[2], -2.0); assert_eq!(geom.vertex_data[3], 0.0); assert_eq!(geom.vertex_data[4], 1.0); assert_eq!(geom.vertex_data[5], 0.0); }
722
723 #[test]
724 fn test_instanced_geometry() {
725 let mut geom = GpuInstancedGeometry::new(12345);
726
727 let positions = vec![0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.5, 0.0, 1.0];
728 let normals = vec![0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0];
729 let indices = vec![0, 1, 2];
730
731 geom.set_geometry(&positions, &normals, &indices);
732
733 let transform = [
735 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
736 ];
737 let color = [1.0, 0.0, 0.0, 1.0];
738
739 geom.add_instance(100, &transform, color);
740 geom.add_instance(101, &transform, color);
741
742 assert_eq!(geom.instance_count(), 2);
743 assert_eq!(geom.vertex_count(), 3);
744 assert_eq!(geom.triangle_count(), 1);
745 }
746}