summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2023-03-16 12:12:39 -0500
committersanine <sanine.not@pm.me>2023-03-16 12:12:39 -0500
commitd94d9fb122e42264eca20bb037fe8a82290bd3e2 (patch)
tree7cb5b0ffd657ba69e193b4f2af6f1ddd3ec75ed2
parent7516044247663dabccc7db110703cfcf8545d95f (diff)
implement ecs node cascading
-rw-r--r--honey/ecs.lua97
-rw-r--r--honey/mat4.lua7
-rw-r--r--honey/std.lua17
-rw-r--r--honey/window.lua8
-rw-r--r--main.lua136
5 files changed, 217 insertions, 48 deletions
diff --git a/honey/ecs.lua b/honey/ecs.lua
index ef682b5..3bdf850 100644
--- a/honey/ecs.lua
+++ b/honey/ecs.lua
@@ -23,7 +23,8 @@ function Filter.new(_, operation, tbl)
end
end
- return function(entity)
+ return function(entity, _self)
+ local entity = _self or entity -- able to call as . or :
return Filter.check(self, entity)
end
end
@@ -97,4 +98,98 @@ function Filter.checkNOR(self, entity)
end
+--===== worlds =====--
+
+World = {}
+World.__index = World
+
+function World.new(_)
+ local self = {}
+ self.systems = {}
+ self.entities = {}
+ setmetatable(self, World)
+ return self
+end
+setmetatable(World, {__call=World.new})
+
+
+local function systemLt(a, b)
+ return (a.priority or 50) < (b.priority or 50)
+end
+function World.addSystem(self, system)
+ assert(system.update, "systems must have an 'update' key")
+ assert(system.filter, "systems must have a 'filter' key")
+ system.entities = {}
+ table.insert(self.systems, system)
+ table.sort(self.systems, systemLt)
+end
+
+
+local function addEntityToSystem(system, entity)
+ -- check if entity is already present
+ if system.entities[entity] then return end
+
+ if system.onAddEntity then
+ system.onAddEntity(entity)
+ end
+ system.entities[entity] = true
+end
+
+
+local function removeEntityFromSystem(system, entity)
+ -- check if entity is already absent
+ if not system.entities[entity] then return end
+
+ if system.onRemoveEntity then
+ system.onRemoveEntity(entity)
+ end
+ system.entities[entity] = nil
+end
+
+
+function World.addEntity(self, entity)
+ self.entities[entity] = true
+ for _, system in ipairs(self.systems) do
+ if system.filter(entity) then
+ addEntityToSystem(system, entity)
+ end
+ end
+end
+
+
+function World.reconfigureEntity(self, entity)
+ for _, system in ipairs(self.systems) do
+ if system.filter(entity) then
+ addEntityToSystem(system, entity)
+ else
+ removeEntityFromSystem(system, entity)
+ end
+ end
+end
+
+
+function World.removeEntity(self, entity)
+ self.entities[entity] = nil
+ for _, system in ipairs(self.systems) do
+ removeEntityFromSystem(system, entity)
+ end
+end
+
+
+function World.reconfigureAllEntities(self)
+ for entity in pairs(self.entities) do
+ self:reconfigureEntity(entity)
+ end
+end
+
+
+function World.update(self, dt)
+ for _, system in ipairs(self.systems) do
+ for entity in pairs(system.entities) do
+ system.update(entity, dt)
+ end
+ end
+end
+
+
return module
diff --git a/honey/mat4.lua b/honey/mat4.lua
index a97129c..8ea73d4 100644
--- a/honey/mat4.lua
+++ b/honey/mat4.lua
@@ -57,6 +57,13 @@ end
setmetatable(Mat4, {__call=Mat4.new})
+function Mat4.Identity()
+ local m = Mat4()
+ m:identity()
+ return m
+end
+
+
function Mat4.__index(self, key)
if type(key) == "number" then
return RowLookup(key, self.data)
diff --git a/honey/std.lua b/honey/std.lua
index ba86eb0..752df4d 100644
--- a/honey/std.lua
+++ b/honey/std.lua
@@ -1,16 +1,11 @@
local init = require 'honey.init'
-local window = require 'honey.window'
-local mesh = require 'honey.mesh'
-local Vec3 = require 'honey.vec3'
-local Mat4 = require 'honey.mat4'
-local Shader = require 'honey.shader'
honey.init = init.init
honey.terminate = init.terminate
-honey.Window = window.Window
-honey.mesh = mesh
-honey.Vec3 = Vec3
-honey.Mat4 = Mat4
-
-honey.Shader = Shader
+honey.ecs = require 'honey.ecs'
+honey.Mat4 = require 'honey.mat4'
+honey.mesh = require 'honey.mesh'
+honey.Shader = require 'honey.shader'
+honey.Vec3 = require 'honey.vec3'
+honey.Window = require 'honey.window'
diff --git a/honey/window.lua b/honey/window.lua
index 471d51e..fa777d0 100644
--- a/honey/window.lua
+++ b/honey/window.lua
@@ -1,7 +1,7 @@
-local window = {}
+local module = {}
local glfw = honey.glfw
-setmetatable(window, {__index=_G})
-setfenv(1, window)
+setmetatable(module, {__index=_G})
+setfenv(1, module)
Window = {}
@@ -157,4 +157,4 @@ function Window.swapBuffers(self)
end
-return window
+return module.Window
diff --git a/main.lua b/main.lua
index 831aa54..9be332b 100644
--- a/main.lua
+++ b/main.lua
@@ -5,21 +5,73 @@ local glfw = honey.glfw
local gl = honey.gl
local Vec3 = honey.Vec3
local Mat4 = honey.Mat4
+local ecs = honey.ecs
+
+
+-- camera matrices
+local camera = {
+ view=Mat4.Identity(),
+ projection=Mat4(),
+}
+camera.view:translate(Vec3{0, 0, -3})
+camera.projection:perspective(math.rad(45), 640/480, 0.1, 100)
+
+
+
+function recursiveComputeTransform(entity)
+ if entity._transformComputed then
+ return entity._transform
+ end
+ if entity.parent == false then
+ entity._transformComputed = true
+ entity._transform = entity.transform
+ return entity.transform
+ end
+
+ entity._transformComputed = true
+ local parentTransform = recursiveComputeTransform(entity.parent)
+ entity._transform = parentTransform * entity.transform
+ return entity._transform
+end
+
+local world = ecs.World()
+-- update transforms
+world:addSystem{
+ filter=ecs.Filter.AND{"transform", "parent"},
+ update=function(entity, dt)
+ recursiveComputeTransform(entity)
+ end,
+ priority=1,
+}
+world:addSystem{
+ filter=ecs.Filter.AND{"transform", "parent"},
+ update=function(entity, dt)
+ entity._transform = nil
+ entity._transformComputed = false
+ end,
+ priority=0,
+}
+
+-- render objects
+world:addSystem{
+ filter=ecs.Filter.AND{"mesh", "shader", "transform"},
+ update=function(entity, dt)
+ entity.shader:use()
+ entity.shader:setMatrix('model', entity._transform)
+ entity.shader:setMatrix('view', camera.view)
+ entity.shader:setMatrix('projection', camera.projection)
+ entity.mesh:drawElements()
+ end,
+ priority=99,
+}
-local window = honey.init()
-gl.Enable(gl.DEPTH_TEST)
-local model = Mat4()
-model:identity()
-local view = Mat4()
-view:identity()
-view:translate(Vec3{0, 0, -3})
+local window = honey.init()
+gl.Enable(gl.DEPTH_TEST)
-local projection = Mat4()
-projection:perspective(math.rad(45), 640/480, 0.1, 100)
local vertexShaderSource = [[
@@ -75,40 +127,60 @@ local octa = honey.mesh.loadFile("assets/octahedron.obj")[1]
local dodeca = honey.mesh.loadFile("assets/dodecahedron.obj")[1]
local icosa = honey.mesh.loadFile("assets/icosahedron.obj")[1]
-local meshes = { tetra, cube, octa, dodeca, icosa }
-local meshIndex = 1
-local mesh = meshes[1]
-window:setKeyCallback(function(_, key, scancode, action, mods)
- if action ~= glfw.PRESS then return end
+local tetraEntity = {
+ parent=false,
+ transform=Mat4.Identity(),
+ mesh=tetra,
+ shader=shader,
+}
+
+local cubeEntity = {
+ parent=tetraEntity,
+ transform=Mat4.Identity(),
+ mesh=cube,
+ shader=shader,
+}
+cubeEntity.transform:translate(Vec3{2, 0, 0})
+
+local octaEntity = {
+ parent=cubeEntity,
+ transform=Mat4.Identity(),
+ mesh=octa,
+ shader=shader,
+}
+octaEntity.transform:translate(Vec3{2, 0, 0})
+
- if key == glfw.KEY_SPACE then
- meshIndex = meshIndex + 1
- if meshIndex > #meshes then meshIndex = 1 end
- mesh = meshes[meshIndex]
- elseif key == glfw.KEY_ESCAPE then
- window:setShouldClose(true)
- end
-end)
+world:addEntity(tetraEntity)
+world:addEntity(cubeEntity)
+world:addEntity(octaEntity)
+
+
+
+local prevTime = 0
while not window:shouldClose() do
local time = glfw.GetTime()
- model:identity()
- model:rotateY(0.5 * math.pi * time)
- model:rotateX(0.05 * math.pi * time)
+ local dt = time - prevTime
+ prevTime = time
+
+ tetraEntity.transform:identity()
+ tetraEntity.transform:rotateY(0.5 * math.pi * time)
+ tetraEntity.transform:rotateX(0.05 * math.pi * time)
--model:scale(0.2 * Vec3{1, 1, 1})
- gl.ClearColor(0.2, 0.4, 1.0, 1.0)
- gl.Clear(gl.COLOR_BUFFER_BIT + gl.DEPTH_BUFFER_BIT)
+ cubeEntity.transform:identity()
+ cubeEntity.transform:translate(Vec3{2, 0, 0})
+ cubeEntity.transform:rotateY(0.3 * math.pi * time)
+ cubeEntity.transform:rotateX(0.08 * math.pi * time)
- shader:use()
- shader:setMatrix('model', model)
- shader:setMatrix('view', view)
- shader:setMatrix('projection', projection)
- mesh:drawElements()
+ gl.ClearColor(0.2, 0.4, 1.0, 1.0)
+ gl.Clear(gl.COLOR_BUFFER_BIT + gl.DEPTH_BUFFER_BIT)
+ world:update(dt)
window:swapBuffers()
glfw.PollEvents()