summaryrefslogtreecommitdiff
path: root/honey/ecs/physics.lua
diff options
context:
space:
mode:
Diffstat (limited to 'honey/ecs/physics.lua')
-rw-r--r--honey/ecs/physics.lua158
1 files changed, 158 insertions, 0 deletions
diff --git a/honey/ecs/physics.lua b/honey/ecs/physics.lua
new file mode 100644
index 0000000..eac3846
--- /dev/null
+++ b/honey/ecs/physics.lua
@@ -0,0 +1,158 @@
+local glm = require 'honey.glm'
+local Vec3 = glm.Vec3
+local Mat4 = glm.Mat4
+local Quaternion = glm.Quaternion
+local ode = honey.ode
+
+local module = {}
+setmetatable(module, {__index=_G})
+setfenv(1, module)
+
+--===== physics =====--
+
+
+system = function(params)
+ local interval = params.interval or 0.016
+ local groupSize = params.groupSize or 20
+ local refs = {}
+ return {
+ db=params.db,
+ space=params.space,
+ world=params.world,
+ contactGroup=ode.JointGroupCreate(groupSize),
+ time=interval,
+
+ priority=1,
+ update=function(self, dt)
+ for i, ref in ipairs(refs) do
+ print(i, ref.tbl, ref.physics)
+ end
+ local query = self.db:queryComponent("physics")
+
+ for id, physics in pairs(query) do
+ if not physics._body then
+ print("add physics body for "..id)
+ local body = ode.BodyCreate(self.world)
+ physics._gc = honey.util.gc_canary(function()
+ print("releasing physics body for " .. id)
+ ode.BodyDestroy(body)
+ body = nil
+ end)
+
+ local collision = self.db:getComponent(id, "collision")
+ if collision then
+ print(id, collision.class)
+ ode.GeomSetBody(collision._geom, body)
+ end
+
+ local mass = ode.MassCreate()
+ local class = physics.mass.class
+ if not class then
+ -- configure mass manually
+ elseif class == "sphere" then
+ ode.MassSetSphere(
+ mass,
+ physics.mass.density,
+ physics.mass.radius
+ )
+ elseif class == "capsule" then
+ ode.MassSetCapsule(
+ mass,
+ physics.mass.density,
+ physics.mass.direction,
+ physics.mass.radius,
+ physics.mass.length
+ )
+ end
+ ode.BodySetMass(body, mass)
+ local m = self.db:getComponent(id, "node").matrix
+ ode.BodySetPosition(
+ body,
+ m[1][4], m[2][4], m[3][4]
+ )
+ ode.BodySetRotation(
+ body,
+ m[1][1], m[1][2], m[1][3],
+ m[2][1], m[2][2], m[2][3],
+ m[3][1], m[3][2], m[3][3]
+ )
+ local vel = physics.velocity or Vec3{0,0,0}
+ ode.BodySetLinearVel(
+ body, vel[1], vel[2], vel[3]
+ )
+ physics.velocity = vel
+
+ local avel = physics.angularVelocity or Vec3{0,0,0}
+ ode.BodySetAngularVel(
+ body, avel[1], avel[2], avel[3]
+ )
+ physics.angularVelocity = avel
+
+ if physics.maxAngularSpeed then
+ ode.BodySetMaxAngularSpeed(body, physics.maxAngularSpeed)
+ end
+
+ physics._body = body
+ end
+ end
+
+ self.time = self.time + dt
+ -- only run the update every [interval] seconds
+ if self.time > interval then
+ self.time = self.time - interval
+
+ -- check for near collisions between geoms
+ ode.SpaceCollide(self.space, function(a, b)
+ -- check for actual collisions
+ local collisions = ode.Collide(a, b, 1)
+ if #collisions > 0 then
+ -- set up the joint params
+ local contact = ode.CreateContact{ surface={
+ mode = ode.ContactBounce + ode.ContactSoftCFM,
+ mu = ode.Infinity,
+ bounce = 0.90,
+ bounce_vel = 0.1,
+ soft_cfm = 0.001,
+ }}
+ ode.ContactSetGeom(contact, collisions[1])
+ -- create the joint
+ local joint = ode.JointCreateContact(
+ self.world,
+ self.contactGroup,
+ contact
+ )
+ -- attach the two bodies
+ local bodyA = ode.GeomGetBody(a)
+ local bodyB = ode.GeomGetBody(b)
+ ode.JointAttach(joint, bodyA, bodyB)
+ end
+ end)
+ -- update the world
+ ode.WorldQuickStep(self.world, interval)
+ -- remove all contact joints
+ ode.JointGroupEmpty(self.contactGroup)
+
+ -- update entity nodes
+ for id, physics in pairs(query) do
+ local x,y,z = ode.BodyGetPosition(physics._body)
+ local d,a,b,c = ode.BodyGetQuaternion(physics._body)
+ local node = self.db:getComponent(id, "node")
+ local q = Quaternion{a,b,c,d}
+ node.matrix
+ :identity()
+ :translate(Vec3{x,y,z})
+ :mul(Quaternion{a,b,c,d}:toMat4())
+
+ local vel = physics.velocity
+ vel[1], vel[2], vel[3] = ode.BodyGetLinearVel(physics._body)
+ local avel = physics.angularVelocity
+ avel[1], avel[2], avel[3] = ode.BodyGetAngularVel(physics._body)
+ end
+ end
+ end,
+ }
+end
+
+
+
+return module