Compare commits

...

2 Commits

12 changed files with 2745 additions and 2174 deletions

View File

@@ -9,16 +9,38 @@ type = "PhysicsEntity"
mesh = "ball.obj" mesh = "ball.obj"
[entities.position] [entities.position]
x = 0.0 x = 15.0
y = 0.0 y = 15.0
z = 0.0 z = 15.0
[entities.position.rotation]
x = 0.0
y = 0.0
z = 0.0
[entities.physics]
body_status = "static"
[entities.physics.cuboid]
x = 1.0
y = 1.0
z = 1.0
[[entities]] [[entities]]
name = "camera.1" name = "camera.1"
type = "Camera" type = "Camera"
[entities.position]
x = 15.0
y = 15.0
z = 15.0
[[entities]] [[entities]]
name = "light.1" name = "light.1"
type = "Light" type = "Light"
mesh = "light.obj" mesh = "light.obj"
[entities.position]
x = 20.0
y = 20.0
z = 20.0

View File

@@ -2,8 +2,8 @@
# Material Count: 1 # Material Count: 1
newmtl None newmtl None
Ns 0 Ns 500
Ka 0.000000 0.000000 0.000000 Ka 0.8 0.8 0.8
Kd 0.8 0.8 0.8 Kd 0.8 0.8 0.8
Ks 0.8 0.8 0.8 Ks 0.8 0.8 0.8
d 1 d 1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl Material
Ns 323.999994
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2

View File

@@ -0,0 +1,46 @@
# Blender v2.91.2 OBJ File: ''
# www.blender.org
mtllib textured-cube.mtl
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vt 0.875000 0.500000
vt 0.625000 0.750000
vt 0.625000 0.500000
vt 0.375000 1.000000
vt 0.375000 0.750000
vt 0.625000 0.000000
vt 0.375000 0.250000
vt 0.375000 0.000000
vt 0.375000 0.500000
vt 0.125000 0.750000
vt 0.125000 0.500000
vt 0.625000 0.250000
vt 0.875000 0.750000
vt 0.625000 1.000000
vn 0.0000 1.0000 0.0000
vn 0.0000 0.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 -1.0000 0.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
usemtl Material
s off
f 5/1/1 3/2/1 1/3/1
f 3/2/2 8/4/2 4/5/2
f 7/6/3 6/7/3 8/8/3
f 2/9/4 8/10/4 6/11/4
f 1/3/5 4/5/5 2/9/5
f 5/12/6 2/9/6 6/7/6
f 5/1/1 7/13/1 3/2/1
f 3/2/2 7/14/2 8/4/2
f 7/6/3 5/12/3 6/7/3
f 2/9/4 4/5/4 8/10/4
f 1/3/5 3/2/5 4/5/5
f 5/12/6 1/3/6 2/9/6

View File

@@ -0,0 +1,10 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl None
Ns 500
Ka 0.8 0.8 0.8
Kd 0.8 0.8 0.8
Ks 0.8 0.8 0.8
d 1
illum 2

