Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/firebird-ng/src/app/utils/three-geometry-editor.ts is written in an unsupported language. File is not indexed.

0001 import {Color, Material, Mesh, Object3D} from "three";
0002 import {createOutline, disposeOriginalMeshesAfterMerge, findObject3DNodes, pruneEmptyNodes} from "./three.utils";
0003 import {mergeBranchGeometries, mergeMeshList, MergeResult} from "./three-geometry-merge";
0004 import * as THREE from "three";
0005 import {ColorRepresentation} from "three/src/math/Color";
0006 import {SimplifyModifier} from "three/examples/jsm/modifiers/SimplifyModifier.js";
0007 
0008 export enum EditThreeNodeActions {
0009 
0010   Merge,   /** Merge children matching patterns (if patterns are provided) or all meshes of the node*/
0011 
0012 }
0013 
0014 export interface EditThreeNodeRule {
0015 
0016   patterns?: string[] | string;
0017   merge?: boolean;
0018   newName?: string;
0019   deleteOrigins?: boolean;
0020   cleanupNodes?: boolean;
0021   outline?: boolean;
0022   outlineThresholdAngle?: number;
0023   simplifyMeshes?: boolean;
0024   simplifyRatio?: number;
0025 
0026   /** [degrees] */
0027   outlineColor?: ColorRepresentation;
0028   material?: Material;
0029   color?: ColorRepresentation;
0030 
0031 }
0032 
0033 function simplifyMeshTree(object: THREE.Object3D, simplifyRatio = 0.5): void {
0034   const simplifier = new SimplifyModifier();
0035   const minVerts = 10;
0036 
0037   object.traverse((child: THREE.Object3D) => {
0038 
0039     // Type coercions and type validations
0040     if (!(child as THREE.Mesh).isMesh) {
0041       return
0042     }
0043     const mesh = child as THREE.Mesh;
0044 
0045     if(!(mesh.geometry as THREE.BufferGeometry).isBufferGeometry) {
0046       return;
0047     }
0048     const geom = mesh.geometry as THREE.BufferGeometry;
0049 
0050     if (!geom.attributes['position']) {
0051       return;
0052     }
0053 
0054     // Do we need to convert looking at the number of vertices?
0055     const verticeCount  = geom.attributes['position'].count;
0056     const targetVerticeCount = Math.floor(verticeCount  * simplifyRatio);
0057 
0058     if (verticeCount < minVerts) {
0059       console.log(`[SimplifyMeshTree] Mesh "${mesh.name || '(unnamed)'}": skipped (too small, vertices=${verticeCount })`);
0060       return;
0061     }
0062 
0063     if (verticeCount < targetVerticeCount) {
0064       console.log(`[SimplifyMeshTree] Mesh "${mesh.name || '(unnamed)'}": skipped (too small targetVerticeCount, targetVerticeCount=${targetVerticeCount})`);
0065       return;
0066     }
0067 
0068 
0069     // Actual simplification
0070     const timeStart = performance.now();
0071     console.log(`[SimplifyMeshTree] Processing "${mesh.name || '(unnamed)'}": vertices before=${verticeCount}, after=${targetVerticeCount}`);
0072 
0073     mesh.geometry = simplifier.modify(geom, targetVerticeCount);
0074 
0075     // Recompute bounding limits
0076     mesh.geometry.computeBoundingBox();
0077     mesh.geometry.computeBoundingSphere();
0078     mesh.geometry.computeVertexNormals();
0079 
0080     // Make sure positions and normals will be updated
0081     mesh.geometry.attributes["position"]["needsUpdate"] = true;
0082     if (mesh.geometry.attributes["normal"]) {
0083       mesh.geometry.attributes["normal"]["needsUpdate"] = true;
0084     }
0085 
0086     const timeEnd = performance.now()
0087     if (timeEnd - timeStart > 500) {
0088       console.warn(`[SimplifyMeshTree] Warn: mesh "${mesh.name || '(unnamed)'}" took ${Math.round(timeEnd-timeStart)}ms to simplify.`);
0089     }
0090   });
0091 }
0092 
0093 function mergeWhatever(node: Object3D, rule: EditThreeNodeRule): MergeResult | undefined {
0094 
0095   let newName = !rule.newName ? node.name + "_merged" : rule.newName;
0096 
0097   if (!rule.patterns) {
0098     // If user provided patterns only children matching patterns (search goes over whole branch) will be merged,
0099     // But if no patterns given, we will merge whole node
0100     return mergeBranchGeometries(node, newName, rule.material);  // Children auto removed
0101   }
0102 
0103   // If we are here, we need to collect what to merge first
0104 
0105   let mergeSubjects = [];
0106   // merge whole node
0107   if (typeof rule.patterns === "string") {
0108     rule.patterns = [rule.patterns];
0109   }
0110 
0111   for (const pattern of rule.patterns) {
0112     mergeSubjects.push(...findObject3DNodes(node, pattern, "Mesh").nodes);
0113   }
0114 
0115   let result = mergeMeshList(mergeSubjects, node, newName, rule.material);
0116   const deleteOrigins = rule?.deleteOrigins ?? true;
0117   if (result && deleteOrigins) {
0118     disposeOriginalMeshesAfterMerge(result);
0119   }
0120   return result;
0121 }
0122 
0123 
0124 export function editThreeNodeContent(node: Object3D, rule: EditThreeNodeRule) {
0125   let {
0126     patterns,
0127     deleteOrigins = true,
0128     cleanupNodes = true,
0129     outline = true,
0130     outlineThresholdAngle = 40,
0131     outlineColor,
0132     simplifyMeshes = false,
0133     simplifyRatio = 0.7,
0134     material,
0135     color,
0136     merge = true,
0137     newName = ""
0138   } = rule;
0139 
0140   let targetMeshes: Mesh[] = [];
0141 
0142   if (merge) {
0143     // Existing merge logic
0144     let result = mergeWhatever(node, rule);
0145     if (!result) {
0146       console.warn("didn't find children to merge. Patterns:");
0147       console.log(patterns)
0148       return;
0149     }
0150     targetMeshes = [result.mergedMesh];
0151   } else {
0152     // New logic for when merge is false
0153     // Find all meshes that match the patterns, similar to mergeWhatever
0154     if (!patterns) {
0155       // If no patterns given, collect all meshes with geometry in the node
0156       node.traverse((child) => {
0157         if ((child as any)?.geometry) {
0158           targetMeshes.push(child as Mesh);
0159         }
0160       });
0161     } else {
0162       // If patterns are given, find all meshes that match
0163       if (typeof patterns === "string") {
0164         patterns = [patterns];
0165       }
0166 
0167       for (const pattern of patterns) {
0168         targetMeshes.push(...findObject3DNodes(node, pattern, "Mesh").nodes);
0169       }
0170     }
0171   }
0172 
0173   // Apply operations to each target mesh
0174   for (const targetMesh of targetMeshes) {
0175 
0176     // Change color
0177     if (color !== undefined && color !== null) {
0178       if (targetMesh.material) {
0179         (targetMesh.material as any).color = new Color(color);
0180       }
0181     }
0182 
0183     // Change material
0184     if (material !== undefined && material !== null) {
0185        targetMesh.material = material;
0186     }
0187 
0188     if (simplifyMeshes) {
0189       simplifyMeshTree(targetMesh, simplifyRatio);
0190     }
0191 
0192     if (outline) {
0193       createOutline(targetMesh, {color: outlineColor, thresholdAngle: outlineThresholdAngle});
0194     }
0195   }
0196 
0197   if (cleanupNodes) {
0198     pruneEmptyNodes(node);
0199   }
0200 }