diff --git a/glium_platform/src/assets.rs b/glium_platform/src/assets.rs index 1b3b4e1..c8cb1d4 100644 --- a/glium_platform/src/assets.rs +++ b/glium_platform/src/assets.rs @@ -12,7 +12,7 @@ use raidillon_assets::model_manager::ModelID; /// Glium platform asset manager implementation. pub struct GliumAssetManager { - pub models: HashMap, + pub models: HashMap>, facade: Box, } @@ -28,8 +28,8 @@ impl GliumAssetManager { impl ModelManager for GliumAssetManager { fn load_gltf(&mut self, id: ModelID, path: &Path) { - let model = load_gltf(path, self.facade.as_ref()).unwrap(); - self.models.insert(id, model); + let models = load_gltf(path, self.facade.as_ref()).unwrap(); + self.models.insert(id, models); } fn unload_model(&mut self, id: ModelID) { @@ -48,6 +48,6 @@ impl ModelManager for GliumAssetManager { // } fn get_model(&self, id: &ModelID) -> Option<&dyn Any> { - self.models.get(id).map(|model| model as &dyn Any) + self.models.get(id).map(|models| models as &dyn Any) } } diff --git a/glium_platform/src/gltf_loader.rs b/glium_platform/src/gltf_loader.rs index e41bbfb..fe4ec37 100644 --- a/glium_platform/src/gltf_loader.rs +++ b/glium_platform/src/gltf_loader.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use glium::{backend::Facade, IndexBuffer, VertexBuffer}; use glium::index::PrimitiveType; use std::{fmt::Debug, path::Path}; @@ -8,31 +8,16 @@ use glium::uniforms::{SamplerWrapFunction, MinifySamplerFilter, MagnifySamplerFi use gltf::image::Format as GltfFormat; use glam::Vec2; -/// Load a glTF 2.0 file from disk and upload the first primitive to the GPU. -pub fn load_gltf

(path: P, facade: &dyn Facade) -> Result +/// Load a glTF 2.0 file from disk and upload all primitives to the GPU. +/// +/// Returns one [`Model`] per glTF primitive (across all meshes). +pub fn load_gltf

