UNPKG

@inweb/viewer-three

Version:

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

211 lines (175 loc) 7.68 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 { EdgesGeometry, LineBasicMaterial, MeshPhongMaterial, Object3D, RGBAFormat, UnsignedByteType, Vector2, WebGLRenderTarget, } from "three"; import { LineSegmentsGeometry } from "three/examples/jsm/lines/LineSegmentsGeometry.js"; import { Wireframe } from "three/examples/jsm/lines/Wireframe.js"; import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js"; import { IComponent, ResizeEvent } from "@inweb/viewer-core"; import { Viewer } from "../Viewer"; import { HighlighterUtils } from "./HighlighterUtils"; export class HighlighterComponent implements IComponent { protected viewer: Viewer; public renderTarget: WebGLRenderTarget; public facesMaterial: MeshPhongMaterial; public edgesMaterial: LineMaterial; public lineMaterial: LineBasicMaterial; public lineGlowMaterial: LineMaterial; constructor(viewer: Viewer) { this.viewer = viewer; const gl2 = viewer.canvas.getContext("webgl2"); if (gl2) { const size = viewer.renderer.getSize(new Vector2()); this.renderTarget = new WebGLRenderTarget(size.x, size.y, { format: RGBAFormat, stencilBuffer: false, samples: 4, type: UnsignedByteType, }); } this.viewer.addEventListener("databasechunk", this.geometryEnd); this.viewer.addEventListener("optionschange", this.optionsChange); this.viewer.addEventListener("resize", this.viewerResize); this.geometryEnd(); } dispose() { this.viewer.removeEventListener("databasechunk", this.geometryEnd); this.viewer.removeEventListener("optionschange", this.optionsChange); this.viewer.removeEventListener("resize", this.viewerResize); } highlight(objects: Object3D | Object3D[]) { const { edgesVisibility } = this.viewer.options; if (!Array.isArray(objects)) objects = [objects]; if (!objects.length) return; objects .filter((object) => !object.userData.isEdge) // <- filtering server generated edges .forEach((object: any) => { if (object.isHighlighted) return; if (object.isLine || object.isLineSegments) { const positions = object.geometry.attributes.position.array; const indices = object.geometry.index ? object.geometry.index.array : null; const lineGeometry = indices ? HighlighterUtils.fromIndexedLine(positions, indices) : HighlighterUtils.fromNonIndexedLine(positions, object.isLineSegments); const wireframe = new Wireframe(lineGeometry, this.lineGlowMaterial); wireframe.position.copy(object.position); wireframe.rotation.copy(object.rotation); wireframe.scale.copy(object.scale); wireframe.visible = edgesVisibility; object.parent.add(wireframe); object.userData.highlightWireframe = wireframe; object.userData.originalMaterial = object.material; object.material = this.lineMaterial; object.isHighlighted = true; } else if (object.isMesh) { const edgesGeometry = new EdgesGeometry(object.geometry, 89); const lineGeometry = new LineSegmentsGeometry().fromEdgesGeometry(edgesGeometry); const wireframe = new Wireframe(lineGeometry, this.edgesMaterial); wireframe.position.copy(object.position); wireframe.rotation.copy(object.rotation); wireframe.scale.copy(object.scale); wireframe.visible = edgesVisibility; object.parent.add(wireframe); object.userData.highlightWireframe = wireframe; object.userData.originalMaterial = object.material; object.material = this.facesMaterial; object.isHighlighted = true; } }); } unhighlight(objects: Object3D | Object3D[]) { if (!Array.isArray(objects)) objects = [objects]; if (!objects.length) return; objects.forEach((object: any) => { if (!object.isHighlighted) return; object.isHighlighted = false; object.material = object.userData.originalMaterial; object.userData.highlightWireframe.removeFromParent(); delete object.userData.originalMaterial; delete object.userData.highlightWireframe; }); } geometryEnd = () => { this.facesMaterial = new MeshPhongMaterial({ transparent: true, specular: 0x222222, shininess: 10, reflectivity: 0.05, polygonOffset: true, polygonOffsetFactor: 1, polygonOffsetUnits: 1, }); this.edgesMaterial = new LineMaterial({ linewidth: 1.5, resolution: new Vector2(window.innerWidth, window.innerHeight), }); this.lineMaterial = new LineBasicMaterial({ transparent: true, depthTest: true, depthWrite: true, }); this.lineGlowMaterial = new LineMaterial({ linewidth: 1.5, transparent: true, opacity: 0.8, resolution: new Vector2(window.innerWidth, window.innerHeight), }); this.syncHighlightColors(); }; optionsChange = () => { this.syncHighlightColors(); this.viewer.update(); }; syncHighlightColors() { const { facesColor, facesTransparancy, facesOverlap } = this.viewer.options; const { edgesColor, edgesVisibility, edgesOverlap } = this.viewer.options; this.facesMaterial.color.setRGB(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255); this.facesMaterial.opacity = (255 - facesTransparancy) / 255; this.facesMaterial.depthTest = !facesOverlap; this.facesMaterial.depthWrite = !facesOverlap; this.edgesMaterial.color.setRGB(edgesColor.r / 255, edgesColor.g / 255, edgesColor.b / 255); this.edgesMaterial.depthTest = !edgesOverlap; this.edgesMaterial.depthWrite = !edgesOverlap; this.lineMaterial.color.setRGB(facesColor.r / 255, facesColor.g / 255, facesColor.b / 255); this.lineGlowMaterial.color.setRGB(edgesColor.r / 255, edgesColor.g / 255, edgesColor.b / 255); this.lineGlowMaterial.depthTest = !edgesOverlap; this.lineGlowMaterial.depthWrite = !edgesOverlap; this.viewer.selected.forEach((selected) => { const wireframe = selected.userData.highlightWireframe; if (wireframe) wireframe.visible = edgesVisibility; }); } viewerResize(event: ResizeEvent) { this.renderTarget?.setSize(event.width, event.height); this.edgesMaterial?.resolution.set(event.width, event.height); this.lineGlowMaterial?.resolution.set(event.width, event.height); } }