From ac0ca8ee3969e332f9408c6a99369b0a8297dfe8 Mon Sep 17 00:00:00 2001 From: bloeys Date: Sat, 24 Sep 2022 23:20:08 +0400 Subject: [PATCH] Generational indices+get/free entity+free list --- entity/entity.go | 34 ++++++++++++++++++++ entity/registry.go | 79 ++++++++++++++++++++++++++++++++++++++++------ main.go | 64 +++++++++++++++++++------------------ 3 files changed, 136 insertions(+), 41 deletions(-) diff --git a/entity/entity.go b/entity/entity.go index 00ec852..a628323 100755 --- a/entity/entity.go +++ b/entity/entity.go @@ -1,5 +1,19 @@ package entity +type EntityFlag byte + +const ( + EntityFlag_Unknown EntityFlag = 0 + EntityFlag_Dead EntityFlag = 1 << (iota - 1) + EntityFlag_Alive +) + +const ( + GenerationShiftBits = 64 - 8 + FlagsShiftBits = 64 - 16 + IndexBitMask = 0x00_00_FFFF_FFFF_FFFF +) + type Entity struct { // Byte 1: Generation; Byte 2: Flags; Bytes 3-8: Index @@ -7,6 +21,26 @@ type Entity struct { Comps []Comp } +func GetGeneration(id uint64) byte { + return byte(id >> GenerationShiftBits) +} + +func GetFlags(id uint64) byte { + return byte(id >> FlagsShiftBits) +} + +func GetIndex(id uint64) uint64 { + return id & IndexBitMask +} + +func (e *Entity) HasFlag(ef EntityFlag) bool { + return GetFlags(e.ID)&byte(ef) > 0 +} + +func NewEntityId(generation, flags byte, index uint64) uint64 { + return index | (uint64(generation) << GenerationShiftBits) | (uint64(flags) << FlagsShiftBits) +} + type Comp interface { Name() string } diff --git a/entity/registry.go b/entity/registry.go index 2185fb2..a0dcaca 100755 --- a/entity/registry.go +++ b/entity/registry.go @@ -1,30 +1,89 @@ package entity -import "github.com/bloeys/nmage/assert" +import ( + "github.com/bloeys/nmage/assert" +) + +type freeListitem struct { + EntityIndex uint64 + nextFree *freeListitem +} type Registry struct { EntityCount uint64 Entities []Entity + FreeList *freeListitem } func (r *Registry) NewEntity() *Entity { assert.T(r.EntityCount < uint64(len(r.Entities)), "Can not add more entities to registry because it is full") - for i := 0; i < len(r.Entities); i++ { + entityToUseIndex := uint64(0) + var entityToUse *Entity = nil - // @TODO: Implement generational indices - e := &r.Entities[i] - if e.ID == 0 { - r.EntityCount++ - e.ID = uint64(i) + 1 + if r.FreeList != nil { - assert.T(e.ID != 0, "Entity ID must not be zero") - return e + entityToUseIndex = r.FreeList.EntityIndex + entityToUse = &r.Entities[entityToUseIndex] + r.FreeList = r.FreeList.nextFree + } else { + + for i := 0; i < len(r.Entities); i++ { + + e := &r.Entities[i] + if GetFlags(e.ID) != byte(EntityFlag_Unknown) && !e.HasFlag(EntityFlag_Dead) { + continue + } + + entityToUse = e + entityToUseIndex = uint64(i) + break } } - panic("failed to create new entity because we did not find a free spot in the registry. Why did the assert not go off?") + if entityToUse == nil { + panic("failed to create new entity because we did not find a free spot in the registry. Why did the assert not go off?") + } + + r.EntityCount++ + entityToUse.ID = NewEntityId(GetGeneration(entityToUse.ID)+1, byte(EntityFlag_Alive), entityToUseIndex) + assert.T(entityToUse.ID != 0, "Entity ID must not be zero") + return entityToUse +} + +func (r *Registry) GetEntity(id uint64) *Entity { + + index := GetIndex(id) + gen := GetGeneration(id) + + e := &r.Entities[index] + eGen := GetGeneration(e.ID) + + if gen != eGen { + return nil + } + + return e +} + +func (r *Registry) FreeEntity(id uint64) { + + e := r.GetEntity(id) + if e == nil { + return + } + + r.EntityCount-- + eIndex := GetIndex(e.ID) + + e.Comps = []Comp{} + e.ID = NewEntityId(GetGeneration(e.ID), byte(EntityFlag_Dead), eIndex) + + r.FreeList = &freeListitem{ + EntityIndex: eIndex, + nextFree: r.FreeList, + } } func NewRegistry(size uint32) *Registry { diff --git a/main.go b/main.go index 4873fbb..db3f561 100755 --- a/main.go +++ b/main.go @@ -8,7 +8,9 @@ import ( "github.com/bloeys/nmage/assets" "github.com/bloeys/nmage/camera" "github.com/bloeys/nmage/engine" + "github.com/bloeys/nmage/entity" "github.com/bloeys/nmage/input" + "github.com/bloeys/nmage/level" "github.com/bloeys/nmage/logging" "github.com/bloeys/nmage/materials" "github.com/bloeys/nmage/meshes" @@ -52,45 +54,45 @@ type OurGame struct { ImGUIInfo nmageimgui.ImguiInfo } -// type TransformComp struct { -// Pos *gglm.Vec3 -// Rot *gglm.Quat -// Scale *gglm.Vec3 -// } +type TransformComp struct { + Pos *gglm.Vec3 + Rot *gglm.Quat + Scale *gglm.Vec3 +} -// func (t TransformComp) Name() string { -// return "Transform Component" -// } +func (t TransformComp) Name() string { + return "Transform Component" +} -// func Test() { +func Test() { -// lvl := level.NewLevel("test level", 1000) -// e := lvl.Registry.NewEntity() + lvl := level.NewLevel("test level", 1000) + e1 := lvl.Registry.NewEntity() -// trComp := entity.GetComp[*TransformComp](e) -// fmt.Println("Got comp 1:", trComp) + trComp := entity.GetComp[*TransformComp](e1) + fmt.Println("Got comp 1:", trComp) -// e.Comps = append(e.Comps, &TransformComp{ -// Pos: gglm.NewVec3(0, 0, 0), -// Rot: gglm.NewQuatEulerXYZ(0, 0, 0), -// Scale: gglm.NewVec3(0, 0, 0), -// }, &TransformComp{ -// Pos: gglm.NewVec3(0, 0, 0), -// Rot: gglm.NewQuatEulerXYZ(0, 0, 0), -// Scale: gglm.NewVec3(1, 1, 1), -// }) + e1.Comps = append(e1.Comps, &TransformComp{ + Pos: gglm.NewVec3(0, 0, 0), + Rot: gglm.NewQuatEulerXYZ(0, 0, 0), + Scale: gglm.NewVec3(0, 0, 0), + }, &TransformComp{ + Pos: gglm.NewVec3(0, 0, 0), + Rot: gglm.NewQuatEulerXYZ(0, 0, 0), + Scale: gglm.NewVec3(1, 1, 1), + }) -// trComp = entity.GetComp[*TransformComp](e) -// fmt.Println("Got comp 2:", trComp) + trComp = entity.GetComp[*TransformComp](e1) + fmt.Println("Got comp 2:", trComp) -// trComps := entity.GetAllCompOfType[*TransformComp](e) -// fmt.Printf("Got comp 3: %+v, %+v\n", trComps[0], trComps[1]) + trComps := entity.GetAllCompOfType[*TransformComp](e1) + fmt.Printf("Got comp 3: %+v, %+v\n", trComps[0], trComps[1]) -// fmt.Printf("Entity: %+v\n", e) -// fmt.Printf("Entity: %+v\n", lvl.Registry.NewEntity()) -// fmt.Printf("Entity: %+v\n", lvl.Registry.NewEntity()) -// fmt.Printf("Entity: %+v\n", lvl.Registry.NewEntity()) -// } + fmt.Printf("Entity: %+v\n", e1) + fmt.Printf("Entity: %+v\n", lvl.Registry.NewEntity()) + fmt.Printf("Entity: %+v\n", lvl.Registry.NewEntity()) + fmt.Printf("Entity: %+v\n", lvl.Registry.NewEntity()) +} func main() {