Drawing Colliders

--[[ A utility function that draws all possible physics colliders and joint geometry

Useful for debugging physics (to see if colliders line up with rendered geometry),
and for experimenting and prototyping with physics, to get the rendering out of way.   --]]

local world
local count = 100

function lovr.load()
  gravity = gravity or 2
  sleepingAllowed = sleepingAllowed or false
  world = lovr.physics.newWorld(0, -gravity, 0, sleepingAllowed)
  -- ground plane
  local box = world:newBoxCollider(vec3(0, 0, 0), vec3(20, 0.1, 20))
  box:setKinematic(true)
  box:setUserData({1, 1, 1})
end


function lovr.update(dt)
  world:update(1 / 72) -- simulation is more stable if executed with fixed step
  -- every 100ms add random shape until there's enough of them
  if lovr.timer.getTime() % 0.1 < dt and count > 0 then
    local collider
    local colliderType = ({'box', 'sphere', 'cylinder', 'capsule'})[count % 4 + 1]
    local position = vec3(2 - 4 * lovr.math.random(), 4, 1 - 2 * lovr.math.random())
    if     colliderType == 'box' then
      local size = vec3(0.1, 0.2, 0.3)
      collider = world:newBoxCollider(position, size)
    elseif colliderType == 'sphere' then
      local radius = 0.2
      collider = world:newSphereCollider(position, radius)
    elseif colliderType == 'cylinder' then
      local radius, length = 0.1, 0.3
      collider = world:newCylinderCollider(position, radius, length)
    elseif colliderType == 'capsule' then
      local radius, length = 0.1, 0.3
      collider = world:newCapsuleCollider(position, radius, length)
    end
    local shade = 0.2 + 0.6 * lovr.math.random()
    collider:setUserData({shade, shade, shade})
    collider:setOrientation(math.pi, lovr.math.random(), lovr.math.random(), lovr.math.random())
    count = count - 1
  end
end


function lovr.draw()
  for i, collider in ipairs(world:getColliders()) do
    -- rendering shapes of each collider
    drawCollider(collider)
    -- debug geometry for joints (no joints are used in this example)
    drawAttachedJoints(collider)
  end
end


function drawCollider(collider)
  local color = collider:getUserData()
  lovr.graphics.setColor(color or 0x202020)
  local shape = collider:getShapes()[1]
  if shape:isSensor() then
    local r,g,b = lovr.graphics.getColor()
    lovr.graphics.setColor(r,g,b,0.2)
  end
  -- shapes
  for _, shape in ipairs(collider:getShapes()) do
    local shapeType = shape:getType()
    local x,y,z, angle, ax,ay,az = collider:getPose()
    -- draw primitive at collider's position with correct dimensions
    if shapeType == 'box' then
      local sx, sy, sz = shape:getDimensions()
      lovr.graphics.box('fill', x,y,z, sx,sy,sz, angle, ax,ay,az)
    elseif shapeType == 'sphere' then
      lovr.graphics.sphere(x,y,z, shape:getRadius())
    elseif shapeType == 'cylinder' then
      local l, r = shape:getLength(), shape:getRadius()
      local x,y,z, angle, ax,ay,az = collider:getPose()
      lovr.graphics.cylinder(x,y,z, l, angle, ax,ay,az, r, r)
    elseif shapeType == 'capsule' then
      local l, r = shape:getLength(), shape:getRadius()
      local x,y,z, angle, ax,ay,az = collider:getPose()
      local m = mat4(x,y,z, 1,1,1, angle, ax,ay,az)
      lovr.graphics.cylinder(x,y,z, l, angle, ax,ay,az, r, r, false)
      lovr.graphics.sphere(vec3(m:mul(0, 0,  l/2)), r)
      lovr.graphics.sphere(vec3(m:mul(0, 0, -l/2)), r)
    end
  end
end


function drawAttachedJoints(collider)
  lovr.graphics.setColor(1,1,1,0.3)
  -- joints are attached to two colliders; function draws joint for second collider
  for j, joint in ipairs(collider:getJoints()) do
    local anchoring, attached = joint:getColliders()
    if attached == collider then
      jointType = joint:getType()
      if jointType == 'ball' then
        local x1, y1, z1, x2, y2, z2 = joint:getAnchors()
        drawAnchor(vec3(x1,y1,z1))
        drawAnchor(vec3(x2,y2,z2))
      elseif jointType == 'slider' then
        local position = joint:getPosition()
        local x,y,z = anchoring:getPosition()
        drawAxis(vec3(x,y,z), vec3(joint:getAxis()))
      elseif jointType == 'distance' then
        local x1, y1, z1, x2, y2, z2 = joint:getAnchors()
        drawAnchor(vec3(x1,y1,z1))
        drawAnchor(vec3(x2,y2,z2))
        drawAxis(vec3(x2,y2,z2), vec3(x1, y1, z1) - vec3(x2,y2,z2))
      elseif jointType == 'hinge' then
        local x1, y1, z1, x2, y2, z2 = joint:getAnchors()
        drawAnchor(vec3(x1,y1,z1))
        drawAnchor(vec3(x2,y2,z2))
        drawAxis(vec3(x1,y1,z1), vec3(joint:getAxis()))
      end
    end
  end
end


function drawAnchor(origin)
  lovr.graphics.sphere(origin, .02)
end


function drawAxis(origin, axis)
  lovr.graphics.line(origin, origin + axis:normalize() * 0.3)
end
View Source