tirbofish/dropbear · diff
optimised model loading, but am using the gltf crate instead of russimp-ng.
this lowers down the import speed for 2 models from 10 seconds to 3 seconds. I'm not exactly sure how to optimise it, but im sure as hell that i need to implement async/await into the project. i suppose thats for another time...
this should resolve Optimise Model Loading
Fixes #41 for the timebeing
Signature present but could not be verified.
Unverified
@@ -51,6 +51,10 @@ winit = { version = "0.30", features = [] } zip = "5.1" walkdir = "2.3" wasmer = { version = "6.1.0-rc.3" } +rayon = "1.11" + +gltf = "1" +thiserror = "2.0" [workspace.dependencies.image] version = "0.25" @@ -23,7 +23,7 @@ gilrs.workspace = true glam.workspace = true log.workspace = true log-once.workspace = true -russimp-ng.workspace = true +# russimp-ng.workspace = true serde.workspace = true spin_sleep.workspace = true wgpu.workspace = true @@ -32,6 +32,8 @@ hecs.workspace = true # once_cell.workspace = true parking_lot.workspace = true lazy_static.workspace = true +gltf.workspace = true +rayon.workspace = true [target.'cfg(not(target_os = "android"))'.dependencies] rfd.workspace = true @@ -1,4 +1,4 @@ -use std::{fs, path::PathBuf}; +use std::{fs, path::PathBuf, time::Instant}; use egui::Context; use glam::{DMat4, DQuat, DVec3, Mat3}; @@ -23,6 +23,7 @@ pub struct Graphics<'a> { pub view: &'a TextureView, pub encoder: &'a mut CommandEncoder, pub screen_size: (f32, f32), + pub diffuse_sampler: Sampler, } pub const NO_TEXTURE: &'static [u8] = include_bytes!("../../resources/no-texture.png"); @@ -35,11 +36,23 @@ impl<'a> Graphics<'a> { encoder: &'a mut CommandEncoder, ) -> Self { let screen_size = (state.config.width as f32, state.config.height as f32); + let diffuse_sampler = state + .device + .create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + ..Default::default() + }); Self { state, view, encoder, screen_size, + diffuse_sampler, } } @@ -309,9 +322,99 @@ impl Texture { self.bind_group.as_ref().unwrap() } + pub(crate) fn create_texture_from_rgba_data( + graphics: &Graphics, + rgba_data: &[u8], + dimensions: (u32, u32), + ) -> Texture { + let texture_size = wgpu::Extent3d { + width: dimensions.0, + height: dimensions.1, + depth_or_array_layers: 1, + }; + + let create_start = Instant::now(); + let diffuse_texture = graphics.state.device.create_texture(&wgpu::TextureDescriptor { + label: Some("diffuse_texture"), + size: texture_size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8Unorm, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + view_formats: &[], + }); + println!("Creating new diffuse texture took {:?}", create_start.elapsed()); + + let write_start = Instant::now(); + graphics.state.queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: &diffuse_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + rgba_data, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(4 * dimensions.0), + rows_per_image: Some(dimensions.1), + }, + texture_size, + ); + println!("Writing texture to graphics queue took {:?}", write_start.elapsed()); + + let sampler_start = Instant::now(); + let diffuse_sampler = graphics.state.device.create_sampler(&wgpu::SamplerDescriptor { + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + ..Default::default() + }); + println!("Creating sampler took {:?}", sampler_start.elapsed()); + + let view = diffuse_texture.create_view(&wgpu::TextureViewDescriptor::default()); + + let bind_group_start = Instant::now(); + let diffuse_bind_group = graphics.state.device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &graphics.state.texture_bind_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&diffuse_sampler), + }, + ], + label: Some("texture_bind_group"), + }); + println!("Creating diffuse bind group took {:?}", bind_group_start.elapsed()); + + println!("Done creating texture"); + + Texture { + texture: diffuse_texture, + sampler: diffuse_sampler, + view, + size: texture_size, + bind_group: Some(diffuse_bind_group), + layout: Some(graphics.state.texture_bind_layout.clone()), + } + } + pub fn new(graphics: &Graphics, diffuse_bytes: &[u8]) -> Self { + let start = Instant::now(); let diffuse_image = image::load_from_memory(diffuse_bytes).unwrap(); + println!("Loading image to memory: {:?}", start.elapsed()); + + let start = Instant::now(); let diffuse_rgba = diffuse_image.to_rgba8(); + println!("Converting diffuse image to rgba8 took {:?}", start.elapsed()); let dimensions = diffuse_image.dimensions(); let texture_size = wgpu::Extent3d { @@ -320,6 +423,7 @@ impl Texture { depth_or_array_layers: 1, }; + let start = Instant::now(); let diffuse_texture = graphics .state .device @@ -333,7 +437,9 @@ impl Texture { usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, view_formats: &[], }); + println!("Creating new diffuse texture took {:?}", start.elapsed()); + let start = Instant::now(); graphics.state.queue.write_texture( wgpu::TexelCopyTextureInfo { texture: &diffuse_texture, @@ -349,7 +455,9 @@ impl Texture { }, texture_size, ); + println!("Writing texture to graphics queue took {:?}", start.elapsed()); + let start = Instant::now(); let diffuse_texture_view = diffuse_texture.create_view(&TextureViewDescriptor::default()); let diffuse_sampler = graphics .state @@ -363,7 +471,9 @@ impl Texture { mipmap_filter: wgpu::FilterMode::Nearest, ..Default::default() }); + println!("Creating sampler took {:?}", start.elapsed()); + let start = Instant::now(); let diffuse_bind_group = graphics .state @@ -382,7 +492,8 @@ impl Texture { ], label: Some("texture_bind_group"), }); - + println!("Creating diffuse bind group took {:?}", start.elapsed()); + println!("Done creating texture"); Self { bind_group: Some(diffuse_bind_group), layout: Some(graphics.state.texture_bind_layout.clone()), @@ -232,7 +232,7 @@ impl Light { let cube_model = Model::load_from_memory( graphics, - include_bytes!("../../resources/cube.obj").to_vec(), + include_bytes!("../../resources/cube.glb").to_vec(), label.clone(), ) .unwrap(); @@ -1,15 +1,18 @@ -use crate::graphics::{Graphics, NO_MODEL, Texture}; +use crate::graphics::{Graphics, Texture}; use crate::utils::ResourceReference; +use image::GenericImageView; use lazy_static::lazy_static; use parking_lot::Mutex; -use russimp_ng::{ - Vector3D, - material::{DataContent, TextureType}, - scene::{PostProcess, Scene}, -}; +// use russimp_ng::{ +// Vector3D, +// material::{DataContent, TextureType}, +// scene::{PostProcess, Scene}, +// }; use std::collections::HashMap; +use std::time::Instant; use std::{mem, ops::Range, path::PathBuf}; use wgpu::{BufferAddress, VertexAttribute, VertexBufferLayout, util::DeviceExt}; +use rayon::prelude::*; pub const GREY_TEXTURE_BYTES: &'static [u8] = include_bytes!("../../resources/grey.png"); @@ -45,118 +48,132 @@ pub struct Mesh { impl Model { pub fn load_from_memory( graphics: &Graphics<'_>, - buffer: Vec<u8>, - label: Option<&str>, + buffer: impl AsRef<[u8]>, + label: Option<&str> ) -> anyhow::Result<Model> { + let start = Instant::now(); let cache_key = label.unwrap_or("default").to_string(); - // Check memory cache first if let Some(cached_model) = MEMORY_MODEL_CACHE.lock().get(&cache_key) { log::debug!("Model loaded from memory cache: {:?}", cache_key); return Ok(cached_model.clone()); } log::debug!("Loading from memory"); - let res_ref = ResourceReference::from_bytes(buffer.clone()); - - let scene = match Scene::from_buffer( - buffer.as_slice(), - vec![ - PostProcess::Triangulate, - PostProcess::FlipUVs, - PostProcess::GenerateNormals, - ], - "obj", - ) { - Ok(v) => v, - Err(_) => { - log::error!("Unable to load {:?} from memory", label); - Scene::from_buffer( - NO_MODEL, - vec![ - PostProcess::Triangulate, - PostProcess::FlipUVs, - PostProcess::GenerateNormals, - ], - "glb", - )? - } - }; + let res_ref = ResourceReference::from_bytes(buffer.as_ref()); - let mut materials = Vec::new(); - for m in &scene.materials { - let mut name = String::new(); - let diffuse_bytes_opt = m - .textures - .iter() - .find(|(t_type, _)| **t_type == TextureType::Diffuse) - .and_then(|(_, tex)| { - name = tex.borrow().filename.clone(); - match &tex.borrow().data { - DataContent::Bytes(b) => Some(b.clone()), - DataContent::Texel(_) => { - log::warn!("Skipping texel-based texture for material '{}'", &name); - None - } - } - }); + let (gltf, buffers, _images) = gltf::import_slice(buffer.as_ref())?; + let mut meshes = Vec::new(); - let diffuse_texture = if let Some(bytes) = diffuse_bytes_opt { - Texture::new(graphics, &bytes) - } else { - if !name.is_empty() { - log::warn!( - "Error loading material {}, using default missing texture", - name - ); - } else { - log::warn!("Error loading material, using default missing texture"); + let mut texture_data = Vec::new(); + for material in gltf.materials() { + log::debug!("Processing material: {:?}", material.name()); + let material_name = material.name().unwrap_or("Unnamed Material").to_string(); + + let image_data = if let Some(pbr) = material.pbr_metallic_roughness().base_color_texture() { + let texture_info = pbr.texture(); + let image = texture_info.source(); + match image.source() { + gltf::image::Source::View { view, mime_type: _ } => { + let buffer_data = &buffers[view.buffer().index()]; + let start = view.offset(); + let end = start + view.length(); + buffer_data[start..end].to_vec() + } + gltf::image::Source::Uri { uri, mime_type: _ } => { + log::warn!("External URI textures not supported: {}", uri); + GREY_TEXTURE_BYTES.to_vec() + } } - Texture::new(graphics, GREY_TEXTURE_BYTES) + } else { + GREY_TEXTURE_BYTES.to_vec() }; + + texture_data.push((material_name, image_data)); + } + + if texture_data.is_empty() { + texture_data.push(("Default".to_string(), GREY_TEXTURE_BYTES.to_vec())); + } + let parallel_start = Instant::now(); + let processed_textures: Vec<_> = texture_data + .into_par_iter() + .map(|(material_name, image_data)| { + let material_start = Instant::now(); + + let load_start = Instant::now(); + let diffuse_image = image::load_from_memory(&image_data).unwrap(); + println!("Loading image to memory: {:?}", load_start.elapsed()); + + let rgba_start = Instant::now(); + let diffuse_rgba = diffuse_image.to_rgba8(); + println!("Converting diffuse image to rgba8 took {:?}", rgba_start.elapsed()); + + let dimensions = diffuse_image.dimensions(); + + println!("Parallel processing of material '{}' took: {:?}", material_name, material_start.elapsed()); + + (material_name, diffuse_rgba.into_raw(), dimensions) + }) + .collect(); + + println!("Total parallel image processing took: {:?}", parallel_start.elapsed()); + + let mut materials = Vec::new(); + for (material_name, rgba_data, dimensions) in processed_textures { + let start = Instant::now(); + + let diffuse_texture = Texture::create_texture_from_rgba_data(graphics, &rgba_data, dimensions); let bind_group = diffuse_texture.bind_group().to_owned(); + materials.push(Material { - name: name, + name: material_name, diffuse_texture, bind_group, }); + + println!("Time to create GPU texture: {:?}", start.elapsed()); } - let mut meshes = Vec::new(); - for mesh in &scene.meshes { - let vertices: Vec<ModelVertex> = mesh - .vertices - .iter() - .enumerate() - .map(|(i, v)| { - let normal = mesh.normals.get(i).copied().unwrap_or(Vector3D { - x: 0.0, - y: 1.0, - z: 0.0, - }); - let tex_coords = mesh - .texture_coords - .get(0) - .and_then(|coords| coords.as_ref().and_then(|vec| vec.get(i))) - .map(|tc| [tc.x, tc.y]) - .unwrap_or([0.0, 0.0]); - ModelVertex { - position: [v.x, v.y, v.z], - tex_coords, - normal: [normal.x, normal.y, normal.z], - } - }) - .collect(); - - let indices: Vec<u32> = mesh - .faces - .iter() - .flat_map(|f| f.0.iter().copied()) - .collect(); - - let vertex_buffer = - graphics + for mesh in gltf.meshes() { + log::debug!("Processing mesh: {:?}", mesh.name()); + for primitive in mesh.primitives() { + let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()])); + + let positions: Vec<[f32; 3]> = reader + .read_positions() + .ok_or_else(|| anyhow::anyhow!("Mesh missing positions"))? + .collect(); + + let normals: Vec<[f32; 3]> = reader + .read_normals() + .map(|iter| iter.collect()) + .unwrap_or_else(|| vec![[0.0, 1.0, 0.0]; positions.len()]); + + let tex_coords: Vec<[f32; 2]> = reader + .read_tex_coords(0) + .map(|iter| iter.into_f32().collect()) + .unwrap_or_else(|| vec![[0.0, 0.0]; positions.len()]); + + let vertices: Vec<ModelVertex> = positions + .iter() + .zip(normals.iter()) + .zip(tex_coords.iter()) + .map(|((pos, norm), tex)| ModelVertex { + position: *pos, + normal: *norm, + tex_coords: *tex, + }) + .collect(); + + let indices: Vec<u32> = reader + .read_indices() + .ok_or_else(|| anyhow::anyhow!("Mesh missing indices"))? + .into_u32() + .collect(); + + let vertex_buffer = graphics .state .device .create_buffer_init(&wgpu::util::BufferInitDescriptor { @@ -164,8 +181,8 @@ impl Model { contents: bytemuck::cast_slice(&vertices), usage: wgpu::BufferUsages::VERTEX, }); - let index_buffer = - graphics + + let index_buffer = graphics .state .device .create_buffer_init(&wgpu::util::BufferInitDescriptor { @@ -174,28 +191,29 @@ impl Model { usage: wgpu::BufferUsages::INDEX, }); - meshes.push(Mesh { - name: mesh.name.clone(), - vertex_buffer, - index_buffer, - num_elements: indices.len() as u32, - material: mesh.material_index as usize, - }); + let material_index = primitive.material().index().unwrap_or(0); + + meshes.push(Mesh { + name: mesh.name().unwrap_or("Unnamed Mesh").to_string(), + vertex_buffer, + index_buffer, + num_elements: indices.len() as u32, + material: material_index, + }); + } } + log::debug!("Successfully loaded model [{:?}]", label); let model = Model { meshes, materials, - label: if let Some(l) = label { - l.to_string() - } else { - String::from("No named model") - }, + label: label.unwrap_or("No named model").to_string(), path: res_ref, }; MEMORY_MODEL_CACHE.lock().insert(cache_key, model.clone()); log::debug!("Model cached from memory: {:?}", label); + log::debug!("Took {:?} to load model: {:?}", start.elapsed(), label); Ok(model) } @@ -214,148 +232,8 @@ impl Model { return Ok(cached_model.clone()); } - let scene = match Scene::from_file( - path.to_str().unwrap(), - vec![ - PostProcess::Triangulate, - PostProcess::FlipUVs, - PostProcess::GenerateNormals, - ], - ) { - Ok(v) => v, - Err(_) => { - log::error!("Unable to locate model [{}]", path.display()); - Scene::from_buffer( - NO_MODEL, - vec![ - PostProcess::Triangulate, - PostProcess::FlipUVs, - PostProcess::GenerateNormals, - ], - "glb", - )? - } - }; - - let mut materials = Vec::new(); - for m in &scene.materials { - let mut name = String::new(); - let diffuse_bytes_opt = m - .textures - .iter() - .find(|(t_type, _)| **t_type == TextureType::Diffuse) - .and_then(|(_, tex)| { - name = tex.borrow().filename.clone(); - match &tex.borrow().data { - DataContent::Bytes(b) => Some(b.clone()), - DataContent::Texel(_) => { - log::warn!("Skipping texel-based texture for material '{}'", &name); - None - } - } - }); - - let diffuse_texture = if let Some(bytes) = diffuse_bytes_opt { - Texture::new(graphics, &bytes) - } else { - if !name.is_empty() { - log::warn!( - "Error loading material {}, using default missing texture", - name - ); - } else { - log::warn!("Error loading material, using default missing texture"); - } - Texture::new(graphics, GREY_TEXTURE_BYTES) - }; - - let bind_group = diffuse_texture.bind_group().to_owned(); - materials.push(Material { - name: name, - diffuse_texture, - bind_group, - }); - } - - let mut meshes = Vec::new(); - for mesh in &scene.meshes { - let vertices: Vec<ModelVertex> = mesh - .vertices - .iter() - .enumerate() - .map(|(i, v)| { - let normal = mesh.normals.get(i).copied().unwrap_or(Vector3D { - x: 0.0, - y: 1.0, - z: 0.0, - }); - let tex_coords = mesh - .texture_coords - .get(0) - .and_then(|coords| coords.as_ref().and_then(|vec| vec.get(i))) - .map(|tc| [tc.x, tc.y]) - .unwrap_or([0.0, 0.0]); - ModelVertex { - position: [v.x, v.y, v.z], - tex_coords, - normal: [normal.x, normal.y, normal.z], - } - }) - .collect(); - - let indices: Vec<u32> = mesh - .faces - .iter() - .flat_map(|f| f.0.iter().copied()) - .collect(); - - let vertex_buffer = - graphics - .state - .device - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some(&format!("{:?} Vertex Buffer", file_name)), - contents: bytemuck::cast_slice(&vertices), - usage: wgpu::BufferUsages::VERTEX, - }); - let index_buffer = - graphics - .state - .device - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some(&format!("{:?} Index Buffer", file_name)), - contents: bytemuck::cast_slice(&indices), - usage: wgpu::BufferUsages::INDEX, - }); - - meshes.push(Mesh { - name: mesh.name.clone(), - vertex_buffer, - index_buffer, - num_elements: indices.len() as u32, - material: mesh.material_index as usize, - }); - } - log::debug!("Successfully loaded model [{:?}]", file_name); - let model = Model { - meshes, - materials, - label: if let Some(l) = label { - l.to_string() - } else { - String::from( - file_name - .unwrap() - .to_str() - .unwrap() - .split(".") - .into_iter() - .next() - .unwrap(), - ) - }, - path: ResourceReference::from_path(path).unwrap(), - }; + let buffer = std::fs::read(path)?; + let model = Self::load_from_memory(graphics, buffer, label)?; MODEL_CACHE.lock().insert(path_str, model.clone()); log::debug!("Model cached and loaded: {:?}", file_name); @@ -76,9 +76,9 @@ impl ResourceReference { } /// Creates a new `ResourceReference` from bytes - pub fn from_bytes(bytes: Vec<u8>) -> Self { + pub fn from_bytes(bytes: impl AsRef<[u8]>,) -> Self { Self { - ref_type: ResourceReferenceType::Bytes(bytes), + ref_type: ResourceReferenceType::Bytes(bytes.as_ref().to_vec()), } } @@ -19,6 +19,7 @@ pub struct InputState { pub pressed_keys: HashSet<KeyCode>, pub mouse_delta: Option<(f64, f64)>, pub is_cursor_locked: bool, + pub last_mouse_pos: Option<(f64, f64)>, } impl Default for InputState { @@ -37,6 +38,7 @@ impl InputState { double_press_threshold: Duration::from_millis(300), mouse_delta: None, is_cursor_locked: false, + last_mouse_pos: Default::default(), } } @@ -236,47 +236,61 @@ impl Keyboard for Editor { } impl Mouse for Editor { + #[cfg(target_os = "linux")] + // this impl doesn't have the mouse being recentered back to the center (to be fixed), this works as a usable alternative fn mouse_move(&mut self, position: PhysicalPosition<f64>) { if (self.is_viewport_focused && matches!(self.viewport_mode, ViewportMode::CameraMove)) - || (matches!(self.editor_state, EditorState::Playing) - && !self.input_state.is_cursor_locked) + || (matches!(self.editor_state, EditorState::Playing) && !self.input_state.is_cursor_locked) { - if let Some(window) = &self.window { - let size = window.inner_size(); - let center = - PhysicalPosition::new(size.width as f64 / 2.0, size.height as f64 / 2.0); - - let dx = position.x - center.x; - let dy = position.y - center.y; + if let Some(last_pos) = self.input_state.last_mouse_pos { + let dx = position.x - last_pos.0; + let dy = position.y - last_pos.1; + if let Some(active_camera) = self.active_camera { - if let Ok(mut q) = self.world.query_one::<( - &mut Camera, - &CameraComponent, - Option<&CameraFollowTarget>, - )>(active_camera) - { + if let Ok(mut q) = self.world.query_one::<(&mut Camera, &CameraComponent, Option<&CameraFollowTarget>)>(active_camera) { if let Some((camera, _, _)) = q.get() { camera.track_mouse_delta(dx, dy); - } else { - log_once::warn_once!( - "Unable to fetch the query result of camera: {:?}", - active_camera - ) } - } else { - log_once::warn_once!( - "Unable to query camera, component and option<camerafollowtarget> for active camera: {:?}", - active_camera - ); } - } else { - log_once::warn_once!("No active camera found"); } + } + self.input_state.last_mouse_pos = Some((position.x, position.y)); + } + + self.input_state.mouse_pos = (position.x, position.y); + } - let _ = window.set_cursor_position(center); + #[cfg(not(target_os = "linux"))] + // for some reason, this doesn't work on linux but works on windows (not tested on anywhere else) + fn mouse_move(&mut self, position: PhysicalPosition<f64>) { + if (self.is_viewport_focused && matches!(self.viewport_mode, ViewportMode::CameraMove)) + || (matches!(self.editor_state, EditorState::Playing) && !self.input_state.is_cursor_locked) + { + if let Some(window) = &self.window { + let size = window.inner_size(); + let center = PhysicalPosition::new(size.width as f64 / 2.0, size.height as f64 / 2.0); + + let distance_from_center = ((position.x - center.x).powi(2) + (position.y - center.y).powi(2)).sqrt(); + + if distance_from_center > 5.0 { + let dx = position.x - center.x; + let dy = position.y - center.y; + + if let Some(active_camera) = self.active_camera { + if let Ok(mut q) = self.world.query_one::<(&mut Camera, &CameraComponent, Option<&CameraFollowTarget>)>(active_camera) { + if let Some((camera, _, _)) = q.get() { + camera.track_mouse_delta(dx, dy); + } + } + } + + let _ = window.set_cursor_position(center); + } + window.set_cursor_visible(false); } } + self.input_state.mouse_pos = (position.x, position.y); } @@ -363,7 +363,10 @@ impl Editor { ui.separator(); if ui.button("Quit").clicked() { match self.save_project_config() { - Ok(_) => {} + Ok(_) => { + log::info!("Saved, quitting..."); + std::process::exit(0); + } Err(e) => { fatal!("Error saving project: {}", e); } @@ -270,15 +270,60 @@ impl Scene for Editor { match &self.signal { Signal::Paste(scene_entity) => { - match AdoptedEntity::new( - graphics, - &scene_entity - .model_path - .to_project_path(self.project_path.clone().unwrap()) - .unwrap(), - Some(&scene_entity.label), - ) { - Ok(adopted) => { + match &scene_entity.model_path.ref_type { + dropbear_engine::utils::ResourceReferenceType::None => { + warn!("Resource has a reference type of None"); + self.signal = Signal::None; + }, + dropbear_engine::utils::ResourceReferenceType::File(reference) => { + match &scene_entity.model_path.to_project_path(self.project_path.clone().unwrap()) { + Some(v) => { + match AdoptedEntity::new( + graphics, + v, + Some(&scene_entity.label), + ) { + Ok(adopted) => { + let entity_id = Arc::get_mut(&mut self.world).unwrap().spawn(( + adopted, + scene_entity.transform, + ModelProperties::default(), + )); + self.selected_entity = Some(entity_id); + log::debug!( + "Successfully paste-spawned {} with ID {:?}", + scene_entity.label, + entity_id + ); + + success_without_console!("Paste!"); + self.signal = Signal::Copy(scene_entity.clone()); + } + Err(e) => { + warn!("Failed to paste-spawn {}: {}", scene_entity.label, e); + } + } + }, + None => { + fatal!("Unable to convert resource reference [{}] to project related path", reference); + self.signal = Signal::None; + }, + }; + }, + dropbear_engine::utils::ResourceReferenceType::Bytes(bytes) => { + let model = match Model::load_from_memory(graphics, bytes, Some(&scene_entity.label)) { + Ok(v) => v, + Err(e) => { + fatal!("Unable to load from memory: {}", e); + self.signal = Signal::None; + return; + }, + }; + let adopted = AdoptedEntity::adopt( + graphics, + model, + Some(&scene_entity.label), + ); let entity_id = Arc::get_mut(&mut self.world).unwrap().spawn(( adopted, scene_entity.transform, @@ -293,9 +338,11 @@ impl Scene for Editor { success_without_console!("Paste!"); self.signal = Signal::Copy(scene_entity.clone()); - } - Err(e) => { - warn!("Failed to paste-spawn {}: {}", scene_entity.label, e); + }, + _ => { + warn!("Unable to copy, not of bytes or path"); + self.signal = Signal::None; + return; } } } @@ -947,32 +994,7 @@ impl Scene for Editor { log::debug!("Creating new cube"); let model = Model::load_from_memory( graphics, - include_bytes!("../../../resources/cube.obj").to_vec(), - Some("Cube") - ); - match model { - Ok(model) => { - let cube = AdoptedEntity::adopt( - graphics, - model, - Some("Cube") - ); - Arc::get_mut(&mut self.world).unwrap().spawn((cube, Transform::new(), ModelProperties::new())); - } - Err(e) => { - fatal!("Failed to load cube model: {}", e); - } - } - success!("Created new cube"); - - self.signal = Signal::None; - } - - if ui.add_sized([ui.available_width(), 30.0], egui::Button::new("Cube")).clicked() { - log::debug!("Creating new cube"); - let model = Model::load_from_memory( - graphics, - include_bytes!("../../../resources/cube.obj").to_vec(), + include_bytes!("../../../resources/cube.glb"), Some("Cube") ); match model { @@ -1 +1 @@ -Subproject commit d0b8e1730dc73b4ec869cc00e213d3de0f305735 +Subproject commit 5cb98c0d508c7f8fd7d6a268d101d6e0fde0f41f Binary files /dev/null and b/resources/cube.glb differ @@ -1,52 +0,0 @@ -# Blender v3.0.0 OBJ File: '' -# www.blender.org -mtllib Cube.mtl -o Cube_Scene -v -0.500000 -0.500000 0.500000 -v -0.500000 0.500000 0.500000 -v 0.500000 0.500000 0.500000 -v 0.500000 -0.500000 0.500000 -v -0.500000 0.500000 -0.500000 -v 0.500000 0.500000 -0.500000 -v -0.500000 -0.500000 -0.500000 -v 0.500000 -0.500000 -0.500000 -vt 0.000000 0.000000 -vt 1.000000 1.000000 -vt 0.000000 1.000000 -vt 1.000000 0.000000 -vt 0.000000 0.000000 -vt 1.000000 1.000000 -vt 0.000000 1.000000 -vt 1.000000 0.000000 -vt 0.000000 0.000000 -vt 1.000000 1.000000 -vt 0.000000 1.000000 -vt 1.000000 0.000000 -vt 0.000000 0.000000 -vt 1.000000 1.000000 -vt 0.000000 1.000000 -vt 1.000000 0.000000 -vt 0.000000 0.000000 -vt 0.000000 1.000000 -vt 1.000000 1.000000 -vt 1.000000 0.000000 -vn 0.0000 -0.0000 1.0000 -vn 0.0000 1.0000 0.0000 -vn 0.0000 0.0000 -1.0000 -vn 0.0000 -1.0000 -0.0000 -vn 1.0000 0.0000 0.0000 -vn -1.0000 0.0000 0.0000 -usemtl Gray -s 1 -f 1/1/1 3/2/1 2/3/1 -f 1/1/1 4/4/1 3/2/1 -f 2/5/2 6/6/2 5/7/2 -f 2/5/2 3/8/2 6/6/2 -f 5/9/3 8/10/3 7/11/3 -f 5/9/3 6/12/3 8/10/3 -f 7/13/4 4/14/4 1/15/4 -f 7/13/4 8/16/4 4/14/4 -f 4/17/5 6/6/5 3/18/5 -f 4/17/5 8/16/5 6/6/5 -f 7/13/6 2/19/6 5/7/6 -f 7/13/6 1/20/6 2/19/6