threepipe
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

TransformControls2.ts 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import {TransformControls} from './TransformControls.js'
  2. import {MathUtils} from 'three'
  3. import type {ICamera, IObject3D, ISceneEvent, IWidget} from '../../core'
  4. import {iObjectCommons} from '../../core'
  5. import {uiDropdown, uiFolderContainer, uiSlider, uiToggle} from 'uiconfig.js'
  6. @uiFolderContainer('Transform Controls')
  7. export class TransformControls2 extends TransformControls implements IWidget, IObject3D {
  8. isWidget = true as const
  9. assetType = 'widget' as const
  10. setDirty = iObjectCommons.setDirty.bind(this)
  11. refreshUi = iObjectCommons.refreshUi.bind(this)
  12. object: IObject3D | undefined
  13. private _keyDownListener(event: KeyboardEvent) {
  14. if (!this.enabled) return
  15. if (!this.object) return
  16. switch (event.code) {
  17. case 'KeyQ':
  18. this.space = this.space === 'local' ? 'world' : 'local'
  19. break
  20. case 'ShiftLeft':
  21. this.translationSnap = 0.5
  22. this.rotationSnap = MathUtils.degToRad(15)
  23. this.scaleSnap = 0.25
  24. break
  25. case 'KeyW':
  26. this.mode = 'translate'
  27. break
  28. case 'KeyE':
  29. this.mode = 'rotate'
  30. break
  31. case 'KeyR':
  32. this.mode = 'scale'
  33. break
  34. case 'Equal':
  35. case 'NumpadAdd':
  36. case 'Plus':
  37. this.size = this.size + 0.1
  38. break
  39. case 'Minus':
  40. case 'NumpadSubtract':
  41. case 'Underscore':
  42. this.size = Math.max(this.size - 0.1, 0.1)
  43. break
  44. case 'KeyX':
  45. this.showX = !this.showX
  46. break
  47. case 'KeyY':
  48. this.showY = !this.showY
  49. break
  50. case 'KeyZ':
  51. this.showZ = !this.showZ
  52. break
  53. case 'Space':
  54. this.enabled = !this.enabled
  55. break
  56. default:
  57. return
  58. }
  59. this.setDirty({refreshScene: true, frameFade: true})
  60. }
  61. private _keyUpListener(event: KeyboardEvent) {
  62. if (!this.enabled) return
  63. // reset events
  64. switch (event.code) {
  65. case 'ShiftLeft':
  66. this.translationSnap = null
  67. this.rotationSnap = null
  68. this.scaleSnap = null
  69. break
  70. default:
  71. break
  72. }
  73. if (!this.object) return
  74. // non-reset events
  75. switch (event.code) {
  76. default:
  77. break
  78. }
  79. }
  80. constructor(camera: ICamera, canvas: HTMLCanvasElement) {
  81. super(camera, canvas)
  82. this.visible = false
  83. this.userData.bboxVisible = false
  84. this.size = 2
  85. this.addEventListener('objectChange', () => {
  86. this?.object?.setDirty({fadeFrame: false})
  87. // todo: do this.setDirty?
  88. })
  89. this.addEventListener('change', () => {
  90. this.setDirty({fadeFrame: false})
  91. })
  92. this._keyUpListener = this._keyUpListener.bind(this)
  93. this._keyDownListener = this._keyDownListener.bind(this)
  94. window.addEventListener('keydown', this._keyDownListener)
  95. window.addEventListener('keyup', this._keyUpListener)
  96. }
  97. dispose() {
  98. window.removeEventListener('keydown', this._keyDownListener)
  99. window.removeEventListener('keyup', this._keyUpListener)
  100. super.dispose()
  101. }
  102. // region properties
  103. enabled: boolean
  104. // axis: 'X' | 'Y' | 'Z' | 'E' | 'XY' | 'YZ' | 'XZ' | 'XYZ' | 'XYZE' | null
  105. // onChange not required for before since they fire 'change' event on changed. see TransformControls.js
  106. @uiDropdown('Mode', ['translate', 'rotate', 'scale'].map(label=>({label})))
  107. mode: 'translate' | 'rotate' | 'scale'
  108. translationSnap: number | null
  109. rotationSnap: number | null
  110. scaleSnap: number | null
  111. @uiDropdown('Space', ['world', 'local'].map(label=>({label})))
  112. space: 'world' | 'local'
  113. @uiSlider('Size', [0.1, 10], 0.1)
  114. size: number
  115. @uiToggle('Show X')
  116. showX: boolean
  117. @uiToggle('Show Y')
  118. showY: boolean
  119. @uiToggle('Show Z')
  120. showZ: boolean
  121. // dragging: boolean
  122. // endregion
  123. /**
  124. * Get the threejs object
  125. * @deprecated
  126. */
  127. get modelObject(): this {
  128. return this as any
  129. }
  130. // todo: https://helpx.adobe.com/after-effects/using/3d-transform-gizmo.html
  131. // region inherited type fixes
  132. traverse: (callback: (object: IObject3D) => void) => void
  133. traverseVisible: (callback: (object: IObject3D) => void) => void
  134. traverseAncestors: (callback: (object: IObject3D) => void) => void
  135. getObjectById: <T extends IObject3D = IObject3D>(id: number) => T | undefined
  136. getObjectByName: <T extends IObject3D = IObject3D>(name: string) => T | undefined
  137. getObjectByProperty: <T extends IObject3D = IObject3D>(name: string, value: string) => T | undefined
  138. copy: (source: this, recursive?: boolean, ...args: any[]) => this
  139. clone: (recursive?: boolean) => this
  140. remove: (...object: IObject3D[]) => this
  141. dispatchEvent: (event: ISceneEvent) => void
  142. parent: IObject3D | null
  143. children: IObject3D[]
  144. // endregion
  145. }