tirbofish/dropbear · commit
b5ee86a97cf8e606d83d2b98c965ee0cc4319ad2
refactor: eucalyptus-editor is finally compilable.
Signature present but could not be verified.
Unverified
@@ -1,22 +1,18 @@ use glam::{DMat4, DQuat, DVec3, Mat4, Quat, Vec3}; -use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use std::{ - collections::{HashMap, hash_map::Entry}, + collections::{HashMap}, path::Path, - sync::{Arc, LazyLock}, + sync::{Arc}, }; use crate::{ - asset::{ASSET_REGISTRY, AssetRegistry}, + asset::{ASSET_REGISTRY}, graphics::{Instance, SharedGraphicsContext}, model::Model, texture::Texture, - utils::{ResourceReference, ResourceReferenceType, EUCA_SCHEME}, }; -use anyhow::anyhow; -use egui::{CollapsingHeader, Ui}; -use std::any::Any; +use egui::{Ui}; use crate::asset::Handle; use crate::model::Material; @@ -190,6 +190,7 @@ macro_rules! features { }) => { #[allow(non_upper_case_globals)] pub mod $mod_name { + #[allow(unused_imports)] use $crate::features; features! { @_impl mod $mod_name { @@ -487,6 +487,36 @@ Hardware: ); } + /// Resizes the offscreen viewport texture without touching the window surface. + pub fn resize_viewport_texture(&mut self, width: u32, height: u32) { + if width == 0 || height == 0 { + return; + } + + if self.viewport_texture.size.width == width && self.viewport_texture.size.height == height { + return; + } + + let mut config = self.config.read().clone(); + config.width = width; + config.height = height; + + self.depth_texture = + Texture::depth_texture(&config, &self.device, Some("depth texture")); + self.viewport_texture = + Texture::viewport(&config, &self.device, Some("viewport texture")); + self.hdr.write().resize(&self.device, width, height); + self.egui_renderer + .lock() + .renderer() + .update_egui_texture_from_wgpu_texture( + &self.device, + &self.viewport_texture.view, + wgpu::FilterMode::Linear, + *self.texture_id, + ); + } + /// Renders the scene and the egui renderer. I don't know what else to say. /// Returns any window-level commands that need to be handled by the App. fn render( @@ -943,21 +973,32 @@ pub struct App { windows: HashMap<WindowId, (State, Arc<SharedGraphicsContext>)>, root_window_id: Option<WindowId>, windows_to_create: Vec<WindowData>, + + + #[allow(dead_code)] + puffin_server: Option<Arc<puffin_http::Server>>, } impl App { /// Creates a new instance of the application. It only sets the default for the struct + the /// window config. fn new(app_data: AppInfo, future_queue: Option<Arc<FutureQueue>>) -> Self { + let mut puffin_server: Option<Arc<puffin_http::Server>> = None; + if feature_list::is_enabled(feature_list::EnablePuffinTracer) { log::info!("Enabling puffin profiler"); puffin::set_scopes_on(true); - if let Err(e) = puffin_http::Server::new("127.0.0.1:8585") { - log::error!("Unable to start puffin http server: {}", e); - } else { - log::info!("Started puffin http server at \"127.0.0.1:8585\""); - }; + match puffin_http::Server::new("127.0.0.1:8585") { + Ok(v) => { + log::info!("Started puffin http server at \"127.0.0.1:8585\""); + + puffin_server = Some(Arc::new(v)); // need to keep as Arc to keep it alive + }, + Err(e) => { + log::error!("Unable to start puffin http server: {}", e); + }, + } } let instance = Arc::new(Instance::new(&wgpu::InstanceDescriptor { @@ -979,6 +1020,7 @@ impl App { root_window_id: None, windows_to_create: Vec::new(), app_data, + puffin_server, }; log::debug!("Created new instance of app"); result @@ -1112,58 +1154,67 @@ impl ApplicationHandler for App { return; } - let (state, graphics) = match self.windows.get_mut(&window_id) { - Some(canvas) => canvas, - None => return, - }; + let request_all_redraws = matches!(&event, WindowEvent::RedrawRequested); + let mut window_commands = Vec::new(); - state - .egui_renderer - .lock() - .handle_input(&state.window, &event); + { + let (state, graphics) = match self.windows.get_mut(&window_id) { + Some(canvas) => canvas, + None => return, + }; - state.scene_manager.handle_event(&event); + state + .egui_renderer + .lock() + .handle_input(&state.window, &event); - match event { - WindowEvent::Resized(size) => { - state.resize(size.width, size.height); + state.scene_manager.handle_event(&event); - *graphics = Arc::new(graphics::SharedGraphicsContext::from_state(state)); - } - WindowEvent::RedrawRequested => { - self.future_queue.poll(); + match event { + WindowEvent::Resized(size) => { + state.resize(size.width, size.height); - puffin::GlobalProfiler::lock().new_frame(); + *graphics = Arc::new(graphics::SharedGraphicsContext::from_state(state)); + } + WindowEvent::RedrawRequested => { + self.future_queue.poll(); - let frame_start = Instant::now(); - - let active_handlers = state.scene_manager.get_active_input_handlers(); - self.input_manager.set_active_handlers(active_handlers); + puffin::GlobalProfiler::lock().new_frame(); - self.input_manager.update(&mut self.gilrs); + let frame_start = Instant::now(); - let render_result = state.render(self.delta_time, event_loop, graphics.clone()); + let active_handlers = state.scene_manager.get_active_input_handlers(); + self.input_manager.set_active_handlers(active_handlers); - let window_commands = render_result.unwrap_or_else(|e| { - log::error!("Render failed: {:?}", e); - Vec::new() - }); + self.input_manager.update(&mut self.gilrs); - let frame_elapsed = frame_start.elapsed(); - let target_frame_time = Duration::from_secs_f32(1.0 / self.target_fps as f32); + let render_result = + state.render(self.delta_time, event_loop, graphics.clone()); - if frame_elapsed < target_frame_time { - SpinSleeper::default().sleep(target_frame_time - frame_elapsed); - } + window_commands = render_result.unwrap_or_else(|e| { + log::error!("Render failed: {:?}", e); + Vec::new() + }); - let total_frame_time = frame_start.elapsed(); - self.delta_time = total_frame_time.as_secs_f32(); + let frame_elapsed = frame_start.elapsed(); + let target_frame_time = Duration::from_secs_f32(1.0 / self.target_fps as f32); - state.window.request_redraw(); - self.future_queue.cleanup(); + if frame_elapsed < target_frame_time { + SpinSleeper::default().sleep(target_frame_time - frame_elapsed); + } - for command in window_commands { - match command { + let total_frame_time = frame_start.elapsed(); + self.delta_time = total_frame_time.as_secs_f32(); + + state.window.request_redraw(); + self.future_queue.cleanup(); + } + _ => {} + } + } + + for command in window_commands { + match command { scene::SceneCommand::RequestWindow(window_data) => { log::info!("Scene requested new window creation"); match self.create_window(event_loop, window_data.attributes) { @@ -1214,16 +1265,24 @@ impl ApplicationHandler for App { scene::SceneCommand::SetFPS(new_fps) => { self.set_target_fps(new_fps); } + scene::SceneCommand::ResizeViewport((width, height)) => { + if let Some((state, graphics)) = self.windows.get_mut(&window_id) { + state.resize_viewport_texture(width, height); + *graphics = + Arc::new(graphics::SharedGraphicsContext::from_state(state)); + } + } _ => {} - } - } - - for (state, _) in self.windows.values() { - state.window.request_redraw(); - } + } + } - return; + if request_all_redraws { + for (state, _) in self.windows.values() { + state.window.request_redraw(); } + } + + match event { WindowEvent::KeyboardInput { event: KeyEvent { @@ -6,15 +6,12 @@ use crate::{ texture::{Texture, TextureWrapMode} }; // use image::GenericImageView; -use parking_lot::{Mutex, RwLock}; +use parking_lot::{RwLock}; use rayon::prelude::*; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; use std::hash::{DefaultHasher, Hash, Hasher}; -use std::ops::Deref; -use std::sync::{Arc, LazyLock}; -use std::time::Instant; -use std::{mem, ops::Range, path::PathBuf}; +use std::sync::{Arc}; +use std::{mem, ops::Range}; use gltf::image::Format; use gltf::texture::MinFilter; use puffin::profile_scope; @@ -151,7 +151,7 @@ impl LightCubePipeline { } else if let Some(transform) = s_trans { transform.matrix().into() } else { - panic!("Unable to locate either a \"Transform\" or an \"EntityTransform\" component for the light {}", light.label); + light_component.to_transform().matrix().into() }; light.instance_buffer.write(&graphics.device, &graphics.queue, &[instance]); @@ -35,6 +35,7 @@ pub enum SceneCommand { RequestWindow(WindowData), CloseWindow(WindowId), SetFPS(u32), + ResizeViewport((u32, u32)), } impl Default for SceneCommand { @@ -146,7 +147,7 @@ impl Manager { } SceneCommand::None => {} SceneCommand::DebugMessage(msg) => log::debug!("{}", msg), - SceneCommand::RequestWindow(_) | SceneCommand::CloseWindow(_) | SceneCommand::SetFPS(_) => { + SceneCommand::RequestWindow(_) | SceneCommand::CloseWindow(_) | SceneCommand::SetFPS(_) | SceneCommand::ResizeViewport(_) => { return vec![command]; } } @@ -33,7 +33,7 @@ impl Component for AnimationComponent { fn init<'a>( ser: &'a Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, - ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> { + ) -> crate::component::ComponentInitFuture<'a, Self> { Box::pin(async move { Ok((ser.clone(), )) }) } @@ -8,7 +8,7 @@ use std::sync::Arc; use egui::{CollapsingHeader, Ui}; use hecs::{Entity, World}; use dropbear_engine::graphics::SharedGraphicsContext; -use crate::component::{Component, ComponentDescriptor, SerializedComponent}; +use crate::component::{Component, ComponentDescriptor, ComponentInitFuture, SerializedComponent}; use crate::ptr::WorldPtr; use crate::scripting::result::DropbearNativeResult; use crate::types::NVector3; @@ -48,7 +48,7 @@ impl Component for Camera { fn init<'a>( ser: &'a Self::SerializedForm, graphics: Arc<SharedGraphicsContext>, - ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> { + ) -> ComponentInitFuture<'a, Self> { Box::pin(async move { let label = ser.label.clone(); let builder = CameraBuilder::from(ser.clone()); @@ -27,16 +27,29 @@ pub struct ComponentRegistry { fqtn_to_type: HashMap<String, TypeId>, /// Maps category name to list of TypeIds in that category categories: HashMap<String, Vec<TypeId>>, + /// Maps serialized TypeId to component TypeId + serialized_to_component: HashMap<TypeId, TypeId>, /// Functions that extract and serialize components from entities extractors: HashMap<TypeId, ExtractorFn>, /// Functions that allow for the entity to load. loaders: HashMap<TypeId, LoaderFn>, /// Functions that update the contents of the component. updaters: HashMap<TypeId, UpdateFn>, + /// Functions that create default serialized components. + defaults: HashMap<TypeId, DefaultFn>, + /// Functions that remove components by type. + removers: HashMap<TypeId, RemoveFn>, + /// Functions that find entities with a component. + finders: HashMap<TypeId, FindFn>, } +/// Describes a handy little future for [`Component::init`], which deals with initialising a component from its serialized form. +/// +/// Typically thrown in as a return parameter as `-> ComponentInitFuture<'a, Self>` +pub type ComponentInitFuture<'a, T: Component> = std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<T::RequiredComponentTypes>> + Send + Sync + 'a>>; + type LoaderFuture<'a> = Pin<Box< - dyn Future<Output = anyhow::Result<Box<dyn for<'b> FnOnce(&'b mut hecs::EntityBuilder)>>> + Send + 'a + dyn Future<Output = anyhow::Result<Box<dyn for<'b> FnOnce(&'b mut hecs::EntityBuilder) + Send + Sync>>> + Send + Sync + 'a >>; type LoaderFn = Box< dyn for<'a> Fn(&'a dyn SerializedComponent, Arc<SharedGraphicsContext>) -> LoaderFuture<'a> @@ -45,6 +58,9 @@ type LoaderFn = Box< >; type ExtractorFn = Box<dyn Fn(&hecs::World, hecs::Entity) -> Option<Box<dyn SerializedComponent>> + Send + Sync>; type UpdateFn = Box<dyn Fn(&mut hecs::World, f32, Arc<SharedGraphicsContext>) + Send + Sync>; +type DefaultFn = Box<dyn Fn() -> Box<dyn SerializedComponent> + Send + Sync>; +type RemoveFn = Box<dyn Fn(&mut hecs::World, hecs::Entity) + Send + Sync>; +type FindFn = Box<dyn Fn(&hecs::World) -> Vec<hecs::Entity> + Send + Sync>; impl ComponentRegistry { pub fn new() -> Self { @@ -52,9 +68,13 @@ impl ComponentRegistry { descriptors: HashMap::new(), fqtn_to_type: HashMap::new(), categories: HashMap::new(), + serialized_to_component: HashMap::new(), extractors: HashMap::new(), loaders: HashMap::new(), updaters: HashMap::new(), + defaults: HashMap::new(), + removers: HashMap::new(), + finders: HashMap::new(), } } @@ -62,9 +82,11 @@ impl ComponentRegistry { pub fn register<T>(&mut self) where T: Component + Send + Sync + 'static, - T::SerializedForm: 'static, + T::SerializedForm: 'static + Default, + T::RequiredComponentTypes: Send + Sync, { let type_id = TypeId::of::<T>(); + let serialized_type_id = TypeId::of::<T::SerializedForm>(); let desc = T::descriptor(); self.fqtn_to_type.insert(desc.fqtn.clone(), type_id); @@ -72,13 +94,15 @@ impl ComponentRegistry { self.categories.entry(cat.clone()).or_default().push(type_id); } self.descriptors.insert(type_id, desc); + self.serialized_to_component + .insert(serialized_type_id, type_id); self.extractors.insert(type_id, Box::new(|world, entity| { let Ok(c) = world.get::<&T>(entity) else { return None }; Some(c.save(world, entity)) })); - self.loaders.insert(type_id, Box::new(|serialized, graphics| { + self.loaders.insert(serialized_type_id, Box::new(|serialized, graphics| { let serialized = serialized .as_any() .downcast_ref::<T::SerializedForm>() @@ -86,7 +110,7 @@ impl ComponentRegistry { Box::pin(async move { let bundle = T::init(serialized, graphics).await?; - let applier: Box<dyn FnOnce(&mut hecs::EntityBuilder)> = + let applier: Box<dyn FnOnce(&mut hecs::EntityBuilder) + Send + Sync> = Box::new(move |builder: &mut hecs::EntityBuilder| { builder.add_bundle(bundle); }); @@ -94,6 +118,22 @@ impl ComponentRegistry { }) })); + self.defaults.insert(type_id, Box::new(|| { + Box::new(T::SerializedForm::default()) + })); + + self.removers.insert(type_id, Box::new(|world, entity| { + let _ = world.remove_one::<T>(entity); + })); + + self.finders.insert(type_id, Box::new(|world| { + world + .query::<(hecs::Entity, &T)>() + .iter() + .map(|(entity, _)| entity) + .collect() + })); + self.updaters.insert(type_id, Box::new(|world, dt, graphics| { let world_ptr = world as *mut hecs::World; // safe assuming world is kept at the DropbearAppBuilder application level (lifetime) let mut query = world.query::<(hecs::Entity, &mut T)>(); @@ -154,6 +194,13 @@ impl ComponentRegistry { self.descriptors.len() } + /// Iterates available component descriptors alongside their numeric ids. + pub fn iter_available_components(&self) -> impl Iterator<Item = (u64, &ComponentDescriptor)> { + self.descriptors + .iter() + .map(|(type_id, desc)| (Self::numeric_id(*type_id), desc)) + } + /// Extract all registered components from an entity pub fn extract_all_components( &self, @@ -200,15 +247,63 @@ impl ComponentRegistry { .unwrap_or_default() } + /// Gets the numeric id for a serialized component instance. + pub fn id_for_component(&self, component: &dyn SerializedComponent) -> Option<u64> { + let serialized_type_id = component.as_any().type_id(); + self.serialized_to_component + .get(&serialized_type_id) + .copied() + .map(Self::numeric_id) + } + + /// Creates a default serialized component by numeric id. + pub fn create_default_component(&self, id: u64) -> Option<Box<dyn SerializedComponent>> { + self.type_id_from_numeric_id(id) + .and_then(|type_id| self.defaults.get(&type_id)) + .map(|create| create()) + } + + /// Removes a component by numeric id from an entity. + pub fn remove_component_by_id( + &self, + world: &mut hecs::World, + entity: hecs::Entity, + id: u64, + ) { + if let Some(type_id) = self.type_id_from_numeric_id(id) { + if let Some(remover) = self.removers.get(&type_id) { + remover(world, entity); + } + } + } + + /// Finds entities with the component matching a numeric id. + pub fn find_entities_by_numeric_id( + &self, + world: &hecs::World, + id: u64, + ) -> Vec<hecs::Entity> { + self.type_id_from_numeric_id(id) + .and_then(|type_id| self.finders.get(&type_id)) + .map(|finder| finder(world)) + .unwrap_or_default() + } + + /// Gets a component descriptor by numeric id. + pub fn get_descriptor_by_numeric_id(&self, id: u64) -> Option<&ComponentDescriptor> { + self.type_id_from_numeric_id(id) + .and_then(|type_id| self.descriptors.get(&type_id)) + } + /// Create a component applier from a serialized component using the registry loader. pub fn load_component<'a>( &'a self, serialized: &'a dyn SerializedComponent, graphics: Arc<SharedGraphicsContext>, ) -> Option<LoaderFuture<'a>> { - let type_id = serialized.as_any().type_id(); + let serialized_type_id = serialized.as_any().type_id(); self.loaders - .get(&type_id) + .get(&serialized_type_id) .map(|loader| loader(serialized, graphics)) } @@ -223,6 +318,107 @@ impl ComponentRegistry { updater(world, dt, graphics.clone()); } } + + /// Inspects all registered components attached to an entity. + pub fn inspect_components( + &self, + world: &mut hecs::World, + entity: hecs::Entity, + ui: &mut egui::Ui, + ) { + if let Ok(mut label) = world.get::<&mut crate::states::Label>(entity) { + ui.horizontal(|ui| { + ui.label("Label"); + ui.add(egui::TextEdit::singleline(label.as_mut_string())); + }); + ui.separator(); + } + + let type_ids = world + .entity(entity) + .map(|e| e.component_types().collect::<Vec<_>>()) + .unwrap_or_default(); + + for type_id in type_ids { + let Some(desc) = self.descriptors.get(&type_id) else { + continue; + }; + + match desc.fqtn.as_str() { + "dropbear_engine::entity::EntityTransform" => { + if let Ok(mut comp) = world.get::<&mut EntityTransform>(entity) { + comp.inspect(ui); + } + } + "eucalyptus_core::properties::CustomProperties" => { + if let Ok(mut comp) = world.get::<&mut crate::properties::CustomProperties>(entity) { + comp.inspect(ui); + } + } + "dropbear_engine::lighting::Light" => { + if let Ok(mut comp) = world.get::<&mut dropbear_engine::lighting::Light>(entity) { + comp.inspect(ui); + } + } + "eucalyptus_core::states::Script" => { + if let Ok(mut comp) = world.get::<&mut crate::states::Script>(entity) { + comp.inspect(ui); + } + } + "dropbear_engine::entity::MeshRenderer" => { + if let Ok(mut comp) = world.get::<&mut MeshRenderer>(entity) { + comp.inspect(ui); + } + } + "dropbear_engine::camera::Camera" => { + if let Ok(mut comp) = world.get::<&mut dropbear_engine::camera::Camera>(entity) { + comp.inspect(ui); + } + } + "eucalyptus_core::physics::rigidbody::RigidBody" => { + if let Ok(mut comp) = world.get::<&mut crate::physics::rigidbody::RigidBody>(entity) { + comp.inspect(ui); + } + } + "eucalyptus_core::physics::collider::ColliderGroup" => { + if let Ok(mut comp) = world.get::<&mut crate::physics::collider::ColliderGroup>(entity) { + comp.inspect(ui); + } + } + "eucalyptus_core::physics::kcc::KCC" => { + if let Ok(mut comp) = world.get::<&mut crate::physics::kcc::KCC>(entity) { + comp.inspect(ui); + } + } + "dropbear_engine::animation::AnimationComponent" => { + if let Ok(mut comp) = world.get::<&mut dropbear_engine::animation::AnimationComponent>(entity) { + comp.inspect(ui); + } + } + _ => { + ui.label(format!("{} (no inspector)", desc.type_name)); + } + } + } + } + + fn numeric_id(type_id: TypeId) -> u64 { + use std::hash::{Hash, Hasher}; + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + type_id.hash(&mut hasher); + let mut id = hasher.finish(); + if id == 0 { + id = 1; + } + id + } + + fn type_id_from_numeric_id(&self, id: u64) -> Option<TypeId> { + self.descriptors + .keys() + .copied() + .find(|type_id| Self::numeric_id(*type_id) == id) + } } impl Default for ComponentRegistry { @@ -277,7 +473,7 @@ pub trait Component: Sized + Sync + Send { fn init<'a>( ser: &'a Self::SerializedForm, graphics: Arc<SharedGraphicsContext>, - ) -> Pin<Box<dyn Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>>; + ) -> ComponentInitFuture<'a, Self>; /// Called every frame to update the component's state. fn update_component(&mut self, world: &hecs::World, entity: hecs::Entity, dt: f32, graphics: Arc<SharedGraphicsContext>); @@ -317,7 +513,7 @@ impl Component for MeshRenderer { fn init<'a>( ser: &'a Self::SerializedForm, graphics: Arc<SharedGraphicsContext>, - ) -> Pin<Box<dyn Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> { + ) -> ComponentInitFuture<'a, Self> { Box::pin(async move { let import_scale = ser.import_scale.unwrap_or(1.0); @@ -12,7 +12,7 @@ use hecs::{Entity, World}; use jni::objects::{JObject, JValue}; use jni::JNIEnv; use dropbear_engine::graphics::SharedGraphicsContext; -use crate::component::{Component, ComponentDescriptor, SerializedComponent}; +use crate::component::{Component, ComponentDescriptor, ComponentInitFuture, SerializedComponent}; use crate::states::SerializedLight; #[typetag::serde] @@ -43,7 +43,7 @@ impl Component for Light { fn init<'a>( ser: &'a Self::SerializedForm, graphics: Arc<SharedGraphicsContext>, - ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> { + ) -> ComponentInitFuture<'a, Self> { Box::pin(async move { let light = Light::new( graphics.clone(), @@ -56,13 +56,13 @@ impl Component for Light { } fn update_component(&mut self, world: &World, entity: Entity, _dt: f32, graphics: Arc<SharedGraphicsContext>) { - if let Ok((light, comp)) = world.query_one::<(&mut Light, &mut LightComponent)>(entity).get() { - light.update(&graphics, comp); + if let Ok(comp) = world.query_one::<&LightComponent>(entity).get() { + self.update(&graphics, comp); } } fn save(&self, world: &World, entity: Entity) -> Box<dyn SerializedComponent> { - if let Ok((_, comp)) = world.query_one::<(&Light, &LightComponent)>(entity).get() { + if let Ok(comp) = world.query_one::<&LightComponent>(entity).get() { Box::new(SerializedLight { label: self.label.clone(), light_component: comp.clone(), @@ -82,7 +82,7 @@ impl Component for ColliderGroup { fn init<'a>( ser: &'a Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, - ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> { + ) -> crate::component::ComponentInitFuture<'a, Self> { Box::pin(async move { Ok((ser.clone(), )) }) } @@ -60,7 +60,7 @@ impl Component for KCC { fn init<'a>( ser: &'a Self::SerializedForm, _: Arc<SharedGraphicsContext>, - ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> { + ) -> crate::component::ComponentInitFuture<'a, Self> { Box::pin(async move { Ok((ser.clone(), )) }) } @@ -212,7 +212,7 @@ impl Component for RigidBody { fn init<'a>( ser: &'a Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, - ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> { + ) -> crate::component::ComponentInitFuture<'a, Self> { Box::pin(async move { Ok((ser.clone(), )) }) } @@ -6,7 +6,7 @@ use std::sync::Arc; use egui::{CollapsingHeader, ComboBox, DragValue, Grid, RichText, TextEdit, Ui}; use hecs::{Entity, World}; use dropbear_engine::graphics::SharedGraphicsContext; -use crate::component::{Component, ComponentDescriptor, SerializedComponent}; +use crate::component::{Component, ComponentDescriptor, ComponentInitFuture, SerializedComponent}; use crate::ptr::WorldPtr; use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; @@ -47,7 +47,7 @@ impl Component for CustomProperties { fn init<'a>( ser: &'a Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, - ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> { + ) -> ComponentInitFuture<'a, Self> { Box::pin(async move { Ok((ser.clone(), )) }) } @@ -27,7 +27,7 @@ use egui::{CollapsingHeader, TextEdit, Ui}; use hecs::{Entity, World}; use dropbear_engine::model::AlphaMode; use dropbear_engine::texture::TextureWrapMode; -use crate::component::{Component, ComponentDescriptor, SerializedComponent}; +use crate::component::{Component, ComponentDescriptor, ComponentInitFuture, SerializedComponent}; use crate::properties::Value; /// A global "singleton" that contains the configuration of a project. @@ -184,7 +184,7 @@ impl Component for Script { fn init<'a>( ser: &'a Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, - ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> { + ) -> ComponentInitFuture<'a, Self> { Box::pin(async move { Ok((ser.clone(), )) }) } @@ -37,7 +37,7 @@ impl Component for EntityTransform { fn init<'a>( ser: &'a Self::SerializedForm, _: Arc<SharedGraphicsContext>, - ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> { + ) -> crate::component::ComponentInitFuture<'a, Self> { Box::pin(async move { Ok((ser.clone(), )) }) } @@ -17,11 +17,6 @@ pub(crate) fn show_menu_bar(ui: &mut Ui, signal: &mut Signal) { panic!("Testing out panicking with new panic module, this is a test") } - if ui_debug.button("Show Entities Loaded").clicked() { - log::debug!("Show Entities Loaded under Debug Menu is clicked"); - *signal = Signal::LogEntities; - } - if ui_debug.button("size_of::<Editor>()").clicked() { log::debug!("size_of::<Editor>() is clicked"); let size = size_of::<crate::editor::Editor>(); @@ -563,17 +563,23 @@ impl<'a> EditorTabViewer<'a> { let component_id = selection.component_type_id; let matches = self .component_registry - .find_components_by_numeric_id(component_id); + .find_entities_by_numeric_id(self.world, component_id); + let descriptor = self + .component_registry + .get_descriptor_by_numeric_id(component_id); if matches.is_empty() { log::warn!("Component id #{} not found in world", component_id); return; } - for (entity, component) in matches { + let name = descriptor + .map(|desc| desc.fqtn.as_str()) + .unwrap_or("<unknown>"); + for entity in matches { log::debug!( "Serializable component '{}' (id #{}) attached to entity {:?}", - component, + name, component_id, entity ); @@ -15,7 +15,6 @@ use dropbear_engine::utils::ResourceReference; use dropbear_engine::entity::{EntityTransform, Transform}; use egui::{self}; use egui_dock::TabViewer; -use eucalyptus_core::traits::registry::ComponentRegistry; use hecs::{Entity, World}; use parking_lot::Mutex; use transform_gizmo_egui::{EnumSet, Gizmo, GizmoMode, GizmoOrientation}; @@ -1,5 +1,5 @@ use egui_ltreeview::{NodeBuilder, TreeViewBuilder}; -use eucalyptus_core::{hierarchy::{Children, Hierarchy, Parent}, physics::{collider::ColliderGroup, rigidbody::RigidBody}, states::{Label, PROJECT}, traits::registry::ComponentRegistry}; +use eucalyptus_core::{component::ComponentRegistry, hierarchy::{Children, Hierarchy, Parent}, physics::{collider::ColliderGroup, rigidbody::RigidBody}, states::{Label, PROJECT}}; use hecs::{Entity, World}; use crate::editor::{EditorTabViewer, Signal, StaticallyKept, TABS_GLOBAL}; @@ -63,10 +63,10 @@ impl<'a> EditorTabViewer<'a> { }); ui.menu_button("Add", |ui| { log_once::debug_once!("Available components: "); - for (id, fqtn) in registry.iter_available_components() { - log_once::debug_once!("id: {}, name: {}", id, fqtn); + for (id, desc) in registry.iter_available_components() { + log_once::debug_once!("id: {}, name: {}", id, desc.fqtn); - if ui.button(fqtn).clicked() { + if ui.button(desc.type_name.as_str()).clicked() { if let Some(component) = registry.create_default_component(id) { *signal = Signal::AddComponent(entity, component); } @@ -80,22 +80,20 @@ impl<'a> EditorTabViewer<'a> { let components = registry.extract_all_components(world, entity); for component in components.iter() { - // if component.type_name().contains("EntityTransform") { - // continue; - // } let Some(component_type_id) = registry.id_for_component(component.as_ref()) else { log_once::warn_once!( - "Component '{}' missing registry id, skipping tree entry", - component.type_name() + "Component missing registry id, skipping tree entry" ); continue; }; let component_node_id = cfg.component_node_id(entity, component_type_id as u64); - let display = - format!("{} (id #{component_type_id})", component.display_name()); + let display = registry + .get_descriptor_by_numeric_id(component_type_id) + .map(|desc| format!("{} (id #{component_type_id})", desc.type_name)) + .unwrap_or_else(|| format!("Unknown (id #{component_type_id})")); let has_rigidbody = world.get::<&RigidBody>(entity).is_ok(); let has_collider = world.get::<&ColliderGroup>(entity).is_ok(); @@ -104,7 +102,7 @@ impl<'a> EditorTabViewer<'a> { .label_ui(|ui| { ui.label(display.clone()); - if has_rigidbody && !has_collider && component.type_name().contains("RigidBody") { + if has_rigidbody && !has_collider && component.typetag_name().contains("RigidBody") { ui.add_space(4.0); ui.small_button("⚠") .on_hover_text("RigidBody has no colliders! Add the ColliderGroup component"); @@ -1,10 +1,6 @@ use std::process::{Command, Stdio}; -use std::thread; use super::*; -use dropbear_engine::{ - entity::MeshRenderer, - input::{Controller, Keyboard, Mouse}, -}; +use dropbear_engine::input::{Controller, Keyboard, Mouse}; use eucalyptus_core::states::Label; use eucalyptus_core::success_without_console; use gilrs::{Button, GamepadId}; @@ -13,7 +9,6 @@ use transform_gizmo_egui::{GizmoMode, GizmoOrientation}; use winit::{ dpi::PhysicalPosition, event::MouseButton, event_loop::ActiveEventLoop, keyboard::KeyCode, }; -use eucalyptus_core::properties::CustomProperties; impl Keyboard for Editor { fn key_down(&mut self, key: KeyCode, _event_loop: &ActiveEventLoop) { @@ -126,55 +121,35 @@ impl Keyboard for Editor { PROJECT.read().project_path.clone() }; - thread::spawn(|| { - #[cfg(unix)] - { - use daemonize::Daemonize; - - let daemonize = Daemonize::new() - .working_directory("/tmp") - .umask(0o027); - - match daemonize.start() { - Ok(_) => { - Command::new("gradlew") - .arg("--stop") - .current_dir(current_dir) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .ok(); - log::debug!("Stopping gradle threads"); - } - Err(_) => { - Command::new("gradlew") - .arg("--stop") - .current_dir(current_dir) - .status() - .ok(); - log::debug!("Stopping gradle threads"); - } - } - } + #[cfg(unix)] + { + Command::new("gradlew") + .arg("--stop") + .current_dir(current_dir) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .ok(); + log::debug!("Stopping gradle threads"); + } - #[cfg(windows)] - { - use std::os::windows::process::CommandExt; - const DETACHED_PROCESS: u32 = 0x00000008; + #[cfg(windows)] + { + use std::os::windows::process::CommandExt; + const DETACHED_PROCESS: u32 = 0x00000008; - Command::new("cmd") - .args(["/C", "gradlew", "--stop"]) - .current_dir(current_dir) - .creation_flags(DETACHED_PROCESS) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .spawn() - .ok(); - log::debug!("Stopping gradle threads"); - } - }); + Command::new("cmd") + .args(["/C", "gradlew", "--stop"]) + .current_dir(current_dir) + .creation_flags(DETACHED_PROCESS) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .ok(); + log::debug!("Stopping gradle threads"); + } }; self.scene_command = SceneCommand::Quit(Some(commands)); @@ -191,32 +166,24 @@ impl Keyboard for Editor { && matches!(tab, EditorTab::ModelEntityList) { if let Some(entity) = &self.selected_entity { - let mut query = self.world.query_one::<( - &Label, - &MeshRenderer, - &EntityTransform, - &CustomProperties, - )>(*entity); - if let Ok((label, renderer, t, props)) = query.get() { - let s_entity = SceneEntity { - label: label.clone(), - components: vec![ - Box::new(props.clone()), - Box::new(SerializedMeshRenderer::from_renderer( - &renderer, - )), - Box::new(t.clone()), - ], - entity_id: None, - }; - self.signal = Signal::Copy(s_entity); + let Ok(label) = self.world.get::<&Label>(*entity) else { + warn!("Unable to copy entity: Unable to obtain label"); + return; + }; - info!("Copied!"); + let components = self + .component_registry + .extract_all_components(self.world.as_ref(), *entity); + let s_entity = SceneEntity { + label: Label::new(label.as_str()), + components, + entity_id: None, + }; + self.signal = Signal::Copy(s_entity); - log::debug!("Copied selected entity"); - } else { - warn!("Unable to copy entity: Unable to obtain lock"); - } + info!("Copied!"); + + log::debug!("Copied selected entity"); } else { warn!("Unable to copy entity: None selected"); } @@ -22,16 +22,14 @@ use dropbear_engine::buffer::ResizableBuffer; use dropbear_engine::entity::EntityTransform; use dropbear_engine::graphics::InstanceRaw; use dropbear_engine::pipelines::light_cube::LightCubePipeline; -use dropbear_engine::texture::{Texture, TextureWrapMode}; -use dropbear_engine::{camera::Camera, entity::{MeshRenderer, Transform}, future::FutureHandle, graphics::{SharedGraphicsContext}, scene::SceneCommand, DropbearWindowBuilder, WindowData}; +use dropbear_engine::{camera::Camera, entity::{Transform}, future::FutureHandle, graphics::{SharedGraphicsContext}, scene::SceneCommand, DropbearWindowBuilder, WindowData}; use egui::{self, Context}; use egui_dock::{DockArea, DockState, NodeIndex, Style}; +use eucalyptus_core::component::{ComponentRegistry, SerializedComponent}; use eucalyptus_core::{register_components, APP_INFO}; use eucalyptus_core::hierarchy::{Children, SceneHierarchy}; use eucalyptus_core::scene::{SceneConfig, SceneEntity}; -use eucalyptus_core::states::{Label, SerializedMeshRenderer}; -use eucalyptus_core::traits::SerializableComponent; -use eucalyptus_core::traits::registry::ComponentRegistry; +use eucalyptus_core::states::Label; use eucalyptus_core::{ camera::{CameraComponent, CameraType, DebugCamera}, fatal, info, @@ -39,7 +37,7 @@ use eucalyptus_core::{ scripting::BuildStatus, states, states::{ - EditorTab, Script, WorldLoadingStatus, PROJECT, SCENES, + EditorTab, WorldLoadingStatus, PROJECT, SCENES, }, success, utils::ViewportMode, @@ -64,14 +62,13 @@ use winit::window::{CursorGrabMode, WindowAttributes}; use winit::{keyboard::KeyCode, window::Window}; use winit::dpi::PhysicalSize; use dropbear_engine::mipmap::MipMapper; -use dropbear_engine::pipelines::{create_render_pipeline, DropbearShaderPipeline}; +use dropbear_engine::pipelines::{DropbearShaderPipeline}; use dropbear_engine::pipelines::shader::MainRenderPipeline; use dropbear_engine::pipelines::GlobalsUniform; use dropbear_engine::sky::{HdrLoader, SkyPipeline, DEFAULT_SKY_TEXTURE}; use eucalyptus_core::physics::collider::{ColliderShapeKey, WireframeGeometry}; use eucalyptus_core::physics::collider::shader::ColliderInstanceRaw; use eucalyptus_core::physics::collider::shader::ColliderWireframePipeline; -use eucalyptus_core::properties::CustomProperties; use crate::about::AboutWindow; use crate::editor::console::EucalyptusConsole; use crate::editor::settings::editor::{EditorSettingsWindow, EDITOR_SETTINGS}; @@ -171,7 +168,7 @@ pub struct Editor { nerd_stats: Rc<RwLock<NerdStats>>, // component registry - component_registry: Arc<ComponentRegistry>, + pub component_registry: Arc<ComponentRegistry>, // play mode process tracking pub(crate) play_mode_process: Option<std::process::Child>, @@ -203,15 +200,16 @@ impl Editor { for plugin in plugin_registry.plugins.values_mut() { let plugin_id = plugin.id().to_string(); - let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - plugin.register_component(&mut component_registry); - })); - - if result.is_ok() { - log::info!("Registered components for plugin '{plugin_id}'"); - } else { - warn!("Plugin '{plugin_id}' panicked during component registration"); - } + log::debug!("Located plugin: {}", plugin_id); + // let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + // plugin.register_component(&mut component_registry); + // })); + + // if result.is_ok() { + // log::info!("Registered components for plugin '{plugin_id}'"); + // } else { + // warn!("Plugin '{plugin_id}' panicked during component registration"); + // } } let component_registry = Arc::new(component_registry); @@ -541,7 +539,7 @@ impl Editor { .load_into_world( world, graphics, - Some(component_registry.as_ref()), + &component_registry.clone(), sender.clone(), false, ) @@ -688,7 +686,7 @@ impl Editor { .load_into_world( &mut temp_world, graphics_shared.clone(), - Some(component_registry_clone.as_ref()), + &component_registry_clone, Some(progress_sender.clone()), false, ) @@ -917,33 +915,22 @@ impl Editor { ui.menu_button("Edit", |ui| { if ui.button("Copy").clicked() { if let Some(entity) = &self.selected_entity { - let mut query = self.world.query_one::<( - &Label, - &MeshRenderer, - &EntityTransform, - &CustomProperties, - )>(*entity); - if let Ok((entity_label, renderer, transform, props)) = query.get() { - let mut components: Vec<Box<dyn SerializableComponent>> = Vec::new(); - - components.push(Box::new(*transform)); - - let serialized_renderer = SerializedMeshRenderer::from_renderer(renderer); - components.push(Box::new(serialized_renderer)); - - components.push(Box::new(props.clone())); - - let s_entity = SceneEntity { - label: entity_label.clone(), - components, - entity_id: None, - }; - self.signal = Signal::Copy(s_entity); - - info!("Copied selected entity!"); - } else { - warn!("Unable to copy entity: Unable to obtain lock"); - } + let Ok(label) = self.world.get::<&Label>(*entity) else { + warn!("Unable to copy entity: Unable to obtain label"); + return; + }; + + let components = self + .component_registry + .extract_all_components(self.world.as_ref(), *entity); + let s_entity = SceneEntity { + label: Label::new(label.as_str()), + components, + entity_id: None, + }; + self.signal = Signal::Copy(s_entity); + + info!("Copied selected entity!"); } else { warn!("Unable to copy entity: None selected"); } @@ -1419,10 +1406,10 @@ pub enum Signal { Undo, Play, StopPlaying, - LogEntities, /// Adds a new component instance using the async init pipeline. - AddComponent(hecs::Entity, Box<dyn SerializableComponent>), + AddComponent(hecs::Entity, Box<dyn SerializedComponent>), RequestNewWindow(WindowData), + UpdateViewportSize((f32, f32)), } #[derive(Debug)] @@ -16,11 +16,9 @@ use dropbear_engine::{ model::{DrawLight, DrawModel}, scene::{Scene, SceneCommand}, }; -use eucalyptus_core::hierarchy::EntityTransformExt; use eucalyptus_core::states::{Label, WorldLoadingStatus}; use log; use parking_lot::Mutex; -use wgpu::Color; use winit::{event_loop::ActiveEventLoop, keyboard::KeyCode}; use eucalyptus_core::physics::collider::ColliderGroup; use eucalyptus_core::physics::collider::ColliderShapeKey; @@ -409,27 +407,6 @@ impl Scene for Editor { prepared_models.push((model, instance_buffer, instances.len() as u32)); } - { - let mut query = self.world.query::<( - &mut LightComponent, - Option<&dropbear_engine::entity::Transform>, - Option<&dropbear_engine::entity::EntityTransform>, - &mut Light, - )>(); - - for (light_component, transform_opt, entity_transform_opt, light) in query.iter() { - let transform = if let Some(entity_transform) = entity_transform_opt { - entity_transform.sync() - } else if let Some(transform) = transform_opt { - *transform - } else { - continue; - }; - - light.update(graphics.as_ref(), light_component, &transform); - } - } - let registry = ASSET_REGISTRY.read(); { let mut render_pass = encoder @@ -3,7 +3,7 @@ use dropbear_engine::camera::Camera; use dropbear_engine::entity::{EntityTransform, Transform}; use eucalyptus_core::camera::CameraComponent; use eucalyptus_core::utils::ViewportMode; -use crate::editor::{EditorTabViewer, StaticallyKept, UndoableAction, TABS_GLOBAL}; +use crate::editor::{EditorTabViewer, Signal, TABS_GLOBAL, UndoableAction}; impl<'a> EditorTabViewer<'a> { pub(crate) fn viewport_tab(&mut self, ui: &mut egui::Ui) { @@ -14,6 +14,8 @@ impl<'a> EditorTabViewer<'a> { let available_rect = ui.available_rect_before_wrap(); let available_size = available_rect.size(); + *self.signal = Signal::UpdateViewportSize((available_size.x, available_size.y)); + let tex_aspect = self.tex_size.width as f32 / self.tex_size.height as f32; let available_aspect = available_size.x / available_size.y; @@ -3,22 +3,19 @@ use app_dirs2::AppDataType; use egui::Ui; use eucalyptus_core::APP_INFO; use eucalyptus_core::states::PluginInfo; -use eucalyptus_core::traits::registry::ComponentRegistry; use indexmap::IndexMap; use libloading as lib; use std::fs::ReadDir; use std::path::PathBuf; use std::time::Instant; +// this is for another time its just a blabber of code + pub trait EditorPlugin: Send + Sync { fn id(&self) -> &str; fn display_name(&self) -> &str; fn ui(&mut self, ui: &mut Ui, editor: &mut Editor); fn tab_title(&self) -> &str; - - /// Allows you to register the struct [Self] as a component to the component registry. - /// This will then be listed as an option for a potential component a user could add. - fn register_component(&mut self, registry: &mut ComponentRegistry); } pub type PluginConstructor = fn() -> Box<dyn EditorPlugin>; @@ -1,24 +1,14 @@ use crate::editor::{Editor, EditorState, Signal}; -use dropbear_engine::asset::ASSET_REGISTRY; -use dropbear_engine::camera::Camera; -use dropbear_engine::entity::{EntityTransform, MeshRenderer, Transform}; use dropbear_engine::graphics::SharedGraphicsContext; -use dropbear_engine::lighting::{Light as EngineLight, LightComponent}; -use dropbear_engine::model::{Material, Model}; -use dropbear_engine::procedural::{ProcedurallyGeneratedObject, ProcObj}; -use dropbear_engine::texture::{Texture, TextureWrapMode}; use dropbear_engine::utils::{ - relative_path_from_euca, EUCA_SCHEME, ResourceReference, ResourceReferenceType, + relative_path_from_euca, EUCA_SCHEME, }; -use dropbear_engine::component::{ComponentInitContext, ComponentInsert, ComponentResources}; use egui::Align2; +use crate::spawn::{push_pending_spawn, PendingSpawn}; use eucalyptus_core::camera::{CameraComponent, CameraType}; -use eucalyptus_core::properties::CustomProperties; use eucalyptus_core::scripting::{build_jvm, BuildStatus}; -use eucalyptus_core::spawn::{push_pending_spawn, PendingSpawn}; -use eucalyptus_core::states::{EditorTab, Label, Script, PROJECT}; +use eucalyptus_core::states::{EditorTab, PROJECT}; use eucalyptus_core::{fatal, info, success, success_without_console, warn, warn_without_console}; -use std::any::TypeId; use std::path::PathBuf; use std::sync::Arc; use winit::keyboard::KeyCode; @@ -34,33 +24,6 @@ fn resolve_editor_path(uri: &str) -> PathBuf { } } -struct InsertMeshRenderer { - renderer: MeshRenderer, - ensure_transform: bool, -} - -impl ComponentInsert for InsertMeshRenderer { - fn insert( - self: Box<Self>, - world: &mut hecs::World, - entity: hecs::Entity, - ) -> anyhow::Result<()> { - if world.get::<&MeshRenderer>(entity).is_ok() { - let _ = world.remove_one::<MeshRenderer>(entity); - } - - world - .insert_one(entity, self.renderer) - .map_err(|e| anyhow::anyhow!(e.to_string()))?; - - if self.ensure_transform && world.get::<&EntityTransform>(entity).is_err() { - let _ = world.insert_one(entity, EntityTransform::default()); - } - - Ok(()) - } -} - pub trait SignalController { fn run_signal(&mut self, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<()>; } @@ -491,88 +454,21 @@ impl SignalController for Editor { self.signal = Signal::None; Ok(()) } - Signal::LogEntities => { - log::debug!("===================="); - log::info!("world total items: {}", self.world.len()); - log::info!("typeid of Label: {:?}", TypeId::of::<Label>()); - log::info!("typeid of MeshRenderer: {:?}", TypeId::of::<MeshRenderer>()); - log::info!("typeid of Transform: {:?}", TypeId::of::<Transform>()); - log::info!( - "typeid of ModelProperties: {:?}", - TypeId::of::<CustomProperties>() - ); - log::info!("typeid of Camera: {:?}", TypeId::of::<Camera>()); - log::info!( - "typeid of CameraComponent: {:?}", - TypeId::of::<CameraComponent>() - ); - log::info!("typeid of Script: {:?}", TypeId::of::<Script>()); - log::info!("typeid of EngineLight: {:?}", TypeId::of::<EngineLight>()); - log::info!( - "typeid of LightComponent: {:?}", - TypeId::of::<LightComponent>() - ); - for i in self.world.iter() { - log::info!("entity id: {:?}", i.entity().id()); - log::info!("entity bytes: {:?}", i.entity().to_bits().get()); - log::info!( - "components [{}]: ", - i.component_types().collect::<Vec<_>>().len() - ); - let mut comp_builder = String::new(); - for j in i.component_types() { - comp_builder.push_str(format!("{:?} ", j).as_str()); - if TypeId::of::<Label>() == j { - log::info!(" |- Label"); - } - - if TypeId::of::<MeshRenderer>() == j { - log::info!(" |- MeshRenderer"); - } - - if TypeId::of::<Transform>() == j { - log::info!(" |- Transform"); - } - - if TypeId::of::<CustomProperties>() == j { - log::info!(" |- ModelProperties"); - } - - if TypeId::of::<Camera>() == j { - log::info!(" |- Camera"); - } - - if TypeId::of::<CameraComponent>() == j { - log::info!(" |- CameraComponent"); - } - - if TypeId::of::<Script>() == j { - log::info!(" |- Script"); - } - - if TypeId::of::<EngineLight>() == j { - log::info!(" |- Light"); - } - - if TypeId::of::<LightComponent>() == j { - log::info!(" |- LightComponent"); - } - log::info!("----------") - } - log::info!("components (typeid) [{}]: ", comp_builder); - } - self.signal = Signal::None; - Ok(()) - } Signal::AddComponent(entity, component) => { - let mut resources = ComponentResources::new(); - resources.insert(graphics.clone()); - let ctx = ComponentInitContext { - entity: *entity, - resources: Arc::new(resources), - }; + let component = component.clone(); + let registry = self.component_registry.clone(); + let graphics_clone = graphics.clone(); + let init_future = async move { + let Some(loader_future) = + registry.load_component(component.as_ref(), graphics_clone.clone()) + else { + return Err(anyhow::anyhow!( + "Component type is not registered in ComponentRegistry" + )); + }; - let init_future = component.init(ctx); + loader_future.await + }; let handle = graphics.future_queue.push(init_future); self.pending_components.push((*entity, handle)); @@ -586,6 +482,19 @@ impl SignalController for Editor { self.signal = Signal::None; Ok(()) } + Signal::UpdateViewportSize((x, y)) => { + let width = x.max(1.0).round() as u32; + let height = y.max(1.0).round() as u32; + let current_size = graphics.viewport_texture.size; + + if current_size.width != width || current_size.height != height { + self.scene_command = dropbear_engine::scene::SceneCommand::ResizeViewport(( + width, height, + )); + } + self.signal = Signal::None; + Ok(()) + } }?; if !show { self.signal = Signal::None; @@ -1,14 +1,37 @@ use crate::editor::Editor; -use dropbear_engine::entity::{EntityTransform, MeshRenderer}; -use dropbear_engine::future::FutureQueue; -use dropbear_engine::graphics::{SharedGraphicsContext}; -use dropbear_engine::model::{Model}; -pub(crate) use eucalyptus_core::spawn::{PendingSpawnController, PENDING_SPAWNS}; -use eucalyptus_core::{fatal, success}; -use hecs::Entity; -use std::sync::Arc; use dropbear_engine::asset::Handle; -use dropbear_engine::component::{ComponentInitContext, ComponentInsert, ComponentResources}; +use dropbear_engine::entity::{EntityTransform, MeshRenderer}; +use dropbear_engine::future::{FutureHandle, FutureQueue}; +use dropbear_engine::graphics::SharedGraphicsContext; +use dropbear_engine::model::Model; +use eucalyptus_core::hierarchy::Parent; +use eucalyptus_core::scene::SceneEntity; +use eucalyptus_core::states::Label; +use eucalyptus_core::{fatal, success, warn}; +use hecs::{Entity, EntityBuilder}; +use parking_lot::Mutex; +use std::sync::{Arc, LazyLock}; + +#[derive(Clone)] +pub struct PendingSpawn { + pub scene_entity: SceneEntity, + pub handle: Option<FutureHandle>, +} + +pub static PENDING_SPAWNS: LazyLock<Mutex<Vec<PendingSpawn>>> = + LazyLock::new(|| Mutex::new(Vec::new())); + +pub fn push_pending_spawn(spawn: PendingSpawn) { + PENDING_SPAWNS.lock().push(spawn); +} + +pub trait PendingSpawnController { + fn check_up( + &mut self, + graphics: Arc<SharedGraphicsContext>, + queue: Arc<FutureQueue>, + ) -> anyhow::Result<()>; +} impl PendingSpawnController for Editor { fn check_up( @@ -27,25 +50,34 @@ impl PendingSpawnController for Editor { ); if spawn.handle.is_none() { - let entity = self.world.spawn((spawn.scene_entity.label.clone(),)); let components = spawn.scene_entity.components.clone(); - - let mut resources = ComponentResources::new(); - resources.insert(graphics.clone()); - let resources = Arc::new(resources); + let registry = self.component_registry.clone(); + let label = spawn.scene_entity.label.clone(); + let graphics = graphics.clone(); let future = async move { - let mut inserts: Vec<Box<dyn ComponentInsert>> = Vec::new(); + let mut appliers: Vec<Box<dyn for<'a> FnOnce(&'a mut EntityBuilder) + Send + Sync>> = + Vec::new(); for component in components { - let ctx = ComponentInitContext { - entity, - resources: resources.clone(), + if component.as_any().downcast_ref::<Parent>().is_some() { + continue; + } + + let Some(loader_future) = + registry.load_component(component.as_ref(), graphics.clone()) + else { + warn!("Skipping unregistered serialized component for '{}'", label); + continue; }; - let insert = component.init(ctx).await?; - inserts.push(insert); + + let applier = loader_future.await?; + appliers.push(applier); } - Ok::<(Entity, Vec<Box<dyn ComponentInsert>>), anyhow::Error>((entity, inserts)) + Ok::<(Label, Vec<Box<dyn for<'a> FnOnce(&'a mut EntityBuilder) + Send + Sync>>), anyhow::Error>(( + label, + appliers, + )) }; let handle = queue.push(Box::pin(future)); @@ -54,21 +86,21 @@ impl PendingSpawnController for Editor { if let Some(handle) = &spawn.handle { if let Some(result) = queue.exchange_owned(handle) { - if let Ok(r) = result.downcast::<anyhow::Result<(Entity, Vec<Box<dyn ComponentInsert>>)>>() { + if let Ok(r) = result.downcast::<anyhow::Result<(Label, Vec<Box<dyn for<'a> FnOnce(&'a mut EntityBuilder) + Send + Sync>>)>>() { match Arc::try_unwrap(r) { - Ok(Ok((entity, inserts))) => { - for insert in inserts { - insert.insert(&mut self.world, entity)?; + Ok(Ok((label, appliers))) => { + let mut builder = EntityBuilder::new(); + builder.add(label.clone()); + for applier in appliers { + applier(&mut builder); } + let entity = self.world.spawn(builder.build()); if self.world.get::<&EntityTransform>(entity).is_err() { let _ = self.world.insert_one(entity, EntityTransform::default()); } - success!( - "Spawned '{}' from pending queue", - spawn.scene_entity.label - ); + success!("Spawned '{}' from pending queue", label); completed.push(index); } Ok(Err(err)) => { @@ -100,11 +132,16 @@ impl PendingSpawnController for Editor { let mut completed_components = Vec::new(); for (index, (entity, handle)) in self.pending_components.iter().enumerate() { if let Some(result) = queue.exchange_owned(handle) { - if let Ok(r) = result.downcast::<anyhow::Result<Box<dyn ComponentInsert>>>() { + if let Ok(r) = result.downcast::<anyhow::Result<Box<dyn for<'a> FnOnce(&'a mut EntityBuilder) + Send + Sync>>>() { match Arc::try_unwrap(r) { - Ok(Ok(insert)) => { - insert.insert(&mut self.world, *entity)?; - success!("Added component to entity {:?}", entity); + Ok(Ok(applier)) => { + let mut builder = EntityBuilder::new(); + applier(&mut builder); + if let Err(e) = self.world.insert(*entity, builder.build()) { + fatal!("Failed to add component bundle: {}", e); + } else { + success!("Added component to entity {:?}", entity); + } completed_components.push(index); } Ok(Err(e)) => { @@ -179,6 +179,8 @@ typedef struct NChannelValuesFfi { typedef NChannelValuesFfi NChannelValues; +typedef void* GraphicsContextPtr; + typedef struct IndexNative { uint32_t index; uint32_t generation; @@ -190,22 +192,30 @@ typedef struct NCollider { uint32_t id; } NCollider; -typedef struct NTransform { - NVector3 position; - NQuaternion rotation; - NVector3 scale; -} NTransform; - typedef struct NColliderArray { NCollider* values; size_t length; size_t capacity; } NColliderArray; -typedef struct NVector2 { - double x; - double y; -} NVector2; +typedef struct u64Array { + uint64_t* values; + size_t length; + size_t capacity; +} u64Array; + +typedef struct ConnectedGamepadIds { + u64Array ids; +} ConnectedGamepadIds; + +typedef struct NColour { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +} NColour; + +typedef void* AssetRegistryPtr; typedef struct AxisLock { bool x; @@ -220,6 +230,11 @@ typedef struct NVector4 { double w; } NVector4; +typedef struct NVector2 { + double x; + double y; +} NVector2; + typedef struct NMaterial { const char* name; uint64_t diffuse_texture; @@ -244,57 +259,48 @@ typedef struct NMaterialArray { size_t capacity; } NMaterialArray; -typedef struct i32Array { - int32_t* values; +typedef struct f64Array { + double* values; size_t length; size_t capacity; -} i32Array; +} f64Array; -typedef struct f64ArrayArray { - double* values; +typedef struct NAnimationChannel { + int32_t target_node; + f64Array times; + NChannelValues values; + NAnimationInterpolation interpolation; +} NAnimationChannel; + +typedef struct NAnimationChannelArray { + NAnimationChannel* values; size_t length; size_t capacity; -} f64ArrayArray; +} NAnimationChannelArray; -typedef struct NSkin { +typedef struct NAnimation { const char* name; - i32Array joints; - f64ArrayArray inverse_bind_matrices; - const int32_t* skeleton_root; -} NSkin; + NAnimationChannelArray channels; + float duration; +} NAnimation; -typedef struct NSkinArray { - NSkin* values; +typedef struct NAnimationArray { + NAnimation* values; size_t length; size_t capacity; -} NSkinArray; +} NAnimationArray; typedef void* InputStatePtr; -typedef void* GraphicsContextPtr; +typedef void* SceneLoaderPtr; typedef void* PhysicsStatePtr; -typedef struct IndexNativeArray { - IndexNative* values; +typedef struct i32Array { + int32_t* values; size_t length; size_t capacity; -} IndexNativeArray; - -typedef struct CharacterCollisionArray { - uint64_t entity_id; - IndexNativeArray collisions; -} CharacterCollisionArray; - -typedef struct Progress { - size_t current; - size_t total; - const char* message; -} Progress; - -typedef void* AssetRegistryPtr; - -typedef void* WorldPtr; +} i32Array; typedef struct NModelVertex { NVector3 position; @@ -326,56 +332,41 @@ typedef struct NMeshArray { size_t capacity; } NMeshArray; -typedef struct f64Array { +typedef struct f64ArrayArray { double* values; size_t length; size_t capacity; -} f64Array; - -typedef struct NAnimationChannel { - int32_t target_node; - f64Array times; - NChannelValues values; - NAnimationInterpolation interpolation; -} NAnimationChannel; - -typedef struct NAnimationChannelArray { - NAnimationChannel* values; - size_t length; - size_t capacity; -} NAnimationChannelArray; +} f64ArrayArray; -typedef struct NAnimation { +typedef struct NSkin { const char* name; - NAnimationChannelArray channels; - float duration; -} NAnimation; + i32Array joints; + f64ArrayArray inverse_bind_matrices; + const int32_t* skeleton_root; +} NSkin; -typedef struct NAnimationArray { - NAnimation* values; +typedef struct NSkinArray { + NSkin* values; size_t length; size_t capacity; -} NAnimationArray; +} NSkinArray; -typedef struct u64Array { - uint64_t* values; +typedef struct NTransform { + NVector3 position; + NQuaternion rotation; + NVector3 scale; +} NTransform; + +typedef struct IndexNativeArray { + IndexNative* values; size_t length; size_t capacity; -} u64Array; - -typedef struct ConnectedGamepadIds { - u64Array ids; -} ConnectedGamepadIds; +} IndexNativeArray; -typedef struct NShapeCastHit { - NCollider collider; - double distance; - NVector3 witness1; - NVector3 witness2; - NVector3 normal1; - NVector3 normal2; - NShapeCastStatus status; -} NShapeCastHit; +typedef struct CharacterCollisionArray { + uint64_t entity_id; + IndexNativeArray collisions; +} CharacterCollisionArray; typedef struct NAttenuation { float constant; @@ -383,15 +374,36 @@ typedef struct NAttenuation { float quadratic; } NAttenuation; +typedef struct RayHit { + NCollider collider; + double distance; +} RayHit; + +typedef struct Progress { + size_t current; + size_t total; + const char* message; +} Progress; + typedef struct RigidBodyContext { IndexNative index; uint64_t entity_id; } RigidBodyContext; -typedef struct RayHit { +typedef struct NRange { + float start; + float end; +} NRange; + +typedef struct NShapeCastHit { NCollider collider; double distance; -} RayHit; + NVector3 witness1; + NVector3 witness2; + NVector3 normal1; + NVector3 normal2; + NShapeCastStatus status; +} NShapeCastHit; typedef struct NNodeTransform { NVector3 translation; @@ -412,22 +424,10 @@ typedef struct NNodeArray { size_t capacity; } NNodeArray; -typedef struct NColour { - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; -} NColour; - -typedef struct NRange { - float start; - float end; -} NRange; - -typedef void* SceneLoaderPtr; - typedef void* CommandBufferPtr; +typedef void* WorldPtr; + int32_t dropbear_gamepad_is_button_pressed(InputStatePtr input, uint64_t gamepad_id, int32_t button_ordinal, bool* out0); int32_t dropbear_gamepad_get_left_stick_position(InputStatePtr input, uint64_t gamepad_id, NVector2* out0); int32_t dropbear_gamepad_get_right_stick_position(InputStatePtr input, uint64_t gamepad_id, NVector2* out0);