diff --git a/firebird-ng/src/app/geometry-prettifiers/calorimetry.prettifier.ts b/firebird-ng/src/app/geometry-prettifiers/calorimetry.prettifier.ts new file mode 100644 index 0000000..0fe62c4 --- /dev/null +++ b/firebird-ng/src/app/geometry-prettifiers/calorimetry.prettifier.ts @@ -0,0 +1,48 @@ +import * as THREE from "three"; +import { + createOutline, + disposeNode, + disposeOriginalMeshesAfterMerge, + findObject3DNodes, + pruneEmptyNodes +} from "../utils/three.utils"; +import {mergeMeshList, MergeResult} from "../utils/three-geometry-merge"; +import {ColorRepresentation} from "three/src/math/Color"; + + +export class CalorimetryGeometryPrettifier { + + doEndcapEcalN(node: THREE.Mesh) { + let crystals = findObject3DNodes(node, "**/crystal_vol_0", "Mesh").nodes; + //console.log(crystals); + + // Merge crystals together + let mergeResult: MergeResult = mergeMeshList(crystals, node, "crystals"); + disposeOriginalMeshesAfterMerge(mergeResult) + + // outline crystals + createOutline(mergeResult.mergedMesh); + + // Support + let innerSupport = findObject3DNodes(node, "**/inner_support*", "Mesh").nodes[0]; + let ring = findObject3DNodes(node, "**/ring*", "Mesh").nodes[0]; + + // Ring is pretty with dark blue outline + createOutline(ring, 0x002a42); + + const supportMaterial = new THREE.MeshStandardMaterial({ + color: 0x19a5f5, + roughness: 0.7, + metalness: 0.869, + transparent: true, + opacity: 1, + side: THREE.DoubleSide + }); + + mergeResult = mergeMeshList([innerSupport, ring], node, "support", supportMaterial); + disposeOriginalMeshesAfterMerge(mergeResult) + + // Cleanup. Removing useless nodes that were left without geometries speeds up overall rendering + pruneEmptyNodes(node); + } +} diff --git a/firebird-ng/src/app/main-display/main-display.component.ts b/firebird-ng/src/app/main-display/main-display.component.ts index f633745..67ee177 100644 --- a/firebird-ng/src/app/main-display/main-display.component.ts +++ b/firebird-ng/src/app/main-display/main-display.component.ts @@ -75,59 +75,8 @@ export class MainDisplayComponent implements OnInit { // console.log("CERN ROOT converted to Object3d: ", rootObject3d); sceneGeometry.add(rootObject3d); - // Add top nodes to menu - let topLevelObj3dNodes = rootObject3d.children[0].children; - - // for(let i= topLevelObj3dNodes.length - 1; i >= 0; i--) { - // console.log(`${i} : ${topLevelObj3dNodes[i].name}`); - // } - - console.log("DISPOSING"); - for(let i= topLevelObj3dNodes.length - 1; i >= 0; i--){ - let obj3dNode = topLevelObj3dNodes[i]; - console.log(`${i} : ${topLevelObj3dNodes[i].name}`); - obj3dNode.name = obj3dNode.userData["name"] = obj3dNode.name; - // Add geometry - // uiManager.addGeometry(obj3dNode, obj3dNode.name); - - if(obj3dNode.name == "EcalEndcapN_21") { - let crystals = findObject3DNodes(obj3dNode, "**/crystal_vol_0", "Mesh").nodes; - //console.log(crystals); - - let mergeResult: MergeResult = mergeMeshList(crystals, obj3dNode, "crystals"); - - // Remove initial nodes - for (let i=mergeResult.childrenToRemove.length-1; i>=0; i-- ) { - disposeNode(mergeResult.childrenToRemove[i]); - mergeResult.childrenToRemove[i].removeFromParent(); - } - - pruneEmptyNodes(obj3dNode); - - } else { - - try { - obj3dNode.removeFromParent(); - } - catch (e) { - console.error(e); - } - try { - console.log("disposeHierarchy: ", obj3dNode.name, obj3dNode); - disposeHierarchy(obj3dNode); - } catch (e) { - console.error(e); - } - - - //mergeBranchGeometries(obj3dNode, obj3dNode.name + "_merged"); - } - } - - let renderer = openThreeManager.rendererManager; - // Now we want to change the materials sceneGeometry.traverse( (child: any) => { @@ -197,7 +146,7 @@ export class MainDisplayComponent implements OnInit { child.material.clipShadows = false; } }); - renderer = openThreeManager.rendererManager; + let renderer = openThreeManager.rendererManager; // Set render priority let scene = threeManager.getSceneManager().getScene(); scene.background = new THREE.Color( 0x3F3F3F ); diff --git a/firebird-ng/src/app/three-geometry.processor.ts b/firebird-ng/src/app/three-geometry.processor.ts index 553536d..a9a5983 100644 --- a/firebird-ng/src/app/three-geometry.processor.ts +++ b/firebird-ng/src/app/three-geometry.processor.ts @@ -1,24 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { EventDisplayService } from 'phoenix-ui-components'; import { Configuration, PhoenixLoader, PresetView, ClippingSetting, PhoenixMenuNode } from 'phoenix-event-display'; -import { - Color, - DoubleSide, - Mesh, - LineSegments, - LineBasicMaterial, - MeshPhongMaterial, - Material, - ObjectLoader, - FrontSide, - Vector3, - Matrix4, - REVISION, - MeshPhysicalMaterial, - MeshStandardMaterial, - ShaderMaterial, - EdgesGeometry, MeshLambertMaterial, -} from "three"; +import * as THREE from "three"; import { PhoenixUIModule } from 'phoenix-ui-components'; import { GeometryService} from './geometry.service'; import { Edm4hepRootEventLoader } from './edm4hep-root-event-loader'; @@ -27,11 +10,15 @@ import {color} from "three/examples/jsm/nodes/shadernode/ShaderNode"; import {getGeoNodesByLevel} from "./utils/cern-root.utils"; import {produceRenderOrder} from "jsrootdi/geom"; import {wildCardCheck} from "./utils/wildcard"; +import {disposeHierarchy, findObject3DNodes} from "./utils/three.utils"; +import {CalorimetryGeometryPrettifier} from "./geometry-prettifiers/calorimetry.prettifier"; export class ThreeGeometryProcessor { - glassMaterial = new LineBasicMaterial( { + calorimetry = new CalorimetryGeometryPrettifier(); + + glassMaterial = new THREE.LineBasicMaterial( { color: 0xf1f1f1, linewidth: 1, linecap: 'round', //ignored by WebGLRenderer @@ -73,7 +60,9 @@ export class ThreeGeometryProcessor { } `; - alphaMaterial = new MeshStandardMaterial( { + shaderMaterial: THREE.ShaderMaterial; + + alphaMaterial = new THREE.MeshStandardMaterial( { color: 0xffffff, alphaHash: this.params.alphaHash, opacity: this.params.alpha @@ -92,12 +81,7 @@ export class ThreeGeometryProcessor { // }); constructor() { - - } - - public process(geometry: any) { - - let shaderMaterial = new ShaderMaterial({ + this.shaderMaterial = new THREE.ShaderMaterial({ uniforms: { thickness: { value: 1.5 @@ -107,47 +91,89 @@ export class ThreeGeometryProcessor { fragmentShader: this.fragmentShader }); + } - // Now we want to change the materials - geometry.traverse( (child: any) => { + public process(geometry: any) { - if(child.type!=="Mesh") { - return; - } + // Add top nodes to menu + let topDetectorNodes = geometry.children[0].children; - child = child as Mesh; + // for(let i= topLevelObj3dNodes.length - 1; i >= 0; i--) { + // console.log(`${i} : ${topLevelObj3dNodes[i].name}`); + // } + console.log("DISPOSING"); + for(let i= topDetectorNodes.length - 1; i >= 0; i--){ + let detNode = topDetectorNodes[i]; + console.log(`${i} : ${topDetectorNodes[i].name}`); + detNode.name = detNode.userData["name"] = detNode.name; + // Add geometry + // uiManager.addGeometry(obj3dNode, obj3dNode.name); - if(!child?.material?.isMaterial) { - return; - } + if(detNode.name == "EcalEndcapN_21") { + this.calorimetry.doEndcapEcalN(detNode) + } else { - // Material - let name:string = child.name; - child.updateMatrixWorld(true); + try { + detNode.removeFromParent(); + } + catch (e) { + console.error(e); + } - //if(name.startsWith("bar_") || name.startsWith("prism_")) { - //child.material = this.alphaMaterial; - const edges = new EdgesGeometry(child.geometry, 30); - //const lineMaterial = new MeshLambertMaterial({ - const lineMaterial = new LineBasicMaterial({ - color: 0x555555, - fog: false, - // Copy clipping planes from parent, using type assertion for TypeScript - clippingPlanes: child.material.clippingPlanes ? child.material.clippingPlanes : [], - clipIntersection: false, - clipShadows: true, - transparent: false - }); + try { + console.log("disposeHierarchy: ", detNode.name, detNode); + disposeHierarchy(detNode); + } catch (e) { + console.error(e); + } - // lineMaterial.clipping = true; - const edgesLine = new LineSegments(edges, lineMaterial); - //const edgesLine = new Mesh(edges, lineMaterial); - child.add(edgesLine); + //mergeBranchGeometries(obj3dNode, obj3dNode.name + "_merged"); + } + } - //} - }); + // Now we want to change the materials + // geometry.traverse( (child: any) => { + // + // if(child.type!=="Mesh") { + // return; + // } + // + // child = child as THREE.Mesh; + // + // + // if(!child?.material?.isMaterial) { + // return; + // } + // + // // Material + // let name:string = child.name; + // child.updateMatrixWorld(true); + // + // //if(name.startsWith("bar_") || name.startsWith("prism_")) { + // //child.material = this.alphaMaterial; + // const edges = new THREE.EdgesGeometry(child.geometry, 30); + // //const lineMaterial = new MeshLambertMaterial({ + // const lineMaterial = new THREE.LineBasicMaterial({ + // color: 0x555555, + // fog: false, + // // Copy clipping planes from parent, using type assertion for TypeScript + // clippingPlanes: child.material.clippingPlanes ? child.material.clippingPlanes : [], + // clipIntersection: false, + // clipShadows: true, + // transparent: false + // + // }); + // + // // lineMaterial.clipping = true; + // const edgesLine = new THREE.LineSegments(edges, lineMaterial); + // //const edgesLine = new Mesh(edges, lineMaterial); + // + // child.add(edgesLine); + // + // //} + // }); } } diff --git a/firebird-ng/src/app/utils/three-geometry-merge.ts b/firebird-ng/src/app/utils/three-geometry-merge.ts index fa1e56f..f6b6054 100644 --- a/firebird-ng/src/app/utils/three-geometry-merge.ts +++ b/firebird-ng/src/app/utils/three-geometry-merge.ts @@ -3,6 +3,7 @@ import {mergeGeometries} from "three/examples/jsm/utils/BufferGeometryUtils"; export interface MergeResult { mergedGeometry: THREE.BufferGeometry; + mergedMesh: THREE.Mesh; material: THREE.Material | undefined; childrenToRemove: THREE.Object3D[]; parentNode: THREE.Object3D; @@ -84,6 +85,7 @@ export function mergeBranchGeometries(parentNode: THREE.Object3D, name: string, parentNode.add(mergedMesh); return { mergedGeometry, + mergedMesh, material, childrenToRemove, parentNode @@ -100,11 +102,11 @@ export function mergeBranchGeometries(parentNode: THREE.Object3D, name: string, * (!) This function doesn't delete original meshes Compared to @see mergeBranchGeometries. * Use MergeResult.childrenToRemove to delete meshes that were merged * + * @param material * @returns MergeResult The result of the merging process including the new parent node, merged geometry, material, and a list of original meshes. */ -export function mergeMeshList(meshes: THREE.Mesh[], parentNode: THREE.Object3D, name: string): MergeResult { +export function mergeMeshList(meshes: THREE.Mesh[], parentNode: THREE.Object3D, name: string, material?: THREE.Material|undefined): MergeResult { const geometries: THREE.BufferGeometry[] = []; - let material: THREE.Material | undefined; // Collect geometries and materials from the provided meshes meshes.forEach(mesh => { @@ -145,6 +147,7 @@ export function mergeMeshList(meshes: THREE.Mesh[], parentNode: THREE.Object3D, return { mergedGeometry, + mergedMesh, material, childrenToRemove: meshes, // Here, we assume the original meshes are what would be removed if needed parentNode diff --git a/firebird-ng/src/app/utils/three.utils.ts b/firebird-ng/src/app/utils/three.utils.ts index 405d121..a3d020c 100644 --- a/firebird-ng/src/app/utils/three.utils.ts +++ b/firebird-ng/src/app/utils/three.utils.ts @@ -3,6 +3,7 @@ import outmatch from 'outmatch'; import * as THREE from "three"; import {mergeGeometries} from 'three/examples/jsm/utils/BufferGeometryUtils'; import {GeoNodeWalkCallback, walkGeoNodes} from "./cern-root.utils"; +import {MergeResult} from "./three-geometry-merge"; export type NodeWalkCallback = (node: any, nodeFullPath: string, level: number) => boolean; @@ -152,12 +153,14 @@ class NoGeometryError extends Error { } } + /** * Applies an outline mesh from lines to a mesh and adds the outline to the mesh's parent. * @param mesh A THREE.Object3D (expected to be a Mesh) to process. * @param material Optional material provided by the user. If not provided, a default material is created. + * @param color Color of material */ -function createOutline(mesh: any, material?: THREE.Material): void { +export function createOutline(mesh: any, color?: THREE.ColorRepresentation, material?: THREE.Material): void { if (!mesh?.geometry) { throw new NoGeometryError(mesh); } @@ -167,7 +170,7 @@ function createOutline(mesh: any, material?: THREE.Material): void { if (!lineMaterial) { lineMaterial = new THREE.LineBasicMaterial({ - color: 0x555555, + color: color ?? new THREE.Color(0x555555), fog: false, clippingPlanes: mesh.material?.clippingPlanes ? mesh.material.clippingPlanes : [], clipIntersection: false, @@ -176,9 +179,14 @@ function createOutline(mesh: any, material?: THREE.Material): void { }); } + // Create a mesh with the outline const edgesLine = new THREE.LineSegments(edges, lineMaterial); + edgesLine.name = (mesh.name ?? "") + "_outline"; + edgesLine.userData = {}; + + // Add to parent mesh.updateMatrixWorld(true); - mesh.parent?.add(edgesLine); + mesh?.parent?.add(edgesLine); } @@ -238,6 +246,14 @@ export function disposeNode(node: any): void { node.removeFromParent(); } +export function disposeOriginalMeshesAfterMerge(mergeResult: MergeResult) { + // Remove initial nodes + for (let i=mergeResult.childrenToRemove.length-1; i>=0; i-- ) { + disposeNode(mergeResult.childrenToRemove[i]); + mergeResult.childrenToRemove[i].removeFromParent(); + } +} + export function disposeHierarchy(node: THREE.Object3D): void { node.children.slice().reverse().forEach(child => { disposeHierarchy(child); @@ -249,6 +265,9 @@ export function disposeHierarchy(node: THREE.Object3D): void { * Recursively removes empty branches from a THREE.Object3D tree. * An empty branch is a node without geometry and without any non-empty children. * @param node - The starting node to prune empty branches from. + * + * Removing useless nodes that were left without geometries speeds up overall rendering + * */ export function pruneEmptyNodes(node: THREE.Object3D): void { // Traverse children from last to first to avoid index shifting issues after removal