Write a very experimental gltf loader
This commit is contained in:
parent
dddf6123c4
commit
e76ca1ec50
|
@ -7,7 +7,10 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
|
base64 = "0.21.4"
|
||||||
|
glam = "0.24.1"
|
||||||
gltf = "1.3.0"
|
gltf = "1.3.0"
|
||||||
image = "0.24.7"
|
image = "0.24.7"
|
||||||
|
percent-encoding = "2.3.0"
|
||||||
thiserror = "1.0.48"
|
thiserror = "1.0.48"
|
||||||
uuid = { version = "1.4.1", features = ["v4"] }
|
uuid = { version = "1.4.1", features = ["v4"] }
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
use crate::{ResourceLoader, LoaderError, Mesh};
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use base64::Engine;
|
||||||
|
use glam::Vec3;
|
||||||
|
|
||||||
|
use crate::{ResourceLoader, LoaderError, Mesh, Vertex, Model};
|
||||||
|
|
||||||
impl From<gltf::Error> for LoaderError {
|
impl From<gltf::Error> for LoaderError {
|
||||||
fn from(value: gltf::Error) -> Self {
|
fn from(value: gltf::Error) -> Self {
|
||||||
|
@ -10,40 +15,56 @@ impl From<gltf::Error> for LoaderError {
|
||||||
pub struct ModelLoader;
|
pub struct ModelLoader;
|
||||||
|
|
||||||
impl ModelLoader {
|
impl ModelLoader {
|
||||||
fn process_node(&self, views: Vec<gltf::buffer::View<'_>>, node: gltf::Node<'_>) -> Vec<Mesh> {
|
fn parse_uri(uri: &str) -> Option<Vec<u8>> {
|
||||||
|
let uri = uri.strip_prefix("data")?;
|
||||||
|
let (mime, data) = uri.split_once(",")?;
|
||||||
|
|
||||||
|
let (_mime, is_base64) = match mime.strip_suffix(";base64") {
|
||||||
|
Some(mime) => (mime, true),
|
||||||
|
None => (mime, false),
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_base64 {
|
||||||
|
base64::engine::general_purpose::STANDARD.decode(data).ok()
|
||||||
|
} else {
|
||||||
|
Some(data.as_bytes().to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_node(&self, buffers: &Vec<Vec<u8>>, node: gltf::Node<'_>) -> Vec<Mesh> {
|
||||||
let mut meshes = vec![];
|
let mut meshes = vec![];
|
||||||
if let Some(mesh) = node.mesh() {
|
if let Some(mesh) = node.mesh() {
|
||||||
for prim in mesh.primitives() {
|
for prim in mesh.primitives() {
|
||||||
let pos_accessor = prim.get(&gltf::Semantic::Positions).unwrap();
|
let pos_accessor = prim.get(&gltf::Semantic::Positions).unwrap();
|
||||||
|
//assert_eq!(pos_accessor.dimensions(), gltf::accessor::Dimensions::Vec3); // TODO: dont do this
|
||||||
|
|
||||||
|
let reader = prim.reader(|buf| Some(buffers[buf.index()].as_slice()));
|
||||||
|
let pos: Vec<Vec3> = reader.read_positions()
|
||||||
|
.unwrap()
|
||||||
|
.map(|pos| Vec3::new(pos[0], pos[1], pos[2]))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let indices: Option<Vec<u32>> = reader.read_indices()
|
||||||
|
.map(|i| match i {
|
||||||
|
gltf::mesh::util::ReadIndices::U8(i) => i.map(|i| i as u32).collect(),
|
||||||
|
gltf::mesh::util::ReadIndices::U16(i) => i.map(|i| i as u32).collect(),
|
||||||
|
gltf::mesh::util::ReadIndices::U32(i) => i.collect(),
|
||||||
|
});
|
||||||
|
|
||||||
let mut new_mesh = Mesh::default();
|
let mut new_mesh = Mesh::default();
|
||||||
|
new_mesh.vertices = pos.into_iter().map(|p| Vertex::new(p, glam::Vec2::new(0.0, 0.0))).collect();
|
||||||
|
new_mesh.indices = indices;
|
||||||
|
|
||||||
assert_eq!(pos_accessor.dimensions(), gltf::accessor::Dimensions::Vec3); // TODO: dont do this
|
meshes.push(new_mesh);
|
||||||
|
|
||||||
let view = pos_accessor.view().unwrap(); // TODO: handle sparse Accessor
|
|
||||||
let stride = view.stride().unwrap_or(0); // if stride is None, its tightly packed
|
|
||||||
let offset = view.offset();
|
|
||||||
let buffer = view.buffer();
|
|
||||||
let buf_len = buffer.length();
|
|
||||||
|
|
||||||
let view = views.iter().next().unwrap();
|
|
||||||
//gltf.views()
|
|
||||||
|
|
||||||
//vertices.data_type() == gltf::
|
|
||||||
//new_mesh.vertices = vertices.;
|
|
||||||
|
|
||||||
if let Some(indicies) = prim.indices() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//meshes.push(mesh);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for child in node.children() {
|
for child in node.children() {
|
||||||
let mut child_meshes = self.process_node(node);
|
let mut child_meshes = self.process_node(buffers, child);
|
||||||
meshes.append(&mut child_meshes);
|
meshes.append(&mut child_meshes);
|
||||||
}
|
}
|
||||||
|
|
||||||
todo!()
|
meshes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,14 +83,44 @@ impl ResourceLoader for ModelLoader {
|
||||||
|
|
||||||
let gltf = gltf::Gltf::open(path)?;
|
let gltf = gltf::Gltf::open(path)?;
|
||||||
|
|
||||||
let buffers: Vec<gltf::Buffer<'_>> = gltf.buffers().collect();
|
//let buffers: Vec<gltf::Buffer<'_>> = gltf.buffers().collect();
|
||||||
|
|
||||||
|
let buffers: Vec<Vec<u8>> = gltf.buffers().map(|b| match b.source() {
|
||||||
|
gltf::buffer::Source::Bin => gltf.blob.as_deref().map(|v| v.to_vec()),
|
||||||
|
gltf::buffer::Source::Uri(uri) => ModelLoader::parse_uri(uri),
|
||||||
|
}).flatten().collect();
|
||||||
|
|
||||||
// TODO: Read in multiple scenes
|
// TODO: Read in multiple scenes
|
||||||
let scene = gltf.scenes().next().unwrap();
|
let scene = gltf.scenes().next().unwrap();
|
||||||
for node in scene.nodes() {
|
|
||||||
|
|
||||||
}
|
let meshes: Vec<Mesh> = scene.nodes()
|
||||||
|
.map(|node| self.process_node(&buffers, node))
|
||||||
|
.flatten().collect();
|
||||||
|
|
||||||
todo!()
|
Ok(Arc::new(Model::new(meshes)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::ResourceLoader;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn test_file_path(path: &str) -> String {
|
||||||
|
let manifest = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||||
|
|
||||||
|
format!("{manifest}/test_files/gltf/{path}")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_loading() {
|
||||||
|
let path = test_file_path("test-embedded.gltf");
|
||||||
|
|
||||||
|
let loader = ModelLoader::default();
|
||||||
|
let model = loader.load(&path).unwrap();
|
||||||
|
let model = Arc::downcast::<Model>(model.as_arc_any()).unwrap();
|
||||||
|
let mesh = &model.meshes[0];
|
||||||
|
assert!(mesh.vertices.len() > 0);
|
||||||
|
assert!(mesh.indices.clone().unwrap().len() > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,17 +2,34 @@
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct Vertex {
|
pub struct Vertex {
|
||||||
pub position: [f32; 3],
|
pub position: glam::Vec3,
|
||||||
pub tex_coords: [f32; 2]
|
pub tex_coords: glam::Vec2
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vertex {
|
||||||
|
pub fn new(position: glam::Vec3, tex_coords: glam::Vec2) -> Self {
|
||||||
|
Self {
|
||||||
|
position,
|
||||||
|
tex_coords,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct Mesh {
|
pub struct Mesh {
|
||||||
pub vertices: Vec<Vertex>,
|
pub vertices: Vec<Vertex>,
|
||||||
pub indices: Option<Vec<u16>>,
|
pub indices: Option<Vec<u32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
pub mesh: Mesh,
|
pub meshes: Vec<Mesh>,
|
||||||
//pub material
|
//pub material
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Model {
|
||||||
|
pub fn new(meshes: Vec<Mesh>) -> Self {
|
||||||
|
Self {
|
||||||
|
meshes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,121 @@
|
||||||
|
{
|
||||||
|
"asset":{
|
||||||
|
"generator":"Khronos glTF Blender I/O v3.6.5",
|
||||||
|
"version":"2.0"
|
||||||
|
},
|
||||||
|
"scene":0,
|
||||||
|
"scenes":[
|
||||||
|
{
|
||||||
|
"name":"Scene",
|
||||||
|
"nodes":[
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nodes":[
|
||||||
|
{
|
||||||
|
"mesh":0,
|
||||||
|
"name":"Cube"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"materials":[
|
||||||
|
{
|
||||||
|
"doubleSided":true,
|
||||||
|
"name":"Material",
|
||||||
|
"pbrMetallicRoughness":{
|
||||||
|
"baseColorFactor":[
|
||||||
|
0.800000011920929,
|
||||||
|
0.800000011920929,
|
||||||
|
0.800000011920929,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"metallicFactor":0,
|
||||||
|
"roughnessFactor":0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meshes":[
|
||||||
|
{
|
||||||
|
"name":"Cube",
|
||||||
|
"primitives":[
|
||||||
|
{
|
||||||
|
"attributes":{
|
||||||
|
"POSITION":0,
|
||||||
|
"TEXCOORD_0":1,
|
||||||
|
"NORMAL":2
|
||||||
|
},
|
||||||
|
"indices":3,
|
||||||
|
"material":0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessors":[
|
||||||
|
{
|
||||||
|
"bufferView":0,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":24,
|
||||||
|
"max":[
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"min":[
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
|
-1
|
||||||
|
],
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":1,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":24,
|
||||||
|
"type":"VEC2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":2,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":24,
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":3,
|
||||||
|
"componentType":5123,
|
||||||
|
"count":36,
|
||||||
|
"type":"SCALAR"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bufferViews":[
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":288,
|
||||||
|
"byteOffset":0,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":192,
|
||||||
|
"byteOffset":288,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":288,
|
||||||
|
"byteOffset":480,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":72,
|
||||||
|
"byteOffset":768,
|
||||||
|
"target":34963
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buffers":[
|
||||||
|
{
|
||||||
|
"byteLength":840,
|
||||||
|
"uri":"data:application/octet-stream;base64,AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AAAAPgAAAD8AAMA+AABAPwAAwD4AAEA/AAAgPwAAAAAAACA/AACAPwAAYD8AAIA+AAAAPgAAgD4AAMA+AAAAAAAAwD4AAIA/AAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAQAOABQAAQAUAAcACgAGABIACgASABYAFwATAAwAFwAMABAADwADAAkADwAJABUABQACAAgABQAIAAsAEQANAAAAEQAAAAQA"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,121 @@
|
||||||
|
{
|
||||||
|
"asset":{
|
||||||
|
"generator":"Khronos glTF Blender I/O v3.6.5",
|
||||||
|
"version":"2.0"
|
||||||
|
},
|
||||||
|
"scene":0,
|
||||||
|
"scenes":[
|
||||||
|
{
|
||||||
|
"name":"Scene",
|
||||||
|
"nodes":[
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nodes":[
|
||||||
|
{
|
||||||
|
"mesh":0,
|
||||||
|
"name":"Cube"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"materials":[
|
||||||
|
{
|
||||||
|
"doubleSided":true,
|
||||||
|
"name":"Material",
|
||||||
|
"pbrMetallicRoughness":{
|
||||||
|
"baseColorFactor":[
|
||||||
|
0.800000011920929,
|
||||||
|
0.800000011920929,
|
||||||
|
0.800000011920929,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"metallicFactor":0,
|
||||||
|
"roughnessFactor":0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meshes":[
|
||||||
|
{
|
||||||
|
"name":"Cube",
|
||||||
|
"primitives":[
|
||||||
|
{
|
||||||
|
"attributes":{
|
||||||
|
"POSITION":0,
|
||||||
|
"TEXCOORD_0":1,
|
||||||
|
"NORMAL":2
|
||||||
|
},
|
||||||
|
"indices":3,
|
||||||
|
"material":0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accessors":[
|
||||||
|
{
|
||||||
|
"bufferView":0,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":24,
|
||||||
|
"max":[
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"min":[
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
|
-1
|
||||||
|
],
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":1,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":24,
|
||||||
|
"type":"VEC2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":2,
|
||||||
|
"componentType":5126,
|
||||||
|
"count":24,
|
||||||
|
"type":"VEC3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bufferView":3,
|
||||||
|
"componentType":5123,
|
||||||
|
"count":36,
|
||||||
|
"type":"SCALAR"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bufferViews":[
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":288,
|
||||||
|
"byteOffset":0,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":192,
|
||||||
|
"byteOffset":288,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":288,
|
||||||
|
"byteOffset":480,
|
||||||
|
"target":34962
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buffer":0,
|
||||||
|
"byteLength":72,
|
||||||
|
"byteOffset":768,
|
||||||
|
"target":34963
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buffers":[
|
||||||
|
{
|
||||||
|
"byteLength":840,
|
||||||
|
"uri":"test-sep.bin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue