Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ gltf-json/Cargo.lock
gltf-utils/target/
gltf-utils/Cargo.lock
*.bk
glTF-Sample-Models
glTF-Sample-Models
glTF-Sample-Assets
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ KHR_materials_variants = ["gltf-json/KHR_materials_variants"]
KHR_materials_volume = ["gltf-json/KHR_materials_volume"]
KHR_materials_specular = ["gltf-json/KHR_materials_specular"]
KHR_materials_emissive_strength = ["gltf-json/KHR_materials_emissive_strength"]
KHR_mesh_quantization = ["gltf-json/KHR_mesh_quantization"]
EXT_texture_webp = ["gltf-json/EXT_texture_webp", "image/webp"]
guess_mime_type = []

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ cargo run --example gltf-tree path/to/asset.gltf

### Tests

Running tests locally requires to clone the [`glTF-Sample-Models`](https://github.com/KhronosGroup/glTF-Sample-Models) repository first.
Running tests locally requires to clone the [`glTF-Sample-Assets`](https://github.com/KhronosGroup/glTF-Sample-Assets) repository first.

```sh
git clone https://github.com/KhronosGroup/glTF-Sample-Models.git
git clone https://github.com/KhronosGroup/glTF-Sample-Assets.git
```
1 change: 1 addition & 0 deletions gltf-json/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ KHR_materials_variants = []
KHR_materials_volume = []
KHR_texture_transform = []
KHR_materials_emissive_strength = []
KHR_mesh_quantization = []
EXT_texture_webp = []
3 changes: 3 additions & 0 deletions gltf-json/src/extensions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ pub const ENABLED_EXTENSIONS: &[&str] = &[
"KHR_materials_ior",
#[cfg(feature = "KHR_materials_emissive_strength")]
"KHR_materials_emissive_strength",
#[cfg(feature = "KHR_mesh_quantization")]
"KHR_mesh_quantization",
// Allowlisted texture extensions. Processing is delegated to the user.
#[cfg(feature = "allow_empty_texture")]
"KHR_texture_basisu",
Expand All @@ -70,5 +72,6 @@ pub const SUPPORTED_EXTENSIONS: &[&str] = &[
"KHR_materials_transmission",
"KHR_materials_ior",
"KHR_materials_emissive_strength",
"KHR_mesh_quantization",
"EXT_texture_webp",
];
34 changes: 21 additions & 13 deletions gltf-json/src/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ mod tests {
};

let mut errors = Vec::new();
root.validate(&root, Path::new, &mut |path, error| {
root.validate(&root, Path::new, &mut |path: &dyn Fn() -> Path, error| {
errors.push((path(), error));
});

Expand All @@ -486,17 +486,25 @@ mod tests {
assert_eq!(*error, Error::Unsupported);
}

root.extensions_required = vec!["KHR_mesh_quantization".to_owned()];
errors.clear();
root.validate(&root, Path::new, &mut |path, error| {
errors.push((path(), error));
});
assert_eq!(1, errors.len());
let (path, error) = errors.get(0).unwrap();
assert_eq!(
path.as_str(),
"extensionsRequired[0] = \"KHR_mesh_quantization\""
);
assert_eq!(*error, Error::Unsupported);
#[cfg(feature = "KHR_mesh_quantization")]
{
assert!(errors.is_empty());
}

#[cfg(not(feature = "KHR_mesh_quantization"))]
{
root.extensions_required = vec!["KHR_mesh_quantization".to_owned()];
errors.clear();
root.validate(&root, Path::new, &mut |path, error| {
errors.push((path(), error));
});
assert_eq!(1, errors.len());
let (path, error) = errors.get(0).unwrap();
assert_eq!(
path.as_str(),
"extensionsRequired[0] = \"KHR_mesh_quantization\""
);
assert_eq!(*error, Error::Unsupported);
}
}
}
1 change: 0 additions & 1 deletion gltf-json/src/texture.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::extensions::texture;
use crate::validation::{Checked, Validate};
use crate::{extensions, image, Extras, Index};
use gltf_derive::Validate;
Expand Down
88 changes: 79 additions & 9 deletions src/mesh/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,17 @@
//! for primitive in mesh.primitives() {
//! println!("- Primitive #{}", primitive.index());
//! let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()]));
//! if let Some(iter) = reader.read_positions() {
//! for vertex_position in iter {
//! println!("{:?}", vertex_position);
//! }
//!
//! #[cfg(not(feature="KHR_mesh_quantization"))]
//! let positions = reader.read_positions();
//! #[cfg(feature="KHR_mesh_quantization")]
//! let positions = match reader.read_positions() {
//! Some(gltf::mesh::util::ReadPositions::F32(iter)) => iter,
//! _ => unreachable!(),
//! };
//!
//! for vertex_position in positions {
//! println!("{:?}", vertex_position);
//! }
//! }
//! }
Expand Down Expand Up @@ -336,21 +343,84 @@ where
pub fn read_positions(&self) -> Option<util::ReadPositions<'s>> {
self.primitive
.get(&Semantic::Positions)
.and_then(|accessor| accessor::Iter::new(accessor, self.get_buffer_data.clone()))
.and_then(|accessor| {
#[cfg(feature = "KHR_mesh_quantization")]
match accessor.data_type() {
json::accessor::ComponentType::I8 => {
accessor::Iter::new(accessor, self.get_buffer_data.clone())
.map(|iter| util::ReadPositions::I8(iter))
}
json::accessor::ComponentType::U8 => {
accessor::Iter::new(accessor, self.get_buffer_data.clone())
.map(|iter| util::ReadPositions::U8(iter))
}
json::accessor::ComponentType::I16 => {
accessor::Iter::new(accessor, self.get_buffer_data.clone())
.map(|iter| util::ReadPositions::I16(iter))
}
json::accessor::ComponentType::U16 => {
accessor::Iter::new(accessor, self.get_buffer_data.clone())
.map(|iter| util::ReadPositions::U16(iter))
}
json::accessor::ComponentType::F32 => {
accessor::Iter::new(accessor, self.get_buffer_data.clone())
.map(|iter| util::ReadPositions::F32(iter))
}
_ => None,
}
#[cfg(not(feature = "KHR_mesh_quantization"))]
accessor::Iter::new(accessor, self.get_buffer_data.clone())
})
}

/// Visits the vertex normals of a primitive.
pub fn read_normals(&self) -> Option<util::ReadNormals<'s>> {
self.primitive
.get(&Semantic::Normals)
.and_then(|accessor| accessor::Iter::new(accessor, self.get_buffer_data.clone()))
self.primitive.get(&Semantic::Normals).and_then(|accessor| {
#[cfg(feature = "KHR_mesh_quantization")]
match accessor.data_type() {
json::accessor::ComponentType::I8 => {
accessor::Iter::new(accessor, self.get_buffer_data.clone())
.map(|iter| util::ReadNormals::I8(iter))
}
json::accessor::ComponentType::I16 => {
accessor::Iter::new(accessor, self.get_buffer_data.clone())
.map(|iter| util::ReadNormals::I16(iter))
}
json::accessor::ComponentType::F32 => {
accessor::Iter::new(accessor, self.get_buffer_data.clone())
.map(|iter| util::ReadNormals::F32(iter))
}
_ => None,
}
#[cfg(not(feature = "KHR_mesh_quantization"))]
accessor::Iter::new(accessor, self.get_buffer_data.clone())
})
}

/// Visits the vertex tangents of a primitive.
pub fn read_tangents(&self) -> Option<util::ReadTangents<'s>> {
self.primitive
.get(&Semantic::Tangents)
.and_then(|accessor| accessor::Iter::new(accessor, self.get_buffer_data.clone()))
.and_then(|accessor| {
#[cfg(feature = "KHR_mesh_quantization")]
match accessor.data_type() {
json::accessor::ComponentType::I8 => {
accessor::Iter::new(accessor, self.get_buffer_data.clone())
.map(|iter| util::ReadTangents::I8(iter))
}
json::accessor::ComponentType::I16 => {
accessor::Iter::new(accessor, self.get_buffer_data.clone())
.map(|iter| util::ReadTangents::I16(iter))
}
json::accessor::ComponentType::F32 => {
accessor::Iter::new(accessor, self.get_buffer_data.clone())
.map(|iter| util::ReadTangents::F32(iter))
}
_ => None,
}
#[cfg(not(feature = "KHR_mesh_quantization"))]
accessor::Iter::new(accessor, self.get_buffer_data.clone())
})
}

