lib: basic ECS

ecs
q3k 2020-08-22 14:41:36 +00:00
parent 179cc63dd1
commit 7ef85bd570
10 changed files with 399 additions and 6 deletions

View File

@ -1,4 +1,4 @@
load("@io_bazel_rules_rust//rust:rust.bzl", "rust_binary")
load("@io_bazel_rules_rust//rust:rust.bzl", "rust_binary", "rust_library")
load("@rules_pkg//:pkg.bzl", "pkg_tar")
rust_binary(
@ -11,16 +11,19 @@ rust_binary(
],
srcs = [
"src/main.rs",
"src/physics/mod.rs",
"src/physics/color.rs",
"src/render/mod.rs",
"src/render/light.rs",
"src/render/material.rs",
"src/render/mesh.rs",
"src/render/mod.rs",
"src/render/renderable.rs",
"src/render/vulkan/mod.rs",
"src/render/vulkan/data.rs",
"src/render/vulkan/material.rs",
"src/render/vulkan/mod.rs",
"src/render/vulkan/pipeline.rs",
"src/render/vulkan/pipeline_forward.rs",
"src/render/vulkan/qfi.rs",
@ -28,13 +31,15 @@ rust_binary(
"src/render/vulkan/surface_binding.rs",
"src/render/vulkan/swapchain_binding.rs",
"src/render/vulkan/worker.rs",
"src/util/mod.rs",
"src/util/counter.rs",
"src/util/file.rs",
"src/util/mod.rs",
"src/util/profiler.rs",
"src/util/resourcemap.rs",
],
deps = [
"//lib/ecs",
"//third_party/cargo:cgmath",
"//third_party/cargo:image",
"//third_party/cargo:winit",

View File

@ -25,6 +25,7 @@ mod render;
mod util;
mod physics;
use ecs::{component, world};
use render::vulkan::data;
use render::light::Omni;
use render::material::{Texture, PBRMaterialBuilder};
@ -32,10 +33,40 @@ use render::mesh::Mesh;
use render::renderable::{Light, Object, Renderable, ResourceManager};
use physics::color;
#[derive(Debug)]
struct Dupa {
zupa: u32,
kupa: Vec<u32>,
}
impl component::Component for Dupa {}
#[derive(Debug)]
struct Position {
x: u32,
y: u32,
z: u32,
}
impl component::Component for Position {}
fn main() {
env_logger::init();
log::info!("Starting...");
let mut world = world::World::new();
world.new_entity().with(Dupa { zupa: 2137, kupa: vec!(1,2,3,4)} ).with(Position { x: 2, y: 13, z: 7 }).build();
world.new_entity().with(Dupa { zupa: 2137, kupa: vec!(1,2,3,4)} ).build();
world.new_entity().with(Dupa { zupa: 2137, kupa: vec!(1,2,3,4)} ).build();
world.new_entity().with(Dupa { zupa: 2137, kupa: vec!(1,2,3,4)} ).build();
for dupa in world.components::<Dupa>() {
log::info!("dupa {:?}", dupa);
}
for dupa in world.components::<Position>() {
log::info!("position {:?}", dupa);
}
let mut rm = ResourceManager::new();
let mesh = {

View File

@ -78,7 +78,7 @@ pub fn srgb_to_cie_xyz(r: f32, g: f32, b: f32) -> (f32, f32, f32) {
if v < 0.004045 {
v / 12.92
} else {
let v: f32 = ((v + 0.055) / (1.055));
let v: f32 = (v + 0.055) / (1.055);
v.powf(2.4) as f32
}
};

View File

@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License along with
// Abrasion. If not, see <https://www.gnu.org/licenses/>.
use log;
use std::ffi::CStr;
use std::fs::File;
use std::io::prelude::*;

18
lib/ecs/BUILD Normal file
View File

@ -0,0 +1,18 @@
load("@io_bazel_rules_rust//rust:rust.bzl", "rust_test", "rust_library")
rust_library(
name = "ecs",
srcs = [
"src/lib.rs",
"src/component.rs",
"src/entity.rs",
"src/system.rs",
"src/world.rs",
],
visibility = ["//engine:__subpackages__"],
)
rust_test(
name = "ecs_test",
crate = ":ecs",
)

9
lib/ecs/src/component.rs Normal file
View File

@ -0,0 +1,9 @@
pub type ID = std::any::TypeId;
pub trait Component: 'static {
}
pub fn id<T: Component>() -> ID {
std::any::TypeId::of::<T>()
}

39
lib/ecs/src/entity.rs Normal file
View File

@ -0,0 +1,39 @@
use crate::component;
use crate::world;
pub type ID = u64;
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
pub struct Entity(pub ID);
impl Entity {
pub fn id(&self) -> ID {
self.0
}
}
pub struct EntityBuilder<'a> {
world: &'a mut world::World,
ent: Entity,
}
impl<'a> EntityBuilder<'a> {
pub fn new(world: &'a mut world::World, id: ID) -> Self {
Self {
world,
ent: Entity(id),
}
}
pub fn with<T: component::Component>(self, c: T) -> Self {
self.world.register_component_entity(component::id::<T>(), Box::new(c), self.ent);
self
}
pub fn build(self) -> ID {
let id = self.ent.id();
self.world.commit(self.ent);
id
}
}

5
lib/ecs/src/lib.rs Normal file
View File

@ -0,0 +1,5 @@
pub mod component;
pub mod entity;
pub mod system;
pub mod world;

96
lib/ecs/src/system.rs Normal file
View File

@ -0,0 +1,96 @@
use crate::{component, world};
pub trait System<'a> {
type SystemData: DynamicSystemData<'a>;
fn run(&mut self, sd: Self::SystemData);
}
trait WorldRunner<'a> {
fn run_world(&mut self, &'a world::World);
}
impl<'a, T> WorldRunner<'a> for T
where
T: System<'a>,
{
fn run_world(&mut self, world: &'a world::World) {
let data = T::SystemData::fetch(world);
self.run(data);
}
}
pub trait DynamicSystemData<'a> {
fn fetch(world: &'a world::World) -> Self;
}
impl<'a, T: component::Component> DynamicSystemData<'a> for world::ReadData<'a, T> {
fn fetch(world: &'a world::World) -> Self {
world.components()
}
}
impl<'a, T: component::Component> DynamicSystemData<'a> for world::ReadWriteData<'a, T> {
fn fetch(world: &'a world::World) -> Self {
world.components_mut()
}
}
pub struct Processor<'a> {
world: &'a world::World,
runners: Vec<Box<dyn WorldRunner<'a>>>,
}
impl<'a> Processor<'a> {
fn add_system<T: System<'a> + 'static>(&mut self, system: T) {
self.runners.push(Box::new(system));
}
fn run(&mut self) {
for runner in &mut self.runners {
runner.run_world(self.world);
}
}
}
#[cfg(test)]
mod test {
use crate::{component, system, world};
struct Position {
x: u32,
y: u32,
z: u32,
}
impl component::Component for Position {}
struct Physics;
impl<'a> system::System<'a> for Physics {
type SystemData = world::ReadWriteData<'a, Position>;
fn run(&mut self, sd: Self::SystemData) {
for mut p in sd.iter_mut() {
p.z += 1;
}
}
}
#[test]
fn processor() {
let mut world = world::World::new();
world.new_entity().with(Position { x: 1, y: 2, z: 3 }).build();
world.new_entity().with(Position { x: 4, y: 5, z: 6 }).build();
let mut p = system::Processor {
world: &world,
runners: Vec::new(),
};
p.add_system(Physics);
let positions = world.components::<Position>();
assert_eq!(vec![3, 6], positions.iter().map(|el| el.z).collect::<Vec<u32>>());
p.run();
assert_eq!(vec![4, 7], positions.iter().map(|el| el.z).collect::<Vec<u32>>());
}
}

