three mesh.clone() 问题

在Three.js或其他3D图形库中,当从基础mesh克隆并尝试修改每个克隆的颜色时,发现所有克隆的颜色相同。原因是克隆的mesh与原mesh共享了材质和几何体。为解决此问题,需要分别克隆material和geometry,并为每个克隆的mesh设置独立的颜色。

发现问题的场景:

有一个mesh,其他mesh都是基于此mesh去clone的,然后每个mesh都需要修改为自己的颜色,代码如下:

const solidLineMesh = this.solidLineMeshTemplate.clone();
solidLineMesh.material.color.setHex(color);

出现问题:每个mesh的颜色都是一样的,并没有根据color动态修改

原因:

mesh.clone()获取的新模型和原来的模型是共享材质的几何体的,所以代码改动如下

const solidLineMesh = this.solidLineMeshTemplate.clone();
solidLineMesh.material = this.solidLineMeshTemplate.material.clone();
solidLineMesh.geometry = this.solidLineMeshTemplate.geometry.clone();
solidLineMesh.material.color.setHex(color);

import * as THREE from 'three'; import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'; import { HIGHT_LIGHT_STYLES, CAMERA_FAR } from '@/constants'; import { nameUtils } from '@/util/nameManager'; import { type EditorSceneSetup } from '@/view/hscope/editor/scene/editorSceneSetup'; import { setMaterials } from './index'; const NORMAL_COLOR = 0x00ffff; // 蓝色; const TEXT_SCALE = 30; // 字体大小 const SEARCH_OPACITY = 0.6; export type HighlightTextType = 'refdes' | 'sensingValue'; class HightLightStyle { meshList: THREE.Mesh[]; hightLightLineGroup: THREE.Group; textGroup: THREE.Group; textVisibleMap: Object; refdesToAttributeMap: Object; mapStyleFunction: Map<string, (mesh: THREE.Mesh<THREE.BufferGeometry, THREE.Material | THREE.Material[]>) => void>; editorScene: EditorSceneSetup; transparencyMeshMap: Map<THREE.Mesh, THREE.Mesh[]> = new Map(); #selectedMeshLamberMaterial = new THREE.MeshPhongMaterial({ color: NORMAL_COLOR, opacity: SEARCH_OPACITY, polygonOffset: true, // 开启偏移,避免器件之间存在重叠渲染相互影响 polygonOffsetFactor: 1, // 偏移因子 polygonOffsetUnits: -1, // 偏移值 side: THREE.DoubleSide, }); initValue( scene: THREE.Scene, refdesToAttribute: Object, highlight: Object, editorScene: EditorSceneSetup, ) { this.hightLightLineGroup = new THREE.Group(); this.textGroup = new THREE.Group(); this.textVisibleMap = highlight; this.refdesToAttributeMap = refdesToAttribute; this.mapStyleFunction = new Map(); this.mapStyleFunction.set(HIGHT_LIGHT_STYLES.NORMAL, mesh => this.normalStyle(mesh)); this.mapStyleFunction.set(HIGHT_LIGHT_STYLES.SEARCH_CHANGE_LIGHT, mesh => this.searchAndSelectedLightStyle(mesh)); this.mapStyleFunction.set(HIGHT_LIGHT_STYLES.DRAG_LIGHT, mesh => this.dragLightStyle(mesh)); this.editorScene = editorScene; scene.add(this.hightLightLineGroup); scene.add(this.textGroup); } createLine(mesh: THREE.Mesh, color: number) { if (this.hightLightLineGroup?.children?.length) { let findTemp = this.hightLightLineGroup.children.find(item => item.name === mesh.name); if (findTemp) { let findMaterial = (findTemp as THREE.LineSegments).material; findMaterial.color.set(new THREE.Color(color)); findMaterial.needsUpdate = true; return; } } let line = this.meshLineSegments(mesh, color); line.name = mesh.name; this.hightLightLineGroup.add(line); } meshLineSegments(mesh: THREE.Mesh, color = NORMAL_COLOR) { // 模型线条显示角度80以上的 let edges = new THREE.EdgesGeometry(mesh.geometry, 80); const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color })); line.name = mesh.name; line.position.copy(mesh.getWorldPosition(new THREE.Vector3())); const quaternion = mesh.getWorldQuaternion(new THREE.Quaternion()); const euler = new THREE.Euler().setFromQuaternion(quaternion); line.rotation.copy(euler); return line; } getSide(name: string) { let side = nameUtils.getMeshBaseBoardSide(name); if (side === 'Top' || side === 'Bottom') { return side; } if (name.includes('Top=')) { side = 'Top'; } else { side = 'Bottom'; } return side; } createLabelDiv(text: string, type: HighlightTextType): HTMLDivElement { const fontSize = 14; const labelDiv = document.createElement('div'); labelDiv.style.fontSize = `${fontSize}px`; labelDiv.className = `highlight-text-content ${type}`; labelDiv.innerText = text; labelDiv.style.color = 'white'; labelDiv.hidden = !this.textVisibleMap[type].isChecked; return labelDiv; } createTextCSS2D(mesh: THREE.Mesh) { const refdes = this.getMeshRefdes(mesh.name); const refdesDiv = this.createLabelDiv(refdes, 'refdes'); const attributes = this.refdesToAttributeMap[refdes]; const attrList = attributes ? Object.entries(attributes) : []; const divs = attrList.map(([type, text]) => this.createLabelDiv(text as string, type as HighlightTextType)); const labelContainer = document.createElement('div'); labelContainer.className = `highlight-text-group ${refdes}`; labelContainer.append(refdesDiv, ...divs); return new CSS2DObject(labelContainer); } getMeshRefdes(meshName: string) { let refdes = nameUtils.getMeshRefdes(meshName); if (!refdes) { let listName = meshName.split('&').pop(); refdes = listName?.split('=')[0] ?? ''; } return refdes; } createTextRefdes(mesh: THREE.Mesh) { if (!mesh?.name || nameUtils.isBaseBoard(mesh?.name)) { return; } let findTemp = this.textGroup?.children.find(item => item.name === mesh.name); if (findTemp) { return; } const textSprite = this.createTextCSS2D(mesh); if (!textSprite) { return; } textSprite.name = mesh.name; const boxValue = new THREE.Box3().setFromObject(mesh); if (!boxValue) { return; } let side = this.getSide(mesh.name); // 取包围盒X,Y轴中心点 字体比器件高2像素 let pos = new THREE.Vector3( (boxValue?.max.x + boxValue?.min.x) * 0.5, (boxValue?.max.y + boxValue?.min.y) * 0.5, side === 'Top' ? boxValue?.max.z + 2 : boxValue?.min.z - 2, ); // 设置固定字体大小 let textScale = TEXT_SCALE; textSprite.position.copy(pos); textSprite.scale.set(textScale, textScale, textScale); this.textGroup.add(textSprite); } /** * @description 选中器件的高亮效果 */ normalStyle(mesh: THREE.Mesh) { this.createLine(mesh, NORMAL_COLOR); this.createTextRefdes(mesh); mesh.material = this.#selectedMeshLamberMaterial; } /** * @description 搜索后所有器件的高亮效果 */ searchLightStyle(mesh: THREE.Mesh) { this.createTextRefdes(mesh); mesh.material = this.#selectedMeshLamberMaterial; } /** * @description 搜索后上下列框器件的高亮效果 */ searchAndSelectedLightStyle(mesh: THREE.Mesh) { this.createTextRefdes(mesh); mesh.material = this.#selectedMeshLamberMaterial; } /** * @description 框选器件的高亮效果 */ dragLightStyle(mesh: THREE.Mesh) { this.createLine(mesh, NORMAL_COLOR); mesh.material = this.#selectedMeshLamberMaterial; } clearHightLight(mesh: THREE.Mesh) { if (!mesh?.name) { return; } let temp = this.hightLightLineGroup?.children.find(item => item.name === mesh.name); if (temp) { temp.geometry.dispose(); temp.material.dispose(); this.hightLightLineGroup.remove(temp); } if (mesh?.originMaterial) { setMaterials(mesh, mesh.originMaterial); } if (nameUtils.isBaseBoard(mesh?.name) || !this.textGroup?.children?.length) { return; } let findTemp = this.textGroup?.children.find(item => item.name === mesh.name); if (findTemp) { this.textGroup.remove(findTemp); findTemp.clear(); } this.removeTransparency(mesh); } doHightLight(mesh: THREE.Mesh, styleStr = HIGHT_LIGHT_STYLES.NORMAL) { if (!mesh) { return; } if (!mesh?.originMaterial) { mesh.originMaterial = mesh.material; } if (this.mapStyleFunction.has(styleStr)) { let func = this.mapStyleFunction.get(styleStr); func && func(mesh); } } updateLinePosition(mesh: THREE.Mesh) { if (!mesh?.name) { return; } let tempLine = this.hightLightLineGroup?.children.find(item => item.name === mesh.name); if (tempLine) { tempLine.position.copy(mesh.getWorldPosition(new THREE.Vector3())); const quaternion = mesh.getWorldQuaternion(new THREE.Quaternion()); const euler = new THREE.Euler().setFromQuaternion(quaternion); tempLine.rotation.copy(euler); } } updateTextPos(mesh: THREE.Mesh) { if (!mesh?.name) { return; } if (nameUtils.isBaseBoard(mesh?.name) || !this.textGroup?.children?.length) { return; } let findTemp = this.textGroup?.children.find(item => item.name === mesh.name); const boxValue = new THREE.Box3().setFromObject(mesh); if (!boxValue || !findTemp) { return; } // 取包围盒X,Y轴中心点 let pos = new THREE.Vector3( (boxValue?.max.x + boxValue?.min.x) * 0.5, (boxValue?.max.y + boxValue?.min.y) * 0.5, boxValue?.max.z, ); findTemp.position.copy(pos); } /** * @description 修改器件的位号,高亮的位号跟着修改,框的name也需修改 */ modifyRefdes(oldName: string, newMesh: THREE.Mesh) { let findTemp = this.textGroup?.children.find(item => item.name === oldName); if (findTemp) { this.textGroup.remove(findTemp); findTemp.clear(); this.createTextRefdes(newMesh); } let findLine = this.hightLightLineGroup?.children.find(item => item.name === oldName); if (findLine) { findLine.name = newMesh.name; } } /** * @description 隐藏或显示器件时,框跟位号跟随隐藏或显示 */ displayHLight(mesh: THREE.Mesh, isV: boolean) { let meshText = this.textGroup?.children.find(item => item.name === mesh.name); if (meshText) { meshText.element.hidden = !isV; } let meshLine = this.hightLightLineGroup?.children.find(item => item.name === mesh.name); if (meshLine) { meshLine.visible = isV; } } showRefdesAccordingCamera(flag: number) { let display; if (flag < 0) { display = 'Top'; } else { display = 'Bottom'; } this.textGroup?.children.forEach(child => { if (child.name) { let side = this.getSide(child.name); child.element.hidden = side !== display; } }); } switchTextVisibleByType(type: HighlightTextType) { this.textVisibleMap[type].isChecked = !this.textVisibleMap[type].isChecked; this.textGroup?.children.forEach(child => { const textDiv = child.element.querySelector(`.${type}`); if (textDiv) { textDiv.hidden = !this.textVisibleMap[type].isChecked; } }); } /** *@description 当被遮挡的时候透明化 * @memberof HightLightStyle */ async doOccludedTransparency(selectedList: Array<{ object: THREE.Mesh }>) { selectedList.forEach(selected => { const highLightMesh = selected.object; if (!highLightMesh || nameUtils.isBaseBoard(highLightMesh.name)) { return; } const baseBoardSide = nameUtils.getMeshBaseBoardName(highLightMesh.name); const correctViewCamera = this.editorScene.oCamera.clone(); if (baseBoardSide === 'Top') { correctViewCamera.position.set(0, 0, CAMERA_FAR); correctViewCamera.up.set(0, 0, 1); } else { correctViewCamera.position.set(Math.cos(-Math.PI / 2), Math.sin(-Math.PI / 2), - CAMERA_FAR); correctViewCamera.up.set(0, 0, -1); } const meshBox3 = new THREE.Box3().setFromObject(highLightMesh); const raycasterTestPoints = [ new THREE.Vector3((meshBox3.min.x + meshBox3.max.x) / 2, (meshBox3.min.y + meshBox3.max.y) / 2, meshBox3.max.z), ]; const occludedMeshList = this.getOccludedByRaycaster( raycasterTestPoints, correctViewCamera, this.getComponentGroup(highLightMesh), ); occludedMeshList.forEach(intersect => { if (intersect instanceof THREE.Mesh && !nameUtils.isBaseBoard(intersect.name) && intersect.name !== highLightMesh.name ) { this.addTransparency(highLightMesh, intersect); } }); }); } /** * @description 包围盒四角或者中心点射线相交的器件将被透明化 * @param raycasterTestPoints 射线器件 * @param camera * @param componentGroup 当前的器件group * @returns */ getOccludedByRaycaster( raycasterTestPoints: THREE.Vector3[], camera: THREE.OrthographicCamera, componentGroup: THREE.Group | null, ): THREE.Object3D[] { const raycaster = new THREE.Raycaster(); let result: THREE.Object3D[] = []; raycasterTestPoints.forEach(point => { const pos = point.project(camera); raycaster.setFromCamera(new THREE.Vector2(pos.x, pos.y), camera); const insertGroup = componentGroup ?? this.editorScene.group; const intersects = raycaster.intersectObject(insertGroup); result.push(...intersects.map(item => item.object)); }); return Array.from(new Set(result)); } /** * 获取当前的器件的group * @param {THREE.Object3D} curObject3D * @returns {(THREE.Object3D | null)} * @memberof HightLightStyle */ getComponentGroup(curObject3D: THREE.Object3D): THREE.Object3D | null { if (!curObject3D) { return null; } if (curObject3D.parent?.name.endsWith('Components')) { return curObject3D.parent; } if (curObject3D?.parent) { return this.getComponentGroup(curObject3D.parent); } return null; } addTransparency(highLightMesh: THREE.Mesh, occludedMesh: THREE.Mesh) { if (!occludedMesh.originMaterial) { occludedMesh.originMaterial = occludedMesh.material; } const transparencyList = this.transparencyMeshMap.get(highLightMesh); if (transparencyList && transparencyList.includes(occludedMesh)) { return; } if (transparencyList && !transparencyList.includes(occludedMesh)) { transparencyList.push(occludedMesh); } if (!transparencyList) { this.transparencyMeshMap.set(highLightMesh, [occludedMesh]); } const newMaterial = occludedMesh.originMaterial.clone(); newMaterial.transparent = true; newMaterial.opacity = 0.2; setMaterials(occludedMesh, newMaterial); } /** *@description 移除透明效果 * @param {THREE.Mesh} unHighlightMesh * @returns void * @memberof HightLightStyle */ removeTransparency(unHighlightMesh: THREE.Mesh) { const transparencyMeshList = this.transparencyMeshMap.get(unHighlightMesh); if (!transparencyMeshList) { return; } this.transparencyMeshMap.delete(unHighlightMesh); transparencyMeshList.forEach(transparencyMesh => { // 其他高亮的器件也没被该透明器件遮挡才可以取消透明化 if (Object.values(this.transparencyMeshMap).find(meshList => meshList.include(transparencyMesh))) { return; } setMaterials(transparencyMesh, transparencyMesh.originMaterial); }); } } let hightLightStyleInstance = new HightLightStyle(); export default hightLightStyleInstance;
最新发布
10-01
<think>好的,我需要帮助用户解决关于three.js中originalMesh.clone()方法的使用或相关问题。首先,我要回忆一下Three.js中克隆方法的基本用法。克隆方法通常用于复制三维对象,这样可以避免重复创建相同的几何体和材质,节省资源。 用户提到的是originalMesh.clone(),应该是指某个已有的网格对象的克隆。我需要确认clone()方法的具体行为。根据Three.js文档,Mesh.clone()会复制几何体(geometry)和材质(material)的引用,而不是创建新的实例。这意味着如果用户直接修改原始网格的几何体或材质,克隆后的对象也会受到影响,因为它们共享相同的资源。这可能是一个常见的问题来源。 接下来,用户可能遇到的问题可能包括克隆后修改属性无效,或者性能问题。例如,如果用户希望克隆后的对象独立于原始对象,可能需要手动复制几何体和材质,而不仅仅是调用clone()。这时候需要建议用户使用geometry.clone()和material.clone()来创建深拷贝,然后再构建新的网格。 另外,还需要考虑内存管理。克隆对象虽然节省了初始加载时间,但如果大量克隆且后续需要单独修改,可能会导致内存增加。需要提醒用户注意资源管理,适时释放不再需要的几何体和材质。 用户的问题还可能涉及到克隆后的对象位置、旋转等属性的初始化。clone()方法会复制原始对象的这些属性,所以克隆后的对象会出现在相同的位置。如果用户需要将它们放置在不同的位置,需要手动设置position属性。 此外,动画和骨骼等复杂属性的克隆是否也被正确处理?可能需要查阅Three.js的文档或社区讨论,确认clone()方法是否覆盖这些情况。如果有遗漏,可能需要手动处理这些部分的克隆。 总结一下,用户可能需要了解如何正确使用clone()方法,以及如何处理克隆后对象的独立性问题。同时,提供一些常见问题的解决方案,比如深拷贝几何体和材质,管理内存,调整位置等。还要提醒用户注意性能影响和资源管理,确保应用运行流畅。</think>### Three.js中originalMesh.clone()方法使用指南 在Three.js中,`originalMesh.clone()`方法用于创建现有网格对象的浅拷贝,其核心功能可概括为: $$ \text{newMesh} = \text{originalMesh.clone()} $$ 此操作会复制网格的几何体(geometry)、材质(material)、位置(position)等属性,但**不会创建新的几何体和材质实例**,这意味着修改原对象属性可能影响克隆对象[^3]。 #### 一、基础用法示例 ```javascript const originalMesh = new THREE.Mesh(geometry, material); const clonedMesh = originalMesh.clone(); // 浅拷贝操作 scene.add(clonedMesh); ``` 此时两个网格将共享相同的几何体和材质数据。 #### 二、关键注意事项 1. **共享资源问题** 若需独立修改克隆体属性,必须创建深拷贝: ```javascript // 深拷贝解决方案 const deepClonedMesh = new THREE.Mesh( originalMesh.geometry.clone(), originalMesh.material.clone() ); ``` 2. **矩阵更新需求** 克隆后需手动更新矩阵以确保正确渲染: ```javascript clonedMesh.position.set(10, 0, 0); clonedMesh.updateMatrix(); // 强制更新世界矩阵 ``` 3. **动画系统兼容性** 若原始网格包含动画混合器(AnimationMixer),需单独为克隆体创建新动画控制器[^1]。 #### 三、性能优化建议 - 批量克隆时推荐预先生成资源池 - 静态场景优先使用实例化网格(InstancedMesh) - 销毁对象时主动调用`dispose()`释放资源: ```javascript clonedMesh.geometry.dispose(); clonedMesh.material.dispose(); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值