s3m-three 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/browser/s3m-three.iife.ext-three.js +163 -15
- package/dist/browser/s3m-three.iife.js +190 -42
- package/dist/core/AtlasAlpha.d.ts +31 -0
- package/dist/core/AtlasAlpha.d.ts.map +1 -0
- package/dist/core/AtlasAlpha.js +82 -0
- package/dist/core/AtlasAlpha.js.map +1 -0
- package/dist/core/AtlasPack.d.ts +57 -0
- package/dist/core/AtlasPack.d.ts.map +1 -0
- package/dist/core/AtlasPack.js +81 -0
- package/dist/core/AtlasPack.js.map +1 -0
- package/dist/core/AtlasUpgrade.d.ts +68 -0
- package/dist/core/AtlasUpgrade.d.ts.map +1 -0
- package/dist/core/AtlasUpgrade.js +154 -0
- package/dist/core/AtlasUpgrade.js.map +1 -0
- package/dist/core/DXTTextureDecode.d.ts.map +1 -1
- package/dist/core/DXTTextureDecode.js +28 -6
- package/dist/core/DXTTextureDecode.js.map +1 -1
- package/dist/core/Draco.d.ts +52 -0
- package/dist/core/Draco.d.ts.map +1 -0
- package/dist/core/Draco.js +323 -0
- package/dist/core/Draco.js.map +1 -0
- package/dist/core/MeshOpt.d.ts +14 -3
- package/dist/core/MeshOpt.d.ts.map +1 -1
- package/dist/core/MeshOpt.js +101 -18
- package/dist/core/MeshOpt.js.map +1 -1
- package/dist/core/S3M1Parser.d.ts +16 -0
- package/dist/core/S3M1Parser.d.ts.map +1 -0
- package/dist/core/S3M1Parser.js +367 -0
- package/dist/core/S3M1Parser.js.map +1 -0
- package/dist/core/S3MBParser.d.ts +43 -1
- package/dist/core/S3MBParser.d.ts.map +1 -1
- package/dist/core/S3MBParser.js +101 -23
- package/dist/core/S3MBParser.js.map +1 -1
- package/dist/core/S3MBlockParser.d.ts +209 -0
- package/dist/core/S3MBlockParser.d.ts.map +1 -0
- package/dist/core/S3MBlockParser.js +789 -0
- package/dist/core/S3MBlockParser.js.map +1 -0
- package/dist/core/TextureFormat.d.ts +0 -1
- package/dist/core/TextureFormat.d.ts.map +1 -1
- package/dist/core/TextureFormat.js +19 -2
- package/dist/core/TextureFormat.js.map +1 -1
- package/dist/core/types.d.ts +65 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/render/DatasetManager.d.ts +11 -0
- package/dist/render/DatasetManager.d.ts.map +1 -1
- package/dist/render/DatasetManager.js +68 -3
- package/dist/render/DatasetManager.js.map +1 -1
- package/dist/render/GeometryBuilder.d.ts +3 -0
- package/dist/render/GeometryBuilder.d.ts.map +1 -1
- package/dist/render/GeometryBuilder.js +189 -17
- package/dist/render/GeometryBuilder.js.map +1 -1
- package/dist/render/GroundManager.d.ts +57 -1
- package/dist/render/GroundManager.d.ts.map +1 -1
- package/dist/render/GroundManager.js +149 -21
- package/dist/render/GroundManager.js.map +1 -1
- package/dist/render/Picker.d.ts.map +1 -1
- package/dist/render/Picker.js +12 -4
- package/dist/render/Picker.js.map +1 -1
- package/dist/render/PointCloudMaterial.d.ts +36 -0
- package/dist/render/PointCloudMaterial.d.ts.map +1 -0
- package/dist/render/PointCloudMaterial.js +159 -0
- package/dist/render/PointCloudMaterial.js.map +1 -0
- package/dist/render/S3MViewer.d.ts +75 -3
- package/dist/render/S3MViewer.d.ts.map +1 -1
- package/dist/render/S3MViewer.js +284 -54
- package/dist/render/S3MViewer.js.map +1 -1
- package/dist/render/StyleMaterial.d.ts +6 -0
- package/dist/render/StyleMaterial.d.ts.map +1 -1
- package/dist/render/StyleMaterial.js +125 -2
- package/dist/render/StyleMaterial.js.map +1 -1
- package/dist/render/TextureFactory.d.ts.map +1 -1
- package/dist/render/TextureFactory.js +64 -10
- package/dist/render/TextureFactory.js.map +1 -1
- package/dist/tileset/S3MTileset.d.ts +177 -1
- package/dist/tileset/S3MTileset.d.ts.map +1 -1
- package/dist/tileset/S3MTileset.js +754 -60
- package/dist/tileset/S3MTileset.js.map +1 -1
- package/dist/vendor/crunch_decoder.module.js +12 -18
- package/dist/vendor/draco_decoder.module.js +3178 -0
- package/docs/API.md +150 -11
- package/package.json +1 -1
- package/src/core/AtlasAlpha.ts +82 -0
- package/src/core/AtlasPack.ts +127 -0
- package/src/core/AtlasUpgrade.ts +182 -0
- package/src/core/DXTTextureDecode.ts +28 -6
- package/src/core/Draco.ts +409 -0
- package/src/core/MeshOpt.ts +124 -18
- package/src/core/S3M1Parser.ts +430 -0
- package/src/core/S3MBParser.ts +112 -21
- package/src/core/S3MBlockParser.ts +953 -0
- package/src/core/TextureFormat.ts +20 -2
- package/src/core/types.ts +63 -0
- package/src/index.ts +3 -1
- package/src/render/DatasetManager.ts +70 -5
- package/src/render/GeometryBuilder.ts +212 -16
- package/src/render/GroundManager.ts +174 -20
- package/src/render/Picker.ts +13 -4
- package/src/render/PointCloudMaterial.ts +174 -0
- package/src/render/S3MViewer.ts +322 -55
- package/src/render/StyleMaterial.ts +135 -2
- package/src/render/TextureFactory.ts +67 -11
- package/src/tileset/S3MTileset.ts +798 -65
- package/src/vendor/crunch_decoder.module.js +12 -18
- package/src/vendor/draco_decoder.module.d.ts +128 -0
- package/src/vendor/draco_decoder.module.js +3178 -0
package/dist/render/S3MViewer.js
CHANGED
|
@@ -21,8 +21,10 @@
|
|
|
21
21
|
* MapLibre / Mapbox 的胶水层在 s3m-maplibre 包的 S3MLayer.ts。
|
|
22
22
|
*/
|
|
23
23
|
import * as THREE from 'three';
|
|
24
|
+
import { usableGeoBounds } from '../tileset/S3MTileset.js';
|
|
24
25
|
import { setMaxAnisotropy } from './TextureFactory.js';
|
|
25
26
|
import { STYLE, STYLE_LABELS, SELECT_MODE, DEFAULT_STYLE, createSharedUniforms, createOutlineMaterial, } from './StyleMaterial.js';
|
|
27
|
+
import { applyPointCloudStyle, isOwnedPointCloudMaterial } from './PointCloudMaterial.js';
|
|
26
28
|
import { Picker } from './Picker.js';
|
|
27
29
|
import { buildFeatureEdgeSegments, buildThickLineGeometry, createLineSharedUniforms, createThickLineMaterial, } from './EdgeLines.js';
|
|
28
30
|
import { resolveFeature, measureSegments, buildAttributes, buildInstanceLookup, pickEntryId, } from '../core/FeatureInfo.js';
|
|
@@ -38,6 +40,10 @@ function styledMaterialOf(mesh) {
|
|
|
38
40
|
const styled = m;
|
|
39
41
|
return styled?.userData?.isStyled ? styled : null;
|
|
40
42
|
}
|
|
43
|
+
/** 点云是 THREE.Points 不是 Mesh,样式 / 拾取 / 卸载链路都靠这个判定放行 */
|
|
44
|
+
function isPointCloudObject(o) {
|
|
45
|
+
return o.isPoints === true;
|
|
46
|
+
}
|
|
41
47
|
/* ------------------------------------------------------------------ 图层 */
|
|
42
48
|
export class S3MViewer {
|
|
43
49
|
options;
|
|
@@ -103,8 +109,24 @@ export class S3MViewer {
|
|
|
103
109
|
_needsRepaint = true;
|
|
104
110
|
_frame = 0;
|
|
105
111
|
_fps = { value: 0, last: 0 };
|
|
106
|
-
|
|
107
|
-
|
|
112
|
+
_ambientLight;
|
|
113
|
+
_hemiLight;
|
|
114
|
+
_keyLight;
|
|
115
|
+
_fillLight;
|
|
116
|
+
_lightOptions = {
|
|
117
|
+
ambientIntensity: 0.4,
|
|
118
|
+
ambientColor: 0xffffff,
|
|
119
|
+
hemisphereIntensity: 0.8,
|
|
120
|
+
hemisphereColor: 0xffffff,
|
|
121
|
+
hemisphereGroundColor: 0xb6c0cb,
|
|
122
|
+
keyLightIntensity: 1.0,
|
|
123
|
+
keyLightColor: 0xffffff,
|
|
124
|
+
fillLightIntensity: 0.35,
|
|
125
|
+
fillLightColor: 0xffffff,
|
|
126
|
+
mode: 'headlamp',
|
|
127
|
+
azimuth: 150,
|
|
128
|
+
altitude: 50,
|
|
129
|
+
};
|
|
108
130
|
_datasetQueue;
|
|
109
131
|
/** 宿主重画信号与地图朝向,构造时注入 */
|
|
110
132
|
_onRequestRepaint = () => { };
|
|
@@ -114,10 +136,19 @@ export class S3MViewer {
|
|
|
114
136
|
_requestRepaint() {
|
|
115
137
|
this._onRequestRepaint();
|
|
116
138
|
}
|
|
139
|
+
triggerRepaint() {
|
|
140
|
+
this._requestRepaint();
|
|
141
|
+
}
|
|
117
142
|
constructor(options = {}) {
|
|
118
143
|
this.options = options;
|
|
119
144
|
this.pickable = options.pickable !== false;
|
|
120
145
|
this._lodRangeScale = options.lodRangeScale ?? 1.0;
|
|
146
|
+
if (options.light) {
|
|
147
|
+
Object.assign(this._lightOptions, options.light);
|
|
148
|
+
}
|
|
149
|
+
if (options.totalMaxTiles) {
|
|
150
|
+
this._datasetManager.totalMaxTiles = options.totalMaxTiles;
|
|
151
|
+
}
|
|
121
152
|
this._onRequestRepaint = options.onRequestRepaint ?? (() => { });
|
|
122
153
|
this._getBearing = options.getBearing;
|
|
123
154
|
this.onStats = options.onStats ?? (() => { });
|
|
@@ -125,6 +156,11 @@ export class S3MViewer {
|
|
|
125
156
|
this.onPick = options.onPick ?? (() => { });
|
|
126
157
|
this.onError = options.onError ?? ((e) => console.warn('[s3m]', e));
|
|
127
158
|
this.groundManager = new GroundManager(options.ground);
|
|
159
|
+
if (!this.scene) {
|
|
160
|
+
this.scene = new THREE.Scene();
|
|
161
|
+
this.scene.add(this.groundManager.group);
|
|
162
|
+
this._addLights();
|
|
163
|
+
}
|
|
128
164
|
this._featureStyleManager = new FeatureStyleManager(() => this.style.floorHeight, () => this._requestRepaint(), () => {
|
|
129
165
|
if (this._selection)
|
|
130
166
|
this._syncSelectionStyle();
|
|
@@ -201,24 +237,64 @@ export class S3MViewer {
|
|
|
201
237
|
this.renderer = null;
|
|
202
238
|
this.canvas = null;
|
|
203
239
|
}
|
|
240
|
+
_createLights() {
|
|
241
|
+
this._ambientLight = new THREE.AmbientLight(this._lightOptions.ambientColor, this._lightOptions.ambientIntensity);
|
|
242
|
+
this._hemiLight = new THREE.HemisphereLight(this._lightOptions.hemisphereColor, this._lightOptions.hemisphereGroundColor, this._lightOptions.hemisphereIntensity);
|
|
243
|
+
this._keyLight = new THREE.DirectionalLight(this._lightOptions.keyLightColor, this._lightOptions.keyLightIntensity);
|
|
244
|
+
this._fillLight = new THREE.DirectionalLight(this._lightOptions.fillLightColor, this._lightOptions.fillLightIntensity);
|
|
245
|
+
this._updateLights();
|
|
246
|
+
}
|
|
204
247
|
_addLights() {
|
|
205
248
|
if (!this.scene)
|
|
206
249
|
return;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
this.scene.add(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
250
|
+
if (!this._ambientLight)
|
|
251
|
+
this._createLights();
|
|
252
|
+
this.scene.add(this._ambientLight, this._hemiLight, this._keyLight, this._fillLight);
|
|
253
|
+
}
|
|
254
|
+
_applyLightOptions() {
|
|
255
|
+
if (this._ambientLight) {
|
|
256
|
+
this._ambientLight.intensity = this._lightOptions.ambientIntensity;
|
|
257
|
+
this._ambientLight.color.set(this._lightOptions.ambientColor);
|
|
258
|
+
}
|
|
259
|
+
if (this._hemiLight) {
|
|
260
|
+
this._hemiLight.intensity = this._lightOptions.hemisphereIntensity;
|
|
261
|
+
this._hemiLight.color.set(this._lightOptions.hemisphereColor);
|
|
262
|
+
this._hemiLight.groundColor.set(this._lightOptions.hemisphereGroundColor);
|
|
263
|
+
}
|
|
264
|
+
if (this._keyLight) {
|
|
265
|
+
this._keyLight.intensity = this._lightOptions.keyLightIntensity;
|
|
266
|
+
this._keyLight.color.set(this._lightOptions.keyLightColor);
|
|
267
|
+
}
|
|
268
|
+
if (this._fillLight) {
|
|
269
|
+
this._fillLight.intensity = this._lightOptions.fillLightIntensity;
|
|
270
|
+
this._fillLight.color.set(this._lightOptions.fillLightColor);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* 动态设置光照配置
|
|
275
|
+
*/
|
|
276
|
+
setLight(options) {
|
|
277
|
+
Object.assign(this._lightOptions, options);
|
|
278
|
+
this._applyLightOptions();
|
|
279
|
+
this._updateLights();
|
|
280
|
+
this._needsRepaint = true;
|
|
281
|
+
this.triggerRepaint();
|
|
215
282
|
}
|
|
216
283
|
/**
|
|
217
|
-
*
|
|
218
|
-
|
|
284
|
+
* 获取当前光照配置快照
|
|
285
|
+
*/
|
|
286
|
+
getLight() {
|
|
287
|
+
return { ...this._lightOptions };
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* 更新光照位置与方向:
|
|
291
|
+
* - 'headlamp': 随地图方位角 bearing 旋转,主光始终在镜头上方,受光面面向镜头;
|
|
292
|
+
* - 'fixed': 固定世界方位角(如模拟恒定日光)。
|
|
219
293
|
*/
|
|
220
294
|
_updateLights() {
|
|
221
|
-
|
|
295
|
+
if (!this._keyLight || !this._fillLight)
|
|
296
|
+
return;
|
|
297
|
+
const bearing = this._lightOptions.mode === 'fixed' ? 0 : (((this._getBearing?.() ?? 0) * Math.PI) / 180);
|
|
222
298
|
const place = (light, azOffsetDeg, elevDeg) => {
|
|
223
299
|
if (!light)
|
|
224
300
|
return;
|
|
@@ -227,8 +303,8 @@ export class S3MViewer {
|
|
|
227
303
|
const c = Math.cos(el);
|
|
228
304
|
light.position.set(Math.sin(az) * c, Math.cos(az) * c, Math.sin(el)).multiplyScalar(1000);
|
|
229
305
|
};
|
|
230
|
-
place(this._keyLight,
|
|
231
|
-
place(this._fillLight,
|
|
306
|
+
place(this._keyLight, this._lightOptions.azimuth, this._lightOptions.altitude);
|
|
307
|
+
place(this._fillLight, this._lightOptions.azimuth + 65, Math.max(10, this._lightOptions.altitude - 20));
|
|
232
308
|
}
|
|
233
309
|
/* ---------------------------------------------------------------- 数据集 */
|
|
234
310
|
_normalizeDatasets(options) {
|
|
@@ -323,8 +399,12 @@ export class S3MViewer {
|
|
|
323
399
|
/**
|
|
324
400
|
* 物理地表网格的高度跟随「主数据集的应用层抬升量」,
|
|
325
401
|
* 保证贴地后地表覆盖 / 地下透视的物理关系与被抬升的模型一致。
|
|
402
|
+
* `ground.trackHeight = false` 时不跟随:地表钉在当前高度,
|
|
403
|
+
* 高程调整只动模型不动切割面。
|
|
326
404
|
*/
|
|
327
405
|
_syncGroundHeight() {
|
|
406
|
+
if (!this.groundManager.trackHeight)
|
|
407
|
+
return;
|
|
328
408
|
const primaryId = this.datasets.keys().next().value;
|
|
329
409
|
if (!primaryId)
|
|
330
410
|
return;
|
|
@@ -455,6 +535,14 @@ export class S3MViewer {
|
|
|
455
535
|
this.groundManager.setExcavationBounds(bounds);
|
|
456
536
|
this._requestRepaint();
|
|
457
537
|
}
|
|
538
|
+
/**
|
|
539
|
+
* 开关「物理地表作为深度遮挡源」:开启后地表以下的几何被真正切掉。
|
|
540
|
+
* 不依赖宿主是否开启 MapLibre terrain——深度缓冲是共用的,谁更近谁遮挡。
|
|
541
|
+
*/
|
|
542
|
+
setGroundOcclude(enabled) {
|
|
543
|
+
this.groundManager.setOcclude(enabled);
|
|
544
|
+
this._requestRepaint();
|
|
545
|
+
}
|
|
458
546
|
/* ---------------------------------------------------------------- 状态纹理同步 */
|
|
459
547
|
_isFeatureHidden(smid, _batchId) {
|
|
460
548
|
if (smid == null)
|
|
@@ -529,6 +617,8 @@ export class S3MViewer {
|
|
|
529
617
|
const styled = styledMaterialOf(mesh);
|
|
530
618
|
if (styled)
|
|
531
619
|
styled.dispose();
|
|
620
|
+
if (isOwnedPointCloudMaterial(mesh.material))
|
|
621
|
+
mesh.material.dispose();
|
|
532
622
|
this._featureStyleManager.disposeMesh(mesh);
|
|
533
623
|
});
|
|
534
624
|
});
|
|
@@ -561,8 +651,10 @@ export class S3MViewer {
|
|
|
561
651
|
this._styleEdgeLine(child);
|
|
562
652
|
continue;
|
|
563
653
|
}
|
|
654
|
+
// 点云是 THREE.Points 不是 Mesh,但同样要进拾取表、吃不透明度、跟着着色模式走
|
|
655
|
+
const isPoints = isPointCloudObject(child);
|
|
564
656
|
const mesh = child;
|
|
565
|
-
if (!mesh.isMesh)
|
|
657
|
+
if (!mesh.isMesh && !isPoints)
|
|
566
658
|
continue;
|
|
567
659
|
const ud = mesh.userData;
|
|
568
660
|
ud.pickId = this._allocPickId();
|
|
@@ -576,42 +668,145 @@ export class S3MViewer {
|
|
|
576
668
|
});
|
|
577
669
|
this._indexMeshSmids(mesh);
|
|
578
670
|
this._addMeshHeight(mesh);
|
|
579
|
-
|
|
671
|
+
// 点云没有「棱」:给一堆孤立点建 EdgesGeometry 又慢又没意义
|
|
672
|
+
if (this._edgesEnabled && !hasNativeEdges && !isPoints)
|
|
580
673
|
this._ensureEdges(mesh);
|
|
581
674
|
// 新瓦片要继承当前的不透明度与着色方式
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
mat
|
|
587
|
-
mat
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
675
|
+
if (isPoints) {
|
|
676
|
+
this._applyPointCloudStyle(child);
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
const mat = styledMaterialOf(mesh);
|
|
680
|
+
if (mat) {
|
|
681
|
+
const o = Math.min(1, Math.max(0.05, this.style.opacity ?? 1));
|
|
682
|
+
mat.opacity = o;
|
|
683
|
+
mat.transparent = o < 0.999;
|
|
684
|
+
mat.depthWrite = o >= 0.999;
|
|
685
|
+
mat.flatShading = this.style.shading !== 'smooth';
|
|
686
|
+
mat.needsUpdate = true;
|
|
687
|
+
this._featureStyleManager.applyOverridesToMesh(mesh);
|
|
688
|
+
}
|
|
591
689
|
}
|
|
690
|
+
// 选中状态也要跟着继承:LOD 细化 / 瓦片重载会换掉 mesh,
|
|
691
|
+
// 不重放就出现「点中的那栋楼一拉近就没高亮了」
|
|
692
|
+
this._replaySelectionOnto(mesh);
|
|
592
693
|
}
|
|
593
694
|
}
|
|
594
|
-
/**
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
695
|
+
/**
|
|
696
|
+
* 把「当前选中」重放到一块新登记的 mesh 上。
|
|
697
|
+
*
|
|
698
|
+
* 为什么需要:选中高亮是直接写在各 mesh 材质的 uSelBatch / uSelWhole 上的,
|
|
699
|
+
* 而 LOD 细化、瓦片重载、淘汰回补都会换掉 mesh —— 不重放的话,用户点中的那栋楼
|
|
700
|
+
* 一细化就失去高亮(状态还在 _selection.smid 里,只是没人往新 mesh 上刷)。
|
|
701
|
+
*/
|
|
702
|
+
/**
|
|
703
|
+
* 判断一块无 SMID 单体化信息的网格(如倾斜摄影或白模整块)是否空间覆盖当前选中命中点
|
|
704
|
+
*/
|
|
705
|
+
_isMeshCoveringSelection(mesh, sel) {
|
|
706
|
+
if (!sel.point)
|
|
707
|
+
return false;
|
|
708
|
+
const geom = mesh.geometry;
|
|
709
|
+
if (!geom)
|
|
710
|
+
return false;
|
|
711
|
+
if (!geom.boundingBox)
|
|
712
|
+
geom.computeBoundingBox();
|
|
713
|
+
if (!geom.boundingBox)
|
|
714
|
+
return false;
|
|
715
|
+
// 将选中的三维场景点转入网格局部坐标系进行包围盒检测
|
|
716
|
+
const pt = new THREE.Vector3(sel.point[0], sel.point[1], sel.point[2]);
|
|
717
|
+
mesh.updateWorldMatrix(true, false);
|
|
718
|
+
const inv = mesh.matrixWorld.clone().invert();
|
|
719
|
+
const localPt = pt.applyMatrix4(inv);
|
|
720
|
+
// 留出 0.5 米容差,避免刚好处于三角网格边缘缝隙时漏判
|
|
721
|
+
const box = geom.boundingBox.clone().expandByScalar(0.5);
|
|
722
|
+
return box.containsPoint(localPt);
|
|
723
|
+
}
|
|
724
|
+
_replaySelectionOnto(mesh) {
|
|
725
|
+
const sel = this._selection;
|
|
726
|
+
if (!sel)
|
|
727
|
+
return;
|
|
728
|
+
const mat = styledMaterialOf(mesh);
|
|
729
|
+
if (!mat)
|
|
730
|
+
return;
|
|
731
|
+
const ud = mesh.userData;
|
|
732
|
+
let batchId = -1;
|
|
733
|
+
if (sel.smid != null) {
|
|
734
|
+
const pickInfo = ud.geoRef?.pickInfo;
|
|
735
|
+
const entry = pickInfo?.[sel.smid] ?? pickInfo?.[String(sel.smid)];
|
|
736
|
+
batchId = entry ? pickEntryId(entry) : -1;
|
|
737
|
+
}
|
|
738
|
+
// 拾取命中的那块 mesh 用原始 batchId;同一 smid 散在别的几何里时按各自的区间算
|
|
739
|
+
if (mesh === sel.mesh)
|
|
740
|
+
batchId = sel.batchId;
|
|
741
|
+
// 这块几何里根本没有这个单体 → 不动它(否则会把无关瓦片一起点亮)
|
|
742
|
+
if (batchId < 0 && mesh !== sel.mesh) {
|
|
743
|
+
if (sel.smid == null && ud.datasetId === sel.datasetId && this._isMeshCoveringSelection(mesh, sel)) {
|
|
744
|
+
batchId = -1;
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
return;
|
|
607
748
|
}
|
|
608
|
-
this._unindexMeshSmids(mesh);
|
|
609
|
-
this._removeMeshHeight(mesh);
|
|
610
|
-
this._featureStyleManager.disposeMesh(mesh);
|
|
611
|
-
const styled = styledMaterialOf(mesh);
|
|
612
|
-
styled?.dispose?.();
|
|
613
|
-
mesh.geometry?.dispose?.();
|
|
614
749
|
}
|
|
750
|
+
mat.userData.uSelBatch.value = batchId;
|
|
751
|
+
mat.userData.uSelWhole.value = batchId < 0 ? 1 : 0;
|
|
752
|
+
if (!this._selectedMeshes.includes(mesh))
|
|
753
|
+
this._selectedMeshes.push(mesh);
|
|
754
|
+
if (isPointCloudObject(mesh))
|
|
755
|
+
this._applyPointCloudStyle(mesh, true);
|
|
756
|
+
this.uniforms.uSelMix.value = 1;
|
|
757
|
+
this.uniforms.uDim.value = this.style.dimOthers ? 1 : 0;
|
|
758
|
+
}
|
|
759
|
+
/** 瓦片被淘汰:从拾取表和场景里摘掉,并深度彻底释放所有几何体与材质显存 */
|
|
760
|
+
_unregisterTile(node, entry) {
|
|
761
|
+
node.group.traverse((child) => {
|
|
762
|
+
const obj = child;
|
|
763
|
+
if (obj.userData?.pickId != null) {
|
|
764
|
+
this._pickables.delete(obj.userData.pickId);
|
|
765
|
+
this._releasePickId(obj.userData.pickId);
|
|
766
|
+
obj.userData.pickId = undefined;
|
|
767
|
+
}
|
|
768
|
+
if (obj.isMesh) {
|
|
769
|
+
const wasSelected = this._selectedMeshes.includes(obj);
|
|
770
|
+
if (wasSelected) {
|
|
771
|
+
this._selectedMeshes = this._selectedMeshes.filter((m) => m !== obj);
|
|
772
|
+
}
|
|
773
|
+
const currentSel = this._selection;
|
|
774
|
+
if (currentSel && currentSel.mesh === obj) {
|
|
775
|
+
const smid = currentSel.smid;
|
|
776
|
+
const carrier = (smid != null
|
|
777
|
+
? [...(this._smidMeshes.get(smid) ?? [])].find((m) => m !== obj && m.parent)
|
|
778
|
+
: this._selectedMeshes.find((m) => m !== obj && m.parent && m.parent.visible) ??
|
|
779
|
+
this._selectedMeshes.find((m) => m !== obj && m.parent));
|
|
780
|
+
if (carrier) {
|
|
781
|
+
// 选中身份交给承载同一 smid 的另一块 mesh(通常是接替显示的子瓦)
|
|
782
|
+
this._selection = { ...currentSel, mesh: carrier };
|
|
783
|
+
this._replaySelectionOnto(carrier);
|
|
784
|
+
}
|
|
785
|
+
else {
|
|
786
|
+
this.select(null);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
this._unindexMeshSmids(obj);
|
|
790
|
+
this._removeMeshHeight(obj);
|
|
791
|
+
this._featureStyleManager.disposeMesh(obj);
|
|
792
|
+
const styled = styledMaterialOf(obj);
|
|
793
|
+
styled?.dispose?.();
|
|
794
|
+
}
|
|
795
|
+
// 深度释放材质(点云、普通材质、材质数组)
|
|
796
|
+
if (isOwnedPointCloudMaterial(obj.material)) {
|
|
797
|
+
obj.material.dispose();
|
|
798
|
+
}
|
|
799
|
+
else if (obj.material) {
|
|
800
|
+
if (Array.isArray(obj.material)) {
|
|
801
|
+
obj.material.forEach((m) => m?.dispose?.());
|
|
802
|
+
}
|
|
803
|
+
else {
|
|
804
|
+
obj.material.dispose?.();
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
// 释放几何体显存缓冲
|
|
808
|
+
obj.geometry?.dispose?.();
|
|
809
|
+
});
|
|
615
810
|
entry.root.remove(node.group);
|
|
616
811
|
}
|
|
617
812
|
/**
|
|
@@ -644,6 +839,9 @@ export class S3MViewer {
|
|
|
644
839
|
this._applyStyleUniforms();
|
|
645
840
|
if ('opacity' in patch || 'depthWrite' in patch)
|
|
646
841
|
this._applyOpacity();
|
|
842
|
+
// 点云不走 StyleMaterial 的 uMode,模式切换要单独同步它的材质
|
|
843
|
+
if ('mode' in patch)
|
|
844
|
+
this._applyPointCloudStyles();
|
|
647
845
|
if ('shading' in patch)
|
|
648
846
|
this._applyShading();
|
|
649
847
|
if ('edges' in patch)
|
|
@@ -718,6 +916,10 @@ export class S3MViewer {
|
|
|
718
916
|
// 关键:若 depthWrite 开启(默认开启),即使半透明也保持深度写入,完美遮挡背面棱线,防止背面线条透出
|
|
719
917
|
const keepDepth = this.style.depthWrite !== false;
|
|
720
918
|
for (const p of this._pickables.values()) {
|
|
919
|
+
if (isPointCloudObject(p.mesh)) {
|
|
920
|
+
this._applyPointCloudStyle(p.mesh);
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
721
923
|
const m = styledMaterialOf(p.mesh);
|
|
722
924
|
if (!m)
|
|
723
925
|
continue;
|
|
@@ -727,6 +929,18 @@ export class S3MViewer {
|
|
|
727
929
|
}
|
|
728
930
|
this._requestRepaint();
|
|
729
931
|
}
|
|
932
|
+
/** 单个点云对象同步当前样式(着色模式 + 不透明度 + 选中高亮) */
|
|
933
|
+
_applyPointCloudStyle(points, selected = this._selectedMeshes.includes(points)) {
|
|
934
|
+
applyPointCloudStyle(points, this.style, this.uniforms, selected);
|
|
935
|
+
}
|
|
936
|
+
/** 全部点云刷新:着色模式切换时用(uniform 是共享引用,改色不用走这里) */
|
|
937
|
+
_applyPointCloudStyles() {
|
|
938
|
+
for (const p of this._pickables.values()) {
|
|
939
|
+
if (isPointCloudObject(p.mesh))
|
|
940
|
+
this._applyPointCloudStyle(p.mesh);
|
|
941
|
+
}
|
|
942
|
+
this._requestRepaint();
|
|
943
|
+
}
|
|
730
944
|
/** 平面/平滑着色:FLAT_SHADED 是编译期宏,改它必须重编译 */
|
|
731
945
|
_applyShading() {
|
|
732
946
|
const flat = this.style.shading !== 'smooth';
|
|
@@ -871,7 +1085,8 @@ export class S3MViewer {
|
|
|
871
1085
|
.map((p) => p.mesh)
|
|
872
1086
|
.filter((m) => {
|
|
873
1087
|
const ud = m.userData;
|
|
874
|
-
|
|
1088
|
+
// 点云没有棱可描
|
|
1089
|
+
return !ud.edges && !ud.nativeEdges && !isPointCloudObject(m);
|
|
875
1090
|
});
|
|
876
1091
|
let i = 0;
|
|
877
1092
|
const step = () => {
|
|
@@ -1379,8 +1594,13 @@ export class S3MViewer {
|
|
|
1379
1594
|
select(result, { silent = false } = {}) {
|
|
1380
1595
|
// 清掉上一次
|
|
1381
1596
|
if (this._selection) {
|
|
1382
|
-
for (const
|
|
1383
|
-
|
|
1597
|
+
for (const obj of this._selectedMeshes) {
|
|
1598
|
+
// 点云:恢复成当前着色模式(去掉选中混色)
|
|
1599
|
+
if (isPointCloudObject(obj)) {
|
|
1600
|
+
this._applyPointCloudStyle(obj, false);
|
|
1601
|
+
continue;
|
|
1602
|
+
}
|
|
1603
|
+
const mat = styledMaterialOf(obj);
|
|
1384
1604
|
if (mat) {
|
|
1385
1605
|
mat.userData.uSelBatch.value = -1;
|
|
1386
1606
|
mat.userData.uSelWhole.value = 0;
|
|
@@ -1432,14 +1652,23 @@ export class S3MViewer {
|
|
|
1432
1652
|
mat.userData.uSelWhole.value = batchId < 0 ? 1 : 0;
|
|
1433
1653
|
this._selectedMeshes.push(mesh);
|
|
1434
1654
|
}
|
|
1655
|
+
// 点云没有单体粒度:整块混成 selectColor,让选中至少看得见
|
|
1656
|
+
for (const mesh of this._selectedMeshes) {
|
|
1657
|
+
if (isPointCloudObject(mesh))
|
|
1658
|
+
this._applyPointCloudStyle(mesh, true);
|
|
1659
|
+
}
|
|
1435
1660
|
this.uniforms.uSelMix.value = 1;
|
|
1436
1661
|
this.uniforms.uDim.value = this.style.dimOthers ? 1 : 0;
|
|
1437
1662
|
if (this.style.outline) {
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1663
|
+
// 棱线 / 外扩轮廓都是按「面」设计的,点云画不出来也不该画
|
|
1664
|
+
const decoMeshes = [...targetMeshes].filter((m) => !isPointCloudObject(m));
|
|
1665
|
+
if (decoMeshes.length) {
|
|
1666
|
+
if ((this.style.outlineStyle ?? 'edges') === 'edges') {
|
|
1667
|
+
this._addSelectionEdges(result, decoMeshes);
|
|
1668
|
+
}
|
|
1669
|
+
else {
|
|
1670
|
+
this._addSelectionHull(result, decoMeshes);
|
|
1671
|
+
}
|
|
1443
1672
|
}
|
|
1444
1673
|
}
|
|
1445
1674
|
this._requestRepaint();
|
|
@@ -1706,7 +1935,8 @@ export class S3MViewer {
|
|
|
1706
1935
|
let maxLon = -Infinity;
|
|
1707
1936
|
let maxLat = -Infinity;
|
|
1708
1937
|
for (const entry of this.datasets.values()) {
|
|
1709
|
-
|
|
1938
|
+
// 同 DatasetManager.getBounds:退化 / 越界的 geoBounds 不参与,避免视角甩到 (0,0)
|
|
1939
|
+
const b = usableGeoBounds(entry.tileset?.config?.geoBounds);
|
|
1710
1940
|
if (!b)
|
|
1711
1941
|
continue;
|
|
1712
1942
|
minLon = Math.min(minLon, b.left);
|