separate GLTF loader to its own crate
This commit is contained in:
parent
3ce9ab6fb3
commit
5542467d7e
|
@ -1829,6 +1829,7 @@ dependencies = [
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
"lyra-ecs",
|
"lyra-ecs",
|
||||||
"lyra-game-derive",
|
"lyra-game-derive",
|
||||||
|
"lyra-gltf",
|
||||||
"lyra-math",
|
"lyra-math",
|
||||||
"lyra-reflect",
|
"lyra-reflect",
|
||||||
"lyra-resource",
|
"lyra-resource",
|
||||||
|
@ -1860,6 +1861,34 @@ dependencies = [
|
||||||
"syn 2.0.77",
|
"syn 2.0.77",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lyra-gltf"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"async-std",
|
||||||
|
"base64 0.21.7",
|
||||||
|
"crossbeam",
|
||||||
|
"glam",
|
||||||
|
"gltf",
|
||||||
|
"image",
|
||||||
|
"infer",
|
||||||
|
"instant",
|
||||||
|
"lyra-ecs",
|
||||||
|
"lyra-math",
|
||||||
|
"lyra-reflect",
|
||||||
|
"lyra-resource",
|
||||||
|
"lyra-scene",
|
||||||
|
"mime",
|
||||||
|
"notify",
|
||||||
|
"notify-debouncer-full",
|
||||||
|
"percent-encoding",
|
||||||
|
"rand",
|
||||||
|
"thiserror",
|
||||||
|
"tracing",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lyra-math"
|
name = "lyra-math"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -1894,14 +1923,12 @@ dependencies = [
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
"crossbeam",
|
"crossbeam",
|
||||||
"glam",
|
"glam",
|
||||||
"gltf",
|
|
||||||
"image",
|
"image",
|
||||||
"infer",
|
"infer",
|
||||||
"instant",
|
"instant",
|
||||||
"lyra-ecs",
|
"lyra-ecs",
|
||||||
"lyra-math",
|
"lyra-math",
|
||||||
"lyra-reflect",
|
"lyra-reflect",
|
||||||
"lyra-scene",
|
|
||||||
"mime",
|
"mime",
|
||||||
"notify",
|
"notify",
|
||||||
"notify-debouncer-full",
|
"notify-debouncer-full",
|
||||||
|
@ -1920,6 +1947,7 @@ dependencies = [
|
||||||
"lyra-ecs",
|
"lyra-ecs",
|
||||||
"lyra-math",
|
"lyra-math",
|
||||||
"lyra-reflect",
|
"lyra-reflect",
|
||||||
|
"lyra-resource",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -10,6 +10,7 @@ lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
|
||||||
lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] }
|
lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] }
|
||||||
lyra-math = { path = "../lyra-math" }
|
lyra-math = { path = "../lyra-math" }
|
||||||
lyra-scene = { path = "../lyra-scene" }
|
lyra-scene = { path = "../lyra-scene" }
|
||||||
|
lyra-gltf = { path = "../lyra-gltf" }
|
||||||
wgsl_preprocessor = { path = "../wgsl-preprocessor" }
|
wgsl_preprocessor = { path = "../wgsl-preprocessor" }
|
||||||
|
|
||||||
winit = "0.30.5"
|
winit = "0.30.5"
|
||||||
|
|
|
@ -29,5 +29,6 @@ pub use lyra_resource as assets;
|
||||||
pub use lyra_ecs as ecs;
|
pub use lyra_ecs as ecs;
|
||||||
pub use lyra_math as math;
|
pub use lyra_math as math;
|
||||||
pub use lyra_reflect as reflect;
|
pub use lyra_reflect as reflect;
|
||||||
|
pub use lyra_gltf as gltf;
|
||||||
|
|
||||||
pub use plugin::DefaultPlugins;
|
pub use plugin::DefaultPlugins;
|
|
@ -1,4 +1,6 @@
|
||||||
|
use lyra_ecs::query::ResMut;
|
||||||
use lyra_ecs::CommandQueue;
|
use lyra_ecs::CommandQueue;
|
||||||
|
use lyra_gltf::GltfLoader;
|
||||||
use lyra_resource::ResourceManager;
|
use lyra_resource::ResourceManager;
|
||||||
|
|
||||||
use crate::game::App;
|
use crate::game::App;
|
||||||
|
@ -113,6 +115,7 @@ impl Plugin for DefaultPlugins {
|
||||||
CommandQueuePlugin.setup(app);
|
CommandQueuePlugin.setup(app);
|
||||||
InputPlugin.setup(app);
|
InputPlugin.setup(app);
|
||||||
ResourceManagerPlugin.setup(app);
|
ResourceManagerPlugin.setup(app);
|
||||||
|
GltfPlugin.setup(app);
|
||||||
WindowPlugin::default().setup(app);
|
WindowPlugin::default().setup(app);
|
||||||
DeltaTimePlugin.setup(app);
|
DeltaTimePlugin.setup(app);
|
||||||
}
|
}
|
||||||
|
@ -128,3 +131,13 @@ impl Plugin for CommandQueuePlugin {
|
||||||
app.world.add_resource(CommandQueue::default());
|
app.world.add_resource(CommandQueue::default());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct GltfPlugin;
|
||||||
|
|
||||||
|
impl Plugin for GltfPlugin {
|
||||||
|
fn setup(&mut self, app: &mut App) {
|
||||||
|
let man: ResMut<ResourceManager> = app.world.get_resource_or_default();
|
||||||
|
man.register_loader::<GltfLoader>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,8 @@ use lyra_ecs::{
|
||||||
Entity, ResourceObject, World,
|
Entity, ResourceObject, World,
|
||||||
};
|
};
|
||||||
use lyra_game_derive::RenderGraphLabel;
|
use lyra_game_derive::RenderGraphLabel;
|
||||||
use lyra_resource::{gltf::Mesh, ResHandle};
|
use lyra_resource::ResHandle;
|
||||||
|
use lyra_gltf::Mesh;
|
||||||
use lyra_scene::SceneGraph;
|
use lyra_scene::SceneGraph;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use tracing::{debug, instrument};
|
use tracing::{debug, instrument};
|
||||||
|
@ -93,10 +94,10 @@ impl MeshPrepNode {
|
||||||
if let Some(index_buffer) = buffers.buffer_indices.as_ref() {
|
if let Some(index_buffer) = buffers.buffer_indices.as_ref() {
|
||||||
let aligned_indices = match mesh.indices.as_ref().unwrap() {
|
let aligned_indices = match mesh.indices.as_ref().unwrap() {
|
||||||
// U16 indices need to be aligned to u32, for wpgu, which are 4-bytes in size.
|
// U16 indices need to be aligned to u32, for wpgu, which are 4-bytes in size.
|
||||||
lyra_resource::gltf::MeshIndices::U16(v) => {
|
lyra_gltf::MeshIndices::U16(v) => {
|
||||||
bytemuck::pod_align_to::<u16, u32>(v).1
|
bytemuck::pod_align_to::<u16, u32>(v).1
|
||||||
}
|
}
|
||||||
lyra_resource::gltf::MeshIndices::U32(v) => {
|
lyra_gltf::MeshIndices::U32(v) => {
|
||||||
bytemuck::pod_align_to::<u32, u32>(v).1
|
bytemuck::pod_align_to::<u32, u32>(v).1
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -137,10 +138,10 @@ impl MeshPrepNode {
|
||||||
let indices = match mesh.indices.as_ref() {
|
let indices = match mesh.indices.as_ref() {
|
||||||
Some(indices) => {
|
Some(indices) => {
|
||||||
let (idx_type, len, contents) = match indices {
|
let (idx_type, len, contents) = match indices {
|
||||||
lyra_resource::gltf::MeshIndices::U16(v) => {
|
lyra_gltf::MeshIndices::U16(v) => {
|
||||||
(wgpu::IndexFormat::Uint16, v.len(), bytemuck::cast_slice(v))
|
(wgpu::IndexFormat::Uint16, v.len(), bytemuck::cast_slice(v))
|
||||||
}
|
}
|
||||||
lyra_resource::gltf::MeshIndices::U32(v) => {
|
lyra_gltf::MeshIndices::U32(v) => {
|
||||||
(wgpu::IndexFormat::Uint32, v.len(), bytemuck::cast_slice(v))
|
(wgpu::IndexFormat::Uint32, v.len(), bytemuck::cast_slice(v))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -518,7 +519,7 @@ impl GpuMaterial {
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
queue: &wgpu::Queue,
|
queue: &wgpu::Queue,
|
||||||
layout: &Arc<wgpu::BindGroupLayout>,
|
layout: &Arc<wgpu::BindGroupLayout>,
|
||||||
mat: &lyra_resource::gltf::Material,
|
mat: &lyra_gltf::Material,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
//let specular = mat.specular.as_ref().unwrap_or_default();
|
//let specular = mat.specular.as_ref().unwrap_or_default();
|
||||||
//let specular_
|
//let specular_
|
||||||
|
|
|
@ -21,7 +21,7 @@ fn texture_to_render(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: &Arc
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaterialSpecular {
|
impl MaterialSpecular {
|
||||||
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Specular) -> Self {
|
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_gltf::Specular) -> Self {
|
||||||
let tex = texture_to_render(device, queue, &bg_layout, &value.texture);
|
let tex = texture_to_render(device, queue, &bg_layout, &value.texture);
|
||||||
let color_tex = texture_to_render(device, queue, &bg_layout, &value.color_texture);
|
let color_tex = texture_to_render(device, queue, &bg_layout, &value.color_texture);
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ pub struct Material {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Material {
|
impl Material {
|
||||||
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Material) -> Self {
|
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_gltf::Material) -> Self {
|
||||||
let diffuse_texture = texture_to_render(device, queue, &bg_layout, &value.base_color_texture);
|
let diffuse_texture = texture_to_render(device, queue, &bg_layout, &value.base_color_texture);
|
||||||
|
|
||||||
let specular = value.specular.as_ref().map(|s| MaterialSpecular::from_resource(device, queue, bg_layout.clone(), s));
|
let specular = value.specular.as_ref().map(|s| MaterialSpecular::from_resource(device, queue, bg_layout.clone(), s));
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
[package]
|
||||||
|
name = "lyra-gltf"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
|
||||||
|
lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] }
|
||||||
|
lyra-math = { path = "../lyra-math" }
|
||||||
|
lyra-scene = { path = "../lyra-scene" }
|
||||||
|
lyra-resource = { path = "../lyra-resource" }
|
||||||
|
anyhow = "1.0.75"
|
||||||
|
base64 = "0.21.4"
|
||||||
|
crossbeam = { version = "0.8.4", features = [ "crossbeam-channel" ] }
|
||||||
|
glam = "0.29.0"
|
||||||
|
gltf = { version = "1.3.0", features = ["KHR_materials_pbrSpecularGlossiness", "KHR_materials_specular"] }
|
||||||
|
image = "0.25.2"
|
||||||
|
# not using custom matcher, or file type from file path
|
||||||
|
infer = { version = "0.15.0", default-features = false }
|
||||||
|
mime = "0.3.17"
|
||||||
|
notify = "6.1.1"
|
||||||
|
notify-debouncer-full = "0.3.1"
|
||||||
|
#notify = { version = "6.1.1", default-features = false, features = [ "fsevent-sys", "macos_fsevent" ]} # disables crossbeam-channel
|
||||||
|
percent-encoding = "2.3.0"
|
||||||
|
thiserror = "1.0.48"
|
||||||
|
tracing = "0.1.37"
|
||||||
|
uuid = { version = "1.4.1", features = ["v4"] }
|
||||||
|
instant = "0.1"
|
||||||
|
async-std = "1.12.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rand = "0.8.5"
|
|
@ -0,0 +1,104 @@
|
||||||
|
pub mod loader;
|
||||||
|
use base64::Engine;
|
||||||
|
pub use loader::*;
|
||||||
|
|
||||||
|
pub mod material;
|
||||||
|
use lyra_reflect::Reflect;
|
||||||
|
use lyra_resource::{ResHandle, ResourceData, UntypedResHandle};
|
||||||
|
use lyra_scene::SceneGraph;
|
||||||
|
pub use material::*;
|
||||||
|
|
||||||
|
pub mod mesh;
|
||||||
|
pub use mesh::*;
|
||||||
|
|
||||||
|
pub mod scene;
|
||||||
|
pub use scene::*;
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub(crate) mod lyra_engine {
|
||||||
|
pub(crate) mod ecs {
|
||||||
|
pub use lyra_ecs::*;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod reflect {
|
||||||
|
pub use lyra_reflect::*;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum UriReadError {
|
||||||
|
#[error("IOError: '{0}'")]
|
||||||
|
IoError(io::Error),
|
||||||
|
|
||||||
|
// From is implemented for this field in each loader module
|
||||||
|
#[error("Base64 decoding error: '{0}'")]
|
||||||
|
Base64Decode(base64::DecodeError),
|
||||||
|
|
||||||
|
#[error("Some data was missing from the uri")]
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a buffer's uri string into a byte buffer.
|
||||||
|
///
|
||||||
|
/// * `containing_path`: The path of the containing folder of the buffers "parent",
|
||||||
|
/// the parent being where this buffer is defined in,
|
||||||
|
/// i.e. parent="resources/models/player.gltf", containing="resource/models"
|
||||||
|
pub(crate) fn gltf_read_buffer_uri(containing_path: &str, uri: &str) -> Result<Vec<u8>, UriReadError> {
|
||||||
|
if let Some((mime, data)) = uri.strip_prefix("data")
|
||||||
|
.and_then(|uri| 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)
|
||||||
|
.map_err(UriReadError::Base64Decode)
|
||||||
|
} else {
|
||||||
|
Ok(data.as_bytes().to_vec())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let full_path = format!("{containing_path}/{uri}");
|
||||||
|
std::fs::read(full_path).map_err(UriReadError::IoError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A loaded Gltf file
|
||||||
|
#[derive(Clone, Default, Reflect)]
|
||||||
|
pub struct Gltf {
|
||||||
|
pub scenes: Vec<ResHandle<SceneGraph>>,
|
||||||
|
pub materials: Vec<ResHandle<Material>>,
|
||||||
|
pub meshes: Vec<ResHandle<Mesh>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResourceData for Gltf {
|
||||||
|
fn dependencies(&self) -> Vec<UntypedResHandle> {
|
||||||
|
let mut deps = vec![];
|
||||||
|
|
||||||
|
for scene in self.scenes.iter() {
|
||||||
|
deps.push(scene.untyped_clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
for mat in self.materials.iter() {
|
||||||
|
deps.push(mat.untyped_clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
for mesh in self.meshes.iter() {
|
||||||
|
deps.push(mesh.untyped_clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
deps
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,23 +7,19 @@ use lyra_math::Transform;
|
||||||
use lyra_scene::{SceneGraph, SceneNode, WorldTransform};
|
use lyra_scene::{SceneGraph, SceneNode, WorldTransform};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{loader::{LoaderError, PinedBoxLoaderFuture, ResourceLoader}, util, ResHandle, ResourceData, ResourceManager, ResourceStorage};
|
use lyra_resource::{loader::{LoaderError, PinedBoxLoaderFuture, ResourceLoader}, ResHandle, ResourceData, ResourceManager, ResourceStorage};
|
||||||
|
use crate::{gltf_read_buffer_uri, UriReadError};
|
||||||
|
|
||||||
use super::{Gltf, GltfNode, Material, Mesh, MeshIndices, MeshVertexAttribute, VertexAttributeData};
|
use super::{Gltf, GltfNode, Material, Mesh, MeshIndices, MeshVertexAttribute, VertexAttributeData};
|
||||||
|
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
impl From<gltf::Error> for LoaderError {
|
|
||||||
fn from(value: gltf::Error) -> Self {
|
|
||||||
LoaderError::DecodingError(value.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
enum ModelLoaderError {
|
enum ModelLoaderError {
|
||||||
#[error("The model ({0}) is missing the BIN section in the gltf file")]
|
#[error("The model ({0}) is missing the BIN section in the gltf file")]
|
||||||
MissingBin(String),
|
MissingBin(String),
|
||||||
#[error("There was an error with decoding a uri defined in the model: '{0}'")]
|
#[error("There was an error with decoding a uri defined in the model: '{0}'")]
|
||||||
UriDecodingError(util::UriReadError),
|
UriDecodingError(UriReadError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ModelLoaderError> for LoaderError {
|
impl From<ModelLoaderError> for LoaderError {
|
||||||
|
@ -45,9 +41,9 @@ pub(crate) struct GltfLoadContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ModelLoader;
|
pub struct GltfLoader;
|
||||||
|
|
||||||
impl ModelLoader {
|
impl GltfLoader {
|
||||||
/* fn parse_uri(containing_path: &str, uri: &str) -> Option<Vec<u8>> {
|
/* fn parse_uri(containing_path: &str, uri: &str) -> Option<Vec<u8>> {
|
||||||
let uri = uri.strip_prefix("data")?;
|
let uri = uri.strip_prefix("data")?;
|
||||||
let (mime, data) = uri.split_once(",")?;
|
let (mime, data) = uri.split_once(",")?;
|
||||||
|
@ -136,7 +132,7 @@ impl ModelLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
for child in gnode.children() {
|
for child in gnode.children() {
|
||||||
let cmesh = ModelLoader::process_node(ctx, materials, scene, &scene_node, child);
|
let cmesh = GltfLoader::process_node(ctx, materials, scene, &scene_node, child);
|
||||||
node.children.push(cmesh);
|
node.children.push(cmesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +155,7 @@ impl ModelLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResourceLoader for ModelLoader {
|
impl ResourceLoader for GltfLoader {
|
||||||
fn extensions(&self) -> &[&str] {
|
fn extensions(&self) -> &[&str] {
|
||||||
&[
|
&[
|
||||||
"gltf", "glb"
|
"gltf", "glb"
|
||||||
|
@ -184,7 +180,8 @@ impl ResourceLoader for ModelLoader {
|
||||||
parent_path.pop();
|
parent_path.pop();
|
||||||
let parent_path = parent_path.display().to_string();
|
let parent_path = parent_path.display().to_string();
|
||||||
|
|
||||||
let gltf = gltf::Gltf::open(&path)?;
|
let gltf = gltf::Gltf::open(&path)
|
||||||
|
.map_err(|ge| LoaderError::DecodingError(ge.into()))?;
|
||||||
|
|
||||||
let mut use_bin = false;
|
let mut use_bin = false;
|
||||||
let buffers: Vec<Vec<u8>> = gltf.buffers().flat_map(|b| match b.source() {
|
let buffers: Vec<Vec<u8>> = gltf.buffers().flat_map(|b| match b.source() {
|
||||||
|
@ -193,7 +190,7 @@ impl ResourceLoader for ModelLoader {
|
||||||
gltf.blob.as_deref().map(|v| v.to_vec())
|
gltf.blob.as_deref().map(|v| v.to_vec())
|
||||||
.ok_or(ModelLoaderError::MissingBin(path.to_string()))
|
.ok_or(ModelLoaderError::MissingBin(path.to_string()))
|
||||||
},
|
},
|
||||||
gltf::buffer::Source::Uri(uri) => util::gltf_read_buffer_uri(&parent_path, uri)
|
gltf::buffer::Source::Uri(uri) => gltf_read_buffer_uri(&parent_path, uri)
|
||||||
.map_err(ModelLoaderError::UriDecodingError),
|
.map_err(ModelLoaderError::UriDecodingError),
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
|
@ -219,7 +216,7 @@ impl ResourceLoader for ModelLoader {
|
||||||
let root_node = graph.root_node();
|
let root_node = graph.root_node();
|
||||||
|
|
||||||
for node in scene.nodes() {
|
for node in scene.nodes() {
|
||||||
let n = ModelLoader::process_node(&mut context, &materials, &mut graph, &root_node, node);
|
let n = GltfLoader::process_node(&mut context, &materials, &mut graph, &root_node, node);
|
||||||
|
|
||||||
if let Some(mesh) = n.mesh {
|
if let Some(mesh) = n.mesh {
|
||||||
gltf_out.meshes.push(mesh.clone());
|
gltf_out.meshes.push(mesh.clone());
|
||||||
|
@ -272,10 +269,12 @@ impl ResourceLoader for ModelLoader {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use lyra_ecs::{query::Entities, relation::ChildOf};
|
use lyra_ecs::{query::Entities, relation::ChildOf};
|
||||||
use lyra_scene::WorldTransform;
|
use lyra_scene::WorldTransform;
|
||||||
|
|
||||||
use crate::tests::busy_wait_resource;
|
//use lyra_resource::tests::busy_wait_resource;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -291,7 +290,7 @@ mod tests {
|
||||||
|
|
||||||
let manager = ResourceManager::new();
|
let manager = ResourceManager::new();
|
||||||
let gltf = manager.request::<Gltf>(&path).unwrap();
|
let gltf = manager.request::<Gltf>(&path).unwrap();
|
||||||
busy_wait_resource(&gltf, 15.0);
|
assert!(gltf.wait_for_load_timeout(Duration::from_secs(10)), "failed to load gltf, hit 10 second timeout");
|
||||||
let gltf = gltf.data_ref().unwrap();
|
let gltf = gltf.data_ref().unwrap();
|
||||||
|
|
||||||
assert_eq!(gltf.scenes.len(), 1);
|
assert_eq!(gltf.scenes.len(), 1);
|
|
@ -2,9 +2,9 @@ use std::{collections::hash_map::DefaultHasher, hash::{Hash, Hasher}};
|
||||||
|
|
||||||
use gltf::texture::{MagFilter, MinFilter};
|
use gltf::texture::{MagFilter, MinFilter};
|
||||||
use lyra_reflect::Reflect;
|
use lyra_reflect::Reflect;
|
||||||
use crate::{lyra_engine, optionally_add_to_dep, ResourceData};
|
use lyra_resource::{optionally_add_to_dep, FilterMode, Image, ResHandle, ResourceData, Texture, TextureSampler, WrappingMode};
|
||||||
|
use crate::{gltf_read_buffer_uri, lyra_engine, UriReadError};
|
||||||
|
|
||||||
use crate::{util, FilterMode, Image, ResHandle, Texture, TextureSampler, WrappingMode};
|
|
||||||
use super::loader::GltfLoadContext;
|
use super::loader::GltfLoadContext;
|
||||||
|
|
||||||
/// PBR metallic roughness
|
/// PBR metallic roughness
|
||||||
|
@ -184,8 +184,6 @@ pub struct Material {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResourceData for Material {
|
impl ResourceData for Material {
|
||||||
|
|
||||||
|
|
||||||
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
||||||
let mut deps = vec![];
|
let mut deps = vec![];
|
||||||
|
|
||||||
|
@ -242,7 +240,7 @@ impl Material {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_source(context: &mut GltfLoadContext, src: gltf::image::Source) -> Result<Vec<u8>, util::UriReadError> {
|
fn read_source(context: &mut GltfLoadContext, src: gltf::image::Source) -> Result<Vec<u8>, UriReadError> {
|
||||||
let gltf_rel_path = context.gltf_parent_path;
|
let gltf_rel_path = context.gltf_parent_path;
|
||||||
// TODO: Don't copy sources
|
// TODO: Don't copy sources
|
||||||
match src {
|
match src {
|
||||||
|
@ -261,7 +259,7 @@ impl Material {
|
||||||
Ok(b)
|
Ok(b)
|
||||||
},
|
},
|
||||||
gltf::buffer::Source::Uri(uri) => {
|
gltf::buffer::Source::Uri(uri) => {
|
||||||
util::gltf_read_buffer_uri(gltf_rel_path, uri)
|
gltf_read_buffer_uri(gltf_rel_path, uri)
|
||||||
.map(|mut buf| {
|
.map(|mut buf| {
|
||||||
buf.drain(0..offset);
|
buf.drain(0..offset);
|
||||||
buf.truncate(len);
|
buf.truncate(len);
|
||||||
|
@ -271,7 +269,7 @@ impl Material {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
gltf::image::Source::Uri { uri, mime_type: _ } => {
|
gltf::image::Source::Uri { uri, mime_type: _ } => {
|
||||||
util::gltf_read_buffer_uri(gltf_rel_path, uri)
|
gltf_read_buffer_uri(gltf_rel_path, uri)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,11 +290,11 @@ impl Material {
|
||||||
|
|
||||||
let samp = tex.sampler();
|
let samp = tex.sampler();
|
||||||
let samp = TextureSampler {
|
let samp = TextureSampler {
|
||||||
mag_filter: samp.mag_filter().map(FilterMode::from_mag_filter),
|
mag_filter: samp.mag_filter().map(filter_mode_from_mag_filter),
|
||||||
min_filter: samp.min_filter().map(FilterMode::from_min_filter),
|
min_filter: samp.min_filter().map(filter_mode_from_min_filter),
|
||||||
mipmap_filter: samp.min_filter().and_then(FilterMode::mipmap_filter),
|
mipmap_filter: samp.min_filter().and_then(filter_mode_mipmap_filter),
|
||||||
wrap_u: samp.wrap_s().into(),
|
wrap_u: wrapping_mode_from_gltf_wrapping_mode(samp.wrap_s()),
|
||||||
wrap_v: samp.wrap_t().into(),
|
wrap_v: wrapping_mode_from_gltf_wrapping_mode(samp.wrap_t()),
|
||||||
wrap_w: WrappingMode::ClampToEdge,
|
wrap_w: WrappingMode::ClampToEdge,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -347,20 +345,9 @@ impl Material {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<gltf::texture::MagFilter> for FilterMode {
|
/// Get the MinFilter mode and the mipmap filter mode from gltf MinFilter
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: gltf::texture::MagFilter) -> Self {
|
fn filter_mode_from_min_filter(minf: MinFilter) -> FilterMode {
|
||||||
match value {
|
|
||||||
gltf::texture::MagFilter::Nearest => Self::Nearest,
|
|
||||||
gltf::texture::MagFilter::Linear => Self::Linear,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilterMode {
|
|
||||||
/// Get the MinFilter mode and the mipmap filter mode from gltf MinFilter
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn from_min_filter(minf: MinFilter) -> FilterMode {
|
|
||||||
match minf {
|
match minf {
|
||||||
MinFilter::Nearest => FilterMode::Nearest,
|
MinFilter::Nearest => FilterMode::Nearest,
|
||||||
MinFilter::Linear => FilterMode::Linear,
|
MinFilter::Linear => FilterMode::Linear,
|
||||||
|
@ -369,18 +356,18 @@ impl FilterMode {
|
||||||
MinFilter::NearestMipmapLinear => FilterMode::Nearest,
|
MinFilter::NearestMipmapLinear => FilterMode::Nearest,
|
||||||
MinFilter::LinearMipmapLinear => FilterMode::Linear,
|
MinFilter::LinearMipmapLinear => FilterMode::Linear,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_mag_filter(magf: MagFilter) -> FilterMode {
|
fn filter_mode_from_mag_filter(magf: MagFilter) -> FilterMode {
|
||||||
match magf {
|
match magf {
|
||||||
MagFilter::Nearest => FilterMode::Nearest,
|
MagFilter::Nearest => FilterMode::Nearest,
|
||||||
MagFilter::Linear => FilterMode::Linear,
|
MagFilter::Linear => FilterMode::Linear,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn mipmap_filter(minf: MinFilter) -> Option<FilterMode> {
|
fn filter_mode_mipmap_filter(minf: MinFilter) -> Option<FilterMode> {
|
||||||
match minf {
|
match minf {
|
||||||
MinFilter::Nearest => None,
|
MinFilter::Nearest => None,
|
||||||
MinFilter::Linear => None,
|
MinFilter::Linear => None,
|
||||||
|
@ -389,16 +376,13 @@ impl FilterMode {
|
||||||
MinFilter::NearestMipmapLinear => Some(FilterMode::Linear),
|
MinFilter::NearestMipmapLinear => Some(FilterMode::Linear),
|
||||||
MinFilter::LinearMipmapLinear => Some(FilterMode::Linear),
|
MinFilter::LinearMipmapLinear => Some(FilterMode::Linear),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<gltf::texture::WrappingMode> for WrappingMode {
|
#[inline(always)]
|
||||||
#[inline(always)]
|
fn wrapping_mode_from_gltf_wrapping_mode(value: gltf::texture::WrappingMode) -> WrappingMode {
|
||||||
fn from(value: gltf::texture::WrappingMode) -> Self {
|
|
||||||
match value {
|
match value {
|
||||||
gltf::texture::WrappingMode::ClampToEdge => Self::ClampToEdge,
|
gltf::texture::WrappingMode::ClampToEdge => WrappingMode::ClampToEdge,
|
||||||
gltf::texture::WrappingMode::MirroredRepeat => Self::MirroredRepeat,
|
gltf::texture::WrappingMode::MirroredRepeat => WrappingMode::MirroredRepeat,
|
||||||
gltf::texture::WrappingMode::Repeat => Self::Repeat,
|
gltf::texture::WrappingMode::Repeat => WrappingMode::Repeat,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
use lyra_math::Transform;
|
||||||
|
use lyra_resource::{optionally_add_to_dep, ResourceData, UntypedResHandle};
|
||||||
|
|
||||||
|
use super::Mesh;
|
||||||
|
use crate::ResHandle;
|
||||||
|
|
||||||
|
/// A Node in the Gltf file
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct GltfNode {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub mesh: Option<ResHandle<Mesh>>,
|
||||||
|
pub transform: Transform,
|
||||||
|
pub children: Vec<GltfNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResourceData for GltfNode {
|
||||||
|
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
||||||
|
let mut deps: Vec<UntypedResHandle> = self.children.iter()
|
||||||
|
.flat_map(|c| c.mesh.as_ref().map(|h| h.untyped_clone()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
optionally_add_to_dep(&mut deps, &self.mesh);
|
||||||
|
|
||||||
|
deps
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
@ -9,12 +9,10 @@ edition = "2021"
|
||||||
lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
|
lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
|
||||||
lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] }
|
lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] }
|
||||||
lyra-math = { path = "../lyra-math" }
|
lyra-math = { path = "../lyra-math" }
|
||||||
lyra-scene = { path = "../lyra-scene" }
|
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
base64 = "0.21.4"
|
base64 = "0.21.4"
|
||||||
crossbeam = { version = "0.8.4", features = [ "crossbeam-channel" ] }
|
crossbeam = { version = "0.8.4", features = [ "crossbeam-channel" ] }
|
||||||
glam = "0.29.0"
|
glam = "0.29.0"
|
||||||
gltf = { version = "1.3.0", features = ["KHR_materials_pbrSpecularGlossiness", "KHR_materials_specular"] }
|
|
||||||
image = "0.25.2"
|
image = "0.25.2"
|
||||||
# not using custom matcher, or file type from file path
|
# not using custom matcher, or file type from file path
|
||||||
infer = { version = "0.15.0", default-features = false }
|
infer = { version = "0.15.0", default-features = false }
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
pub mod loader;
|
|
||||||
pub use loader::*;
|
|
||||||
|
|
||||||
pub mod material;
|
|
||||||
use lyra_reflect::Reflect;
|
|
||||||
use lyra_scene::SceneGraph;
|
|
||||||
use crate::ResourceData;
|
|
||||||
pub use material::*;
|
|
||||||
|
|
||||||
pub mod mesh;
|
|
||||||
pub use mesh::*;
|
|
||||||
|
|
||||||
pub mod scene;
|
|
||||||
pub use scene::*;
|
|
||||||
|
|
||||||
use crate::ResHandle;
|
|
||||||
|
|
||||||
use crate::lyra_engine;
|
|
||||||
|
|
||||||
/// A loaded Gltf file
|
|
||||||
#[derive(Clone, Default, Reflect)]
|
|
||||||
pub struct Gltf {
|
|
||||||
pub scenes: Vec<ResHandle<SceneGraph>>,
|
|
||||||
pub materials: Vec<ResHandle<Material>>,
|
|
||||||
pub meshes: Vec<ResHandle<Mesh>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResourceData for Gltf {
|
|
||||||
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
|
||||||
let mut deps = vec![];
|
|
||||||
|
|
||||||
for scene in self.scenes.iter() {
|
|
||||||
deps.push(scene.untyped_clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
for mat in self.materials.iter() {
|
|
||||||
deps.push(mat.untyped_clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
for mesh in self.meshes.iter() {
|
|
||||||
deps.push(mesh.untyped_clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
deps
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
use lyra_math::Transform;
|
|
||||||
use lyra_scene::SceneGraph;
|
|
||||||
use crate::{optionally_add_to_dep, ResourceData, UntypedResHandle};
|
|
||||||
|
|
||||||
use super::Mesh;
|
|
||||||
use crate::ResHandle;
|
|
||||||
|
|
||||||
/// A Node in the Gltf file
|
|
||||||
#[derive(Clone, Default)]
|
|
||||||
pub struct GltfNode {
|
|
||||||
pub name: Option<String>,
|
|
||||||
pub mesh: Option<ResHandle<Mesh>>,
|
|
||||||
pub transform: Transform,
|
|
||||||
pub children: Vec<GltfNode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResourceData for GltfNode {
|
|
||||||
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
|
||||||
let mut deps: Vec<UntypedResHandle> = self.children.iter()
|
|
||||||
.flat_map(|c| c.mesh.as_ref().map(|h| h.untyped_clone()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
optionally_add_to_dep(&mut deps, &self.mesh);
|
|
||||||
|
|
||||||
deps
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Scene in a Gltf file
|
|
||||||
/* #[derive(Clone)]
|
|
||||||
pub struct GltfScene {
|
|
||||||
pub nodes: Vec<GltfNode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResourceData for GltfScene {
|
|
||||||
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
|
||||||
let deps: Vec<UntypedResHandle> = self.nodes.iter()
|
|
||||||
.map(|n| n.dependencies())
|
|
||||||
.flatten()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
deps
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GltfScene {
|
|
||||||
fn collect_node(&self, parent_node: &GltfNode, node: &GltfNode) -> Vec<(ResHandle<Mesh>, Transform)> {
|
|
||||||
let mut v = vec![];
|
|
||||||
|
|
||||||
if let Some(mesh) = &node.mesh {
|
|
||||||
v.push((mesh.clone(), parent_node.transform + node.transform));
|
|
||||||
}
|
|
||||||
|
|
||||||
for child in node.children.iter() {
|
|
||||||
let mut tmp = self.collect_node(node, child);
|
|
||||||
v.append(&mut tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
v
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Collects all Gltf meshes and gets their world Transform.
|
|
||||||
pub fn collect_world_meshes(&self) -> Vec<(ResHandle<Mesh>, Transform)> {
|
|
||||||
let mut v = vec![];
|
|
||||||
|
|
||||||
// process the root nodes in the scene
|
|
||||||
for parent_node in self.nodes.iter() {
|
|
||||||
if let Some(mesh) = &parent_node.mesh {
|
|
||||||
v.push((mesh.clone(), parent_node.transform));
|
|
||||||
}
|
|
||||||
|
|
||||||
for child in parent_node.children.iter() {
|
|
||||||
let mut tmp = self.collect_node(parent_node, child);
|
|
||||||
v.append(&mut tmp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
impl ResourceData for SceneGraph {
|
|
||||||
fn dependencies(&self) -> Vec<crate::UntypedResHandle> {
|
|
||||||
self.world().view::<&crate::UntypedResHandle>()
|
|
||||||
.iter()
|
|
||||||
.map(|han| han.clone())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,8 +12,6 @@ pub use dep_state::*;
|
||||||
|
|
||||||
pub mod loader;
|
pub mod loader;
|
||||||
|
|
||||||
pub mod gltf;
|
|
||||||
|
|
||||||
mod world_ext;
|
mod world_ext;
|
||||||
pub use world_ext::*;
|
pub use world_ext::*;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{any::{Any, TypeId}, marker::PhantomData, ops::{Deref, DerefMut}, sync::{Arc, Condvar, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}};
|
use std::{any::{Any, TypeId}, marker::PhantomData, ops::{Deref, DerefMut}, sync::{Arc, Condvar, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}, time::Duration};
|
||||||
|
|
||||||
use lyra_ecs::Component;
|
use lyra_ecs::Component;
|
||||||
use lyra_reflect::Reflect;
|
use lyra_reflect::Reflect;
|
||||||
|
@ -198,17 +198,14 @@ impl UntypedResHandle {
|
||||||
/// This blocks the thread without consuming CPU time; its backed by a
|
/// This blocks the thread without consuming CPU time; its backed by a
|
||||||
/// [`Condvar`](std::sync::Condvar).
|
/// [`Condvar`](std::sync::Condvar).
|
||||||
pub fn wait_for_load(&self) {
|
pub fn wait_for_load(&self) {
|
||||||
let d = self.read();
|
self.wait_for_load_timeout_option_impl(None);
|
||||||
|
|
||||||
if matches!(d.state, ResourceState::Ready(_)) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let cv = d.condvar.clone();
|
/// Does the same as [`UntypedResHandle::wait_for_load`] but has a timeout.
|
||||||
drop(d);
|
///
|
||||||
|
/// Returns true if the resource was loaded before hitting the timeout.
|
||||||
let l = cv.0.lock().unwrap();
|
pub fn wait_for_load_timeout(&self, timeout: Duration) -> bool {
|
||||||
let _unused = cv.1.wait(l).unwrap();
|
self.wait_for_load_timeout_option_impl(Some(timeout))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wait for the entire resource, including its dependencies to be loaded.
|
/// Wait for the entire resource, including its dependencies to be loaded.
|
||||||
|
@ -216,7 +213,41 @@ impl UntypedResHandle {
|
||||||
/// This blocks the thread without consuming CPU time; its backed by a
|
/// This blocks the thread without consuming CPU time; its backed by a
|
||||||
/// [`Condvar`](std::sync::Condvar).
|
/// [`Condvar`](std::sync::Condvar).
|
||||||
pub fn wait_recurse_dependencies_load(&self) {
|
pub fn wait_recurse_dependencies_load(&self) {
|
||||||
self.wait_for_load();
|
self.wait_recurse_dependencies_load_timeout_option_impl(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Does the same as [`UntypedResHandle::wait_recurse_dependencies_load`] but has a timeout.
|
||||||
|
///
|
||||||
|
/// Returns true if the resource was loaded before hitting the timeout.
|
||||||
|
pub fn wait_recurse_dependencies_load_timeout(&self, timeout: Duration) -> bool {
|
||||||
|
self.wait_recurse_dependencies_load_timeout_option_impl(Some(timeout))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_for_load_timeout_option_impl(&self, timeout: Option<Duration>) -> bool {
|
||||||
|
let d = self.read();
|
||||||
|
|
||||||
|
if matches!(d.state, ResourceState::Ready(_)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cv = d.condvar.clone();
|
||||||
|
drop(d);
|
||||||
|
|
||||||
|
let l = cv.0.lock().unwrap();
|
||||||
|
|
||||||
|
if let Some(timeout) = timeout {
|
||||||
|
let (_unused, timeout) = cv.1.wait_timeout(l, timeout).unwrap();
|
||||||
|
!timeout.timed_out()
|
||||||
|
} else {
|
||||||
|
let _unused = cv.1.wait(l).unwrap();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_recurse_dependencies_load_timeout_option_impl(&self, timeout: Option<Duration>) -> bool {
|
||||||
|
if !self.wait_for_load_timeout_option_impl(timeout) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let res = self.read();
|
let res = self.read();
|
||||||
match &res.state {
|
match &res.state {
|
||||||
|
@ -226,11 +257,16 @@ impl UntypedResHandle {
|
||||||
// waiting for some resources and finish early.
|
// waiting for some resources and finish early.
|
||||||
while self.recurse_dependency_state().is_loading() {
|
while self.recurse_dependency_state().is_loading() {
|
||||||
for dep in data.recur_dependencies() {
|
for dep in data.recur_dependencies() {
|
||||||
dep.wait_for_load();
|
if !dep.wait_for_load_timeout_option_impl(timeout) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
},
|
},
|
||||||
_ => unreachable!() // the self.wait_for_load ensures that the state is ready
|
// self.wait_for_load at the start ensures that the state is ready
|
||||||
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use notify_debouncer_full::{DebouncedEvent, FileIdMap};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{gltf::ModelLoader, loader::{ImageLoader, LoaderError, ResourceLoader}, resource::ResHandle, ResourceData, ResourceState, UntypedResHandle};
|
use crate::{loader::{ImageLoader, LoaderError, ResourceLoader}, resource::ResHandle, ResourceData, ResourceState, UntypedResHandle};
|
||||||
|
|
||||||
/// A trait for type erased storage of a resource.
|
/// A trait for type erased storage of a resource.
|
||||||
/// Implemented for [`ResHandle<T>`]
|
/// Implemented for [`ResHandle<T>`]
|
||||||
|
@ -82,7 +82,7 @@ impl Default for ResourceManager {
|
||||||
ResourceManagerState {
|
ResourceManagerState {
|
||||||
resources: HashMap::new(),
|
resources: HashMap::new(),
|
||||||
uuid_resources: HashMap::new(),
|
uuid_resources: HashMap::new(),
|
||||||
loaders: vec![ Arc::new(ImageLoader), Arc::new(ModelLoader) ],
|
loaders: vec![ Arc::new(ImageLoader), ],
|
||||||
watchers: HashMap::new(),
|
watchers: HashMap::new(),
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
@ -369,7 +369,7 @@ impl ResourceManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub mod tests {
|
||||||
use std::{io, ops::Deref};
|
use std::{io, ops::Deref};
|
||||||
|
|
||||||
use instant::Instant;
|
use instant::Instant;
|
||||||
|
@ -384,7 +384,7 @@ pub(crate) mod tests {
|
||||||
format!("{manifest}/test_files/img/{path}")
|
format!("{manifest}/test_files/img/{path}")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn busy_wait_resource<R: ResourceData>(handle: &ResHandle<R>, timeout: f32) {
|
pub fn busy_wait_resource<R: ResourceData>(handle: &ResHandle<R>, timeout: f32) {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
while !handle.is_loaded() {
|
while !handle.is_loaded() {
|
||||||
// loop until the image is loaded
|
// loop until the image is loaded
|
||||||
|
@ -398,7 +398,7 @@ pub(crate) mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn busy_wait_resource_reload<R: ResourceData>(handle: &ResHandle<R>, timeout: f32) {
|
pub fn busy_wait_resource_reload<R: ResourceData>(handle: &ResHandle<R>, timeout: f32) {
|
||||||
let version = handle.version();
|
let version = handle.version();
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
|
|
|
@ -1,42 +1 @@
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use thiserror::Error;
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum UriReadError {
|
|
||||||
#[error("IOError: '{0}'")]
|
|
||||||
IoError(io::Error),
|
|
||||||
|
|
||||||
// From is implemented for this field in each loader module
|
|
||||||
#[error("Base64 decoding error: '{0}'")]
|
|
||||||
Base64Decode(base64::DecodeError),
|
|
||||||
|
|
||||||
#[error("Some data was missing from the uri")]
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a buffer's uri string into a byte buffer.
|
|
||||||
///
|
|
||||||
/// * `containing_path`: The path of the containing folder of the buffers "parent",
|
|
||||||
/// the parent being where this buffer is defined in,
|
|
||||||
/// i.e. parent="resources/models/player.gltf", containing="resource/models"
|
|
||||||
pub(crate) fn gltf_read_buffer_uri(containing_path: &str, uri: &str) -> Result<Vec<u8>, UriReadError> {
|
|
||||||
if let Some((mime, data)) = uri.strip_prefix("data")
|
|
||||||
.and_then(|uri| 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)
|
|
||||||
.map_err(UriReadError::Base64Decode)
|
|
||||||
} else {
|
|
||||||
Ok(data.as_bytes().to_vec())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let full_path = format!("{containing_path}/{uri}");
|
|
||||||
std::fs::read(full_path).map_err(UriReadError::IoError)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,3 +10,4 @@ anyhow = "1.0.81"
|
||||||
lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
|
lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
|
||||||
lyra-math = { path = "../lyra-math" }
|
lyra-math = { path = "../lyra-math" }
|
||||||
lyra-reflect = { path = "../lyra-reflect" }
|
lyra-reflect = { path = "../lyra-reflect" }
|
||||||
|
lyra-resource = { path = "../lyra-resource" }
|
|
@ -1,5 +1,6 @@
|
||||||
mod node;
|
mod node;
|
||||||
use lyra_reflect::Reflect;
|
use lyra_reflect::Reflect;
|
||||||
|
use lyra_resource::{ResourceData, UntypedResHandle};
|
||||||
pub use node::*;
|
pub use node::*;
|
||||||
|
|
||||||
mod world_transform;
|
mod world_transform;
|
||||||
|
@ -135,6 +136,23 @@ impl SceneGraph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ResourceData for SceneGraph {
|
||||||
|
fn dependencies(&self) -> Vec<UntypedResHandle> {
|
||||||
|
self.world().view::<&UntypedResHandle>()
|
||||||
|
.iter()
|
||||||
|
.map(|han| han.clone())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a node under a parent node.
|
/// Add a node under a parent node.
|
||||||
///
|
///
|
||||||
/// The spawned entity will have a `ChildOf` relation targeting the provided parent node,
|
/// The spawned entity will have a `ChildOf` relation targeting the provided parent node,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{any::TypeId, ops::Deref};
|
use std::{any::TypeId, ops::Deref};
|
||||||
use lyra_resource::{gltf::{Gltf, Material, Mesh}, FilterMode, ResHandle, Texture, WrappingMode};
|
use lyra_resource::{FilterMode, ResHandle, Texture, WrappingMode};
|
||||||
use lyra_game::scene::SceneGraph;
|
use lyra_game::{scene::SceneGraph, gltf::{Gltf, Material, Mesh, PbrGlossiness, Specular, MeshIndices, AlphaMode}};
|
||||||
use lyra_reflect::Reflect;
|
use lyra_reflect::Reflect;
|
||||||
use lyra_scripting_derive::{lua_wrap_handle, wrap_lua_struct};
|
use lyra_scripting_derive::{lua_wrap_handle, wrap_lua_struct};
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ wrap_lua_struct!(lyra_resource::TextureSampler,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
wrap_lua_struct!(lyra_resource::gltf::PbrGlossiness,
|
wrap_lua_struct!(PbrGlossiness,
|
||||||
// this can be safely skipped since it wont be a component or resource.
|
// this can be safely skipped since it wont be a component or resource.
|
||||||
skip(lua_reflect),
|
skip(lua_reflect),
|
||||||
fields={
|
fields={
|
||||||
|
@ -68,7 +68,7 @@ wrap_lua_struct!(lyra_resource::gltf::PbrGlossiness,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
wrap_lua_struct!(lyra_resource::gltf::Specular,
|
wrap_lua_struct!(Specular,
|
||||||
// this can be safely skipped since it wont be a component or resource.
|
// this can be safely skipped since it wont be a component or resource.
|
||||||
skip(lua_reflect),
|
skip(lua_reflect),
|
||||||
fields={
|
fields={
|
||||||
|
@ -104,13 +104,13 @@ lua_wrap_handle!(Mesh,
|
||||||
let table = lua.create_table()?;
|
let table = lua.create_table()?;
|
||||||
|
|
||||||
match &data.indices {
|
match &data.indices {
|
||||||
Some(lyra_resource::gltf::MeshIndices::U16(v)) => {
|
Some(MeshIndices::U16(v)) => {
|
||||||
for (i, ind) in v.iter().enumerate() {
|
for (i, ind) in v.iter().enumerate() {
|
||||||
let i = i as i64 + 1; // lua indexes start at 1
|
let i = i as i64 + 1; // lua indexes start at 1
|
||||||
table.raw_set(i, *ind)?;
|
table.raw_set(i, *ind)?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(lyra_resource::gltf::MeshIndices::U32(v)) => {
|
Some(MeshIndices::U32(v)) => {
|
||||||
for (i, ind) in v.iter().enumerate() {
|
for (i, ind) in v.iter().enumerate() {
|
||||||
let i = i as i64 + 1; // lua indexes start at 1
|
let i = i as i64 + 1; // lua indexes start at 1
|
||||||
table.raw_set(i, *ind)?;
|
table.raw_set(i, *ind)?;
|
||||||
|
@ -178,9 +178,9 @@ lua_wrap_handle!(Material,
|
||||||
alpha_cutoff,
|
alpha_cutoff,
|
||||||
(alpha_mode, {
|
(alpha_mode, {
|
||||||
match data.alpha_mode {
|
match data.alpha_mode {
|
||||||
lyra_resource::gltf::AlphaMode::Opaque => "opaque",
|
AlphaMode::Opaque => "opaque",
|
||||||
lyra_resource::gltf::AlphaMode::Mask => "mask",
|
AlphaMode::Mask => "mask",
|
||||||
lyra_resource::gltf::AlphaMode::Blend => "blend",
|
AlphaMode::Blend => "blend",
|
||||||
}.into_lua(lua)
|
}.into_lua(lua)
|
||||||
}),
|
}),
|
||||||
(specular, {
|
(specular, {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use lyra_engine::{
|
use lyra_engine::{
|
||||||
assets::{gltf::Gltf, ResourceManager}, ecs::query::View, game::App, input::{
|
assets::ResourceManager, gltf::Gltf, ecs::query::View, game::App, input::{
|
||||||
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
||||||
InputActionPlugin, KeyCode, LayoutId,
|
InputActionPlugin, KeyCode, LayoutId,
|
||||||
}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{
|
}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
use lyra_engine::{
|
use lyra_engine::{
|
||||||
assets::{gltf::Gltf, ResourceManager},
|
assets::ResourceManager, gltf::Gltf,
|
||||||
ecs::{
|
ecs::{
|
||||||
query::{Res, View},
|
query::{Res, View},
|
||||||
system::{BatchedSystem, Criteria, CriteriaSchedule, IntoSystem},
|
system::{BatchedSystem, Criteria, CriteriaSchedule, IntoSystem},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use lyra_engine::{
|
use lyra_engine::{
|
||||||
assets::{gltf::Gltf, ResourceManager}, game::App, input::{
|
assets::ResourceManager, gltf::Gltf, game::App, input::{
|
||||||
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
||||||
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
|
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
|
||||||
}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{
|
}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use lyra_engine::{assets::{gltf::Gltf, ResourceManager}, ecs::query::{Res, View}, game::App, input::{Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput}, math::{self, Quat, Transform, Vec3}, render::light::{directional::DirectionalLight, PointLight}, scene::{CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN}, DeltaTime};
|
use lyra_engine::{assets::ResourceManager, gltf::Gltf, ecs::query::{Res, View}, game::App, input::{Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput}, math::{self, Quat, Transform, Vec3}, render::light::{directional::DirectionalLight, PointLight}, scene::{CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN}, DeltaTime};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use lyra_engine::{
|
use lyra_engine::{
|
||||||
assets::{gltf::Gltf, ResourceManager},
|
assets::ResourceManager, gltf::Gltf,
|
||||||
game::App,
|
game::App,
|
||||||
input::{
|
input::{
|
||||||
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use lyra_engine::{
|
use lyra_engine::{
|
||||||
assets::{gltf::Gltf, ResourceManager}, ecs::query::View, game::App, input::{
|
assets::ResourceManager, gltf::Gltf, ecs::query::View, game::App, input::{
|
||||||
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
||||||
InputActionPlugin, KeyCode, LayoutId,
|
InputActionPlugin, KeyCode, LayoutId,
|
||||||
}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{
|
}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::ptr::NonNull;
|
||||||
|
|
||||||
use lyra_engine::assets::ResourceManager;
|
use lyra_engine::assets::ResourceManager;
|
||||||
use lyra_engine::{
|
use lyra_engine::{
|
||||||
assets::gltf::Gltf,
|
gltf::Gltf,
|
||||||
ecs::{
|
ecs::{
|
||||||
query::{Res, View},
|
query::{Res, View},
|
||||||
system::{Criteria, CriteriaSchedule, IntoSystem},
|
system::{Criteria, CriteriaSchedule, IntoSystem},
|
||||||
|
|
Loading…
Reference in New Issue