Warning, /firebird/firebird-ng/src/app/utils/three-geometry-merge.ts is written in an unsupported language. File is not indexed.
0001 import * as THREE from "three";
0002 import {mergeGeometries} from "three/examples/jsm/utils/BufferGeometryUtils";
0003
0004 export interface MergeResult {
0005 mergedGeometry: THREE.BufferGeometry;
0006 mergedMesh: THREE.Mesh;
0007 material: THREE.Material | undefined;
0008 childrenToRemove: THREE.Object3D[];
0009 parentNode: THREE.Object3D;
0010 }
0011
0012 export class NoGeometriesFoundError extends Error {
0013 constructor(message: string = "No geometries found in the provided node.") {
0014 super(message);
0015 this.name = "NoGeometriesFoundError";
0016 }
0017 }
0018
0019 export class NoMaterialError extends Error {
0020 constructor(message: string = "No material set or found in geometries.") {
0021 super(message);
0022 this.name = "NoMaterialError";
0023 }
0024 }
0025
0026 /**
0027 * Merges all geometries in a branch of the scene graph into a single geometry.
0028 * @param parentNode The parent node of the branch to merge.
0029 * @param name Name of the new merged geometry node
0030 * @param material Material to assign to the merged geometry, if empty the first material found will be used
0031 * @returns MergeResult object containing the merged geometry, material, children to remove, and parent node
0032 */
0033 export function mergeBranchGeometries(parentNode: THREE.Object3D, name: string, material?: THREE.Material | undefined): MergeResult {
0034 const geometries: THREE.BufferGeometry[] = [];
0035 const childrenToRemove: THREE.Object3D[] = [];
0036
0037 // Recursively collect geometries from the branch
0038 const collectGeometries = (node: THREE.Object3D): void => {
0039 node.traverse((child: any) => {
0040
0041 let isBufferGeometry = child?.geometry?.isBufferGeometry ?? false;
0042 //console.log(isBufferGeometry);
0043 if (isBufferGeometry) {
0044 child.updateMatrixWorld(true);
0045 const clonedGeometry = child.geometry.clone();
0046 clonedGeometry.applyMatrix4(child.matrixWorld);
0047 geometries.push(clonedGeometry);
0048 material = material || child.material;
0049 childrenToRemove.push(child);
0050 }
0051 });
0052 };
0053
0054 collectGeometries(parentNode);
0055
0056 if (geometries.length === 0) {
0057 throw new NoGeometriesFoundError();
0058 } else if (material === undefined) {
0059 throw new NoMaterialError();
0060 }
0061
0062 // Merge all collected geometries
0063 const mergedGeometry = mergeGeometries(geometries, false);
0064
0065 // Transform the merged geometry to the local space of the parent node
0066 const parentInverseMatrix = new THREE.Matrix4().copy(parentNode.matrixWorld).invert();
0067 mergedGeometry.applyMatrix4(parentInverseMatrix);
0068
0069 // Create a new mesh with the merged geometry and the collected material
0070 const mergedMesh = new THREE.Mesh(mergedGeometry, material);
0071
0072 // Remove the original children that are meshes and add the new merged mesh
0073 // Remove and dispose the original children
0074 childrenToRemove.forEach((child: any) => {
0075 child.geometry.dispose();
0076 child?.parent?.remove(child);
0077 // Remove empty parents
0078 if( (child?.parent?.children?.length ?? 1) === 0 ) {
0079 child?.parent?.parent?.remove(child.parent);
0080 }
0081 });
0082
0083 mergedMesh.name = name;
0084
0085 parentNode.add(mergedMesh);
0086 return {
0087 mergedGeometry,
0088 mergedMesh,
0089 material,
0090 childrenToRemove,
0091 parentNode
0092 };
0093 }
0094
0095
0096 /**
0097 * Merges all geometries from a list of meshes into a single geometry and attaches it to a new parent node.
0098 * @param meshes An array of THREE.Mesh objects whose geometries are to be merged.
0099 * @param parentNode The new parent node to which the merged mesh will be added.
0100 * @param name The name to assign to the merged mesh.
0101 *
0102 * (!) This function doesn't delete original meshes Compared to @see mergeBranchGeometries.
0103 * Use MergeResult.childrenToRemove to delete meshes that were merged
0104 *
0105 * @param material
0106 * @returns MergeResult The result of the merging process including the new parent node, merged geometry, material, and a list of original meshes.
0107 */
0108 export function mergeMeshList(meshes: THREE.Mesh[], parentNode: THREE.Object3D, name: string, material?: THREE.Material|undefined): MergeResult|undefined {
0109 const geometries: THREE.BufferGeometry[] = [];
0110
0111 // Collect geometries and materials from the provided meshes
0112 meshes.forEach(mesh => {
0113 if (mesh?.geometry?.isBufferGeometry) {
0114 mesh.updateMatrixWorld(true);
0115 const clonedGeometry = mesh.geometry.clone();
0116 clonedGeometry.applyMatrix4(mesh.matrixWorld);
0117 geometries.push(clonedGeometry);
0118 // Check if mesh.material is an array and handle it
0119 if (!material) { // Only set if material has not been set yet
0120 if (Array.isArray(mesh.material)) {
0121 material = mesh.material[0]; // Use the first material if it's an array
0122 } else {
0123 material = mesh.material; // Use the material directly if it's not an array
0124 }
0125 }
0126 }
0127 });
0128
0129 if (geometries.length === 0) {
0130 return undefined;
0131 //throw new NoGeometriesFoundError();
0132 }
0133 if (!material) {
0134 return undefined;
0135 //throw new NoMaterialError();
0136 }
0137
0138 // Merge all collected geometries
0139 const mergedGeometry = mergeGeometries(geometries, false);
0140
0141 // Transform the merged geometry to the local space of the parent node
0142 const parentInverseMatrix = new THREE.Matrix4().copy(parentNode.matrixWorld).invert();
0143 mergedGeometry.applyMatrix4(parentInverseMatrix);
0144
0145 // Create a new mesh with the merged geometry and the collected material
0146 const mergedMesh = new THREE.Mesh(mergedGeometry, material);
0147 mergedMesh.name = name;
0148 parentNode.add(mergedMesh);
0149
0150 return {
0151 mergedGeometry,
0152 mergedMesh,
0153 material,
0154 childrenToRemove: meshes, // Here, we assume the original meshes are what would be removed if needed
0155 parentNode
0156 };
0157 }