/// Visits the vertex colors of a primitive.
Expand Down
55 changes: 55 additions & 0 deletions src/mesh/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,70 @@ use crate::accessor::Iter;
use crate::Buffer;

/// XYZ vertex positions of type `[f32; 3]`.
#[cfg(not(feature = "KHR_mesh_quantization"))]
pub type ReadPositions<'a> = Iter<'a, [f32; 3]>;

/// XYZ vertex positions as potentially quantized data
#[cfg(feature = "KHR_mesh_quantization")]
pub enum ReadPositions<'a> {
/// Position data of type I8
I8(Iter<'a, [i8; 3]>),
/// Position data of type U8
U8(Iter<'a, [u8; 3]>),
/// Position data of type I16
I16(Iter<'a, [i16; 3]>),
/// Position data of type U16
U16(Iter<'a, [u16; 3]>),
/// Position data of type F32
F32(Iter<'a, [f32; 3]>),
}

#[cfg(feature = "KHR_mesh_quantization")]
impl<'a> ReadPositions<'a> {
/// Returns size hint of internal iterator
pub fn size_hint(&self) -> (usize, Option<usize>) {
match self {
Self::I8(iter) => iter.size_hint(),
Self::U8(iter) => iter.size_hint(),
Self::I16(iter) => iter.size_hint(),
Self::U16(iter) => iter.size_hint(),
Self::F32(iter) => iter.size_hint(),
}
}
}

/// XYZ vertex normals of type `[f32; 3]`.
#[cfg(not(feature = "KHR_mesh_quantization"))]
pub type ReadNormals<'a> = Iter<'a, [f32; 3]>;

/// XYZ vertex normals as potentially quantized data
#[cfg(feature = "KHR_mesh_quantization")]
pub enum ReadNormals<'a> {
/// Position data of type I8
I8(Iter<'a, [i8; 3]>),
/// Position data of type I16
I16(Iter<'a, [i16; 3]>),
/// Position data of type F32
F32(Iter<'a, [f32; 3]>),
}

/// XYZW vertex tangents of type `[f32; 4]` where the `w` component is a
/// sign value (-1 or +1) indicating the handedness of the tangent basis.
#[cfg(not(feature = "KHR_mesh_quantization"))]
pub type ReadTangents<'a> = Iter<'a, [f32; 4]>;

/// XYZW vertex tangents of potentially quantized data where the `w` component is a
/// sign value (-1 or +1) indicating the handedness of the tangent basis.
#[cfg(feature = "KHR_mesh_quantization")]
pub enum ReadTangents<'a> {
/// Position data of type I8
I8(Iter<'a, [i8; 3]>),
/// Position data of type I16
I16(Iter<'a, [i16; 3]>),
/// Position data of type F32
F32(Iter<'a, [f32; 3]>),
}

/// XYZ vertex position displacements of type `[f32; 3]`.
pub type ReadPositionDisplacements<'a> = Iter<'a, [f32; 3]>;

Expand Down
4 changes: 3 additions & 1 deletion tests/import_sample_models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ fn check_import_result(
Err(gltf::Error::Validation(errors)) => {
assert!(errors
.iter()
.all(|(_path, error)| *error == Error::Unsupported));
.all(|(_path, error)| *error == Error::Unsupported),
"errors: {errors:?}",
);
println!("skipped");
}
Err(otherwise) => {
Expand Down
19 changes: 19 additions & 0 deletions tests/test_wrapper.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::io::Read;
use std::{fs, io};

#[cfg(feature="KHR_mesh_quantization")]
use gltf::mesh::util::ReadPositions;
use gltf::mesh::Bounds;

#[test]
Expand Down Expand Up @@ -37,8 +39,16 @@ fn test_sparse_accessor_with_base_buffer_view_yield_exact_size_hints() {
let primitive = mesh.primitives().next().unwrap();
let reader = primitive
.reader(|buffer: gltf::Buffer| buffers.get(buffer.index()).map(|data| &data.0[..]));

#[cfg(not(feature = "KHR_mesh_quantization"))]
let mut positions = reader.read_positions().unwrap();

#[cfg(feature = "KHR_mesh_quantization")]
let mut positions = match reader.read_positions().unwrap() {
ReadPositions::F32(iter) => iter,
_ => unreachable!("Meshes in gltf sample repo should not use quantization"),
};

const EXPECTED_POSITION_COUNT: usize = 14;
for i in (0..=EXPECTED_POSITION_COUNT).rev() {
assert_eq!(positions.size_hint(), (i, Some(i)));
Expand All @@ -54,8 +64,17 @@ fn test_sparse_accessor_with_base_buffer_view_yield_all_values() {
let primitive = mesh.primitives().next().unwrap();
let reader = primitive
.reader(|buffer: gltf::Buffer| buffers.get(buffer.index()).map(|data| &data.0[..]));

#[cfg(not(feature = "KHR_mesh_quantization"))]
let positions: Vec<[f32; 3]> = reader.read_positions().unwrap().collect::<Vec<_>>();

#[cfg(feature = "KHR_mesh_quantization")]
let positions = match reader.read_positions().unwrap() {
ReadPositions::F32(iter) => iter,
_ => unreachable!("Meshes in gltf sample repo should not use quantization"),
}
.collect::<Vec<_>>();

const EXPECTED_POSITIONS: [[f32; 3]; 14] = [
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
Expand Down