Browse Source

Improve on physics examples (#98)

* Remove obsolete Drawing Colliders example

The code is flawed, and made obsolete with an external library.

* Improve Hand and Zip_Line physics examples
Josip 2 years ago
parent
commit
967f07bb8a

+ 0 - 124
examples/Physics/Drawing_Colliders/main.lua

@@ -1,124 +0,0 @@
---[[ 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(pass)
-  for i, collider in ipairs(world:getColliders()) do
-    -- rendering shapes of each collider
-    drawCollider(pass, collider)
-    -- debug geometry for joints (no joints are used in this example)
-    drawAttachedJoints(pass, collider)
-  end
-end
-
-
-function drawCollider(pass, collider)
-  local color = collider:getUserData()
-  local shape = collider:getShapes()[1]
-  local alpha = shape:isSensor() and 0.2 or 1.0
-  pass:setColor(color or 0x202020, alpha)
-  -- 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()
-      pass:box(x,y,z, sx,sy,sz, angle, ax,ay,az)
-    elseif shapeType == 'sphere' then
-      pass: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()
-      pass:cylinder(x,y,z, r, l, angle, ax,ay,az)
-    elseif shapeType == 'capsule' then
-      local l, r = shape:getLength(), shape:getRadius()
-      local x,y,z, angle, ax,ay,az = collider:getPose()
-      pass:capsule(x,y,z, r, l, angle, ax,ay,az)
-    end
-  end
-end
-
-
-function drawAttachedJoints(pass, collider)
-  pass: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(pass, vec3(x1,y1,z1))
-        drawAnchor(pass, vec3(x2,y2,z2))
-      elseif jointType == 'slider' then
-        local position = joint:getPosition()
-        local x,y,z = anchoring:getPosition()
-        drawAxis(pass, vec3(x,y,z), vec3(joint:getAxis()))
-      elseif jointType == 'distance' then
-        local x1, y1, z1, x2, y2, z2 = joint:getAnchors()
-        drawAnchor(pass, vec3(x1,y1,z1))
-        drawAnchor(pass, vec3(x2,y2,z2))
-        drawAxis(pass, 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(pass, vec3(x1,y1,z1))
-        drawAnchor(pass, vec3(x2,y2,z2))
-        drawAxis(pass, vec3(x1,y1,z1), vec3(joint:getAxis()))
-      end
-    end
-  end
-end
-
-
-function drawAnchor(pass, origin)
-  pass:sphere(origin, .02)
-end
-
-
-function drawAxis(origin, axis)
-  pass:line(origin, origin + axis:normalize() * 0.3)
-end

+ 21 - 19
examples/Physics/Hand_Physics/main.lua

@@ -8,21 +8,21 @@ from hand controller. This results in lousy and unconvincing collisions with oth
 physics engine doesn't know the speed of hand colliders at the moment of collision.
 physics engine doesn't know the speed of hand colliders at the moment of collision.
 
 
 An improvement is to set linear and angular speed of kinematic hand colliders so that they
 An improvement is to set linear and angular speed of kinematic hand colliders so that they
-approach the target (actual location/orientation of hand controller). This works excellent for one
-controller. When you try to squeeze an object between two hands, physics break. This is because
-kinematic hand controllers are never affected by physics engine and unrealistic material
-penetration cannot be resolved.
+approach the target (actual location/orientation of hand controller). This works well for one
+hand physics, however physics start to glitch when you try to squeeze object between two hands.
+This is because kinematic hand controllers can never be affected by collision forces, so the
+squeezed collider cannot push back against them and the collision cannot be resolved.
 
 
 The approach taken here is to have hand controllers behave as normal dynamic colliders that can be
 The approach taken here is to have hand controllers behave as normal dynamic colliders that can be
 affected by other collisions. To track hand controllers, we apply force and torque on collider
 affected by other collisions. To track hand controllers, we apply force and torque on collider
 objects that's proportional to distance from correct position.
 objects that's proportional to distance from correct position.
 
 
 This means hand colliders won't have 1:1 mapping with actual hand controllers, they will actually
 This means hand colliders won't have 1:1 mapping with actual hand controllers, they will actually
-'bend' under large force. Also the colliders can actually become stuck behind another object. This
-is sometimes frustrating to use, so in this example hand colliders can ghost through objects or
-become solid using trigger button.
+'bend' under large force. Also the colliders can become stuck and burried beneath other objects.
+This is frustrating to users, so in this example hand colliders can ghost through objects or
+become solid, using the trigger button.
 
 
-Grabbing objects is done by creating two joints between hand collider and object to hold them
+Grabbing objects is done by creating two joints between hand collider and object, to hold them
 together. This enables pulling, stacking and throwing.                                      --]]
 together. This enables pulling, stacking and throwing.                                      --]]
 
 
 local hands = { -- palms that can push and grab objects
 local hands = { -- palms that can push and grab objects
@@ -37,6 +37,8 @@ local collisionCallbacks = {}
 local boxes = {}
 local boxes = {}
 
 
 local framerate = 1 / 72 -- fixed framerate is recommended for physics updates
 local framerate = 1 / 72 -- fixed framerate is recommended for physics updates
+local hand_torque = 20
+local hand_force = 30000
 
 
 function lovr.load()
 function lovr.load()
   world = lovr.physics.newWorld(0, -2, 0, false) -- low gravity and no collider sleeping
   world = lovr.physics.newWorld(0, -2, 0, false) -- low gravity and no collider sleeping
@@ -58,9 +60,9 @@ function lovr.load()
   -- make colliders for two hands
   -- make colliders for two hands
   for i = 1, 2 do
   for i = 1, 2 do
     hands.colliders[i] = world:newBoxCollider(vec3(0,2,0), vec3(0.04, 0.08, 0.08))
     hands.colliders[i] = world:newBoxCollider(vec3(0,2,0), vec3(0.04, 0.08, 0.08))
-    hands.colliders[i]:setLinearDamping(0.2)
-    hands.colliders[i]:setAngularDamping(0.3)
-    hands.colliders[i]:setMass(0.1)
+    hands.colliders[i]:setLinearDamping(0.7)
+    hands.colliders[i]:setAngularDamping(0.9)
+    hands.colliders[i]:setMass(0.5)
     registerCollisionCallback(hands.colliders[i],
     registerCollisionCallback(hands.colliders[i],
       function(collider, world)
       function(collider, world)
         -- store collider that was last touched by hand
         -- store collider that was last touched by hand
@@ -91,15 +93,16 @@ function lovr.update(dt)
     local vr = mat4(hands.colliders[i]:getPose()) -- vr pose of palm colliders
     local vr = mat4(hands.colliders[i]:getPose()) -- vr pose of palm colliders
     local angle, ax,ay,az = quat(rw):mul(quat(vr):conjugate()):unpack()
     local angle, ax,ay,az = quat(rw):mul(quat(vr):conjugate()):unpack()
     angle = ((angle + math.pi) % (2 * math.pi) - math.pi) -- for minimal motion wrap to (-pi, +pi) range
     angle = ((angle + math.pi) % (2 * math.pi) - math.pi) -- for minimal motion wrap to (-pi, +pi) range
-    hands.colliders[i]:applyTorque(vec3(ax, ay, az):mul(angle * dt * 1))
-    hands.colliders[i]:applyForce((vec3(rw:mul(0,0,0)) - vec3(vr:mul(0,0,0))):mul(dt * 2000))
+    hands.colliders[i]:applyTorque(vec3(ax, ay, az):mul(angle * dt * hand_torque))
+    hands.colliders[i]:applyForce((vec3(rw) - vec3(vr)):mul(dt * hand_force))
     -- solidify when trigger touched
     -- solidify when trigger touched
     hands.solid[i] = lovr.headset.isDown(hand, 'trigger')
     hands.solid[i] = lovr.headset.isDown(hand, 'trigger')
     hands.colliders[i]:getShapes()[1]:setSensor(not hands.solid[i])
     hands.colliders[i]:getShapes()[1]:setSensor(not hands.solid[i])
     -- hold/release colliders
     -- hold/release colliders
     if lovr.headset.isDown(hand, 'grip') and hands.touching[i] and not hands.holding[i] then
     if lovr.headset.isDown(hand, 'grip') and hands.touching[i] and not hands.holding[i] then
       hands.holding[i] = hands.touching[i]
       hands.holding[i] = hands.touching[i]
-      lovr.physics.newBallJoint(hands.colliders[i], hands.holding[i], vr:mul(0,0,0))
+      -- grab object with ball joint to drag it, and slider joint to also match the orientation
+      lovr.physics.newBallJoint(hands.colliders[i], hands.holding[i], vr:mul(0, 0, 0))
       lovr.physics.newSliderJoint(hands.colliders[i], hands.holding[i], quat(vr):direction())
       lovr.physics.newSliderJoint(hands.colliders[i], hands.holding[i], quat(vr):direction())
     end
     end
     if lovr.headset.wasReleased(hand, 'grip') and hands.holding[i] then
     if lovr.headset.wasReleased(hand, 'grip') and hands.holding[i] then
@@ -115,9 +118,8 @@ end
 
 
 function lovr.draw(pass)
 function lovr.draw(pass)
   for i, collider in ipairs(hands.colliders) do
   for i, collider in ipairs(hands.colliders) do
-    local alpha = hands.solid[i] and 1 or 0.2
-    pass:setColor(0.75, 0.56, 0.44, alpha)
-    drawBoxCollider(pass, collider)
+    pass:setColor(0.75, 0.56, 0.44)
+    drawBoxCollider(pass, collider, not hands.solid[i])
   end
   end
   lovr.math.setRandomSeed(0)
   lovr.math.setRandomSeed(0)
   for i, collider in ipairs(boxes) do
   for i, collider in ipairs(boxes) do
@@ -128,7 +130,7 @@ function lovr.draw(pass)
 end
 end
 
 
 
 
-function drawBoxCollider(pass, collider)
+function drawBoxCollider(pass, collider, is_sensor)
   -- query current pose (location and orientation)
   -- query current pose (location and orientation)
   local pose = mat4(collider:getPose())
   local pose = mat4(collider:getPose())
   -- query dimensions of box
   -- query dimensions of box
@@ -136,7 +138,7 @@ function drawBoxCollider(pass, collider)
   local size = vec3(shape:getDimensions())
   local size = vec3(shape:getDimensions())
   -- draw box
   -- draw box
   pose:scale(size)
   pose:scale(size)
-  pass:box(pose)
+  pass:box(pose, is_sensor and 'line' or 'fill')
 end
 end
 
 
 
 

+ 1 - 32
examples/Physics/Zip_Line/main.lua

@@ -1,4 +1,4 @@
---[[ A zipline made of
+--[[ A zipline demo combining several joint types
 
 
 Capsule is suspended from trolley using distance joint. Trolley is attached to hanger
 Capsule is suspended from trolley using distance joint. Trolley is attached to hanger
 using slider joint. Beware that slider joint loses its accuracy/stability when attached
 using slider joint. Beware that slider joint loses its accuracy/stability when attached
@@ -50,34 +50,3 @@ function lovr.draw(pass)
     end
     end
   end
   end
 end
 end
-
-
-function makeRope(origin, destination, thickness, elements)
-  local length = (destination - origin):length()
-  thickness = thickness or length / 100
-  elements = elements or 30
-  elementSize = length / elements
-  local orientation = vec3(destination - origin):normalize()
-  local first, last, prev
-  for i = 1, elements do
-    local position = vec3(origin):lerp(destination, (i - 0.5) / elements)
-    local anchor   = vec3(origin):lerp(destination, (i - 1.0) / elements)
-    element = world:newBoxCollider(position, vec3(thickness, thickness, elementSize * 0.95))
-    element:setRestitution(0.1)
-    element:setGravityIgnored(true)
-    element:setOrientation(quat(orientation))
-    element:setLinearDamping(0.01)
-    element:setAngularDamping(0.01)
-    element:setMass(0.001)
-    if prev then
-      local joint = lovr.physics.newBallJoint(prev, element, anchor)
-      joint:setResponseTime(10)
-      joint:setTightness(1)
-    else
-      first = element
-    end
-    prev = element
-  end
-  last = prev
-  return first, last
-end