diff --git a/nusamai-gltf/examples/geometry_to_gltf.rs b/nusamai-gltf/examples/geometry_to_gltf.rs index 3dd3a2af7..c4a8025da 100644 --- a/nusamai-gltf/examples/geometry_to_gltf.rs +++ b/nusamai-gltf/examples/geometry_to_gltf.rs @@ -46,11 +46,7 @@ struct Triangles { impl Triangles { pub fn new(indices: Vec, vertices: IndexSet<[u32; 3]>) -> Self { - Self { - indices, - vertices, - ..Default::default() - } + Self { indices, vertices } } } @@ -364,7 +360,7 @@ fn make_gltf_json(triangles: &Triangles) -> String { buffer.byte_length = indices_byte_length + vertices_byte_length; buffer.uri = Some("data.bin".to_string()); - gltf.buffers = Some(vec![buffer]); + gltf.buffers = vec![buffer]; // glTF のバッファビューを作成 let mut buffer_view1 = BufferView::new(); @@ -379,7 +375,7 @@ fn make_gltf_json(triangles: &Triangles) -> String { buffer_view2.byte_offset = indices_byte_length; buffer_view2.target = Some(BufferViewTarget::ArrayBuffer); - gltf.buffer_views = Some(vec![buffer_view1, buffer_view2]); + gltf.buffer_views = vec![buffer_view1, buffer_view2]; // glTF のアクセサを作成 let mut accessor1 = Accessor::new(); @@ -389,8 +385,8 @@ fn make_gltf_json(triangles: &Triangles) -> String { accessor1.count = indices.len() as u32; accessor1.type_ = AccessorType::Scalar; let max_indices = indices.iter().max().unwrap(); - accessor1.max = Some(vec![*max_indices as f32]); - accessor1.min = Some(vec![0.0]); + accessor1.max = vec![*max_indices as f32].into(); + accessor1.min = vec![0.0].into(); let mut accessor2 = Accessor::new(); accessor2.buffer_view = Some(1); @@ -413,7 +409,7 @@ fn make_gltf_json(triangles: &Triangles) -> String { accessor2.max = Some(max_vertex.to_vec()); accessor2.min = Some(min_vertex.to_vec()); - gltf.accessors = Some(vec![accessor1, accessor2]); + gltf.accessors = vec![accessor1, accessor2]; // glTF のメッシュを作成 let mut mesh = Mesh::new(); @@ -428,19 +424,19 @@ fn make_gltf_json(triangles: &Triangles) -> String { mesh.primitives = vec![primitive1]; - gltf.meshes = Some(vec![mesh]); + gltf.meshes = vec![mesh]; // glTF のシーンを作成 let mut scene = Scene::new(); scene.nodes = Some(vec![0]); - gltf.scenes = Some(vec![scene]); + gltf.scenes = vec![scene]; // glTF のノードを作成 let mut node = Node::new(); node.mesh = Some(0); - gltf.nodes = Some(vec![node]); + gltf.nodes = vec![node]; // glTF のシーンを設定 gltf.scene = Some(0); diff --git a/nusamai-gltf/examples/make_gltf.rs b/nusamai-gltf/examples/make_gltf.rs index cd40497d5..390930290 100644 --- a/nusamai-gltf/examples/make_gltf.rs +++ b/nusamai-gltf/examples/make_gltf.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; /// glTFを生成するサンプル /// /// % cargo run --example make_gltf --release @@ -9,46 +8,16 @@ use std::vec; use nusamai_gltf::*; fn main() -> io::Result<()> { - let asset = Asset { - version: "2.0".to_string(), - copyright: None, - generator: None, - min_version: None, - extensions: None, - extras: None, - }; - let mut gltf = Gltf { - extensions_used: None, - extensions_required: None, - accessors: None, - animations: None, - asset, - buffers: None, - buffer_views: None, - cameras: None, - images: None, - materials: None, - meshes: None, - nodes: None, - samplers: None, - scene: None, - scenes: None, - skins: None, - textures: None, - extensions: None, - extras: None, + ..Default::default() }; let byte_length = 44; let mut buffer = Buffer { - name: None, byte_length, - uri: None, - extensions: None, - extras: None, + ..Default::default() }; - buffer.uri = Some("data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=".to_string()); + buffer.uri = "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=".to_string().into(); let buffer_view1 = BufferView { name: None, @@ -69,81 +38,54 @@ fn main() -> io::Result<()> { }; let accessor1 = Accessor { - name: None, buffer_view: Some(0), - byte_offset: 0, component_type: ComponentType::UnsignedShort, count: 3, type_: AccessorType::Scalar, - max: Some(vec![2.0]), - min: Some(vec![0.0]), - sparse: None, - normalized: None, - extensions: None, - extras: None, + max: vec![2.0].into(), + min: vec![0.0].into(), + ..Default::default() }; let accessor2 = Accessor { - name: None, buffer_view: Some(1), - byte_offset: 0, component_type: ComponentType::Float, count: 3, type_: AccessorType::Vec3, - max: Some(vec![1.0, 1.0, 0.0]), - min: Some(vec![0.0, 0.0, 0.0]), - sparse: None, - normalized: None, - extensions: None, - extras: None, + max: vec![1.0, 1.0, 0.0].into(), + min: vec![0.0, 0.0, 0.0].into(), + ..Default::default() }; let mut primitive = MeshPrimitive { - attributes: HashMap::new(), indices: Some(0), - material: None, mode: PrimitiveMode::Triangles, - targets: None, - extensions: None, - extras: None, + ..Default::default() }; primitive.attributes.insert("POSITION".to_string(), 1); let mesh = Mesh { primitives: vec![primitive], - weights: None, - name: None, - extensions: None, - extras: None, + ..Default::default() }; let node = Node { - camera: None, - children: None, - skin: None, - matrix: None, mesh: Some(0), - rotation: None, - scale: None, - translation: None, - weights: None, - name: None, - extensions: None, - extras: None, + ..Default::default() }; let scene = Scene { name: None, - nodes: Some(vec![0]), + nodes: vec![0].into(), }; - gltf.buffers = Some(vec![buffer]); - gltf.buffer_views = Some(vec![buffer_view1, buffer_view2]); - gltf.accessors = Some(vec![accessor1, accessor2]); - gltf.meshes = Some(vec![mesh]); - gltf.nodes = Some(vec![node]); - gltf.scenes = Some(vec![scene]); - gltf.scene = Some(0); + gltf.buffers = vec![buffer]; + gltf.buffer_views = vec![buffer_view1, buffer_view2]; + gltf.accessors = vec![accessor1, accessor2]; + gltf.meshes = vec![mesh]; + gltf.nodes = vec![node]; + gltf.scenes = vec![scene]; + gltf.scene = 0.into(); println!("gltf: {:?}", gltf); diff --git a/nusamai-gltf/src/models/accessor.rs b/nusamai-gltf/src/models/accessor.rs index c2c460e00..275297eaa 100644 --- a/nusamai-gltf/src/models/accessor.rs +++ b/nusamai-gltf/src/models/accessor.rs @@ -137,7 +137,7 @@ pub struct Accessor { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } impl Accessor { diff --git a/nusamai-gltf/src/models/animation.rs b/nusamai-gltf/src/models/animation.rs index 583de5f11..e599be367 100644 --- a/nusamai-gltf/src/models/animation.rs +++ b/nusamai-gltf/src/models/animation.rs @@ -75,6 +75,7 @@ pub struct Animation { /// An array of animation channels. An animation channel combines an animation sampler with a target property being animated. pub channels: Vec, + /// An array of animation samplers. An animation sampler combines timestamps with a sequence of output values and defines an interpolation algorithm. pub samplers: Vec, @@ -84,7 +85,7 @@ pub struct Animation { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } #[derive(Serialize, Deserialize, Debug, Default)] diff --git a/nusamai-gltf/src/models/asset.rs b/nusamai-gltf/src/models/asset.rs index 6224b78f1..bf524a55e 100644 --- a/nusamai-gltf/src/models/asset.rs +++ b/nusamai-gltf/src/models/asset.rs @@ -18,6 +18,7 @@ pub struct Asset { pub generator: Option, /// The glTF version in the form of `.` that this asset targets. + #[serde(default = "default_version")] pub version: String, /// The minimum glTF version in the form of `.` that this asset targets. This property **MUST NOT** be greater than the asset version. @@ -30,7 +31,11 @@ pub struct Asset { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, +} + +fn default_version() -> String { + "2.0".to_string() } #[derive(Serialize, Deserialize, Debug, Default)] diff --git a/nusamai-gltf/src/models/buffer.rs b/nusamai-gltf/src/models/buffer.rs index 8ae96141f..7531ea540 100644 --- a/nusamai-gltf/src/models/buffer.rs +++ b/nusamai-gltf/src/models/buffer.rs @@ -35,7 +35,7 @@ pub struct Buffer { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } #[derive(Serialize, Deserialize, Debug, Default)] @@ -72,7 +72,7 @@ pub struct BufferView { /// The stride, in bytes, between vertex attributes. When this is not defined, data is tightly packed. When two or more accessors use the same buffer view, this field MUST be defined. #[serde(skip_serializing_if = "Option::is_none")] - pub byte_stride: Option, + pub byte_stride: Option, /// The hint representing the intended GPU buffer type to use with this buffer view. #[serde(skip_serializing_if = "Option::is_none")] diff --git a/nusamai-gltf/src/models/camera.rs b/nusamai-gltf/src/models/camera.rs index f0865a640..afc5809f9 100644 --- a/nusamai-gltf/src/models/camera.rs +++ b/nusamai-gltf/src/models/camera.rs @@ -76,7 +76,7 @@ pub struct Camera { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } #[derive(Serialize, Deserialize, Debug, Default)] diff --git a/nusamai-gltf/src/models/extensions/gltf.rs b/nusamai-gltf/src/models/extensions/gltf/ext_structural_metadata.rs similarity index 92% rename from nusamai-gltf/src/models/extensions/gltf.rs rename to nusamai-gltf/src/models/extensions/gltf/ext_structural_metadata.rs index 26bc0e6ba..9f27270ea 100644 --- a/nusamai-gltf/src/models/extensions/gltf.rs +++ b/nusamai-gltf/src/models/extensions/gltf/ext_structural_metadata.rs @@ -1,13 +1,11 @@ +//! EXT_structural_metadata +//! +//! https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata + use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; -#[derive(Serialize, Deserialize, Debug, Default)] -pub struct Gltf { - #[serde(rename = "EXT_structural_metadata")] - pub ext_structural_metadata: ExtStructuralMetadata, -} - #[derive(Serialize, Deserialize, Debug, Default)] #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] @@ -21,15 +19,15 @@ pub struct ExtStructuralMetadata { pub schema_uri: Option, /// An array of property table definitions, which may be referenced by index. - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub property_tables: Option>, /// An array of indexes of property textures in the root `EXT_structural_metadata` object. - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub property_textures: Option>, /// An array of indexes of property attributes in the root `EXT_structural_metadata` object. - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, skip_serializing_if = "Option::is_none")] pub property_attributes: Option>, } @@ -54,14 +52,12 @@ pub struct Schema { pub version: Option, /// A dictionary, where each key is a class ID and each value is an object defining the class. - #[serde(skip_serializing_if = "HashMap::is_empty")] - #[serde(default)] + #[serde(default, skip_serializing_if = "HashMap::is_empty")] pub classes: HashMap, /// A dictionary, where each key is an enum ID and each value is an object defining the values for the enum. - #[serde(skip_serializing_if = "HashMap::is_empty")] - #[serde(default)] - pub enums: HashMap, + #[serde(default, skip_serializing_if = "HashMap::is_empty")] + pub enums: HashMap, /// JSON object with extension-specific objects. #[serde(skip_serializing_if = "Option::is_none")] @@ -69,7 +65,7 @@ pub struct Schema { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } /// Class in EXT_structural_metadata @@ -85,7 +81,7 @@ pub struct Class { pub description: Option, /// A dictionary, where each key is a property ID and each value is an object defining the property. - #[serde(skip_serializing_if = "HashMap::is_empty")] + #[serde(default, skip_serializing_if = "HashMap::is_empty")] pub properties: HashMap, /// JSON object with extension-specific objects. @@ -94,13 +90,13 @@ pub struct Class { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } /// ElementType enumeration #[derive(Serialize, Deserialize, Debug, Default)] #[serde(rename_all = "UPPERCASE")] -pub enum ElementType { +pub enum ClassPropertyType { #[default] Scalar, Vec2, @@ -112,13 +108,12 @@ pub enum ElementType { String, Boolean, Enum, - // Add other types as needed } /// ComponentType enumeration #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "UPPERCASE")] -pub enum ComponentType { +pub enum ClassPropertyComponentType { Int8, UInt8, Int16, @@ -129,7 +124,6 @@ pub enum ComponentType { UInt64, Float32, Float64, - // Add other types as needed } /// Class Property in EXT_structural_metadata @@ -147,11 +141,11 @@ pub struct ClassProperty { /// The element type. #[serde(rename = "type")] - pub type_: ElementType, + pub type_: ClassPropertyType, /// The datatype of the element's components. Only applicable to `SCALAR`, `VECN`, and `MATN` types." #[serde(skip_serializing_if = "Option::is_none")] - pub component_type: Option, + pub component_type: Option, /// Enum ID as declared in the `enums` dictionary. Required when `type` is `ENUM`. #[serde(skip_serializing_if = "Option::is_none")] @@ -207,30 +201,29 @@ pub struct ClassProperty { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } /// ValueType enumeration #[derive(Serialize, Deserialize, Debug, Default)] #[serde(rename_all = "UPPERCASE")] -pub enum ValueType { - #[default] +pub enum EnumValueType { Int8, UInt8, Int16, + #[default] UInt16, Int32, UInt32, Int64, UInt64, - // Add other types as needed } /// Enum in EXT_structural_metadata #[derive(Serialize, Deserialize, Debug, Default)] #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] -pub struct EnumMetadata { +pub struct Enum { /// The name of the enum, e.g. for display purposes. #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, @@ -240,8 +233,8 @@ pub struct EnumMetadata { pub description: Option, /// The type of the integer enum value. - #[serde(default = "default_value_type")] - pub value_type: ValueType, + #[serde(default)] + pub value_type: EnumValueType, /// An array of enum values. pub values: Vec, @@ -252,11 +245,7 @@ pub struct EnumMetadata { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, -} - -fn default_value_type() -> ValueType { - ValueType::UInt16 + pub extras: Option, } /// Enum Value in EXT_structural_metadata @@ -280,7 +269,7 @@ pub struct EnumValue { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } /// Property Table in EXT_structural_metadata @@ -299,6 +288,7 @@ pub struct PropertyTable { pub count: u32, /// A dictionary, where each key corresponds to a property ID and each value is an object describing where property values are stored. + #[serde(default, skip_serializing_if = "HashMap::is_empty")] pub properties: HashMap, /// JSON object with extension-specific objects. @@ -307,7 +297,7 @@ pub struct PropertyTable { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } #[derive(Serialize, Deserialize, Debug, Default)] @@ -320,6 +310,7 @@ pub struct PropertyTexture { class: String, /// A dictionary, where each key corresponds to a property ID in the class' `properties` dictionary and each value is an object describing where property values are stored. Required properties must be included in this dictionary. + #[serde(default, skip_serializing_if = "HashMap::is_empty")] properties: HashMap, } @@ -364,6 +355,7 @@ pub struct PropertyAttribute { class: String, /// A dictionary, where each key corresponds to a property ID in the class' `properties` dictionary and each value is an object describing where property values are stored. Required properties must be included in this dictionary. + #[serde(default, skip_serializing_if = "HashMap::is_empty")] properties: HashMap, } @@ -394,12 +386,11 @@ pub struct PropertyAttributeProperty { #[derive(Serialize, Deserialize, Debug, Default)] #[serde(rename_all = "UPPERCASE")] pub enum OffsetType { - #[default] UInt8, UInt16, + #[default] UInt32, UInt64, - // Add other types as needed } /// Property Table Property in EXT_structural_metadata @@ -419,11 +410,11 @@ pub struct PropertyTableProperty { pub string_offsets: Option, /// The type of values in `arrayOffsets`. - #[serde(default = "default_offset_type")] + #[serde(default)] pub array_offset_type: OffsetType, /// The type of values in `stringOffsets`. - #[serde(default = "default_offset_type")] + #[serde(default)] pub string_offset_type: OffsetType, /// An offset to apply to property values. @@ -448,9 +439,5 @@ pub struct PropertyTableProperty { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, -} - -fn default_offset_type() -> OffsetType { - OffsetType::UInt32 + pub extras: Option, } diff --git a/nusamai-gltf/src/models/extensions/gltf/mod.rs b/nusamai-gltf/src/models/extensions/gltf/mod.rs new file mode 100644 index 000000000..836724843 --- /dev/null +++ b/nusamai-gltf/src/models/extensions/gltf/mod.rs @@ -0,0 +1,16 @@ +mod ext_structural_metadata; + +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct Gltf { + #[serde(rename = "EXT_structural_metadata")] + #[serde(skip_serializing_if = "Option::is_none")] + pub ext_structural_metadata: Option, + + #[serde(flatten)] + others: HashMap, +} diff --git a/nusamai-gltf/src/models/extensions/mesh.rs b/nusamai-gltf/src/models/extensions/mesh/ext_mesh_features.rs similarity index 65% rename from nusamai-gltf/src/models/extensions/mesh.rs rename to nusamai-gltf/src/models/extensions/mesh/ext_mesh_features.rs index ea7829aea..504f87a7a 100644 --- a/nusamai-gltf/src/models/extensions/mesh.rs +++ b/nusamai-gltf/src/models/extensions/mesh/ext_mesh_features.rs @@ -1,7 +1,28 @@ +//! EXT_mesh_features +//! +//! https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_mesh_features + use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; +/// EXT_mesh_features glTF Mesh Primitive extension +#[derive(Serialize, Deserialize, Debug, Default)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct ExtMeshFeatures { + /// An array of feature ID sets. + pub feature_ids: Vec, + + /// JSON object with extension-specific objects. + #[serde(skip_serializing_if = "Option::is_none")] + pub extensions: Option>, + + /// Application-specific data. + #[serde(skip_serializing_if = "Option::is_none")] + pub extras: Option, +} + /// Feature ID in EXT_mesh_features #[derive(Serialize, Deserialize, Debug, Default)] #[serde(rename_all = "camelCase")] @@ -36,7 +57,7 @@ pub struct FeatureId { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } /// Feature ID Texture in EXT_mesh_features @@ -61,72 +82,9 @@ pub struct FeatureIdTexture { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } fn default_channels() -> Vec { vec![0] } - -/// EXT_mesh_features glTF Mesh Primitive extension -#[derive(Serialize, Deserialize, Debug, Default)] -#[serde(deny_unknown_fields)] -pub struct Primitive { - #[serde(skip_serializing_if = "Option::is_none", rename = "EXT_mesh_features")] - pub ext_mesh_features: Option, - - #[serde( - skip_serializing_if = "Option::is_none", - rename = "EXT_structural_metadata" - )] - pub ext_structural_metadata: Option, - - #[serde( - skip_serializing_if = "Option::is_none", - rename = "KHR_materials_variants" - )] - pub khr_materials_variants: Option, -} - -// TODO: Implement KHR_materials_variants -#[derive(Serialize, Deserialize, Debug, Default)] -pub struct KhrMaterialsVariants { - #[serde(flatten)] - pub others: HashMap, -} - -#[derive(Serialize, Deserialize, Debug, Default)] -#[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] -pub struct ExtMeshFeatures { - /// An array of feature ID sets. - pub feature_ids: Vec, - - /// JSON object with extension-specific objects. - #[serde(skip_serializing_if = "Option::is_none")] - pub extensions: Option>, - - /// Application-specific data. - #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, -} - -/// EXT_structural_metadata glTF Mesh Primitive extension -#[derive(Serialize, Deserialize, Debug, Default)] -pub struct ExtStructuralMetadata { - /// An array of indexes of property textures in the root `EXT_structural_metadata` object. - #[serde(skip_serializing_if = "Option::is_none")] - pub property_textures: Option>, - - /// An array of indexes of property attributes in the root `EXT_structural_metadata` object. - #[serde(skip_serializing_if = "Option::is_none")] - pub property_attributes: Option>, - - /// JSON object with extension-specific objects. - #[serde(skip_serializing_if = "Option::is_none")] - pub extensions: Option>, - - /// Application-specific data. - #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, -} diff --git a/nusamai-gltf/src/models/extensions/mesh/ext_structural_metadata.rs b/nusamai-gltf/src/models/extensions/mesh/ext_structural_metadata.rs new file mode 100644 index 000000000..9c5c44a78 --- /dev/null +++ b/nusamai-gltf/src/models/extensions/mesh/ext_structural_metadata.rs @@ -0,0 +1,27 @@ +//! EXT_structural_metadata +//! +//! https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata + +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::collections::HashMap; + +/// EXT_structural_metadata glTF Mesh Primitive extension +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct ExtStructuralMetadata { + /// An array of indexes of property textures in the root `EXT_structural_metadata` object. + #[serde(skip_serializing_if = "Option::is_none")] + pub property_textures: Option>, + + /// An array of indexes of property attributes in the root `EXT_structural_metadata` object. + #[serde(skip_serializing_if = "Option::is_none")] + pub property_attributes: Option>, + + /// JSON object with extension-specific objects. + #[serde(skip_serializing_if = "Option::is_none")] + pub extensions: Option>, + + /// Application-specific data. + #[serde(skip_serializing_if = "Option::is_none")] + pub extras: Option, +} diff --git a/nusamai-gltf/src/models/extensions/mesh/khr_materials_variants.rs b/nusamai-gltf/src/models/extensions/mesh/khr_materials_variants.rs new file mode 100644 index 000000000..82e2a75a1 --- /dev/null +++ b/nusamai-gltf/src/models/extensions/mesh/khr_materials_variants.rs @@ -0,0 +1,10 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::collections::HashMap; + +// TODO: Implement KHR_materials_variants +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct KhrMaterialsVariants { + #[serde(flatten)] + pub others: HashMap, +} diff --git a/nusamai-gltf/src/models/extensions/mesh/mod.rs b/nusamai-gltf/src/models/extensions/mesh/mod.rs new file mode 100644 index 000000000..afa7837e5 --- /dev/null +++ b/nusamai-gltf/src/models/extensions/mesh/mod.rs @@ -0,0 +1,27 @@ +pub mod ext_mesh_features; +pub mod ext_structural_metadata; +pub mod khr_materials_variants; + +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +#[derive(Serialize, Deserialize, Debug, Default)] +#[serde(deny_unknown_fields)] +pub struct MeshPrimitive { + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(rename = "EXT_mesh_features")] + pub ext_mesh_features: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(rename = "EXT_structural_metadata")] + pub ext_structural_metadata: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(rename = "KHR_materials_variants")] + pub khr_materials_variants: Option, + + #[serde(flatten)] + others: HashMap, +} diff --git a/nusamai-gltf/src/models/gltf.rs b/nusamai-gltf/src/models/gltf.rs index fec707835..07811c74f 100644 --- a/nusamai-gltf/src/models/gltf.rs +++ b/nusamai-gltf/src/models/gltf.rs @@ -4,8 +4,6 @@ use super::{ Accessor, Animation, Asset, Buffer, BufferView, Camera, Image, Material, Mesh, Node, Sampler, Scene, Skin, Texture, }; -use std::collections::HashMap; - use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -15,92 +13,80 @@ use serde_json::Value; #[serde(deny_unknown_fields)] pub struct Gltf { /// Names of glTF extensions used in this asset. - #[serde(skip_serializing_if = "Option::is_none")] - pub extensions_used: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub extensions_used: Vec, /// Names of glTF extensions required to properly load this asset. - #[serde(skip_serializing_if = "Option::is_none")] - pub extensions_required: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub extensions_required: Vec, /// An array of accessors. An accessor is a typed view into a bufferView. - #[serde(skip_serializing_if = "Option::is_none")] - pub accessors: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub accessors: Vec, /// An array of keyframe animations. - #[serde(skip_serializing_if = "Option::is_none")] - pub animations: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub animations: Vec, /// Metadata about the glTF asset. pub asset: Asset, /// An array of buffers. A buffer points to binary geometry, animation, or skins. - #[serde(skip_serializing_if = "Option::is_none")] - pub buffers: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub buffers: Vec, /// An array of bufferViews. A bufferView is a view into a buffer generally representing a subset of the buffer. - #[serde(skip_serializing_if = "Option::is_none")] - pub buffer_views: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub buffer_views: Vec, /// An array of cameras. A camera defines a projection matrix. - #[serde(skip_serializing_if = "Option::is_none")] - pub cameras: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub cameras: Vec, /// An array of images. An image defines data used to create a texture. - #[serde(skip_serializing_if = "Option::is_none")] - pub images: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub images: Vec, /// An array of materials. A material defines the appearance of a primitive. - #[serde(skip_serializing_if = "Option::is_none")] - pub materials: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub materials: Vec, /// An array of meshes. A mesh is a set of primitives to be rendered. - #[serde(skip_serializing_if = "Option::is_none")] - pub meshes: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub meshes: Vec, /// An array of nodes. - #[serde(skip_serializing_if = "Option::is_none")] - pub nodes: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub nodes: Vec, /// An array of samplers. A sampler contains properties for texture filtering and wrapping modes. - #[serde(skip_serializing_if = "Option::is_none")] - pub samplers: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub samplers: Vec, /// The index of the default scene. This property MUST NOT be defined, when scenes is undefined. #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] pub scene: Option, /// An array of scenes. - #[serde(skip_serializing_if = "Option::is_none")] - pub scenes: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub scenes: Vec, /// An array of skins. A skin is defined by joints and matrices. - #[serde(skip_serializing_if = "Option::is_none")] - pub skins: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub skins: Vec, /// An array of textures. - #[serde(skip_serializing_if = "Option::is_none")] - pub textures: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub textures: Vec, /// JSON object with extension-specific objects. #[serde(skip_serializing_if = "Option::is_none")] - pub extensions: Option, + pub extensions: Option, /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, -} - -#[derive(Serialize, Deserialize, Debug, Default)] -#[serde(rename_all = "camelCase")] -pub struct GLTFExtensions { - #[serde( - skip_serializing_if = "Option::is_none", - rename = "EXT_structural_metadata" - )] - pub ext_structural_metadata: Option, - - #[serde(flatten)] - others: HashMap, + pub extras: Option, } impl Gltf { diff --git a/nusamai-gltf/src/models/image.rs b/nusamai-gltf/src/models/image.rs index 1d0b2248d..6f5ca0851 100644 --- a/nusamai-gltf/src/models/image.rs +++ b/nusamai-gltf/src/models/image.rs @@ -41,7 +41,7 @@ pub struct Image { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } #[derive(Serialize, Deserialize, Debug, Default)] diff --git a/nusamai-gltf/src/models/material.rs b/nusamai-gltf/src/models/material.rs index c53a64cf1..4b888988c 100644 --- a/nusamai-gltf/src/models/material.rs +++ b/nusamai-gltf/src/models/material.rs @@ -39,7 +39,7 @@ pub struct MaterialNormalTextureInfo { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } #[derive(Serialize, Deserialize, Debug, Default)] @@ -74,7 +74,7 @@ pub struct MaterialOcclusionTextureInfo { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } #[derive(Serialize, Deserialize, Debug, Default)] @@ -168,7 +168,7 @@ pub struct Material { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } impl Default for Material { diff --git a/nusamai-gltf/src/models/mesh.rs b/nusamai-gltf/src/models/mesh.rs index 8ae3b424c..c26b8a462 100644 --- a/nusamai-gltf/src/models/mesh.rs +++ b/nusamai-gltf/src/models/mesh.rs @@ -29,7 +29,7 @@ impl Default for PrimitiveMode { #[serde(deny_unknown_fields)] pub struct MeshPrimitive { /// A plain JSON object, where each key corresponds to a mesh attribute semantic and each value is the index of the accessor containing attribute's data. - pub attributes: HashMap, + pub attributes: HashMap, // required /// The index of the accessor that contains the vertex indices. When this is undefined, the primitive defines non-indexed geometry. When defined, the accessor **MUST** have `SCALAR` type and an unsigned integer component type. #[serde(skip_serializing_if = "Option::is_none")] @@ -49,11 +49,11 @@ pub struct MeshPrimitive { /// JSON object with extension-specific objects. #[serde(skip_serializing_if = "Option::is_none")] - pub extensions: Option, + pub extensions: Option, /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } impl MeshPrimitive { @@ -86,7 +86,7 @@ pub struct Mesh { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } #[derive(Serialize, Deserialize, Debug, Default)] diff --git a/nusamai-gltf/src/models/node.rs b/nusamai-gltf/src/models/node.rs index 46fe7329f..a7473e04e 100644 --- a/nusamai-gltf/src/models/node.rs +++ b/nusamai-gltf/src/models/node.rs @@ -54,7 +54,7 @@ pub struct Node { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } #[derive(Serialize, Deserialize, Debug, Default)] diff --git a/nusamai-gltf/src/models/sampler.rs b/nusamai-gltf/src/models/sampler.rs index f41fd6a64..5473fb9d7 100644 --- a/nusamai-gltf/src/models/sampler.rs +++ b/nusamai-gltf/src/models/sampler.rs @@ -75,7 +75,7 @@ pub struct Sampler { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } #[derive(Serialize, Deserialize, Debug, Default)] diff --git a/nusamai-gltf/src/models/texture.rs b/nusamai-gltf/src/models/texture.rs index 84cdbb40f..cc89463d5 100644 --- a/nusamai-gltf/src/models/texture.rs +++ b/nusamai-gltf/src/models/texture.rs @@ -27,7 +27,7 @@ pub struct Texture { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } #[derive(Serialize, Deserialize, Debug, Default)] diff --git a/nusamai-gltf/src/models/texture_info.rs b/nusamai-gltf/src/models/texture_info.rs index 1c6b9ad4a..6bf1b5f69 100644 --- a/nusamai-gltf/src/models/texture_info.rs +++ b/nusamai-gltf/src/models/texture_info.rs @@ -21,7 +21,7 @@ pub struct TextureInfo { /// Application-specific data. #[serde(skip_serializing_if = "Option::is_none")] - pub extras: Option>, + pub extras: Option, } #[derive(Serialize, Deserialize, Debug, Default)]