(path: P, facade: &dyn Facade) -> Result> where P: AsRef + Debug, { // -- parse the asset & bring buffer blobs into memory -- let (doc, buffers, images) = gltf::import(path.as_ref()).context("failed to import glTF file")?; - // -- grab the very first mesh / primitive -- - let mesh = doc.meshes().next().context("glTF has no meshes")?; - let primitive = mesh.primitives().next().context("mesh has no primitives")?; - - // ---------- MATERIAL ---------- - let mut mat = Material::default(); - - let mat_idx = primitive.material().index().context("primitive has no material")?; - let material = doc.materials().nth(mat_idx).unwrap(); - let pbr = material.pbr_metallic_roughness(); - - // Factors -------------------------------------------------- - mat.base_color_factor = pbr.base_color_factor(); - mat.metal_factor = pbr.metallic_factor(); - mat.roughness_factor = pbr.roughness_factor(); - mat.emissive_factor = material.emissive_factor(); - // Helper to update sampler settings from glTF sampler fn update_sampler(mat: &mut Material, t: &gltf::texture::Texture<'_>) { let sampler_info = t.sampler(); @@ -64,64 +49,92 @@ where } } - // Base-color texture (sRGB) - if let Some(info) = pbr.base_color_texture() { - update_sampler(&mut mat, &info.texture()); - let view = info.texture().source().index(); - mat.base_color = Some(glium_srgb_texture(facade, &images[view])?); - } + let mut out: Vec = Vec::new(); - // Metallic-Roughness (linear) - if let Some(info) = pbr.metallic_roughness_texture() { - update_sampler(&mut mat, &info.texture()); - let view = info.texture().source().index(); - mat.metallic_roughness = Some(glium_linear_texture(facade, &images[view])?); - } + for mesh in doc.meshes() { + for primitive in mesh.primitives() { + // ---------- MATERIAL ---------- + let mut mat = Material::default(); + let material = primitive.material(); + let pbr = material.pbr_metallic_roughness(); - // Normal map (linear) - if let Some(info) = primitive.material().normal_texture() { - update_sampler(&mut mat, &info.texture()); - let view = info.texture().source().index(); - mat.normal = Some(glium_linear_texture(facade, &images[view])?); - } + // Factors -------------------------------------------------- + mat.base_color_factor = pbr.base_color_factor(); + mat.metal_factor = pbr.metallic_factor(); + mat.roughness_factor = pbr.roughness_factor(); + mat.emissive_factor = material.emissive_factor(); - // Occlusion (linear) - if let Some(info) = primitive.material().occlusion_texture() { - update_sampler(&mut mat, &info.texture()); - let view = info.texture().source().index(); - mat.occlusion = Some(glium_linear_texture(facade, &images[view])?); - } + // Base-color texture (sRGB) + if let Some(info) = pbr.base_color_texture() { + update_sampler(&mut mat, &info.texture()); + let view = info.texture().source().index(); + mat.base_color = Some(glium_srgb_texture(facade, &images[view])?); + } - // Emissive (sRGB) - if let Some(info) = primitive.material().emissive_texture() { - update_sampler(&mut mat, &info.texture()); - let view = info.texture().source().index(); - mat.emissive = Some(glium_srgb_texture(facade, &images[view])?); - } + // Metallic-Roughness (linear) + if let Some(info) = pbr.metallic_roughness_texture() { + update_sampler(&mut mat, &info.texture()); + let view = info.texture().source().index(); + mat.metallic_roughness = Some(glium_linear_texture(facade, &images[view])?); + } - // KHR_texture_transform - if let Some(tex) = pbr.base_color_texture() { - if let Some(xform) = tex.texture_transform() { - mat.uv_offset = Vec2::new(xform.offset()[0], xform.offset()[1]); - mat.uv_scale = Vec2::new(xform.scale()[0], xform.scale()[1]); + // Normal map (linear) + if let Some(info) = material.normal_texture() { + update_sampler(&mut mat, &info.texture()); + let view = info.texture().source().index(); + mat.normal = Some(glium_linear_texture(facade, &images[view])?); + } + + // Occlusion (linear) + if let Some(info) = material.occlusion_texture() { + update_sampler(&mut mat, &info.texture()); + let view = info.texture().source().index(); + mat.occlusion = Some(glium_linear_texture(facade, &images[view])?); + } + + // Emissive (sRGB) + if let Some(info) = material.emissive_texture() { + update_sampler(&mut mat, &info.texture()); + let view = info.texture().source().index(); + mat.emissive = Some(glium_srgb_texture(facade, &images[view])?); + } + + // KHR_texture_transform (base color only, for now) + if let Some(tex) = pbr.base_color_texture() { + if let Some(xform) = tex.texture_transform() { + mat.uv_offset = Vec2::new(xform.offset()[0], xform.offset()[1]); + mat.uv_scale = Vec2::new(xform.scale()[0], xform.scale()[1]); + } + } + + // ---- Vertex/index data ---- + let reader = primitive.reader(|buf| Some(&buffers[buf.index()].0)); + + let positions: Vec<[f32; 3]> = reader.read_positions().context("missing POSITION")?.collect(); + let normals: Vec<[f32; 3]> = reader.read_normals().context("missing NORMAL")?.collect(); + let tex_coords: Vec<[f32; 2]> = reader + .read_tex_coords(0) + .map(|tc| tc.into_f32().collect()) + .unwrap_or_else(|| vec![[0.0, 0.0]; positions.len()]); + let indices: Vec = reader.read_indices().context("missing indices")?.into_u32().collect(); + + // Interleave + let vertices: Vec = (0..positions.len()) + .map(|i| Vertex { position: positions[i], normal: normals[i], tex_coords: tex_coords[i] }) + .collect(); + + let vbuf = VertexBuffer::immutable(facade, &vertices)?; + let ibuf = IndexBuffer::immutable(facade, PrimitiveType::TrianglesList, &indices)?; + + out.push(Model { mesh: Mesh { vbuf, ibuf }, material: mat }); } } - // ---- Vertex/index data ---- - let reader = primitive.reader(|buf| Some(&buffers[buf.index()].0)); + if out.is_empty() { + bail!("glTF has no mesh primitives"); + } - let positions: Vec<[f32; 3]> = reader.read_positions().context("missing POSITION")?.collect(); - let normals: Vec<[f32; 3]> = reader.read_normals().context("missing NORMAL")?.collect(); - let tex_coords: Vec<[f32; 2]> = reader.read_tex_coords(0).map(|tc| tc.into_f32().collect()).unwrap_or_else(|| vec![[0.0, 0.0]; positions.len()]); - let indices: Vec = reader.read_indices().context("missing indices")?.into_u32().collect(); - - // Interleave - let vertices: Vec = (0..positions.len()).map(|i| Vertex { position: positions[i], normal: normals[i], tex_coords: tex_coords[i] }).collect(); - - let vbuf = VertexBuffer::immutable(facade, &vertices)?; - let ibuf = IndexBuffer ::immutable(facade, PrimitiveType::TrianglesList, &indices)?; - - Ok(Model { mesh: Mesh { vbuf, ibuf }, material: mat }) + Ok(out) } /// Linear-space texture (RGBA8) from glTF image data. diff --git a/glium_platform/src/render/basic.rs b/glium_platform/src/render/basic.rs index 3ed144b..6553cc0 100644 --- a/glium_platform/src/render/basic.rs +++ b/glium_platform/src/render/basic.rs @@ -73,41 +73,43 @@ impl RenderingSystem for BasicMeshRenderingSystem { _ => continue, }; - let model = match model.downcast_ref::() { - Some(model) => model, + let models = match model.downcast_ref::>() { + Some(models) => models, None => continue, }; - let mesh = &model.mesh; - let mat = &model.material; + for model in models { + let mesh = &model.mesh; + let mat = &model.material; - let tex_ref: &SrgbTexture2d = mat.base_color.as_ref().unwrap_or(&self.white_tex); + let tex_ref: &SrgbTexture2d = mat.base_color.as_ref().unwrap_or(&self.white_tex); - let mut sampler = tex_ref.sampled(); - sampler = sampler.wrap_function(SamplerWrapFunction::Repeat); - sampler = sampler.minify_filter(MinifySamplerFilter::Linear); - sampler = sampler.magnify_filter(MagnifySamplerFilter::Linear); + let mut sampler = tex_ref.sampled(); + sampler = sampler.wrap_function(SamplerWrapFunction::Repeat); + sampler = sampler.minify_filter(MinifySamplerFilter::Linear); + sampler = sampler.magnify_filter(MagnifySamplerFilter::Linear); - let c = mat.base_color_factor; + let c = mat.base_color_factor; - let uniforms = uniform! { - model: tr.matrix().to_cols_array_2d(), - view: cam.view().to_cols_array_2d(), - projection: cam.projection().to_cols_array_2d(), - u_light: [light_dir_view.x, light_dir_view.y, light_dir_view.z], - tex: sampler, - color: [c[0], c[1], c[2]], - uv_offset: [mat.uv_offset.x, mat.uv_offset.y], - uv_scale: [mat.uv_scale.x, mat.uv_scale.y], - }; + let uniforms = uniform! { + model: tr.matrix().to_cols_array_2d(), + view: cam.view().to_cols_array_2d(), + projection: cam.projection().to_cols_array_2d(), + u_light: [light_dir_view.x, light_dir_view.y, light_dir_view.z], + tex: sampler, + color: [c[0], c[1], c[2]], + uv_offset: [mat.uv_offset.x, mat.uv_offset.y], + uv_scale: [mat.uv_scale.x, mat.uv_scale.y], + }; - ctx.target.draw( - &mesh.vbuf, - &mesh.ibuf, - &self.program, - &uniforms, - &self.params, - ).unwrap(); + ctx.target.draw( + &mesh.vbuf, + &mesh.ibuf, + &self.program, + &uniforms, + &self.params, + ).unwrap(); + } } } }