orbit_control.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package camera
  5. import (
  6. "github.com/g3n/engine/core"
  7. "github.com/g3n/engine/gui"
  8. "github.com/g3n/engine/math32"
  9. "github.com/g3n/engine/window"
  10. "math"
  11. )
  12. // OrbitEnabled specifies which control types are enabled.
  13. type OrbitEnabled int
  14. // The possible control types.
  15. const (
  16. OrbitNone OrbitEnabled = 0x00
  17. OrbitRot OrbitEnabled = 0x01
  18. OrbitZoom OrbitEnabled = 0x02
  19. OrbitPan OrbitEnabled = 0x04
  20. OrbitKeys OrbitEnabled = 0x08
  21. OrbitAll OrbitEnabled = 0xFF
  22. )
  23. // orbitState bitmask
  24. type orbitState int
  25. const (
  26. stateNone = orbitState(iota)
  27. stateRotate
  28. stateZoom
  29. statePan
  30. )
  31. // OrbitControl is a camera controller that allows orbiting a target point while looking at it.
  32. // It allows the user to rotate, zoom, and pan a 3D scene using the mouse or keyboard.
  33. type OrbitControl struct {
  34. core.Dispatcher // Embedded event dispatcher
  35. cam *Camera // Controlled camera
  36. target math32.Vector3 // Camera target, around which the camera orbits
  37. up math32.Vector3 // The orbit axis (Y+)
  38. enabled OrbitEnabled // Which controls are enabled
  39. state orbitState // Current control state
  40. // Public properties
  41. MinDistance float32 // Minimum distance from target (default is 1)
  42. MaxDistance float32 // Maximum distance from target (default is infinity)
  43. MinPolarAngle float32 // Minimum polar angle in radians (default is 0)
  44. MaxPolarAngle float32 // Maximum polar angle in radians (default is Pi)
  45. MinAzimuthAngle float32 // Minimum azimuthal angle in radians (default is negative infinity)
  46. MaxAzimuthAngle float32 // Maximum azimuthal angle in radians (default is infinity)
  47. RotSpeed float32 // Rotation speed factor (default is 1)
  48. ZoomSpeed float32 // Zoom speed factor (default is 0.1)
  49. KeyRotSpeed float32 // Rotation delta in radians used on each rotation key event (default is the equivalent of 15 degrees)
  50. KeyZoomSpeed float32 // Zoom delta used on each zoom key event (default is 2)
  51. KeyPanSpeed float32 // Pan delta used on each pan key event (default is 35)
  52. // Internal
  53. rotStart math32.Vector2
  54. panStart math32.Vector2
  55. zoomStart float32
  56. }
  57. // NewOrbitControl creates and returns a pointer to a new orbit control for the specified camera.
  58. func NewOrbitControl(cam *Camera) *OrbitControl {
  59. oc := new(OrbitControl)
  60. oc.Dispatcher.Initialize()
  61. oc.cam = cam
  62. oc.target = *math32.NewVec3()
  63. oc.up = *math32.NewVector3(0, 1, 0)
  64. oc.enabled = OrbitAll
  65. oc.MinDistance = 1.0
  66. oc.MaxDistance = float32(math.Inf(1))
  67. oc.MinPolarAngle = 0
  68. oc.MaxPolarAngle = math32.Pi // 180 degrees as radians
  69. oc.MinAzimuthAngle = float32(math.Inf(-1))
  70. oc.MaxAzimuthAngle = float32(math.Inf(1))
  71. oc.RotSpeed = 1.0
  72. oc.ZoomSpeed = 0.1
  73. oc.KeyRotSpeed = 15 * math32.Pi / 180 // 15 degrees as radians
  74. oc.KeyZoomSpeed = 2.0
  75. oc.KeyPanSpeed = 35.0
  76. // Subscribe to events
  77. gui.Manager().SubscribeID(window.OnMouseUp, &oc, oc.onMouse)
  78. gui.Manager().SubscribeID(window.OnMouseDown, &oc, oc.onMouse)
  79. gui.Manager().SubscribeID(window.OnScroll, &oc, oc.onScroll)
  80. gui.Manager().SubscribeID(window.OnKeyDown, &oc, oc.onKey)
  81. gui.Manager().SubscribeID(window.OnKeyRepeat, &oc, oc.onKey)
  82. oc.SubscribeID(window.OnCursor, &oc, oc.onCursor)
  83. return oc
  84. }
  85. // Dispose unsubscribes from all events.
  86. func (oc *OrbitControl) Dispose() {
  87. gui.Manager().UnsubscribeID(window.OnMouseUp, &oc)
  88. gui.Manager().UnsubscribeID(window.OnMouseDown, &oc)
  89. gui.Manager().UnsubscribeID(window.OnScroll, &oc)
  90. gui.Manager().UnsubscribeID(window.OnKeyDown, &oc)
  91. gui.Manager().UnsubscribeID(window.OnKeyRepeat, &oc)
  92. oc.UnsubscribeID(window.OnCursor, &oc)
  93. }
  94. // Reset resets the orbit control.
  95. func (oc *OrbitControl) Reset() {
  96. oc.target = *math32.NewVec3()
  97. }
  98. // Enabled returns the current OrbitEnabled bitmask.
  99. func (oc *OrbitControl) Enabled() OrbitEnabled {
  100. return oc.enabled
  101. }
  102. // SetEnabled sets the current OrbitEnabled bitmask.
  103. func (oc *OrbitControl) SetEnabled(bitmask OrbitEnabled) {
  104. oc.enabled = bitmask
  105. }
  106. // Rotate rotates the camera around the target by the specified angles.
  107. func (oc *OrbitControl) Rotate(thetaDelta, phiDelta float32) {
  108. const EPS = 0.0001
  109. // Compute direction vector from target to camera
  110. tcam := oc.cam.Position()
  111. tcam.Sub(&oc.target)
  112. // Calculate angles based on current camera position plus deltas
  113. radius := tcam.Length()
  114. theta := math32.Atan2(tcam.X, tcam.Z) + thetaDelta
  115. phi := math32.Acos(tcam.Y/radius) + phiDelta
  116. // Restrict phi and theta to be between desired limits
  117. phi = math32.Clamp(phi, oc.MinPolarAngle, oc.MaxPolarAngle)
  118. phi = math32.Clamp(phi, EPS, math32.Pi-EPS)
  119. theta = math32.Clamp(theta, oc.MinAzimuthAngle, oc.MaxAzimuthAngle)
  120. // Calculate new cartesian coordinates
  121. tcam.X = radius * math32.Sin(phi) * math32.Sin(theta)
  122. tcam.Y = radius * math32.Cos(phi)
  123. tcam.Z = radius * math32.Sin(phi) * math32.Cos(theta)
  124. // Update camera position and orientation
  125. oc.cam.SetPositionVec(oc.target.Clone().Add(&tcam))
  126. oc.cam.LookAt(&oc.target, &oc.up)
  127. }
  128. // Zoom moves the camera closer or farther from the target the specified amount
  129. // and also updates the camera's orthographic size to match.
  130. func (oc *OrbitControl) Zoom(delta float32) {
  131. // Compute direction vector from target to camera
  132. tcam := oc.cam.Position()
  133. tcam.Sub(&oc.target)
  134. // Calculate new distance from target and apply limits
  135. dist := tcam.Length() * (1 + delta/10)
  136. dist = math32.Max(oc.MinDistance, math32.Min(oc.MaxDistance, dist))
  137. tcam.SetLength(dist)
  138. // Update orthographic size and camera position with new distance
  139. oc.cam.UpdateSize(tcam.Length())
  140. oc.cam.SetPositionVec(oc.target.Clone().Add(&tcam))
  141. }
  142. // Pan pans the camera and target the specified amount on the plane perpendicular to the viewing direction.
  143. func (oc *OrbitControl) Pan(deltaX, deltaY float32) {
  144. // Compute direction vector from camera to target
  145. position := oc.cam.Position()
  146. vdir := oc.target.Clone().Sub(&position)
  147. // Conversion constant between an on-screen cursor delta and its projection on the target plane
  148. c := 2 * vdir.Length() * math32.Tan((oc.cam.Fov()/2.0)*math32.Pi/180.0) / oc.winSize()
  149. // Calculate pan components, scale by the converted offsets and combine them
  150. var pan, panX, panY math32.Vector3
  151. panX.CrossVectors(&oc.up, vdir).Normalize()
  152. panY.CrossVectors(vdir, &panX).Normalize()
  153. panY.MultiplyScalar(c * deltaY)
  154. panX.MultiplyScalar(c * deltaX)
  155. pan.AddVectors(&panX, &panY)
  156. // Add pan offset to camera and target
  157. oc.cam.SetPositionVec(position.Add(&pan))
  158. oc.target.Add(&pan)
  159. }
  160. // onMouse is called when an OnMouseDown/OnMouseUp event is received.
  161. func (oc *OrbitControl) onMouse(evname string, ev interface{}) {
  162. // If nothing enabled ignore event
  163. if oc.enabled == OrbitNone {
  164. return
  165. }
  166. switch evname {
  167. case window.OnMouseDown:
  168. gui.Manager().SetCursorFocus(oc)
  169. mev := ev.(*window.MouseEvent)
  170. switch mev.Button {
  171. case window.MouseButtonLeft: // Rotate
  172. if oc.enabled&OrbitRot != 0 {
  173. oc.state = stateRotate
  174. oc.rotStart.Set(mev.Xpos, mev.Ypos)
  175. }
  176. case window.MouseButtonMiddle: // Zoom
  177. if oc.enabled&OrbitZoom != 0 {
  178. oc.state = stateZoom
  179. oc.zoomStart = mev.Ypos
  180. }
  181. case window.MouseButtonRight: // Pan
  182. if oc.enabled&OrbitPan != 0 {
  183. oc.state = statePan
  184. oc.panStart.Set(mev.Xpos, mev.Ypos)
  185. }
  186. }
  187. case window.OnMouseUp:
  188. gui.Manager().SetCursorFocus(nil)
  189. oc.state = stateNone
  190. }
  191. }
  192. // onCursor is called when an OnCursor event is received.
  193. func (oc *OrbitControl) onCursor(evname string, ev interface{}) {
  194. // If nothing enabled ignore event
  195. if oc.enabled == OrbitNone || oc.state == stateNone {
  196. return
  197. }
  198. mev := ev.(*window.CursorEvent)
  199. switch oc.state {
  200. case stateRotate:
  201. c := -2 * math32.Pi * oc.RotSpeed / oc.winSize()
  202. oc.Rotate(c*(mev.Xpos-oc.rotStart.X),
  203. c*(mev.Ypos-oc.rotStart.Y))
  204. oc.rotStart.Set(mev.Xpos, mev.Ypos)
  205. case stateZoom:
  206. oc.Zoom(oc.ZoomSpeed * (mev.Ypos - oc.zoomStart))
  207. oc.zoomStart = mev.Ypos
  208. case statePan:
  209. oc.Pan(mev.Xpos-oc.panStart.X,
  210. mev.Ypos-oc.panStart.Y)
  211. oc.panStart.Set(mev.Xpos, mev.Ypos)
  212. }
  213. }
  214. // onScroll is called when an OnScroll event is received.
  215. func (oc *OrbitControl) onScroll(evname string, ev interface{}) {
  216. if oc.enabled&OrbitZoom != 0 {
  217. sev := ev.(*window.ScrollEvent)
  218. oc.Zoom(-sev.Yoffset)
  219. }
  220. }
  221. // onKey is called when an OnKeyDown/OnKeyRepeat event is received.
  222. func (oc *OrbitControl) onKey(evname string, ev interface{}) {
  223. // If keyboard control is disabled ignore event
  224. if oc.enabled&OrbitKeys == 0 {
  225. return
  226. }
  227. kev := ev.(*window.KeyEvent)
  228. if kev.Mods == 0 && oc.enabled&OrbitRot != 0 {
  229. switch kev.Key {
  230. case window.KeyUp:
  231. oc.Rotate(0, -oc.KeyRotSpeed)
  232. case window.KeyDown:
  233. oc.Rotate(0, oc.KeyRotSpeed)
  234. case window.KeyLeft:
  235. oc.Rotate(-oc.KeyRotSpeed, 0)
  236. case window.KeyRight:
  237. oc.Rotate(oc.KeyRotSpeed, 0)
  238. }
  239. }
  240. if kev.Mods == window.ModControl && oc.enabled&OrbitZoom != 0 {
  241. switch kev.Key {
  242. case window.KeyUp:
  243. oc.Zoom(-oc.KeyZoomSpeed)
  244. case window.KeyDown:
  245. oc.Zoom(oc.KeyZoomSpeed)
  246. }
  247. }
  248. if kev.Mods == window.ModShift && oc.enabled&OrbitPan != 0 {
  249. switch kev.Key {
  250. case window.KeyUp:
  251. oc.Pan(0, oc.KeyPanSpeed)
  252. case window.KeyDown:
  253. oc.Pan(0, -oc.KeyPanSpeed)
  254. case window.KeyLeft:
  255. oc.Pan(oc.KeyPanSpeed, 0)
  256. case window.KeyRight:
  257. oc.Pan(-oc.KeyPanSpeed, 0)
  258. }
  259. }
  260. }
  261. // winSize returns the window height or width based on the camera reference axis.
  262. func (oc *OrbitControl) winSize() float32 {
  263. width, size := window.Get().GetSize()
  264. if oc.cam.Axis() == Horizontal {
  265. size = width
  266. }
  267. return float32(size)
  268. }