UNPKG

@shopware-ag/dive

Version:

Shopware Spatial Framework

427 lines (385 loc) 14.3 kB
import { Box3, Color, Object3D } from 'three'; import DIVEAmbientLight from '../../light/AmbientLight.ts'; import DIVEPointLight from '../../light/PointLight.ts'; import DIVESceneLight from '../../light/SceneLight.ts'; import { DIVEModel } from '../../model/Model.ts'; import { DIVELoadingManager } from '../../loadingmanager/LoadingManager.ts'; import { DIVECommunication } from '../../com/Communication.ts'; import { DIVEPrimitive } from '../../primitive/Primitive.ts'; import { type DIVEScene } from '../Scene.ts'; import { type TransformControls } from 'three/examples/jsm/controls/TransformControls'; import { type COMLight, type COMModel, type COMEntity, type COMPrimitive, type COMGroup, } from '../../com/types'; import { type DIVESceneObject } from '../../types'; import { DIVEGroup } from '../../group/Group.ts'; /** * A basic scene node to hold grid, floor and all lower level roots. * * @module */ export class DIVERoot extends Object3D { readonly isDIVERoot: true = true; private loadingManager: DIVELoadingManager; constructor() { super(); this.name = 'Root'; this.loadingManager = new DIVELoadingManager(); } public ComputeSceneBB(): Box3 { const bb = new Box3(); this.traverse((object: Object3D) => { if ('isObject3D' in object) { bb.expandByObject(object); } }); return bb; } public GetSceneObject<T extends DIVESceneObject>( object: Partial<COMEntity> & { id: string }, ): T | undefined { let foundObject: T | undefined; this.traverse((object3D) => { if (foundObject) return; if (object3D.userData.id === object.id) { foundObject = object3D as T; } }); return foundObject; } public AddSceneObject(object: COMEntity): void { switch (object.entityType) { case 'pov': { break; } case 'light': { this.updateLight(object as COMLight); break; } case 'model': { this.updateModel(object); break; } case 'primitive': { this.updatePrimitive(object); break; } case 'group': { this.updateGroup(object); break; } default: { console.warn( `DIVERoot.AddSceneObject: Unknown entity type: ${object.entityType}`, ); } } } public UpdateSceneObject( object: Partial<COMEntity> & { id: string; entityType: string }, ): void { switch (object.entityType) { case 'pov': { break; } case 'light': { this.updateLight(object as COMLight); break; } case 'model': { this.updateModel(object); break; } case 'primitive': { this.updatePrimitive(object); break; } case 'group': { this.updateGroup(object); break; } default: { console.warn( `DIVERoot.UpdateSceneObject: Unknown entity type: ${object.entityType}`, ); } } } public DeleteSceneObject( object: Partial<COMEntity> & { id: string; entityType: string }, ): void { switch (object.entityType) { case 'pov': { break; } case 'light': { this.deleteLight(object); break; } case 'model': { this.deleteModel(object); break; } case 'primitive': { this.deletePrimitive(object); break; } case 'group': { this.deleteGroup(object); break; } default: { console.warn( `DIVERoot.DeleteSceneObject: Unknown entity type: ${object.entityType}`, ); } } } public PlaceOnFloor( object: Partial<COMEntity> & { id: string; entityType: string }, ): void { switch (object.entityType) { case 'pov': case 'light': { break; } case 'model': case 'primitive': { this.placeOnFloor(object); break; } default: { console.warn( `DIVERoot.PlaceOnFloor: Unknown entity type: ${object.entityType}`, ); } } } private updateLight( light: Partial<COMLight> & { id: string; entityType: string; type: string; }, ): void { let sceneObject = this.GetSceneObject(light); if (!sceneObject) { switch (light.type) { case 'scene': { sceneObject = new DIVESceneLight(); break; } case 'ambient': { sceneObject = new DIVEAmbientLight(); break; } case 'point': { sceneObject = new DIVEPointLight(); break; } default: { console.warn( `DIVERoot.updateLight: Unknown light type: ${light.type}`, ); return; } } sceneObject.userData.id = light.id; this.add(sceneObject); } if (light.name !== undefined && light.name !== null) sceneObject.name = light.name; if (light.position !== undefined && light.position !== null) sceneObject.position.set( light.position.x, light.position.y, light.position.z, ); if (light.intensity !== undefined && light.intensity !== null) (sceneObject as DIVEAmbientLight | DIVEPointLight).SetIntensity( light.intensity, ); if (light.enabled !== undefined && light.enabled !== null) (sceneObject as DIVEAmbientLight | DIVEPointLight).SetEnabled( light.enabled, ); if (light.color !== undefined && light.color !== null) (sceneObject as DIVEAmbientLight | DIVEPointLight).SetColor( new Color(light.color), ); if (light.visible !== undefined && light.visible !== null) (sceneObject as DIVEAmbientLight | DIVEPointLight).visible = light.visible; if (light.parentId !== undefined) this.setParent({ ...light, parentId: light.parentId }); } private updateModel(model: Partial<COMModel> & { id: string }): void { let sceneObject = this.GetSceneObject<DIVESceneObject>(model); if (!sceneObject) { const created = new DIVEModel(); sceneObject = created; sceneObject.userData.id = model.id; sceneObject.userData.uri = model.uri; this.add(sceneObject); } if (model.uri !== undefined) { this.loadingManager.LoadGLTF(model.uri).then((gltf) => { (sceneObject as DIVEModel).SetModel(gltf); DIVECommunication.get(model.id!)?.PerformAction( 'MODEL_LOADED', { id: model.id! }, ); }); } if (model.name !== undefined) sceneObject.name = model.name; if (model.position !== undefined) (sceneObject as DIVEModel).SetPosition(model.position); if (model.rotation !== undefined) (sceneObject as DIVEModel).SetRotation(model.rotation); if (model.scale !== undefined) (sceneObject as DIVEModel).SetScale(model.scale); if (model.visible !== undefined) (sceneObject as DIVEModel).SetVisibility(model.visible); if (model.material !== undefined) (sceneObject as DIVEModel).SetMaterial(model.material); if (model.parentId !== undefined) this.setParent({ ...model, parentId: model.parentId }); } private updatePrimitive( primitive: Partial<COMPrimitive> & { id: string }, ): void { let sceneObject = this.GetSceneObject<DIVESceneObject>(primitive); if (!sceneObject) { const created = new DIVEPrimitive(); sceneObject = created; sceneObject.userData.id = primitive.id; this.add(sceneObject); } if (primitive.name !== undefined) sceneObject.name = primitive.name; if (primitive.geometry !== undefined) (sceneObject as DIVEPrimitive).SetGeometry(primitive.geometry); if (primitive.position !== undefined) (sceneObject as DIVEPrimitive).SetPosition(primitive.position); if (primitive.rotation !== undefined) (sceneObject as DIVEPrimitive).SetRotation(primitive.rotation); if (primitive.scale !== undefined) (sceneObject as DIVEPrimitive).SetScale(primitive.scale); if (primitive.visible !== undefined) (sceneObject as DIVEPrimitive).SetVisibility(primitive.visible); if (primitive.material !== undefined) (sceneObject as DIVEPrimitive).SetMaterial(primitive.material); if (primitive.parentId !== undefined) this.setParent({ ...primitive, parentId: primitive.parentId }); } private updateGroup(group: Partial<COMGroup> & { id: string }): void { let sceneObject = this.GetSceneObject<DIVESceneObject>(group); if (!sceneObject) { const created = new DIVEGroup(); sceneObject = created; sceneObject.userData.id = group.id; this.add(sceneObject); } if (group.name !== undefined) sceneObject.name = group.name; if (group.position !== undefined) (sceneObject as DIVEGroup).SetPosition(group.position); if (group.rotation !== undefined) (sceneObject as DIVEGroup).SetRotation(group.rotation); if (group.scale !== undefined) (sceneObject as DIVEGroup).SetScale(group.scale); if (group.visible !== undefined) (sceneObject as DIVEGroup).SetVisibility(group.visible); if (group.bbVisible !== undefined) (sceneObject as DIVEGroup).SetLinesVisibility(group.bbVisible); if (group.parentId !== undefined) this.setParent({ ...group, parentId: group.parentId }); } private deleteLight(light: Partial<COMLight> & { id: string }): void { const sceneObject = this.GetSceneObject(light); if (!sceneObject) { console.warn( `DIVERoot.deleteLight: Light with id ${light.id} not found`, ); return; } this.detachTransformControls(sceneObject); sceneObject.parent!.remove(sceneObject); } private deleteModel(model: Partial<COMModel> & { id: string }): void { const sceneObject = this.GetSceneObject(model); if (!sceneObject) { console.warn( `DIVERoot.deleteModel: Model with id ${model.id} not found`, ); return; } this.detachTransformControls(sceneObject); sceneObject.parent!.remove(sceneObject); } private deletePrimitive( primitive: Partial<COMPrimitive> & { id: string }, ): void { const sceneObject = this.GetSceneObject(primitive); if (!sceneObject) { console.warn( `DIVERoot.deletePrimitive: Primitive with id ${primitive.id} not found`, ); return; } this.detachTransformControls(sceneObject); sceneObject.parent!.remove(sceneObject); } private deleteGroup(group: Partial<COMGroup> & { id: string }): void { const sceneObject = this.GetSceneObject<DIVEGroup>(group); if (!sceneObject) { console.warn( `DIVERoot.deleteGroup: Group with id ${group.id} not found`, ); return; } this.detachTransformControls(sceneObject); for (let i = sceneObject.members.length - 1; i >= 0; i--) { this.attach(sceneObject.members[i]); } sceneObject.parent!.remove(sceneObject); } private placeOnFloor(object: Partial<COMEntity> & { id: string }): void { const sceneObject = this.GetSceneObject(object); if (!sceneObject) return; (sceneObject as DIVEModel | DIVEPrimitive).PlaceOnFloor(); } private setParent( object: Partial<COMEntity> & { id: string; parentId: string | null }, ): void { const sceneObject = this.GetSceneObject<DIVESceneObject>(object); if (!sceneObject) return; if (object.parentId !== null) { const parent = this.GetSceneObject<DIVESceneObject>({ id: object.parentId, }); if (!parent) return; // attach to new parent (if exists in scene) parent.attach(sceneObject); } else { // attach to root if no parent is found this.attach(sceneObject); } } private detachTransformControls(object: Object3D): void { // this is only neccessary due to using the old TransformControls instead of the new DIVEGizmo this.findScene(object).children.find((object) => { if ('isTransformControls' in object) { (object as TransformControls).detach(); } }); } private findScene(object: Object3D): DIVEScene { if (object.parent !== null) { return this.findScene(object.parent); } return object as DIVEScene; } }