From 0d96ebc90c9740e7e66a70aa11168b11f49d220b Mon Sep 17 00:00:00 2001 From: sanine-a Date: Fri, 12 May 2023 14:10:48 -0500 Subject: run working program with refactored code --- honey/asset/mesh.lua | 12 ++- honey/asset/shader.lua | 4 +- honey/ecs/ecs.lua | 6 +- honey/ecs/node.lua | 77 ++++++++--------- honey/ecs/render.lua | 144 +++++++++++++----------------- honey/ecs/script.lua | 28 +++--- honey/init.lua | 26 ++---- honey/notes.md | 1 + honey/std.lua | 10 +-- main.lua | 231 ++++++++++--------------------------------------- 10 files changed, 183 insertions(+), 356 deletions(-) diff --git a/honey/asset/mesh.lua b/honey/asset/mesh.lua index 876c5e4..5e0a26d 100644 --- a/honey/asset/mesh.lua +++ b/honey/asset/mesh.lua @@ -136,14 +136,20 @@ end --===== builtin meshes =====-- -function createBuiltins() - cache["builtin.quad"] = {Mesh( +local function builtin(name, vertices, indices) + local vao, vbo, ebo, count = createMesh(vertices, indices) + cache[name] = { {vao=vao, vbo=vbo, ebo=ebo, count=count } } +end + +init = function() + builtin( + "builtin.quad", { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1 }, { 0, 1, 3, 0, 3, 2 } - )} + ) end diff --git a/honey/asset/shader.lua b/honey/asset/shader.lua index 0e8c93a..a23c17b 100644 --- a/honey/asset/shader.lua +++ b/honey/asset/shader.lua @@ -35,8 +35,8 @@ local function buildProgram(sources) local shader = compileShader(readFile(sources.vertex), gl.VERTEX_SHADER) table.insert(shaders, shader) end - if sources.vertex then - local shader = compileShader(readFile(sources.vertex), gl.VERTEX_SHADER) + if sources.fragment then + local shader = compileShader(readFile(sources.fragment), gl.FRAGMENT_SHADER) table.insert(shaders, shader) end diff --git a/honey/ecs/ecs.lua b/honey/ecs/ecs.lua index d7eb4ac..0c84041 100644 --- a/honey/ecs/ecs.lua +++ b/honey/ecs/ecs.lua @@ -77,11 +77,9 @@ end -- load database from file function EntityDb.load(self, filename) - print(collectgarbage("count")) self.entities = {} self.components = {} collectgarbage() - print(collectgarbage("count")) local env = { Entity = function(id, components) self:createEntity(id) @@ -320,7 +318,6 @@ function SystemDb.update(self, dt) -- check for dangling dependencies local dangle = "" for dependency in pairs(self.dangling) do - print(dependency) dangle = dangle .. string.format( "unresolved dangling dependency: %s\n", dependency @@ -330,11 +327,10 @@ function SystemDb.update(self, dt) error(dangle) end - for _, name in ipairs(self.sort) do local system = self.systems[name] local params = self.params[name] - system.f(self.db, dt, params) + system.f(self.entityDb, dt, params) end end diff --git a/honey/ecs/node.lua b/honey/ecs/node.lua index 4953af5..6a47a1f 100644 --- a/honey/ecs/node.lua +++ b/honey/ecs/node.lua @@ -1,52 +1,47 @@ +local ecs = require 'honey.ecs.ecs' + local module = {} setmetatable(module, {__index=_G}) setfenv(1, module) --===== transform cascading =====-- -system = function(params) - return { - db = params.db, - - priority = 2, - update = function(self, dt) - local nodes = self.db:queryComponent("node") - - -- prepare nodes - for id, node in pairs(nodes) do - node._visited = false - node._child = nil - end - - -- helper function - local function recursiveTransform(id, node) - if node._visited then - return node._matrix - end - - if not node.parent then - node._matrix = node.matrix - else - local parentNode = self.db:getComponent(node.parent, "node") - local parentMatrix = recursiveTransform(node.parent, parentNode) - node._matrix = parentMatrix * node.matrix - if node.name then - if not parentNode._child then parentNode._child = {} end - parentNode._child[node.name] = honey.ecs.Accessor(self.db, id) - end - end - node._visited = true - return node._matrix - end - - -- compute nodes - for id, node in pairs(nodes) do - recursiveTransform(id, node) - end - end, - } +local function recursiveTransform(db, id, node) + if node._visited then + return node._matrix + end + + if not node.parent then + node._matrix = node.matrix + else + local parentNode = db:getComponent(node.parent, "node") + local parentMatrix = recursiveTransform(db, node.parent, parentNode) + node._matrix = parentMatrix * node.matrix + if node.name then + if not parentNode._child then parentNode._child = {} end + parentNode._child[node.name] = ecs.Accessor(db, id) + end + end + node._visited = true + return node._matrix end +system = {ecs.System("node", function(db, dt, params) + local nodes = db:queryComponent("node") + + -- prepare nodes + for id, node in pairs(nodes) do + node._visited = false + node._child = nil + end + + -- compute nodes + for id, node in pairs(nodes) do + recursiveTransform(db, id, node) + end +end)} + + return module diff --git a/honey/ecs/render.lua b/honey/ecs/render.lua index 1e41e7a..8cca6cf 100644 --- a/honey/ecs/render.lua +++ b/honey/ecs/render.lua @@ -1,3 +1,9 @@ +local ecs = require 'honey.ecs.ecs' + +local image = require 'honey.asset.image' +local shader = require 'honey.asset.shader' +local mesh = require 'honey.asset.mesh' + local glm = require 'honey.glm' local Vec3 = glm.Vec3 local Mat4 = glm.Mat4 @@ -5,103 +11,77 @@ local Mat4 = glm.Mat4 local gl = honey.gl local glfw = honey.glfw +local node = require 'honey.ecs.node' + local module = {} setmetatable(module, {__index=_G}) setfenv(1, module) --===== rendering =====-- -function draw(model, view, projection, textures, shader, mesh) - shader:use() +local function drawMesh(program, uniforms, matrices, vao, count) + gl.UseProgram(program) - -- bind textures - local texOffset = 0 - for name, texTbl in pairs(textures or {}) do - local texture = honey.image.loadImage(texTbl.filename, texTbl.params) - gl.BindTexture(gl.TEXTURE_2D + texOffset, texture.texture) - shader:setInt(name, texOffset) - texOffset = texOffset + 1 - end - - -- configure default uniforms - shader:configure{ - float={ - time=glfw.GetTime(), - }, + -- bind matrices + shader.configure(program, { matrix={ - view=view, - projection=projection, - model=model, + model = matrices.model, + view = matrices.view, + projection = matrices.projection, }, - } + }) - -- draw mesh - mesh:drawElements() - - -- unbind textures - for i=0,texOffset-1 do - gl.BindTexture(gl.TEXTURE_2D + i, 0) + -- bind textures + local offset = 0 + for name, tbl in pairs(uniforms.textures or {}) do + local tex = image.get(tbl.filename) + gl.BindTexture(gl.TEXTURE_2D + offset, tex) + shader.setInt(program, name, offset) + offset = offset+1 end + + -- configure additional uniforms + shader.configure(program, uniforms) + + -- render mesh + gl.BindVertexArray(vao) + gl.DrawElements(gl.TRIANGLES, count, gl.UNSIGNED_INT, 0) end -system = function(params) - return { - db = params.db, - priority = params.priority or 99, - update = function(self, dt) - for id, camera in pairs(self.db:queryComponent("camera")) do - local projection = camera.projection - local cameraTransform = self.db:getComponent(id, "node") - local view = Mat4() - if cameraTransform then - honey.glm.mat4_inv(cameraTransform._matrix.data, view.data) - else - view:identity() - end + +local render = ecs.System("render", function(db, dt, p) + for id, camera in pairs(db:queryComponent("camera")) do + -- get camera's view and projection matrices + local projection = camera.projection + local view = Mat4() + local node = db:getComponent(id, "node") + if node then + honey.glm.mat4_inv(node._matrix.data, view.data) + else + view:identity() + end - local entities = self.db:queryComponent("renderMesh") - for entity, tbl in pairs(entities) do - -- get model - local node = self.db:getComponent(entity, "node") - local model = - (node and node._matrix) or - Mat4():identity() - -- get shader - if not tbl.shader then - print(node) - print(node and node._matrix) - print(node and node.name) - print(node and node.parent) - for k, v in pairs(tbl) do - print(k, v) - end - end - local shader = honey.shader.loadShader( - tbl.shader.vertex, tbl.shader.fragment - ) - -- get mesh - local mesh = honey.mesh.loadCached( - tbl.mesh.filename, tbl.mesh.index, tbl.mesh.debug - ) - draw(model, view, projection, tbl.textures, shader, mesh) - end - - entities = self.db:queryComponent("renderQuad") - local quadmesh = honey.mesh.loadCached("builtin.quad", 1) - for entity, tbl in pairs(entities) do - -- get model - local model = Mat4():identity() - -- get shader - local shader = honey.shader.loadShader( - tbl.shader.vertex, tbl.shader.fragment - ) - draw(model, view, projection, tbl.textures, shader, quadmesh) - end - end - end, - } -end + -- iterate over all scene meshes + local meshes = db:queryComponent("renderMesh") + for id, tbl in pairs(meshes) do + -- get model matrix + local node = db:getComponent(id, "node") + local model = (node and node._matrix) or Mat4():identity() + + -- get shader program + local program = shader.get(tbl.shader) + + -- get mesh + local vao, count = mesh.get(tbl.mesh.filename, tbl.mesh.index) + + -- draw c: + drawMesh(program, tbl.uniforms or {}, {view=view, model=model, projection=projection}, vao, count) + end + end +end) +render:addDependencies(node.system) +system = {render} return module diff --git a/honey/ecs/script.lua b/honey/ecs/script.lua index 9ae7d72..46af4fa 100644 --- a/honey/ecs/script.lua +++ b/honey/ecs/script.lua @@ -1,3 +1,6 @@ +local ecs = require 'honey.ecs.ecs' +local node = require 'honey.ecs.node' + local module = {} setmetatable(module, {__index=_G}) setfenv(1, module) @@ -16,8 +19,8 @@ end --===== dispatch messages to handlers =====-- -dispatch = function(entities, msg, data) - local query = entities:queryComponent(msg) +dispatch = function(db, msg, data) + local query = db:queryComponent(msg) for id, handler in pairs(query) do local f = getFunction(handler) f(entities, id, data) @@ -26,19 +29,16 @@ end --===== script system =====-- -system = function(params) - return { - db=params.db, - update=function(self, dt) - local entities = self.db:queryComponent("script") - for id, script in pairs(entities) do - local f = getFunction(script) - f(self.db, id, dt) - end - end - } -end +local script = ecs.System("script", function(db, dt, params) + local entities = db:queryComponent("script") + for id, script in pairs(entities) do + local f = getFunction(script) + f(db, id, dt) + end +end) +script:addDependencies(node.system) +system = {script} return module diff --git a/honey/init.lua b/honey/init.lua index d2f5279..7ceee2f 100644 --- a/honey/init.lua +++ b/honey/init.lua @@ -1,13 +1,10 @@ local glfw = honey.glfw local gl = honey.gl -local window = require 'honey.window' -local module = {} -setmetatable(module, {__index=_G}) -setfenv(1, module) +local mesh = require 'honey.asset.mesh' -function init(width, height, title) +function honey.init(width, height, title) local width = width or 640 local height = height or 480 local title = title or "honey3d" @@ -16,34 +13,29 @@ function init(width, height, title) glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE) glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 4) glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 1) - local window = honey.Window(width, height, title) - glfw.MakeContextCurrent(window.win) + honey.window = glfw.CreateWindow(width, height, title, glfw.monitor_NULL, glfw.window_NULL) + glfw.MakeContextCurrent(honey.window) gl.InitGlad() gl.Enable(gl.DEPTH_TEST) honey.ode.InitODE() - honey.mesh.createBuiltins() - - return window + mesh.init() end -function loop(window, update) +function honey.loop(update) local prevTime = 0 - while not window:shouldClose() do + while glfw.WindowShouldClose(honey.window) == glfw.FALSE do local time = glfw.GetTime() local dt = time - prevTime prevTime = time update(dt) - window:swapBuffers() + glfw.SwapBuffers(honey.window) glfw.PollEvents() end end -function terminate() +function honey.terminate() glfw.Terminate() end - - -return module diff --git a/honey/notes.md b/honey/notes.md index 16ccc49..863c019 100644 --- a/honey/notes.md +++ b/honey/notes.md @@ -9,6 +9,7 @@ assets are cached. each asset type has a module (e.g. `mesh`, `image`, `sound`, * `get(filename)` - cached load of the given file. throws error if it cannot be found * `forget(filename` - remove the cached copy, if any of the given file * `clearCache()` - clear the full cache for that asset type + * `init()` - optional, initializes any builtins that must come after opengl or other things individual assets may have additional functions. diff --git a/honey/std.lua b/honey/std.lua index 222798c..8b70699 100644 --- a/honey/std.lua +++ b/honey/std.lua @@ -1,15 +1,15 @@ local init = require 'honey.init' -honey.init = init.init -honey.loop = init.loop -honey.terminate = init.terminate - honey.asset = {} honey.asset.image = require 'honey.asset.image' honey.asset.mesh = require 'honey.asset.mesh' honey.asset.shader = require 'honey.asset.shader' -honey.ecs = require 'honey.ecs' + +honey.ecs = require 'honey.ecs.ecs' +honey.ecs.node = require 'honey.ecs.node' +honey.ecs.render = require 'honey.ecs.render' +honey.ecs.script = require 'honey.ecs.script' -- glm is so frequently used that we load it globally, not locally diff --git a/main.lua b/main.lua index 10ffebe..c251071 100644 --- a/main.lua +++ b/main.lua @@ -1,197 +1,54 @@ require 'honey.std' --- initialize honey -window = honey.init() -window:setInputMode(glfw.CURSOR, glfw.CURSOR_DISABLED) - --- setup vector graphics -local vg = nvg.CreateContext() -local november = nvg.CreateFont(vg, "November", "assets/november.regular.ttf") -local vw, vh = 640, 480 - --- setup physics -local space = ode.HashSpaceCreate(ode.Space0) -local world = ode.WorldCreate() -ode.WorldSetGravity(world, 0, -10, 0) -ode.WorldSetCFM(world, 1e-5) -local physicsGc = honey.util.gc_canary(function() - ode.SpaceDestroy(space) - ode.WorldDestroy(world) -end) - - --- setup ecs -local entities = honey.ecs.EntityDb() -local systems = honey.ecs.SystemDb(entities) -local script = honey.ecs.script -systems:addSystem(honey.ecs.node.system) -systems:addSystem(honey.ecs.render.system) -systems:addSystem(honey.ecs.script.system) -systems:addSystem(honey.ecs.collision.system, {space=space}) -systems:addSystem(honey.ecs.physics.system, {space=space, world=world}) - -function setupEntities() - local terrain = entities:createEntityWithComponents{ - node = { - matrix = Mat4():identity():translate(Vec3{4,0,0}), - }, - renderMesh = { - textures = { - ourTexture = { - filename = "assets/green+grass-1024x1024.jpg", - }, - }, - shader = { - vertex = "vertex.glsl", - fragment = "fragment.glsl", - }, - mesh = { filename = "assets/terrain.obj", index=1 }, - }, - collision = { - class = "trimesh", - filename = "assets/terrain.obj", - }, - } - --- local plane = entities:createEntity() --- entities:addComponents(plane, { --- node = { --- matrix = Mat4() --- :identity() --- :rotateZ(math.rad(5)) --- }, --- collision = { --- class = "plane", --- }, --- }) --- --- local planeMesh = entities:createEntityWithComponents{ --- node = { --- parent = plane, --- matrix = Mat4():identity():rotateX(0.5*math.pi):scale(Vec3{20,20,20}), --- }, --- renderMesh = { --- textures = { --- ourTexture={ --- filename="assets/green+grass-1024x1024.jpg" --- } --- }, --- shader = { vertex="vertex.glsl", fragment="fragment.glsl" }, --- mesh = { filename="builtin.quad", index=1 }, --- }, --- } - - - - local id = entities:createEntity() - entities:addComponents(id, { - renderMesh = { - textures = { - ourTexture={ filename="77155.png" } - }, - shader = { vertex="vertex.glsl", fragment="fragment.glsl" }, - mesh = { filename="assets/icosahedron.obj", index=1 }, - }, - node = { - matrix = Mat4() - :identity() - :translate(Vec3{0,1,0}) - :rotateZ(math.rad(45)), - }, - collision = { - class = "sphere", - radius = 1, - }, - physics = { - mass = { - class = "sphere", - density = 1, - radius = 1, - }, - surface = true, - velocity = Vec3{ 0, 0, 0 }, - angularVelocity = Vec3{ 0, 0, 0 }, - }, - }) - - local id2 = entities:createEntity() - entities:addComponents(id2, { - renderMesh = { - shader = { vertex="vertex.glsl", fragment="fragment.glsl" }, - --mesh = { filename="assets/tetrahedron.obj", index=1 }, - mesh = { filename="builtin.quad", index=1 }, - }, - node = { - parent=id, - matrix=Mat4():identity():translate(Vec3{0, 2, 0}), - }, - }) - - local quad = entities:createEntity() - entities:addComponents(quad, { - renderQuad = { - shader = { vertex="vertex.glsl", fragment="fragment.glsl" }, - textures = { ourTexture = { filename = "44d9a0ec1c18e6126a5e9d9d9317f5ac.png" } }, - }, - }) - - require('character')(entities) - - local skybox = entities:createEntityWithComponents{ - node = { - matrix = Mat4():identity():scale(Vec3{2,2,2}):rotateX(math.rad(90)) - }, - renderMesh = { - mesh = { - filename="assets/skybox.obj", - index=1, - }, - shader = { vertex="vertex.glsl", fragment="fragment.glsl" }, - textures = { - ourTexture = { - filename = "assets/skyboxsun5deg2_tn.jpg" - --filename = "assets/skybox.png" - } - }, - }, - } - - local misc = entities:createEntityWithComponents{ - onKey = { script = "scripts.loadSaveQuit" }, - onFramebufferSize = { script = "scripts.viewportResize" }, - } -end -setupEntities() +local gl = honey.gl - --- connect event handler scripts -window:bindEvents(entities) - ---entities:load("save") - - --- main loop -local time = 0 -honey.loop(window, function(dt) - time = time + dt - if time > 1 then - time = time-1 - print(collectgarbage("count")) - end +-- initialize honey +honey.init() + + +local db = honey.ecs.EntityDb() +local systems = honey.ecs.SystemDb(db) + +systems:addSystems(honey.ecs.node.system) +systems:addSystems(honey.ecs.render.system) +systems:addSystems(honey.ecs.script.system) + +-- camera +db:createEntityWithComponents{ + node = { + matrix = Mat4() + :identity() + :translate(Vec3{0,0,10}), + }, + camera = { + projection = Mat4() + :perspective(90, 640/480, 0.1, 1000) + }, +} + +-- mesh +db:createEntityWithComponents{ + node = { + matrix = Mat4() + :identity(), + }, + renderMesh = { + mesh = { + filename = "assets/dodecahedron.obj", + index = 1, + }, + textures = { + ourTexture = { filename = "assets/green-grass.jpg" }, + }, + shader = { vertex="vertex.glsl", fragment="fragment.glsl" }, + }, +} + +honey.loop(function(dt) gl.ClearColor(0.2, 0.4, 1.0, 1.0) gl.Clear(gl.COLOR_BUFFER_BIT + gl.DEPTH_BUFFER_BIT + gl.STENCIL_BUFFER_BIT) - - gl.Enable(gl.DEPTH_TEST) - gl.Disable(gl.CULL_FACE) - systems:update(dt) - nvg.BeginFrame(vg, vw, vh, 1.0) - nvg.StrokeColor(vg, nvg.RGBf(1, 1, 1)) - nvg.FontFace(vg, "November") - nvg.Text(vg, 50, 50, "fps: "..tostring(math.floor(1/dt))) - nvg.EndFrame(vg) end) - -- clean up honey.terminate() -- cgit v1.2.1