tirbofish/dropbear · diff
skinned animation officially works now (hooray!!!)
Signature present but could not be verified.
Unverified
@@ -321,7 +321,6 @@ impl AnimationComponent { } } - /// Recursively calculates and caches the global world matrix for a node. fn resolve_global_transform( &self, node_idx: usize, @@ -1164,6 +1164,7 @@ pub trait DrawModel<'a> { model: &'a Model, camera_bind_group: &'a wgpu::BindGroup, light_bind_group: &'a wgpu::BindGroup, + skin_bind_group: Option<&'a wgpu::BindGroup>, ); fn draw_model_instanced( &mut self, @@ -1171,6 +1172,7 @@ pub trait DrawModel<'a> { instances: Range<u32>, camera_bind_group: &'a wgpu::BindGroup, light_bind_group: &'a wgpu::BindGroup, + skin_bind_group: Option<&'a wgpu::BindGroup>, ); } @@ -1216,8 +1218,9 @@ where model: &'b Model, camera_bind_group: &'b wgpu::BindGroup, light_bind_group: &'a wgpu::BindGroup, + skin_bind_group: Option<&'a wgpu::BindGroup>, ) { - self.draw_model_instanced(model, 0..1, camera_bind_group, light_bind_group); + self.draw_model_instanced(model, 0..1, camera_bind_group, light_bind_group, skin_bind_group); } fn draw_model_instanced( @@ -1226,6 +1229,7 @@ where instances: Range<u32>, camera_bind_group: &'b wgpu::BindGroup, light_bind_group: &'a wgpu::BindGroup, + skin_bind_group: Option<&'a wgpu::BindGroup>, ) { for mesh in &model.meshes { let material = &model.materials[mesh.material]; @@ -1235,7 +1239,7 @@ where instances.clone(), camera_bind_group, light_bind_group, - None, // Provide an AnimationComponent if available in a future update + skin_bind_group, ); } } @@ -5,6 +5,7 @@ use glam::{DMat4, Mat4}; use wgpu::util::DeviceExt; use std::collections::HashMap; use std::sync::Arc; +use std::{fs, path::{Path, PathBuf}}; use super::*; use crate::signal::SignalController; use crate::spawn::PendingSpawnController; @@ -19,7 +20,7 @@ use dropbear_engine::{ use eucalyptus_core::states::{Label, WorldLoadingStatus}; use log; use parking_lot::Mutex; -use winit::{event_loop::ActiveEventLoop, keyboard::KeyCode}; +use winit::{event::WindowEvent, event_loop::ActiveEventLoop, keyboard::KeyCode}; use eucalyptus_core::physics::collider::ColliderGroup; use eucalyptus_core::physics::collider::ColliderShapeKey; use eucalyptus_core::physics::collider::shader::{ColliderInstanceRaw, create_wireframe_geometry}; @@ -532,17 +533,13 @@ impl Scene for Editor { } render_pass.set_bind_group(3, globals_bind_group, &[]); - for mesh in &model.meshes { - let material = &model.materials[mesh.material]; - render_pass.draw_mesh_instanced( - mesh, - material, - 0..instance_count, - &camera.bind_group, - lcp.bind_group(), - Some(default_skinning_bind_group), - ); - } + render_pass.draw_model_instanced( + model, + 0..instance_count, + &camera.bind_group, + lcp.bind_group(), + Some(default_skinning_bind_group), + ); } } @@ -599,17 +596,13 @@ impl Scene for Editor { render_pass.set_vertex_buffer(1, instance_buffer.slice(1)); render_pass.set_bind_group(3, globals_bind_group, &[]); - for mesh in &model.meshes { - let material = &model.materials[mesh.material]; - render_pass.draw_mesh_instanced( - mesh, - material, - 0..1, - &camera.bind_group, - lcp.bind_group(), - Some(&skin_bind_group), - ); - } + render_pass.draw_model_instanced( + model, + 0..1, + &camera.bind_group, + lcp.bind_group(), + Some(&skin_bind_group), + ); } } @@ -798,12 +791,123 @@ impl Scene for Editor { fn exit(&mut self, _event_loop: &ActiveEventLoop) {} + fn handle_event(&mut self, event: &WindowEvent) { + match event { + WindowEvent::DroppedFile(path) => { + log::debug!("Dropped file: {}", path.display()); + self.handle_file_drop(path); + }, + WindowEvent::HoveredFile(path_buf) => { + log_once::debug_once!("Hovering file: {}", path_buf.display()); + }, + WindowEvent::HoveredFileCancelled => { + log_once::debug_once!("Hover cancelled"); + } + _ => {} + } + } + fn run_command(&mut self) -> SceneCommand { std::mem::replace(&mut self.scene_command, SceneCommand::None) } } impl Editor { + fn handle_file_drop(&mut self, path: &PathBuf) { + let project_root = { PROJECT.read().project_path.clone() }; + if project_root.as_os_str().is_empty() { + log::warn!("Drop ignored: no project loaded"); + return; + } + + if !path.exists() { + log::warn!("Drop ignored: '{}' does not exist", path.display()); + return; + } + + if path.is_dir() { + log::warn!("Drop ignored: '{}' is a directory", path.display()); + return; + } + + let extension = path + .extension() + .and_then(|ext| ext.to_str()) + .map(|ext| ext.to_ascii_lowercase()); + + let target_dir = match extension.as_deref() { + Some("kt") => Self::script_drop_dir(&project_root), + Some("eucp") | Some("eucs") => project_root.join("scenes"), + _ => project_root.join("resources"), + }; + + if let Err(err) = fs::create_dir_all(&target_dir) { + log::warn!( + "Drop failed: unable to create '{}' ({})", + target_dir.display(), + err + ); + return; + } + + let Some(file_name) = path.file_name() else { + log::warn!("Drop ignored: invalid file name for '{}'", path.display()); + return; + }; + + let target_path = Self::unique_drop_target(&target_dir, file_name); + if let Err(err) = fs::copy(path, &target_path) { + log::warn!( + "Drop failed: unable to copy '{}' to '{}' ({})", + path.display(), + target_path.display(), + err + ); + } else { + log::info!("Dropped asset copied to '{}'", target_path.display()); + } + } + + fn script_drop_dir(project_root: &Path) -> PathBuf { + let src_root = project_root.join("src"); + let main_kotlin = src_root.join("main").join("kotlin"); + if main_kotlin.exists() { + return main_kotlin; + } + + if src_root.exists() { + return main_kotlin; + } + + main_kotlin + } + + fn unique_drop_target(base_dir: &Path, file_name: &std::ffi::OsStr) -> PathBuf { + let mut candidate = base_dir.join(file_name); + if !candidate.exists() { + return candidate; + } + + let stem = Path::new(file_name) + .file_stem() + .and_then(|s| s.to_str()) + .unwrap_or("file"); + let ext = Path::new(file_name).extension().and_then(|e| e.to_str()); + + let mut index = 1; + loop { + let file_name = match ext { + Some(ext) => format!("{}-{}.{}", stem, index, ext), + None => format!("{}-{}", stem, index), + }; + candidate = base_dir.join(file_name); + if !candidate.exists() { + return candidate; + } + index += 1; + } + } + fn editor_specific_render(&mut self, graphics: &Arc<SharedGraphicsContext>) { self.size = graphics.viewport_texture.size; self.texture_id = Some(*graphics.texture_id.clone()); @@ -806,17 +806,13 @@ impl Scene for PlayMode { } render_pass.set_bind_group(3, globals_bind_group, &[]); - for mesh in &model.meshes { - let material = &model.materials[mesh.material]; - render_pass.draw_mesh_instanced( - mesh, - material, - 0..instance_count, - &camera.bind_group, - lcp.bind_group(), - Some(default_skinning_bind_group), - ); - } + render_pass.draw_model_instanced( + model, + 0..instance_count, + &camera.bind_group, + lcp.bind_group(), + Some(default_skinning_bind_group), + ); } } @@ -873,17 +869,13 @@ impl Scene for PlayMode { render_pass.set_vertex_buffer(1, instance_buffer.slice(1)); render_pass.set_bind_group(3, globals_bind_group, &[]); - for mesh in &model.meshes { - let material = &model.materials[mesh.material]; - render_pass.draw_mesh_instanced( - mesh, - material, - 0..1, - &camera.bind_group, - lcp.bind_group(), - Some(&skin_bind_group), - ); - } + render_pass.draw_model_instanced( + model, + 0..1, + &camera.bind_group, + lcp.bind_group(), + Some(&skin_bind_group), + ); } }