123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- 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);
- }
- });
- }
- }
|