191
lib/ecs/src/world.rs Normal file
View File

@ -0,0 +1,191 @@
use std::collections::BTreeMap;
use std::cell::{Ref, RefCell, RefMut};
use std::ops::Deref;
use std::marker::PhantomData;
use std::iter::Iterator;
use crate::entity;
use crate::component;
pub struct ReadData<'a, T: component::Component> {
underlying: &'a RefCell<Vec<Box<dyn component::Component>>>,
phantom: PhantomData<&'a T>,
}
impl<'a, T: component::Component> ReadData<'a, T> {
pub fn iter(&self) -> ReadDataIter<'a, T> {
ReadDataIter {
underlying: Some(Ref::map(self.underlying.borrow(), |el| el.as_slice())),
phantom: PhantomData,
}
}
}
pub struct ReadDataIter<'a, T: component::Component> {
underlying: Option<Ref<'a, [Box<dyn component::Component>]>>,
phantom: PhantomData<&'a T>,
}
impl <'a, T: component::Component> Iterator for ReadDataIter<'a, T> {
type Item = Ref<'a, T>;
fn next(&mut self) -> Option<Self::Item> {
if self.underlying.as_ref().unwrap().len() == 0 {
return None;
}
let (u, n) = Ref::map_split(self.underlying.take().unwrap(), |slice| {
let (left, right) = slice.split_at(1);
let ptr = left[0].as_ref();
let left = unsafe { & *(ptr as *const (dyn component::Component) as *const T) };
return (left, right);
});
self.underlying = Some(n);
Some(u)
}
}
pub struct ReadWriteData<'a, T: component::Component> {
underlying: &'a RefCell<Vec<Box<dyn component::Component>>>,
phantom: PhantomData<&'a T>,
}
impl<'a, T: component::Component> ReadWriteData<'a, T> {
pub fn iter_mut(&self) -> ReadWriteDataIter<'a, T> {
ReadWriteDataIter {
underlying: Some(RefMut::map(self.underlying.borrow_mut(), |el| el.as_mut_slice())),
phantom: PhantomData,
}
}
}
pub struct ReadWriteDataIter<'a, T: component::Component> {
underlying: Option<RefMut<'a, [Box<dyn component::Component>]>>,
phantom: PhantomData<&'a T>,
}
impl <'a, T: component::Component> Iterator for ReadWriteDataIter<'a, T> {
type Item = RefMut<'a, T>;
fn next(&mut self) -> Option<Self::Item> {
if self.underlying.as_ref().unwrap().len() == 0 {
return None;
}
let (u, n) = RefMut::map_split(self.underlying.take().unwrap(), |slice| {
let (left, right) = slice.split_at_mut(1);
let ptr: &mut dyn component::Component = &mut *(left[0]);
let left = unsafe {
&mut *(ptr as *mut (dyn component::Component) as *mut T)
};
return (left, right);
});
self.underlying = Some(n);
Some(u)
}
}
pub struct World {
entities: BTreeMap<entity::ID, entity::Entity>,
entity_ids_by_component: BTreeMap<component::ID, Vec<entity::ID>>,
components_by_id: BTreeMap<component::ID, RefCell<Vec<Box<dyn component::Component>>>>,
next_id: entity::ID,
empty: RefCell<Vec<Box<dyn component::Component>>>,
}
impl World {
pub fn new() -> Self {
Self {
entities: BTreeMap::new(),
entity_ids_by_component: BTreeMap::new(),
components_by_id: BTreeMap::new(),
next_id: 1u64,
empty: RefCell::new(Vec::new()),
}
}
pub fn new_entity(&mut self) -> entity::EntityBuilder {
let id = self.next_id;
self.next_id += 1;
entity::EntityBuilder::new(self, id)
}
pub fn register_component_entity(
&mut self,
cid: component::ID,
c: Box<dyn component::Component>,
e: entity::Entity
) {
let vec = self.entity_ids_by_component.entry(cid).or_insert(vec!());
vec.push(e.id());
let vec = self.components_by_id.entry(cid).or_insert(RefCell::new(vec!()));
vec.borrow_mut().push(c);
}
pub fn commit(&mut self, ent: entity::Entity) {
self.entities.insert(ent.id(), ent);
}
pub fn components<'a, T: component::Component>(&'a self) -> ReadData<T> {
let underlying = match self.components_by_id.get(&component::id::<T>()) {
None => &self.empty,
Some(r) => &r,
};
ReadData {
underlying: underlying,
phantom: PhantomData,
}
}
pub fn components_mut<'a, T: component::Component>(&'a self) -> ReadWriteData<T> {
let underlying = match self.components_by_id.get(&component::id::<T>()) {
None => &self.empty,
Some(r) => &r,
};
ReadWriteData {
underlying: underlying,
phantom: PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use crate::entity;
use crate::component;
use crate::world;
#[derive(Debug)]
struct Position {
x: u32,
y: u32,
z: u32,
}
impl component::Component for Position {}
#[derive(Debug)]
struct Name(String);
impl component::Component for Name {}
impl Name {
fn new(s: &str) -> Name {
Name(String::from(s))
}
}
#[test]
fn new_list() {
let mut world = world::World::new();
world.new_entity().with(Name::new("foo")).with(Position { x: 1, y: 2, z: 3 }).build();
world.new_entity().with(Name::new("bar")).with(Position { x: 4, y: 5, z: 6 }).build();
let mut named = world.components::<Name>().iter();
let mut named2 = world.components::<Name>().iter();
//assert_eq!(named.len(), 2);
assert_eq!(String::from("foo"), named.next().unwrap().0);
assert_eq!(String::from("foo"), named2.next().unwrap().0);
assert_eq!(String::from("bar"), named.next().unwrap().0);
assert_eq!(String::from("bar"), named2.next().unwrap().0);
}
}