From c2b62a5f535f89a91f9a74c8e43fe1142d7e9a4d Mon Sep 17 00:00:00 2001 From: mitchellhansen Date: Sat, 20 Feb 2021 01:45:24 -0800 Subject: [PATCH] entity spawning. great success --- conf/entity_spawns.toml | 9 +- src/main.rs | 238 ++++++++++------------------------------ src/render/state.rs | 34 ++---- src/runtime/state.rs | 60 +++++++++- src/runtime/system.rs | 158 +++++++++++++++++++++++--- 5 files changed, 275 insertions(+), 224 deletions(-) diff --git a/conf/entity_spawns.toml b/conf/entity_spawns.toml index edb1615..4ddbb59 100644 --- a/conf/entity_spawns.toml +++ b/conf/entity_spawns.toml @@ -1,13 +1,10 @@ - [[entities]] -[entities.entity1] name = "terrain.1" -type = "Mesh" +type = "Terrain" mesh = "test-textured.obj" [[entities]] -[entities.entity2] name = "ball.1" -type = "" -mesh = "test-textured.obj" +type = "PhysicsEntity" +mesh = "ball.obj" diff --git a/src/main.rs b/src/main.rs index 49367ad..8f3d5d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,6 +57,8 @@ use std::collections::HashMap; use futures::FutureExt; use config::Config; use log::LevelFilter; +use crate::runtime::state::RuntimeState; +use winit_24::event_loop::EventLoopProxy; mod camera; mod components; @@ -108,12 +110,13 @@ fn main() { let logger = env_logger::builder().filter(Some("minimal_viable_game_engine"), LevelFilter::Info).init(); - let mut settings = Config::default(); - settings - // File::with_name(..) is shorthand for File::from(Path::new(..)) - .merge(File::with_name("conf/entity_spawns.toml")).unwrap(); - log::info!("{:#?}", settings); + + + // for i in settings.get("entities") { + // + // } + let mut world = World::default(); @@ -121,6 +124,11 @@ fn main() { .add_system(render::system::imgui_prepare_system()) .build(); + let mut load_schedule = Schedule::builder() + .add_system(runtime::system::runtime_load_system()) + .add_system(runtime::system::runtime_spawn_system()) + .build(); + let mut render_schedule = Schedule::builder() .add_system(render::system::render_test_system()) .build(); @@ -176,7 +184,6 @@ fn main() { // The renderer let mut renderer = render::state::RenderState::init(&window, &mut imgui_context); - preload_meshes("./resources"); entity_loading(&mut world, &mut renderer); resources.insert(renderer); @@ -199,48 +206,12 @@ fn main() { // And our event stack resources.insert(Vec::>::new()); - }; - - let event_loop_proxy = event_loop.create_proxy(); - - std::thread::spawn(move || { - let mut gilrs = Gilrs::new().unwrap(); - // Iterate over all connected gamepads - let mut gamepad: Option = None; - for (_id, gamepad_) in gilrs.gamepads() { - if gamepad_.name() == "PS4" { - gamepad = Some(gamepad_); - } - // println!( - // "{} is {:?} {:?}", - // gamepad_.name(), - // gamepad_.power_info(), - // gamepad_.id() - // ); - } - let mut active_gamepad = None; - loop { - while let Some(GilEvent { id, event, time }) = gilrs.next_event() { - //println!("{:?} New event from {}: {:?}", time, id, event); - active_gamepad = Some(id); - event_loop_proxy - .send_event(OwnedEventExtension::GamepadEvent { - gil_event: GilEvent { id, event, time }, - }) - .ok(); - } - - // // You can also use cached gamepad state - // if let Some(gamepad) = active_gamepad.map(|id| gilrs.gamepad(id)) { - // if gamepad.is_pressed(Button::South) { - // println!("Button South is pressed (XBox - A, PS - X)"); - // } - // } + // Our init and runtime data + resources.insert(RuntimeState::new()) + }; - std::thread::sleep(std::time::Duration::from_millis(50)); - } - }); + setup_gamepad(&event_loop); let mut elapsed_time: f32 = { // deltatime since last frame @@ -256,7 +227,11 @@ fn main() { event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Poll; match event { - event::Event::NewEvents(cause) => {} + event::Event::NewEvents(cause) => { + if cause == winit_24::event::StartCause::Init { + load_schedule.execute(&mut world, &mut resources); + } + } // This is the big boy section of the event loop // We : dispatch events and clear the queue, query the loops @@ -341,76 +316,51 @@ fn main() { }); } -pub fn preload_meshes(resources_path: &str) -> HashMap { +pub fn setup_gamepad(event_loop: &EventLoop) { - log::info!("Preloading meshes..."); - let mut output = HashMap::default(); + let event_loop_proxy = event_loop.create_proxy(); - let paths = fs::read_dir(resources_path).unwrap(); - for file in paths { - let filepath = file.unwrap().path().into_os_string(); - let filepath = filepath.to_str().unwrap(); - if filepath.ends_with(".obj") { - let mesh = load_obj(filepath).unwrap(); - output.insert(filepath.to_string(), mesh); + std::thread::spawn(move || { + let mut gilrs = Gilrs::new().unwrap(); + // Iterate over all connected gamepads + let mut gamepad: Option = None; + for (_id, gamepad_) in gilrs.gamepads() { + if gamepad_.name() == "PS4" { + gamepad = Some(gamepad_); + } + // println!( + // "{} is {:?} {:?}", + // gamepad_.name(), + // gamepad_.power_info(), + // gamepad_.id() + // ); } - } + let mut active_gamepad = None; - output - // I guess it's fine to have them loaded to the gpu - // But I also want to preserve the raw data -} + loop { + while let Some(GilEvent { id, event, time }) = gilrs.next_event() { + //println!("{:?} New event from {}: {:?}", time, id, event); + active_gamepad = Some(id); + event_loop_proxy + .send_event(OwnedEventExtension::GamepadEvent { + gil_event: GilEvent { id, event, time }, + }) + .ok(); + } -pub fn load_colliding_mesh_entity(world: &mut World, renderer: &mut render::state::RenderState, mesh_path: &str) { + // // You can also use cached gamepad state + // if let Some(gamepad) = active_gamepad.map(|id| gilrs.gamepad(id)) { + // if gamepad.is_pressed(Button::South) { + // println!("Button South is pressed (XBox - A, PS - X)"); + // } + // } - let mut static_floor_body = RigidBodyBuilder::new_static() - .position(Isometry3::new(Vector3::new(0.0, -8.0, 0.0), Vector::y())) - .build(); + std::thread::sleep(std::time::Duration::from_millis(50)); + } + }); +} - //let pair = import_mesh("./resources/terrain.obj"); - let raw_mesh = load_obj(mesh_path).unwrap(); - - let floor_collider = ColliderBuilder::trimesh( - raw_mesh.vertices - .iter() - .map(|v| v.position()) - .collect(), - raw_mesh.indices - ).build(); - - let plane_mesh = renderer.load_mesh_to_buffer( - mesh_path, - Some(wgpu::Color { - r: 1.0, - g: 0.7, - b: 0.3, - a: 1.0, - }), - ).unwrap(); - - let plane_entity: Entity = world.push(( - Position { - x: 0.0, - y: -8.0, - z: 0.0, - rot: Euler { - x: Deg(0.0), - y: Deg(0.0), - z: Deg(0.0), - }, - }, - plane_mesh, - Physics { - rigid_body: static_floor_body, - rigid_body_handle: None, - }, - Collider { - collider: floor_collider, - collider_handle: None, - }, - )); -} pub fn entity_loading(world: &mut World, renderer: &mut render::state::RenderState) { let monkey_mesh = @@ -420,7 +370,7 @@ pub fn entity_loading(world: &mut World, renderer: &mut render::state::RenderSta let ball_mesh = renderer.load_mesh_to_buffer("./resources/ball.obj", Some(wgpu::Color::BLUE)).unwrap(); - load_colliding_mesh_entity(world, renderer, "./resources/test-textured.obj"); + //load_colliding_mesh_entity(world, renderer, "./resources/test-textured.obj"); let camera_ent: Entity = world.push(( Camera { @@ -435,8 +385,6 @@ pub fn entity_loading(world: &mut World, renderer: &mut render::state::RenderSta CameraController::new(3.0, 1.0), )); - - let light_entity: Entity = world.push(( Position { x: 0.0, @@ -462,71 +410,5 @@ pub fn entity_loading(world: &mut World, renderer: &mut render::state::RenderSta renderer.create_light(), )); - let offset = cgmath::vec3(2.0, 2.0, 2.0); - let transform = Decomposed { - disp: offset.clone(), - rot: Quaternion::from_axis_angle(offset.normalize(), Deg(50.0)), - scale: 1.0, - }; - - let monkey_entity: Entity = world.push(( - Position { - x: 1.0, - y: 5.0, - z: 2.0, - rot: Euler { - x: Deg(90.0), - y: Deg(45.0), - z: Deg(15.0), - }, //mx: cgmath::Matrix4::from(transform), - }, - monkey_mesh, - )); - - let mut dynamic_ball_body = RigidBodyBuilder::new_dynamic() - .can_sleep(false) - .mass(1.0) - //.position(Isometry3::new(Vector3::new(0.0, 10.0, 0.0), Vector::y())) - .translation(0.0, 35.0, 0.0) - .build(); - - let raw_mesh = load_obj("./resources/ball.obj").unwrap(); - - let ball_collider = ColliderBuilder::trimesh( - raw_mesh.vertices - .iter() - .map(|v| { - let position = v.position(); - Point::::new(position.x, position.y, position.z) - }) - .collect(), - raw_mesh.indices, - ) - .build(); - - - - - let ball_mesh: Entity = world.push(( - Position { - x: 0.0, - y: 20.0, - z: 0.0, - rot: Euler { - x: Deg(25.0), - y: Deg(45.0), - z: Deg(15.0), - }, - }, - ball_mesh, - Physics { - rigid_body: dynamic_ball_body, - rigid_body_handle: None, - }, - Collider { - collider: ball_collider, - collider_handle: None, - }, - )); } diff --git a/src/render/state.rs b/src/render/state.rs index eedd769..bccd90f 100644 --- a/src/render/state.rs +++ b/src/render/state.rs @@ -108,11 +108,10 @@ impl RenderState { } } - /// Create a bare vertex & indices buffer - /// TODO I really should remove this / consolidate it + /// Create a buffer for a mesh fn create_buffer( device: &wgpu::Device, - raw_mesh: RawMesh, + raw_mesh: &RawMesh, ) -> (Arc, Arc) { let vertex_buf = Arc::new( device.create_buffer_init(&wgpu::util::BufferInitDescriptor { @@ -122,16 +121,6 @@ impl RenderState { }), ); - //println!("{:x?}", raw_mesh.indices); - - - // let mut hack = Vec::::new(); - // for ind_chunk in raw_mesh.indices { - // hack.push(ind_chunk[0]); - // hack.push(ind_chunk[1]); - // hack.push(ind_chunk[2]); - // } - let index_buf = Arc::new( device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("index-buffer"), @@ -143,17 +132,12 @@ impl RenderState { (vertex_buf, index_buf) } - /// Take a meshes - pub fn upload_mesh_to_buffer(mesh: RawMesh) { - - } - - pub fn load_mesh_to_buffer(&self, filepath: &str, color: Option) -> Result { + /// Take a meshes raw representation and upload it to a GPU buffer + pub fn upload_mesh_to_buffer(&mut self, mesh: &RawMesh, color: Option) -> Result { - let raw_mesh = load_obj(filepath)?; - let index_count = raw_mesh.indices.len() * 3; // TODO bad bad bad bad! + let index_count = mesh.indices.len() * 3; // TODO bad bad bad bad! - let (vertex_buf, index_buf) = RenderState::create_buffer(&self.device, raw_mesh); + let (vertex_buf, index_buf) = RenderState::create_buffer(&self.device, mesh); let uniform_size = mem::size_of::() as wgpu::BufferAddress; @@ -189,6 +173,12 @@ impl RenderState { }) } + /// explicitly load from file, and upload to gpu the mesh + pub fn load_mesh_to_buffer(&mut self, filepath: &str, color: Option) -> Result { + let raw_mesh = load_obj(filepath)?; + self.upload_mesh_to_buffer(&raw_mesh, color) + } + /// When creating a light we have to give it a target view to render to /// This is major danger scary since we have a 10 light limit, and only /// 2 views created at this moment, need to smarten this up diff --git a/src/runtime/state.rs b/src/runtime/state.rs index bd52b97..3437c00 100644 --- a/src/runtime/state.rs +++ b/src/runtime/state.rs @@ -1,6 +1,11 @@ +use std::collections::HashMap; +use std::fs; +use std::path::PathBuf; use std::time::Instant; use cgmath::{Euler, Quaternion}; +use config::Config; +use config::File; use legion::world::SubWorld; use legion::IntoQuery; use legion::*; @@ -8,15 +13,66 @@ use nalgebra::Quaternion as naQuaternion; use rapier3d::dynamics::{IntegrationParameters, JointSet, RigidBodySet}; use rapier3d::geometry::{BroadPhase, ColliderSet, NarrowPhase}; use rapier3d::pipeline::PhysicsPipeline; + use crate::camera::{Camera, CameraController}; use crate::components::{Collider, LoopState, Mesh, Physics, Position}; +use crate::geometry::{load_obj, RawMesh}; +pub struct EntityMeta { + pub name: String, + pub ent_type: String, + pub mesh: String, +} pub struct RuntimeState { - + config_db: Config, + mesh_cache: HashMap, } impl RuntimeState { - pub fn build() { + pub fn new() -> RuntimeState { + let mut settings = Config::default(); + settings + // File::with_name(..) is shorthand for File::from(Path::new(..)) + .merge(File::with_name("conf/entity_spawns.toml")) + .unwrap(); + + RuntimeState { + config_db: settings, + mesh_cache: Default::default(), + } + } + + pub fn get_mesh(&mut self, mesh: &str) -> Option<&RawMesh> { + self.mesh_cache.get(mesh) + } + + pub fn get_configured_entities(&mut self) -> Vec { + let mut out = Vec::new(); + for entity in self.config_db.get_array("entities").unwrap() { + let table = entity.into_table().unwrap(); + out.push(EntityMeta { + name: table.get("name").unwrap().kind.to_string(), + ent_type: table.get("type").unwrap().kind.to_string(), + mesh: table.get("mesh").unwrap().kind.to_string(), + }); + } + out + } + + pub fn preload_meshes(&mut self, resources_path: PathBuf) { + log::info!("Preloading meshes..."); + + let paths = fs::read_dir(resources_path).unwrap(); + for file in paths { + let file = file.unwrap(); + let filepath = file.path().clone(); + let filename = String::from(file.file_name().to_str().unwrap()); + + if filename.ends_with(".obj") { + let mesh = load_obj(filepath.to_str().unwrap()).unwrap(); + self.mesh_cache.insert(filename, mesh); + } + } } } diff --git a/src/runtime/system.rs b/src/runtime/system.rs index 5aae9a2..93d1b95 100644 --- a/src/runtime/system.rs +++ b/src/runtime/system.rs @@ -1,33 +1,159 @@ +use std::path::PathBuf; use std::time::Instant; -use cgmath::{Euler, Quaternion}; -use legion::world::SubWorld; -use legion::IntoQuery; +use cgmath::{Euler, Quaternion, Deg}; +use imgui::FontSource; use legion::*; +use legion::IntoQuery; +use legion::systems::CommandBuffer; +use legion::world::SubWorld; use nalgebra::Quaternion as naQuaternion; -use rapier3d::dynamics::{IntegrationParameters, JointSet, RigidBodySet}; -use rapier3d::geometry::{BroadPhase, ColliderSet, NarrowPhase}; -use rapier3d::pipeline::{PhysicsPipeline, ChannelEventCollector}; +use rapier3d::dynamics::{IntegrationParameters, JointSet, RigidBodyBuilder, RigidBodySet}; +use rapier3d::geometry::{BroadPhase, ColliderBuilder, ColliderSet, NarrowPhase}; +use rapier3d::na::{Isometry3, Vector, Vector3}; +use rapier3d::pipeline::{ChannelEventCollector, PhysicsPipeline}; use crate::camera::{Camera, CameraController}; use crate::components::{Collider, LoopState, Mesh, Physics, Position}; -use imgui::FontSource; +use crate::geometry::RawMesh; use crate::physics::state::PhysicsState; - +use crate::render::state::RenderState; +use crate::runtime::state::RuntimeState; #[system] -#[write_component(Collider)] -#[write_component(Physics)] #[write_component(Mesh)] pub fn runtime_load( world: &mut SubWorld, - #[resource] physics_state: &mut PhysicsState, - #[resource] physics_pipeline: &mut PhysicsPipeline, + #[resource] runtime_state: &mut RuntimeState, +) { + runtime_state.preload_meshes(PathBuf::from("./resources")); +} + +#[system] +#[write_component(Mesh)] +pub fn runtime_spawn( + cmd: &mut CommandBuffer, + world: &mut SubWorld, + #[resource] runtime_state: &mut RuntimeState, + #[resource] renderer: &mut RenderState, ) { - // Make sure all the entities we care about are added to the system - let mut query = <(&mut Collider, &mut Physics, &mut Mesh)>::query(); - for (collider, physics, mesh) in query.iter_mut(world) { + + for entity in runtime_state.get_configured_entities(){ + match entity.ent_type.as_ref() { + "Terrain" => { + let raw_mesh = match runtime_state.get_mesh(entity.mesh.as_str()) { + None => { + log::warn!("Skipping entity with invalid mesh file {:?} ", entity.mesh); + continue; + } + Some(mesh) => mesh + }; + + let mut static_body = RigidBodyBuilder::new_static() + .position(Isometry3::new(Vector3::new(0.0, -8.0, 0.0), Vector::y())) + .build(); + + let mesh_collider = ColliderBuilder::trimesh( + raw_mesh.vertices + .iter() + .map(|v| v.position()) + .collect(), + raw_mesh.indices.clone(), + ).build(); + + let gpu_mesh_buffer = renderer.upload_mesh_to_buffer( + raw_mesh, + Some(wgpu::Color { + r: 1.0, + g: 0.7, + b: 0.3, + a: 1.0, + }) + ).unwrap(); + + + let entity: Entity = cmd.push(( + Position { + x: 0.0, + y: -8.0, + z: 0.0, + rot: Euler { + x: Deg(0.0), + y: Deg(0.0), + z: Deg(0.0), + }, + }, + gpu_mesh_buffer, + Physics { + rigid_body: static_body, + rigid_body_handle: None, + }, + Collider { + collider: mesh_collider, + collider_handle: None, + }, + )); + }, + "PhysicsEntity" => { + + let raw_mesh = match runtime_state.get_mesh(entity.mesh.as_str()) { + None => { + log::warn!("Skipping entity with invalid mesh file {:?} ", entity.mesh); + continue; + } + Some(mesh) => mesh + }; + + let mut dynamic_body = RigidBodyBuilder::new_dynamic() + .can_sleep(false) + .mass(1.0) + .translation(0.0, 35.0, 0.0) + .build(); + + let collider = ColliderBuilder::trimesh( + raw_mesh.vertices + .iter() + .map(|v| v.position()) + .collect(), + raw_mesh.indices.clone(), + ).build(); + + let gpu_mesh_buffer = renderer.upload_mesh_to_buffer( + raw_mesh, + Some(wgpu::Color { + r: 1.0, + g: 0.7, + b: 0.3, + a: 1.0, + }) + ).unwrap(); + + let entity: Entity = cmd.push(( + Position { + x: 0.0, + y: 20.0, + z: 0.0, + rot: Euler { + x: Deg(25.0), + y: Deg(45.0), + z: Deg(15.0), + }, + }, + gpu_mesh_buffer, + Physics { + rigid_body: dynamic_body, + rigid_body_handle: None, + }, + Collider { + collider: collider, + collider_handle: None, + }, + )); + } + _ => {}, + } + } -} \ No newline at end of file +}