Skip to content

Commit

Permalink
Merge pull request #404 from kpreid/push
Browse files Browse the repository at this point in the history
Add `Root::push()` and `Index::push()` to assist with glTF construction.
  • Loading branch information
alteous authored Dec 29, 2023
2 parents 5647436 + a9c5622 commit d5b04fc
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 33 deletions.
58 changes: 26 additions & 32 deletions examples/export/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ fn export(output: Output) {

let (min, max) = bounding_coords(&triangle_vertices);

let mut root = gltf_json::Root::default();

let buffer_length = triangle_vertices.len() * mem::size_of::<Vertex>();
let buffer = json::Buffer {
let buffer = root.push(json::Buffer {
byte_length: USize64::from(buffer_length),
extensions: Default::default(),
extras: Default::default(),
Expand All @@ -83,19 +85,19 @@ fn export(output: Output) {
} else {
None
},
};
let buffer_view = json::buffer::View {
buffer: json::Index::new(0),
byte_length: buffer.byte_length,
});
let buffer_view = root.push(json::buffer::View {
buffer,
byte_length: USize64::from(buffer_length),
byte_offset: None,
byte_stride: Some(json::buffer::Stride(mem::size_of::<Vertex>())),
extensions: Default::default(),
extras: Default::default(),
name: None,
target: Some(Valid(json::buffer::Target::ArrayBuffer)),
};
let positions = json::Accessor {
buffer_view: Some(json::Index::new(0)),
});
let positions = root.push(json::Accessor {
buffer_view: Some(buffer_view),
byte_offset: Some(USize64(0)),
count: USize64::from(triangle_vertices.len()),
component_type: Valid(json::accessor::GenericComponentType(
Expand All @@ -109,9 +111,9 @@ fn export(output: Output) {
name: None,
normalized: false,
sparse: None,
};
let colors = json::Accessor {
buffer_view: Some(json::Index::new(0)),
});
let colors = root.push(json::Accessor {
buffer_view: Some(buffer_view),
byte_offset: Some(USize64::from(3 * mem::size_of::<f32>())),
count: USize64::from(triangle_vertices.len()),
component_type: Valid(json::accessor::GenericComponentType(
Expand All @@ -125,13 +127,13 @@ fn export(output: Output) {
name: None,
normalized: false,
sparse: None,
};
});

let primitive = json::mesh::Primitive {
attributes: {
let mut map = std::collections::BTreeMap::new();
map.insert(Valid(json::mesh::Semantic::Positions), json::Index::new(0));
map.insert(Valid(json::mesh::Semantic::Colors(0)), json::Index::new(1));
map.insert(Valid(json::mesh::Semantic::Positions), positions);
map.insert(Valid(json::mesh::Semantic::Colors(0)), colors);
map
},
extensions: Default::default(),
Expand All @@ -142,33 +144,25 @@ fn export(output: Output) {
targets: None,
};

let mesh = json::Mesh {
let mesh = root.push(json::Mesh {
extensions: Default::default(),
extras: Default::default(),
name: None,
primitives: vec![primitive],
weights: None,
};
});

let node = json::Node {
mesh: Some(json::Index::new(0)),
let node = root.push(json::Node {
mesh: Some(mesh),
..Default::default()
};
});

let root = json::Root {
accessors: vec![positions, colors],
buffers: vec![buffer],
buffer_views: vec![buffer_view],
meshes: vec![mesh],
root.push(json::Scene {
extensions: Default::default(),
extras: Default::default(),
name: None,
nodes: vec![node],
scenes: vec![json::Scene {
extensions: Default::default(),
extras: Default::default(),
name: None,
nodes: vec![json::Index::new(0)],
}],
..Default::default()
};
});

match output {
Output::Standard => {
Expand Down
44 changes: 44 additions & 0 deletions gltf-json/src/extensions/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,28 @@ impl crate::root::Get<crate::extensions::scene::khr_lights_punctual::Light> for
}
}

#[cfg(feature = "KHR_lights_punctual")]
impl AsRef<[crate::extensions::scene::khr_lights_punctual::Light]> for crate::Root {
fn as_ref(&self) -> &[crate::extensions::scene::khr_lights_punctual::Light] {
self.extensions
.as_ref()
.and_then(|extensions| extensions.khr_lights_punctual.as_ref())
.map(|khr_lights_punctual| khr_lights_punctual.lights.as_slice())
.unwrap_or(&[])
}
}
#[cfg(feature = "KHR_lights_punctual")]
impl AsMut<Vec<crate::extensions::scene::khr_lights_punctual::Light>> for crate::Root {
fn as_mut(&mut self) -> &mut Vec<crate::extensions::scene::khr_lights_punctual::Light> {
&mut self
.extensions
.get_or_insert_with(Default::default)
.khr_lights_punctual
.get_or_insert_with(Default::default)
.lights
}
}

#[cfg(feature = "KHR_materials_variants")]
#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
pub struct KhrMaterialsVariants {
Expand All @@ -72,3 +94,25 @@ impl crate::root::Get<crate::extensions::scene::khr_materials_variants::Variant>
.get(id.value())
}
}

#[cfg(feature = "KHR_materials_variants")]
impl AsRef<[crate::extensions::scene::khr_materials_variants::Variant]> for crate::Root {
fn as_ref(&self) -> &[crate::extensions::scene::khr_materials_variants::Variant] {
self.extensions
.as_ref()
.and_then(|extensions| extensions.khr_materials_variants.as_ref())
.map(|khr_materials_variants| khr_materials_variants.variants.as_slice())
.unwrap_or(&[])
}
}
#[cfg(feature = "KHR_materials_variants")]
impl AsMut<Vec<crate::extensions::scene::khr_materials_variants::Variant>> for crate::Root {
fn as_mut(&mut self) -> &mut Vec<crate::extensions::scene::khr_materials_variants::Variant> {
&mut self
.extensions
.get_or_insert_with(Default::default)
.khr_materials_variants
.get_or_insert_with(Default::default)
.variants
}
}
90 changes: 89 additions & 1 deletion gltf-json/src/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,48 @@ use crate::{
};
use validation::Validate;

// TODO: As a breaking change, simplify by replacing uses of `Get<T>` with `AsRef<[T]>`.

/// Helper trait for retrieving top-level objects by a universal identifier.
pub trait Get<T> {
/// Retrieves a single value at the given index.
fn get(&self, id: Index<T>) -> Option<&T>;
}

/// Represents an offset into an array of type `T` owned by the root glTF object.
/// Represents an offset into a vector of type `T` owned by the root glTF object.
///
/// This type may be used with the following functions:
///
/// * [`Root::get()`] to retrieve objects from [`Root`].
/// * [`Root::push()`] to add new objects to [`Root`].
pub struct Index<T>(u32, marker::PhantomData<fn() -> T>);

impl<T> Index<T> {
/// Given a vector of glTF objects, call [`Vec::push()`] to insert it into the vector,
/// then return an [`Index`] for it.
///
/// This allows you to easily obtain [`Index`] values with the correct index and type when
/// creating a glTF asset. Note that for [`Root`], you can call [`Root::push()`] without
/// needing to retrieve the correct vector first.
///
/// # Panics
///
/// Panics if the vector has [`u32::MAX`] or more elements, in which case an `Index` cannot be
/// created.
pub fn push(vec: &mut Vec<T>, value: T) -> Index<T> {
let len = vec.len();
let Ok(index): Result<u32, _> = len.try_into() else {
panic!(
"glTF vector of {ty} has {len} elements, which exceeds the Index limit",
ty = std::any::type_name::<T>(),
);
};

vec.push(value);
Index::new(index)
}
}

/// The root object of a glTF 2.0 asset.
#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
pub struct Root {
Expand Down Expand Up @@ -127,6 +160,26 @@ impl Root {
(self as &dyn Get<T>).get(index)
}

/// Insert the given value into this (as via [`Vec::push()`]), then return the [`Index`] to it.
///
/// This allows you to easily obtain [`Index`] values with the correct index and type when
/// creating a glTF asset.
///
/// If you have a mutable borrow conflict when using this method, consider using the more
/// explicit [`Index::push()`] method, passing it only the necessary vector.
///
/// # Panics
///
/// Panics if there are already [`u32::MAX`] or more elements of this type,
/// in which case an `Index` cannot be created.
#[track_caller]
pub fn push<T>(&mut self, value: T) -> Index<T>
where
Self: AsMut<Vec<T>>,
{
Index::push(self.as_mut(), value)
}

/// Deserialize from a JSON string slice.
#[allow(clippy::should_implement_trait)]
pub fn from_str(str_: &str) -> Result<Self, Error> {
Expand Down Expand Up @@ -299,6 +352,16 @@ macro_rules! impl_get {
self.$field.get(index.value())
}
}
impl AsRef<[$ty]> for Root {
fn as_ref(&self) -> &[$ty] {
&self.$field
}
}
impl AsMut<Vec<$ty>> for Root {
fn as_mut(&mut self) -> &mut Vec<$ty> {
&mut self.$field
}
}
};
}

Expand Down Expand Up @@ -345,4 +408,29 @@ mod tests {
Index<Material>: Send + Sync,
{
}

#[test]
fn index_push() {
let some_object = "hello";

let mut vec = Vec::new();
assert_eq!(Index::push(&mut vec, some_object), Index::new(0));
assert_eq!(Index::push(&mut vec, some_object), Index::new(1));
}

#[test]
fn root_push() {
let some_object = Buffer {
byte_length: validation::USize64(1),
#[cfg(feature = "names")]
name: None,
uri: None,
extensions: None,
extras: Default::default(),
};

let mut root = Root::default();
assert_eq!(root.push(some_object.clone()), Index::new(0));
assert_eq!(root.push(some_object), Index::new(1));
}
}

0 comments on commit d5b04fc

Please sign in to comment.