cam.lua 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. local m = {}
  2. m.fov = math.pi / 2
  3. m.near_plane = 0.01
  4. m.upvector = Vec3(0, 1, 0)
  5. m.position = Vec3(0, 3, 4)
  6. m.center = Vec3(0, 0, 0)
  7. m.zoom_speed = 1.0
  8. m.orbit_speed = 1.0
  9. m.pan_speed = 1.0
  10. m.azimuth = math.pi / 2
  11. m.radius = m.position:distance(m.center)
  12. m.polar = math.acos((m.position.y - m.center.y) / m.radius)
  13. -- angles are measured from the up vector
  14. m.polar_upper = 0.1
  15. m.polar_lower = math.pi - m.polar_upper
  16. m.radius_lower = 0.1
  17. -- these are read-only; overwriten in nudge() and resize()
  18. m.pose = Mat4():target(m.position, m.center, m.upvector)
  19. m.projection = Mat4():perspective(m.fov, 1, m.near_plane, 0)
  20. -- should be called on top of lovr.draw()
  21. function m.setCamera(pass)
  22. pass:setViewPose(1, m.pose)
  23. pass:setProjection(1, m.projection)
  24. end
  25. -- make relative changes to camera position
  26. function m.nudge(delta_azimuth, delta_polar, delta_radius)
  27. delta_azimuth = delta_azimuth or 0
  28. delta_polar = delta_polar or 0
  29. delta_radius = delta_radius or 0
  30. m.azimuth = m.azimuth + delta_azimuth
  31. m.polar = math.max(m.polar_upper, math.min(m.polar_lower, m.polar + delta_polar))
  32. m.radius = math.max(m.radius_lower, m.radius + delta_radius)
  33. m.position.x = m.center.x + m.radius * math.sin(m.polar) * math.cos(m.azimuth)
  34. m.position.y = m.center.y + m.radius * math.cos(m.polar)
  35. m.position.z = m.center.z + m.radius * math.sin(m.polar) * math.sin(m.azimuth)
  36. m.pose:target(m.position, m.center, m.upvector)
  37. end
  38. -- should be called from lovr.resize()
  39. function m.resize(width, height)
  40. local aspect = width / height
  41. m.projection = Mat4():perspective(m.fov, aspect, m.near_plane, 0)
  42. end
  43. m.resize(lovr.system.getWindowDimensions())
  44. -- should be called from lovr.mousemoved()
  45. function m.mousemoved(x, y, dx, dy)
  46. if lovr.system.isMouseDown(3) then
  47. if lovr.system.isMouseDown(1) then
  48. m.center.y = m.center.y + m.pan_speed * 0.01 * dy
  49. else
  50. local view = mat4(m.pose):invert()
  51. local camera_right = vec3(view[1], view[5], view[9])
  52. local camera_forward = vec3(view[2], 0, view[10]):normalize()
  53. m.center:add(camera_right * (m.pan_speed * 0.005 * -dx))
  54. m.center:add(camera_forward * (m.pan_speed * 0.005 * dy))
  55. end
  56. m.nudge()
  57. elseif lovr.system.isMouseDown(1) then
  58. m.nudge(m.orbit_speed * 0.0025 * dx, m.orbit_speed * 0.0025 * -dy, 0)
  59. end
  60. end
  61. -- should be called from lovr.wheelmoved()
  62. function m.wheelmoved(dx, dy)
  63. m.nudge(0, 0, -dy * m.zoom_speed * 0.12)
  64. end
  65. -- quick way to start using camera module - just call this function
  66. function m.integrate()
  67. local stub_fn = function() end
  68. local existing_cb = {
  69. draw = lovr.draw or stub_fn,
  70. resize = lovr.resize or stub_fn,
  71. mousemoved = lovr.mousemoved or stub_fn,
  72. wheelmoved = lovr.wheelmoved or stub_fn,
  73. }
  74. local function wrap(callback)
  75. return function(...)
  76. m[callback](...)
  77. existing_cb[callback](...)
  78. end
  79. end
  80. lovr.mousemoved = wrap('mousemoved')
  81. lovr.wheelmoved = wrap('wheelmoved')
  82. lovr.resize = wrap('resize')
  83. lovr.draw = function(pass)
  84. m.setCamera(pass)
  85. existing_cb.draw(pass)
  86. end
  87. end
  88. return m