orbit_control.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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. // Target returns the current orbit target.
  99. func (oc *OrbitControl) Target() math32.Vector3 {
  100. return oc.target
  101. }
  102. // Enabled returns the current OrbitEnabled bitmask.
  103. func (oc *OrbitControl) Enabled() OrbitEnabled {
  104. return oc.enabled
  105. }
  106. // SetEnabled sets the current OrbitEnabled bitmask.
  107. func (oc *OrbitControl) SetEnabled(bitmask OrbitEnabled) {
  108. oc.enabled = bitmask
  109. }
  110. // Rotate rotates the camera around the target by the specified angles.
  111. func (oc *OrbitControl) Rotate(thetaDelta, phiDelta float32) {
  112. const EPS = 0.0001
  113. // Compute direction vector from target to camera
  114. tcam := oc.cam.Position()
  115. tcam.Sub(&oc.target)
  116. // Calculate angles based on current camera position plus deltas
  117. radius := tcam.Length()
  118. theta := math32.Atan2(tcam.X, tcam.Z) + thetaDelta
  119. phi := math32.Acos(tcam.Y/radius) + phiDelta
  120. // Restrict phi and theta to be between desired limits
  121. phi = math32.Clamp(phi, oc.MinPolarAngle, oc.MaxPolarAngle)
  122. phi = math32.Clamp(phi, EPS, math32.Pi-EPS)
  123. theta = math32.Clamp(theta, oc.MinAzimuthAngle, oc.MaxAzimuthAngle)
  124. // Calculate new cartesian coordinates
  125. tcam.X = radius * math32.Sin(phi) * math32.Sin(theta)
  126. tcam.Y = radius * math32.Cos(phi)
  127. tcam.Z = radius * math32.Sin(phi) * math32.Cos(theta)
  128. // Update camera position and orientation
  129. oc.cam.SetPositionVec(oc.target.Clone().Add(&tcam))
  130. oc.cam.LookAt(&oc.target, &oc.up)
  131. }
  132. // Zoom moves the camera closer or farther from the target the specified amount
  133. // and also updates the camera's orthographic size to match.
  134. func (oc *OrbitControl) Zoom(delta float32) {
  135. // Compute direction vector from target to camera
  136. tcam := oc.cam.Position()
  137. tcam.Sub(&oc.target)
  138. // Calculate new distance from target and apply limits
  139. dist := tcam.Length() * (1 + delta/10)
  140. dist = math32.Max(oc.MinDistance, math32.Min(oc.MaxDistance, dist))
  141. tcam.SetLength(dist)
  142. // Update orthographic size and camera position with new distance
  143. oc.cam.UpdateSize(tcam.Length())
  144. oc.cam.SetPositionVec(oc.target.Clone().Add(&tcam))
  145. }
  146. // Pan pans the camera and target the specified amount on the plane perpendicular to the viewing direction.
  147. func (oc *OrbitControl) Pan(deltaX, deltaY float32) {
  148. // Compute direction vector from camera to target
  149. position := oc.cam.Position()
  150. vdir := oc.target.Clone().Sub(&position)
  151. // Conversion constant between an on-screen cursor delta and its projection on the target plane
  152. c := 2 * vdir.Length() * math32.Tan((oc.cam.Fov()/2.0)*math32.Pi/180.0) / oc.winSize()
  153. // Calculate pan components, scale by the converted offsets and combine them
  154. var pan, panX, panY math32.Vector3
  155. panX.CrossVectors(&oc.up, vdir).Normalize()
  156. panY.CrossVectors(vdir, &panX).Normalize()
  157. panY.MultiplyScalar(c * deltaY)
  158. panX.MultiplyScalar(c * deltaX)
  159. pan.AddVectors(&panX, &panY)
  160. // Add pan offset to camera and target
  161. oc.cam.SetPositionVec(position.Add(&pan))
  162. oc.target.Add(&pan)
  163. }
  164. // onMouse is called when an OnMouseDown/OnMouseUp event is received.
  165. func (oc *OrbitControl) onMouse(evname string, ev interface{}) {
  166. // If nothing enabled ignore event
  167. if oc.enabled == OrbitNone {
  168. return
  169. }
  170. switch evname {
  171. case window.OnMouseDown:
  172. gui.Manager().SetCursorFocus(oc)
  173. mev := ev.(*window.MouseEvent)
  174. switch mev.Button {
  175. case window.MouseButtonLeft: // Rotate
  176. if oc.enabled&OrbitRot != 0 {
  177. oc.state = stateRotate
  178. oc.rotStart.Set(mev.Xpos, mev.Ypos)
  179. }
  180. case window.MouseButtonMiddle: // Zoom
  181. if oc.enabled&OrbitZoom != 0 {
  182. oc.state = stateZoom
  183. oc.zoomStart = mev.Ypos
  184. }
  185. case window.MouseButtonRight: // Pan
  186. if oc.enabled&OrbitPan != 0 {
  187. oc.state = statePan
  188. oc.panStart.Set(mev.Xpos, mev.Ypos)
  189. }
  190. }
  191. case window.OnMouseUp:
  192. gui.Manager().SetCursorFocus(nil)
  193. oc.state = stateNone
  194. }
  195. }
  196. // onCursor is called when an OnCursor event is received.
  197. func (oc *OrbitControl) onCursor(evname string, ev interface{}) {
  198. // If nothing enabled ignore event
  199. if oc.enabled == OrbitNone || oc.state == stateNone {
  200. return
  201. }
  202. mev := ev.(*window.CursorEvent)
  203. switch oc.state {
  204. case stateRotate:
  205. c := -2 * math32.Pi * oc.RotSpeed / oc.winSize()
  206. oc.Rotate(c*(mev.Xpos-oc.rotStart.X),
  207. c*(mev.Ypos-oc.rotStart.Y))
  208. oc.rotStart.Set(mev.Xpos, mev.Ypos)
  209. case stateZoom:
  210. oc.Zoom(oc.ZoomSpeed * (mev.Ypos - oc.zoomStart))
  211. oc.zoomStart = mev.Ypos
  212. case statePan:
  213. oc.Pan(mev.Xpos-oc.panStart.X,
  214. mev.Ypos-oc.panStart.Y)
  215. oc.panStart.Set(mev.Xpos, mev.Ypos)
  216. }
  217. }
  218. // onScroll is called when an OnScroll event is received.
  219. func (oc *OrbitControl) onScroll(evname string, ev interface{}) {
  220. if oc.enabled&OrbitZoom != 0 {
  221. sev := ev.(*window.ScrollEvent)
  222. oc.Zoom(-sev.Yoffset)
  223. }
  224. }
  225. // onKey is called when an OnKeyDown/OnKeyRepeat event is received.
  226. func (oc *OrbitControl) onKey(evname string, ev interface{}) {
  227. // If keyboard control is disabled ignore event
  228. if oc.enabled&OrbitKeys == 0 {
  229. return
  230. }
  231. kev := ev.(*window.KeyEvent)
  232. if kev.Mods == 0 && oc.enabled&OrbitRot != 0 {
  233. switch kev.Key {
  234. case window.KeyUp:
  235. oc.Rotate(0, -oc.KeyRotSpeed)
  236. case window.KeyDown:
  237. oc.Rotate(0, oc.KeyRotSpeed)
  238. case window.KeyLeft:
  239. oc.Rotate(-oc.KeyRotSpeed, 0)
  240. case window.KeyRight:
  241. oc.Rotate(oc.KeyRotSpeed, 0)
  242. }
  243. }
  244. if kev.Mods == window.ModControl && oc.enabled&OrbitZoom != 0 {
  245. switch kev.Key {
  246. case window.KeyUp:
  247. oc.Zoom(-oc.KeyZoomSpeed)
  248. case window.KeyDown:
  249. oc.Zoom(oc.KeyZoomSpeed)
  250. }
  251. }
  252. if kev.Mods == window.ModShift && oc.enabled&OrbitPan != 0 {
  253. switch kev.Key {
  254. case window.KeyUp:
  255. oc.Pan(0, oc.KeyPanSpeed)
  256. case window.KeyDown:
  257. oc.Pan(0, -oc.KeyPanSpeed)
  258. case window.KeyLeft:
  259. oc.Pan(oc.KeyPanSpeed, 0)
  260. case window.KeyRight:
  261. oc.Pan(-oc.KeyPanSpeed, 0)
  262. }
  263. }
  264. }
  265. // winSize returns the window height or width based on the camera reference axis.
  266. func (oc *OrbitControl) winSize() float32 {
  267. width, size := window.Get().GetSize()
  268. if oc.cam.Axis() == Horizontal {
  269. size = width
  270. }
  271. return float32(size)
  272. }