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 sys = honey.standardSystems local dispatch = sys.dispatch 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 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 = ecs.EntityDb() local systems = ecs.SystemDb(entities) systems:addSystem(sys.transform) systems:addSystem(sys.renderCamera) systems:addSystem(sys.script) systems:addSystem(sys.collision, {space=space}) systems:addSystem(sys.physics, {space=space, world=world}) package.loaded['baseRotationScript'] = function(entities, id, dt) local transform = entities:getComponent(id, "transform") transform.matrix:rotateZ(math.pi * dt) end package.loaded['cameraRotationScript'] = function(entities, id, dt) local transform = entities:getComponent(id, "transform") local z = entities:getComponent(id, "z") transform.matrix :identity() :translate(Vec3{0, 0, z.value + math.sin(math.pi * glfw.GetTime())}) end package.loaded['cameraKeyHandler'] = function(entities, id, data) local z = entities:getComponent(id, "z") if data.key == glfw.KEY_W then z.value = z.value + 1 elseif data.key == glfw.KEY_S then z.value = z.value - 1 end end function setupEntities() local plane = entities:createEntity() entities:addComponents(plane, { transform = { matrix = Mat4() :identity() :rotateZ(math.rad(5)) }, collision = { class = "plane", }, }) 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 }, }, transform = { 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, }, 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 }, }, transform = { 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" } }, }, }) local capsule = entities:createEntityWithComponents{ transform = { matrix = Mat4():identity():translate(Vec3{0,10,0}) }, collision = { class = "capsule", radius = 1, length = 2, }, physics = { mass = { class = "capsule", density = 1, direction = 3, radius = 1, length = 2, }, }, renderMesh = { mesh = { filename="assets/capsule.obj", index=1 }, shader = { vertex="vertex.glsl", fragment="fragment.glsl" }, }, } local camera = entities:createEntity() entities:addComponents(camera, { camera={ projection=Mat4():perspective(math.rad(45), 640/480, 0.1, 100), render="screen", }, transform={ matrix=Mat4():identity():rotateX(math.rad(-20)):translate(Vec3{0, 10, 30}), }, z = {value=-60}, onKey = { script="cameraKeyHandler", }, onWindowResize = { script = "cameraHandleResize" }, }) end setupEntities() -- close window on ESCAPE key window:setKeyCallback(function(_, key, scancode, action) dispatch(entities, "onKey", {key=key, scancode=scancode, action=action}) if action == glfw.PRESS then if key == glfw.KEY_ESCAPE then window:setShouldClose(true) elseif key == glfw.KEY_SPACE then entities:save("save") elseif key == glfw.KEY_L then entities:load("save") end end end) -- resize window correctly window:setFramebufferSizeCallback(function(_, width, height) gl.Viewport(0, 0, width, height) vw, vh = width, height dispatch(entities, "onWindowResize", { width=width, height=height }) end) package.loaded["cameraHandleResize"] = function(entities, id, data) local camera = entities:getComponent(id, "camera") camera.projection:perspectiveResize(data.width/data.height) end --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 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()