1use std::sync::Arc;
4
5use crate::json::Value;
6use draco_core::Mesh;
7#[cfg(feature = "draco-decode")]
8use draco_core::{DecoderBuffer, MeshDecoder};
9
10use crate::{Document, Error, PrimitiveRef, Result};
11
12pub const KHR_DRACO_MESH_COMPRESSION: &str = "KHR_draco_mesh_compression";
14
15pub const EXT_MESHOPT_COMPRESSION: &str = "EXT_meshopt_compression";
20
21pub const KHR_MESHOPT_COMPRESSION: &str = "KHR_meshopt_compression";
28
29pub fn meshopt_extension(extensions: Option<&Value>) -> Option<(&'static str, &Value)> {
31 let extensions = extensions?;
32 for name in [EXT_MESHOPT_COMPRESSION, KHR_MESHOPT_COMPRESSION] {
33 if let Some(value) = extensions.get(name) {
34 return Some((name, value));
35 }
36 }
37 None
38}
39
40pub fn meshopt_extension_mut(extensions: Option<&mut Value>) -> Option<(&'static str, &mut Value)> {
42 let extensions = extensions?;
43 let name = if extensions.get(EXT_MESHOPT_COMPRESSION).is_some() {
44 EXT_MESHOPT_COMPRESSION
45 } else if extensions.get(KHR_MESHOPT_COMPRESSION).is_some() {
46 KHR_MESHOPT_COMPRESSION
47 } else {
48 return None;
49 };
50 extensions.get_mut(name).map(|value| (name, value))
51}
52
53pub const BINARY_FREE_EXTENSIONS: &[&str] = &[
66 "KHR_materials_unlit",
68 "KHR_materials_emissive_strength",
69 "KHR_materials_ior",
70 "KHR_materials_specular",
71 "KHR_materials_anisotropy",
72 "KHR_materials_transmission",
73 "KHR_materials_dispersion",
74 "KHR_materials_volume",
75 "KHR_materials_iridescence",
76 "KHR_materials_sheen",
77 "KHR_materials_clearcoat",
78 "KHR_materials_pbrSpecularGlossiness",
80 "KHR_texture_transform",
82 "EXT_texture_webp",
84 "EXT_texture_avif",
85 "KHR_texture_basisu",
86 "KHR_lights_punctual",
89 "KHR_materials_variants",
90 "KHR_mesh_quantization",
93 "CESIUM_RTC",
95 "EXT_mesh_features",
105];
106
107pub const EXT_MESH_GPU_INSTANCING: &str = "EXT_mesh_gpu_instancing";
109
110pub const EXT_STRUCTURAL_METADATA: &str = "EXT_structural_metadata";
112
113const PROPERTY_TABLE_SLOTS: [&str; 3] = ["values", "arrayOffsets", "stringOffsets"];
115
116fn instancing_accessors(root: &Value) -> impl Iterator<Item = &Value> {
128 root.get("nodes")
129 .and_then(Value::as_array)
130 .unwrap_or(&[])
131 .iter()
132 .filter_map(|node| {
133 node.get("extensions")?
134 .get(EXT_MESH_GPU_INSTANCING)?
135 .get("attributes")?
136 .as_object()
137 })
138 .flatten()
139 .map(|(_, value)| value)
140}
141
142fn instancing_accessors_mut(root: &mut Value) -> impl Iterator<Item = &mut Value> {
144 root.get_mut("nodes")
145 .and_then(Value::as_array_mut)
146 .map(|nodes| nodes.iter_mut())
147 .into_iter()
148 .flatten()
149 .filter_map(|node| {
150 node.get_mut("extensions")?
151 .get_mut(EXT_MESH_GPU_INSTANCING)?
152 .get_mut("attributes")?
153 .as_object_mut()
154 })
155 .flatten()
156 .map(|(_, value)| value)
157}
158
159fn property_table_views(root: &Value) -> impl Iterator<Item = &Value> {
168 root.get("extensions")
169 .and_then(|extensions| extensions.get(EXT_STRUCTURAL_METADATA))
170 .and_then(|metadata| metadata.get("propertyTables"))
171 .and_then(Value::as_array)
172 .unwrap_or(&[])
173 .iter()
174 .filter_map(|table| table.get("properties")?.as_object())
175 .flatten()
176 .flat_map(|(_, property)| {
177 PROPERTY_TABLE_SLOTS
178 .iter()
179 .filter_map(|slot| property.get(slot))
180 })
181}
182
183fn property_table_views_mut(root: &mut Value) -> impl Iterator<Item = &mut Value> {
185 root.get_mut("extensions")
186 .and_then(|extensions| extensions.get_mut(EXT_STRUCTURAL_METADATA))
187 .and_then(|metadata| metadata.get_mut("propertyTables"))
188 .and_then(Value::as_array_mut)
189 .map(|tables| tables.iter_mut())
190 .into_iter()
191 .flatten()
192 .filter_map(|table| table.get_mut("properties")?.as_object_mut())
193 .flatten()
194 .flat_map(|(_, property)| {
195 property
196 .as_object_mut()
197 .map(|entries| {
198 entries
199 .iter_mut()
200 .filter(|(key, _)| PROPERTY_TABLE_SLOTS.contains(&key.as_str()))
201 .map(|(_, value)| value)
202 })
203 .into_iter()
204 .flatten()
205 })
206}
207
208fn keep_reference(value: &Value, used: &mut [bool], kind: &str) -> Result<()> {
210 let index = value
211 .as_u64()
212 .and_then(|value| usize::try_from(value).ok())
213 .filter(|index| *index < used.len())
214 .ok_or_else(|| Error::Extension(format!("{kind} is invalid")))?;
215 used[index] = true;
216 Ok(())
217}
218
219#[derive(Clone, Copy, Debug, Default)]
225pub struct MeshGpuInstancingExtension;
226impl ExtensionHandler for MeshGpuInstancingExtension {
227 fn name(&self) -> &'static str {
228 EXT_MESH_GPU_INSTANCING
229 }
230 fn allows_binary_transform(&self) -> bool {
231 true
232 }
233 fn collect_binary_references(
234 &self,
235 document: &Document,
236 accessors: &mut [bool],
237 _buffer_views: &mut [bool],
238 ) -> Result<()> {
239 for value in instancing_accessors(document.as_value()) {
240 keep_reference(value, accessors, "EXT_mesh_gpu_instancing accessor")?;
241 }
242 Ok(())
243 }
244 fn remap_binary_references(
245 &self,
246 document: &mut Document,
247 accessors: &[Option<usize>],
248 _buffer_views: &[Option<usize>],
249 ) -> Result<()> {
250 for value in instancing_accessors_mut(document.as_value_mut()) {
251 remap_reference(value, accessors, "EXT_mesh_gpu_instancing accessor")?;
252 }
253 Ok(())
254 }
255}
256
257#[derive(Clone, Copy, Debug, Default)]
259pub struct StructuralMetadataExtension;
260impl ExtensionHandler for StructuralMetadataExtension {
261 fn name(&self) -> &'static str {
262 EXT_STRUCTURAL_METADATA
263 }
264 fn allows_binary_transform(&self) -> bool {
265 true
266 }
267 fn collect_binary_references(
268 &self,
269 document: &Document,
270 _accessors: &mut [bool],
271 buffer_views: &mut [bool],
272 ) -> Result<()> {
273 for value in property_table_views(document.as_value()) {
274 keep_reference(value, buffer_views, "EXT_structural_metadata bufferView")?;
275 }
276 Ok(())
277 }
278 fn remap_binary_references(
279 &self,
280 document: &mut Document,
281 _accessors: &[Option<usize>],
282 buffer_views: &[Option<usize>],
283 ) -> Result<()> {
284 for value in property_table_views_mut(document.as_value_mut()) {
285 remap_reference(value, buffer_views, "EXT_structural_metadata bufferView")?;
286 }
287 Ok(())
288 }
289}
290
291#[derive(Clone, Copy, Debug)]
299pub struct BinaryFreeExtension(pub &'static str);
300impl ExtensionHandler for BinaryFreeExtension {
301 fn name(&self) -> &'static str {
302 self.0
303 }
304 fn allows_binary_transform(&self) -> bool {
305 true
306 }
307}
308
309#[derive(Clone, Debug, Default)]
311pub struct ResourceStore {
312 pub buffers: Vec<Vec<u8>>,
314}
315
316#[derive(Default)]
318pub struct ExtensionValidationContext {
319 accessors_without_buffer_view: Vec<usize>,
320}
321
322impl ExtensionValidationContext {
323 pub fn allow_accessor_without_buffer_view(&mut self, index: usize) {
325 if !self.accessors_without_buffer_view.contains(&index) {
326 self.accessors_without_buffer_view.push(index);
327 }
328 }
329 pub fn allows_accessor_without_buffer_view(&self, index: usize) -> bool {
331 self.accessors_without_buffer_view.contains(&index)
332 }
333}
334
335pub trait ExtensionHandler: Send + Sync {
337 fn name(&self) -> &'static str;
339 fn validate(
342 &self,
343 _document: &Document,
344 _context: &mut ExtensionValidationContext,
345 ) -> Result<()> {
346 Ok(())
347 }
348 fn allows_binary_transform(&self) -> bool {
352 false
353 }
354 fn collect_binary_references(
360 &self,
361 _document: &Document,
362 _accessors: &mut [bool],
363 _buffer_views: &mut [bool],
364 ) -> Result<()> {
365 Ok(())
366 }
367 fn remap_binary_references(
371 &self,
372 _document: &mut Document,
373 _accessors: &[Option<usize>],
374 _buffer_views: &[Option<usize>],
375 ) -> Result<()> {
376 Ok(())
377 }
378 fn decode_primitive(
381 &self,
382 _document: &Document,
383 _resources: &ResourceStore,
384 _primitive: PrimitiveRef<'_>,
385 ) -> Option<Result<Mesh>> {
386 None
387 }
388}
389
390#[derive(Clone)]
391pub struct ExtensionRegistry {
393 handlers: Vec<Arc<dyn ExtensionHandler>>,
394}
395impl ExtensionRegistry {
396 pub fn new() -> Self {
398 Self::default()
399 }
400 pub fn register<H: ExtensionHandler + 'static>(&mut self, handler: H) -> Result<()> {
402 if self
403 .handlers
404 .iter()
405 .any(|existing| existing.name() == handler.name())
406 {
407 return Err(Error::Extension(format!(
408 "extension handler {} is already registered",
409 handler.name()
410 )));
411 }
412 self.handlers.push(Arc::new(handler));
413 Ok(())
414 }
415 pub fn contains(&self, name: &str) -> bool {
417 self.handlers.iter().any(|handler| handler.name() == name)
418 }
419 pub fn allows_binary_transform(&self, name: &str) -> bool {
421 self.handlers
422 .iter()
423 .any(|handler| handler.name() == name && handler.allows_binary_transform())
424 }
425 pub fn validate(&self, document: &Document) -> Result<ExtensionValidationContext> {
427 let mut context = ExtensionValidationContext::default();
428 for handler in &self.handlers {
429 handler.validate(document, &mut context)?;
430 }
431 Ok(context)
432 }
433 #[cfg(feature = "draco-encode")]
434 pub(crate) fn collect_binary_references(
435 &self,
436 document: &Document,
437 accessors: &mut [bool],
438 buffer_views: &mut [bool],
439 ) -> Result<()> {
440 for handler in &self.handlers {
441 if handler.allows_binary_transform() {
442 handler.collect_binary_references(document, accessors, buffer_views)?;
443 }
444 }
445 Ok(())
446 }
447 #[cfg(feature = "draco-encode")]
448 pub(crate) fn remap_binary_references(
449 &self,
450 document: &mut Document,
451 accessors: &[Option<usize>],
452 buffer_views: &[Option<usize>],
453 ) -> Result<()> {
454 for handler in &self.handlers {
455 if handler.allows_binary_transform() {
456 handler.remap_binary_references(document, accessors, buffer_views)?;
457 }
458 }
459 Ok(())
460 }
461 pub fn decode_primitive(
463 &self,
464 document: &Document,
465 resources: &ResourceStore,
466 primitive: PrimitiveRef<'_>,
467 ) -> Result<Mesh> {
468 for handler in &self.handlers {
469 if let Some(result) = handler.decode_primitive(document, resources, primitive) {
470 return result;
471 }
472 }
473 Err(Error::Extension(
474 "primitive has no registered geometry extension decoder".into(),
475 ))
476 }
477}
478
479#[derive(Clone, Copy, Debug, Default)]
481pub struct DracoExtension;
482impl ExtensionHandler for DracoExtension {
483 fn name(&self) -> &'static str {
484 KHR_DRACO_MESH_COMPRESSION
485 }
486 fn allows_binary_transform(&self) -> bool {
487 true
488 }
489 fn collect_binary_references(
490 &self,
491 document: &Document,
492 _accessors: &mut [bool],
493 buffer_views: &mut [bool],
494 ) -> Result<()> {
495 if buffer_views.is_empty() {
496 return Ok(());
497 }
498 for mesh in document.meshes() {
499 for primitive in mesh
500 .value()
501 .get("primitives")
502 .and_then(Value::as_array)
503 .unwrap_or(&[])
504 {
505 let Some(extension) = primitive
506 .get("extensions")
507 .and_then(|value| value.get(KHR_DRACO_MESH_COMPRESSION))
508 else {
509 continue;
510 };
511 let index = extension
512 .get("bufferView")
513 .and_then(Value::as_u64)
514 .and_then(|value| usize::try_from(value).ok())
515 .filter(|index| *index < buffer_views.len())
516 .ok_or_else(|| Error::Extension("Draco bufferView is invalid".into()))?;
517 buffer_views[index] = true;
518 }
519 }
520 Ok(())
521 }
522 fn remap_binary_references(
523 &self,
524 document: &mut Document,
525 _accessors: &[Option<usize>],
526 buffer_views: &[Option<usize>],
527 ) -> Result<()> {
528 if buffer_views.is_empty() {
529 return Ok(());
530 }
531 let Some(meshes) = document
532 .as_value_mut()
533 .get_mut("meshes")
534 .and_then(Value::as_array_mut)
535 else {
536 return Ok(());
537 };
538 for mesh in meshes {
539 let Some(primitives) = mesh.get_mut("primitives").and_then(Value::as_array_mut) else {
540 continue;
541 };
542 for primitive in primitives {
543 let Some(value) = primitive
544 .get_mut("extensions")
545 .and_then(|value| value.get_mut(KHR_DRACO_MESH_COMPRESSION))
546 .and_then(|value| value.get_mut("bufferView"))
547 else {
548 continue;
549 };
550 remap_reference(value, buffer_views, "Draco bufferView")?;
551 }
552 }
553 Ok(())
554 }
555 fn validate(
556 &self,
557 document: &Document,
558 context: &mut ExtensionValidationContext,
559 ) -> Result<()> {
560 let accessors = document
561 .as_value()
562 .get("accessors")
563 .and_then(Value::as_array)
564 .unwrap_or(&[]);
565 for mesh in document.meshes() {
566 for primitive_index in mesh
567 .value()
568 .get("primitives")
569 .and_then(Value::as_array)
570 .into_iter()
571 .flatten()
572 .enumerate()
573 {
574 let primitive = primitive_index.1;
575 let Some(_parsed) = parse_draco_extension(
576 primitive
577 .get("extensions")
578 .and_then(|extensions| extensions.get(KHR_DRACO_MESH_COMPRESSION)),
579 )?
580 else {
581 continue;
582 };
583 for accessor in primitive
584 .get("attributes")
585 .and_then(Value::as_object)
586 .into_iter()
587 .flat_map(|attrs| attrs.iter().map(|(_, value)| value))
588 .chain(primitive.get("indices"))
589 {
590 if let Some(index) = accessor
591 .as_u64()
592 .and_then(|value| usize::try_from(value).ok())
593 {
594 if accessors.get(index).is_some_and(|value| {
595 value.get("bufferView").is_none() && value.get("sparse").is_none()
596 }) {
597 context.allow_accessor_without_buffer_view(index);
598 }
599 }
600 }
601 }
602 }
603 Ok(())
604 }
605 #[cfg(feature = "draco-decode")]
606 fn decode_primitive(
607 &self,
608 document: &Document,
609 resources: &ResourceStore,
610 primitive: PrimitiveRef<'_>,
611 ) -> Option<Result<Mesh>> {
612 let extension = primitive.extension(self.name())?;
613 Some((|| {
614 let parsed = parse_draco_extension(Some(extension))?
615 .ok_or_else(|| Error::Extension("missing Draco extension".into()))?;
616 let view = document.as_value()["bufferViews"]
617 .as_array()
618 .and_then(|views| views.get(parsed.buffer_view))
619 .ok_or_else(|| Error::Extension("Draco bufferView out of range".into()))?;
620 let buffer = view
621 .get("buffer")
622 .and_then(Value::as_u64)
623 .and_then(|value| usize::try_from(value).ok())
624 .and_then(|index| resources.buffers.get(index))
625 .ok_or_else(|| Error::Extension("Draco buffer is not resolved".into()))?;
626 let start = view.get("byteOffset").and_then(Value::as_u64).unwrap_or(0) as usize;
627 let length = view
628 .get("byteLength")
629 .and_then(Value::as_u64)
630 .and_then(|value| usize::try_from(value).ok())
631 .ok_or_else(|| Error::Extension("Draco bufferView length is invalid".into()))?;
632 let end = start
633 .checked_add(length)
634 .filter(|end| *end <= buffer.len())
635 .ok_or_else(|| Error::Extension("Draco bufferView out of bounds".into()))?;
636 let mut mesh = Mesh::new();
637 MeshDecoder::new()
638 .decode(&mut DecoderBuffer::new(&buffer[start..end]), &mut mesh)
639 .map_err(Error::Decode)?;
640 Ok(mesh)
641 })())
642 }
643}
644
645fn remap_reference(value: &mut Value, map: &[Option<usize>], kind: &str) -> Result<()> {
646 let old = value
647 .as_u64()
648 .and_then(|value| usize::try_from(value).ok())
649 .ok_or_else(|| Error::Extension(format!("{kind} is invalid")))?;
650 let new = map
651 .get(old)
652 .and_then(|value| *value)
653 .ok_or_else(|| Error::Extension(format!("{kind} was removed")))?;
654 *value = Value::from(new);
655 Ok(())
656}
657
658#[cfg_attr(not(feature = "draco-decode"), allow(dead_code))]
659#[derive(Clone, Debug)]
660pub(crate) struct DracoContract {
661 pub buffer_view: usize,
662 pub attributes: Vec<(String, u32)>,
663}
664
665pub(crate) fn parse_draco_extension(value: Option<&Value>) -> Result<Option<DracoContract>> {
666 let Some(value) = value else {
667 return Ok(None);
668 };
669 let buffer_view = value
670 .get("bufferView")
671 .and_then(Value::as_u64)
672 .and_then(|value| usize::try_from(value).ok())
673 .ok_or_else(|| Error::Extension("Draco bufferView is invalid".into()))?;
674 let attributes = value
675 .get("attributes")
676 .and_then(Value::as_object)
677 .ok_or_else(|| Error::Extension("Draco attributes is invalid".into()))?
678 .iter()
679 .map(|(name, value)| {
680 value
681 .as_u64()
682 .and_then(|value| u32::try_from(value).ok())
683 .map(|value| (name.clone(), value))
684 .ok_or_else(|| Error::Extension(format!("Draco attribute {name} is invalid")))
685 })
686 .collect::<Result<Vec<_>>>()?;
687 Ok(Some(DracoContract {
688 buffer_view,
689 attributes,
690 }))
691}
692
693impl Default for ExtensionRegistry {
694 fn default() -> Self {
695 let mut registry = Self {
696 handlers: Vec::new(),
697 };
698 registry
699 .register(DracoExtension)
700 .expect("built-in extension names are unique");
701 #[cfg(feature = "write")]
707 {
708 registry
709 .register(MeshGpuInstancingExtension)
710 .expect("built-in extension names are unique");
711 registry
712 .register(StructuralMetadataExtension)
713 .expect("built-in extension names are unique");
714 for name in BINARY_FREE_EXTENSIONS {
715 registry
716 .register(BinaryFreeExtension(name))
717 .expect("built-in extension names are unique");
718 }
719 }
720 registry
721 }
722}