parent
9b821f26eb
commit
574a2ee54c
7 changed files with 308 additions and 76 deletions
@ -0,0 +1,27 @@ |
||||
{ |
||||
// Use IntelliSense to learn about possible attributes. |
||||
// Hover to view descriptions of existing attributes. |
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 |
||||
"version": "0.2.0", |
||||
"configurations": [ |
||||
{ |
||||
"type": "lldb", |
||||
"request": "launch", |
||||
"name": "Debug unit tests in library 'gaemstone'", |
||||
"cargo": { |
||||
"args": [ |
||||
"test", |
||||
"--no-run", |
||||
"--lib", |
||||
"--package=gaemstone" |
||||
], |
||||
"filter": { |
||||
"name": "gaemstone", |
||||
"kind": "lib" |
||||
} |
||||
}, |
||||
"args": [], |
||||
"cwd": "${workspaceFolder}" |
||||
} |
||||
] |
||||
} |
@ -0,0 +1,160 @@ |
||||
use std::alloc::Layout; |
||||
use std::collections::HashMap; |
||||
|
||||
use super::archetable::{Archetable, EntityRow}; |
||||
use super::component::Component; |
||||
use super::entity::Entity; |
||||
use super::entity_index::EntityIndex; |
||||
use super::prelude::Archetype; |
||||
use super::world::World; |
||||
|
||||
pub struct Archegraph { |
||||
node_count: usize, |
||||
node_storage: Vec<Option<Node>>, |
||||
// unused_indices: Vec<NodeIndex>,
|
||||
type_lookup: HashMap<Archetype, NodeIndex>, |
||||
} |
||||
|
||||
pub type NodeIndex = usize; |
||||
|
||||
pub struct Node { |
||||
index: NodeIndex, |
||||
archetable: Archetable, |
||||
with: HashMap<Component, NodeIndex>, |
||||
without: HashMap<Component, NodeIndex>, |
||||
} |
||||
|
||||
pub type GraphEntityIndex = EntityIndex<(NodeIndex, EntityRow)>; |
||||
|
||||
impl Archegraph { |
||||
pub fn new() -> Self { |
||||
let empty_node = Node::new(0, Archetable::new_empty()); |
||||
let mut type_lookup = HashMap::new(); |
||||
type_lookup.insert(Archetype::EMPTY, 0); |
||||
Self { |
||||
node_count: 1, |
||||
node_storage: vec![Some(empty_node)], |
||||
// unused_indices: Vec::new(),
|
||||
type_lookup, |
||||
} |
||||
} |
||||
|
||||
#[inline] |
||||
#[must_use] |
||||
pub fn node_count(&self) -> usize { |
||||
self.node_count |
||||
} |
||||
|
||||
#[must_use] |
||||
pub fn root(&self) -> &Node { |
||||
self.node_storage[0].as_ref().unwrap() |
||||
} |
||||
|
||||
#[must_use] |
||||
pub fn root_mut(&mut self) -> &mut Node { |
||||
self.node_storage[0].as_mut().unwrap() |
||||
} |
||||
|
||||
#[must_use] |
||||
pub fn get(&self, index: NodeIndex) -> Option<&Node> { |
||||
self.node_storage.get(index).and_then(Option::as_ref) |
||||
} |
||||
|
||||
#[must_use] |
||||
pub fn get_mut(&mut self, index: NodeIndex) -> Option<&mut Node> { |
||||
self.node_storage.get_mut(index).and_then(Option::as_mut) |
||||
} |
||||
|
||||
#[must_use] |
||||
pub fn lookup_or_create( |
||||
&mut self, |
||||
archetype: &Archetype, |
||||
entity_index: &GraphEntityIndex, |
||||
) -> &mut Node { |
||||
if let Some(index) = self.type_lookup.get(&archetype) { |
||||
// SAFETY: `type_lookup` is guaranteed to point to a valid archetable.
|
||||
self.node_storage[*index].as_mut().unwrap() |
||||
} else { |
||||
let index = self.reserve_archetable_index(); |
||||
let layouts = self.get_layouts(archetype, entity_index); |
||||
let archetable = Archetable::new(archetype.clone(), layouts); |
||||
let mut node = Node::new(index, archetable); |
||||
|
||||
self.type_lookup.insert(archetype.clone(), index); |
||||
self.fill_edges(&mut node, entity_index); |
||||
|
||||
let entry = &mut self.node_storage[index]; |
||||
*entry = Some(node); |
||||
entry.as_mut().unwrap() |
||||
} |
||||
} |
||||
|
||||
fn get_layout(&self, component: Component, entity_index: &GraphEntityIndex) -> Option<Layout> { |
||||
// FIXME: Relations can be components too.
|
||||
let entity: Entity = component.try_into().ok()?; |
||||
let (node_index, row) = entity_index.get(entity.id())?.data; |
||||
let archetable = self.get(node_index).unwrap().get(); |
||||
let component_index = archetable.archetype().position(World::LAYOUT)?; |
||||
let column = archetable.get_column(component_index).unwrap(); |
||||
let column = unsafe { column.as_typed_slice::<Layout>() }; |
||||
Some(column[row]) |
||||
} |
||||
|
||||
fn get_layouts( |
||||
&self, |
||||
archetype: &Archetype, |
||||
entity_index: &GraphEntityIndex, |
||||
) -> Vec<Option<Layout>> { |
||||
let iter = archetype.components().iter(); |
||||
iter.map(|c| self.get_layout(*c, entity_index)).collect() |
||||
} |
||||
|
||||
#[must_use] |
||||
fn reserve_archetable_index(&mut self) -> usize { |
||||
self.node_count += 1; |
||||
// self.unused_indices.pop().unwrap_or_else(|| {
|
||||
self.node_storage.push(None); |
||||
self.node_storage.len() - 1 |
||||
// })
|
||||
} |
||||
|
||||
fn fill_edges(&mut self, node: &mut Node, entity_index: &GraphEntityIndex) { |
||||
let archetype = node.archetable.archetype(); |
||||
for component in archetype.components() { |
||||
let other_archetype = archetype.clone().without(*component); |
||||
let other_node = self.lookup_or_create(&other_archetype, entity_index); |
||||
node.without.insert(*component, other_node.index); |
||||
other_node.with.insert(*component, node.index); |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl Node { |
||||
#[must_use] |
||||
fn new(index: NodeIndex, archetable: Archetable) -> Self { |
||||
Self { |
||||
index, |
||||
archetable, |
||||
with: HashMap::new(), |
||||
without: HashMap::new(), |
||||
} |
||||
} |
||||
|
||||
#[inline] |
||||
#[must_use] |
||||
pub fn index(&self) -> NodeIndex { |
||||
self.index |
||||
} |
||||
|
||||
#[inline] |
||||
#[must_use] |
||||
pub fn get(&self) -> &Archetable { |
||||
&self.archetable |
||||
} |
||||
|
||||
#[inline] |
||||
#[must_use] |
||||
pub fn get_mut(&mut self) -> &mut Archetable { |
||||
&mut self.archetable |
||||
} |
||||
} |
Loading…
Reference in new issue