s3m-three 0.4.17 → 0.4.19
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/dist/browser/assets/s3mWorker-4iTAmWIu.js +1 -0
- package/dist/browser/s3m-three.iife.ext-three.js +406 -91
- package/dist/browser/s3m-three.iife.js +434 -119
- package/dist/core/AttributeQuery.d.ts +13 -0
- package/dist/core/AttributeQuery.d.ts.map +1 -1
- package/dist/core/AttributeQuery.js +20 -3
- package/dist/core/AttributeQuery.js.map +1 -1
- package/dist/core/AuthFetch.d.ts +52 -0
- package/dist/core/AuthFetch.d.ts.map +1 -0
- package/dist/core/AuthFetch.js +44 -0
- package/dist/core/AuthFetch.js.map +1 -0
- package/dist/core/GaussianSplat.d.ts +64 -0
- package/dist/core/GaussianSplat.d.ts.map +1 -0
- package/dist/core/GaussianSplat.js +139 -0
- package/dist/core/GaussianSplat.js.map +1 -0
- package/dist/core/S3M1Parser.d.ts +1 -1
- package/dist/core/S3M1Parser.d.ts.map +1 -1
- package/dist/core/S3M1Parser.js +320 -25
- package/dist/core/S3M1Parser.js.map +1 -1
- package/dist/core/S3MBParser.d.ts.map +1 -1
- package/dist/core/S3MBParser.js +13 -0
- package/dist/core/S3MBParser.js.map +1 -1
- package/dist/core/S3MBlockParser.d.ts.map +1 -1
- package/dist/core/S3MBlockParser.js +56 -14
- package/dist/core/S3MBlockParser.js.map +1 -1
- package/dist/core/SplatLod.d.ts +36 -0
- package/dist/core/SplatLod.d.ts.map +1 -0
- package/dist/core/SplatLod.js +54 -0
- package/dist/core/SplatLod.js.map +1 -0
- package/dist/core/SplatProjection.d.ts +75 -0
- package/dist/core/SplatProjection.d.ts.map +1 -0
- package/dist/core/SplatProjection.js +162 -0
- package/dist/core/SplatProjection.js.map +1 -0
- package/dist/core/SplatSort.d.ts +13 -0
- package/dist/core/SplatSort.d.ts.map +1 -0
- package/dist/core/SplatSort.js +14 -0
- package/dist/core/SplatSort.js.map +1 -0
- package/dist/core/TexCoordZ.d.ts +94 -0
- package/dist/core/TexCoordZ.d.ts.map +1 -0
- package/dist/core/TexCoordZ.js +125 -0
- package/dist/core/TexCoordZ.js.map +1 -0
- package/dist/core/TextureDecode.d.ts +19 -0
- package/dist/core/TextureDecode.d.ts.map +1 -1
- package/dist/core/TextureDecode.js +53 -11
- package/dist/core/TextureDecode.js.map +1 -1
- package/dist/core/TileGeometry.d.ts +29 -0
- package/dist/core/TileGeometry.d.ts.map +1 -1
- package/dist/core/TileGeometry.js +130 -14
- package/dist/core/TileGeometry.js.map +1 -1
- package/dist/core/degradations.d.ts +37 -0
- package/dist/core/degradations.d.ts.map +1 -0
- package/dist/core/degradations.js +32 -0
- package/dist/core/degradations.js.map +1 -0
- package/dist/core/types.d.ts +36 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/render/DatasetManager.d.ts +18 -1
- package/dist/render/DatasetManager.d.ts.map +1 -1
- package/dist/render/DatasetManager.js +4 -1
- package/dist/render/DatasetManager.js.map +1 -1
- package/dist/render/FeatureStyleManager.d.ts.map +1 -1
- package/dist/render/FeatureStyleManager.js +7 -5
- package/dist/render/FeatureStyleManager.js.map +1 -1
- package/dist/render/GeometryBuilder.d.ts +24 -0
- package/dist/render/GeometryBuilder.d.ts.map +1 -1
- package/dist/render/GeometryBuilder.js +196 -5
- package/dist/render/GeometryBuilder.js.map +1 -1
- package/dist/render/HostRenderer.d.ts +105 -0
- package/dist/render/HostRenderer.d.ts.map +1 -0
- package/dist/render/HostRenderer.js +170 -0
- package/dist/render/HostRenderer.js.map +1 -0
- package/dist/render/Picker.d.ts.map +1 -1
- package/dist/render/Picker.js +19 -5
- package/dist/render/Picker.js.map +1 -1
- package/dist/render/S3MViewer.d.ts +79 -0
- package/dist/render/S3MViewer.d.ts.map +1 -1
- package/dist/render/S3MViewer.js +235 -16
- package/dist/render/S3MViewer.js.map +1 -1
- package/dist/render/SplatMaterial.d.ts +103 -0
- package/dist/render/SplatMaterial.d.ts.map +1 -0
- package/dist/render/SplatMaterial.js +388 -0
- package/dist/render/SplatMaterial.js.map +1 -0
- package/dist/render/SplatSorter.d.ts +14 -0
- package/dist/render/SplatSorter.d.ts.map +1 -0
- package/dist/render/SplatSorter.js +59 -0
- package/dist/render/SplatSorter.js.map +1 -0
- package/dist/render/StyleMaterial.d.ts +48 -0
- package/dist/render/StyleMaterial.d.ts.map +1 -1
- package/dist/render/StyleMaterial.js +292 -69
- package/dist/render/StyleMaterial.js.map +1 -1
- package/dist/render/TextureFactory.d.ts +10 -0
- package/dist/render/TextureFactory.d.ts.map +1 -1
- package/dist/render/TextureFactory.js +35 -5
- package/dist/render/TextureFactory.js.map +1 -1
- package/dist/render/lightingPreset.d.ts +53 -0
- package/dist/render/lightingPreset.d.ts.map +1 -0
- package/dist/render/lightingPreset.js +36 -0
- package/dist/render/lightingPreset.js.map +1 -0
- package/dist/render/materialAlpha.d.ts +51 -0
- package/dist/render/materialAlpha.d.ts.map +1 -0
- package/dist/render/materialAlpha.js +68 -0
- package/dist/render/materialAlpha.js.map +1 -0
- package/dist/tileset/S3MTileset.d.ts +50 -0
- package/dist/tileset/S3MTileset.d.ts.map +1 -1
- package/dist/tileset/S3MTileset.js +60 -5
- package/dist/tileset/S3MTileset.js.map +1 -1
- package/dist/vendor/browser-node-stub.js +10 -0
- package/dist/worker/s3mWorker.js +1166 -979
- package/dist/worker/s3mWorker.js.map +1 -1
- package/docs/API.md +55 -1
- package/docs/FORMAT-NOTES.md +338 -0
- package/package.json +1 -1
- package/src/core/AttributeQuery.ts +26 -3
- package/src/core/AuthFetch.ts +82 -0
- package/src/core/GaussianSplat.ts +175 -0
- package/src/core/S3M1Parser.ts +330 -24
- package/src/core/S3MBParser.ts +18 -0
- package/src/core/S3MBlockParser.ts +56 -11
- package/src/core/SplatLod.ts +83 -0
- package/src/core/SplatProjection.ts +231 -0
- package/src/core/SplatSort.ts +30 -0
- package/src/core/TexCoordZ.ts +160 -0
- package/src/core/TextureDecode.ts +63 -11
- package/src/core/TileGeometry.ts +138 -14
- package/src/core/degradations.ts +74 -0
- package/src/core/types.ts +32 -0
- package/src/index.ts +12 -1
- package/src/render/DatasetManager.ts +19 -1
- package/src/render/FeatureStyleManager.ts +7 -5
- package/src/render/GeometryBuilder.ts +239 -6
- package/src/render/HostRenderer.ts +222 -0
- package/src/render/Picker.ts +21 -5
- package/src/render/S3MViewer.ts +277 -15
- package/src/render/SplatMaterial.ts +433 -0
- package/src/render/SplatSorter.ts +59 -0
- package/src/render/StyleMaterial.ts +339 -69
- package/src/render/TextureFactory.ts +39 -5
- package/src/render/lightingPreset.ts +76 -0
- package/src/render/materialAlpha.ts +75 -0
- package/src/tileset/S3MTileset.ts +91 -5
- package/src/vendor/browser-node-stub.js +10 -0
- package/dist/browser/assets/s3mWorker-BjPOUgjg.js +0 -1
package/dist/render/S3MViewer.js
CHANGED
|
@@ -22,10 +22,15 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import * as THREE from 'three';
|
|
24
24
|
import { usableGeoBounds } from '../tileset/S3MTileset.js';
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
25
|
+
import { authFetch } from '../core/AuthFetch.js';
|
|
26
|
+
import { sortSplatMesh } from './SplatSorter.js';
|
|
27
|
+
import { splatCenterBounds } from './SplatMaterial.js';
|
|
28
|
+
import { setSplatViewport } from './GeometryBuilder.js';
|
|
29
|
+
import { setMaxAnisotropy, setAnisotropyCap, setCompressedTexturesSupported, setTextureOffThreadDecode, setMaxTextureSize as setTextureSizeCap, setSrgbTextureDecode } from './TextureFactory.js';
|
|
30
|
+
import { getMaterialQuality, setMaterialQuality, setColorPipeline } from './StyleMaterial.js';
|
|
31
|
+
import { resolveLightingPreset } from './lightingPreset.js';
|
|
27
32
|
import { decodeTextureWithWorker } from '../worker/S3MWorkerClient.js';
|
|
28
|
-
import { STYLE, STYLE_LABELS, SELECT_MODE, DEFAULT_STYLE, createSharedUniforms, createStyledMaterial, createOutlineMaterial, resolveEdgeModes, sameEdgeModes, } from './StyleMaterial.js';
|
|
33
|
+
import { STYLE, STYLE_LABELS, SELECT_MODE, DEFAULT_STYLE, createSharedUniforms, createStyledMaterial, createOutlineMaterial, resolveEdgeModes, sameEdgeModes, restoreBaseBlendState, } from './StyleMaterial.js';
|
|
29
34
|
import { applyPointCloudStyle, isOwnedPointCloudMaterial } from './PointCloudMaterial.js';
|
|
30
35
|
import { Picker } from './Picker.js';
|
|
31
36
|
import { GpuEdgePass } from './GpuEdgePass.js';
|
|
@@ -81,6 +86,8 @@ const STYLE_SCALAR_KEYS = [
|
|
|
81
86
|
'texMix',
|
|
82
87
|
'roofThreshold',
|
|
83
88
|
'ambientGain',
|
|
89
|
+
'metalness',
|
|
90
|
+
'roughness',
|
|
84
91
|
'edgeWidth',
|
|
85
92
|
'edgeOpacity',
|
|
86
93
|
'edgeOffset',
|
|
@@ -190,6 +197,9 @@ export class S3MViewer {
|
|
|
190
197
|
_nativeEdgeLines = new Set();
|
|
191
198
|
/** 点云对象集合:透明度动画时只刷它们,避免每帧遍历全部 pickables */
|
|
192
199
|
_pointClouds = new Set();
|
|
200
|
+
/** 泼溅排序状态:相机不动且没有新瓦片时,排序结果可以直接复用 */
|
|
201
|
+
_lastSplatSortView = new THREE.Matrix4();
|
|
202
|
+
_splatsSorted = false;
|
|
193
203
|
/** 当前材质是否处于 transparent 状态,用来避免逐帧重复切换 program */
|
|
194
204
|
_opacityTransparent = false;
|
|
195
205
|
/**
|
|
@@ -213,6 +223,8 @@ export class S3MViewer {
|
|
|
213
223
|
_hemiLight;
|
|
214
224
|
_keyLight;
|
|
215
225
|
_fillLight;
|
|
226
|
+
/** 环境贴图的 PMREM 渲染目标(setEnvironment 建的,unmount 时释放) */
|
|
227
|
+
_envRT = null;
|
|
216
228
|
_lightOptions = {
|
|
217
229
|
ambientIntensity: 0.4,
|
|
218
230
|
ambientColor: 0xffffff,
|
|
@@ -231,7 +243,11 @@ export class S3MViewer {
|
|
|
231
243
|
/** 宿主重画信号与地图朝向,构造时注入 */
|
|
232
244
|
_onRequestRepaint = () => { };
|
|
233
245
|
_getBearing;
|
|
246
|
+
/** 鉴权适配(全局默认),构造时注入 */
|
|
247
|
+
_authenticate;
|
|
234
248
|
_lodRangeScale = 1.0;
|
|
249
|
+
/** 图层级降级策略,数据集可覆盖,见 `S3MViewerOptions.unsupported` */
|
|
250
|
+
_unsupported;
|
|
235
251
|
/** LOD 判定屏幕高度的 DPR 上限。Infinity = 不截断(默认,行为同旧版) */
|
|
236
252
|
_lodPixelRatioCap = Infinity;
|
|
237
253
|
/** 宿主请求重画(MapLibre 传 triggerRepaint,纯 three 宿主传自己的渲染循环) */
|
|
@@ -245,9 +261,14 @@ export class S3MViewer {
|
|
|
245
261
|
this.options = options;
|
|
246
262
|
this.pickable = options.pickable !== false;
|
|
247
263
|
this._lodRangeScale = options.lodRangeScale ?? 1.0;
|
|
264
|
+
this._unsupported = options.unsupported ?? 'warn';
|
|
248
265
|
this._lodPixelRatioCap = options.lodPixelRatioCap ?? Infinity;
|
|
249
266
|
setTextureSizeCap(options.maxTextureSize ?? Infinity);
|
|
250
267
|
setMaterialQuality(options.materialQuality ?? 'pbr');
|
|
268
|
+
if (options.lightingPreset && options.lightingPreset !== 'default') {
|
|
269
|
+
Object.assign(this._lightOptions, resolveLightingPreset(options.lightingPreset));
|
|
270
|
+
this._applyColorPipeline(options.lightingPreset);
|
|
271
|
+
}
|
|
251
272
|
if (options.light) {
|
|
252
273
|
Object.assign(this._lightOptions, options.light);
|
|
253
274
|
}
|
|
@@ -256,6 +277,7 @@ export class S3MViewer {
|
|
|
256
277
|
}
|
|
257
278
|
this._onRequestRepaint = options.onRequestRepaint ?? (() => { });
|
|
258
279
|
this._getBearing = options.getBearing;
|
|
280
|
+
this._authenticate = options.authenticate;
|
|
259
281
|
this.onStats = options.onStats ?? (() => { });
|
|
260
282
|
this.onReady = options.onReady ?? (() => { });
|
|
261
283
|
this.onPick = options.onPick ?? (() => { });
|
|
@@ -317,7 +339,10 @@ export class S3MViewer {
|
|
|
317
339
|
this._fps.last = performance.now();
|
|
318
340
|
this._datasetQueue = this._normalizeDatasets(options);
|
|
319
341
|
if (options.attributes)
|
|
320
|
-
this.attributeQuery = new AttributeQuery(
|
|
342
|
+
this.attributeQuery = new AttributeQuery({
|
|
343
|
+
...options.attributes,
|
|
344
|
+
authenticate: options.attributes.authenticate ?? this._authenticate,
|
|
345
|
+
});
|
|
321
346
|
}
|
|
322
347
|
/* ---------------------------------------------------------------- 生命周期 */
|
|
323
348
|
/**
|
|
@@ -404,6 +429,8 @@ export class S3MViewer {
|
|
|
404
429
|
this.groundManager.dispose();
|
|
405
430
|
this.picker?.dispose();
|
|
406
431
|
this.picker = null;
|
|
432
|
+
this._envRT?.dispose();
|
|
433
|
+
this._envRT = null;
|
|
407
434
|
this.outlineMaterial.dispose();
|
|
408
435
|
this._gpuEdge?.dispose();
|
|
409
436
|
this._gpuEdge = null;
|
|
@@ -461,6 +488,32 @@ export class S3MViewer {
|
|
|
461
488
|
this._fillLight.color.set(this._lightOptions.fillLightColor);
|
|
462
489
|
}
|
|
463
490
|
}
|
|
491
|
+
/**
|
|
492
|
+
* 运行时切换光照预设:覆盖四个强度,颜色/朝向保持不变。
|
|
493
|
+
* 之后再调 `setLight({...})` 仍然可以逐项微调。
|
|
494
|
+
*/
|
|
495
|
+
/**
|
|
496
|
+
* supermap 完整版 = 光照强度 + 颜色管线一起切:
|
|
497
|
+
* direct 管线下纹理不做 sRGB 解码、片元按官方常数 Lambert 直乘(见 lightingPreset.ts)。
|
|
498
|
+
* 两个都是模块级全局态,只影响之后创建的材质 / 纹理 —— 已加载瓦片要重载才换。
|
|
499
|
+
*/
|
|
500
|
+
_applyColorPipeline(preset) {
|
|
501
|
+
const direct = preset === 'supermap';
|
|
502
|
+
setColorPipeline(direct ? 'direct' : 'linear');
|
|
503
|
+
setSrgbTextureDecode(!direct);
|
|
504
|
+
// 官方不开各向异性:远处直接采高 mip 平均成纯色("灰色滤镜"的一部分)。
|
|
505
|
+
// 我们默认 Infinity(倾斜摄影立面清晰度优先),supermap 模式必须收回。
|
|
506
|
+
setAnisotropyCap(direct ? 1 : Infinity);
|
|
507
|
+
}
|
|
508
|
+
setLightingPreset(preset) {
|
|
509
|
+
Object.assign(this._lightOptions, resolveLightingPreset(preset));
|
|
510
|
+
this._applyColorPipeline(preset);
|
|
511
|
+
this.options.lightingPreset = preset;
|
|
512
|
+
this._applyLightOptions();
|
|
513
|
+
this._updateLights();
|
|
514
|
+
this._needsRepaint = true;
|
|
515
|
+
this.triggerRepaint();
|
|
516
|
+
}
|
|
464
517
|
/**
|
|
465
518
|
* 动态设置光照配置
|
|
466
519
|
*/
|
|
@@ -525,20 +578,28 @@ export class S3MViewer {
|
|
|
525
578
|
};
|
|
526
579
|
place(this._keyLight, this._lightOptions.azimuth, this._lightOptions.altitude);
|
|
527
580
|
place(this._fillLight, this._lightOptions.azimuth + 65, Math.max(10, this._lightOptions.altitude - 20));
|
|
581
|
+
// direct 管线的官方 Lambert 用主光方向(共享 uniform,所有材质同步生效)
|
|
582
|
+
if (this.uniforms?.uSunDirection && this._keyLight) {
|
|
583
|
+
this.uniforms.uSunDirection.value.copy(this._keyLight.position).normalize();
|
|
584
|
+
}
|
|
528
585
|
}
|
|
529
586
|
/* ---------------------------------------------------------------- 数据集 */
|
|
530
587
|
_normalizeDatasets(options) {
|
|
531
588
|
if (Array.isArray(options.datasets))
|
|
532
|
-
return options.datasets
|
|
589
|
+
return options.datasets
|
|
590
|
+
.filter((d) => !!d?.configUrl)
|
|
591
|
+
.map((d) => ({ ...d, polygonOffset: d.polygonOffset ?? options.polygonOffset }));
|
|
533
592
|
if (options.configUrl)
|
|
534
|
-
return [{ id: 'default', name: '默认数据集', configUrl: options.configUrl, useWorker: options.useWorker }];
|
|
593
|
+
return [{ id: 'default', name: '默认数据集', configUrl: options.configUrl, useWorker: options.useWorker, polygonOffset: options.polygonOffset }];
|
|
535
594
|
return [];
|
|
536
595
|
}
|
|
537
596
|
/** 追加一个数据集。图层 addLayer 之前调用会抛错。 */
|
|
538
597
|
async addDataset(ds) {
|
|
539
598
|
if (!this.scene)
|
|
540
599
|
throw new Error('[s3m] 图层尚未 addLayer,不能添加数据集');
|
|
541
|
-
|
|
600
|
+
// 图层级默认值:数据集自己指定了就用自己的,见 `S3MViewerOptions.unsupported`
|
|
601
|
+
const merged = { ...ds, unsupported: ds.unsupported ?? this._unsupported };
|
|
602
|
+
const entry = await this._datasetManager.addDataset(merged, this.scene, this.geo, this.uniforms, this._lodRangeScale, this.options.buildTile, this.options.maxTiles ?? 64, this.options.maxConcurrent ?? 4, {
|
|
542
603
|
onTile: (node, e) => {
|
|
543
604
|
this._registerTile(node, e);
|
|
544
605
|
this.onStats(this.stats);
|
|
@@ -569,7 +630,7 @@ export class S3MViewer {
|
|
|
569
630
|
triggerRepaint: () => {
|
|
570
631
|
this._requestRepaint();
|
|
571
632
|
},
|
|
572
|
-
}, this.options.maxTriangles ?? Infinity, this.options.maxImageCache ?? 128);
|
|
633
|
+
}, this.options.maxTriangles ?? Infinity, this.options.maxImageCache ?? 128, this._authenticate);
|
|
573
634
|
return entry;
|
|
574
635
|
}
|
|
575
636
|
/**
|
|
@@ -1147,7 +1208,9 @@ export class S3MViewer {
|
|
|
1147
1208
|
}
|
|
1148
1209
|
/* ---------------------------------------------------------------- 按业务字段着色 */
|
|
1149
1210
|
setAttributeSource(options) {
|
|
1150
|
-
this.attributeQuery = options
|
|
1211
|
+
this.attributeQuery = options
|
|
1212
|
+
? new AttributeQuery({ ...options, authenticate: options.authenticate ?? this._authenticate })
|
|
1213
|
+
: null;
|
|
1151
1214
|
}
|
|
1152
1215
|
getLoadedSmids() {
|
|
1153
1216
|
return [...this._smidMeshes.keys()];
|
|
@@ -1345,6 +1408,9 @@ export class S3MViewer {
|
|
|
1345
1408
|
continue;
|
|
1346
1409
|
if (isPoints)
|
|
1347
1410
|
this._pointClouds.add(child);
|
|
1411
|
+
// 新瓦片里的泼溅实例还没按当前视角排过,下一帧要重排
|
|
1412
|
+
if (isPoints)
|
|
1413
|
+
this._splatsSorted = false;
|
|
1348
1414
|
const ud = mesh.userData;
|
|
1349
1415
|
ud.pickId = this._allocPickId();
|
|
1350
1416
|
ud.datasetId = entry.id;
|
|
@@ -1378,9 +1444,11 @@ export class S3MViewer {
|
|
|
1378
1444
|
if (mat) {
|
|
1379
1445
|
const o = clampOpacity(this.style.opacity);
|
|
1380
1446
|
// 不写 mat.opacity:全局透明度走共享 uOpacity,材质自身 opacity 保留模型自带 alpha
|
|
1381
|
-
|
|
1447
|
+
// 混合开关不能写成 `o < 0.999` 一刀切:材质自身可能是 blend / 顶点 alpha
|
|
1448
|
+
// 透明(s3mOpts.transparent,如 SMID 4421),注册时就被这句踩掉,
|
|
1449
|
+
// 表现为「刚进去没透明度,切换材质质感重建后才恢复」。
|
|
1382
1450
|
const keepDepth = this.style.depthWrite !== false;
|
|
1383
|
-
mat
|
|
1451
|
+
restoreBaseBlendState(mat, o < 0.999, keepDepth);
|
|
1384
1452
|
// shading 走共享 uniform(uFlatShading),不写 mat.flatShading / needsUpdate——
|
|
1385
1453
|
// transparent / depthWrite 也不是 program 决定项,整套样式继承零重编译
|
|
1386
1454
|
if (mat.userData.uFlatShading) {
|
|
@@ -1434,9 +1502,10 @@ export class S3MViewer {
|
|
|
1434
1502
|
mesh.userData.tileFadeKey = undefined;
|
|
1435
1503
|
this.animator.unregister(key);
|
|
1436
1504
|
// 淡入期间强制开了 transparent + depthWrite,结束时不能自己拍板关掉——
|
|
1437
|
-
//
|
|
1505
|
+
// 先恢复「材质自身语义 || 图层透明度」的基础状态(材质可能是 blend /
|
|
1506
|
+
// 顶点 alpha 透明,如 SMID 4421),再让单体淡化流程有需要时重新打开。
|
|
1507
|
+
restoreBaseBlendState(mat, this._opacityTransparent, this.style.depthWrite !== false);
|
|
1438
1508
|
this._featureStyleManager.applyOverridesToMesh(mesh);
|
|
1439
|
-
mat.depthWrite = this.style.depthWrite !== false;
|
|
1440
1509
|
this._requestRepaint();
|
|
1441
1510
|
},
|
|
1442
1511
|
});
|
|
@@ -1637,6 +1706,46 @@ export class S3MViewer {
|
|
|
1637
1706
|
getStyle() {
|
|
1638
1707
|
return { ...this._styleTarget, modeLabel: STYLE_LABELS[this._styleTarget.mode] };
|
|
1639
1708
|
}
|
|
1709
|
+
/**
|
|
1710
|
+
* 挂 / 摘环境贴图。
|
|
1711
|
+
*
|
|
1712
|
+
* 金属度(`setStyle({ metalness })`)**必须**有环境图才看得出质感:
|
|
1713
|
+
* PBR 的金属几乎没有漫反射,画面全靠 `scene.environment` 里的环境反射,
|
|
1714
|
+
* 没有它时 metalness 一高就整片发黑,只剩两盏平行灯打成的小高光点。
|
|
1715
|
+
*
|
|
1716
|
+
* @param source 等距柱状图(.hdr / .jpg / .png,2:1)地址,或已加载的 `THREE.Texture`;
|
|
1717
|
+
* `null` = 关掉环境图
|
|
1718
|
+
* @param intensity 环境光强度(`scene.environmentIntensity`,默认 1)
|
|
1719
|
+
*
|
|
1720
|
+
* 传入 URL 时纹理由内部加载并转成 PMREM(预滤波 mipmap);传 Texture 时不会接管它的生命周期。
|
|
1721
|
+
*/
|
|
1722
|
+
async setEnvironment(source, intensity = 1) {
|
|
1723
|
+
if (!this.renderer || !this.scene)
|
|
1724
|
+
return;
|
|
1725
|
+
this.scene.environmentIntensity = Math.max(0, intensity);
|
|
1726
|
+
if (source == null) {
|
|
1727
|
+
this.scene.environment = null;
|
|
1728
|
+
this._envRT?.dispose();
|
|
1729
|
+
this._envRT = null;
|
|
1730
|
+
this._requestRepaint();
|
|
1731
|
+
return;
|
|
1732
|
+
}
|
|
1733
|
+
const fromUrl = typeof source === 'string';
|
|
1734
|
+
const tex = fromUrl ? await new THREE.TextureLoader().loadAsync(source) : source;
|
|
1735
|
+
// PMREM 只认等距柱状图;普通贴图也按 2:1 全景处理
|
|
1736
|
+
tex.mapping = THREE.EquirectangularReflectionMapping;
|
|
1737
|
+
const pmrem = new THREE.PMREMGenerator(this.renderer);
|
|
1738
|
+
pmrem.compileEquirectangularShader();
|
|
1739
|
+
const rt = pmrem.fromEquirectangular(tex);
|
|
1740
|
+
pmrem.dispose();
|
|
1741
|
+
if (fromUrl)
|
|
1742
|
+
tex.dispose();
|
|
1743
|
+
this.scene.environment = rt.texture;
|
|
1744
|
+
// 上一张(同一个 viewer 反复换)要释放,否则显存里会一直堆
|
|
1745
|
+
this._envRT?.dispose();
|
|
1746
|
+
this._envRT = rt;
|
|
1747
|
+
this._requestRepaint();
|
|
1748
|
+
}
|
|
1640
1749
|
/** 把某个样式键的取值转成动画通道能吃的形状(标量 or 线性 RGB 三元组) */
|
|
1641
1750
|
_animTargetOf(key, patch) {
|
|
1642
1751
|
const value = patch[key];
|
|
@@ -1816,6 +1925,11 @@ export class S3MViewer {
|
|
|
1816
1925
|
if (u.uAmbientGain && s.ambientGain != null) {
|
|
1817
1926
|
u.uAmbientGain.value = s.ambientGain;
|
|
1818
1927
|
}
|
|
1928
|
+
// 金属度 / 粗糙度:负值 = 不覆盖,保持材质(数据)自身的值
|
|
1929
|
+
if (u.uMetalness)
|
|
1930
|
+
u.uMetalness.value = s.metalness ?? -1;
|
|
1931
|
+
if (u.uRoughness)
|
|
1932
|
+
u.uRoughness.value = s.roughness ?? -1;
|
|
1819
1933
|
if (u.uOpacity && s.opacity != null) {
|
|
1820
1934
|
u.uOpacity.value = clampOpacity(s.opacity);
|
|
1821
1935
|
}
|
|
@@ -1862,8 +1976,9 @@ export class S3MViewer {
|
|
|
1862
1976
|
continue;
|
|
1863
1977
|
if ((m.userData.uTileFade?.value ?? 1) < 1)
|
|
1864
1978
|
continue;
|
|
1865
|
-
|
|
1866
|
-
|
|
1979
|
+
// 材质自身可能是 blend / 顶点 alpha 透明(s3mOpts.transparent),
|
|
1980
|
+
// 关全局混合时不能把它一刀切成不透明
|
|
1981
|
+
restoreBaseBlendState(m, transparent, keepDepth);
|
|
1867
1982
|
}
|
|
1868
1983
|
}
|
|
1869
1984
|
// 点云不走 StyleMaterial,自己有一套 uOpacity,逐帧刷的只有这几个对象
|
|
@@ -2202,7 +2317,7 @@ export class S3MViewer {
|
|
|
2202
2317
|
* @param url 棱线缓存地址(二进制 Float32 序列:x,y,z,x,y,z,… 成对成段)
|
|
2203
2318
|
*/
|
|
2204
2319
|
async loadCreases(url) {
|
|
2205
|
-
const resp = await
|
|
2320
|
+
const resp = await authFetch(url, this._authenticate, 'creases');
|
|
2206
2321
|
if (!resp.ok)
|
|
2207
2322
|
throw new Error(`[s3m] 棱线缓存加载失败: ${resp.status} ${url}`);
|
|
2208
2323
|
const buf = await resp.arrayBuffer();
|
|
@@ -2541,6 +2656,10 @@ export class S3MViewer {
|
|
|
2541
2656
|
}
|
|
2542
2657
|
_buildPickResult(hit, ref, exactPoint, ray) {
|
|
2543
2658
|
const ud = ref.mesh.userData;
|
|
2659
|
+
// 泼溅走独立分支:它既没有 instanceMatrix,也没有 geoRef.pickInfo 里的单体表,
|
|
2660
|
+
// 走下面那段会在 `_instanceFeature()` 拿到 null 直接返回 —— 表现是「点得中但拿不到结果」。
|
|
2661
|
+
if (ud.isGaussianSplat)
|
|
2662
|
+
return this._buildSplatPickResult(hit, ref, exactPoint, ray);
|
|
2544
2663
|
let smid = null;
|
|
2545
2664
|
let batchId = hit.batchId;
|
|
2546
2665
|
let instanceIndex = hit.instanceIndex;
|
|
@@ -2684,6 +2803,72 @@ export class S3MViewer {
|
|
|
2684
2803
|
mesh: ref.mesh,
|
|
2685
2804
|
};
|
|
2686
2805
|
}
|
|
2806
|
+
/**
|
|
2807
|
+
* 泼溅的拾取结果。
|
|
2808
|
+
*
|
|
2809
|
+
* 整块泼溅瓦片当一个单体(没有单体级 smid 可报),命中点由 `splatRaycast` 给出 ——
|
|
2810
|
+
* 它按每个高斯的 3σ 球求交,比拾取那一趟为了命中率摊开的 4px 方块准得多。
|
|
2811
|
+
* 包围盒必须由 `iCenter` 算:geometry 的 position 只是 [-1,1]² 的四边形角点。
|
|
2812
|
+
*/
|
|
2813
|
+
_buildSplatPickResult(hit, ref, exactPoint, ray) {
|
|
2814
|
+
const anchor = this.sceneAnchor;
|
|
2815
|
+
if (!anchor)
|
|
2816
|
+
return null;
|
|
2817
|
+
const mesh = ref.mesh;
|
|
2818
|
+
let hitPoint = exactPoint ?? null;
|
|
2819
|
+
if (!hitPoint && ray)
|
|
2820
|
+
hitPoint = this._intersectMeshAtRay(mesh, ray);
|
|
2821
|
+
const local = splatCenterBounds(mesh.geometry);
|
|
2822
|
+
const box = local
|
|
2823
|
+
? {
|
|
2824
|
+
minX: local.minX,
|
|
2825
|
+
minY: local.minY,
|
|
2826
|
+
minZ: local.minZ,
|
|
2827
|
+
maxX: local.maxX,
|
|
2828
|
+
maxY: local.maxY,
|
|
2829
|
+
maxZ: local.maxZ,
|
|
2830
|
+
count: local.count,
|
|
2831
|
+
triangles: 0,
|
|
2832
|
+
}
|
|
2833
|
+
: null;
|
|
2834
|
+
const attributes = buildAttributes({
|
|
2835
|
+
smid: null,
|
|
2836
|
+
box,
|
|
2837
|
+
triangles: 0,
|
|
2838
|
+
floorHeight: this.style.floorHeight,
|
|
2839
|
+
});
|
|
2840
|
+
const s = anchor.meterInMercator;
|
|
2841
|
+
let point = null;
|
|
2842
|
+
let lngLat = null;
|
|
2843
|
+
let altitude = null;
|
|
2844
|
+
let centerLngLat = null;
|
|
2845
|
+
if (hitPoint) {
|
|
2846
|
+
point = [hitPoint.x, hitPoint.y, hitPoint.z];
|
|
2847
|
+
lngLat = this._lngLatFromWorld(hitPoint.x * s + anchor.x, -hitPoint.y * s + anchor.y);
|
|
2848
|
+
altitude = hitPoint.z + (anchor.altitude ?? 0);
|
|
2849
|
+
// 泼溅命中的就是一个高斯,没有「单体中心」可言,用命中点当中心
|
|
2850
|
+
centerLngLat = lngLat;
|
|
2851
|
+
}
|
|
2852
|
+
else if (box) {
|
|
2853
|
+
const v = new THREE.Vector3((box.minX + box.maxX) / 2, (box.minY + box.maxY) / 2, (box.minZ + box.maxZ) / 2).applyMatrix4(mesh.matrixWorld);
|
|
2854
|
+
centerLngLat = this._lngLatFromWorld(v.x * s + anchor.x, -v.y * s + anchor.y);
|
|
2855
|
+
altitude = v.z + (anchor.altitude ?? 0);
|
|
2856
|
+
}
|
|
2857
|
+
return {
|
|
2858
|
+
datasetId: ref.datasetId,
|
|
2859
|
+
datasetName: this.datasets.get(ref.datasetId)?.name ?? ref.datasetId,
|
|
2860
|
+
objectId: hit.objectId,
|
|
2861
|
+
batchId: -1,
|
|
2862
|
+
instanceIndex: undefined,
|
|
2863
|
+
skeletonName: ref.skeletonName,
|
|
2864
|
+
centerLngLat,
|
|
2865
|
+
point,
|
|
2866
|
+
lngLat,
|
|
2867
|
+
altitude,
|
|
2868
|
+
...attributes,
|
|
2869
|
+
mesh,
|
|
2870
|
+
};
|
|
2871
|
+
}
|
|
2687
2872
|
/** 按 canvas 左上角的 CSS 像素坐标拾取。 */
|
|
2688
2873
|
pick(x, y) {
|
|
2689
2874
|
if (!this.picker || !this.scene || !this.camera || !this.sceneAnchor)
|
|
@@ -3369,6 +3554,25 @@ export class S3MViewer {
|
|
|
3369
3554
|
}
|
|
3370
3555
|
this._requestRepaint();
|
|
3371
3556
|
}
|
|
3557
|
+
/**
|
|
3558
|
+
* 泼溅实例按视深 back-to-front 重排:alpha 混合是顺序相关的,
|
|
3559
|
+
* 乱序混合就是满屏彩色噪点。相机没动且没有新瓦片时整个跳过;
|
|
3560
|
+
* 单个瓦片顺序本来就对时 sortSplatMesh 内部也会跳过重写。
|
|
3561
|
+
*/
|
|
3562
|
+
_sortSplats() {
|
|
3563
|
+
const cam = this.camera;
|
|
3564
|
+
if (!cam)
|
|
3565
|
+
return;
|
|
3566
|
+
if (this._splatsSorted && this._lastSplatSortView.equals(cam.matrixWorldInverse))
|
|
3567
|
+
return;
|
|
3568
|
+
for (const pc of this._pointClouds) {
|
|
3569
|
+
if (!pc.userData.isGaussianSplat)
|
|
3570
|
+
continue;
|
|
3571
|
+
sortSplatMesh(pc, cam.matrixWorldInverse);
|
|
3572
|
+
}
|
|
3573
|
+
this._lastSplatSortView.copy(cam.matrixWorldInverse);
|
|
3574
|
+
this._splatsSorted = true;
|
|
3575
|
+
}
|
|
3372
3576
|
/**
|
|
3373
3577
|
* 渲染一帧。所有宿主相关量都由调用方喂进来,viewer 不碰任何地图 API:
|
|
3374
3578
|
*
|
|
@@ -3440,11 +3644,14 @@ export class S3MViewer {
|
|
|
3440
3644
|
this._updateLights();
|
|
3441
3645
|
// 等宽棱线按设备像素撑开,分辨率每帧同步一次
|
|
3442
3646
|
this._updateLineResolution(canvas);
|
|
3647
|
+
// 泼溅的最小像素半径 / 抗锯齿低通同样要把 ndc 换算成像素
|
|
3648
|
+
setSplatViewport(canvas.width, canvas.height);
|
|
3443
3649
|
this.renderer.setViewport(0, 0, canvas.width, canvas.height);
|
|
3444
3650
|
this.renderer.resetState();
|
|
3445
3651
|
// 完全透明时什么都不画:透明物体照样写深度,画了会在底图上挖出一个洞。
|
|
3446
3652
|
// 这里跳过的是「本层的绘制」,宿主自己的清屏 / 底图照旧。
|
|
3447
3653
|
if ((this.uniforms.uOpacity?.value ?? 1) > OPACITY_EPS) {
|
|
3654
|
+
this._sortSplats();
|
|
3448
3655
|
this.renderer.render(this.scene, this.camera);
|
|
3449
3656
|
// GPU 描边必须画在主场景之后:它的全屏 quad 参与宿主深度测试,
|
|
3450
3657
|
// 依赖本帧已经写好的深度(被地形挡住的地方不该描边)
|
|
@@ -3488,7 +3695,9 @@ export class S3MViewer {
|
|
|
3488
3695
|
programs: 0,
|
|
3489
3696
|
textures: 0,
|
|
3490
3697
|
geometries: 0,
|
|
3698
|
+
degradations: [],
|
|
3491
3699
|
};
|
|
3700
|
+
let degradations = null;
|
|
3492
3701
|
for (const entry of this.datasets.values()) {
|
|
3493
3702
|
const s = entry.tileset?.stats;
|
|
3494
3703
|
if (!s)
|
|
@@ -3499,7 +3708,17 @@ export class S3MViewer {
|
|
|
3499
3708
|
total.meshes += s.meshes;
|
|
3500
3709
|
total.vertices += s.vertices;
|
|
3501
3710
|
total.triangles += s.triangles;
|
|
3711
|
+
// 降级项跨数据集去重:同一类只报一次给 UI
|
|
3712
|
+
if (s.degradations?.length) {
|
|
3713
|
+
degradations ??= [];
|
|
3714
|
+
for (const d of s.degradations) {
|
|
3715
|
+
if (!degradations.includes(d))
|
|
3716
|
+
degradations.push(d);
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3502
3719
|
}
|
|
3720
|
+
if (degradations)
|
|
3721
|
+
total.degradations = degradations;
|
|
3503
3722
|
// GPU 侧预算观测:draw call / program / 显存驻留。没有渲染器(未 mount /
|
|
3504
3723
|
// 纯解析场景)时保持 0,不抛错。
|
|
3505
3724
|
const info = this.renderer?.info;
|