Initial commit
This commit is contained in:
commit
ac0d491786
21 changed files with 1094 additions and 0 deletions
66
ecs/component_manager.odin
Normal file
66
ecs/component_manager.odin
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
package ecs
|
||||
|
||||
import "core:log"
|
||||
|
||||
ComponentManager :: struct {
|
||||
component_types: map[typeid]ComponentType,
|
||||
component_pools: map[typeid]^ComponentPool(any),
|
||||
next_component_type: ComponentType,
|
||||
}
|
||||
|
||||
component_manager_create :: proc() -> ComponentManager {
|
||||
component_manager := ComponentManager {
|
||||
component_types = make(map[typeid]ComponentType, context.allocator),
|
||||
component_pools = make(map[typeid]^ComponentPool(any), context.allocator),
|
||||
next_component_type = 0
|
||||
}
|
||||
|
||||
return component_manager
|
||||
}
|
||||
|
||||
component_manager_register_component :: proc($T: typeid, component_manager: ^ComponentManager) {
|
||||
log.assertf(T not_in component_manager.component_types, "Registering component more than once")
|
||||
|
||||
component_manager.component_types[T] = component_manager.next_component_type
|
||||
component_pool := new(ComponentPool(T), context.allocator)
|
||||
component_pool^ = component_pool_create(T)
|
||||
component_manager.component_pools[T] = cast(^ComponentPool(any))component_pool
|
||||
component_manager.next_component_type += 1
|
||||
}
|
||||
|
||||
component_manager_get_component_type :: proc($T: typeid, component_manager: ^ComponentManager) -> ComponentType {
|
||||
log.assertf(T in component_manager.component_types, "Component not registered before use")
|
||||
return component_manager.component_types[T]
|
||||
}
|
||||
|
||||
component_manager_add_component :: proc($T: typeid, component_manager: ^ComponentManager, entity_id: EntityID, component: T) {
|
||||
component_pool := cast(^ComponentPool(T))component_manager.component_pools[T]
|
||||
component_pool_insert_data(T, component_pool, entity_id, component)
|
||||
}
|
||||
|
||||
component_manager_remove_component :: proc($T: typeid, component_manager: ^ComponentManager, entity_id: EntityID) {
|
||||
component_pool := cast(^ComponentPool(T))component_manager.component_pools[T]
|
||||
component_pool_remove_data(T, component_pool, entity_id)
|
||||
}
|
||||
|
||||
component_manager_get_component :: proc($T: typeid, component_manager: ^ComponentManager, entity_id: EntityID) -> ^T {
|
||||
component_pool := cast(^ComponentPool(T))component_manager.component_pools[T]
|
||||
return component_pool_get(T, component_pool, entity_id)
|
||||
}
|
||||
|
||||
component_manager_destroy_entity :: proc(component_manager: ^ComponentManager, entity_id: EntityID) {
|
||||
for _, component_pool in component_manager.component_pools {
|
||||
component_pool_destroy_entity(component_pool, entity_id)
|
||||
}
|
||||
}
|
||||
|
||||
component_manager_destroy :: proc(component_manager: ^ComponentManager) {
|
||||
for _, &component_pool in component_manager.component_pools {
|
||||
component_pool_destroy(component_pool)
|
||||
free(component_pool)
|
||||
}
|
||||
|
||||
delete(component_manager.component_pools)
|
||||
delete(component_manager.component_types)
|
||||
}
|
||||
|
||||
68
ecs/component_pool.odin
Normal file
68
ecs/component_pool.odin
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package ecs
|
||||
|
||||
import "core:log"
|
||||
|
||||
ComponentPool :: struct($T: typeid) {
|
||||
data: []T,
|
||||
entity_to_index: map[EntityID]uintptr,
|
||||
index_to_entity: map[uintptr]EntityID,
|
||||
size: uintptr,
|
||||
}
|
||||
|
||||
component_pool_create :: proc($T: typeid) -> ComponentPool(T) {
|
||||
component_pool := ComponentPool(T) {
|
||||
data = make([]T, ENTITY_MAX),
|
||||
entity_to_index = make(map[EntityID]uintptr, context.allocator),
|
||||
index_to_entity = make(map[uintptr]EntityID, context.allocator),
|
||||
size = 0,
|
||||
}
|
||||
|
||||
return component_pool
|
||||
}
|
||||
|
||||
component_pool_insert_data :: proc($T: typeid, component_pool: ^ComponentPool(T), entity_id: EntityID, component: T) {
|
||||
log.assertf(entity_id not_in component_pool.entity_to_index, "Component already added to entity")
|
||||
|
||||
new_idx := component_pool.size
|
||||
component_pool.entity_to_index[entity_id] = new_idx
|
||||
component_pool.index_to_entity[new_idx] = entity_id
|
||||
component_pool.data[new_idx] = component
|
||||
|
||||
component_pool.size += 1
|
||||
}
|
||||
|
||||
component_pool_remove_data :: proc(component_pool: ^ComponentPool(any), entity_id: EntityID) {
|
||||
log.assertf(entity_id in component_pool.entity_to_index, "Entity doesn't have component")
|
||||
|
||||
removed_entity_idx := component_pool.entity_to_index[entity_id]
|
||||
last_element_idx := component_pool.size - 1
|
||||
component_pool.data[removed_entity_idx] = component_pool.data[last_element_idx]
|
||||
|
||||
last_element_entity_id := component_pool.index_to_entity[last_element_idx]
|
||||
component_pool.entity_to_index[last_element_entity_id] = removed_entity_idx
|
||||
component_pool.index_to_entity[removed_entity_idx] = last_element_entity_id
|
||||
|
||||
delete_key(&component_pool.entity_to_index, entity_id)
|
||||
delete_key(&component_pool.index_to_entity, last_element_idx)
|
||||
|
||||
component_pool.size -= 1
|
||||
}
|
||||
|
||||
component_pool_get :: proc($T: typeid, component_pool: ^ComponentPool(T), entity_id: EntityID) -> ^T {
|
||||
log.assertf(entity_id in component_pool.entity_to_index, "Entity doesn't have component")
|
||||
|
||||
idx := component_pool.entity_to_index[entity_id]
|
||||
return &component_pool.data[idx]
|
||||
}
|
||||
|
||||
component_pool_destroy_entity :: proc(component_pool: ^ComponentPool(any), entity_id: EntityID) {
|
||||
if entity_id in component_pool.entity_to_index {
|
||||
component_pool_remove_data(component_pool, entity_id)
|
||||
}
|
||||
}
|
||||
|
||||
component_pool_destroy :: proc(component_pool: ^ComponentPool(any)) {
|
||||
delete(component_pool.entity_to_index)
|
||||
delete(component_pool.index_to_entity)
|
||||
delete(component_pool.data)
|
||||
}
|
||||
9
ecs/constants.odin
Normal file
9
ecs/constants.odin
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package ecs
|
||||
|
||||
ENTITY_MAX :: 4096
|
||||
COMPONENT_MAX :: 32
|
||||
|
||||
ID :: u32
|
||||
|
||||
EntityID :: ID
|
||||
ComponentType :: u8
|
||||
82
ecs/coordinator.odin
Normal file
82
ecs/coordinator.odin
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
package ecs
|
||||
|
||||
Coordinator :: struct {
|
||||
component_manager: ^ComponentManager,
|
||||
entity_manager: ^EntityManager,
|
||||
system_manager: ^SystemManager,
|
||||
}
|
||||
|
||||
coordinator_create :: proc() -> Coordinator {
|
||||
coordinator := Coordinator{}
|
||||
coordinator.component_manager = new(ComponentManager, context.allocator)
|
||||
coordinator.component_manager^ = component_manager_create()
|
||||
|
||||
coordinator.entity_manager = new(EntityManager, context.allocator)
|
||||
coordinator.entity_manager^ = entity_manager_create()
|
||||
|
||||
coordinator.system_manager = new(SystemManager, context.allocator)
|
||||
coordinator.system_manager^ = system_manager_create()
|
||||
|
||||
return coordinator
|
||||
}
|
||||
|
||||
coordinator_create_entity :: proc(coordinator: ^Coordinator) -> EntityID {
|
||||
return entity_manager_create_entity(coordinator.entity_manager)
|
||||
}
|
||||
|
||||
coordinator_destroy_entity :: proc(coordinator: ^Coordinator, entity_id: EntityID) {
|
||||
component_manager_destroy_entity(coordinator.component_manager, entity_id)
|
||||
entity_manager_destroy_entity(coordinator.entity_manager, entity_id)
|
||||
system_manager_destroy_entity(coordinator.system_manager, entity_id)
|
||||
}
|
||||
|
||||
coordinator_register_component :: proc($T: typeid, coordinator: ^Coordinator) {
|
||||
component_manager_register_component(T, coordinator.component_manager)
|
||||
}
|
||||
|
||||
coordinator_add_component :: proc($T: typeid, coordinator: ^Coordinator, entity_id: EntityID, component: T) {
|
||||
component_manager_add_component(T, coordinator.component_manager, entity_id, component)
|
||||
|
||||
signature := entity_manager_get_signature(coordinator.entity_manager, entity_id)
|
||||
signature_set(&signature, component_manager_get_component_type(T, coordinator.component_manager))
|
||||
entity_manager_set_signature(coordinator.entity_manager, entity_id, signature)
|
||||
|
||||
system_manager_change_entity_signature(coordinator.system_manager, entity_id, signature)
|
||||
}
|
||||
|
||||
coordinator_remove_component :: proc($T: typeid, coordinator: ^Coordinator, entity_id: EntityID) {
|
||||
component_manager_remove_component(T, coordinator.component_manager, entity_id)
|
||||
|
||||
signature := entity_manager_get_signature(coordinator.entity_manager, entity_id)
|
||||
signature_unset(&signature, component_manager_get_component_type(T, coordinator.component_manager))
|
||||
entity_manager_set_signature(coordinator.entity_manager, entity_id, signature)
|
||||
|
||||
system_manager_change_entity_signature(coordinator.system_manager, entity_id, signature)
|
||||
}
|
||||
|
||||
coordinator_get_component :: proc($T: typeid, coordinator: ^Coordinator, entity_id: EntityID) -> ^T {
|
||||
return component_manager_get_component(T, coordinator.component_manager, entity_id)
|
||||
}
|
||||
|
||||
coordinator_get_component_type :: proc($T: typeid, coordinator: ^Coordinator) -> ComponentType {
|
||||
return component_manager_get_component_type(T, coordinator.component_manager)
|
||||
}
|
||||
|
||||
coordinator_register_system :: proc($T: typeid, coordinator: ^Coordinator) -> ^T {
|
||||
return system_manager_register_system(T, coordinator.system_manager)
|
||||
}
|
||||
|
||||
coordinator_set_system_signature :: proc($T: typeid, coordinator: ^Coordinator, signature: Signature) {
|
||||
system_manager_set_signature(T, coordinator.system_manager, signature)
|
||||
}
|
||||
|
||||
coordinator_destroy :: proc(coordinator: ^Coordinator) {
|
||||
component_manager_destroy(coordinator.component_manager)
|
||||
entity_manager_destroy(coordinator.entity_manager)
|
||||
system_manager_destroy(coordinator.system_manager)
|
||||
|
||||
free(coordinator.component_manager)
|
||||
free(coordinator.entity_manager)
|
||||
free(coordinator.system_manager)
|
||||
}
|
||||
|
||||
54
ecs/entity_manager.odin
Normal file
54
ecs/entity_manager.odin
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package ecs
|
||||
|
||||
import "core:log"
|
||||
import "core:container/queue"
|
||||
|
||||
EntityManager :: struct {
|
||||
available_entities: queue.Queue(EntityID),
|
||||
signatures: [ENTITY_MAX]Signature,
|
||||
living_entity_count: u32,
|
||||
}
|
||||
|
||||
entity_manager_create :: proc() -> EntityManager {
|
||||
entity_manager := EntityManager{living_entity_count = 0}
|
||||
queue.init(&entity_manager.available_entities)
|
||||
|
||||
for entity_id in 0..<ENTITY_MAX {
|
||||
queue.push_back(&entity_manager.available_entities, cast(EntityID)entity_id)
|
||||
}
|
||||
|
||||
return entity_manager
|
||||
}
|
||||
|
||||
entity_manager_create_entity :: proc(entity_manager: ^EntityManager) -> EntityID {
|
||||
log.assertf(entity_manager.living_entity_count < ENTITY_MAX, "Too many entities created")
|
||||
|
||||
entity_id := queue.pop_front(&entity_manager.available_entities)
|
||||
entity_manager.living_entity_count += 1
|
||||
|
||||
return entity_id
|
||||
}
|
||||
|
||||
entity_manager_destroy_entity :: proc(entity_manager: ^EntityManager, entity_id: EntityID) {
|
||||
log.assertf(entity_id < ENTITY_MAX, "Entity ID out of range")
|
||||
|
||||
signature_clear(&entity_manager.signatures[entity_id])
|
||||
queue.push_back(&entity_manager.available_entities, entity_id)
|
||||
entity_manager.living_entity_count -= 1
|
||||
}
|
||||
|
||||
entity_manager_set_signature :: proc(entity_manager: ^EntityManager, entity_id: EntityID, signature: Signature) {
|
||||
log.assertf(entity_id < ENTITY_MAX, "Entity ID out of range")
|
||||
|
||||
entity_manager.signatures[entity_id] = signature
|
||||
}
|
||||
|
||||
entity_manager_get_signature :: proc(entity_manager: ^EntityManager, entity_id: EntityID) -> Signature {
|
||||
log.assertf(entity_id < ENTITY_MAX, "Entity ID out of range")
|
||||
|
||||
return entity_manager.signatures[entity_id]
|
||||
}
|
||||
|
||||
entity_manager_destroy :: proc(entity_manager: ^EntityManager) {
|
||||
queue.destroy(&entity_manager.available_entities)
|
||||
}
|
||||
34
ecs/physics_system.odin
Normal file
34
ecs/physics_system.odin
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package ecs
|
||||
|
||||
Vec3 :: distinct [3]f32
|
||||
|
||||
Gravity :: struct {
|
||||
force: Vec3
|
||||
}
|
||||
|
||||
RigidBody :: struct {
|
||||
velocity: Vec3,
|
||||
acceleration: Vec3,
|
||||
}
|
||||
|
||||
Transform :: struct {
|
||||
position: Vec3,
|
||||
rotation: Vec3,
|
||||
scale: Vec3,
|
||||
}
|
||||
|
||||
PhysicsSystem :: struct {
|
||||
using base: SystemBase,
|
||||
}
|
||||
|
||||
|
||||
physics_system_update :: proc(physics_system: ^PhysicsSystem, coordinator: ^Coordinator, dt: f32) {
|
||||
for entity in physics_system.entities {
|
||||
rigid_body := coordinator_get_component(RigidBody, coordinator, entity)
|
||||
transform := coordinator_get_component(Transform, coordinator, entity)
|
||||
gravity := coordinator_get_component(Gravity, coordinator, entity)
|
||||
|
||||
transform.position += rigid_body.velocity * dt
|
||||
rigid_body.velocity += gravity.force * dt
|
||||
}
|
||||
}
|
||||
10
ecs/render_system.odin
Normal file
10
ecs/render_system.odin
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package ecs
|
||||
|
||||
RenderSystem :: struct {
|
||||
using base: SystemBase,
|
||||
}
|
||||
|
||||
render_system_update :: proc(render_system: ^RenderSystem, coordinator: ^Coordinator) {
|
||||
for entity in render_system.entities {
|
||||
}
|
||||
}
|
||||
19
ecs/signature.odin
Normal file
19
ecs/signature.odin
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package ecs
|
||||
|
||||
Signature :: bit_set[0..<COMPONENT_MAX]
|
||||
|
||||
signature_create :: proc() -> Signature {
|
||||
return Signature{}
|
||||
}
|
||||
|
||||
signature_set :: proc(signature: ^Signature, type: ComponentType) {
|
||||
signature^ += {cast(int)type}
|
||||
}
|
||||
|
||||
signature_unset :: proc(signature: ^Signature, type: ComponentType) {
|
||||
signature^ -= {cast(int)type}
|
||||
}
|
||||
|
||||
signature_clear :: proc(signature: ^Signature) {
|
||||
signature^ = {}
|
||||
}
|
||||
65
ecs/system_manager.odin
Normal file
65
ecs/system_manager.odin
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package ecs
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:log"
|
||||
|
||||
SystemBase :: struct {
|
||||
entities: map[EntityID]struct{}, // Treat as set
|
||||
}
|
||||
|
||||
SystemManager :: struct {
|
||||
signatures: map[typeid]Signature,
|
||||
systems: map[typeid]^SystemBase
|
||||
}
|
||||
|
||||
system_manager_create :: proc() -> SystemManager {
|
||||
system_manager := SystemManager{
|
||||
signatures = make(map[typeid]Signature, context.allocator),
|
||||
systems = make(map[typeid]^SystemBase, context.allocator),
|
||||
}
|
||||
|
||||
return system_manager
|
||||
}
|
||||
|
||||
system_manager_register_system :: proc($T: typeid, system_manager: ^SystemManager) -> ^T {
|
||||
log.assertf(T not_in system_manager.systems, "Registering system more than once")
|
||||
log.assertf(intrinsics.type_has_field(T, "entities"), "Registering non system type")
|
||||
|
||||
system := new(T, context.allocator)
|
||||
system.entities = make(map[EntityID]struct{})
|
||||
system_manager.systems[T] = system
|
||||
|
||||
return system
|
||||
}
|
||||
|
||||
system_manager_set_signature :: proc($T: typeid, system_manager: ^SystemManager, signature: Signature) {
|
||||
log.assertf(T in system_manager.systems, "System used before registered")
|
||||
system_manager.signatures[T] = signature
|
||||
}
|
||||
|
||||
system_manager_destroy_entity :: proc(system_manager: ^SystemManager, entity_id: EntityID) {
|
||||
for _, system in system_manager.systems {
|
||||
delete_key(&system.entities, entity_id)
|
||||
}
|
||||
}
|
||||
|
||||
system_manager_change_entity_signature :: proc(system_manager: ^SystemManager, entity_id: EntityID, entity_signature: Signature) {
|
||||
for type, system in system_manager.systems {
|
||||
system_signature := system_manager.signatures[type]
|
||||
if (entity_signature & system_signature) == system_signature {
|
||||
system.entities[entity_id] = {}
|
||||
} else {
|
||||
delete_key(&system.entities, entity_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
system_manager_destroy :: proc(system_manager: ^SystemManager) {
|
||||
for _, system in system_manager.systems {
|
||||
delete(system.entities)
|
||||
free(system)
|
||||
}
|
||||
|
||||
delete(system_manager.systems)
|
||||
delete(system_manager.signatures)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue