orbit_control.go 9.7 KB

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