From e5553bb81c93cd76456d07d3a1d7e8bb1b249b6e Mon Sep 17 00:00:00 2001 From: sanine-a Date: Thu, 23 Mar 2023 13:35:37 -0500 Subject: implement skeleton of physics system --- assets/november.regular.ttf | Bin 0 -> 11056 bytes honey/ecs-systems.lua | 8 +-- honey/ecs.lua | 19 +++--- honey/mat4.lua | 6 ++ honey/quaternion.lua | 30 ++++++++++ honey/std.lua | 1 + main.lua | 139 ++++++++++++++++++++++++++++++-------------- 7 files changed, 146 insertions(+), 57 deletions(-) create mode 100644 assets/november.regular.ttf create mode 100644 honey/quaternion.lua diff --git a/assets/november.regular.ttf b/assets/november.regular.ttf new file mode 100644 index 0000000..778df78 Binary files /dev/null and b/assets/november.regular.ttf differ diff --git a/honey/ecs-systems.lua b/honey/ecs-systems.lua index a511b3d..237aa78 100644 --- a/honey/ecs-systems.lua +++ b/honey/ecs-systems.lua @@ -28,11 +28,11 @@ end -- update transforms transformCascade = { filter=ecs.Filter.AND{"transform", "parent"}, - preUpdate=function(entity) + prepareEntity=function(self, entity) entity._transform = nil entity._transformComputed = false end, - update=function(entity, dt) + update=function(self, entity, dt) recursiveComputeTransform(entity) end, priority=98, @@ -45,7 +45,7 @@ function renderCam(camera, priority) local priority = priority or 99 return { filter=ecs.Filter.AND{"mesh", "shader", "transform"}, - update=function(entity, dt) + update=function(self, entity, dt) entity.shader:use() entity.shader:configure{ matrix={ @@ -64,7 +64,7 @@ end update = { filter=ecs.Filter.AND{"update"}, - update=function(entity, dt) + update=function(self, entity, dt) entity.update(entity, dt) end, priority=50, diff --git a/honey/ecs.lua b/honey/ecs.lua index 23bb0ba..d611b37 100644 --- a/honey/ecs.lua +++ b/honey/ecs.lua @@ -123,6 +123,9 @@ function Level.addSystem(self, system) system.entities = {} table.insert(self.systems, system) table.sort(self.systems, systemLt) + if system.setup then + system.setup(system) + end end @@ -131,7 +134,7 @@ local function addEntityToSystem(system, id, entity) if system.entities[id] then return end if system.onAddEntity then - system.onAddEntity(id, entity) + system:onAddEntity(id, entity) end system.entities[id] = true end @@ -142,7 +145,7 @@ local function removeEntityFromSystem(system, id, entity) if not system.entities[id] then return end if system.onRemoveEntity then - system.onRemoveEntity(id, entity) + system:onRemoveEntity(id, entity) end system.entities[id] = nil end @@ -195,20 +198,20 @@ end function Level.update(self, dt) for _, system in ipairs(self.systems) do if system.preUpdate then + system:preUpdate() + end + if system.prepareEntity then for id in pairs(system.entities) do local entity = self.entities[id] - system.preUpdate(entity) + system:prepareEntity(entity) end end for id in pairs(system.entities) do local entity = self.entities[id] - system.update(entity, dt) + system:update(entity, dt) end if system.postUpdate then - for id in pairs(system.entities) do - local entity = self.entities[id] - system.postUpdate(entity) - end + system:postUpdate() end end end diff --git a/honey/mat4.lua b/honey/mat4.lua index 6ea17fd..cdc5e7d 100644 --- a/honey/mat4.lua +++ b/honey/mat4.lua @@ -114,6 +114,12 @@ function Mat4.zero(self) end +function Mat4.mul(self, other) + glm.mat4_mul(self.data, other.data, self.data) + return self +end + + function Mat4.translate(self, vec) glm.translate(self.data, vec.data) return self diff --git a/honey/quaternion.lua b/honey/quaternion.lua new file mode 100644 index 0000000..6fbfdd5 --- /dev/null +++ b/honey/quaternion.lua @@ -0,0 +1,30 @@ +local glm = honey.glm +local Mat4 = require "honey.mat4" + +local module = {} +setmetatable(module, {__index=_G}) +setfenv(1, module) + +Quaternion = {} +Quaternion.__index = Quaternion + + +function Quaternion.new(_, tbl) + local tbl = tbl or { 0, 0, 0, 0 } + local self = {} + self.data = glm.quat_create() + glm.quat_init(self.data, unpack(tbl)) + setmetatable(self, Quaternion) + return self +end +setmetatable(Quaternion, {__call=Quaternion.new}) + + +function Quaternion.toMat4(self) + local m = Mat4() + glm.quat_mat4(self.data, m.data) + return m +end + + +return module.Quaternion diff --git a/honey/std.lua b/honey/std.lua index 7cb7695..80b071e 100644 --- a/honey/std.lua +++ b/honey/std.lua @@ -11,3 +11,4 @@ honey.mesh = require 'honey.mesh' honey.Shader = require 'honey.shader' honey.Vec3 = require 'honey.vec3' honey.Window = require 'honey.window' +honey.Quaternion = require 'honey.quaternion' diff --git a/main.lua b/main.lua index 9f26eb3..2c92223 100644 --- a/main.lua +++ b/main.lua @@ -4,54 +4,20 @@ 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 physics -local world = ode.WorldCreate() -local space = ode.HashSpaceCreate(ode.Space0) -ode.WorldSetGravity(world, 0, -10, 0) -ode.WorldSetCFM(world, 1e-5) -ode.CreatePlane(space, 0, 1, 0, 0) -local contactgroup = ode.JointGroupCreate(16) -local body = ode.BodyCreate(world) -local geom = ode.CreateSphere(space, 0.5) -local mass = ode.MassCreate() -ode.MassSetSphere(mass, 1, 0.5) -ode.BodySetMass(body, mass) -ode.GeomSetBody(geom, body) -ode.BodySetPosition(body, 0, 3, 0) - -function physicsStep() - ode.SpaceCollide(space, function(o1, o2) - local b1 = ode.GeomGetBody(o1) - local b2 = ode.GeomGetBody(o2) - local contact = ode.CreateContact{ - surface = { - mode = ode.ContactBounce + ode.ContactSoftCFM, - mu = ode.Infinity, - bounce = 1.0, - bounce_vel = 0.1, - soft_cfm = 0.001, - }, - } - local collisions = ode.Collide(o1, o2, 1) - if #collisions > 0 then - print("collision detected!") - ode.ContactSetGeom(contact, collisions[1]) - local joint = ode.JointCreateContact(world, contactgroup, contact) - ode.JointAttach(joint, b1, b2) - end - end) - ode.WorldQuickStep(world, 0.01) - ode.JointGroupEmpty(contactgroup) -end - +-- setup vector graphics +local vg = nvg.CreateContext() +local november = nvg.CreateFont(vg, "November", "assets/november.regular.ttf") +local vw, vh = 640, 480 -- create camera matrices local camera = { @@ -65,6 +31,63 @@ level:addSystem(systems.transformCascade) level:addSystem(systems.renderCam(camera)) level:addSystem(systems.update) +-- physics system +local world = ode.WorldCreate() +local space = ode.HashSpaceCreate(ode.Space0) +level:addSystem{ + setup=function(self) + self.time = 0 + self.world=world + self.space=space + ode.WorldSetGravity(self.world, 0, -10, 0) + ode.WorldSetCFM(self.world, 1e-5) + self.contactgroup = ode.JointGroupCreate(32) + self.__gc = honey.util.gc_canary(function() + ode.WorldDestroy(self.world) + ode.SpaceDestroy(self.space) + end) + end, + filter = ecs.Filter.AND{"transform", "collisionShape", "physicsBody"}, + preUpdate = function(self) + self.dt = nil + if self.time > 0.01 then + self.time = self.time - 0.01 + ode.SpaceCollide(self.space, function(o1, o2) + local b1 = ode.GeomGetBody(o1) + local b2 = ode.GeomGetBody(o2) + local contact = ode.CreateContact{ + surface = { + mode = ode.ContactBounce + ode.ContactSoftCFM, + mu = ode.Infinity, + bounce = 0.99, + bounce_vel = 0.1, + soft_cfm = 0.001, + }, + } + local collisions = ode.Collide(o1, o2, 1) + if #collisions > 0 then + print("collision detected!") + ode.ContactSetGeom(contact, collisions[1]) + local joint = ode.JointCreateContact(self.world, self.contactgroup, contact) + ode.JointAttach(joint, b1, b2) + end + end) + ode.WorldQuickStep(self.world, 0.01) + ode.JointGroupEmpty(self.contactgroup) + end + end, + update = function(self, entity, dt) + if not self.dt then + self.dt = dt + self.time = self.time + dt + end + + local x, y, z = ode.BodyGetPosition(entity.physicsBody) + local a, b, c, d = ode.BodyGetQuaternion(entity.physicsBody) + entity.transform:identity():translate(Vec3{x, y, z}):mul(Quaternion{a, b, c, d}:toMat4()) + end, +} + -- create shader local shader = honey.Shader{ vertexFile = "vertex.glsl", @@ -127,13 +150,20 @@ local ball = { parent=false, mesh=icosa, shader=shader, - update=function(self, dt) - local x, y, z = ode.BodyGetPosition(body) - self.transform:identity():translate(Vec3{x, y, z}) - end, + collisionShape=ode.CreateSphere(space, 0.5), + physicsBody=ode.BodyCreate(world), } level:addEntity(ball) +local mass = ode.MassCreate() +ode.MassSetSphere(mass, 1, 0.5) +ode.BodySetMass(ball.physicsBody, mass) +ode.GeomSetBody(ball.collisionShape, ball.physicsBody) +ode.BodySetPosition(ball.physicsBody, 0, 3, 0) +ode.BodySetLinearVel(ball.physicsBody, 1, 0, 0) + +ode.CreatePlane(space, 0, 1, 0, -2) + -- close window on ESCAPE key window:setKeyCallback(function(_, key) if key == glfw.KEY_ESCAPE then @@ -145,14 +175,33 @@ end) window:setFramebufferSizeCallback(function(_, width, height) gl.Viewport(0, 0, width, height) camera.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) level:update(dt) - physicsStep() + 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.EndFrame(vg) end) -- clean up -- cgit v1.2.1