tirbofish/dropbear · diff
refactor: got redback-runtime compilable
Signature present but could not be verified.
Unverified
@@ -87,6 +87,7 @@ bitflags = "2.10" features = "0.10" puffin_http = "0.16" dyn-clone = "1.0" +downcast-rs = "2.0" [workspace.dependencies.image] version = "0.24" @@ -46,6 +46,7 @@ bytemuck.workspace = true thiserror.workspace = true combine.workspace = true dyn-clone.workspace = true +downcast-rs.workspace = true [features] default = [] @@ -30,8 +30,11 @@ impl Component for AnimationComponent { Ok((Self::default(), )) } - async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> { - Ok((ser, )) + 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>> { + Box::pin(async move { Ok((ser.clone(), )) }) } fn update_component(&mut self, world: &World, entity: Entity, dt: f32, graphics: Arc<SharedGraphicsContext>) { @@ -45,10 +45,18 @@ impl Component for Camera { Ok((cam, comp)) } - async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> { - let label = ser.label.clone(); - let builder = CameraBuilder::from(ser.clone()); - Ok((Camera::new(graphics.clone(), builder, Some(label.as_str())), CameraComponent::from(ser))) + 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>> { + Box::pin(async move { + let label = ser.label.clone(); + let builder = CameraBuilder::from(ser.clone()); + Ok(( + Camera::new(graphics.clone(), builder, Some(label.as_str())), + CameraComponent::from(ser.clone()), + )) + }) } fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, graphics: Arc<SharedGraphicsContext>) { @@ -1,5 +1,7 @@ use std::any::TypeId; use std::collections::HashMap; +use std::future::Future; +use std::pin::Pin; use std::sync::Arc; use egui::{CollapsingHeader, Ui}; use hecs::{Entity, World}; @@ -14,6 +16,7 @@ use dropbear_engine::utils::{ResourceReference, ResourceReferenceType}; use crate::hierarchy::EntityTransformExt; use crate::states::{SerializedMaterialCustomisation, SerializedMeshRenderer}; use crate::utils::ResolveReference; +use downcast_rs::{Downcast, impl_downcast}; pub use typetag::*; @@ -26,9 +29,22 @@ pub struct ComponentRegistry { categories: HashMap<String, Vec<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>, } +type LoaderFuture<'a> = Pin<Box< + dyn Future<Output = anyhow::Result<Box<dyn for<'b> FnOnce(&'b mut hecs::EntityBuilder)>>> + Send + 'a +>>; +type LoaderFn = Box< + dyn for<'a> Fn(&'a dyn SerializedComponent, Arc<SharedGraphicsContext>) -> LoaderFuture<'a> + + Send + + Sync +>; 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>; impl ComponentRegistry { pub fn new() -> Self { @@ -36,34 +52,56 @@ impl ComponentRegistry { descriptors: HashMap::new(), fqtn_to_type: HashMap::new(), categories: HashMap::new(), - extractors: Default::default(), + extractors: HashMap::new(), + loaders: HashMap::new(), + updaters: HashMap::new(), } } /// Register a component type with the registry - pub fn register<T: Component + 'static + Sync + Send>(&mut self) { + pub fn register<T>(&mut self) + where + T: Component + Send + Sync + 'static, + T::SerializedForm: 'static, + { let type_id = TypeId::of::<T>(); - let descriptor = T::descriptor(); - - self.fqtn_to_type.insert(descriptor.fqtn.clone(), type_id); + let desc = T::descriptor(); - if let Some(ref category) = descriptor.category { - self.categories - .entry(category.clone()) - .or_insert_with(Vec::new) - .push(type_id); + self.fqtn_to_type.insert(desc.fqtn.clone(), type_id); + if let Some(ref cat) = desc.category { + self.categories.entry(cat.clone()).or_default().push(type_id); } - - self.extractors.insert( - type_id, - Box::new(|world: &hecs::World, entity: hecs::Entity| { - world.get::<&T>(entity).ok().map(|component| { - component.save(world, entity) - }) + self.descriptors.insert(type_id, desc); + + 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| { + let serialized = serialized + .as_any() + .downcast_ref::<T::SerializedForm>() + .expect("type mismatch in loader — registry bug"); + + Box::pin(async move { + let bundle = T::init(serialized, graphics).await?; + let applier: Box<dyn FnOnce(&mut hecs::EntityBuilder)> = + Box::new(move |builder: &mut hecs::EntityBuilder| { + builder.add_bundle(bundle); + }); + Ok(applier) }) - ); - - self.descriptors.insert(type_id, descriptor); + })); + + 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)>(); + for (entity, component) in query.iter() { + let world_ref = unsafe { &*world_ptr }; + component.update_component(world_ref, entity, dt, graphics.clone()); + } + })); } /// Get descriptor for a specific component type @@ -161,6 +199,30 @@ impl ComponentRegistry { }) .unwrap_or_default() } + + /// 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(); + self.loaders + .get(&type_id) + .map(|loader| loader(serialized, graphics)) + } + + /// Updates all registered components that exist in the world. + pub fn update_components( + &self, + world: &mut hecs::World, + dt: f32, + graphics: Arc<SharedGraphicsContext>, + ) { + for updater in self.updaters.values() { + updater(world, dt, graphics.clone()); + } + } } impl Default for ComponentRegistry { @@ -171,8 +233,9 @@ impl Default for ComponentRegistry { /// A blanket trait for types that can be serialized as a component. #[typetag::serde(tag = "type")] -pub trait SerializedComponent: dyn_clone::DynClone + Send + Sync {} +pub trait SerializedComponent: Downcast + dyn_clone::DynClone + Send + Sync {} +impl_downcast!(SerializedComponent); dyn_clone::clone_trait_object!(SerializedComponent); #[derive(Clone, Debug)] @@ -188,7 +251,7 @@ pub struct ComponentDescriptor { } /// Defines a type that can be considered a component of an entity. -pub trait Component: Sized { +pub trait Component: Sized + Sync + Send { /// A custom format of the component for saving the state of the component to disk. /// /// To have your type available, you must include this blanket trait: @@ -211,7 +274,10 @@ pub trait Component: Sized { /// Converts [`Self::SerializedForm`] into a [`Component`] instance that can be added to /// `hecs::EntityBuilder` during scene initialisation. - async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes>; + fn init<'a>( + ser: &'a Self::SerializedForm, + graphics: Arc<SharedGraphicsContext>, + ) -> Pin<Box<dyn Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>>; /// 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>); @@ -248,109 +314,130 @@ impl Component for MeshRenderer { Ok((MeshRenderer::from_handle(Handle::NULL), )) } - async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> { - let import_scale = ser.import_scale.unwrap_or(1.0); - - let handle = match &ser.handle.ref_type { - ResourceReferenceType::None => { - log::debug!("ResourceReferenceType is None, setting to `Handle::NULL`"); - Handle::NULL - } - ResourceReferenceType::File(_) => { - log::debug!("Loading model from file: {:?}", ser.handle); - let path = ser.handle.resolve()?; - let buffer = std::fs::read(&path)?; - Model::load_from_memory_raw(graphics.clone(), buffer, Some(ser.label.as_str()), ASSET_REGISTRY.clone()).await? - } - ResourceReferenceType::Bytes(bytes) => { - log::debug!("Loading model from bytes [Len: {}]", bytes.len()); - Model::load_from_memory_raw(graphics.clone(), bytes, Some(ser.label.as_str()), ASSET_REGISTRY.clone()).await? - } - ResourceReferenceType::ProcObj(obj) => match obj { - dropbear_engine::procedural::ProcObj::Cuboid { size_bits } => { - let size = [ - f32::from_bits(size_bits[0]), - f32::from_bits(size_bits[1]), - f32::from_bits(size_bits[2]), - ]; - log::debug!("Loading model from cuboid: {:?}", size); - - let size_vec = glam::DVec3::new(size[0] as f64, size[1] as f64, size[2] as f64); - ProcedurallyGeneratedObject::cuboid(size_vec).build_model( + fn init<'a>( + ser: &'a Self::SerializedForm, + graphics: Arc<SharedGraphicsContext>, + ) -> Pin<Box<dyn Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> { + Box::pin(async move { + let import_scale = ser.import_scale.unwrap_or(1.0); + + let handle = match &ser.handle.ref_type { + ResourceReferenceType::None => { + log::debug!("ResourceReferenceType is None, setting to `Handle::NULL`"); + Handle::NULL + } + ResourceReferenceType::File(_) => { + log::debug!("Loading model from file: {:?}", ser.handle); + let path = ser.handle.resolve()?; + let buffer = std::fs::read(&path)?; + Model::load_from_memory_raw( graphics.clone(), - None, + buffer, Some(ser.label.as_str()), ASSET_REGISTRY.clone(), ) + .await? } - }, - }; - - let mut renderer = MeshRenderer::from_handle(handle); - renderer.set_import_scale(import_scale); - - for (label, m) in ser.texture_override { - if let Some(mat) = renderer.material_snapshot.get_mut(&label) { - mat.tint = m.tint; - mat.emissive_factor = m.emissive_factor; - mat.metallic_factor = m.metallic_factor; - mat.roughness_factor = m.roughness_factor; - mat.alpha_mode = m.alpha_mode; - mat.alpha_cutoff = m.alpha_cutoff; - mat.double_sided = m.double_sided; - mat.occlusion_strength = m.occlusion_strength; - mat.normal_scale = m.normal_scale; - mat.uv_tiling = m.uv_tiling; - mat.texture_tag = m.texture_tag; - mat.wrap_mode = m.wrap_mode; - - let get_tex = async |resource: Option<ResourceReference>| -> Option<anyhow::Result<Texture>> { - if let Some(dif) = resource { - match dif.ref_type { - ResourceReferenceType::None => { - None - } - ResourceReferenceType::File(_) => { - let path = dif.resolve().ok()?; - let tex = Texture::from_file(graphics.clone(), &path, Some(&label)).await; - Some(tex) - } - ResourceReferenceType::Bytes(bytes) => { - let tex = Texture::from_bytes(graphics.clone(), &bytes, Some(&label)); - Some(Ok(tex)) - } - ResourceReferenceType::ProcObj(_) => { - Some(Err(anyhow::anyhow!("Using a ProcObj as a texture is not valid, for texture with label {}", label))) + ResourceReferenceType::Bytes(bytes) => { + log::debug!("Loading model from bytes [Len: {}]", bytes.len()); + Model::load_from_memory_raw( + graphics.clone(), + bytes, + Some(ser.label.as_str()), + ASSET_REGISTRY.clone(), + ) + .await? + } + ResourceReferenceType::ProcObj(obj) => match obj { + dropbear_engine::procedural::ProcObj::Cuboid { size_bits } => { + let size = [ + f32::from_bits(size_bits[0]), + f32::from_bits(size_bits[1]), + f32::from_bits(size_bits[2]), + ]; + log::debug!("Loading model from cuboid: {:?}", size); + + let size_vec = glam::DVec3::new( + size[0] as f64, + size[1] as f64, + size[2] as f64, + ); + ProcedurallyGeneratedObject::cuboid(size_vec).build_model( + graphics.clone(), + None, + Some(ser.label.as_str()), + ASSET_REGISTRY.clone(), + ) + } + }, + }; + + let mut renderer = MeshRenderer::from_handle(handle); + renderer.set_import_scale(import_scale); + + for (label, m) in &ser.texture_override { + if let Some(mat) = renderer.material_snapshot.get_mut(&label.clone()) { + mat.tint = m.tint; + mat.emissive_factor = m.emissive_factor; + mat.metallic_factor = m.metallic_factor; + mat.roughness_factor = m.roughness_factor; + mat.alpha_mode = m.alpha_mode; + mat.alpha_cutoff = m.alpha_cutoff; + mat.double_sided = m.double_sided; + mat.occlusion_strength = m.occlusion_strength; + mat.normal_scale = m.normal_scale; + mat.uv_tiling = m.uv_tiling; + mat.texture_tag = m.texture_tag.clone(); + mat.wrap_mode = m.wrap_mode; + + let get_tex = async |resource: &Option<ResourceReference>| -> Option<anyhow::Result<Texture>> { + if let Some(dif) = resource { + match &dif.ref_type { + ResourceReferenceType::None => { + None + } + ResourceReferenceType::File(_) => { + let path = dif.resolve().ok()?; + let tex = Texture::from_file(graphics.clone(), &path, Some(&label)).await; + Some(tex) + } + ResourceReferenceType::Bytes(bytes) => { + let tex = Texture::from_bytes(graphics.clone(), bytes, Some(&label)); + Some(Ok(tex)) + } + ResourceReferenceType::ProcObj(_) => { + Some(Err(anyhow::anyhow!("Using a ProcObj as a texture is not valid, for texture with label {}", label))) + } } + } else { + None } - } else { - None - } - }; + }; - if let Some(tex) = get_tex(m.diffuse_texture).await { - mat.diffuse_texture = tex?; - } + if let Some(tex) = get_tex(&m.diffuse_texture).await { + mat.diffuse_texture = tex?; + } - if let Some(tex) = get_tex(m.emissive_texture).await { - mat.emissive_texture = Some(tex?); - } + if let Some(tex) = get_tex(&m.emissive_texture).await { + mat.emissive_texture = Some(tex?); + } - if let Some(tex) = get_tex(m.normal_texture).await { - mat.normal_texture = tex?; - } + if let Some(tex) = get_tex(&m.normal_texture).await { + mat.normal_texture = tex?; + } - if let Some(tex) = get_tex(m.occlusion_texture).await { - mat.occlusion_texture = Some(tex?); - } + if let Some(tex) = get_tex(&m.occlusion_texture).await { + mat.occlusion_texture = Some(tex?); + } - if let Some(tex) = get_tex(m.metallic_roughness_texture).await { - mat.metallic_roughness_texture = Some(tex?); + if let Some(tex) = get_tex(&m.metallic_roughness_texture).await { + mat.metallic_roughness_texture = Some(tex?); + } } } - } - Ok((renderer, )) + Ok((renderer, )) + }) } fn update_component(&mut self, world: &World, entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) { @@ -40,14 +40,19 @@ impl Component for Light { Ok((light, comp)) } - async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> { - let light = Light::new( - graphics.clone(), - ser.light_component.clone(), - Some(ser.label.as_str()) - ).await; - - Ok((light, ser.light_component)) + 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>> { + Box::pin(async move { + let light = Light::new( + graphics.clone(), + ser.light_component.clone(), + Some(ser.label.as_str()) + ).await; + + Ok((light, ser.light_component.clone())) + }) } fn update_component(&mut self, world: &World, entity: Entity, _dt: f32, graphics: Arc<SharedGraphicsContext>) { @@ -60,7 +65,6 @@ impl Component for Light { if let Ok((_, comp)) = world.query_one::<(&Light, &LightComponent)>(entity).get() { Box::new(SerializedLight { label: self.label.clone(), - transform: comp.to_transform(), light_component: comp.clone(), enabled: comp.enabled, entity_id: Some(entity), @@ -79,8 +79,11 @@ impl Component for ColliderGroup { Ok((Self::new(), )) } - async fn init(ser: Self::SerializedForm, graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> { - Ok((ser, )) + 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>> { + Box::pin(async move { Ok((ser.clone(), )) }) } fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} @@ -57,8 +57,11 @@ impl Component for KCC { Ok((Self::default(), )) } - async fn init(ser: Self::SerializedForm, _: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> { - Ok((ser, )) + fn init<'a>( + ser: &'a Self::SerializedForm, + _: Arc<SharedGraphicsContext>, + ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> { + Box::pin(async move { Ok((ser.clone(), )) }) } fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} @@ -209,8 +209,11 @@ impl Component for RigidBody { Ok((Self::default(), )) } - async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> { - Ok((ser, )) + 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>> { + Box::pin(async move { Ok((ser.clone(), )) }) } fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} @@ -44,8 +44,11 @@ impl Component for CustomProperties { Ok((Self::new(), )) } - async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> { - Ok((ser, )) + 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>> { + Box::pin(async move { Ok((ser.clone(), )) }) } fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} @@ -9,7 +9,7 @@ use crate::states::{SerializableCamera, Label, SerializedLight, Script, Serializ 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::lighting::{Light, LightComponent}; use glam::{DQuat, DVec3}; use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; use ron::ser::PrettyConfig; @@ -20,7 +20,7 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use crossbeam_channel::Sender; use egui::Ui; -use hecs::Entity; +use hecs::{Entity, EntityBuilder}; use crate::component::{Component, ComponentRegistry, SerializedComponent}; use crate::physics::collider::ColliderGroup; use crate::physics::kcc::KCC; @@ -157,7 +157,7 @@ impl SceneConfig { &mut self, world: &mut hecs::World, graphics: Arc<SharedGraphicsContext>, - _registry: Option<&ComponentRegistry>, + registry: &ComponentRegistry, progress_sender: Option<Sender<WorldLoadingStatus>>, is_play_mode: bool, ) -> anyhow::Result<hecs::Entity> { @@ -165,28 +165,19 @@ impl SceneConfig { let _ = s.send(WorldLoadingStatus::Idle); } - log::info!( + // clear world to make room for new entities + log::debug!( "Loading scene [{}], clearing world with {} entities", self.scene_name, world.len() ); world.clear(); - #[allow(unused_variables)] - let project_config = if cfg!(feature = "editor") { - let cfg = PROJECT.read(); - cfg.project_path.clone() - } else { - log::debug!("Not using the editor feature, returning empty pathbuffer"); - PathBuf::new() - }; - log::info!("World cleared, now has {} entities", world.len()); - let mut resources = ComponentResources::new(); - resources.insert(graphics.clone()); - let resources = Arc::new(resources); + let mut label_to_entity: HashMap<Label, hecs::Entity> = HashMap::new(); + // gather all entities let entity_configs: Vec<(usize, SceneEntity)> = { let cloned = self.entities.clone(); cloned @@ -196,8 +187,7 @@ impl SceneConfig { .collect() }; - let mut label_to_entity: HashMap<Label, hecs::Entity> = HashMap::new(); - + // fetch all entities for (index, entity_config) in entity_configs { let SceneEntity { label, @@ -220,18 +210,11 @@ impl SceneConfig { }); } - let entity = world.spawn((label_for_map.clone(),)); - - let mut has_entity_transform = false; + // all entities will ALWAYS have a label. if it doesnt, its not a valid entity + let mut builder = EntityBuilder::new(); + builder.add(label_for_map.clone()); - for component in components { - if component - .as_any() - .downcast_ref::<EntityTransform>() - .is_some() - { - has_entity_transform = true; - } + for component in components.iter() { if component.as_any().downcast_ref::<Parent>().is_some() { log::debug!( @@ -241,79 +224,23 @@ impl SceneConfig { continue; } - Self::init_component( - &component, - entity, - world, - resources.clone(), - &label_for_logs, - ) - .await?; - } - - if has_entity_transform { - if let Ok(( - entity_transform, - renderer_opt, - light_opt, - light_comp_opt - )) = world.query_one::<( - &EntityTransform, - Option<&mut MeshRenderer>, - Option<&mut EngineLight>, - Option<&mut LightComponent>, - )>(entity).get() - { - let transform = entity_transform.sync(); - - if let Some(renderer) = renderer_opt { - renderer.update(&transform); - log::debug!("Updated renderer transform for '{}'", label_for_logs); - } + let Some(loader_future) = + registry.load_component(component.as_ref(), graphics.clone()) + else { + log::warn!( + "Skipping unregistered serialized component for '{}'", + label_for_logs + ); + continue; + }; - if let (Some(light), Some(light_comp)) = (light_opt, light_comp_opt) { - light.update(graphics.as_ref(), light_comp, &transform); - log::debug!("Updated light transform for '{}'", label_for_logs); - } - } + let applier = loader_future.await?; + applier(&mut builder); } - // adding to physics - if let Ok(( - label, - e_trans, - rigid, - col_group, - kcc - )) = world.query_one::<( - &Label, - &EntityTransform, - Option<&mut RigidBody>, - Option<&mut ColliderGroup>, - Option<&mut KCC> - )>(entity).get() { - - // rigidbody - if let Some(body) = rigid { - body.entity = label.clone(); - - self.physics_state.register_rigidbody(body, e_trans.sync()); - } - - // collider group - if let Some(group) = col_group { - for collider in &mut group.colliders { - collider.entity = label.clone(); + let entity = world.spawn(builder.build()); - self.physics_state.register_collider(collider); - } - } - - // kinematic character controller - if let Some(kcc) = kcc { - kcc.entity = label.clone(); - } - } + self.register_physics_for_entity(world, entity); if let Some(previous) = label_to_entity.insert(label_for_map.clone(), entity) { log::warn!( @@ -326,6 +253,53 @@ impl SceneConfig { log::debug!("Loaded entity '{}'", label_for_logs); } + self.rebuild_hierarchy(world, &label_to_entity); + self.ensure_default_light(world, graphics.clone(), progress_sender.as_ref()).await?; + + log::info!("Loaded {} entities from scene", self.entities.len()); + + let camera_entity = + self.select_active_camera(world, graphics, progress_sender.as_ref(), is_play_mode)?; + Ok(camera_entity) + } + + fn register_physics_for_entity(&mut self, world: &mut hecs::World, entity: hecs::Entity) { + if let Ok(( + label, + e_trans, + rigid, + col_group, + kcc + )) = world.query_one::<( + &Label, + &EntityTransform, + Option<&mut RigidBody>, + Option<&mut ColliderGroup>, + Option<&mut KCC> + )>(entity).get() { + if let Some(body) = rigid { + body.entity = label.clone(); + self.physics_state.register_rigidbody(body, e_trans.sync()); + } + + if let Some(group) = col_group { + for collider in &mut group.colliders { + collider.entity = label.clone(); + self.physics_state.register_collider(collider); + } + } + + if let Some(kcc) = kcc { + kcc.entity = label.clone(); + } + } + } + + fn rebuild_hierarchy( + &self, + world: &mut hecs::World, + label_to_entity: &HashMap<Label, hecs::Entity>, + ) { let mut parent_children_map: HashMap<Label, Vec<Label>> = HashMap::new(); for entity_label in label_to_entity.keys() { @@ -397,26 +371,144 @@ impl SceneConfig { ); } } + } + async fn ensure_default_light( + &self, + world: &mut hecs::World, + graphics: Arc<SharedGraphicsContext>, + progress_sender: Option<&Sender<WorldLoadingStatus>>, + ) -> anyhow::Result<()> { + let mut has_light = false; + if world + .query::<(&LightComponent, &Light)>() + .iter() + .next() + .is_some() { - let mut has_light = false; - if world - .query::<(&LightComponent, &EngineLight)>() + has_light = true; + } + + if !has_light { + log::info!("No lights in scene, spawning default light"); + + let legacy_lights: Vec<hecs::Entity> = world + .query::<(Entity, &Label)>() .iter() - .next() - .is_some() - { - has_light = true; + .filter_map(|(entity, label)| { + if label.as_str() == "Default Light" { + Some(entity) + } else { + None + } + }) + .collect(); + + for entity in legacy_lights { + if let Err(err) = world.despawn(entity) { + log::warn!( + "Failed to remove legacy 'Default Light' entity {:?}: {}", + entity, + err + ); + } else { + log::debug!( + "Removed legacy 'Default Light' placeholder entity {:?}", + entity + ); + } } - if !has_light { - log::info!("No lights in scene, spawning default light"); + if let Some(s) = progress_sender { + let _ = s.send(WorldLoadingStatus::LoadingEntity { + index: 0, + name: String::from("Default Light"), + total: 1, + }); + } + let comp = LightComponent::directional(glam::DVec3::ONE, 1.0); + let light_direction = LightComponent::default_direction(); + let rotation = + DQuat::from_rotation_arc(DVec3::new(0.0, 0.0, -1.0), light_direction); + let light = + Light::new(graphics.clone(), comp.clone(), Some("Default Light")) + .await; + + let light_config = SerializedLight { + label: "Default Light".to_string(), + light_component: comp.clone(), + enabled: true, + entity_id: None, + }; - let legacy_lights: Vec<hecs::Entity> = world + world.spawn(( + Label::from("Default Light"), + comp, + light, + light_config, + CustomProperties::default(), + )); + } + + Ok(()) + } + + fn select_active_camera( + &self, + world: &mut hecs::World, + graphics: Arc<SharedGraphicsContext>, + progress_sender: Option<&Sender<WorldLoadingStatus>>, + is_play_mode: bool, + ) -> anyhow::Result<hecs::Entity> { + use crate::camera::CameraType; + + if is_play_mode { + log::debug!("Running in play mode"); + let starting_camera = world + .query::<(Entity, &Camera, &CameraComponent)>() + .iter() + .find_map(|(entity, _, component)| { + if component.starting_camera { + log::debug!("Found starting camera: {:?}", entity); + Some(entity) + } else { + None + } + }); + + if let Some(camera_entity) = starting_camera { + log::debug!("Using starting camera for play mode"); + Ok(camera_entity) + } else { + panic!("Unable to locate any starting camera while playing") + } + } else { + let debug_camera = { + world + .query::<(Entity, &Camera, &CameraComponent)>() + .iter() + .find_map(|(entity, _, component)| { + if matches!(component.camera_type, CameraType::Debug) { + log::debug!("Found debug camera: {:?}", entity); + Some(entity) + } else { + None + } + }) + }; + + if let Some(camera_entity) = debug_camera { + log::info!("Using existing debug camera for editor"); + Ok(camera_entity) + } else { + log::info!("No debug or starting camera found, creating viewport camera for editor"); + + let legacy_cameras: Vec<hecs::Entity> = world .query::<(Entity, &Label)>() .iter() .filter_map(|(entity, label)| { - if label.as_str() == "Default Light" { + if label.as_str() == "Viewport Camera" { + log::debug!("Found 'Viewport Camera' entity: {:?}", entity); Some(entity) } else { None @@ -424,150 +516,33 @@ impl SceneConfig { }) .collect(); - for entity in legacy_lights { + for entity in legacy_cameras { if let Err(err) = world.despawn(entity) { log::warn!( - "Failed to remove legacy 'Default Light' entity {:?}: {}", + "Failed to remove legacy 'Viewport Camera' entity {:?}: {}", entity, err ); } else { log::debug!( - "Removed legacy 'Default Light' placeholder entity {:?}", + "Removed legacy 'Viewport Camera' placeholder entity {:?}", entity ); } } - if let Some(ref s) = progress_sender { + if let Some(s) = progress_sender { let _ = s.send(WorldLoadingStatus::LoadingEntity { index: 0, - name: String::from("Default Light"), + name: String::from("Viewport Camera"), total: 1, }); } - let comp = LightComponent::directional(glam::DVec3::ONE, 1.0); - let light_direction = LightComponent::default_direction(); - let rotation = - DQuat::from_rotation_arc(DVec3::new(0.0, 0.0, -1.0), light_direction); - let trans = Transform { - position: glam::DVec3::new(2.0, 4.0, 2.0), - rotation, - ..Default::default() - }; - let light = - EngineLight::new(graphics.clone(), comp.clone(), trans, Some("Default Light")) - .await; - - let light_config = SerializedLight { - label: "Default Light".to_string(), - transform: trans, - light_component: comp.clone(), - enabled: true, - entity_id: None, - }; - - { - world.spawn(( - Label::from("Default Light"), - comp, - trans, - light, - light_config, - CustomProperties::default(), - )); - } - } - } - - log::info!("Loaded {} entities from scene", self.entities.len()); - { - use crate::camera::CameraType; - - if is_play_mode { - log::debug!("Running in play mode"); - let starting_camera = world - .query::<(Entity, &Camera, &CameraComponent)>() - .iter() - .find_map(|(entity, _, component)| { - if component.starting_camera { - log::debug!("Found starting camera: {:?}", entity); - Some(entity) - } else { - None - } - }); - - if let Some(camera_entity) = starting_camera { - log::debug!("Using starting camera for play mode"); - Ok(camera_entity) - } else { - panic!("Unable to locate any starting camera while playing") - } - } else { - let debug_camera = { - world - .query::<(Entity, &Camera, &CameraComponent)>() - .iter() - .find_map(|(entity, _, component)| { - if matches!(component.camera_type, CameraType::Debug) { - log::debug!("Found debug camera: {:?}", entity); - Some(entity) - } else { - None - } - }) - }; - - { - if let Some(camera_entity) = debug_camera { - log::info!("Using existing debug camera for editor"); - Ok(camera_entity) - } else { - log::info!("No debug or starting camera found, creating viewport camera for editor"); - - let legacy_cameras: Vec<hecs::Entity> = world - .query::<(Entity, &Label)>() - .iter() - .filter_map(|(entity, label)| { - if label.as_str() == "Viewport Camera" { - log::debug!("Found 'Viewport Camera' entity: {:?}", entity); - Some(entity) - } else { - None - } - }) - .collect(); - - for entity in legacy_cameras { - if let Err(err) = world.despawn(entity) { - log::warn!( - "Failed to remove legacy 'Viewport Camera' entity {:?}: {}", - entity, - err - ); - } else { - log::debug!( - "Removed legacy 'Viewport Camera' placeholder entity {:?}", - entity - ); - } - } - - if let Some(ref s) = progress_sender { - let _ = s.send(WorldLoadingStatus::LoadingEntity { - index: 0, - name: String::from("Viewport Camera"), - total: 1, - }); - } - let camera = Camera::predetermined(graphics.clone(), Some("Viewport Camera")); - let component = crate::camera::DebugCamera::new(); - let label = Label::new("Viewport Camera"); - let camera_entity = { world.spawn((label, camera, component)) }; - Ok(camera_entity) - } - } + let camera = Camera::predetermined(graphics.clone(), Some("Viewport Camera")); + let component = crate::camera::DebugCamera::new(); + let label = Label::new("Viewport Camera"); + let camera_entity = world.spawn((label, camera, component)); + Ok(camera_entity) } } } @@ -181,8 +181,11 @@ impl Component for Script { Ok((Self::default(), )) } - async fn init(ser: Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> { - Ok((ser, )) + 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>> { + Box::pin(async move { Ok((ser.clone(), )) }) } fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} @@ -297,7 +300,6 @@ pub struct Property { #[derive(Debug, Serialize, Deserialize, Clone)] pub struct SerializedLight { pub label: String, - pub transform: Transform, pub light_component: LightComponent, pub enabled: bool, @@ -309,7 +311,6 @@ impl Default for SerializedLight { fn default() -> Self { Self { label: "Default Light".to_string(), - transform: Transform::default(), light_component: LightComponent::default(), enabled: true, entity_id: None, @@ -34,8 +34,11 @@ impl Component for EntityTransform { Ok((Self::default(), )) } - async fn init(ser: Self::SerializedForm, _: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> { - Ok((ser, )) + fn init<'a>( + ser: &'a Self::SerializedForm, + _: Arc<SharedGraphicsContext>, + ) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<Self::RequiredComponentTypes>> + Send + 'a>> { + Box::pin(async move { Ok((ser.clone(), )) }) } fn update_component(&mut self, _: &World, _: Entity, _: f32, _: Arc<SharedGraphicsContext>) {} @@ -22,7 +22,6 @@ use log; use parking_lot::Mutex; use wgpu::Color; use winit::{event_loop::ActiveEventLoop, keyboard::KeyCode}; -use dropbear_engine::component::ComponentResources; use eucalyptus_core::physics::collider::ColliderGroup; use eucalyptus_core::physics::collider::ColliderShapeKey; use eucalyptus_core::physics::collider::shader::{ColliderInstanceRaw, create_wireframe_geometry}; @@ -124,13 +123,8 @@ impl Scene for Editor { } } - { - let mut resources = ComponentResources::new(); - resources.insert(graphics.clone()); - let resources = Arc::new(resources); - self.component_registry - .update_components(self.world.as_mut(), dt, &resources); - } + self.component_registry + .update_components(self.world.as_mut(), dt, graphics.clone()); if !self.is_world_loaded.is_fully_loaded() { log::debug!("Scene is not fully loaded, initialising..."); @@ -282,63 +276,7 @@ impl Scene for Editor { } } - { - let sim_world = self.world.as_mut(); - - for (_entity_id, camera, component) in sim_world - .query::<(Entity, &mut Camera, &mut CameraComponent)>() - .iter() - { - component.update(camera); - camera.update(graphics.clone()); - } - } - - { - let sim_world = self.world.as_mut(); - - { - let query = sim_world.query_mut::<(&mut MeshRenderer, &Transform)>(); - for (renderer, transform) in query { - renderer.update(transform); - } - } - - { - let mut updates = Vec::new(); - for (entity, transform) in sim_world.query::<(Entity, &EntityTransform)>().iter() { - let final_transform = transform.propagate(sim_world, entity); - updates.push((entity, final_transform)); - } - - for (entity, final_transform) in updates { - if let Ok(mut renderer) = sim_world.get::<&mut MeshRenderer>(entity) { - renderer.update(&final_transform); - } - } - } - - { - let light_query = sim_world.query_mut::<( - &mut LightComponent, - Option<&Transform>, - Option<&EntityTransform>, - &mut Light, - )>(); - - for (light_component, transform_opt, entity_transform_opt, light) in light_query { - 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); - } - } - } + if let Some(l) = &mut self.light_cube_pipeline { l.update(graphics.clone(), &self.world); @@ -7,6 +7,7 @@ use dropbear_engine::pipelines::DropbearShaderPipeline; use dropbear_engine::pipelines::GlobalsUniform; use dropbear_engine::pipelines::light_cube::LightCubePipeline; use dropbear_engine::pipelines::shader::MainRenderPipeline; +use eucalyptus_core::component::ComponentRegistry; use eucalyptus_core::physics::collider::shader::ColliderWireframePipeline; use eucalyptus_core::physics::collider::shader::ColliderInstanceRaw; use eucalyptus_core::physics::collider::{ColliderShapeKey, WireframeGeometry}; @@ -19,7 +20,6 @@ use eucalyptus_core::input::InputState; use eucalyptus_core::scripting::{ScriptManager, ScriptTarget}; use eucalyptus_core::states::{WorldLoadingStatus, SCENES, Script}; use eucalyptus_core::scene::loading::{SceneLoadResult, SCENE_LOADER}; -use eucalyptus_core::traits::registry::ComponentRegistry; use eucalyptus_core::ptr::{CommandBufferPtr, GraphicsContextPtr, InputStatePtr, PhysicsStatePtr, WorldPtr}; use eucalyptus_core::command::COMMAND_BUFFER; use eucalyptus_core::scene::loading::IsSceneLoaded; @@ -29,8 +29,6 @@ use log::error; use wgpu::SurfaceConfiguration; use winit::window::Fullscreen; use dropbear_engine::sky::{HdrLoader, SkyPipeline, DEFAULT_SKY_TEXTURE}; -// use yakui_winit::YakuiWinit; -use dropbear_engine::texture::Texture; use eucalyptus_core::physics::PhysicsState; use eucalyptus_core::rapier3d::prelude::*; use eucalyptus_core::register_components; @@ -352,7 +350,7 @@ impl PlayMode { let load_status = scene_to_load.load_into_world( &mut temp_world, graphics_cloned, - Some(&component_registry), + &component_registry.clone(), Some(tx), true, ).await; @@ -420,7 +418,7 @@ impl PlayMode { let camera = scene_to_load.load_into_world( &mut temp_world, graphics_cloned, - Some(&component_registry), + &component_registry.clone(), Some(tx), true, ).await; @@ -23,7 +23,6 @@ use dropbear_engine::lighting::{Light, LightComponent}; use dropbear_engine::lighting::MAX_LIGHTS; use dropbear_engine::model::{DrawLight, DrawModel, Model}; use dropbear_engine::scene::{Scene, SceneCommand}; -use eucalyptus_core::camera::CameraComponent; use eucalyptus_core::command::CommandBufferPoller; use eucalyptus_core::hierarchy::{EntityTransformExt, Parent}; use eucalyptus_core::physics::kcc::KCC; @@ -32,7 +31,6 @@ use eucalyptus_core::rapier3d::geometry::SharedShape; use eucalyptus_core::states::{Label, PROJECT}; use eucalyptus_core::states::SCENES; use eucalyptus_core::scene::loading::{IsSceneLoaded, SceneLoadResult, SCENE_LOADER}; -use eucalyptus_core::traits::ComponentResources; use crate::PlayMode; use eucalyptus_core::physics::collider::shader::create_wireframe_geometry; use kino_ui::widgets::{Anchor, Border, Fill}; @@ -334,84 +332,10 @@ impl Scene for PlayMode { } } - { - let mut resources = ComponentResources::new(); - resources.insert(graphics.clone()); - let resources = Arc::new(resources); - self.component_registry - .update_components(self.world.as_mut(), dt, &resources); - } - - { - let mut query = self.world.query::<(&mut MeshRenderer, &Transform)>(); - for (renderer, transform) in query.iter() { - renderer.update(transform); - } - } - - { - let mut updates = Vec::new(); - for (entity, transform) in self.world.query::<(Entity, &EntityTransform)>().iter() { - let final_transform = transform.propagate(&self.world, entity); - updates.push((entity, final_transform)); - } - - for (entity, final_transform) in updates { - if let Ok(mut renderer) = self.world.get::<&mut MeshRenderer>(entity) { - renderer.update(&final_transform); - } - } - } - - { - let registry = ASSET_REGISTRY.read(); - let mut query = self - .world - .query::<(&MeshRenderer, &mut AnimationComponent)>(); - - for (renderer, animation) in query.iter() { - let handle = renderer.model(); - if handle.is_null() { - continue; - } - - let Some(model) = registry.get_model(handle) else { - continue; - }; - - animation.update(dt, model); - animation.prepare_gpu_resources(graphics.clone()); - } - } - - { - let mut light_query = self - .world - .query::<(&mut LightComponent, Option<&Transform>, Option<&EntityTransform>, &mut Light)>(); - - for (light_comp, transform_opt, entity_transform_opt, light) in light_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; - }; + self.component_registry + .update_components(self.world.as_mut(), dt, graphics.clone()); - light.update(graphics.as_ref(), light_comp, &transform); - } - } - - { - for (camera, component) in self - .world - .query::<(&mut Camera, &mut CameraComponent)>() - .iter() - { - component.update(camera); - camera.update(graphics.clone()); - } - } + if let Some(l) = &mut self.light_cube_pipeline { l.update(graphics.clone(), &self.world); @@ -759,21 +683,11 @@ impl Scene for PlayMode { { 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); + for (light_component, light) in query.iter() { + light.update(graphics.as_ref(), light_component); } } @@ -184,22 +184,11 @@ typedef struct IndexNative { uint32_t generation; } IndexNative; -typedef struct IndexNativeArray { - IndexNative* values; - size_t length; - size_t capacity; -} IndexNativeArray; - -typedef struct CharacterCollisionArray { +typedef struct NCollider { + IndexNative index; uint64_t entity_id; - IndexNativeArray collisions; -} CharacterCollisionArray; - -typedef struct NAttenuation { - float constant; - float linear; - float quadratic; -} NAttenuation; + uint32_t id; +} NCollider; typedef struct NTransform { NVector3 position; @@ -207,17 +196,22 @@ typedef struct NTransform { NVector3 scale; } NTransform; -typedef struct NColour { - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; -} NColour; +typedef struct NColliderArray { + NCollider* values; + size_t length; + size_t capacity; +} NColliderArray; -typedef struct NRange { - float start; - float end; -} NRange; +typedef struct NVector2 { + double x; + double y; +} NVector2; + +typedef struct AxisLock { + bool x; + bool y; + bool z; +} AxisLock; typedef struct NVector4 { double x; @@ -226,10 +220,29 @@ typedef struct NVector4 { double w; } NVector4; -typedef struct NVector2 { - double x; - double y; -} NVector2; +typedef struct NMaterial { + const char* name; + uint64_t diffuse_texture; + uint64_t normal_texture; + NVector4 tint; + NVector3 emissive_factor; + float metallic_factor; + float roughness_factor; + const float* alpha_cutoff; + bool double_sided; + float occlusion_strength; + float normal_scale; + NVector2 uv_tiling; + const uint64_t* emissive_texture; + const uint64_t* metallic_roughness_texture; + const uint64_t* occlusion_texture; +} NMaterial; + +typedef struct NMaterialArray { + NMaterial* values; + size_t length; + size_t capacity; +} NMaterialArray; typedef struct i32Array { int32_t* values; @@ -237,6 +250,52 @@ typedef struct i32Array { size_t capacity; } i32Array; +typedef struct f64ArrayArray { + double* values; + size_t length; + size_t capacity; +} f64ArrayArray; + +typedef struct NSkin { + const char* name; + i32Array joints; + f64ArrayArray inverse_bind_matrices; + const int32_t* skeleton_root; +} NSkin; + +typedef struct NSkinArray { + NSkin* values; + size_t length; + size_t capacity; +} NSkinArray; + +typedef void* InputStatePtr; + +typedef void* GraphicsContextPtr; + +typedef void* PhysicsStatePtr; + +typedef struct IndexNativeArray { + IndexNative* 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; + typedef struct NModelVertex { NVector3 position; NVector3 normal; @@ -267,16 +326,36 @@ typedef struct NMeshArray { size_t capacity; } NMeshArray; -typedef struct NCollider { - IndexNative index; - uint64_t entity_id; - uint32_t id; -} NCollider; +typedef struct f64Array { + double* values; + size_t length; + size_t capacity; +} f64Array; -typedef struct RayHit { - NCollider collider; - double distance; -} RayHit; +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; + +typedef struct NAnimation { + const char* name; + NAnimationChannelArray channels; + float duration; +} NAnimation; + +typedef struct NAnimationArray { + NAnimation* values; + size_t length; + size_t capacity; +} NAnimationArray; typedef struct u64Array { uint64_t* values; @@ -288,22 +367,6 @@ typedef struct ConnectedGamepadIds { u64Array ids; } ConnectedGamepadIds; -typedef struct AxisLock { - bool x; - bool y; - bool z; -} AxisLock; - -typedef void* WorldPtr; - -typedef struct NColliderArray { - NCollider* values; - size_t length; - size_t capacity; -} NColliderArray; - -typedef void* InputStatePtr; - typedef struct NShapeCastHit { NCollider collider; double distance; @@ -314,9 +377,21 @@ typedef struct NShapeCastHit { NShapeCastStatus status; } NShapeCastHit; -typedef void* PhysicsStatePtr; +typedef struct NAttenuation { + float constant; + float linear; + float quadratic; +} NAttenuation; -typedef void* AssetRegistryPtr; +typedef struct RigidBodyContext { + IndexNative index; + uint64_t entity_id; +} RigidBodyContext; + +typedef struct RayHit { + NCollider collider; + double distance; +} RayHit; typedef struct NNodeTransform { NVector3 translation; @@ -337,96 +412,21 @@ typedef struct NNodeArray { size_t capacity; } NNodeArray; -typedef struct f64Array { - 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; - -typedef struct NAnimation { - const char* name; - NAnimationChannelArray channels; - float duration; -} NAnimation; - -typedef struct NAnimationArray { - NAnimation* values; - size_t length; - size_t capacity; -} NAnimationArray; - -typedef struct Progress { - size_t current; - size_t total; - const char* message; -} Progress; - -typedef void* CommandBufferPtr; - -typedef struct NMaterial { - const char* name; - uint64_t diffuse_texture; - uint64_t normal_texture; - NVector4 tint; - NVector3 emissive_factor; - float metallic_factor; - float roughness_factor; - const float* alpha_cutoff; - bool double_sided; - float occlusion_strength; - float normal_scale; - NVector2 uv_tiling; - const uint64_t* emissive_texture; - const uint64_t* metallic_roughness_texture; - const uint64_t* occlusion_texture; -} NMaterial; +typedef struct NColour { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +} NColour; -typedef struct NMaterialArray { - NMaterial* values; - size_t length; - size_t capacity; -} NMaterialArray; +typedef struct NRange { + float start; + float end; +} NRange; typedef void* SceneLoaderPtr; -typedef struct RigidBodyContext { - IndexNative index; - uint64_t entity_id; -} RigidBodyContext; - -typedef struct f64ArrayArray { - double* values; - size_t length; - size_t capacity; -} f64ArrayArray; - -typedef struct NSkin { - const char* name; - i32Array joints; - f64ArrayArray inverse_bind_matrices; - const int32_t* skeleton_root; -} NSkin; - -typedef struct NSkinArray { - NSkin* values; - size_t length; - size_t capacity; -} NSkinArray; - -typedef void* GraphicsContextPtr; +typedef void* CommandBufferPtr; 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);