UNPKG

@inweb/viewer-three

Version:

JavaScript library for rendering CAD and BIM files in a browser using Three.js

215 lines (178 loc) 7.97 kB
/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a // license agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// import { Box3, Object3D, Vector3 } from "three"; import { ILoader } from "@inweb/viewer-core"; import { IModelImpl } from "./IModelImpl"; import { Viewer } from "../Viewer"; // Basic model implementation. export class ModelImpl implements IModelImpl { public handle: string; public scene: Object3D; public loader: ILoader; public viewer: Viewer; constructor(scene: Object3D) { this.handle = "1"; this.scene = scene; } dispose() { function disposeMaterial(material: any) { // if (material.alphaMap) material.alphaMap.dispose(); // if (material.anisotropyMap) material.anisotropyMap.dispose(); // if (material.aoMap) material.aoMap.dispose(); // if (material.bumpMap) material.bumpMap.dispose(); // if (material.clearcoatMap) material.clearcoatMap.dispose(); // if (material.clearcoatNormalMap) material.clearcoatNormalMap.dispose(); // if (material.clearcoatRoughnessMap) material.clearcoatRoughnessMap.dispose(); // if (material.displacementMap) material.displacementMap.dispose(); // if (material.emissiveMap) material.emissiveMap.dispose(); // if (material.gradientMap) material.gradientMap.dispose(); // if (material.envMap) material.envMap.dispose(); // if (material.iridescenceMap) material.iridescenceMap.dispose(); // if (material.lightMap) material.lightMap.dispose(); // if (material.map) material.map.dispose(); // if (material.metalnessMap) material.metalnessMap.dispose(); // if (material.normalMap) material.normalMap.dispose(); // if (material.roughnessMap) material.roughnessMap.dispose(); // if (material.sheenColorMap) material.sheenColorMap.dispose(); // if (material.sheenRoughnessMap) material.sheenRoughnessMap.dispose(); // if (material.specularMap) material.specularMap.dispose(); // if (material.thicknessMap) material.thicknessMap.dispose(); // if (material.transmissionMap) material.transmissionMap.dispose(); material.dispose(); } function disposeMaterials(material: any) { const materials = Array.isArray(material) ? material : [material]; materials.forEach((material: any) => disposeMaterial(material)); } function disposeObject(object: any) { if (object.geometry) object.geometry.dispose(); if (object.material) disposeMaterials(object.material); } this.scene.traverse(disposeObject); this.scene.clear(); } getExtents(target: Box3): Box3 { this.scene.traverseVisible((object) => !object.children.length && target.expandByObject(object)); return target; } getObjects(): Object3D[] { const objects = []; this.scene.traverse((object) => objects.push(object)); return objects; } getVisibleObjects(): Object3D[] { const objects = []; this.scene.traverseVisible((object) => objects.push(object)); return objects; } hasObject(object: Object3D): boolean { while (object) { if (object === this.scene) return true; object = object.parent; } return false; } getOwnObjects(objects: Object3D | Object3D[]): Object3D[] { if (!Array.isArray(objects)) objects = [objects]; return objects.filter((object) => this.hasObject(object)); } getObjectsByHandles(handles: string | string[]): Object3D[] { const handleSet = new Set(handles); const objects = []; this.scene.traverse((object) => handleSet.has(object.userData.handle) && objects.push(object)); return objects; } getHandlesByObjects(objects: Object3D | Object3D[]): string[] { if (!Array.isArray(objects)) objects = [objects]; const handlesSet = new Set<string>(); this.getOwnObjects(objects).forEach((object) => handlesSet.add(object.userData.handle)); return Array.from(handlesSet); } hideObjects(objects: Object3D | Object3D[]): this { if (!Array.isArray(objects)) objects = [objects]; this.getOwnObjects(objects).forEach((object) => (object.visible = false)); return this; } hideAllObjects(): this { return this.isolateObjects([]); } isolateObjects(objects: Object3D | Object3D[]): this { if (!Array.isArray(objects)) objects = [objects]; const visibleSet = new Set(objects); this.getOwnObjects(objects).forEach((object) => object.traverseAncestors((parent) => visibleSet.add(parent))); this.scene.traverse((object) => (object.visible = visibleSet.has(object))); return this; } showObjects(objects: Object3D | Object3D[]): this { if (!Array.isArray(objects)) objects = [objects]; this.getOwnObjects(objects).forEach((object) => { object.visible = true; object.traverseAncestors((parent) => (parent.visible = true)); }); return this; } showAllObjects(): this { this.scene.traverse((object) => (object.visible = true)); return this; } showOriginalObjects(objects: Object3D | Object3D[]): this { return this; } hideOriginalObjects(objects: Object3D | Object3D[]): this { return this; } explode(scale = 0, coeff = 4): this { function calcExplodeDepth(object: Object3D, depth: number): number { let res = depth; object.children.forEach((x: Object3D) => { const objectDepth = calcExplodeDepth(x, depth + 1); if (res < objectDepth) res = objectDepth; }); object.userData.originalPosition = object.position.clone(); object.userData.originalCenter = new Box3().setFromObject(object).getCenter(new Vector3()); object.userData.isExplodeLocked = depth > 2 && object.children.length === 0; return res; } scale /= 100; if (!this.scene.userData.explodeDepth) this.scene.userData.explodeDepth = calcExplodeDepth(this.scene, 1); const maxDepth = this.scene.userData.explodeDepth; const scaledExplodeDepth = scale * maxDepth + 1; const explodeDepth = 0 | scaledExplodeDepth; const currentSegmentFraction = scaledExplodeDepth - explodeDepth; function explodeObject(object: Object3D, depth: number) { object.position.copy(object.userData.originalPosition); if (depth > 0 && depth <= explodeDepth && !object.userData.isExplodeLocked) { let objectScale = scale * coeff; if (depth === explodeDepth) objectScale *= currentSegmentFraction; const parentCenter = object.parent.userData.originalCenter; const objectCenter = object.userData.originalCenter; const objectOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale); object.position.add(objectOffset); } object.children.forEach((x) => explodeObject(x, depth + 1)); } explodeObject(this.scene, 0); this.scene.updateMatrixWorld(); return this; } }