threepipe
Version:
A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.
698 lines • 29.1 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { Color, EquirectangularReflectionMapping, Object3D, Scene, UVMapping, Vector3, } from 'three';
import { autoGPUInstanceMeshes, bindToValue, Box3B } from '../../three';
import { onChange2, onChange3, serialize } from 'ts-browser-helpers';
import { PerspectiveCamera2 } from '../camera/PerspectiveCamera2';
import { ThreeSerialization } from '../../utils';
import { iObjectCommons } from './iObjectCommons';
import { uiButton, uiColor, uiConfig, uiFolderContainer, uiImage, uiSlider, uiToggle } from 'uiconfig.js';
import { getFittingDistance } from '../../three/utils/camera';
let RootScene = class RootScene extends Scene {
get mainCamera() {
return this._mainCamera || this._dummyCam;
}
set mainCamera(camera) {
const cam = this.mainCamera;
if (!camera)
camera = this.defaultCamera;
if (cam === camera)
return;
if (cam) {
cam.deactivateMain(undefined, true);
cam.removeEventListener('cameraUpdate', this._mainCameraUpdate);
}
if (camera) {
this._mainCamera = camera;
camera.addEventListener('cameraUpdate', this._mainCameraUpdate);
camera.activateMain(undefined, true);
}
else {
this._mainCamera = null;
}
this.dispatchEvent({ type: 'activeCameraChange', lastCamera: cam, camera }); // deprecated
this.dispatchEvent({ type: 'mainCameraChange', lastCamera: cam, camera });
this.setDirty();
}
get renderCamera() {
return this._renderCamera ?? this.mainCamera;
}
set renderCamera(camera) {
const cam = this._renderCamera;
this._renderCamera = camera;
this.dispatchEvent({ type: 'renderCameraChange', lastCamera: cam, camera });
}
/**
* Create a scene instance. This is done automatically in the {@link ThreeViewer} and must not be created separately.
* @param camera
* @param objectProcessor
*/
constructor(camera, objectProcessor) {
super();
this.isRootScene = true;
this.assetType = 'model';
// private _processors = new ObjectProcessorMap<'environment' | 'background'>()
// private _sceneObjects: ISceneObject[] = []
this._mainCamera = null;
this.backgroundColor = null; // read in three.js WebGLBackground
this.background = null;
/**
* The intensity for the environment light.
*/
this.backgroundIntensity = 1;
/**
* The default environment map used when rendering materials in the scene
*/
this.environment = null;
/**
* The intensity for the environment light.
*/
this.envMapIntensity = 1;
/**
* Rotation in radians of the default environment map.
* Same as {@link environment}.rotation.
*
* Note - this is not serialized here, but inside the texture.
*/
this.envMapRotation = 0;
/**
* Extra textures/envmaps that can be used by objects/materials/plugins and will be serialized.
*/
this.textureSlots = {};
/**
* Fixed direction environment reflections irrespective of camera position.
*/
this.fixedEnvMapDirection = false;
// private _environmentLight?: IEnvironmentLight
// required just because we don't want activeCamera to be null.
this._dummyCam = new PerspectiveCamera2('');
this._mainCameraUpdate = (e) => {
this.setDirty({ refreshScene: false });
this.refreshActiveCameraNearFar();
if (e.key === 'fov')
this.dollyActiveCameraFov();
this.dispatchEvent({ ...e, type: 'mainCameraUpdate' });
this.dispatchEvent({ ...e, type: 'activeCameraUpdate' }); // deprecated
};
// cached values
this._sceneBounds = new Box3B;
this._sceneBoundingRadius = 0;
this.refreshUi = iObjectCommons.refreshUi.bind(this);
this._v1 = new Vector3();
this._v2 = new Vector3();
/**
* For Programmatically toggling autoNearFar. This property is not supposed to be in the UI or serialized.
* Use camera.userData.autoNearFar for UI and serialization
* This is used in PickingPlugin
* autoNearFar will still be disabled if this is true and camera.userData.autoNearFar is false
*/
this.autoNearFarEnabled = true;
this.setDirty = this.setDirty.bind(this);
iObjectCommons.upgradeObject3D.call(this, undefined, objectProcessor);
// this is called from parentDispatch since scene is a parent.
this.addEventListener('materialUpdate', (e) => this.dispatchEvent({ ...e, type: 'sceneMaterialUpdate' }));
this.addEventListener('objectUpdate', this.refreshScene);
this.addEventListener('geometryUpdate', this.refreshScene);
this.addEventListener('geometryChanged', this.refreshScene);
this.defaultCamera = camera;
this.modelRoot = new Object3D();
this.modelRoot.userData.rootSceneModelRoot = true;
this.modelRoot.name = 'Scene'; // for the UI
// this.modelRoot.addEventListener('update', this.setDirty) // todo: where was this dispatched from/used ?
// eslint-disable-next-line deprecation/deprecation
this.add(this.modelRoot);
// this.addSceneObject(this.modelRoot as any, {addToRoot: true, autoScale: false})
// this.addSceneObject(this.defaultCamera, {addToRoot: true})
// eslint-disable-next-line deprecation/deprecation
this.add(this.defaultCamera);
this.mainCamera = this.defaultCamera;
// this.boxHelper = new Box3Helper(this.getBounds())
// this.boxHelper.userData.bboxVisible = false
// this.boxHelper.visible = false
// this.add(this.boxHelper)
}
/**
* Add a widget (non-physical/interactive) object to the scene. like gizmos, ui components etc.
* @param model
* @param options
*/
// addWidget(model: IWidget, options: AnyOptions = {}): void {
// if (model.assetType !== 'widget') {
// console.warn('Invalid asset type for ', model, ', adding anyway')
// }
// this.add(model.modelObject)
//
// // todo: dispatch event, add event listeners, etc
// }
/**
* Add any object to the scene.
* @param imported
* @param options
*/
addObject(imported, options) {
if (options?.clearSceneObjects || options?.disposeSceneObjects) {
this.clearSceneModels(options.disposeSceneObjects);
}
if (!imported)
return imported;
if (!imported.isObject3D) {
console.error('Invalid object, cannot add to scene.', imported);
return imported;
}
this._addObject3D(imported, options);
this.dispatchEvent({ type: 'addSceneObject', object: imported, options });
return imported;
}
/**
* Load model root scene exported to GLTF format. Used internally by {@link ThreeViewer.addSceneObject}.
* @param obj
* @param options
*/
loadModelRoot(obj, options) {
if (options?.clearSceneObjects || options?.disposeSceneObjects) {
this.clearSceneModels(options.disposeSceneObjects);
}
if (!obj.userData?.rootSceneModelRoot) {
console.error('Invalid model root scene object. Trying to add anyway.', obj);
}
if (obj.userData) {
// todo deep merge all userdata?
if (obj.userData.__importData)
this.modelRoot.userData.__importData = {
...this.modelRoot.userData.__importData,
...obj.userData.__importData,
};
if (obj.userData.gltfAsset) {
this.modelRoot.userData.__gltfAsset = {
...this.modelRoot.userData.__gltfAsset,
...obj.userData.gltfAsset,
};
}
if (obj.userData.gltfExtras)
this.modelRoot.userData.__gltfExtras = {
...this.modelRoot.userData.__gltfExtras,
...obj.userData.gltfExtras,
};
}
if (obj.userData?.gltfAsset?.copyright)
obj.children.forEach(c => !c.userData.license && (c.userData.license = obj.userData.gltfAsset?.copyright));
if (obj.animations) {
if (!this.modelRoot.animations)
this.modelRoot.animations = [];
for (const animation of obj.animations) {
if (this.modelRoot.animations.includes(animation))
continue;
this.modelRoot.animations.push(animation);
}
}
return [...obj.children] // need to clone
.map(c => this.addObject(c, { ...options, clearSceneObjects: false, disposeSceneObjects: false }));
}
_addObject3D(model, { autoCenter = false, centerGeometries = false, centerGeometriesKeepPosition = true, autoScale = false, autoScaleRadius = 2., addToRoot = false, license } = {}) {
const obj = model;
if (!obj) {
console.error('Invalid object, cannot add to scene.');
return;
}
// eslint-disable-next-line deprecation/deprecation
if (addToRoot)
this.add(obj);
else
this.modelRoot.add(obj);
if (autoCenter && !obj.userData.isCentered && !obj.userData.pseudoCentered) { // pseudoCentered is legacy
obj.autoCenter?.();
}
else {
obj.userData.isCentered = true; // mark as centered, so that autoCenter is not called again when file is reloaded.
}
if (autoScale && !obj.userData.autoScaled) {
obj.autoScale?.(obj.userData.autoScaleRadius || autoScaleRadius);
}
else {
obj.userData.autoScaled = true; // mark as auto-scaled, so that autoScale is not called again when file is reloaded.
}
if (centerGeometries && !obj.userData.geometriesCentered) {
this.centerAllGeometries(centerGeometriesKeepPosition, obj);
obj.userData.geometriesCentered = true;
}
else {
obj.userData.geometriesCentered = true; // mark as centered, so that geometry center is not called again when file is reloaded.
}
if (license)
obj.userData.license = [obj.userData.license, license].filter(v => v).join(', ');
this.setDirty({ refreshScene: true });
}
centerAllGeometries(keepPosition = true, obj) {
const geoms = new Set();
(obj ?? this.modelRoot).traverse((o) => o.geometry && geoms.add(o.geometry));
const undos = [];
geoms.forEach(g => undos.push(g.center2(undefined, keepPosition)));
return () => undos.forEach(u => u());
}
clearSceneModels(dispose = false, setDirty = true) {
if (dispose)
return this.disposeSceneModels(setDirty);
this.modelRoot.clear();
this.modelRoot.children = [];
setDirty && this.setDirty({ refreshScene: true });
}
disposeSceneModels(setDirty = true, clear = true) {
if (clear) {
[...this.modelRoot.children].forEach(child => child.dispose ? child.dispose() : child.removeFromParent());
this.modelRoot.clear();
if (setDirty)
this.setDirty({ refreshScene: true });
}
else {
this.modelRoot.children.forEach(child => child.dispose && child.dispose());
}
}
_onEnvironmentChange() {
// console.warn('environment changed')
if (this.environment?.mapping === UVMapping) {
this.environment.mapping = EquirectangularReflectionMapping; // for PMREMGenerator
this.environment.needsUpdate = true;
}
this.dispatchEvent({ type: 'environmentChanged', environment: this.environment });
this.setDirty({ refreshScene: true, geometryChanged: false });
this.refreshUi?.();
}
onBackgroundChange() {
this.dispatchEvent({ type: 'backgroundChanged', background: this.background, backgroundColor: this.backgroundColor });
this.setDirty({ refreshScene: true, geometryChanged: false });
this.refreshUi?.();
}
/**
* @deprecated Use {@link addObject}
*/
add(...object) {
super.add(...object);
// this._onSceneUpdate() // this is not needed, since it will be bubbled up from the object3d and we will get event objectUpdate
return this;
}
/**
* Sets the backgroundColor property from a string, number or Color, and updates the scene.
* @param color
*/
setBackgroundColor(color) {
this.backgroundColor = color ? new Color(color) : null;
}
/**
* Mark the scene dirty, and force render in the next frame.
* @param options - set `refreshScene` to true to mark that any object transformations have changed. It might trigger effects like frame fade depening on plugins.
* @returns {this}
*/
setDirty(options) {
// todo: for onChange calls -> check options.key for specific key that's changed and use it to determine refreshScene
if (options?.sceneUpdate) {
console.warn('sceneUpdate is deprecated, use refreshScene instead.');
options.refreshScene = true;
}
if (options?.refreshScene) {
this.refreshScene(options);
}
else {
this.dispatchEvent({ type: 'update' }); // todo remove
iObjectCommons.setDirty.call(this, { ...options, scene: this });
} // this sets dirty in the viewer
return this;
}
/**
* For visualizing the scene bounds. API incomplete.
* @type {Box3Helper}
*/
// readonly boxHelper: Box3Helper
refreshScene(event) {
if (event && event.type === 'objectUpdate' && event.object === this)
return this; // ignore self
// todo test the isCamera here. this is for animation object plugin
if (event?.sceneUpdate === false || event?.refreshScene === false || event?.object?.isCamera)
return this.setDirty(event); // so that it doesn't trigger frame fade, shadow refresh etc
// console.warn(event)
this.refreshActiveCameraNearFar();
// this.dollyActiveCameraFov()
this._sceneBounds = this.getBounds(false, true);
// this.boxHelper?.boxHelper?.copy?.(this._sceneBounds)
this._sceneBoundingRadius = this._sceneBounds.getSize(new Vector3()).length() / 2.;
this.dispatchEvent({ ...event, type: 'sceneUpdate', hierarchyChanged: ['addedToParent', 'removedFromParent'].includes(event?.change || '') });
iObjectCommons.setDirty.call(this, event);
return this;
}
/**
* Dispose the scene and clear all resources.
* @warn Not fully implemented yet, just clears the scene.
*/
dispose(clear = true) {
this.disposeSceneModels(false, clear);
if (clear) {
[...this.children].forEach(child => child.dispose ? child.dispose() : child.removeFromParent());
this.clear();
}
// todo: dispose more stuff?
this.environment?.dispose();
if (this.background?.isTexture)
this.background?.dispose?.();
if (clear) {
this.environment = null;
this.background = null;
}
return;
}
/**
* Returns the bounding box of the whole scene (model root and other meta objects).
* To get the bounds of just the objects added by the user(not by plugins) use `new Box3B().expandByObject(scene.modelRoot)`
* @param precise
* @param ignoreInvisible
* @param ignoreWidgets
* @param ignoreObject
* @returns {Box3B}
*/
getBounds(precise = false, ignoreInvisible = true, ignoreWidgets = true, ignoreObject) {
// See bboxVisible in userdata in Box3B
return new Box3B().expandByObject(this, precise, ignoreInvisible, (o) => {
if (ignoreWidgets && (o.isWidget || o.assetType === 'widget'))
return true;
return ignoreObject?.(o) ?? false;
});
}
/**
* Similar to {@link getBounds}, but returns the bounding box of just the {@link modelRoot}.
* @param precise
* @param ignoreInvisible
* @param ignoreWidgets
* @param ignoreObject
* @returns {Box3B}
*/
getModelBounds(precise = false, ignoreInvisible = true, ignoreWidgets = true, ignoreObject) {
if (this.modelRoot == undefined)
return new Box3B();
return new Box3B().expandByObject(this.modelRoot, precise, ignoreInvisible, (o) => {
if (ignoreWidgets && o.assetType === 'widget')
return true;
return ignoreObject?.(o) ?? false;
});
}
autoGPUInstanceMeshes() {
const geoms = new Set();
this.modelRoot.traverse((o) => o.geometry && geoms.add(o.geometry));
geoms.forEach((g) => autoGPUInstanceMeshes(g));
}
/**
* Refreshes the scene active camera near far values, based on the scene bounding box.
* This is called automatically every time the camera is updated.
*/
refreshActiveCameraNearFar() {
const camera = this.mainCamera;
if (!camera)
return;
if (!this.autoNearFarEnabled || camera.userData.autoNearFar === false) {
camera.near = camera.userData.minNearPlane ?? 0.5;
camera.far = camera.userData.maxFarPlane ?? 1000;
return;
}
// 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
const bbox = this.getBounds(false); // todo: can we use this._sceneBounds or will it have some issue with animation?
camera.getWorldPosition(this._v1).sub(bbox.getCenter(this._v2));
const radius = 1.5 * bbox.getSize(this._v2).length() / 2.;
const dist = this._v1.length();
// new way
const dist1 = Math.max(0.1, -this._v1.normalize().dot(camera.getWorldDirection(new Vector3())));
const near = Math.max(Math.max(camera.userData.minNearPlane ?? 0.5, 0.001), dist1 * (dist - radius));
const far = Math.min(Math.max(near + radius, dist1 * (dist + radius)), camera.userData.maxFarPlane ?? 1000);
// old way, has issues when panning very far from the camera target
// const near = Math.max(camera.userData.minNearPlane ?? 0.2, dist - radius)
// const far = Math.min(Math.max(near + 1, dist + radius), camera.userData.maxFarPlane ?? 1000)
camera.near = near;
camera.far = far;
// todo try using minimum of all 6 endpoints of bbox.
// camera.near = 3
// camera.far = 20
}
/**
* Refreshes the scene active camera near far values, based on the scene bounding box.
* This is called automatically every time the camera fov is updated.
*/
dollyActiveCameraFov() {
const camera = this.mainCamera;
if (!camera)
return;
if (!camera.userData.dollyFov) {
return;
}
const bbox = this.getModelBounds(false, true, true);
// todo this is not exact because of 1.5, this needs to be calculated based on current position and last fov
const cameraZ = getFittingDistance(camera, bbox) * 1.5;
const direction = new Vector3().subVectors(camera.target, camera.position).normalize();
camera.position.copy(direction.multiplyScalar(-cameraZ).add(camera.target));
camera.setDirty();
}
updateShaderProperties(material) {
if (material.uniforms.sceneBoundingRadius)
material.uniforms.sceneBoundingRadius.value = this._sceneBoundingRadius;
else
console.warn('RootScene: no uniform: sceneBoundingRadius');
return this;
}
/**
* Serialize the scene properties
* @param meta
* @returns {any}
*/
toJSON(meta) {
const o = ThreeSerialization.Serialize(this, meta, true);
// console.log(o)
return o;
}
/**
* Deserialize the scene properties
* @param json - object from {@link toJSON}
* @param meta
* @returns {this<TCamera>}
*/
fromJSON(json, meta) {
const env = json.environment;
if (env !== undefined) {
this.environment = ThreeSerialization.Deserialize(env, this.environment, meta, false);
delete json.environment;
}
ThreeSerialization.Deserialize(json, this, meta, true);
json.environment = env;
return this;
}
addEventListener(type, listener) {
if (type === 'activeCameraChange')
console.error('activeCameraChange is deprecated. Use mainCameraChange instead.');
if (type === 'activeCameraUpdate')
console.error('activeCameraUpdate is deprecated. Use mainCameraUpdate instead.');
if (type === 'sceneMaterialUpdate')
console.error('sceneMaterialUpdate is deprecated. Use materialUpdate instead.');
if (type === 'update')
console.error('update is deprecated. Use sceneUpdate instead.');
super.addEventListener(type, listener);
}
// endregion
// region deprecated
// /**
// * Set the scene environment map, this will be processed with PMREM automatically later.
// * @param asset
// * @returns {void}
// */
// public setEnvironment(asset: ITexture|null|undefined): void {
// if (!asset) {
// // eslint-disable-next-line deprecation/deprecation
// this.environment = null
// this._onEnvironmentChange()
// return
// }
// if (!asset.isTexture) {
// console.error('Unknown Environment type', asset)
// return
// }
// if (asset.mapping === UVMapping) {
// asset.mapping = EquirectangularReflectionMapping // for PMREMGenerator
// asset.needsUpdate = true
// }
// // eslint-disable-next-line deprecation/deprecation
// this.environment = asset
// // eslint-disable-next-line deprecation/deprecation
// // this.background = texture // for testing.
// this._onEnvironmentChange()
// }
//
// /**
// * Get the current scene environment map
// * @returns {ITexture<Texture>}
// */
// getEnvironment(): ITexture | null {
// return this.environment || null
// }
/**
* Find objects by name exact match in the complete hierarchy.
* @deprecated Use {@link getObjectByName} instead.
* @param name - name
* @param parent - optional root node to start search from
* @returns Array of found objects
*/
findObjectsByName(name, parent) {
const o = [];
(parent ?? this).traverse(object => {
if (object.name === name)
o.push(object);
});
return o;
}
/**
* @deprecated
* Sets the camera pointing towards the object at a specific distance.
* @param rootObject - The object to point at.
* @param centerOffset - The distance offset from the object to point at.
* @param targetOffset - The distance offset for the target from the center of object to point at.
* @param options - Not used yet.
*/
resetCamera(rootObject = undefined, centerOffset = new Vector3(1, 1, 1), targetOffset = new Vector3(0, 0, 0)) {
if (this._mainCamera) {
this.matrixWorldNeedsUpdate = true;
this.updateMatrixWorld(true);
const bounds = rootObject ? new Box3B().expandByObject(rootObject, true, true) : this.getBounds(true);
const center = bounds.getCenter(new Vector3());
const radius = bounds.getSize(new Vector3()).length() * 0.5;
center.add(targetOffset.clone().multiplyScalar(radius));
this._mainCamera.position = new Vector3(// todo: for nested cameras?
center.x + centerOffset.x * radius, center.y + centerOffset.y * radius, center.z + centerOffset.z * radius);
this._mainCamera.target = center;
// this.scene.mainCamera.controls?.targetOffset.set(0, 0, 0)
this.setDirty();
}
}
/**
* Minimum Camera near plane
* @deprecated - use camera.minNearPlane instead
*/
get minNearDistance() {
console.error('minNearDistance is deprecated. Use camera.userData.minNearPlane instead');
return this.mainCamera.userData.minNearPlane ?? 0.02;
}
/**
* @deprecated - use camera.minNearPlane instead
*/
set minNearDistance(value) {
console.error('minNearDistance is deprecated. Use camera.userData.minNearPlane instead');
if (this.mainCamera)
this.mainCamera.userData.minNearPlane = value;
}
/**
* @deprecated
*/
get activeCamera() {
console.error('activeCamera is deprecated. Use mainCamera instead.');
return this.mainCamera;
}
/**
* @deprecated
*/
set activeCamera(camera) {
console.error('activeCamera is deprecated. Use mainCamera instead.');
this.mainCamera = camera;
}
/**
* Get the threejs scene object
* @deprecated
*/
get modelObject() {
return this;
}
/**
* @deprecated use {@link envMapIntensity} instead
*/
get environmentIntensity() {
return this.envMapIntensity;
}
/**
* @deprecated use {@link envMapIntensity} instead
*/
set environmentIntensity(value) {
this.envMapIntensity = value;
}
/**
* Add any processed scene object to the scene.
* @deprecated renamed to {@link addObject}
* @param imported
* @param options
*/
addSceneObject(imported, options) {
return this.addObject(imported, options);
}
/**
* Equivalent to setDirty({refreshScene: true}), dispatches 'sceneUpdate' event with the specified options.
* @deprecated use refreshScene
* @param options
*/
updateScene(options) {
console.warn('updateScene is deprecated. Use refreshScene instead');
return this.refreshScene(options || {});
}
/**
* @deprecated renamed to {@link clearSceneModels}
*/
removeSceneModels() {
this.clearSceneModels();
}
};
__decorate([
uiColor('Background Color', (s) => ({
onChange: () => s?.onBackgroundChange(),
})),
serialize(),
onChange2(RootScene.prototype.onBackgroundChange)
], RootScene.prototype, "backgroundColor", void 0);
__decorate([
onChange2(RootScene.prototype.onBackgroundChange),
serialize(),
uiImage('Background Image')
], RootScene.prototype, "background", void 0);
__decorate([
serialize(),
onChange3(RootScene.prototype.setDirty),
uiSlider('Background Intensity', [0, 10], 0.01)
], RootScene.prototype, "backgroundIntensity", void 0);
__decorate([
uiImage('Environment'),
serialize(),
onChange2(RootScene.prototype._onEnvironmentChange)
], RootScene.prototype, "environment", void 0);
__decorate([
uiSlider('Environment Intensity', [0, 10], 0.01),
serialize(),
onChange3(RootScene.prototype.setDirty)
], RootScene.prototype, "envMapIntensity", void 0);
__decorate([
uiSlider('Environment Rotation', [-Math.PI, Math.PI], 0.01),
bindToValue({ obj: 'environment', key: 'rotation', onChange: RootScene.prototype.setDirty, onChangeParams: false })
], RootScene.prototype, "envMapRotation", void 0);
__decorate([
serialize()
], RootScene.prototype, "textureSlots", void 0);
__decorate([
uiToggle('Fixed Env Direction'),
serialize(),
onChange3(RootScene.prototype.setDirty)
], RootScene.prototype, "fixedEnvMapDirection", void 0);
__decorate([
uiConfig(),
serialize()
], RootScene.prototype, "defaultCamera", void 0);
__decorate([
uiButton('Center All Geometries', { sendArgs: false })
], RootScene.prototype, "centerAllGeometries", null);
__decorate([
uiButton('Auto GPU Instance Meshes')
], RootScene.prototype, "autoGPUInstanceMeshes", null);
RootScene = __decorate([
uiFolderContainer('Root Scene')
], RootScene);
export { RootScene };
//# sourceMappingURL=RootScene.js.map