entity spawning. great success

master
mitchellhansen 4 years ago
parent a1305bed2d
commit c2b62a5f53

@ -1,13 +1,10 @@
[[entities]] [[entities]]
[entities.entity1]
name = "terrain.1" name = "terrain.1"
type = "Mesh" type = "Terrain"
mesh = "test-textured.obj" mesh = "test-textured.obj"
[[entities]] [[entities]]
[entities.entity2]
name = "ball.1" name = "ball.1"
type = "" type = "PhysicsEntity"
mesh = "test-textured.obj" mesh = "ball.obj"

@ -57,6 +57,8 @@ use std::collections::HashMap;
use futures::FutureExt; use futures::FutureExt;
use config::Config; use config::Config;
use log::LevelFilter; use log::LevelFilter;
use crate::runtime::state::RuntimeState;
use winit_24::event_loop::EventLoopProxy;
mod camera; mod camera;
mod components; mod components;
@ -108,12 +110,13 @@ fn main() {
let logger = env_logger::builder().filter(Some("minimal_viable_game_engine"), LevelFilter::Info).init(); 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(); let mut world = World::default();
@ -121,6 +124,11 @@ fn main() {
.add_system(render::system::imgui_prepare_system()) .add_system(render::system::imgui_prepare_system())
.build(); .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() let mut render_schedule = Schedule::builder()
.add_system(render::system::render_test_system()) .add_system(render::system::render_test_system())
.build(); .build();
@ -176,7 +184,6 @@ fn main() {
// The renderer // The renderer
let mut renderer = render::state::RenderState::init(&window, &mut imgui_context); let mut renderer = render::state::RenderState::init(&window, &mut imgui_context);
preload_meshes("./resources");
entity_loading(&mut world, &mut renderer); entity_loading(&mut world, &mut renderer);
resources.insert(renderer); resources.insert(renderer);
@ -199,48 +206,12 @@ fn main() {
// And our event stack // And our event stack
resources.insert(Vec::<OwnedEvent<OwnedEventExtension>>::new()); resources.insert(Vec::<OwnedEvent<OwnedEventExtension>>::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<Gamepad> = 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 { // Our init and runtime data
while let Some(GilEvent { id, event, time }) = gilrs.next_event() { resources.insert(RuntimeState::new())
//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)");
// }
// }
std::thread::sleep(std::time::Duration::from_millis(50)); setup_gamepad(&event_loop);
}
});
let mut elapsed_time: f32 = { let mut elapsed_time: f32 = {
// deltatime since last frame // deltatime since last frame
@ -256,7 +227,11 @@ fn main() {
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll; *control_flow = ControlFlow::Poll;
match event { 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 // This is the big boy section of the event loop
// We : dispatch events and clear the queue, query the loops // We : dispatch events and clear the queue, query the loops
@ -341,76 +316,51 @@ fn main() {
}); });
} }
pub fn preload_meshes(resources_path: &str) -> HashMap<String, RawMesh> { pub fn setup_gamepad(event_loop: &EventLoop<OwnedEventExtension>) {
log::info!("Preloading meshes..."); let event_loop_proxy = event_loop.create_proxy();
let mut output = HashMap::default();
let paths = fs::read_dir(resources_path).unwrap(); std::thread::spawn(move || {
for file in paths { let mut gilrs = Gilrs::new().unwrap();
let filepath = file.unwrap().path().into_os_string(); // Iterate over all connected gamepads
let filepath = filepath.to_str().unwrap(); let mut gamepad: Option<Gamepad> = None;
if filepath.ends_with(".obj") { for (_id, gamepad_) in gilrs.gamepads() {
let mesh = load_obj(filepath).unwrap(); if gamepad_.name() == "PS4" {
output.insert(filepath.to_string(), mesh); gamepad = Some(gamepad_);
}
// println!(
// "{} is {:?} {:?}",
// gamepad_.name(),
// gamepad_.power_info(),
// gamepad_.id()
// );
} }
} let mut active_gamepad = None;
output loop {
// I guess it's fine to have them loaded to the gpu while let Some(GilEvent { id, event, time }) = gilrs.next_event() {
// But I also want to preserve the raw data //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() std::thread::sleep(std::time::Duration::from_millis(50));
.position(Isometry3::new(Vector3::new(0.0, -8.0, 0.0), Vector::y())) }
.build(); });
}
//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) { pub fn entity_loading(world: &mut World, renderer: &mut render::state::RenderState) {
let monkey_mesh = let monkey_mesh =
@ -420,7 +370,7 @@ pub fn entity_loading(world: &mut World, renderer: &mut render::state::RenderSta
let ball_mesh = let ball_mesh =
renderer.load_mesh_to_buffer("./resources/ball.obj", Some(wgpu::Color::BLUE)).unwrap(); 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(( let camera_ent: Entity = world.push((
Camera { Camera {
@ -435,8 +385,6 @@ pub fn entity_loading(world: &mut World, renderer: &mut render::state::RenderSta
CameraController::new(3.0, 1.0), CameraController::new(3.0, 1.0),
)); ));
let light_entity: Entity = world.push(( let light_entity: Entity = world.push((
Position { Position {
x: 0.0, x: 0.0,
@ -462,71 +410,5 @@ pub fn entity_loading(world: &mut World, renderer: &mut render::state::RenderSta
renderer.create_light(), 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::<f32>::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,
},
));
} }

@ -108,11 +108,10 @@ impl RenderState {
} }
} }
/// Create a bare vertex & indices buffer /// Create a buffer for a mesh
/// TODO I really should remove this / consolidate it
fn create_buffer( fn create_buffer(
device: &wgpu::Device, device: &wgpu::Device,
raw_mesh: RawMesh, raw_mesh: &RawMesh,
) -> (Arc<Buffer>, Arc<Buffer>) { ) -> (Arc<Buffer>, Arc<Buffer>) {
let vertex_buf = Arc::new( let vertex_buf = Arc::new(
device.create_buffer_init(&wgpu::util::BufferInitDescriptor { device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
@ -122,16 +121,6 @@ impl RenderState {
}), }),
); );
//println!("{:x?}", raw_mesh.indices);
// let mut hack = Vec::<u32>::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( let index_buf = Arc::new(
device.create_buffer_init(&wgpu::util::BufferInitDescriptor { device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("index-buffer"), label: Some("index-buffer"),
@ -143,17 +132,12 @@ impl RenderState {
(vertex_buf, index_buf) (vertex_buf, index_buf)
} }
/// Take a meshes /// Take a meshes raw representation and upload it to a GPU buffer
pub fn upload_mesh_to_buffer(mesh: RawMesh) { pub fn upload_mesh_to_buffer(&mut self, mesh: &RawMesh, color: Option<wgpu::Color>) -> Result<Mesh, String> {
}
pub fn load_mesh_to_buffer(&self, filepath: &str, color: Option<wgpu::Color>) -> Result<Mesh, String> {
let raw_mesh = load_obj(filepath)?; let index_count = mesh.indices.len() * 3; // TODO bad bad bad bad!
let index_count = raw_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::<EntityUniforms>() as wgpu::BufferAddress; let uniform_size = mem::size_of::<EntityUniforms>() 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<wgpu::Color>) -> Result<Mesh, String> {
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 /// 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 /// 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 /// 2 views created at this moment, need to smarten this up

@ -1,6 +1,11 @@
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use std::time::Instant; use std::time::Instant;
use cgmath::{Euler, Quaternion}; use cgmath::{Euler, Quaternion};
use config::Config;
use config::File;
use legion::world::SubWorld; use legion::world::SubWorld;
use legion::IntoQuery; use legion::IntoQuery;
use legion::*; use legion::*;
@ -8,15 +13,66 @@ use nalgebra::Quaternion as naQuaternion;
use rapier3d::dynamics::{IntegrationParameters, JointSet, RigidBodySet}; use rapier3d::dynamics::{IntegrationParameters, JointSet, RigidBodySet};
use rapier3d::geometry::{BroadPhase, ColliderSet, NarrowPhase}; use rapier3d::geometry::{BroadPhase, ColliderSet, NarrowPhase};
use rapier3d::pipeline::PhysicsPipeline; use rapier3d::pipeline::PhysicsPipeline;
use crate::camera::{Camera, CameraController}; use crate::camera::{Camera, CameraController};
use crate::components::{Collider, LoopState, Mesh, Physics, Position}; 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 { pub struct RuntimeState {
config_db: Config,
mesh_cache: HashMap<String, RawMesh>,
} }
impl RuntimeState { 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<EntityMeta> {
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);
}
}
} }
} }

@ -1,32 +1,158 @@
use std::path::PathBuf;
use std::time::Instant; use std::time::Instant;
use cgmath::{Euler, Quaternion}; use cgmath::{Euler, Quaternion, Deg};
use legion::world::SubWorld; use imgui::FontSource;
use legion::IntoQuery;
use legion::*; use legion::*;
use legion::IntoQuery;
use legion::systems::CommandBuffer;
use legion::world::SubWorld;
use nalgebra::Quaternion as naQuaternion; use nalgebra::Quaternion as naQuaternion;
use rapier3d::dynamics::{IntegrationParameters, JointSet, RigidBodySet}; use rapier3d::dynamics::{IntegrationParameters, JointSet, RigidBodyBuilder, RigidBodySet};
use rapier3d::geometry::{BroadPhase, ColliderSet, NarrowPhase}; use rapier3d::geometry::{BroadPhase, ColliderBuilder, ColliderSet, NarrowPhase};
use rapier3d::pipeline::{PhysicsPipeline, ChannelEventCollector}; use rapier3d::na::{Isometry3, Vector, Vector3};
use rapier3d::pipeline::{ChannelEventCollector, PhysicsPipeline};
use crate::camera::{Camera, CameraController}; use crate::camera::{Camera, CameraController};
use crate::components::{Collider, LoopState, Mesh, Physics, Position}; use crate::components::{Collider, LoopState, Mesh, Physics, Position};
use imgui::FontSource; use crate::geometry::RawMesh;
use crate::physics::state::PhysicsState; use crate::physics::state::PhysicsState;
use crate::render::state::RenderState;
use crate::runtime::state::RuntimeState;
#[system] #[system]
#[write_component(Collider)]
#[write_component(Physics)]
#[write_component(Mesh)] #[write_component(Mesh)]
pub fn runtime_load( pub fn runtime_load(
world: &mut SubWorld, world: &mut SubWorld,
#[resource] physics_state: &mut PhysicsState, #[resource] runtime_state: &mut RuntimeState,
#[resource] physics_pipeline: &mut PhysicsPipeline, ) {
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 entity in runtime_state.get_configured_entities(){
for (collider, physics, mesh) in query.iter_mut(world) { 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,
},
));
}
_ => {},
}
} }

Loading…
Cancel
Save