require 'honey.std' local glfw = honey.glfw local gl = honey.gl local Vec3 = honey.Vec3 local Mat4 = honey.Mat4 local Quaternion = honey.Quaternion local ecs = honey.ecs local systems = honey.standardSystems local ode = honey.ode local nvg = honey.nvg -- initialize honey local window = honey.init() -- setup vector graphics local vg = nvg.CreateContext() local november = nvg.CreateFont(vg, "November", "assets/november.regular.ttf") local vw, vh = 640, 480 -- setup ecs local edb = ecs.EntityDb() local sdb = ecs.SystemDb() local camera = edb:createEntity() edb:addComponent(camera, "camera", { projection=Mat4():perspective(math.rad(45), 640/480, 0.1, 100), }) edb:addComponent(camera, "transform", { matrix=Mat4():identity():translate(Vec3{0, 0, -6}), }) sdb:addSystem{ update = function(self, dt) local transform = edb:getComponent(camera, "transform") transform.matrix:translate(Vec3{0, 0, -0.2*math.sin(math.pi * glfw.GetTime())}) end } sdb:addSystem{ update = function(self, dt) local entities = self.db:queryComponent("transform") -- prepare transforms for id, transform in pairs(entities) do transform._visited = false end -- helper function local function recursiveTransform(transform) if transform._visited then return transform._matrix end if not transform.parent then transform._matrix = transform.matrix else local parentTransform = self.db:getComponent(transform.parent, "transform") local parentMatrix = recursiveTransform(parentTransform) transform._matrix = parentMatrix * transform.matrix end transform._visited = true return transform._matrix end -- compute transforms for id, transform in pairs(entities) do recursiveTransform(transform) end end, db = edb, priority = 0, } sdb:addSystem{ camera = camera, update = function(self, dt) local cameraParams = self.db:getComponent(camera, "camera") local cameraTransform = self.db:getComponent(camera, "transform") local view if cameraTransform then view = cameraTransform._matrix else view = Mat4():identity() end local entities = self.db:queryComponent("renderMesh") for entity, tbl in pairs(entities) do -- get shader local shader = honey.shader.loadShader(tbl.shader.vertex, tbl.shader.fragment) shader:use() -- bind textures local texOffset = 0 for name, texTbl in pairs(tbl.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 local query = self.db:getComponent(entity, "transform") local model = (query and query._matrix) or Mat4():identity() shader:configure{ float={ time=glfw.GetTime(), }, matrix={ view=view, projection=cameraParams.projection, model=model, }, } -- draw mesh local mesh = honey.mesh.loadMesh(tbl.mesh.filename, tbl.mesh.index) mesh:drawElements() -- unbind textures for i=0,texOffset-1 do gl.BindTexture(gl.TEXTURE_2D + i, 0) end end end, db = edb, priority = 99, } local id = edb:createEntity() edb:addComponent(id, "renderMesh", { textures = { ourTexture={ filename="77155.png" }, }, shader = { vertex="vertex.glsl", fragment="fragment.glsl" }, mesh = { filename="assets/icosahedron.obj", index=1 }, }) edb:addComponent(id, "transform", { matrix = Mat4():identity():rotateZ(math.rad(45)), }) sdb:addSystem{ update = function(self, dt) local transform = edb:getComponent(id, "transform") transform.matrix:rotateZ(math.pi * dt) end, } local id2 = edb:createEntity() edb:addComponent(id2, "renderMesh", { shader = { vertex="vertex.glsl", fragment="fragment.glsl" }, mesh = { filename="assets/tetrahedron.obj", index=1 }, }) edb:addComponent(id2, "transform", { parent=id, matrix=Mat4():identity():translate(Vec3{0, 2, 0}), }) -- close window on ESCAPE key window:setKeyCallback(function(_, key, scancode, action) if action == glfw.PRESS then if key == glfw.KEY_ESCAPE then window:setShouldClose(true) end end end) -- resize window correctly window:setFramebufferSizeCallback(function(_, width, height) gl.Viewport(0, 0, width, height) local cameraParams = edb:getComponent(camera, "camera") cameraParams.projection:perspectiveResize(width/height) vw, vh = width, height end) -- averager (for fps) function averager(memory) local buf = {} local avg = 0 for i=1,memory do table.insert(buf, 0) end return function(value) table.insert(buf, value) local val = table.remove(buf, 1) avg = avg + value - val return avg / memory end end local fpsAverage = averager(200) -- main loop honey.loop(window, 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) sdb: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(fpsAverage(1/dt)))) nvg.BeginPath(vg) nvg.MoveTo(vg, 50, 50) nvg.LineTo(vg, 50, 100) nvg.LineTo(vg, 100, 50) nvg.Stroke(vg) nvg.EndFrame(vg) end) -- clean up honey.terminate()