2372
resources/tilted-terrain.obj Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,16 @@
use wgpu::{TextureView, Buffer, BindGroup};
use std::sync::Arc; use std::sync::Arc;
use rapier3d::dynamics::{RigidBody, RigidBodyHandle};
use rapier3d::geometry::ColliderHandle;
use rapier3d::geometry::Collider as r3dCollider;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use cgmath::Deg;
use cgmath::{Deg, Euler};
use rapier3d::dynamics::{RigidBody, RigidBodyHandle};
use rapier3d::geometry::Collider as r3dCollider;
use rapier3d::geometry::ColliderHandle;
use wgpu::{BindGroup, Buffer, TextureView};
use crate::runtime::state::{TomlPositionDescription, TomlRotationDescription};
// a component is any type that is 'static, sized, send and sync // a component is any type that is 'static, sized, send and sync
pub struct ImguiWindow<'a> { pub struct ImguiWindow<'a> {
pub window: imgui::Window<'a>, pub window: imgui::Window<'a>,
} }
@@ -28,6 +30,47 @@ pub struct Position {
pub rot: cgmath::Euler<Deg<f32>>, pub rot: cgmath::Euler<Deg<f32>>,
} }
impl From<TomlPositionDescription> for Position {
fn from(pos: TomlPositionDescription) -> Self {
let euler = match pos.rot {
None => Euler {
x: Deg(0.0),
y: Deg(0.0),
z: Deg(0.0),
},
Some(v) => Euler {
x: Deg(v.x),
y: Deg(v.y),
z: Deg(v.z),
},
};
Position {
x: pos.x,
y: pos.y,
z: pos.z,
rot: euler,
}
}
}
impl From<Option<TomlPositionDescription>> for Position {
fn from(pos: Option<TomlPositionDescription>) -> Self {
match pos {
None => Position {
x: 0.0,
y: 0.0,
z: 0.0,
rot: Euler {
x: Deg(0.0),
y: Deg(0.0),
z: Deg(0.0),
},
},
Some(v) => Position::from(v),
}
}
}
#[derive(Clone, Default, PartialEq, Eq, Hash, Copy, Debug)] #[derive(Clone, Default, PartialEq, Eq, Hash, Copy, Debug)]
pub struct RangeCopy<Idx> { pub struct RangeCopy<Idx> {
pub start: Idx, pub start: Idx,

View File

@@ -92,12 +92,22 @@ https://github.com/amethyst/legion
ECS ECS
animation animation
config / save loading (sorta!)
render 3d (good!) render 3d (good!)
input/io (yep!) input/io (yep!)
collision / physics (yep!) collision / physics (yep!)
entities & behaviours (got the entities!) entities & behaviours (got the entities!)
scripting! scripting!
Todo:
Load scene imgui interface w/ toml files
FPS graph port from voxel raycaster
better imgui interface with components & systems
Figure out eventing, GameInput, passing all events, etc.
+ texturing
*/ */
//log::info!(""); //log::info!("");
@@ -112,14 +122,6 @@ 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();
// for i in settings.get("entities") {
//
// }
let mut world = World::default(); let mut world = World::default();
let mut imgui_prepare_schedule = Schedule::builder() let mut imgui_prepare_schedule = Schedule::builder()
@@ -194,7 +196,7 @@ fn main() {
// Physics // Physics
let (physics_state, physics_pipeline) = let (physics_state, physics_pipeline) =
PhysicsState::build(rapier3d::math::Vector::new(0.0, -9.81, 0.05)); PhysicsState::build(rapier3d::math::Vector::new(0.0, -9.81, 0.0));
resources.insert(physics_state); resources.insert(physics_state);
resources.insert(physics_pipeline); resources.insert(physics_pipeline);

View File

@@ -117,7 +117,7 @@ pub fn render_test(
if renderer.lights_are_dirty { if renderer.lights_are_dirty {
renderer.lights_are_dirty = false; renderer.lights_are_dirty = false;
let mut query = <(&mut DirectionalLight, &mut Point3<f32>)>::query(); let mut query = <(&mut DirectionalLight, &mut Position)>::query();
for (i, (light, pos)) in query.iter_mut(world).enumerate() { for (i, (light, pos)) in query.iter_mut(world).enumerate() {
renderer.queue.write_buffer( renderer.queue.write_buffer(
&renderer.light_uniform_buf, &renderer.light_uniform_buf,
@@ -129,7 +129,7 @@ pub fn render_test(
push_debug_group_checked("shadow passes", &mut encoder); push_debug_group_checked("shadow passes", &mut encoder);
let mut query = <(&mut DirectionalLight, &mut Point3<f32>)>::query(); let mut query = <(&mut DirectionalLight, &mut Position)>::query();
for (i, (light, pos)) in query.iter_mut(world).enumerate() { for (i, (light, pos)) in query.iter_mut(world).enumerate() {
insert_debug_marker_checked(&format!("shadow pass {} (light at position {:?})", i, pos), &mut encoder); insert_debug_marker_checked(&format!("shadow pass {} (light at position {:?})", i, pos), &mut encoder);

View File

@@ -17,11 +17,55 @@ use crate::components::{Collider, LoopState, Mesh, Physics, Position};
use crate::geometry::{load_obj, RawMesh}; use crate::geometry::{load_obj, RawMesh};
use std::io::Read; use std::io::Read;
pub struct EntityMeta {
#[derive(Deserialize, Clone)]
pub struct TomlBallPhysicsBodyDescription {
pub radius: String
}
#[derive(Deserialize, Clone)]
pub struct TomlCuboidPhysicsBodyDescription {
pub x: f32,
pub y: f32,
pub z: f32,
}
#[derive(Deserialize, Clone)]
pub struct TomlPhysicsDescription {
pub body_status: String,
pub ball: Option<TomlBallPhysicsBodyDescription>,
pub cuboid: Option<TomlCuboidPhysicsBodyDescription>,
}
#[derive(Deserialize, Clone)]
pub struct TomlRotationDescription {
pub x: f32,
pub y: f32,
pub z: f32,
}
#[derive(Deserialize, Clone)]
pub struct TomlPositionDescription {
pub x: f32,
pub y: f32,
pub z: f32,
pub rot: Option<TomlRotationDescription>,
}
#[derive(Deserialize, Clone)]
pub struct TomlEntityDescription {
pub name: String, pub name: String,
pub ent_type: String, #[serde(rename = "type")]
pub type_name: String,
pub mesh: Option<String>, pub mesh: Option<String>,
pub position: Option<Position> pub position: Option<TomlPositionDescription>,
pub physics: Option<TomlPhysicsDescription>,
}
#[derive(Deserialize, Clone)]
pub struct TomlEntityContainer {
pub entities: Vec<TomlEntityDescription>,
} }
pub struct RuntimeState { pub struct RuntimeState {
@@ -29,38 +73,15 @@ pub struct RuntimeState {
mesh_cache: HashMap<String, RawMesh>, mesh_cache: HashMap<String, RawMesh>,
} }
#[derive(Deserialize)]
pub struct TomlPositionDescription {
pub x: f32,
pub y: f32,
pub z: f32,
}
#[derive(Deserialize)]
pub struct TomlEntityDescription {
pub name: String,
#[serde(rename = "type")]
pub type_name: String,
pub mesh: Option<String>,
pub position: Option<TomlPositionDescription>,
}
#[derive(Deserialize)]
pub struct TomlEntityContainer {
pub entities: Vec<TomlEntityDescription>,
}
impl RuntimeState { impl RuntimeState {
pub fn new() -> RuntimeState { pub fn new() -> RuntimeState {
// TODO: Hook this file to the gui
let mut file = fs::File::open("./conf/entity_spawns.toml").unwrap(); let mut file = fs::File::open("./conf/entity_spawns.toml").unwrap();
let mut content = String::new(); let mut content = String::new();
file.read_to_string(&mut content).unwrap(); file.read_to_string(&mut content).unwrap();
// TODO: gracefully fail
let mut settings : TomlEntityContainer = toml::from_str(content.as_str()).unwrap(); let mut settings : TomlEntityContainer = toml::from_str(content.as_str()).unwrap();
// settings
// // File::with_name(..) is shorthand for File::from(Path::new(..))
// .merge(File::with_name("conf/entity_spawns.toml"))
// .unwrap();
RuntimeState { RuntimeState {
config_db: settings, config_db: settings,
@@ -72,40 +93,8 @@ impl RuntimeState {
self.mesh_cache.get(mesh) self.mesh_cache.get(mesh)
} }
pub fn get_configured_entities(&mut self) -> Vec<EntityMeta> { pub fn get_entities(&mut self) -> Vec<TomlEntityDescription> {
let mut out = Vec::new(); self.config_db.entities.clone()
for entity in &self.config_db.entities {
let position = match &entity.position {
None => { None }
Some(pos) => {
Some(Position {
x: pos.x,
y: pos.y,
z: pos.z,
rot: Euler {
x: Deg(0.0),
y: Deg(0.0),
z: Deg(0.0)
}
})
}
};
// log::info!("{:?}", position);
// log::info!("{:?}", entity);
// log::info!("{:?}", entity.into_table());
// log::info!("{:?}", entity.into_array());
out.push(EntityMeta {
name: entity.name.clone(),
ent_type: entity.type_name.clone(),
mesh: entity.mesh.clone(),
position: position
});
}
out
} }
pub fn preload_meshes(&mut self, resources_path: PathBuf) { pub fn preload_meshes(&mut self, resources_path: PathBuf) {

View File

@@ -1,14 +1,17 @@
use std::f32::consts::PI;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::Instant; use std::time::Instant;
use cgmath::{Euler, Quaternion, Deg, Rad, Point3}; use cgmath::{Deg, Euler, Point3, Quaternion, Rad};
use imgui::FontSource; use imgui::FontSource;
use legion::*;
use legion::IntoQuery;
use legion::systems::CommandBuffer; use legion::systems::CommandBuffer;
use legion::world::SubWorld; use legion::world::SubWorld;
use legion::IntoQuery;
use legion::*;
use nalgebra::Quaternion as naQuaternion; use nalgebra::Quaternion as naQuaternion;
use rapier3d::dynamics::{IntegrationParameters, JointSet, RigidBodyBuilder, RigidBodySet}; use rapier3d::dynamics::{
IntegrationParameters, JointSet, MassProperties, RigidBodyBuilder, RigidBodySet,
};
use rapier3d::geometry::{BroadPhase, ColliderBuilder, ColliderSet, NarrowPhase}; use rapier3d::geometry::{BroadPhase, ColliderBuilder, ColliderSet, NarrowPhase};
use rapier3d::na::{Isometry3, Vector, Vector3}; use rapier3d::na::{Isometry3, Vector, Vector3};
use rapier3d::pipeline::{ChannelEventCollector, PhysicsPipeline}; use rapier3d::pipeline::{ChannelEventCollector, PhysicsPipeline};
@@ -19,14 +22,10 @@ use crate::geometry::RawMesh;
use crate::physics::state::PhysicsState; use crate::physics::state::PhysicsState;
use crate::render::state::RenderState; use crate::render::state::RenderState;
use crate::runtime::state::RuntimeState; use crate::runtime::state::RuntimeState;
use std::f32::consts::PI;
#[system] #[system]
#[write_component(Mesh)] #[write_component(Mesh)]
pub fn runtime_load( pub fn runtime_load(world: &mut SubWorld, #[resource] runtime_state: &mut RuntimeState) {
world: &mut SubWorld,
#[resource] runtime_state: &mut RuntimeState,
) {
runtime_state.preload_meshes(PathBuf::from("./resources")); runtime_state.preload_meshes(PathBuf::from("./resources"));
} }
@@ -38,54 +37,48 @@ pub fn runtime_spawn(
#[resource] runtime_state: &mut RuntimeState, #[resource] runtime_state: &mut RuntimeState,
#[resource] renderer: &mut RenderState, #[resource] renderer: &mut RenderState,
) { ) {
for entity in &runtime_state.get_entities() {
for entity in runtime_state.get_configured_entities(){ match entity.type_name.as_ref() {
match entity.ent_type.as_ref() {
"PhysicsEntity" => { "PhysicsEntity" => {
let mesh_name = entity.mesh.unwrap(); let mesh_name = entity.mesh.as_ref().unwrap();
let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) { let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) {
None => { None => {
log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name); log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name);
continue; continue;
} }
Some(mesh) => mesh Some(mesh) => mesh,
}; };
let collider = ColliderBuilder::trimesh(
raw_mesh.vertices.iter().map(|v| v.position()).collect(),
raw_mesh.indices.clone(),
)
.build();
let collider = ColliderBuilder::capsule_y(2.0, 1.0).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 position = Position::from(entity.position.clone());
let mut dynamic_body = RigidBodyBuilder::new_dynamic() let mut dynamic_body = RigidBodyBuilder::new_dynamic()
.can_sleep(false) .can_sleep(false)
.mass(1.0) .mass(100.0)
.translation(0.0, 0.0, 0.0) .translation(position.x, position.y, position.z)
.build(); .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(( let entity: Entity = cmd.push((
Position { 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, gpu_mesh_buffer,
Physics { Physics {
rigid_body: dynamic_body, rigid_body: dynamic_body,
@@ -96,51 +89,46 @@ pub fn runtime_spawn(
collider_handle: None, collider_handle: None,
}, },
)); ));
}, }
"Terrain" => { "Terrain" => {
let mesh_name = entity.mesh.unwrap(); let mesh_name = entity.mesh.clone().unwrap();
let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) { let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) {
None => { None => {
log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name); log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name);
continue; continue;
} }
Some(mesh) => mesh Some(mesh) => mesh,
}; };
let position = Position::from(entity.position.clone());
let mut static_body = RigidBodyBuilder::new_static() let mut static_body = RigidBodyBuilder::new_static()
.position(Isometry3::new(Vector3::new(0.0, -8.0, 0.0), Vector::y())) .position(Isometry3::new(
Vector3::new(position.x, position.y, position.z),
Vector::y(),
))
.build(); .build();
let mesh_collider = ColliderBuilder::trimesh( let mesh_collider = ColliderBuilder::trimesh(
raw_mesh.vertices raw_mesh.vertices.iter().map(|v| v.position()).collect(),
.iter()
.map(|v| v.position())
.collect(),
raw_mesh.indices.clone(), raw_mesh.indices.clone(),
).build(); )
.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 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(( let entity: Entity = cmd.push((
Position { 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, gpu_mesh_buffer,
Physics { Physics {
rigid_body: static_body, rigid_body: static_body,
@@ -151,63 +139,51 @@ pub fn runtime_spawn(
collider_handle: None, collider_handle: None,
}, },
)); ));
}, }
"Camera" => { "Camera" => {
let position = Position::from(entity.position.clone());
let entity: Entity = cmd.push(( let entity: Entity = cmd.push((
Camera { Camera {
position: cgmath::Point3 { position: cgmath::Point3 {
x: 0.0, x: position.x,
y: 0.0, y: position.y,
z: 10.0, z: position.z,
}, },
yaw: Rad(-PI), yaw: Rad(-PI),
pitch: Rad(PI / 2.0), pitch: Rad(PI / 2.0),
}, },
CameraController::new(3.0, 1.0), CameraController::new(5.0, 1.0),
)); ));
}, }
"Light" => { "Light" => {
let mesh_name = entity.mesh.unwrap(); let mesh_name = entity.mesh.clone().unwrap();
let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) { let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) {
None => { None => {
log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name); log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name);
continue; continue;
} }
Some(mesh) => mesh Some(mesh) => mesh,
}; };
let gpu_mesh_buffer = renderer.upload_mesh_to_buffer( let position = Position::from(entity.position.clone());
raw_mesh,
Some(wgpu::Color {
r: 1.0,
g: 0.7,
b: 0.3,
a: 1.0,
})
).unwrap();
let light_entity: Entity = cmd.push(( let gpu_mesh_buffer = renderer
Position { .upload_mesh_to_buffer(
x: 5.0, raw_mesh,
y: 10.0, Some(wgpu::Color {
z: 5.0, r: 1.0,
rot: Euler { g: 0.7,
x: Deg(0.0), b: 0.3,
y: Deg(-25.0), a: 1.0,
z: Deg(0.0), }),
}, )
}, .unwrap();
gpu_mesh_buffer,
renderer.create_light(), let light_entity: Entity =
)); cmd.push((position, gpu_mesh_buffer, renderer.create_light()));
} }
_ => {}
_ => {},
} }
} }
} }