From d94d9fb122e42264eca20bb037fe8a82290bd3e2 Mon Sep 17 00:00:00 2001 From: sanine Date: Thu, 16 Mar 2023 12:12:39 -0500 Subject: implement ecs node cascading --- honey/ecs.lua | 97 ++++++++++++++++++++++++++++++++++++++- honey/mat4.lua | 7 +++ honey/std.lua | 17 +++---- honey/window.lua | 8 ++-- main.lua | 136 ++++++++++++++++++++++++++++++++++++++++++------------- 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() -- cgit v1.2.1