Update the gltf loader to support multiple meshes in one file
This commit is contained in:
parent
71e991db77
commit
0f2209a5d5
3 changed files with 115 additions and 100 deletions
|
|
@ -12,7 +12,7 @@ use raidillon_assets::model_manager::ModelID;
|
||||||
|
|
||||||
/// Glium platform asset manager implementation.
|
/// Glium platform asset manager implementation.
|
||||||
pub struct GliumAssetManager {
|
pub struct GliumAssetManager {
|
||||||
pub models: HashMap<ModelID, Model>,
|
pub models: HashMap<ModelID, Vec<Model>>,
|
||||||
facade: Box<dyn Facade>,
|
facade: Box<dyn Facade>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,8 +28,8 @@ impl GliumAssetManager {
|
||||||
|
|
||||||
impl ModelManager for GliumAssetManager {
|
impl ModelManager for GliumAssetManager {
|
||||||
fn load_gltf(&mut self, id: ModelID, path: &Path) {
|
fn load_gltf(&mut self, id: ModelID, path: &Path) {
|
||||||
let model = load_gltf(path, self.facade.as_ref()).unwrap();
|
let models = load_gltf(path, self.facade.as_ref()).unwrap();
|
||||||
self.models.insert(id, model);
|
self.models.insert(id, models);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unload_model(&mut self, id: ModelID) {
|
fn unload_model(&mut self, id: ModelID) {
|
||||||
|
|
@ -48,6 +48,6 @@ impl ModelManager for GliumAssetManager {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
fn get_model(&self, id: &ModelID) -> Option<&dyn Any> {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use glium::{backend::Facade, IndexBuffer, VertexBuffer};
|
use glium::{backend::Facade, IndexBuffer, VertexBuffer};
|
||||||
use glium::index::PrimitiveType;
|
use glium::index::PrimitiveType;
|
||||||
use std::{fmt::Debug, path::Path};
|
use std::{fmt::Debug, path::Path};
|
||||||
|
|
@ -8,31 +8,16 @@ use glium::uniforms::{SamplerWrapFunction, MinifySamplerFilter, MagnifySamplerFi
|
||||||
use gltf::image::Format as GltfFormat;
|
use gltf::image::Format as GltfFormat;
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
|
|
||||||
/// Load a glTF 2.0 file from disk and upload the first primitive to the GPU.
|
/// Load a glTF 2.0 file from disk and upload all primitives to the GPU.
|
||||||
pub fn load_gltf<P>(path: P, facade: &dyn Facade) -> Result<Model>
|
///
|
||||||
|
/// Returns one [`Model`] per glTF primitive (across all meshes).
|
||||||
|
pub fn load_gltf<P>(path: P, facade: &dyn Facade) -> Result<Vec<Model>>
|
||||||
where
|
where
|
||||||
P: AsRef<Path> + Debug,
|
P: AsRef<Path> + Debug,
|
||||||
{
|
{
|
||||||
// -- parse the asset & bring buffer blobs into memory --
|
// -- parse the asset & bring buffer blobs into memory --
|
||||||
let (doc, buffers, images) = gltf::import(path.as_ref()).context("failed to import glTF file")?;
|
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
|
// Helper to update sampler settings from glTF sampler
|
||||||
fn update_sampler(mat: &mut Material, t: &gltf::texture::Texture<'_>) {
|
fn update_sampler(mat: &mut Material, t: &gltf::texture::Texture<'_>) {
|
||||||
let sampler_info = t.sampler();
|
let sampler_info = t.sampler();
|
||||||
|
|
@ -64,64 +49,92 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base-color texture (sRGB)
|
let mut out: Vec<Model> = Vec::new();
|
||||||
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])?);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Metallic-Roughness (linear)
|
for mesh in doc.meshes() {
|
||||||
if let Some(info) = pbr.metallic_roughness_texture() {
|
for primitive in mesh.primitives() {
|
||||||
update_sampler(&mut mat, &info.texture());
|
// ---------- MATERIAL ----------
|
||||||
let view = info.texture().source().index();
|
let mut mat = Material::default();
|
||||||
mat.metallic_roughness = Some(glium_linear_texture(facade, &images[view])?);
|
let material = primitive.material();
|
||||||
}
|
let pbr = material.pbr_metallic_roughness();
|
||||||
|
|
||||||
// Normal map (linear)
|
// Factors --------------------------------------------------
|
||||||
if let Some(info) = primitive.material().normal_texture() {
|
mat.base_color_factor = pbr.base_color_factor();
|
||||||
update_sampler(&mut mat, &info.texture());
|
mat.metal_factor = pbr.metallic_factor();
|
||||||
let view = info.texture().source().index();
|
mat.roughness_factor = pbr.roughness_factor();
|
||||||
mat.normal = Some(glium_linear_texture(facade, &images[view])?);
|
mat.emissive_factor = material.emissive_factor();
|
||||||
}
|
|
||||||
|
|
||||||
// Occlusion (linear)
|
// Base-color texture (sRGB)
|
||||||
if let Some(info) = primitive.material().occlusion_texture() {
|
if let Some(info) = pbr.base_color_texture() {
|
||||||
update_sampler(&mut mat, &info.texture());
|
update_sampler(&mut mat, &info.texture());
|
||||||
let view = info.texture().source().index();
|
let view = info.texture().source().index();
|
||||||
mat.occlusion = Some(glium_linear_texture(facade, &images[view])?);
|
mat.base_color = Some(glium_srgb_texture(facade, &images[view])?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emissive (sRGB)
|
// Metallic-Roughness (linear)
|
||||||
if let Some(info) = primitive.material().emissive_texture() {
|
if let Some(info) = pbr.metallic_roughness_texture() {
|
||||||
update_sampler(&mut mat, &info.texture());
|
update_sampler(&mut mat, &info.texture());
|
||||||
let view = info.texture().source().index();
|
let view = info.texture().source().index();
|
||||||
mat.emissive = Some(glium_srgb_texture(facade, &images[view])?);
|
mat.metallic_roughness = Some(glium_linear_texture(facade, &images[view])?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// KHR_texture_transform
|
// Normal map (linear)
|
||||||
if let Some(tex) = pbr.base_color_texture() {
|
if let Some(info) = material.normal_texture() {
|
||||||
if let Some(xform) = tex.texture_transform() {
|
update_sampler(&mut mat, &info.texture());
|
||||||
mat.uv_offset = Vec2::new(xform.offset()[0], xform.offset()[1]);
|
let view = info.texture().source().index();
|
||||||
mat.uv_scale = Vec2::new(xform.scale()[0], xform.scale()[1]);
|
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<u32> = reader.read_indices().context("missing indices")?.into_u32().collect();
|
||||||
|
|
||||||
|
// Interleave
|
||||||
|
let vertices: Vec<Vertex> = (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 ----
|
if out.is_empty() {
|
||||||
let reader = primitive.reader(|buf| Some(&buffers[buf.index()].0));
|
bail!("glTF has no mesh primitives");
|
||||||
|
}
|
||||||
|
|
||||||
let positions: Vec<[f32; 3]> = reader.read_positions().context("missing POSITION")?.collect();
|
Ok(out)
|
||||||
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<u32> = reader.read_indices().context("missing indices")?.into_u32().collect();
|
|
||||||
|
|
||||||
// Interleave
|
|
||||||
let vertices: Vec<Vertex> = (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 })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Linear-space texture (RGBA8) from glTF image data.
|
/// Linear-space texture (RGBA8) from glTF image data.
|
||||||
|
|
|
||||||
|
|
@ -73,41 +73,43 @@ impl RenderingSystem for BasicMeshRenderingSystem {
|
||||||
_ => continue,
|
_ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let model = match model.downcast_ref::<Model>() {
|
let models = match model.downcast_ref::<Vec<Model>>() {
|
||||||
Some(model) => model,
|
Some(models) => models,
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mesh = &model.mesh;
|
for model in models {
|
||||||
let mat = &model.material;
|
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();
|
let mut sampler = tex_ref.sampled();
|
||||||
sampler = sampler.wrap_function(SamplerWrapFunction::Repeat);
|
sampler = sampler.wrap_function(SamplerWrapFunction::Repeat);
|
||||||
sampler = sampler.minify_filter(MinifySamplerFilter::Linear);
|
sampler = sampler.minify_filter(MinifySamplerFilter::Linear);
|
||||||
sampler = sampler.magnify_filter(MagnifySamplerFilter::Linear);
|
sampler = sampler.magnify_filter(MagnifySamplerFilter::Linear);
|
||||||
|
|
||||||
let c = mat.base_color_factor;
|
let c = mat.base_color_factor;
|
||||||
|
|
||||||
let uniforms = uniform! {
|
let uniforms = uniform! {
|
||||||
model: tr.matrix().to_cols_array_2d(),
|
model: tr.matrix().to_cols_array_2d(),
|
||||||
view: cam.view().to_cols_array_2d(),
|
view: cam.view().to_cols_array_2d(),
|
||||||
projection: cam.projection().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],
|
u_light: [light_dir_view.x, light_dir_view.y, light_dir_view.z],
|
||||||
tex: sampler,
|
tex: sampler,
|
||||||
color: [c[0], c[1], c[2]],
|
color: [c[0], c[1], c[2]],
|
||||||
uv_offset: [mat.uv_offset.x, mat.uv_offset.y],
|
uv_offset: [mat.uv_offset.x, mat.uv_offset.y],
|
||||||
uv_scale: [mat.uv_scale.x, mat.uv_scale.y],
|
uv_scale: [mat.uv_scale.x, mat.uv_scale.y],
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.target.draw(
|
ctx.target.draw(
|
||||||
&mesh.vbuf,
|
&mesh.vbuf,
|
||||||
&mesh.ibuf,
|
&mesh.ibuf,
|
||||||
&self.program,
|
&self.program,
|
||||||
&uniforms,
|
&uniforms,
|
||||||
&self.params,
|
&self.params,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue