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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. import {
  2. Color,
  3. EquirectangularReflectionMapping,
  4. EventListener,
  5. IUniform,
  6. Object3D,
  7. Scene,
  8. UVMapping,
  9. Vector3,
  10. } from 'three'
  11. import type {IObject3D, IObjectProcessor} from '../IObject'
  12. import {type ICamera} from '../ICamera'
  13. import {Box3B} from '../../three'
  14. import {AnyOptions, onChange2, onChange3, serialize} from 'ts-browser-helpers'
  15. import {PerspectiveCamera2} from '../camera/PerspectiveCamera2'
  16. import {ThreeSerialization} from '../../utils'
  17. import {ITexture} from '../ITexture'
  18. import {AddObjectOptions, IScene, ISceneEvent, ISceneEventTypes, ISceneSetDirtyOptions, IWidget} from '../IScene'
  19. import {iObjectCommons} from './iObjectCommons'
  20. import {RootSceneImportResult} from '../../assetmanager'
  21. import {uiColor, uiConfig, uiFolderContainer, uiImage, UiObjectConfig, uiSlider, uiToggle} from 'uiconfig.js'
  22. @uiFolderContainer('Root Scene')
  23. export class RootScene extends Scene<ISceneEvent, ISceneEventTypes> implements IScene<ISceneEvent, ISceneEventTypes> {
  24. readonly isRootScene = true
  25. assetType = 'model' as const
  26. uiConfig!: UiObjectConfig
  27. // private _processors = new ObjectProcessorMap<'environment' | 'background'>()
  28. // private _sceneObjects: ISceneObject[] = []
  29. private _mainCamera: ICamera | null = null
  30. /**
  31. * The root object where all imported objects are added.
  32. */
  33. readonly modelRoot: IObject3D
  34. @uiColor<RootScene>('Background Color', (s)=>({
  35. onChange: ()=>s?.onBackgroundChange(),
  36. }))
  37. @serialize() @onChange2(RootScene.prototype.onBackgroundChange)
  38. backgroundColor: Color | null = null // read in three.js WebGLBackground
  39. @onChange2(RootScene.prototype.onBackgroundChange)
  40. @serialize() @uiImage('Background Image')
  41. background: null | Color | ITexture | 'environment' = null
  42. /**
  43. * The intensity for the environment light.
  44. */
  45. @serialize() @onChange3(RootScene.prototype.setDirty)
  46. @uiSlider('Background Intensity', [0, 10], 0.01)
  47. backgroundIntensity = 1
  48. @uiImage('Environment')
  49. @serialize() @onChange2(RootScene.prototype._onEnvironmentChange)
  50. environment: ITexture | null = null
  51. /**
  52. * The intensity for the environment light.
  53. */
  54. @uiSlider('Environment Intensity', [0, 10], 0.01)
  55. @serialize() @onChange3(RootScene.prototype.setDirty)
  56. envMapIntensity = 1
  57. /**
  58. * Fixed direction environment reflections irrespective of camera position.
  59. */
  60. @uiToggle('Fixed Env Direction')
  61. @serialize() @onChange3(RootScene.prototype.setDirty)
  62. fixedEnvMapDirection = false
  63. /**
  64. * The default camera in the scene
  65. */
  66. @uiConfig() @serialize() readonly defaultCamera: ICamera
  67. // private _environmentLight?: IEnvironmentLight
  68. // required just because we don't want activeCamera to be null.
  69. private _dummyCam = new PerspectiveCamera2('') as ICamera
  70. get mainCamera(): ICamera {
  71. return this._mainCamera || this._dummyCam
  72. }
  73. set mainCamera(camera: ICamera | undefined) {
  74. const cam = this.mainCamera
  75. if (!camera) camera = this.defaultCamera
  76. if (cam === camera) return
  77. if (cam) {
  78. cam.deactivateMain(undefined, true)
  79. cam.removeEventListener('cameraUpdate', this._mainCameraUpdate)
  80. }
  81. if (camera) {
  82. this._mainCamera = camera
  83. camera.addEventListener('cameraUpdate', this._mainCameraUpdate)
  84. camera.activateMain(undefined, true)
  85. } else {
  86. this._mainCamera = null
  87. }
  88. this.dispatchEvent({type: 'activeCameraChange', lastCamera: cam, camera}) // deprecated
  89. this.dispatchEvent({type: 'mainCameraChange', lastCamera: cam, camera})
  90. this.setDirty()
  91. }
  92. private _renderCamera: ICamera | undefined
  93. get renderCamera() {
  94. return this._renderCamera ?? this.mainCamera
  95. }
  96. set renderCamera(camera: ICamera) {
  97. const cam = this._renderCamera
  98. this._renderCamera = camera
  99. this.dispatchEvent({type: 'renderCameraChange', lastCamera: cam, camera})
  100. }
  101. /**
  102. * Create a scene instance. This is done automatically in the {@link ThreeViewer} and must not be created separately.
  103. * @param camera
  104. * @param objectProcessor
  105. */
  106. constructor(camera: ICamera, objectProcessor?: IObjectProcessor) {
  107. super()
  108. this.setDirty = this.setDirty.bind(this)
  109. iObjectCommons.upgradeObject3D.call(this, undefined, objectProcessor)
  110. // this is called from parentDispatch since scene is a parent.
  111. this.addEventListener('materialUpdate', ()=>this.dispatchEvent({type: 'sceneMaterialUpdate'}))
  112. this.addEventListener('objectUpdate', this.refreshScene)
  113. this.addEventListener('geometryUpdate', this.refreshScene)
  114. this.addEventListener('geometryChanged', this.refreshScene)
  115. this.defaultCamera = camera
  116. this.modelRoot = new Object3D() as IObject3D
  117. this.modelRoot.userData.rootSceneModelRoot = true
  118. this.modelRoot.name = 'Scene' // for the UI
  119. // this.modelRoot.addEventListener('update', this.setDirty) // todo: where was this dispatched from/used ?
  120. // eslint-disable-next-line deprecation/deprecation
  121. this.add(this.modelRoot as any)
  122. // this.addSceneObject(this.modelRoot as any, {addToRoot: true, autoScale: false})
  123. // this.addSceneObject(this.defaultCamera, {addToRoot: true})
  124. // eslint-disable-next-line deprecation/deprecation
  125. this.add(this.defaultCamera)
  126. this.mainCamera = this.defaultCamera
  127. // this.boxHelper = new Box3Helper(this.getBounds())
  128. // this.boxHelper.userData.bboxVisible = false
  129. // this.boxHelper.visible = false
  130. // this.add(this.boxHelper)
  131. }
  132. /**
  133. * Add a widget (non-physical/interactive) object to the scene. like gizmos, ui components etc.
  134. * @param model
  135. * @param options
  136. */
  137. // addWidget(model: IWidget, options: AnyOptions = {}): void {
  138. // if (model.assetType !== 'widget') {
  139. // console.warn('Invalid asset type for ', model, ', adding anyway')
  140. // }
  141. // this.add(model.modelObject)
  142. //
  143. // // todo: dispatch event, add event listeners, etc
  144. // }
  145. /**
  146. * Add any object to the scene.
  147. * @param imported
  148. * @param options
  149. */
  150. addObject<T extends IObject3D|Object3D = IObject3D>(imported: T, options?: AddObjectOptions): T&IObject3D {
  151. if (options?.clearSceneObjects || options?.disposeSceneObjects) {
  152. this.clearSceneModels(options.disposeSceneObjects)
  153. }
  154. if (!imported) return imported
  155. if (!imported.isObject3D) {
  156. console.error('Invalid object, cannot add to scene.', imported)
  157. return imported as T&IObject3D
  158. }
  159. this._addObject3D(<IObject3D>imported, options)
  160. this.dispatchEvent({type: 'addSceneObject', object: <IObject3D>imported})
  161. return imported as T&IObject3D
  162. }
  163. /**
  164. * Load model root scene exported to GLTF format. Used internally by {@link ThreeViewer.addSceneObject}.
  165. * @param obj
  166. * @param options
  167. */
  168. loadModelRoot(obj: RootSceneImportResult, options?: AddObjectOptions) {
  169. if (options?.clearSceneObjects || options?.disposeSceneObjects) {
  170. this.clearSceneModels(options.disposeSceneObjects)
  171. }
  172. if (!obj.userData?.rootSceneModelRoot) {
  173. console.error('Invalid model root scene object. Trying to add anyway.', obj)
  174. }
  175. if (obj.userData) {
  176. // todo deep merge all userdata?
  177. if (obj.userData.__importData)
  178. this.modelRoot.userData.__importData = {
  179. ...this.modelRoot.userData.__importData,
  180. ...obj.userData.__importData,
  181. }
  182. if (obj.userData.gltfAsset) {
  183. this.modelRoot.userData.__gltfAsset = { // todo: merge values?
  184. ...this.modelRoot.userData.__gltfAsset,
  185. ...obj.userData.gltfAsset,
  186. }
  187. }
  188. if (obj.userData.gltfExtras)
  189. this.modelRoot.userData.__gltfExtras = {
  190. ...this.modelRoot.userData.__gltfExtras,
  191. ...obj.userData.gltfExtras,
  192. }
  193. }
  194. if (obj.userData?.gltfAsset?.copyright) obj.children.forEach(c => !c.userData.license && (c.userData.license = obj.userData.gltfAsset?.copyright))
  195. if (obj.animations) {
  196. if (!this.modelRoot.animations) this.modelRoot.animations = []
  197. for (const animation of obj.animations) {
  198. if (this.modelRoot.animations.includes(animation)) continue
  199. this.modelRoot.animations.push(animation)
  200. }
  201. }
  202. return [...obj.children] // need to clone
  203. .map(c=>this.addObject(c, {...options, clearSceneObjects: false, disposeSceneObjects: false}))
  204. }
  205. private _addObject3D(model: IObject3D|null, {autoCenter = false, centerGeometries = false, centerGeometriesKeepPosition = true, autoScale = false, autoScaleRadius = 2., addToRoot = false, license}: AddObjectOptions = {}): void {
  206. const obj = model
  207. if (!obj) {
  208. console.error('Invalid object, cannot add to scene.')
  209. return
  210. }
  211. // eslint-disable-next-line deprecation/deprecation
  212. if (addToRoot) this.add(obj)
  213. else this.modelRoot.add(obj)
  214. if (autoCenter && !obj.userData.isCentered) {
  215. obj.autoCenter?.()
  216. } else {
  217. obj.userData.isCentered = true // mark as centered, so that autoCenter is not called again when file is reloaded.
  218. }
  219. if (autoScale && !obj.userData.autoScaled) {
  220. obj.autoScale?.(obj.userData.autoScaleRadius || autoScaleRadius)
  221. } else {
  222. obj.userData.autoScaled = true // mark as auto-scaled, so that autoScale is not called again when file is reloaded.
  223. }
  224. if (centerGeometries && !obj.userData.geometriesCentered) {
  225. obj.traverse((o)=>{
  226. if (o.geometry) o.geometry.center(undefined, centerGeometriesKeepPosition)
  227. })
  228. obj.userData.geometriesCentered = true
  229. } else {
  230. obj.userData.geometriesCentered = true // mark as centered, so that geometry center is not called again when file is reloaded.
  231. }
  232. if (license) obj.userData.license = [obj.userData.license, license].filter(v=>v).join(', ')
  233. this.setDirty({refreshScene: true})
  234. }
  235. clearSceneModels(dispose = false, setDirty = true): void {
  236. if (dispose) return this.disposeSceneModels(setDirty)
  237. this.modelRoot.clear()
  238. this.modelRoot.children = []
  239. setDirty && this.setDirty({refreshScene: true})
  240. }
  241. disposeSceneModels(setDirty = true) {
  242. [...this.modelRoot.children].forEach(child => child.dispose ? child.dispose() : child.removeFromParent())
  243. this.modelRoot.clear()
  244. if (setDirty) this.setDirty({refreshScene: true})
  245. }
  246. private _onEnvironmentChange() {
  247. // console.warn('environment changed')
  248. if (this.environment?.mapping === UVMapping) {
  249. this.environment.mapping = EquirectangularReflectionMapping // for PMREMGenerator
  250. this.environment.needsUpdate = true
  251. }
  252. this.dispatchEvent({type: 'environmentChanged', environment: this.environment})
  253. this.setDirty({refreshScene: true, geometryChanged: false})
  254. this.refreshUi?.()
  255. }
  256. onBackgroundChange() {
  257. this.dispatchEvent({type: 'backgroundChanged', background: this.background, backgroundColor: this.backgroundColor})
  258. this.setDirty({refreshScene: true, geometryChanged: false})
  259. this.refreshUi?.()
  260. }
  261. /**
  262. * @deprecated Use {@link addObject}
  263. */
  264. add(...object: Object3D[]): this {
  265. super.add(...object)
  266. // this._onSceneUpdate() // this is not needed, since it will be bubbled up from the object3d and we will get event objectUpdate
  267. return this
  268. }
  269. /**
  270. * Sets the backgroundColor property from a string, number or Color, and updates the scene.
  271. * @param color
  272. */
  273. setBackgroundColor(color: string | number | Color | null) {
  274. this.backgroundColor = color ? new Color(color) : null
  275. }
  276. /**
  277. * Mark the scene dirty, and force render in the next frame.
  278. * @param options - set `refreshScene` to true to mark that any object transformations have changed. It might trigger effects like frame fade depening on plugins.
  279. * @returns {this}
  280. */
  281. setDirty(options?: ISceneSetDirtyOptions): this {
  282. // todo: for onChange calls -> check options.key for specific key that's changed and use it to determine refreshScene
  283. if (options?.sceneUpdate) {
  284. console.warn('sceneUpdate is deprecated, use refreshScene instead.')
  285. options.refreshScene = true
  286. }
  287. if (options?.refreshScene) {
  288. this.refreshScene(options)
  289. } else {
  290. this.dispatchEvent({type: 'update'}) // todo remove
  291. iObjectCommons.setDirty.call(this, {...options, scene: this})
  292. } // this sets dirty in the viewer
  293. return this
  294. }
  295. private _mainCameraUpdate = () => {
  296. this.setDirty({refreshScene: false})
  297. this.refreshActiveCameraNearFar()
  298. this.dispatchEvent({type: 'mainCameraUpdate'})
  299. this.dispatchEvent({type: 'activeCameraUpdate'}) // deprecated
  300. }
  301. // cached values
  302. private _sceneBounds: Box3B = new Box3B
  303. private _sceneBoundingRadius = 0
  304. /**
  305. * For visualizing the scene bounds. API incomplete.
  306. * @type {Box3Helper}
  307. */
  308. // readonly boxHelper: Box3Helper
  309. refreshScene(event?: Partial<ISceneEvent> & ISceneSetDirtyOptions): this {
  310. if (event && event.type === 'objectUpdate' && event.object === this) return this // ignore self
  311. if (event?.sceneUpdate === false || event?.refreshScene === false) return this.setDirty(event) // so that it doesn't trigger frame fade, shadow refresh etc
  312. // console.warn(event)
  313. this.refreshActiveCameraNearFar()
  314. this._sceneBounds = this.getBounds(false, true)
  315. // this.boxHelper?.boxHelper?.copy?.(this._sceneBounds)
  316. this._sceneBoundingRadius = this._sceneBounds.getSize(new Vector3()).length() / 2.
  317. this.dispatchEvent({...event, type: 'sceneUpdate', hierarchyChanged: ['addedToParent', 'removedFromParent'].includes(event?.change || '')})
  318. iObjectCommons.setDirty.call(this, event)
  319. return this
  320. }
  321. refreshUi = iObjectCommons.refreshUi.bind(this)
  322. /**
  323. * Dispose the scene and clear all resources.
  324. * @warn Not fully implemented yet, just clears the scene.
  325. */
  326. dispose(): void {
  327. this.disposeSceneModels();
  328. [...this.children].forEach(child => child.dispose ? child.dispose() : child.removeFromParent())
  329. this.clear()
  330. // todo: dispose more stuff?
  331. this.environment?.dispose()
  332. if ((this.background as ITexture)?.isTexture) (this.background as ITexture)?.dispose?.()
  333. this.environment = null
  334. this.background = null
  335. return
  336. }
  337. /**
  338. * Returns the bounding box of the scene model root.
  339. * @param precise
  340. * @param ignoreInvisible
  341. * @param ignoreWidgets
  342. * @param ignoreObject
  343. * @returns {Box3B}
  344. */
  345. getBounds(precise = false, ignoreInvisible = true, ignoreWidgets = true, ignoreObject?: (obj: Object3D)=>boolean): Box3B {
  346. // See bboxVisible in userdata in Box3B
  347. return new Box3B().expandByObject(this, precise, ignoreInvisible, (o: any)=>{
  348. if (ignoreWidgets && ((o as IWidget).isWidget || o.assetType === 'widget')) return true
  349. return ignoreObject?.(o) ?? false
  350. })
  351. }
  352. private _v1 = new Vector3()
  353. private _v2 = new Vector3()
  354. /**
  355. * For Programmatically toggling autoNearFar. This property is not supposed to be in the UI or serialized.
  356. * Use camera.userData.autoNearFar for UI and serialization
  357. * This is used in PickingPlugin
  358. * autoNearFar will still be disabled if this is true and camera.userData.autoNearFar is false
  359. */
  360. autoNearFarEnabled = true
  361. /**
  362. * Refreshes the scene active camera near far values, based on the scene bounding box.
  363. * This is called automatically every time the camera is updated.
  364. */
  365. refreshActiveCameraNearFar(): void {
  366. const camera = this.mainCamera as ICamera
  367. if (!camera) return
  368. if (!this.autoNearFarEnabled || camera.userData.autoNearFar === false) {
  369. camera.near = camera.userData.minNearPlane ?? 0.5
  370. camera.far = camera.userData.maxFarPlane ?? 1000
  371. return
  372. }
  373. // todo check if this takes too much time with large scenes(when moving the camera and not animating), but we also need to support animations
  374. const bbox = this.getBounds(false) // todo: can we use this._sceneBounds or will it have some issue with animation?
  375. camera.getWorldPosition(this._v1).sub(bbox.getCenter(this._v2))
  376. const radius = 1.5 * bbox.getSize(this._v2).length() / 2.
  377. const dist = this._v1.length()
  378. // new way
  379. const dist1 = Math.max(0.1, -this._v1.normalize().dot(camera.getWorldDirection(new Vector3())))
  380. const near = Math.max(Math.max(camera.userData.minNearPlane ?? 0.5, 0.001), dist1 * (dist - radius))
  381. const far = Math.min(Math.max(near + radius, dist1 * (dist + radius)), camera.userData.maxFarPlane ?? 1000)
  382. // old way, has issues when panning very far from the camera target
  383. // const near = Math.max(camera.userData.minNearPlane ?? 0.2, dist - radius)
  384. // const far = Math.min(Math.max(near + 1, dist + radius), camera.userData.maxFarPlane ?? 1000)
  385. camera.near = near
  386. camera.far = far
  387. // todo try using minimum of all 6 endpoints of bbox.
  388. // camera.near = 3
  389. // camera.far = 20
  390. }
  391. updateShaderProperties(material: {defines: Record<string, string|number|undefined>, uniforms: {[name: string]: IUniform}}): this {
  392. if (material.uniforms.sceneBoundingRadius) material.uniforms.sceneBoundingRadius.value = this._sceneBoundingRadius
  393. else console.warn('BaseRenderer: no uniform: frameCount')
  394. return this
  395. }
  396. /**
  397. * Serialize the scene properties
  398. * @param meta
  399. * @returns {any}
  400. */
  401. toJSON(meta?: any): any {
  402. const o = ThreeSerialization.Serialize(this, meta, true)
  403. // console.log(o)
  404. return o
  405. }
  406. /**
  407. * Deserialize the scene properties
  408. * @param json - object from {@link toJSON}
  409. * @param meta
  410. * @returns {this<TCamera>}
  411. */
  412. fromJSON(json: any, meta?: any): this {
  413. const env = json.environment
  414. if (env !== undefined) {
  415. this.environment = ThreeSerialization.Deserialize(env, this.environment, meta, false)
  416. delete json.environment
  417. }
  418. ThreeSerialization.Deserialize(json, this, meta, true)
  419. json.environment = env
  420. return this
  421. }
  422. addEventListener<T extends ISceneEventTypes>(type: T, listener: EventListener<ISceneEvent, T, this>): void {
  423. if (type === 'activeCameraChange') console.error('activeCameraChange is deprecated. Use mainCameraChange instead.')
  424. if (type === 'activeCameraUpdate') console.error('activeCameraUpdate is deprecated. Use mainCameraUpdate instead.')
  425. if (type === 'sceneMaterialUpdate') console.error('sceneMaterialUpdate is deprecated. Use materialUpdate instead.')
  426. if (type === 'update') console.error('update is deprecated. Use sceneUpdate instead.')
  427. super.addEventListener(type, listener)
  428. }
  429. // region inherited type fixes
  430. // re-declaring from IObject3D because: https://github.com/microsoft/TypeScript/issues/16936
  431. traverse: (callback: (object: IObject3D) => void) => void
  432. traverseVisible: (callback: (object: IObject3D) => void) => void
  433. traverseAncestors: (callback: (object: IObject3D) => void) => void
  434. getObjectById: <T extends IObject3D = IObject3D>(id: number) => T | undefined
  435. getObjectByName: <T extends IObject3D = IObject3D>(name: string) => T | undefined
  436. getObjectByProperty: <T extends IObject3D = IObject3D>(name: string, value: string) => T | undefined
  437. copy: (source: this, recursive?: boolean, ...args: any[]) => this
  438. clone: (recursive?: boolean) => this
  439. remove: (...object: IObject3D[]) => this
  440. dispatchEvent: (event: ISceneEvent) => void
  441. parent: null
  442. children: IObject3D[]
  443. // endregion
  444. // region deprecated
  445. // /**
  446. // * Set the scene environment map, this will be processed with PMREM automatically later.
  447. // * @param asset
  448. // * @returns {void}
  449. // */
  450. // public setEnvironment(asset: ITexture|null|undefined): void {
  451. // if (!asset) {
  452. // // eslint-disable-next-line deprecation/deprecation
  453. // this.environment = null
  454. // this._onEnvironmentChange()
  455. // return
  456. // }
  457. // if (!asset.isTexture) {
  458. // console.error('Unknown Environment type', asset)
  459. // return
  460. // }
  461. // if (asset.mapping === UVMapping) {
  462. // asset.mapping = EquirectangularReflectionMapping // for PMREMGenerator
  463. // asset.needsUpdate = true
  464. // }
  465. // // eslint-disable-next-line deprecation/deprecation
  466. // this.environment = asset
  467. // // eslint-disable-next-line deprecation/deprecation
  468. // // this.background = texture // for testing.
  469. // this._onEnvironmentChange()
  470. // }
  471. //
  472. // /**
  473. // * Get the current scene environment map
  474. // * @returns {ITexture<Texture>}
  475. // */
  476. // getEnvironment(): ITexture | null {
  477. // return this.environment || null
  478. // }
  479. /**
  480. * Find objects by name exact match in the complete hierarchy.
  481. * @deprecated Use {@link getObjectByName} instead.
  482. * @param name - name
  483. * @param parent - optional root node to start search from
  484. * @returns Array of found objects
  485. */
  486. public findObjectsByName(name: string, parent?: IObject3D): IObject3D[] {
  487. const o: IObject3D[] = [];
  488. (parent ?? this).traverse(object => {
  489. if (object.name === name) o.push(object)
  490. })
  491. return o
  492. }
  493. /**
  494. * @deprecated
  495. * Sets the camera pointing towards the object at a specific distance.
  496. * @param rootObject - The object to point at.
  497. * @param centerOffset - The distance offset from the object to point at.
  498. * @param targetOffset - The distance offset for the target from the center of object to point at.
  499. * @param options - Not used yet.
  500. */
  501. resetCamera(rootObject:Object3D|undefined = undefined, centerOffset = new Vector3(1, 1, 1), targetOffset = new Vector3(0, 0, 0)): void {
  502. if (this._mainCamera) {
  503. this.matrixWorldNeedsUpdate = true
  504. this.updateMatrixWorld(true)
  505. const bounds = rootObject ? new Box3B().expandByObject(rootObject, true, true) : this.getBounds(true)
  506. const center = bounds.getCenter(new Vector3())
  507. const radius = bounds.getSize(new Vector3()).length() * 0.5
  508. center.add(targetOffset.clone().multiplyScalar(radius))
  509. this._mainCamera.position = new Vector3( // todo: for nested cameras?
  510. center.x + centerOffset.x * radius,
  511. center.y + centerOffset.y * radius,
  512. center.z + centerOffset.z * radius,
  513. )
  514. this._mainCamera.target = center
  515. // this.scene.mainCamera.controls?.targetOffset.set(0, 0, 0)
  516. this.setDirty()
  517. }
  518. }
  519. /**
  520. * Minimum Camera near plane
  521. * @deprecated - use camera.minNearPlane instead
  522. */
  523. get minNearDistance(): number {
  524. console.error('minNearDistance is deprecated. Use camera.userData.minNearPlane instead')
  525. return this.mainCamera.userData.minNearPlane ?? 0.02
  526. }
  527. /**
  528. * @deprecated - use camera.minNearPlane instead
  529. */
  530. set minNearDistance(value: number) {
  531. console.error('minNearDistance is deprecated. Use camera.userData.minNearPlane instead')
  532. if (this.mainCamera)
  533. this.mainCamera.userData.minNearPlane = value
  534. }
  535. /**
  536. * @deprecated
  537. */
  538. get activeCamera(): ICamera {
  539. console.error('activeCamera is deprecated. Use mainCamera instead.')
  540. return this.mainCamera
  541. }
  542. /**
  543. * @deprecated
  544. */
  545. set activeCamera(camera: ICamera | undefined) {
  546. console.error('activeCamera is deprecated. Use mainCamera instead.')
  547. this.mainCamera = camera
  548. }
  549. /**
  550. * Get the threejs scene object
  551. * @deprecated
  552. */
  553. get modelObject(): this {
  554. return this as any
  555. }
  556. /**
  557. * @deprecated use {@link envMapIntensity} instead
  558. */
  559. get environmentIntensity(): number {
  560. return this.envMapIntensity
  561. }
  562. /**
  563. * @deprecated use {@link envMapIntensity} instead
  564. */
  565. set environmentIntensity(value: number) {
  566. this.envMapIntensity = value
  567. }
  568. /**
  569. * Add any processed scene object to the scene.
  570. * @deprecated renamed to {@link addObject}
  571. * @param imported
  572. * @param options
  573. */
  574. addSceneObject<T extends IObject3D|Object3D = IObject3D>(imported: T, options?: AddObjectOptions): T {
  575. return this.addObject(imported, options)
  576. }
  577. /**
  578. * Equivalent to setDirty({refreshScene: true}), dispatches 'sceneUpdate' event with the specified options.
  579. * @deprecated use refreshScene
  580. * @param options
  581. */
  582. updateScene(options?: AnyOptions): this {
  583. console.warn('updateScene is deprecated. Use refreshScene instead')
  584. return this.refreshScene(options || {})
  585. }
  586. /**
  587. * @deprecated renamed to {@link clearSceneModels}
  588. */
  589. removeSceneModels() {
  590. this.clearSceneModels()
  591. }
  592. // endregion
  593. }