tirbofish/dropbear · commit
f785b02ee1ce9d6c404c07b12f6ffece4fedafca
wip: mess
Signature present but could not be verified.
Unverified
@@ -7,6 +7,7 @@ use crate::{ texture::Texture, }; use crate::graphics::SharedGraphicsContext; +use crate::utils::ResourceReference; use crate::model::Model; pub static ASSET_REGISTRY: LazyLock<Arc<RwLock<AssetRegistry>>> = LazyLock::new(|| Arc::new(RwLock::new(AssetRegistry::new()))); @@ -261,6 +262,20 @@ impl AssetRegistry { self.model_labels.get(label).cloned() } + pub fn list_models(&self) -> Vec<(Handle<Model>, String, ResourceReference)> { + self.models + .values() + .map(|model| (Handle::new(model.hash), model.label.clone(), model.path.clone())) + .collect() + } + + pub fn get_model_handle_by_reference(&self, reference: &ResourceReference) -> Option<Handle<Model>> { + self.models + .values() + .find(|model| &model.path == reference) + .map(|model| Handle::new(model.hash)) + } + pub fn model_handle_by_hash(&self, hash: u64) -> Option<Handle<Model>> { self.models.contains_key(&hash).then(|| Handle::new(hash)) } @@ -227,46 +227,22 @@ impl Transform { ui.label("Scale:"); }); - let mut uniform_scale = self.scale.x == self.scale.y - && self.scale.y == self.scale.z; - ui.horizontal(|ui| { - if ui.checkbox(&mut uniform_scale, "Uniform").changed() { - if uniform_scale { - let avg = (self.scale.x + self.scale.y + self.scale.z) / 3.0; - self.scale = DVec3::splat(avg); - } - } - }); + ui.colored_label(egui::Color32::from_rgb(200, 80, 80), "X:"); + ui.add(egui::DragValue::new(&mut self.scale.x) + .speed(0.01) + .fixed_decimals(3)); - if uniform_scale { - ui.horizontal(|ui| { - ui.label("XYZ:"); - if ui.add(egui::DragValue::new(&mut self.scale.x) - .speed(0.01) - .fixed_decimals(3)).changed() - { - self.scale = DVec3::splat(self.scale.x); - } - }); - } else { - ui.horizontal(|ui| { - ui.colored_label(egui::Color32::from_rgb(200, 80, 80), "X:"); - ui.add(egui::DragValue::new(&mut self.scale.x) - .speed(0.01) - .fixed_decimals(3)); - - ui.colored_label(egui::Color32::from_rgb(80, 200, 80), "Y:"); - ui.add(egui::DragValue::new(&mut self.scale.y) - .speed(0.01) - .fixed_decimals(3)); - - ui.colored_label(egui::Color32::from_rgb(80, 120, 220), "Z:"); - ui.add(egui::DragValue::new(&mut self.scale.z) - .speed(0.01) - .fixed_decimals(3)); - }); - } + ui.colored_label(egui::Color32::from_rgb(80, 200, 80), "Y:"); + ui.add(egui::DragValue::new(&mut self.scale.y) + .speed(0.01) + .fixed_decimals(3)); + + ui.colored_label(egui::Color32::from_rgb(80, 120, 220), "Z:"); + ui.add(egui::DragValue::new(&mut self.scale.z) + .speed(0.01) + .fixed_decimals(3)); + }); } } @@ -10,6 +10,5 @@ fn main() -> anyhow::Result<()> { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=src"); - println!("cargo:rerun-if-changed=../../include/dropbear.h"); Ok(()) } @@ -23,13 +23,6 @@ impl Component for AnimationComponent { } } - async fn first_time(_graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> - where - Self: Sized - { - Ok((Self::default(), )) - } - fn init<'a>( ser: &'a Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, @@ -37,7 +30,7 @@ impl Component for AnimationComponent { Box::pin(async move { Ok((ser.clone(), )) }) } - fn update_component(&mut self, world: &World, entity: Entity, dt: f32, graphics: Arc<SharedGraphicsContext>) { + fn update_component(&mut self, world: &World, _physics: &mut crate::physics::PhysicsState, entity: Entity, dt: f32, graphics: Arc<SharedGraphicsContext>) { let Ok(renderer) = world.get::<&MeshRenderer>(entity) else { return; }; @@ -63,26 +56,49 @@ impl Component for AnimationComponent { } impl InspectableComponent for AnimationComponent { - fn inspect(&mut self, ui: &mut Ui) { + fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { CollapsingHeader::new("Animation").default_open(true).show(ui, |ui| { - ui.label("Active animation:"); let mut enabled = self.active_animation_index.is_some(); let mut value = self.active_animation_index.unwrap_or(0); ui.horizontal(|ui| { + ui.label("Active Animation"); if ui.checkbox(&mut enabled, "Enable").changed() { self.active_animation_index = if enabled { Some(value) } else { None }; } - ui.add_enabled(enabled, egui::DragValue::new(&mut value)); + let response = ui.add_enabled( + enabled, + egui::DragValue::new(&mut value).speed(1.0).range(0..=1_000_000), + ); - if enabled && self.active_animation_index != Some(value) { + if enabled && response.changed() { self.active_animation_index = Some(value); } }); - ui.label("Not implemented yet!"); - // todo: complete some more + ui.horizontal(|ui| { + ui.label("Playing"); + ui.checkbox(&mut self.is_playing, ""); + }); + + ui.horizontal(|ui| { + ui.label("Looping"); + ui.checkbox(&mut self.looping, ""); + }); + + ui.horizontal(|ui| { + ui.label("Speed"); + ui.add(egui::DragValue::new(&mut self.speed).speed(0.01).range(0.0..=10.0)); + }); + + ui.horizontal(|ui| { + ui.label("Time"); + ui.add(egui::DragValue::new(&mut self.time).speed(0.01).range(0.0..=1_000_000.0)); + if ui.button("Reset").clicked() { + self.time = 0.0; + } + }); }); } } @@ -36,15 +36,6 @@ impl Component for Camera { } } - async fn first_time(graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> - where - Self: Sized - { - let comp = CameraComponent::new(); - let cam = Camera::predetermined(graphics.clone(), Some("default camera")); - Ok((cam, comp)) - } - fn init<'a>( ser: &'a Self::SerializedForm, graphics: Arc<SharedGraphicsContext>, @@ -59,7 +50,7 @@ impl Component for Camera { }) } - fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, graphics: Arc<SharedGraphicsContext>) { + fn update_component(&mut self, _world: &World, _physics: &mut crate::physics::PhysicsState, _entity: Entity, _dt: f32, graphics: Arc<SharedGraphicsContext>) { self.update(graphics.clone()) } @@ -73,9 +64,79 @@ impl Component for Camera { } impl InspectableComponent for Camera { - fn inspect(&mut self, ui: &mut Ui) { + fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { CollapsingHeader::new("Camera3D").show(ui, |ui| { - ui.label("Not implemented yet!"); + let mut changed = false; + + ui.horizontal(|ui| { + ui.label("Label"); + changed |= ui.text_edit_singleline(&mut self.label).changed(); + }); + + ui.horizontal(|ui| { + ui.label("Eye"); + changed |= ui.add(egui::DragValue::new(&mut self.eye.x).speed(0.1)).changed(); + changed |= ui.add(egui::DragValue::new(&mut self.eye.y).speed(0.1)).changed(); + changed |= ui.add(egui::DragValue::new(&mut self.eye.z).speed(0.1)).changed(); + }); + + ui.horizontal(|ui| { + ui.label("Target"); + changed |= ui.add(egui::DragValue::new(&mut self.target.x).speed(0.1)).changed(); + changed |= ui.add(egui::DragValue::new(&mut self.target.y).speed(0.1)).changed(); + changed |= ui.add(egui::DragValue::new(&mut self.target.z).speed(0.1)).changed(); + }); + + ui.horizontal(|ui| { + ui.label("Up"); + changed |= ui.add(egui::DragValue::new(&mut self.up.x).speed(0.1)).changed(); + changed |= ui.add(egui::DragValue::new(&mut self.up.y).speed(0.1)).changed(); + changed |= ui.add(egui::DragValue::new(&mut self.up.z).speed(0.1)).changed(); + }); + + ui.horizontal(|ui| { + ui.label("Aspect"); + changed |= ui + .add(egui::DragValue::new(&mut self.aspect).speed(0.01).range(0.1..=10.0)) + .changed(); + }); + + ui.horizontal(|ui| { + ui.label("Near Plane"); + changed |= ui + .add(egui::DragValue::new(&mut self.znear).speed(0.01).range(0.01..=1000.0)) + .changed(); + }); + + ui.horizontal(|ui| { + ui.label("Far Plane"); + changed |= ui + .add(egui::DragValue::new(&mut self.zfar).speed(1.0).range(0.1..=10000.0)) + .changed(); + }); + + ui.horizontal(|ui| { + ui.label("FOV"); + changed |= ui + .add(egui::Slider::new(&mut self.settings.fov_y, 1.0..=179.0).suffix("°")) + .changed(); + }); + + ui.horizontal(|ui| { + ui.label("Speed"); + changed |= ui.add(egui::DragValue::new(&mut self.settings.speed).speed(0.1)).changed(); + }); + + ui.horizontal(|ui| { + ui.label("Sensitivity"); + changed |= ui + .add(egui::DragValue::new(&mut self.settings.sensitivity).speed(0.001)) + .changed(); + }); + + if changed { + self.update_view_proj(); + } }); } } @@ -3,23 +3,26 @@ use std::collections::HashMap; use std::future::Future; use std::pin::Pin; use std::sync::Arc; -use egui::{CollapsingHeader, Ui}; +use egui::{CollapsingHeader, ComboBox, DragValue, RichText, Ui, UiBuilder}; use hecs::{Entity, World}; use serde::{Deserialize, Serialize}; use dropbear_engine::asset::{Handle, ASSET_REGISTRY}; use dropbear_engine::entity::{EntityTransform, MeshRenderer}; use dropbear_engine::graphics::SharedGraphicsContext; use dropbear_engine::model::{Model}; -use dropbear_engine::procedural::ProcedurallyGeneratedObject; +use dropbear_engine::procedural::{ProcObj, ProcedurallyGeneratedObject}; use dropbear_engine::texture::Texture; use dropbear_engine::utils::{ResourceReference, ResourceReferenceType}; use crate::hierarchy::EntityTransformExt; +use crate::physics::PhysicsState; use crate::states::{SerializedMaterialCustomisation, SerializedMeshRenderer}; use crate::utils::ResolveReference; use downcast_rs::{Downcast, impl_downcast}; pub use typetag::*; +pub const DRAGGED_ASSET_ID: &str = "dragged_asset_reference"; + pub struct ComponentRegistry { /// Maps TypeId to ComponentDescriptor for quick lookups descriptors: HashMap<TypeId, ComponentDescriptor>, @@ -59,11 +62,11 @@ type LoaderFn = Box< + 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>; +type UpdateFn = Box<dyn Fn(&mut hecs::World, &mut PhysicsState, 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>; -type InspectFn = Box<dyn Fn(&mut hecs::World, hecs::Entity, &mut egui::Ui) + Send + Sync>; +type InspectFn = Box<dyn Fn(&mut hecs::World, hecs::Entity, &mut egui::Ui, Arc<SharedGraphicsContext>) + Send + Sync>; // fn inspect(&mut self, ui: &mut egui::Ui); @@ -140,18 +143,18 @@ impl ComponentRegistry { .collect() })); - self.updaters.insert(type_id, Box::new(|world, dt, graphics| { + self.updaters.insert(type_id, Box::new(|world, physics, 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()); + component.update_component(world_ref, physics, entity, dt, graphics.clone()); } })); - self.inspectors.insert(type_id, Box::new(|world, entity, ui| { + self.inspectors.insert(type_id, Box::new(|world, entity, ui, graphics| { if let Ok(mut comp) = world.get::<&mut T>(entity) { - comp.inspect(ui); + comp.inspect(ui, graphics); } })); } @@ -323,11 +326,12 @@ impl ComponentRegistry { pub fn update_components( &self, world: &mut hecs::World, + physics: &mut PhysicsState, dt: f32, graphics: Arc<SharedGraphicsContext>, ) { for updater in self.updaters.values() { - updater(world, dt, graphics.clone()); + updater(world, physics, dt, graphics.clone()); } } @@ -337,6 +341,7 @@ impl ComponentRegistry { world: &mut hecs::World, entity: hecs::Entity, ui: &mut egui::Ui, + graphics: Arc<SharedGraphicsContext>, ) { if let Ok(mut label) = world.get::<&mut crate::states::Label>(entity) { ui.horizontal(|ui| { @@ -357,7 +362,7 @@ impl ComponentRegistry { }; if let Some(inspector) = self.inspectors.get(&type_id) { - inspector(world, entity, ui); + inspector(world, entity, ui, graphics.clone()); } else { ui.label(format!("{} (no inspector)", desc.type_name)); } @@ -426,10 +431,6 @@ pub trait Component: Sync + Send { fn descriptor() -> ComponentDescriptor; - /// Creates a new instance of the component for times when there is no existing component to - /// initialise from. - async fn first_time(graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes>; - /// Converts [`Self::SerializedForm`] into a [`Component`] instance that can be added to /// `hecs::EntityBuilder` during scene initialisation. fn init<'a>( @@ -438,21 +439,21 @@ pub trait Component: Sync + Send { ) -> 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>); + fn update_component(&mut self, world: &hecs::World, physics: &mut PhysicsState, entity: hecs::Entity, dt: f32, graphics: Arc<SharedGraphicsContext>); /// Called when saving the scene to disk. Returns the [`Self::SerializedForm`] of the component that can be /// saved to disk. fn save(&self, world: &hecs::World, entity: hecs::Entity) -> Box<dyn SerializedComponent>; } -#[typetag::serde] -impl SerializedComponent for SerializedMeshRenderer {} - pub trait InspectableComponent: Send + Sync { /// In the editor, how the component will be represented in the `Resource Viewer` dock. - fn inspect(&mut self, ui: &mut egui::Ui); + fn inspect(&mut self, ui: &mut egui::Ui, graphics: Arc<SharedGraphicsContext>); } +#[typetag::serde] +impl SerializedComponent for SerializedMeshRenderer {} + // sample for MeshRenderer impl Component for MeshRenderer { type SerializedForm = SerializedMeshRenderer; @@ -467,13 +468,6 @@ impl Component for MeshRenderer { } } - async fn first_time(_graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> - where - Self: Sized - { - Ok((MeshRenderer::from_handle(Handle::NULL), )) - } - fn init<'a>( ser: &'a Self::SerializedForm, graphics: Arc<SharedGraphicsContext>, @@ -486,14 +480,14 @@ impl Component for MeshRenderer { log::debug!("ResourceReferenceType is None, setting to `Handle::NULL`"); Handle::NULL } - ResourceReferenceType::File(_) => { + ResourceReferenceType::File(reference) => { 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()), + Some(reference), ASSET_REGISTRY.clone(), ) .await? @@ -503,7 +497,7 @@ impl Component for MeshRenderer { Model::load_from_memory_raw( graphics.clone(), bytes, - Some(ser.label.as_str()), + None, ASSET_REGISTRY.clone(), ) .await? @@ -600,7 +594,7 @@ impl Component for MeshRenderer { }) } - fn update_component(&mut self, world: &World, entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) { + fn update_component(&mut self, world: &World, _physics: &mut PhysicsState, entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) { if let Ok(transform) = world.query_one::<&EntityTransform>(entity).get() { self.update(&transform.propagate(&world, entity)) } @@ -705,9 +699,399 @@ impl Component for MeshRenderer { } impl InspectableComponent for MeshRenderer { - fn inspect(&mut self, ui: &mut egui::Ui) { + fn inspect(&mut self, ui: &mut egui::Ui, graphics: Arc<SharedGraphicsContext>) { + fn is_probably_model_uri(uri: &str) -> bool { + let uri = uri.to_ascii_lowercase(); + uri.ends_with(".glb") + || uri.ends_with(".gltf") + || uri.ends_with(".obj") + || uri.ends_with(".fbx") + } + + let apply_cuboid = |renderer: &mut MeshRenderer, size: [f32; 3]| { + let size_vec = glam::DVec3::new(size[0] as f64, size[1] as f64, size[2] as f64); + let handle = ProcedurallyGeneratedObject::cuboid(size_vec).build_model( + graphics.clone(), + None, + Some("Cuboid"), + ASSET_REGISTRY.clone(), + ); + + let size_bits = [size[0].to_bits(), size[1].to_bits(), size[2].to_bits()]; + { + let mut registry = ASSET_REGISTRY.write(); + if let Some(model) = registry.get_model(handle).cloned() { + let mut model = model; + model.path = ResourceReference::from_reference(ResourceReferenceType::ProcObj( + ProcObj::Cuboid { size_bits }, + )); + registry.update_model(handle, model); + } + } + + renderer.set_model(handle); + renderer.reset_texture_override(); + }; + CollapsingHeader::new("Mesh Renderer").show(ui, |ui| { - ui.label("Not implemented yet (MeshRenderer)"); + let registry = ASSET_REGISTRY.read(); + let current_model = registry.get_model(self.model()).cloned(); + let model_list = registry.list_models(); + drop(registry); + + let model_reference = current_model + .as_ref() + .map(|model| model.path.clone()) + .unwrap_or_default(); + let model_label = current_model + .as_ref() + .map(|model| model.label.clone()) + .unwrap_or_else(|| "None".to_string()); + + let model_title = match &model_reference.ref_type { + ResourceReferenceType::None => "None".to_string(), + ResourceReferenceType::ProcObj(ProcObj::Cuboid { .. }) => "Cuboid".to_string(), + _ => model_label, + }; + + ui.vertical(|ui| { + let expand_id = ui.make_persistent_id(format!("mesh_renderer_expand_{}", self.model().id)); + let mut expanded = ui + .ctx() + .data_mut(|d| d.get_temp::<bool>(expand_id).unwrap_or(false)); + + let mut selected_model: Option<Handle<Model>> = None; + let mut choose_proc_cuboid = false; + let mut choose_none = false; + + let (rect, response) = ui.allocate_exact_size( + egui::vec2(ui.available_width(), 72.0), + egui::Sense::click(), + ); + + let fill = if response.hovered() { + ui.visuals().widgets.hovered.bg_fill + } else { + ui.visuals().widgets.inactive.bg_fill + }; + + ui.painter().rect_filled(rect, 4.0, fill); + ui.painter().rect_stroke( + rect, + 4.0, + ui.visuals().widgets.inactive.bg_stroke, + egui::StrokeKind::Inside, + ); + + let mut card_ui = ui.new_child( + UiBuilder::new() + .layout(egui::Layout::top_down(egui::Align::Min)) + .max_rect(rect), + ); + + card_ui.horizontal(|ui| { + let arrow = if expanded { "v" } else { ">" }; + if ui.button(arrow).clicked() { + expanded = !expanded; + } + + ui.vertical(|ui| { + ui.label(RichText::new(&model_title).strong()); + ui.label( + RichText::new("Drop a model from the Asset Viewer") + .small() + .color(ui.visuals().weak_text_color()), + ); + }); + + ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { + ComboBox::from_id_salt("mesh_renderer_model_picker") + .selected_text(&model_title) + .show_ui(ui, |ui| { + if ui + .selectable_label( + matches!(model_reference.ref_type, ResourceReferenceType::None), + "None", + ) + .clicked() + { + choose_none = true; + } + + ui.separator(); + + if ui + .selectable_label( + matches!( + model_reference.ref_type, + ResourceReferenceType::ProcObj(ProcObj::Cuboid { .. }) + ), + "Cuboid", + ) + .clicked() + { + choose_proc_cuboid = true; + } + + ui.separator(); + + for (handle, label, _) in &model_list { + if handle.is_null() { + continue; + } + let is_selected = self.model() == *handle; + if ui.selectable_label(is_selected, label).clicked() { + selected_model = Some(*handle); + } + } + }); + }); + }); + + let pointer_released = ui.input(|i| i.pointer.any_released()); + if pointer_released && response.hovered() { + let drag_id = egui::Id::new(DRAGGED_ASSET_ID); + let dragged_reference = ui + .ctx() + .data_mut(|d| d.get_temp::<Option<ResourceReference>>(drag_id).unwrap_or(None)); + if let Some(reference) = dragged_reference { + if let Some(uri) = reference.as_uri() { + if is_probably_model_uri(uri) { + if let Some(handle) = + ASSET_REGISTRY.read().get_model_handle_by_reference(&reference) + { + self.set_model(handle); + self.reset_texture_override(); + } + ui.ctx().data_mut(|d| { + d.insert_temp(drag_id, None::<ResourceReference>) + }); + } + } + } + } + + ui.ctx().data_mut(|d| d.insert_temp(expand_id, expanded)); + + if choose_proc_cuboid { + let default_size = match &model_reference.ref_type { + ResourceReferenceType::ProcObj(ProcObj::Cuboid { size_bits }) => [ + f32::from_bits(size_bits[0]), + f32::from_bits(size_bits[1]), + f32::from_bits(size_bits[2]), + ], + _ => [1.0, 1.0, 1.0], + }; + apply_cuboid(self, default_size); + } else if choose_none { + self.set_model(Handle::NULL); + self.reset_texture_override(); + } else if let Some(handle) = selected_model { + self.set_model(handle); + self.reset_texture_override(); + } + + if expanded { + ui.add_space(6.0); + + if let ResourceReferenceType::File(reference) = &model_reference.ref_type { + if is_probably_model_uri(reference) { + let mut import_scale = self.import_scale(); + ui.horizontal(|ui| { + ui.label("Import Scale"); + let resp = ui.add( + DragValue::new(&mut import_scale) + .speed(0.01) + .range(0.0001..=10_000.0), + ); + + if resp.changed() { + self.set_import_scale(import_scale); + } + + if ui.button("Reset").clicked() { + self.set_import_scale(1.0); + } + }); + ui.add_space(6.0); + } + } + + if let ResourceReferenceType::ProcObj(ProcObj::Cuboid { size_bits }) = &model_reference.ref_type { + let mut size = [ + f32::from_bits(size_bits[0]), + f32::from_bits(size_bits[1]), + f32::from_bits(size_bits[2]), + ]; + + ui.label(RichText::new("Cuboid").strong()); + ui.horizontal(|ui| { + ui.label("Extents:"); + let mut changed = false; + ui.label("X"); + changed |= ui + .add(DragValue::new(&mut size[0]).speed(0.05).range(0.01..=10_000.0)) + .changed(); + ui.label("Y"); + changed |= ui + .add(DragValue::new(&mut size[1]).speed(0.05).range(0.01..=10_000.0)) + .changed(); + ui.label("Z"); + changed |= ui + .add(DragValue::new(&mut size[2]).speed(0.05).range(0.01..=10_000.0)) + .changed(); + + if changed { + apply_cuboid(self, size); + } + }); + + ui.separator(); + } + + ui.label("Materials"); + + for (material_name, material) in self.material_snapshot.iter_mut() { + ui.separator(); + ui.label(material_name.as_str()); + + let mut tint_rgb = [material.tint[0], material.tint[1], material.tint[2]]; + if egui::color_picker::color_edit_button_rgb(ui, &mut tint_rgb).changed() { + material.tint[0] = tint_rgb[0]; + material.tint[1] = tint_rgb[1]; + material.tint[2] = tint_rgb[2]; + } + + let mut emissive_rgb = [ + material.emissive_factor[0], + material.emissive_factor[1], + material.emissive_factor[2], + ]; + if egui::color_picker::color_edit_button_rgb(ui, &mut emissive_rgb).changed() { + material.emissive_factor[0] = emissive_rgb[0]; + material.emissive_factor[1] = emissive_rgb[1]; + material.emissive_factor[2] = emissive_rgb[2]; + } + + ui.horizontal(|ui| { + ui.label("Metallic"); + ui.add(DragValue::new(&mut material.metallic_factor) + .speed(0.01) + .range(0.0..=1.0)); + }); + + ui.horizontal(|ui| { + ui.label("Roughness"); + ui.add(DragValue::new(&mut material.roughness_factor) + .speed(0.01) + .range(0.0..=1.0)); + }); + + ui.horizontal(|ui| { + ui.label("Occlusion Strength"); + ui.add(DragValue::new(&mut material.occlusion_strength) + .speed(0.01) + .range(0.0..=1.0)); + }); + + ui.horizontal(|ui| { + ui.label("Normal Scale"); + ui.add(DragValue::new(&mut material.normal_scale) + .speed(0.01) + .range(0.0..=10.0)); + }); + + ui.horizontal(|ui| { + ui.label("Alpha Mode"); + egui::ComboBox::from_id_salt(format!("alpha_mode_{}", material_name)) + .selected_text(match material.alpha_mode { + dropbear_engine::model::AlphaMode::Opaque => "Opaque", + dropbear_engine::model::AlphaMode::Mask => "Mask", + dropbear_engine::model::AlphaMode::Blend => "Blend", + }) + .show_ui(ui, |ui| { + ui.selectable_value( + &mut material.alpha_mode, + dropbear_engine::model::AlphaMode::Opaque, + "Opaque", + ); + ui.selectable_value( + &mut material.alpha_mode, + dropbear_engine::model::AlphaMode::Mask, + "Mask", + ); + ui.selectable_value( + &mut material.alpha_mode, + dropbear_engine::model::AlphaMode::Blend, + "Blend", + ); + }); + }); + + let mut cutoff = material.alpha_cutoff.unwrap_or(0.5); + ui.horizontal(|ui| { + ui.label("Alpha Cutoff"); + if ui.add(DragValue::new(&mut cutoff) + .speed(0.01) + .range(0.0..=1.0)) + .changed() + { + material.alpha_cutoff = Some(cutoff); + } + }); + + ui.horizontal(|ui| { + ui.label("Double Sided"); + ui.checkbox(&mut material.double_sided, ""); + }); + + ui.horizontal(|ui| { + ui.label("Wrap"); + egui::ComboBox::from_id_salt(format!("wrap_mode_{}", material_name)) + .selected_text(match material.wrap_mode { + dropbear_engine::texture::TextureWrapMode::Repeat => "Repeat", + dropbear_engine::texture::TextureWrapMode::Clamp => "Clamp", + }) + .show_ui(ui, |ui| { + ui.selectable_value( + &mut material.wrap_mode, + dropbear_engine::texture::TextureWrapMode::Repeat, + "Repeat", + ); + ui.selectable_value( + &mut material.wrap_mode, + dropbear_engine::texture::TextureWrapMode::Clamp, + "Clamp", + ); + }); + }); + + ui.horizontal(|ui| { + ui.label("UV Tiling"); + ui.add( + DragValue::new(&mut material.uv_tiling[0]) + .speed(0.05) + .range(0.01..=10_000.0), + ); + ui.label("x"); + ui.add( + DragValue::new(&mut material.uv_tiling[1]) + .speed(0.05) + .range(0.01..=10_000.0), + ); + }); + + let mut texture_tag = material.texture_tag.clone().unwrap_or_default(); + if ui.text_edit_singleline(&mut texture_tag).changed() { + material.texture_tag = if texture_tag.trim().is_empty() { + None + } else { + Some(texture_tag) + }; + } + } + } + }); }); } } @@ -31,12 +31,11 @@ use dropbear_engine::camera::Camera; use dropbear_engine::entity::{EntityTransform, MeshRenderer}; use dropbear_engine::lighting::Light; use properties::CustomProperties; -use crate::camera::CameraComponent; use crate::component::ComponentRegistry; use crate::physics::collider::ColliderGroup; use crate::physics::kcc::KCC; use crate::physics::rigidbody::RigidBody; -use crate::states::{SerializableCamera, SerializedLight, Script, SerializedMeshRenderer}; +use crate::states::{Script}; /// The appdata directory for storing any information. /// @@ -1,10 +1,11 @@ use std::sync::Arc; -use egui::{CollapsingHeader, Ui}; +use egui::{CollapsingHeader, DragValue, Ui}; use crate::ptr::WorldPtr; use crate::scripting::jni::utils::{FromJObject, ToJObject}; use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; use crate::types::NVector3; +use dropbear_engine::asset::ASSET_REGISTRY; use dropbear_engine::entity::{EntityTransform, Transform}; use dropbear_engine::lighting::{Light, LightComponent, LightType}; use glam::{DQuat, DVec3}; @@ -31,15 +32,6 @@ impl Component for Light { } } - async fn first_time(graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> - where - Self: Sized - { - let comp = LightComponent::default(); - let light = Light::new(graphics.clone(), comp.clone(), None).await; - Ok((light, comp)) - } - fn init<'a>( ser: &'a Self::SerializedForm, graphics: Arc<SharedGraphicsContext>, @@ -55,7 +47,7 @@ impl Component for Light { }) } - fn update_component(&mut self, world: &World, entity: Entity, _dt: f32, graphics: Arc<SharedGraphicsContext>) { + fn update_component(&mut self, world: &World, _physics: &mut crate::physics::PhysicsState, entity: Entity, _dt: f32, graphics: Arc<SharedGraphicsContext>) { if let Ok(comp) = world.query_one::<&LightComponent>(entity).get() { self.update(&graphics, comp); } @@ -76,9 +68,89 @@ impl Component for Light { } impl InspectableComponent for Light { - fn inspect(&mut self, ui: &mut Ui) { + fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { CollapsingHeader::new("Light").default_open(true).show(ui, |ui| { - ui.label("Not implemented yet"); + ui.horizontal(|ui| { + ui.label("Label"); + ui.text_edit_singleline(&mut self.label); + }); + + let registry = ASSET_REGISTRY.read(); + let current_label = registry + .get_label_from_model_handle(self.cube_model) + .unwrap_or_else(|| "(unlabeled)".to_string()); + + ui.horizontal(|ui| { + ui.label("Cube Model"); + ui.label(current_label.as_str()); + }); + + let label_id = ui.make_persistent_id("light_cube_model_label"); + let mut model_label = ui + .data_mut(|d| d.get_temp::<String>(label_id)) + .unwrap_or_else(|| current_label.clone()); + + let mut invalid_label = false; + ui.horizontal(|ui| { + ui.label("Model Label"); + let response = ui.text_edit_singleline(&mut model_label); + if response.changed() { + ui.data_mut(|d| d.insert_temp(label_id, model_label.clone())); + } + + if ui.button("Apply").clicked() { + if let Some(handle) = registry.get_model_handle_from_label(&model_label) { + self.cube_model = handle; + ui.data_mut(|d| d.insert_temp(label_id, model_label.clone())); + } else { + invalid_label = true; + } + } + }); + + if invalid_label { + ui.label("Unknown model label"); + } + + ui.add_space(6.0); + ui.label("Uniform"); + + ui.horizontal(|ui| { + ui.label("Position"); + ui.add(DragValue::new(&mut self.uniform.position[0]).speed(0.01)); + ui.add(DragValue::new(&mut self.uniform.position[1]).speed(0.01)); + ui.add(DragValue::new(&mut self.uniform.position[2]).speed(0.01)); + }); + + ui.horizontal(|ui| { + ui.label("Direction"); + ui.add(DragValue::new(&mut self.uniform.direction[0]).speed(0.01)); + ui.add(DragValue::new(&mut self.uniform.direction[1]).speed(0.01)); + ui.add(DragValue::new(&mut self.uniform.direction[2]).speed(0.01)); + }); + + let mut colour_rgb = [ + self.uniform.colour[0], + self.uniform.colour[1], + self.uniform.colour[2], + ]; + if egui::color_picker::color_edit_button_rgb(ui, &mut colour_rgb).changed() { + self.uniform.colour[0] = colour_rgb[0]; + self.uniform.colour[1] = colour_rgb[1]; + self.uniform.colour[2] = colour_rgb[2]; + } + + ui.horizontal(|ui| { + ui.label("Attenuation"); + ui.add(DragValue::new(&mut self.uniform.constant).speed(0.01)); + ui.add(DragValue::new(&mut self.uniform.linear).speed(0.01)); + ui.add(DragValue::new(&mut self.uniform.quadratic).speed(0.01)); + }); + + ui.horizontal(|ui| { + ui.label("Cutoff"); + ui.add(DragValue::new(&mut self.uniform.cutoff).speed(0.01)); + }); }); } } @@ -27,7 +27,7 @@ use ::jni::JNIEnv; use rapier3d::prelude::ColliderBuilder; use serde::{Deserialize, Serialize}; use std::sync::Arc; -use egui::{CollapsingHeader, Ui}; +use egui::{CollapsingHeader, ComboBox, Ui}; use crate::physics::collider::shared::{get_collider, get_collider_mut}; use crate::scripting::native::DropbearNativeError; use crate::scripting::result::DropbearNativeResult; @@ -72,13 +72,6 @@ impl Component for ColliderGroup { } } - async fn first_time(_: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> - where - Self: Sized - { - Ok((Self::new(), )) - } - fn init<'a>( ser: &'a Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, @@ -86,7 +79,17 @@ impl Component for ColliderGroup { Box::pin(async move { Ok((ser.clone(), )) }) } - fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} + fn update_component(&mut self, world: &World, _physics: &mut PhysicsState, entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) { + let Ok(label) = world.get::<&Label>(entity) else { + return; + }; + + for collider in &mut self.colliders { + if collider.entity != *label { + collider.entity = Label::new(label.as_str()); + } + } + } fn save(&self, _world: &World, _entity: Entity) -> Box<dyn SerializedComponent> { Box::new(self.clone()) @@ -94,9 +97,34 @@ impl Component for ColliderGroup { } impl InspectableComponent for ColliderGroup { - fn inspect(&mut self, ui: &mut Ui) { + fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { CollapsingHeader::new("Colliders").default_open(true).show(ui, |ui| { - ui.label("Not implemented yet!"); + let mut remove_index: Option<usize> = None; + + for (index, collider) in self.colliders.iter_mut().enumerate() { + ui.push_id(index, |ui| { + CollapsingHeader::new(format!("Collider {}", index + 1)) + .default_open(true) + .show(ui, |ui| { + collider.inspect(ui); + + ui.add_space(6.0); + if ui.button("Remove Collider").clicked() { + remove_index = Some(index); + } + }); + }); + + ui.add_space(6.0); + } + + if let Some(index) = remove_index { + self.colliders.remove(index); + } + + if ui.button("Add Collider").clicked() { + self.colliders.push(Collider::new()); + } }); } } @@ -140,6 +168,163 @@ pub struct Collider { pub rotation: [f32; 3], } +impl Collider { + pub fn inspect(&mut self, ui: &mut egui::Ui) { + ui.vertical(|ui| { + ui.label("Shape:"); + let current_shape = self.shape_type_name(); + ComboBox::from_id_salt("collider_shape") + .selected_text(current_shape) + .show_ui(ui, |ui| { + if ui.selectable_label(current_shape == "Box", "Box").clicked() { + if current_shape != "Box" { + self.shape = ColliderShape::Box { half_extents: [0.5, 0.5, 0.5].into() }; + } + } + if ui.selectable_label(current_shape == "Sphere", "Sphere").clicked() { + if current_shape != "Sphere" { + self.shape = ColliderShape::Sphere { radius: 0.5 }; + } + } + if ui.selectable_label(current_shape == "Capsule", "Capsule").clicked() { + if current_shape != "Capsule" { + self.shape = ColliderShape::Capsule { half_height: 0.5, radius: 0.25 }; + } + } + if ui.selectable_label(current_shape == "Cylinder", "Cylinder").clicked() { + if current_shape != "Cylinder" { + self.shape = ColliderShape::Cylinder { half_height: 0.5, radius: 0.25 }; + } + } + if ui.selectable_label(current_shape == "Cone", "Cone").clicked() { + if current_shape != "Cone" { + self.shape = ColliderShape::Cone { half_height: 0.5, radius: 0.25 }; + } + } + }); + + ui.add_space(8.0); + + match &mut self.shape { + ColliderShape::Box { half_extents } => { + ui.label("Half Extents:"); + ui.horizontal(|ui| { + ui.label("X:"); + ui.add(egui::DragValue::new(&mut half_extents.x) + .speed(0.01)); + ui.label("Y:"); + ui.add(egui::DragValue::new(&mut half_extents.y) + .speed(0.01)); + ui.label("Z:"); + ui.add(egui::DragValue::new(&mut half_extents.z) + .speed(0.01)); + }); + } + ColliderShape::Sphere { radius } => { + ui.horizontal(|ui| { + ui.label("Radius:"); + ui.add(egui::DragValue::new(radius) + .speed(0.01)); + }); + } + ColliderShape::Capsule { half_height, radius } => { + ui.horizontal(|ui| { + ui.label("Half Height:"); + ui.add(egui::DragValue::new(half_height) + .speed(0.01)); + }); + ui.horizontal(|ui| { + ui.label("Radius:"); + ui.add(egui::DragValue::new(radius) + .speed(0.01)); + }); + } + ColliderShape::Cylinder { half_height, radius } => { + ui.horizontal(|ui| { + ui.label("Half Height:"); + ui.add(egui::DragValue::new(half_height) + .speed(0.01)); + }); + ui.horizontal(|ui| { + ui.label("Radius:"); + ui.add(egui::DragValue::new(radius) + .speed(0.01)); + }); + } + ColliderShape::Cone { half_height, radius } => { + ui.horizontal(|ui| { + ui.label("Half Height:"); + ui.add(egui::DragValue::new(half_height) + .speed(0.01)); + }); + ui.horizontal(|ui| { + ui.label("Radius:"); + ui.add(egui::DragValue::new(radius) + .speed(0.01)); + }); + } + } + + ui.add_space(8.0); + + ui.separator(); + ui.label("Physical Properties:"); + + ui.horizontal(|ui| { + ui.label("Density:"); + ui.add(egui::DragValue::new(&mut self.density) + .speed(0.01)); + }); + + ui.horizontal(|ui| { + ui.label("Friction:"); + ui.add(egui::Slider::new(&mut self.friction, 0.0..=2.0)); + }); + + ui.horizontal(|ui| { + ui.label("Restitution:"); + ui.add(egui::Slider::new(&mut self.restitution, 0.0..=1.0)); + }); + + ui.checkbox(&mut self.is_sensor, "Is Sensor (No physical response)"); + + ui.add_space(8.0); + + ui.separator(); + ui.label("Local Offset:"); + + ui.label("Translation:"); + ui.horizontal(|ui| { + ui.label("X:"); + ui.add(egui::DragValue::new(&mut self.translation[0]).speed(0.01)); + ui.label("Y:"); + ui.add(egui::DragValue::new(&mut self.translation[1]).speed(0.01)); + ui.label("Z:"); + ui.add(egui::DragValue::new(&mut self.translation[2]).speed(0.01)); + }); + + ui.label("Rotation (degrees):"); + ui.horizontal(|ui| { + ui.label("X:"); + let mut deg_x = self.rotation[0].to_degrees(); + if ui.add(egui::DragValue::new(&mut deg_x).speed(1.0)).changed() { + self.rotation[0] = deg_x.to_radians(); + } + ui.label("Y:"); + let mut deg_y = self.rotation[1].to_degrees(); + if ui.add(egui::DragValue::new(&mut deg_y).speed(1.0)).changed() { + self.rotation[1] = deg_y.to_radians(); + } + ui.label("Z:"); + let mut deg_z = self.rotation[2].to_degrees(); + if ui.add(egui::DragValue::new(&mut deg_z).speed(1.0)).changed() { + self.rotation[2] = deg_z.to_radians(); + } + }); + }); + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ColliderShapeType { Box, @@ -3,12 +3,12 @@ pub mod character_collision; -use rapier3d::control::{CharacterCollision, KinematicCharacterController}; +use rapier3d::control::{CharacterAutostep, CharacterCollision, CharacterLength, KinematicCharacterController}; use serde::{Deserialize, Serialize}; use crate::states::Label; use std::any::Any; use std::sync::Arc; -use egui::Ui; +use egui::{ComboBox, DragValue, Ui}; use hecs::{Entity, World}; use crate::physics::PhysicsState; use crate::scripting::jni::utils::ToJObject; @@ -18,6 +18,7 @@ use crate::types::{IndexNative, NQuaternion, NVector3}; use jni::objects::{JObject, JValue}; use jni::JNIEnv; use rapier3d::dynamics::RigidBodyType; +use rapier3d::na::{UnitVector3, Vector3}; use rapier3d::math::Rotation; use rapier3d::prelude::QueryFilter; use dropbear_engine::animation::AnimationComponent; @@ -50,13 +51,6 @@ impl Component for KCC { } } - async fn first_time(_: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> - where - Self: Sized - { - Ok((Self::default(), )) - } - fn init<'a>( ser: &'a Self::SerializedForm, _: Arc<SharedGraphicsContext>, @@ -64,7 +58,7 @@ impl Component for KCC { Box::pin(async move { Ok((ser.clone(), )) }) } - fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} + fn update_component(&mut self, _world: &World, _physics: &mut PhysicsState, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} fn save(&self, _world: &World, _entity: Entity) -> Box<dyn SerializedComponent> { Box::new(self.clone()) @@ -72,9 +66,125 @@ impl Component for KCC { } impl InspectableComponent for KCC { - fn inspect(&mut self, ui: &mut Ui) { + fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { egui::CollapsingHeader::new("Kinematic Character Controller").default_open(true).show(ui, |ui| { - ui.label("Not implemented yet!") + fn edit_character_length(ui: &mut Ui, id_salt: impl std::hash::Hash, value: &mut CharacterLength, text: &str) { + ui.horizontal(|ui| { + ui.label(text); + + let (mut kind, mut v) = match *value { + CharacterLength::Absolute(x) => (0, x), + CharacterLength::Relative(x) => (1, x), + }; + + ComboBox::from_id_salt(id_salt) + .selected_text(match kind { + 0 => "Absolute", + _ => "Relative", + }) + .show_ui(ui, |ui| { + ui.selectable_value(&mut kind, 0, "Absolute"); + ui.selectable_value(&mut kind, 1, "Relative"); + }); + + ui.add(DragValue::new(&mut v).speed(0.01)); + + *value = if kind == 0 { + CharacterLength::Absolute(v) + } else { + CharacterLength::Relative(v) + }; + }); + } + + ui.vertical(|ui| { + ui.label("Up Vector:"); + let up = self.controller.up; + let mut up_v = Vector3::new(up.x, up.y, up.z); + + ui.horizontal(|ui| { + ui.label("X:"); + ui.add(DragValue::new(&mut up_v.x).speed(0.01)); + ui.label("Y:"); + ui.add(DragValue::new(&mut up_v.y).speed(0.01)); + ui.label("Z:"); + ui.add(DragValue::new(&mut up_v.z).speed(0.01)); + }); + + if up_v.norm_squared() > 0.0 { + self.controller.up = UnitVector3::new_normalize(up_v).into(); + } + + ui.add_space(8.0); + + edit_character_length(ui, "kcc_offset", &mut self.controller.offset, "Offset:"); + + ui.add_space(8.0); + ui.separator(); + + ui.checkbox(&mut self.controller.slide, "Slide against floor?"); + + ui.label("Slope Angles (degrees):"); + ui.horizontal(|ui| { + ui.label("Max climb:"); + let mut deg = self.controller.max_slope_climb_angle.to_degrees(); + if ui.add(DragValue::new(&mut deg).speed(1.0)).changed() { + self.controller.max_slope_climb_angle = deg.to_radians(); + } + }); + ui.horizontal(|ui| { + ui.label("Min slide:"); + let mut deg = self.controller.min_slope_slide_angle.to_degrees(); + if ui.add(DragValue::new(&mut deg).speed(1.0)).changed() { + self.controller.min_slope_slide_angle = deg.to_radians(); + } + }); + + ui.add_space(8.0); + + ui.horizontal(|ui| { + ui.label("Normal nudge:"); + ui.add( + egui::Slider::new(&mut self.controller.normal_nudge_factor, 0.001..=0.5) + .logarithmic(true) + .smallest_positive(0.001) + .largest_finite(0.5) + .suffix(" m") + ); + }); + + ui.add_space(8.0); + ui.separator(); + + let mut enable_snap = self.controller.snap_to_ground.is_some(); + ui.checkbox(&mut enable_snap, "Snap to ground"); + if enable_snap && self.controller.snap_to_ground.is_none() { + self.controller.snap_to_ground = Some(CharacterLength::Absolute(0.2)); + } else if !enable_snap { + self.controller.snap_to_ground = None; + } + + if let Some(ref mut snap) = self.controller.snap_to_ground { + edit_character_length(ui, "kcc_snap_to_ground", snap, "Snap distance:"); + } + + ui.add_space(8.0); + ui.separator(); + + let mut enable_autostep = self.controller.autostep.is_some(); + ui.checkbox(&mut enable_autostep, "Enable autostep"); + if enable_autostep && self.controller.autostep.is_none() { + self.controller.autostep = Some(CharacterAutostep::default()); + } else if !enable_autostep { + self.controller.autostep = None; + } + + if let Some(step) = &mut self.controller.autostep { + edit_character_length(ui, "kcc_autostep_max_height", &mut step.max_height, "Max height:"); + edit_character_length(ui, "kcc_autostep_min_width", &mut step.min_width, "Min width:"); + ui.checkbox(&mut step.include_dynamic_bodies, "Include dynamic bodies"); + } + }); }); } } @@ -8,12 +8,12 @@ use std::any::Any; use std::sync::Arc; use ::jni::objects::{JObject, JValue}; use ::jni::JNIEnv; -use egui::{CollapsingHeader, Ui}; +use egui::{CollapsingHeader, ComboBox, DragValue, Ui}; use hecs::{Entity, World}; use rapier3d::prelude::RigidBodyType; use serde::{Deserialize, Serialize}; use dropbear_engine::graphics::SharedGraphicsContext; -use crate::component::{Component, ComponentDescriptor, SerializedComponent}; +use crate::component::{Component, ComponentDescriptor, InspectableComponent, SerializedComponent}; use crate::types::{IndexNative, NCollider, RigidBodyContext, NVector3}; use crate::physics::PhysicsState; use crate::ptr::{PhysicsStatePtr, WorldPtr}; @@ -202,13 +202,6 @@ impl Component for RigidBody { } } - async fn first_time(_graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> - where - Self: Sized - { - Ok((Self::default(), )) - } - fn init<'a>( ser: &'a Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, @@ -216,17 +209,98 @@ impl Component for RigidBody { Box::pin(async move { Ok((ser.clone(), )) }) } - fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} + fn update_component(&mut self, _world: &World, _physics: &mut PhysicsState, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} fn save(&self, _world: &World, _entity: Entity) -> Box<dyn SerializedComponent> { Box::new(self.clone()) } } -impl crate::component::InspectableComponent for RigidBody { - fn inspect(&mut self, ui: &mut Ui) { +impl InspectableComponent for RigidBody { + fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { CollapsingHeader::new("RigidBody").default_open(true).show(ui, |ui| { - ui.label("Not implemented yet!"); + ui.vertical(|ui| { + let mut selected = self.mode; + ComboBox::from_id_salt("rb") + .selected_text(format!("{:?}", self.mode)) + .show_ui(ui, |ui| { + ui.selectable_value(&mut selected, RigidBodyMode::Dynamic, "Dynamic"); + ui.selectable_value(&mut selected, RigidBodyMode::Fixed, "Fixed"); + ui.selectable_value(&mut selected, RigidBodyMode::KinematicPosition, "Kinematic Position"); + ui.selectable_value(&mut selected, RigidBodyMode::KinematicVelocity, "Kinematic Velocity"); + }); + + if selected != self.mode { + self.mode = selected; + } + + ui.add_space(8.0); + + ui.horizontal(|ui| { + ui.label("Gravity Scale:"); + ui.add(DragValue::new(&mut self.gravity_scale) + .speed(0.1) + .range(0.0..=10.0)); + }); + + ui.checkbox(&mut self.can_sleep, "Can Sleep"); + ui.checkbox(&mut self.sleeping, "Initially sleeping?"); + ui.checkbox(&mut self.ccd_enabled, "CCD Enabled"); + + ui.add_space(8.0); + + ui.label("Linear Velocity:"); + ui.horizontal(|ui| { + ui.label("X:"); + ui.add(DragValue::new(&mut self.linvel[0]).speed(0.1)); + ui.label("Y:"); + ui.add(DragValue::new(&mut self.linvel[1]).speed(0.1)); + ui.label("Z:"); + ui.add(DragValue::new(&mut self.linvel[2]).speed(0.1)); + }); + + ui.label("Angular Velocity:"); + ui.horizontal(|ui| { + ui.label("X:"); + ui.add(DragValue::new(&mut self.angvel[0]).speed(0.1)); + ui.label("Y:"); + ui.add(DragValue::new(&mut self.angvel[1]).speed(0.1)); + ui.label("Z:"); + ui.add(DragValue::new(&mut self.angvel[2]).speed(0.1)); + }); + + ui.add_space(8.0); + + ui.horizontal(|ui| { + ui.label("Linear Damping:"); + ui.add(DragValue::new(&mut self.linear_damping) + .speed(0.01) + .range(0.0..=10.0)); + }); + + ui.horizontal(|ui| { + ui.label("Angular Damping:"); + ui.add(DragValue::new(&mut self.angular_damping) + .speed(0.01) + .range(0.0..=10.0)); + }); + + ui.add_space(8.0); + + ui.label("Lock Translation:"); + ui.horizontal(|ui| { + ui.checkbox(&mut self.lock_translation.x, "X"); + ui.checkbox(&mut self.lock_translation.y, "Y"); + ui.checkbox(&mut self.lock_translation.z, "Z"); + }); + + ui.label("Lock Rotation:"); + ui.horizontal(|ui| { + ui.checkbox(&mut self.lock_rotation.x, "X"); + ui.checkbox(&mut self.lock_rotation.y, "Y"); + ui.checkbox(&mut self.lock_rotation.z, "Z"); + }); + }); }); } } @@ -37,13 +37,6 @@ impl Component for CustomProperties { } } - async fn first_time(_graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> - where - Self: Sized - { - Ok((Self::new(), )) - } - fn init<'a>( ser: &'a Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, @@ -51,7 +44,7 @@ impl Component for CustomProperties { Box::pin(async move { Ok((ser.clone(), )) }) } - fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} + fn update_component(&mut self, _world: &World, _physics: &mut crate::physics::PhysicsState, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} fn save(&self, _world: &World, _entity: Entity) -> Box<dyn SerializedComponent> { Box::new(self.clone()) @@ -59,7 +52,7 @@ impl Component for CustomProperties { } impl InspectableComponent for CustomProperties { - fn inspect(&mut self, ui: &mut Ui) { + fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { CollapsingHeader::new("Custom Properties").default_open(true).show(ui, |ui| { ui.vertical(|ui| { Grid::new("properties").striped(true).show(ui, |ui| { @@ -174,13 +174,6 @@ impl Component for Script { } } - async fn first_time(_graphics: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> - where - Self: Sized - { - Ok((Self::default(), )) - } - fn init<'a>( ser: &'a Self::SerializedForm, _graphics: Arc<SharedGraphicsContext>, @@ -188,7 +181,7 @@ impl Component for Script { Box::pin(async move { Ok((ser.clone(), )) }) } - fn update_component(&mut self, _world: &World, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} + fn update_component(&mut self, _world: &World, _physics: &mut crate::physics::PhysicsState, _entity: Entity, _dt: f32, _graphics: Arc<SharedGraphicsContext>) {} fn save(&self, _world: &World, _entity: Entity) -> Box<dyn SerializedComponent> { Box::new(self.clone()) @@ -196,30 +189,32 @@ impl Component for Script { } impl InspectableComponent for Script { - fn inspect(&mut self, ui: &mut Ui) { - CollapsingHeader::new("Logic") - .default_open(true) - .show(ui, |ui| { - let mut local_del: Option<usize> = None; - for (i, tag) in self.tags.iter_mut().enumerate() { - let current_width = ui.available_width(); - ui.horizontal(|ui| { - ui.add_sized( - [current_width * 70.0 / 100.0, 20.0], - TextEdit::singleline(tag), - ); - if ui.button("🗑️").clicked() { - local_del = Some(i); - } - }); - } - if let Some(i) = local_del { - self.tags.remove(i); - } - if ui.button("➕ Add").clicked() { - self.tags.push(String::new()) - } - }); + fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { + CollapsingHeader::new("Scripting").default_open(true).show(ui, |ui| { + CollapsingHeader::new("Tags") + .default_open(true) + .show(ui, |ui| { + let mut local_del: Option<usize> = None; + for (i, tag) in self.tags.iter_mut().enumerate() { + let current_width = ui.available_width(); + ui.horizontal(|ui| { + ui.add_sized( + [current_width * 70.0 / 100.0, 20.0], + TextEdit::singleline(tag), + ); + if ui.button("🗑️").clicked() { + local_del = Some(i); + } + }); + } + if let Some(i) = local_del { + self.tags.remove(i); + } + if ui.button("➕ Add").clicked() { + self.tags.push(String::new()) + } + }); + }); } } @@ -30,10 +30,6 @@ impl Component for EntityTransform { } } - async fn first_time(_: Arc<SharedGraphicsContext>) -> anyhow::Result<Self::RequiredComponentTypes> { - Ok((Self::default(), )) - } - fn init<'a>( ser: &'a Self::SerializedForm, _: Arc<SharedGraphicsContext>, @@ -41,7 +37,7 @@ impl Component for EntityTransform { Box::pin(async move { Ok((ser.clone(), )) }) } - fn update_component(&mut self, _: &World, _: Entity, _: f32, _: Arc<SharedGraphicsContext>) {} + fn update_component(&mut self, _: &World, _physics: &mut crate::physics::PhysicsState, _: Entity, _: f32, _: Arc<SharedGraphicsContext>) {} fn save(&self, _: &World, _: Entity) -> Box<dyn SerializedComponent> { Box::new(self.clone()) @@ -49,7 +45,7 @@ impl Component for EntityTransform { } impl InspectableComponent for EntityTransform { - fn inspect(&mut self, ui: &mut Ui) { + fn inspect(&mut self, ui: &mut Ui, _graphics: Arc<SharedGraphicsContext>) { CollapsingHeader::new("Entity Transform").show(ui, |ui| { CollapsingHeader::new("Local").show(ui, |ui| { self.local_mut().inspect(ui); @@ -283,7 +279,7 @@ fn propogate_transform( #[dropbear_macro::entity] entity: hecs::Entity, ) -> DropbearNativeResult<NTransform> { - if let Ok(mut et) = world.get::<&mut EntityTransform>(entity) { + if let Ok(et) = world.get::<&mut EntityTransform>(entity) { let result = et.propagate(world, entity); Ok(result.into()) } else { @@ -6,6 +6,7 @@ use eucalyptus_core::states::PROJECT; use hecs::Entity; use crate::editor::{ComponentNodeSelection, DraggedAsset, EditorTabViewer, FsEntry, StaticallyKept, TABS_GLOBAL}; +use eucalyptus_core::component::DRAGGED_ASSET_ID; impl<'a> EditorTabViewer<'a> { pub(crate) fn show_asset_viewer(&mut self, ui: &mut egui::Ui) { @@ -41,7 +42,10 @@ impl<'a> EditorTabViewer<'a> { if let Some(&node_id) = dragged.source.first() { if let Some(asset) = cfg.asset_node_assets.get(&node_id).cloned() { - cfg.dragged_asset = Some(asset); + cfg.dragged_asset = Some(asset.clone()); + ui.ctx().data_mut(|d| { + d.insert_temp(egui::Id::new(DRAGGED_ASSET_ID), Some(asset.path.clone())) + }); } } } @@ -22,6 +22,7 @@ use transform_gizmo_egui::{EnumSet, Gizmo, GizmoMode, GizmoOrientation}; pub struct EditorTabViewer<'a> { pub view: egui::TextureId, pub tex_size: Extent3d, + pub graphics: Arc<SharedGraphicsContext>, pub gizmo: &'a mut Gizmo, pub world: &'a mut World, pub selected_entity: &'a mut Option<Entity>, @@ -223,7 +224,6 @@ impl<'a> TabViewer for EditorTabViewer<'a> { self.build_console(ui); } EditorTab::Console => { - ui.heading("Console"); ui.separator(); ui.horizontal(|ui| { @@ -171,16 +171,9 @@ impl Keyboard for Editor { return; }; - let mut components = self + let components = self .component_registry .extract_all_components(self.world.as_ref(), *entity); - components.retain(|component| { - self.component_registry - .id_for_component(component.as_ref()) - .and_then(|id| self.component_registry.get_descriptor_by_numeric_id(id)) - .map(|desc| desc.fqtn != "dropbear_engine::entity::EntityTransform") - .unwrap_or(true) - }); let s_entity = SceneEntity { label: Label::new(label.as_str()), components, @@ -66,6 +66,7 @@ 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::PhysicsState; use eucalyptus_core::physics::collider::{ColliderShapeKey, WireframeGeometry}; use eucalyptus_core::physics::collider::shader::ColliderInstanceRaw; use eucalyptus_core::physics::collider::shader::ColliderWireframePipeline; @@ -77,6 +78,7 @@ use crate::editor::settings::project::ProjectSettingsWindow; pub struct Editor { pub scene_command: SceneCommand, pub world: Box<World>, + pub physics_state: PhysicsState, pub dock_state: DockState<EditorTab>, pub texture_id: Option<egui::TextureId>, pub size: Extent3d, @@ -226,6 +228,7 @@ impl Editor { // is_cursor_locked: false, window: None, world: Box::new(World::new()), + physics_state: PhysicsState::new(), show_new_project: false, project_name: String::new(), project_path: Arc::new(Mutex::new(None)), @@ -793,7 +796,7 @@ impl Editor { Ok(()) } - pub fn show_ui(&mut self, ctx: &Context) { + pub fn show_ui(&mut self, ctx: &Context, graphics: Arc<SharedGraphicsContext>) { if let Some(scene_name) = self.pending_scene_creation.take() { let result = self.create_new_scene(scene_name.as_str()); self.new_scene_name.clear(); @@ -1091,6 +1094,7 @@ impl Editor { ui, &mut EditorTabViewer { view, + graphics: graphics.clone(), gizmo: &mut self.gizmo, tex_size: self.size, world: &mut self.world, @@ -14,7 +14,7 @@ impl<'a> EditorTabViewer<'a> { ui.separator(); self.component_registry - .inspect_components(self.world, inspect_entity, ui); + .inspect_components(self.world, inspect_entity, ui, self.graphics.clone()); } } else if !local_scene_settings { ui.label("No entity selected, therefore no info to provide. Go on, what are you waiting for? Click an entity!"); @@ -122,7 +122,7 @@ impl Scene for Editor { } self.component_registry - .update_components(self.world.as_mut(), dt, graphics.clone()); + .update_components(self.world.as_mut(), &mut self.physics_state, dt, graphics.clone()); if !self.is_world_loaded.is_fully_loaded() { log::debug!("Scene is not fully loaded, initialising..."); @@ -784,7 +784,7 @@ impl Editor { self.texture_id = Some(*graphics.texture_id.clone()); self.window = Some(graphics.window.clone()); - self.show_ui(&graphics.get_egui_context()); + self.show_ui(&graphics.get_egui_context(), graphics.clone()); eucalyptus_core::logging::render(&graphics.get_egui_context()); } } @@ -14,159 +14,6 @@ impl InspectableComponent for Collider { _signal: &mut Signal, label: &mut String ) { - ui.vertical(|ui| { - self.entity = Label::new(label.clone()); - - ui.label("Shape:"); - let current_shape = self.shape_type_name(); - ComboBox::from_id_salt("collider_shape") - .selected_text(current_shape) - .show_ui(ui, |ui| { - if ui.selectable_label(current_shape == "Box", "Box").clicked() { - if current_shape != "Box" { - self.shape = ColliderShape::Box { half_extents: [0.5, 0.5, 0.5] }; - } - } - if ui.selectable_label(current_shape == "Sphere", "Sphere").clicked() { - if current_shape != "Sphere" { - self.shape = ColliderShape::Sphere { radius: 0.5 }; - } - } - if ui.selectable_label(current_shape == "Capsule", "Capsule").clicked() { - if current_shape != "Capsule" { - self.shape = ColliderShape::Capsule { half_height: 0.5, radius: 0.25 }; - } - } - if ui.selectable_label(current_shape == "Cylinder", "Cylinder").clicked() { - if current_shape != "Cylinder" { - self.shape = ColliderShape::Cylinder { half_height: 0.5, radius: 0.25 }; - } - } - if ui.selectable_label(current_shape == "Cone", "Cone").clicked() { - if current_shape != "Cone" { - self.shape = ColliderShape::Cone { half_height: 0.5, radius: 0.25 }; - } - } - }); - - ui.add_space(8.0); - - match &mut self.shape { - ColliderShape::Box { half_extents } => { - ui.label("Half Extents:"); - ui.horizontal(|ui| { - ui.label("X:"); - ui.add(egui::DragValue::new(&mut half_extents[0]) - .speed(0.01)); - ui.label("Y:"); - ui.add(egui::DragValue::new(&mut half_extents[1]) - .speed(0.01)); - ui.label("Z:"); - ui.add(egui::DragValue::new(&mut half_extents[2]) - .speed(0.01)); - }); - } - ColliderShape::Sphere { radius } => { - ui.horizontal(|ui| { - ui.label("Radius:"); - ui.add(egui::DragValue::new(radius) - .speed(0.01)); - }); - } - ColliderShape::Capsule { half_height, radius } => { - ui.horizontal(|ui| { - ui.label("Half Height:"); - ui.add(egui::DragValue::new(half_height) - .speed(0.01)); - }); - ui.horizontal(|ui| { - ui.label("Radius:"); - ui.add(egui::DragValue::new(radius) - .speed(0.01)); - }); - } - ColliderShape::Cylinder { half_height, radius } => { - ui.horizontal(|ui| { - ui.label("Half Height:"); - ui.add(egui::DragValue::new(half_height) - .speed(0.01)); - }); - ui.horizontal(|ui| { - ui.label("Radius:"); - ui.add(egui::DragValue::new(radius) - .speed(0.01)); - }); - } - ColliderShape::Cone { half_height, radius } => { - ui.horizontal(|ui| { - ui.label("Half Height:"); - ui.add(egui::DragValue::new(half_height) - .speed(0.01)); - }); - ui.horizontal(|ui| { - ui.label("Radius:"); - ui.add(egui::DragValue::new(radius) - .speed(0.01)); - }); - } - } - - ui.add_space(8.0); - - ui.separator(); - ui.label("Physical Properties:"); - - ui.horizontal(|ui| { - ui.label("Density:"); - ui.add(egui::DragValue::new(&mut self.density) - .speed(0.01)); - }); - - ui.horizontal(|ui| { - ui.label("Friction:"); - ui.add(egui::Slider::new(&mut self.friction, 0.0..=2.0)); - }); - - ui.horizontal(|ui| { - ui.label("Restitution:"); - ui.add(egui::Slider::new(&mut self.restitution, 0.0..=1.0)); - }); - - ui.checkbox(&mut self.is_sensor, "Is Sensor (No physical response)"); - - ui.add_space(8.0); - - ui.separator(); - ui.label("Local Offset:"); - - ui.label("Translation:"); - ui.horizontal(|ui| { - ui.label("X:"); - ui.add(egui::DragValue::new(&mut self.translation[0]).speed(0.01)); - ui.label("Y:"); - ui.add(egui::DragValue::new(&mut self.translation[1]).speed(0.01)); - ui.label("Z:"); - ui.add(egui::DragValue::new(&mut self.translation[2]).speed(0.01)); - }); - - ui.label("Rotation (degrees):"); - ui.horizontal(|ui| { - ui.label("X:"); - let mut deg_x = self.rotation[0].to_degrees(); - if ui.add(egui::DragValue::new(&mut deg_x).speed(1.0)).changed() { - self.rotation[0] = deg_x.to_radians(); - } - ui.label("Y:"); - let mut deg_y = self.rotation[1].to_degrees(); - if ui.add(egui::DragValue::new(&mut deg_y).speed(1.0)).changed() { - self.rotation[1] = deg_y.to_radians(); - } - ui.label("Z:"); - let mut deg_z = self.rotation[2].to_degrees(); - if ui.add(egui::DragValue::new(&mut deg_z).speed(1.0)).changed() { - self.rotation[2] = deg_z.to_radians(); - } - }); - }); + } } @@ -333,7 +333,7 @@ impl Scene for PlayMode { } self.component_registry - .update_components(self.world.as_mut(), dt, graphics.clone()); + .update_components(self.world.as_mut(), &mut self.physics_state, dt, graphics.clone()); @@ -179,99 +179,96 @@ typedef struct NChannelValuesFfi { typedef NChannelValuesFfi NChannelValues; -typedef void* InputStatePtr; - typedef struct IndexNative { uint32_t index; uint32_t generation; } IndexNative; +typedef struct IndexNativeArray { + IndexNative* values; + size_t length; + size_t capacity; +} IndexNativeArray; + +typedef struct CharacterCollisionArray { + uint64_t entity_id; + IndexNativeArray collisions; +} CharacterCollisionArray; + typedef struct NCollider { IndexNative index; uint64_t entity_id; uint32_t id; } NCollider; -typedef struct RayHit { +typedef struct NShapeCastHit { NCollider collider; double distance; -} RayHit; - -typedef void* SceneLoaderPtr; - -typedef struct RigidBodyContext { - IndexNative index; - uint64_t entity_id; -} RigidBodyContext; - -typedef void* CommandBufferPtr; + NVector3 witness1; + NVector3 witness2; + NVector3 normal1; + NVector3 normal2; + NShapeCastStatus status; +} NShapeCastHit; -typedef struct NVector4 { - double x; - double y; - double z; - double w; -} NVector4; +typedef void* PhysicsStatePtr; typedef struct NVector2 { double x; double y; } NVector2; -typedef struct i32Array { - int32_t* values; - size_t length; - size_t capacity; -} i32Array; - -typedef struct NModelVertex { +typedef struct NTransform { NVector3 position; - NVector3 normal; - NVector4 tangent; - NVector2 tex_coords0; - NVector2 tex_coords1; - NVector4 colour0; - i32Array joints0; - NVector4 weights0; -} NModelVertex; + NQuaternion rotation; + NVector3 scale; +} NTransform; -typedef struct NModelVertexArray { - NModelVertex* values; - size_t length; - size_t capacity; -} NModelVertexArray; +typedef struct NRange { + float start; + float end; +} NRange; -typedef struct NMesh { - const char* name; - int32_t num_elements; - int32_t material_index; - NModelVertexArray vertices; -} NMesh; +typedef void* InputStatePtr; -typedef struct NMeshArray { - NMesh* values; +typedef void* SceneLoaderPtr; + +typedef struct f64Array { + double* values; size_t length; size_t capacity; -} NMeshArray; +} 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 struct i32Array { + int32_t* values; + size_t length; + size_t capacity; +} i32Array; typedef struct NNodeTransform { NVector3 translation; @@ -292,105 +289,107 @@ typedef struct NNodeArray { size_t capacity; } NNodeArray; -typedef void* AssetRegistryPtr; - typedef void* GraphicsContextPtr; -typedef struct u64Array { - uint64_t* values; +typedef struct f64ArrayArray { + double* values; size_t length; size_t capacity; -} u64Array; - -typedef struct ConnectedGamepadIds { - u64Array ids; -} ConnectedGamepadIds; +} f64ArrayArray; -typedef void* WorldPtr; +typedef struct NSkin { + const char* name; + i32Array joints; + f64ArrayArray inverse_bind_matrices; + const int32_t* skeleton_root; +} NSkin; -typedef struct f64Array { - double* values; +typedef struct NSkinArray { + NSkin* values; size_t length; size_t capacity; -} f64Array; +} NSkinArray; -typedef struct NAnimationChannel { - int32_t target_node; - f64Array times; - NChannelValues values; - NAnimationInterpolation interpolation; -} NAnimationChannel; +typedef struct NAttenuation { + float constant; + float linear; + float quadratic; +} NAttenuation; -typedef struct NAnimationChannelArray { - NAnimationChannel* values; - size_t length; - size_t capacity; -} NAnimationChannelArray; +typedef void* AssetRegistryPtr; -typedef struct NAnimation { - const char* name; - NAnimationChannelArray channels; - float duration; -} NAnimation; +typedef struct NColour { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +} NColour; -typedef struct NAnimationArray { - NAnimation* values; +typedef struct NVector4 { + double x; + double y; + double z; + double w; +} NVector4; + +typedef struct NModelVertex { + NVector3 position; + NVector3 normal; + NVector4 tangent; + NVector2 tex_coords0; + NVector2 tex_coords1; + NVector4 colour0; + i32Array joints0; + NVector4 weights0; +} NModelVertex; + +typedef struct NModelVertexArray { + NModelVertex* values; size_t length; size_t capacity; -} NAnimationArray; +} NModelVertexArray; -typedef struct NColliderArray { - NCollider* values; +typedef struct NMesh { + const char* name; + int32_t num_elements; + int32_t material_index; + NModelVertexArray vertices; +} NMesh; + +typedef struct NMeshArray { + NMesh* values; size_t length; size_t capacity; -} NColliderArray; +} NMeshArray; -typedef struct IndexNativeArray { - IndexNative* values; +typedef struct u64Array { + uint64_t* values; size_t length; size_t capacity; -} IndexNativeArray; - -typedef struct CharacterCollisionArray { - uint64_t entity_id; - IndexNativeArray collisions; -} CharacterCollisionArray; - -typedef void* PhysicsStatePtr; +} u64Array; -typedef struct AxisLock { - bool x; - bool y; - bool z; -} AxisLock; +typedef struct ConnectedGamepadIds { + u64Array ids; +} ConnectedGamepadIds; -typedef struct NRange { - float start; - float end; -} NRange; +typedef void* CommandBufferPtr; -typedef struct NTransform { - NVector3 position; - NQuaternion rotation; - NVector3 scale; -} NTransform; +typedef struct NColliderArray { + NCollider* values; + size_t length; + size_t capacity; +} NColliderArray; -typedef struct NColour { - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; -} NColour; +typedef struct Progress { + size_t current; + size_t total; + const char* message; +} Progress; -typedef struct NShapeCastHit { +typedef struct RayHit { NCollider collider; double distance; - NVector3 witness1; - NVector3 witness2; - NVector3 normal1; - NVector3 normal2; - NShapeCastStatus status; -} NShapeCastHit; +} RayHit; typedef struct NMaterial { const char* name; @@ -416,17 +415,18 @@ typedef struct NMaterialArray { size_t capacity; } NMaterialArray; -typedef struct NAttenuation { - float constant; - float linear; - float quadratic; -} NAttenuation; +typedef struct RigidBodyContext { + IndexNative index; + uint64_t entity_id; +} RigidBodyContext; -typedef struct Progress { - size_t current; - size_t total; - const char* message; -} Progress; +typedef struct AxisLock { + bool x; + bool y; + bool z; +} AxisLock; + +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);