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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import {IObject3D} from '../IObject'
  2. import {IUiConfigContainer, UiObjectConfig} from 'uiconfig.js'
  3. import {ICamera} from '../ICamera'
  4. export function makeIObject3DUiConfig(this: IObject3D, isMesh?:boolean): UiObjectConfig {
  5. if (!this) return {}
  6. if (this.uiConfig) return this.uiConfig
  7. const config: UiObjectConfig = {
  8. type: 'folder',
  9. label: this.name || 'unnamed',
  10. expanded: true,
  11. limitedUi: true,
  12. children: [
  13. {
  14. type: 'checkbox',
  15. label: 'Visible',
  16. property: [this, 'visible'],
  17. limitedUi: true,
  18. },
  19. {
  20. type: 'button',
  21. label: 'Pick/Focus',
  22. value: ()=>{
  23. // todo instead of dispatching, make a IObject3D.select function
  24. this.dispatchEvent({type: 'select', ui: true, value: this, bubbleToParent: true, focusCamera: true})
  25. },
  26. },
  27. {
  28. type: 'button',
  29. label: 'Pick Parent',
  30. hidden: ()=>!this.parent,
  31. value: ()=>{
  32. const parent = this.parent
  33. if (parent) {
  34. parent.dispatchEvent({type: 'select', ui: true, bubbleToParent: true, value: parent})
  35. }
  36. },
  37. },
  38. {
  39. type: 'input',
  40. label: 'Name',
  41. property: [this, 'name'],
  42. },
  43. {
  44. type: 'checkbox',
  45. label: 'Casts Shadow',
  46. hidden: () => !(this as any).isMesh,
  47. property: [this, 'castShadow'],
  48. // onChange: this.setDirty,
  49. },
  50. {
  51. type: 'checkbox',
  52. label: 'Receive Shadow',
  53. hidden: () => !(this as any).isMesh,
  54. property: [this, 'receiveShadow'],
  55. // onChange: this.setDirty,
  56. },
  57. {
  58. type: 'checkbox',
  59. label: 'Frustum culled',
  60. property: [this, 'frustumCulled'],
  61. // onChange: this.setDirty,
  62. },
  63. {
  64. type: 'vec3',
  65. label: 'Position',
  66. property: [this, 'position'],
  67. limitedUi: true,
  68. },
  69. {
  70. type: 'vec3',
  71. label: 'Rotation',
  72. property: [this, 'rotation'],
  73. limitedUi: true,
  74. },
  75. {
  76. type: 'vec3',
  77. label: 'Scale',
  78. property: [this, 'scale'],
  79. },
  80. {
  81. type: 'input',
  82. label: 'Render Order',
  83. property: [this, 'renderOrder'],
  84. },
  85. {
  86. type: 'button',
  87. label: 'Auto Scale',
  88. hidden: ()=>!this.autoScale,
  89. prompt: ['Auto Scale Radius: Object will be scaled to the given radius', this.userData.autoScaleRadius || '2', true],
  90. value: ()=>{
  91. const def = (this.userData.autoScaleRadius || 2) + ''
  92. const res = prompt('Auto Scale Radius: Object will be scaled to the given radius', def)
  93. if (res === null) return
  94. const rad = parseFloat(res || def)
  95. if (Math.abs(rad) > 0) this.autoScale?.(rad)
  96. },
  97. },
  98. // {
  99. // type: 'button',
  100. // label: 'Auto Center',
  101. // value: ()=>{
  102. // autoCenterObject3D(object)
  103. // },
  104. // },
  105. this.userData.license !== undefined ? {
  106. type: 'input',
  107. label: 'License/Credits',
  108. property: [this.userData, 'license'],
  109. limitedUi: true,
  110. } : {},
  111. ],
  112. }
  113. if (this.isMesh && isMesh !== false) {
  114. // todo: move to make mesh ui function?
  115. const ui = [
  116. // morph targets
  117. ()=>{
  118. const dict = Object.entries(this.morphTargetDictionary || {})
  119. return dict.length ? {
  120. label: 'Morph Targets',
  121. type: 'folder',
  122. children: dict.map(([name, i])=>({
  123. type: 'slider',
  124. label: name,
  125. bounds: [0, 1],
  126. stepSize: 0.0001,
  127. property: [this.morphTargetInfluences, i as any],
  128. onChange: (e: any)=>{
  129. this.setDirty?.({refreshScene: e.last, frameFade: false, refreshUi: false})
  130. },
  131. })),
  132. } : undefined
  133. },
  134. // geometry
  135. ()=>(this.geometry as IUiConfigContainer)?.uiConfig,
  136. // material(s)
  137. ()=>Array.isArray(this.material) ? this.material.length < 1 ? undefined : {
  138. label: 'Materials',
  139. type: 'folder',
  140. children: (this.material as IUiConfigContainer[]).map((a)=>a?.uiConfig).filter(a=>a),
  141. } : (this.material as IUiConfigContainer)?.uiConfig,
  142. ]
  143. ;(config.children as UiObjectConfig[]).push(...ui)
  144. }
  145. // todo: if we are replacing all the cameras in the scene, is this even required?
  146. if (this.isCamera) {
  147. // todo: move to make camera ui function?
  148. const ui: UiObjectConfig[] = [
  149. {
  150. type: 'button',
  151. label: 'Set View',
  152. value: ()=>{
  153. // todo: call setView on the camera, which will dispatch the event
  154. (this as ICamera).dispatchEvent({type: 'setView', ui: true, camera: this as ICamera})
  155. config.uiRefresh?.(true, 'postFrame')
  156. },
  157. },
  158. {
  159. type: 'button',
  160. label: 'Activate main',
  161. hidden: ()=>(this as ICamera)?.isMainCamera,
  162. value: ()=>{
  163. // todo: call activateMain on the camera, which will dispatch the event
  164. (this as ICamera).dispatchEvent({type: 'activateMain', ui: true, camera: this as ICamera})
  165. config.uiRefresh?.(true, 'postFrame')
  166. },
  167. },
  168. {
  169. type: 'button',
  170. label: 'Deactivate main',
  171. hidden: ()=>!(this as ICamera)?.isMainCamera,
  172. value: ()=>{
  173. // todo: call activateMain on the camera, which will dispatch the event
  174. (this as ICamera).dispatchEvent({type: 'activateMain', ui: true, camera: undefined})
  175. config.uiRefresh?.(true, 'postFrame')
  176. },
  177. },
  178. {
  179. type: 'checkbox',
  180. label: 'Auto LookAt Target',
  181. getValue: ()=>(this as ICamera).userData.autoLookAtTarget ?? false,
  182. setValue: (v)=>{
  183. (this as ICamera).userData.autoLookAtTarget = v
  184. config.uiRefresh?.(true, 'postFrame')
  185. },
  186. },
  187. ]
  188. ;(config.children as UiObjectConfig[]).push(...ui)
  189. }
  190. // todo: lights?
  191. // todo: issue when selected object is moved to picking from SceneUI
  192. // (config.children as UiObjectConfig[]).push(makeHierarchyUi(object))
  193. this.uiConfig = config
  194. return config
  195. }
  196. // function makeHierarchyUi(object: Object3D, root?: Object3D): UiObjectConfig {
  197. // const dispatch = ()=>(root || object).dispatchEvent({type: 'select', ui: true, value: object})
  198. // if (object.children.length === 0) return {
  199. // type: 'button',
  200. // label: 'Select ' + (object.name || 'unnamed'),
  201. // // limitedUi: true,
  202. // value: dispatch,
  203. // }
  204. // return {
  205. // type: 'folder',
  206. // label: 'Select ' + (object.name || 'unnamed'),
  207. // // limitedUi: true,
  208. // children: object.children.map((child)=>makeHierarchyUi(child, root || object)),
  209. // value: dispatch,
  210. // onExpand: dispatch,
  211. // }
  212. // }