Space Stretch

SourceEdit
-- left grip + right grip: grab VR space and move / stretch / rotate
-- left grip + right grip + right trigger: reset the view

local motion = {
  pose = lovr.math.newMat4()
}

local palette = {0x0d2b45, 0x203c56, 0x544e68, 0x8d697a, 0xd08159, 0xffaa5e, 0xffd4a3, 0xffecd6}
lovr.graphics.setBackgroundColor(palette[1])


function lovr.update(dt)
  local left_vr  = motion.pose * vector(lovr.headset.getPosition('hand/left'))
  local right_vr = motion.pose * vector(lovr.headset.getPosition('hand/right'))
  if lovr.headset.wasPressed('hand/left', 'grip') then
    motion.left_anchor_vr = left_vr
  end
  if lovr.headset.wasPressed('hand/right', 'grip') then
    motion.right_anchor_vr = right_vr
  end
  if lovr.headset.isDown('hand/left',  'grip') and
     lovr.headset.isDown('hand/right', 'grip') then
    local x, y, z, scale, _, _, angle, ax, ay, az = motion.pose:unpack()
    -- Scale: get the ratio of distances between anchors over current controllers distance
    local offset_scale = motion.left_anchor_vr:distance(motion.right_anchor_vr) / left_vr:distance(right_vr)
    offset_scale = 1 + (offset_scale - 1)
    scale = scale * offset_scale
    -- the change of scale must also affect the viewpoint location
    x, y, z = x * offset_scale, y * offset_scale, z * offset_scale
    -- Position: the mid-point of anchors is compared to midpoint of current controllers position
    local midpoint_anchor     = motion.left_anchor_vr:lerp(motion.right_anchor_vr, 0.5)
    local midpoint_controller = left_vr:lerp(right_vr, 0.5)
    local offset_position = midpoint_anchor - midpoint_controller
    x, y, z = x + offset_position.x, y + offset_position.y, z + offset_position.z
    motion.pose:set(x, y, z, scale, scale, scale, angle, ax, ay, az)  -- apply transition and scaling
    -- Rotation: get angle between current controllers and anchors in XZ
    local l_to_r_anchor = motion.right_anchor_vr - motion.left_anchor_vr
    local l_to_r_controller = right_vr - left_vr
    local sign = 1
    if l_to_r_controller:cross(l_to_r_anchor):dot(vector(0, 1, 0)) < 0 then
      sign = -1
    end
    l_to_r_anchor = (l_to_r_anchor * vector(1, 0, 1)):normalize()
    l_to_r_controller = (l_to_r_controller * vector(1, 0, 1)):normalize()
    local cos_angle = l_to_r_controller:dot(l_to_r_anchor)
    cos_angle = math.max(-1, math.min(1, cos_angle))
    local offset_rotation = math.acos(cos_angle) * sign
    motion.pose:rotate(offset_rotation, 0,1,0) -- apply rotation
    -- pose & anchor reset
    if lovr.headset.isDown('hand/right', 'trigger') then
      motion.pose:set()
      motion.left_anchor_vr = left_vr
      motion.right_anchor_vr = right_vr
    end
  end
end


function lovr.draw(pass)
  pass:transform(mat4(motion.pose):invert())
  -- Render hands
  for _, hand in ipairs(lovr.headset.getHands()) do
    -- Whenever pose of hand or head is used, need to account for VR movement
    local position = motion.pose * vector(lovr.headset.getPosition(hand))
    if lovr.headset.isDown(hand, 'grip') then
      pass:setColor(palette[6])
    else
      pass:setColor(palette[8])
    end
    pass:sphere(position, .02)
  end
  -- An example scene
  local t = lovr.timer.getTime()
  pass:setCullMode('back')
  local step = 0.5
  for x = -5, 5, step do
    for z = -5, 5, step do
      local y = 0.5 * math.sin(t * 0.2 + (x * 0.5)^2 + (z * 0.5)^2)
      pass:setColor(palette[2 + math.floor(y * 10) % (#palette - 1)])
      pass:sphere(x, y, z, step / 2)
    end
  end
end