Generational indices+get/free entity+free list

This commit is contained in:
bloeys
2022-09-24 23:20:08 +04:00
parent 35ff496a9a
commit ac0ca8ee39
3 changed files with 136 additions and 41 deletions

View File

@ -1,5 +1,19 @@
package entity 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 { type Entity struct {
// Byte 1: Generation; Byte 2: Flags; Bytes 3-8: Index // Byte 1: Generation; Byte 2: Flags; Bytes 3-8: Index
@ -7,6 +21,26 @@ type Entity struct {
Comps []Comp 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 { type Comp interface {
Name() string Name() string
} }

View File

@ -1,30 +1,89 @@
package entity package entity
import "github.com/bloeys/nmage/assert" import (
"github.com/bloeys/nmage/assert"
)
type freeListitem struct {
EntityIndex uint64
nextFree *freeListitem
}
type Registry struct { type Registry struct {
EntityCount uint64 EntityCount uint64
Entities []Entity Entities []Entity
FreeList *freeListitem
} }
func (r *Registry) NewEntity() *Entity { func (r *Registry) NewEntity() *Entity {
assert.T(r.EntityCount < uint64(len(r.Entities)), "Can not add more entities to registry because it is full") assert.T(r.EntityCount < uint64(len(r.Entities)), "Can not add more entities to registry because it is full")
entityToUseIndex := uint64(0)
var entityToUse *Entity = nil
if r.FreeList != nil {
entityToUseIndex = r.FreeList.EntityIndex
entityToUse = &r.Entities[entityToUseIndex]
r.FreeList = r.FreeList.nextFree
} else {
for i := 0; i < len(r.Entities); i++ { for i := 0; i < len(r.Entities); i++ {
// @TODO: Implement generational indices
e := &r.Entities[i] e := &r.Entities[i]
if e.ID == 0 { if GetFlags(e.ID) != byte(EntityFlag_Unknown) && !e.HasFlag(EntityFlag_Dead) {
r.EntityCount++ continue
e.ID = uint64(i) + 1 }
entityToUse = e
entityToUseIndex = uint64(i)
break
}
}
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
}
assert.T(e.ID != 0, "Entity ID must not be zero")
return e return e
} }
func (r *Registry) FreeEntity(id uint64) {
e := r.GetEntity(id)
if e == nil {
return
} }
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--
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 { func NewRegistry(size uint32) *Registry {

64
main.go
View File

@ -8,7 +8,9 @@ import (
"github.com/bloeys/nmage/assets" "github.com/bloeys/nmage/assets"
"github.com/bloeys/nmage/camera" "github.com/bloeys/nmage/camera"
"github.com/bloeys/nmage/engine" "github.com/bloeys/nmage/engine"
"github.com/bloeys/nmage/entity"
"github.com/bloeys/nmage/input" "github.com/bloeys/nmage/input"
"github.com/bloeys/nmage/level"
"github.com/bloeys/nmage/logging" "github.com/bloeys/nmage/logging"
"github.com/bloeys/nmage/materials" "github.com/bloeys/nmage/materials"
"github.com/bloeys/nmage/meshes" "github.com/bloeys/nmage/meshes"
@ -52,45 +54,45 @@ type OurGame struct {
ImGUIInfo nmageimgui.ImguiInfo ImGUIInfo nmageimgui.ImguiInfo
} }
// type TransformComp struct { type TransformComp struct {
// Pos *gglm.Vec3 Pos *gglm.Vec3
// Rot *gglm.Quat Rot *gglm.Quat
// Scale *gglm.Vec3 Scale *gglm.Vec3
// } }
// func (t TransformComp) Name() string { func (t TransformComp) Name() string {
// return "Transform Component" return "Transform Component"
// } }
// func Test() { func Test() {
// lvl := level.NewLevel("test level", 1000) lvl := level.NewLevel("test level", 1000)
// e := lvl.Registry.NewEntity() e1 := lvl.Registry.NewEntity()
// trComp := entity.GetComp[*TransformComp](e) trComp := entity.GetComp[*TransformComp](e1)
// fmt.Println("Got comp 1:", trComp) fmt.Println("Got comp 1:", trComp)
// e.Comps = append(e.Comps, &TransformComp{ e1.Comps = append(e1.Comps, &TransformComp{
// Pos: gglm.NewVec3(0, 0, 0), Pos: gglm.NewVec3(0, 0, 0),
// Rot: gglm.NewQuatEulerXYZ(0, 0, 0), Rot: gglm.NewQuatEulerXYZ(0, 0, 0),
// Scale: gglm.NewVec3(0, 0, 0), Scale: gglm.NewVec3(0, 0, 0),
// }, &TransformComp{ }, &TransformComp{
// Pos: gglm.NewVec3(0, 0, 0), Pos: gglm.NewVec3(0, 0, 0),
// Rot: gglm.NewQuatEulerXYZ(0, 0, 0), Rot: gglm.NewQuatEulerXYZ(0, 0, 0),
// Scale: gglm.NewVec3(1, 1, 1), Scale: gglm.NewVec3(1, 1, 1),
// }) })
// trComp = entity.GetComp[*TransformComp](e) trComp = entity.GetComp[*TransformComp](e1)
// fmt.Println("Got comp 2:", trComp) fmt.Println("Got comp 2:", trComp)
// trComps := entity.GetAllCompOfType[*TransformComp](e) trComps := entity.GetAllCompOfType[*TransformComp](e1)
// fmt.Printf("Got comp 3: %+v, %+v\n", trComps[0], trComps[1]) fmt.Printf("Got comp 3: %+v, %+v\n", trComps[0], trComps[1])
// fmt.Printf("Entity: %+v\n", e) 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()) 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() { func main() {