import * as THREE from 'three'; export class PltDataRenderer { constructor(updateProgress, onComplete) { this.updateProgress = updateProgress || (() => {}); this.onComplete = onComplete || (() => {}); this.chunkSize = 5000; // 每个区域的最大三角面片数(用于分块加载) this.defaultMaterial = new THREE.MeshPhongMaterial({ color: 0x4477ff, specular: 0x111111, shininess: 30, side: THREE.DoubleSide, // 关键修改:双面渲染 flatShading: false, // 平滑着色 transparent: true, opacity: 0.95 }); this.wireframeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }); } async render(pltData, scene, specificZoneIndex = null) { console.log("原始PLT数据结构诊断:", { zones: pltData.zones.map(zone => ({ name: zone.name, verticesType: zone.vertices?.constructor?.name, verticesLength: zone.vertices?.length, verticesSample: zone.vertices?.slice(0, 3), indicesType: zone.indices?.constructor?.name, indicesLength: zone.indices?.length, indicesSample: zone.indices?.slice(0, 3) })) }); if (!pltData?.zones?.length) { console.error('Invalid PLT data'); return; } // 清空现有PLT相关对象 this._cleanupPreviousRender(scene); // 如果指定了具体区域索引,则只渲染该区域 if (specificZoneIndex !== null) { if (specificZoneIndex < 0 || specificZoneIndex >= pltData.zones.length) { console.error(`Invalid zone index: ${specificZoneIndex} (total zones: ${pltData.zones.length})`); return; } const zone = pltData.zones[specificZoneIndex]; await this._renderZone(zone, scene, specificZoneIndex, pltData.zones.length); console.log(`[DEBUG] 仅渲染了区域 ${specificZoneIndex}: ${zone.name}`); return; } // 否则渲染所有区域(原有逻辑) const totalZones = pltData.zones.length; for (const [index, zone] of pltData.zones.entries()) { try { await this._renderZone(zone, scene, index, totalZones); this.updateProgress((index + 1) / totalZones); } catch (error) { console.error(`Failed to render zone ${zone.name}:`, error); } await new Promise(resolve => setTimeout(resolve, 0)); } this.onComplete(); } async _renderZone(zone, scene, zoneIndex, totalZones) { return new Promise((resolve) => { // 使用requestAnimationFrame避免阻塞UI requestAnimationFrame(() => { if (!zone.vertices || !zone.indices) { console.warn(`Zone ${zone.name} has no geometry data`); return resolve(); } // 创建几何体 const geometry = this._createZoneGeometry(zone); // 分配材质(交替使用实体和线框材质) const material = zoneIndex % 2 === 0 ? this.defaultMaterial.clone() : this.wireframeMaterial.clone(); // 设置区域特定颜色 if (zoneIndex % 2 === 0) { material.color.setHSL(zoneIndex / totalZones, 0.7, 0.5); } else { material.color.setHSL(zoneIndex / totalZones, 0.7, 0.7); } // 创建网格 const mesh = new THREE.Mesh(geometry, material); mesh.name = `plt_zone_${zoneIndex}`; mesh.userData = { zoneName: zone.name, zoneType: zone.type, isPLT: true }; // 添加到场景 scene.add(mesh); // 添加区域标签(可选) this._addZoneLabel(zone, mesh, zoneIndex); console.log(`Rendered zone ${zone.name} with ${geometry.index.count / 3} triangles`); resolve(); }); }); } _createZoneGeometry(zone) { const geometry = new THREE.BufferGeometry(); // 顶点数据 geometry.setAttribute( 'position', new THREE.BufferAttribute(zone.vertices, 3) ); // 安全极值计算函数 const getArrayExtremes = (arr) => { let min = Infinity, max = -Infinity; for (let i = 0; i < arr.length; i++) { if (arr[i] < min) min = arr[i]; if (arr[i] > max) max = arr[i]; } return { min, max }; }; // 索引处理 let indicesArray = zone.indices || new Uint32Array(0); if (indicesArray.length > 0) { // 计算极值 const { min: minIndex, max: maxIndex } = getArrayExtremes(indicesArray); const vertexCount = zone.vertices.length / 3; // 索引验证 if (maxIndex >= vertexCount) { console.warn(`修正${maxIndex - vertexCount + 1}个超出范围的索引`); const validIndices = new Uint32Array(indicesArray.length); for (let i = 0; i < indicesArray.length; i++) { validIndices[i] = indicesArray[i] >= vertexCount ? 0 : indicesArray[i]; } indicesArray = validIndices; } // 四边形转换 if (indicesArray.length % 4 === 0) { console.log('四边形->三角形转换'); const triCount = indicesArray.length / 4 * 6; const triangles = new Uint32Array(triCount); for (let i = 0; i < indicesArray.length / 4; i++) { const base = i * 4; triangles[i*6] = indicesArray[base]; triangles[i*6+1] = indicesArray[base+1]; triangles[i*6+2] = indicesArray[base+2]; triangles[i*6+3] = indicesArray[base]; triangles[i*6+4] = indicesArray[base+2]; triangles[i*6+5] = indicesArray[base+3]; } indicesArray = triangles; } geometry.setIndex(new THREE.BufferAttribute(indicesArray, 1)); } // 计算边界 geometry.computeBoundingSphere(); geometry.computeBoundingBox(); console.log('几何体验证:', { vertices: zone.vertices.length / 3, triangles: indicesArray.length / 3, radius: geometry.boundingSphere.radius.toFixed(2) }); geometry.computeVertexNormals(); console.log('法线检查:', { firstNormal: geometry.attributes.normal?.array.slice(0, 3) }); return geometry; } _addZoneLabel(zone, mesh, zoneIndex) { // 使用CSS2DRenderer需要先初始化 if (!this.labelRenderer) return; const labelDiv = document.createElement('div'); labelDiv.className = 'plt-zone-label'; labelDiv.textContent = `${zone.name}`; labelDiv.style.backgroundColor = `hsla(${zoneIndex * 360 / 8}, 70%, 50%, 0.7)`; const label = new THREE.CSS2DObject(labelDiv); label.position.set(0, 0, 0); // 位置会在动画中更新 mesh.userData.label = label; mesh.add(label); } _cleanupPreviousRender(scene) { scene.children.forEach(obj => { if (obj.userData?.isPLT) { // 清理几何体和材质 if (obj.geometry) obj.geometry.dispose(); if (obj.material) { if (Array.isArray(obj.material)) { obj.material.forEach(m => m.dispose()); } else { obj.material.dispose(); } } // 清理标签 if (obj.userData.label) { obj.remove(obj.userData.label); } scene.remove(obj); } }); } // 更新标签位置(需要在动画循环中调用) updateLabels(camera) { if (!this.labelRenderer) return; this.labelRenderer.render(scene, camera); scene.children.forEach(obj => { if (obj.userData?.label) { // 将标签定位到几何中心 obj.geometry.computeBoundingSphere(); const center = obj.geometry.boundingSphere.center.clone(); obj.localToWorld(center); obj.userData.label.position.copy(center); } }); } }