s3m-three 0.3.10 → 0.3.12
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/s3m-three.iife.ext-three.js +102 -20
- package/dist/browser/s3m-three.iife.js +102 -20
- package/dist/render/EdgeLines.d.ts.map +1 -1
- package/dist/render/EdgeLines.js +5 -2
- package/dist/render/EdgeLines.js.map +1 -1
- package/dist/render/Picker.d.ts.map +1 -1
- package/dist/render/Picker.js +109 -12
- package/dist/render/Picker.js.map +1 -1
- package/dist/render/S3MViewer.d.ts +35 -0
- package/dist/render/S3MViewer.d.ts.map +1 -1
- package/dist/render/S3MViewer.js +175 -14
- package/dist/render/S3MViewer.js.map +1 -1
- package/dist/render/StyleMaterial.d.ts.map +1 -1
- package/dist/render/StyleMaterial.js +18 -4
- package/dist/render/StyleMaterial.js.map +1 -1
- package/docs/ANIMATION.md +17 -7
- package/docs/API.md +360 -6
- package/package.json +1 -1
- package/src/render/EdgeLines.ts +5 -2
- package/src/render/Picker.ts +111 -12
- package/src/render/S3MViewer.ts +182 -14
- package/src/render/StyleMaterial.ts +18 -4
package/dist/render/S3MViewer.js
CHANGED
|
@@ -49,6 +49,19 @@ function styledMaterialOf(mesh) {
|
|
|
49
49
|
function isPointCloudObject(o) {
|
|
50
50
|
return o.isPoints === true;
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* 全局透明度钳到 [0, 1],**允许到 0**。
|
|
54
|
+
*
|
|
55
|
+
* 以前这里有个 0.05 下限,本意是「别让整层彻底看不见、又还在挡东西」。但那个问题
|
|
56
|
+
* 真正的来源不是透明度,而是「透明物体仍然写深度」:全透明还写深度就会在底图上挖洞。
|
|
57
|
+
* 现在到 0 时 `renderFrame()` 直接跳过绘制(不写颜色也不写深度),语义就干净了。
|
|
58
|
+
*/
|
|
59
|
+
function clampOpacity(v) {
|
|
60
|
+
const n = v ?? 1;
|
|
61
|
+
return n < 0 ? 0 : n > 1 ? 1 : n;
|
|
62
|
+
}
|
|
63
|
+
/** 低于这个透明度就当作完全不可见:跳过绘制,避免全透明物体写深度 */
|
|
64
|
+
const OPACITY_EPS = 1e-3;
|
|
52
65
|
/* ------------------------------------------------------------------ 可动画的样式键 */
|
|
53
66
|
/**
|
|
54
67
|
* 能逐帧插值的标量样式键。
|
|
@@ -147,10 +160,18 @@ export class S3MViewer {
|
|
|
147
160
|
_tileFadeSeq = 0;
|
|
148
161
|
/** setMode 编排的代次,用来作废被新调用顶掉的旧编排 */
|
|
149
162
|
_modeGen = 0;
|
|
163
|
+
/** `shading` 另一种变体是否已预热(每个 viewer 只做一次) */
|
|
164
|
+
_shadingPrewarmed = false;
|
|
165
|
+
/** 是否允许自动预热(选项 `prewarmShading`,默认开) */
|
|
166
|
+
_prewarmShadingEnabled = true;
|
|
167
|
+
/** 预热进行中:期间跳过渲染,避免把「翻转后的变体」画到屏幕上 */
|
|
168
|
+
_prewarming = false;
|
|
150
169
|
/** 路径运动的规格表:通道 key → 路径 + 偏移 */
|
|
151
170
|
_pathSpecs = new Map();
|
|
152
171
|
/** 路径采样临时对象,避免每帧 new */
|
|
153
172
|
_pathScratch = new Float32Array(3);
|
|
173
|
+
/** 求切线用的第二个采样点(包围盒换算 GPU 路径朝向时用) */
|
|
174
|
+
_pathScratch2 = new Float32Array(3);
|
|
154
175
|
/** 预制棱线缓存(Crease Cache):从外部 source-creases 载入的全局统一 LineSegments,性能最高、视觉最干净 */
|
|
155
176
|
_creaseLines = null;
|
|
156
177
|
/** 数据自带的棱线对象(LineSegments),改棱线颜色时只刷这些,不再全场景 traverse */
|
|
@@ -220,6 +241,7 @@ export class S3MViewer {
|
|
|
220
241
|
this.onReady = options.onReady ?? (() => { });
|
|
221
242
|
this.onPick = options.onPick ?? (() => { });
|
|
222
243
|
this.onError = options.onError ?? ((e) => console.warn('[s3m]', e));
|
|
244
|
+
this._prewarmShadingEnabled = options.prewarmShading !== false;
|
|
223
245
|
this.groundManager = new GroundManager(options.ground);
|
|
224
246
|
if (!this.scene) {
|
|
225
247
|
this.scene = new THREE.Scene();
|
|
@@ -264,6 +286,8 @@ export class S3MViewer {
|
|
|
264
286
|
// `style: { edges: true }` 形同虚设、_edgesEnabled 永远是 false。
|
|
265
287
|
// 这里按 style 实际值调一次 setEdges,让开关真正生效(与 setEdges 后来的手工调用一致)。
|
|
266
288
|
this.setEdges(!!this.style.edges);
|
|
289
|
+
// 初始化图层整体不透明度:确保共享 uniform uOpacity、材质混合开关与棱线外观与初始 style.opacity 严格一致
|
|
290
|
+
this._applyOpacity();
|
|
267
291
|
this._fps.last = performance.now();
|
|
268
292
|
this._datasetQueue = this._normalizeDatasets(options);
|
|
269
293
|
if (options.attributes)
|
|
@@ -329,7 +353,7 @@ export class S3MViewer {
|
|
|
329
353
|
}
|
|
330
354
|
try {
|
|
331
355
|
// 旧上下文已失效,dispose 可能抛错,忽略即可
|
|
332
|
-
this.renderer?.dispose();
|
|
356
|
+
this.renderer?.dispose?.();
|
|
333
357
|
}
|
|
334
358
|
catch {
|
|
335
359
|
/* noop */
|
|
@@ -369,7 +393,7 @@ export class S3MViewer {
|
|
|
369
393
|
this._pathSpecs.clear();
|
|
370
394
|
this._heightMin = Infinity;
|
|
371
395
|
this._heightMax = -Infinity;
|
|
372
|
-
this.renderer?.dispose();
|
|
396
|
+
this.renderer?.dispose?.();
|
|
373
397
|
this.renderer = null;
|
|
374
398
|
this.canvas = null;
|
|
375
399
|
}
|
|
@@ -774,6 +798,54 @@ export class S3MViewer {
|
|
|
774
798
|
this.animator.unregister(key);
|
|
775
799
|
this._pathSpecs.delete(key);
|
|
776
800
|
}
|
|
801
|
+
/**
|
|
802
|
+
* 换算 GPU 路径单体在**当前时间**的位置与朝向。
|
|
803
|
+
*
|
|
804
|
+
* 位移发生在顶点着色器里,JS 侧要拿到「屏幕上现在在哪」只能按同一套公式在 CPU 复算:
|
|
805
|
+
* `t = phase + uPathTime * speed`,再按 loop 模式夹到 0~1,最后查弧长表。
|
|
806
|
+
* 与 StyleMaterial 的 row5 分支逐行对应,改那边时这里必须同步。
|
|
807
|
+
*/
|
|
808
|
+
_gpuPathPoseAt(smid) {
|
|
809
|
+
const gp = this._featureStyleManager.getFeatureStyle(smid)?.gpuPath;
|
|
810
|
+
if (!gp)
|
|
811
|
+
return null;
|
|
812
|
+
const path = this.gpuPathManager.getPath(gp.pathId);
|
|
813
|
+
if (!path || path.length <= 0)
|
|
814
|
+
return null;
|
|
815
|
+
const time = this.uniforms.uPathTime?.value ?? 0;
|
|
816
|
+
let t = (gp.phase ?? 0) + time * (gp.speed ?? 0);
|
|
817
|
+
const loop = gp.loop;
|
|
818
|
+
if (loop === 'clamp' || loop === false) {
|
|
819
|
+
t = Math.min(1, Math.max(0, t));
|
|
820
|
+
}
|
|
821
|
+
else if (loop === 'pingpong') {
|
|
822
|
+
t = t % 2;
|
|
823
|
+
if (t < 0)
|
|
824
|
+
t += 2;
|
|
825
|
+
t = t > 1 ? 2 - t : t;
|
|
826
|
+
}
|
|
827
|
+
else {
|
|
828
|
+
t = t - Math.floor(t);
|
|
829
|
+
}
|
|
830
|
+
path.sampleAt(t, this._pathScratch);
|
|
831
|
+
const pos = [this._pathScratch[0], this._pathScratch[1], this._pathScratch[2]];
|
|
832
|
+
let yaw = 0;
|
|
833
|
+
if (gp.autoHeading !== false) {
|
|
834
|
+
const d = 1e-3;
|
|
835
|
+
path.sampleAt(Math.min(1, t + d), this._pathScratch2);
|
|
836
|
+
let dx = this._pathScratch2[0] - pos[0];
|
|
837
|
+
let dy = this._pathScratch2[1] - pos[1];
|
|
838
|
+
if (dx * dx + dy * dy < 1e-8) {
|
|
839
|
+
// 已经在终点:退回用「后一点减前一点」求切线
|
|
840
|
+
path.sampleAt(Math.max(0, t - d), this._pathScratch2);
|
|
841
|
+
dx = pos[0] - this._pathScratch2[0];
|
|
842
|
+
dy = pos[1] - this._pathScratch2[1];
|
|
843
|
+
}
|
|
844
|
+
if (dx * dx + dy * dy >= 1e-8)
|
|
845
|
+
yaw = Math.atan2(dy, dx);
|
|
846
|
+
}
|
|
847
|
+
return { pos, yaw };
|
|
848
|
+
}
|
|
777
849
|
/**
|
|
778
850
|
* 注册 GPU 路径到路径库
|
|
779
851
|
* @param points 折线点数组或 Path 实例
|
|
@@ -1262,10 +1334,11 @@ export class S3MViewer {
|
|
|
1262
1334
|
else {
|
|
1263
1335
|
const mat = styledMaterialOf(mesh);
|
|
1264
1336
|
if (mat) {
|
|
1265
|
-
const o =
|
|
1337
|
+
const o = clampOpacity(this.style.opacity);
|
|
1266
1338
|
// 不写 mat.opacity:全局透明度走共享 uOpacity,材质自身 opacity 保留模型自带 alpha
|
|
1267
1339
|
mat.transparent = o < 0.999;
|
|
1268
|
-
|
|
1340
|
+
const keepDepth = this.style.depthWrite !== false;
|
|
1341
|
+
mat.depthWrite = mat.transparent ? keepDepth : (this.style.depthWrite !== false);
|
|
1269
1342
|
mat.flatShading = this.style.shading !== 'smooth';
|
|
1270
1343
|
mat.needsUpdate = true;
|
|
1271
1344
|
this._featureStyleManager.applyOverridesToMesh(mesh);
|
|
@@ -1318,8 +1391,7 @@ export class S3MViewer {
|
|
|
1318
1391
|
// 淡入期间强制开了 transparent + depthWrite,结束时不能自己拍板关掉——
|
|
1319
1392
|
// 图层可能本来就半透明、单体也可能还有淡化。交给淡化流程重新裁决一次。
|
|
1320
1393
|
this._featureStyleManager.applyOverridesToMesh(mesh);
|
|
1321
|
-
|
|
1322
|
-
mat.depthWrite = true;
|
|
1394
|
+
mat.depthWrite = this.style.depthWrite !== false;
|
|
1323
1395
|
this._requestRepaint();
|
|
1324
1396
|
},
|
|
1325
1397
|
});
|
|
@@ -1689,6 +1761,9 @@ export class S3MViewer {
|
|
|
1689
1761
|
if (u.uAmbientGain && s.ambientGain != null) {
|
|
1690
1762
|
u.uAmbientGain.value = s.ambientGain;
|
|
1691
1763
|
}
|
|
1764
|
+
if (u.uOpacity && s.opacity != null) {
|
|
1765
|
+
u.uOpacity.value = clampOpacity(s.opacity);
|
|
1766
|
+
}
|
|
1692
1767
|
if (this.outlineMaterial) {
|
|
1693
1768
|
if (this.outlineMaterial.uniforms.uElevation && s.selectElevation != null) {
|
|
1694
1769
|
this.outlineMaterial.uniforms.uElevation.value = s.selectElevation;
|
|
@@ -1713,7 +1788,7 @@ export class S3MViewer {
|
|
|
1713
1788
|
* 现在逐帧只写一个 float(`uOpacity`),混合开关只在**真正跨过阈值**时切一次。
|
|
1714
1789
|
*/
|
|
1715
1790
|
_applyOpacity() {
|
|
1716
|
-
const o =
|
|
1791
|
+
const o = clampOpacity(this.style.opacity);
|
|
1717
1792
|
if (this.uniforms.uOpacity)
|
|
1718
1793
|
this.uniforms.uOpacity.value = o;
|
|
1719
1794
|
const transparent = o < 0.999;
|
|
@@ -1764,6 +1839,62 @@ export class S3MViewer {
|
|
|
1764
1839
|
}
|
|
1765
1840
|
this._requestRepaint();
|
|
1766
1841
|
}
|
|
1842
|
+
/**
|
|
1843
|
+
* 预热另一种 `shading` 变体,让首次切换不再同步编译着色器。
|
|
1844
|
+
*
|
|
1845
|
+
* 做法是「把材质翻到另一种变体 → 编译 → 翻回来」:three 的 program 缓存按
|
|
1846
|
+
* cacheKey(含 flatShading / defines)分桶,编译一次就留在缓存里,之后
|
|
1847
|
+
* `_applyShading()` 切过去只是取缓存。
|
|
1848
|
+
*
|
|
1849
|
+
* 翻转期间材质是「错误变体」,所以用 `_prewarming` 让 renderFrame 跳过渲染——
|
|
1850
|
+
* 少画一两帧看不出来,把平滑的楼画成平面的就很明显。
|
|
1851
|
+
* 优先用 `compileAsync`(KHR_parallel_shader_compile),没有就退回同步 `compile`。
|
|
1852
|
+
*/
|
|
1853
|
+
async prewarmShading() {
|
|
1854
|
+
if (this._shadingPrewarmed || this._prewarming)
|
|
1855
|
+
return;
|
|
1856
|
+
const r = this.renderer;
|
|
1857
|
+
const scene = this.scene;
|
|
1858
|
+
const camera = this.camera;
|
|
1859
|
+
if (!r || !scene || !camera)
|
|
1860
|
+
return;
|
|
1861
|
+
const mats = [];
|
|
1862
|
+
const seen = new Set();
|
|
1863
|
+
for (const p of this._pickables.values()) {
|
|
1864
|
+
const m = styledMaterialOf(p.mesh);
|
|
1865
|
+
if (!m || seen.has(m))
|
|
1866
|
+
continue;
|
|
1867
|
+
seen.add(m);
|
|
1868
|
+
mats.push(m);
|
|
1869
|
+
}
|
|
1870
|
+
if (mats.length === 0)
|
|
1871
|
+
return;
|
|
1872
|
+
this._shadingPrewarmed = true;
|
|
1873
|
+
this._prewarming = true;
|
|
1874
|
+
// 当前是 smooth(flatShading = false)就预热 flat,反之预热 smooth
|
|
1875
|
+
const target = this.style.shading === 'smooth';
|
|
1876
|
+
const saved = mats.map((m) => m.flatShading);
|
|
1877
|
+
for (const m of mats)
|
|
1878
|
+
m.flatShading = target;
|
|
1879
|
+
try {
|
|
1880
|
+
const anyR = r;
|
|
1881
|
+
if (typeof anyR.compileAsync === 'function')
|
|
1882
|
+
await anyR.compileAsync(scene, camera);
|
|
1883
|
+
else
|
|
1884
|
+
r.compile(scene, camera);
|
|
1885
|
+
}
|
|
1886
|
+
catch {
|
|
1887
|
+
// 预热失败不影响主流程:最坏情况退回「首次切换时编译一次」
|
|
1888
|
+
}
|
|
1889
|
+
finally {
|
|
1890
|
+
for (let i = 0; i < mats.length; i++) {
|
|
1891
|
+
mats[i].flatShading = saved[i];
|
|
1892
|
+
mats[i].needsUpdate = true;
|
|
1893
|
+
}
|
|
1894
|
+
this._prewarming = false;
|
|
1895
|
+
this._requestRepaint();
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1767
1898
|
/**
|
|
1768
1899
|
* 高度范围增量维护。
|
|
1769
1900
|
*
|
|
@@ -1971,7 +2102,7 @@ export class S3MViewer {
|
|
|
1971
2102
|
const mat = new THREE.LineBasicMaterial({
|
|
1972
2103
|
color: new THREE.Color(s.edgeColor),
|
|
1973
2104
|
transparent: true,
|
|
1974
|
-
opacity: Math.min(1, Math.max(0, s.edgeOpacity ?? 0.6)) *
|
|
2105
|
+
opacity: Math.min(1, Math.max(0, s.edgeOpacity ?? 0.6)) * clampOpacity(s.opacity),
|
|
1975
2106
|
depthWrite: false,
|
|
1976
2107
|
});
|
|
1977
2108
|
const lines = new THREE.LineSegments(geo, mat);
|
|
@@ -2003,7 +2134,7 @@ export class S3MViewer {
|
|
|
2003
2134
|
const s = this.style;
|
|
2004
2135
|
const fade = this.uniforms.uEdgeFade?.value ?? 1;
|
|
2005
2136
|
const opacity = Math.min(1, Math.max(0, s.edgeOpacity ?? 0.6)) *
|
|
2006
|
-
|
|
2137
|
+
clampOpacity(s.opacity) *
|
|
2007
2138
|
fade;
|
|
2008
2139
|
const u = this.edgeMaterial.uniforms;
|
|
2009
2140
|
u.uColor.value.set(s.edgeColor);
|
|
@@ -2025,7 +2156,7 @@ export class S3MViewer {
|
|
|
2025
2156
|
const baseOpacity = this.style.edgeOpacity ?? 0.6;
|
|
2026
2157
|
mat.opacity =
|
|
2027
2158
|
baseOpacity *
|
|
2028
|
-
|
|
2159
|
+
clampOpacity(this.style.opacity) *
|
|
2029
2160
|
(this.uniforms.uEdgeFade?.value ?? 1);
|
|
2030
2161
|
mat.transparent = true;
|
|
2031
2162
|
mat.polygonOffset = true;
|
|
@@ -2560,13 +2691,30 @@ export class S3MViewer {
|
|
|
2560
2691
|
rotM3 = new THREE.Matrix3().set(rotMat[0], rotMat[1], rotMat[2], rotMat[3], rotMat[4], rotMat[5], rotMat[6], rotMat[7], rotMat[8]);
|
|
2561
2692
|
rotPiv = off.pivot ? new THREE.Vector3(off.pivot[0], off.pivot[1], off.pivot[2]) : new THREE.Vector3(0, 0, 0);
|
|
2562
2693
|
}
|
|
2694
|
+
// GPU 路径:位移发生在顶点着色器里,包围盒要按同一套公式在 CPU 复算,
|
|
2695
|
+
// 否则「运动中的车」的包围盒会停在路径原点(相机 fitBounds / 属性过滤都会错)。
|
|
2696
|
+
const gpuPose = smid != null ? this._gpuPathPoseAt(smid) : null;
|
|
2697
|
+
let headM3 = null;
|
|
2698
|
+
if (gpuPose && gpuPose.yaw !== 0) {
|
|
2699
|
+
const c = Math.cos(gpuPose.yaw);
|
|
2700
|
+
const s = Math.sin(gpuPose.yaw);
|
|
2701
|
+
// 与着色器的 mat3( c, s, 0, -s, c, 0, 0, 0, 1 ) 等价:GLSL 是列优先,
|
|
2702
|
+
// three 的 Matrix3.set 是行优先,所以这里写成 (c, -s; s, c) 才是同一个矩阵。
|
|
2703
|
+
headM3 = new THREE.Matrix3().set(c, -s, 0, s, c, 0, 0, 0, 1);
|
|
2704
|
+
}
|
|
2563
2705
|
for (const c of corners) {
|
|
2564
2706
|
if (rotM3 && rotPiv)
|
|
2565
2707
|
c.sub(rotPiv).applyMatrix3(rotM3).add(rotPiv);
|
|
2708
|
+
if (headM3) {
|
|
2709
|
+
const piv = rotPiv ?? new THREE.Vector3(0, 0, 0);
|
|
2710
|
+
c.sub(piv).applyMatrix3(headM3).add(piv);
|
|
2711
|
+
}
|
|
2566
2712
|
if (trans)
|
|
2567
2713
|
c.add(new THREE.Vector3(trans[0], trans[1], trans[2]));
|
|
2568
2714
|
else if (elev)
|
|
2569
2715
|
c.z += elev;
|
|
2716
|
+
if (gpuPose)
|
|
2717
|
+
c.add(new THREE.Vector3(gpuPose.pos[0], gpuPose.pos[1], gpuPose.pos[2]));
|
|
2570
2718
|
c.applyMatrix4(mesh.matrixWorld);
|
|
2571
2719
|
const s = this.sceneAnchor.meterInMercator;
|
|
2572
2720
|
const [lng, lat] = this._lngLatFromWorld(c.x * s + this.sceneAnchor.x, -c.y * s + this.sceneAnchor.y);
|
|
@@ -2804,7 +2952,7 @@ export class S3MViewer {
|
|
|
2804
2952
|
const u = this.selectionEdgeMaterial.uniforms;
|
|
2805
2953
|
u.uColor.value.set(s.outlineColor ?? s.selectColor);
|
|
2806
2954
|
u.uWidth.value = Math.max(0.1, s.outlineWidthPx ?? 3);
|
|
2807
|
-
u.uOpacity.value =
|
|
2955
|
+
u.uOpacity.value = clampOpacity(s.opacity);
|
|
2808
2956
|
// 只加全局 selectElevation:单体自己的拔高已经写在状态纹理第 2 行 z 里,
|
|
2809
2957
|
// 顶点着色器采样时带出来了,这里再加一次就会双重拔高
|
|
2810
2958
|
if (u.uElevation)
|
|
@@ -2894,6 +3042,10 @@ export class S3MViewer {
|
|
|
2894
3042
|
const gl = this.renderer.getContext();
|
|
2895
3043
|
if (gl && typeof gl.isContextLost === 'function' && gl.isContextLost())
|
|
2896
3044
|
return false;
|
|
3045
|
+
// shading 预热期间材质是「另一种变体」,这一帧先不画:少画一两帧看不出来,
|
|
3046
|
+
// 把平滑的楼画成平面的一眼就能看出来。
|
|
3047
|
+
if (this._prewarming)
|
|
3048
|
+
return true;
|
|
2897
3049
|
// 先推进动画,再刷脏标记,最后渲染 —— 保证本帧画出来的是最新值
|
|
2898
3050
|
const animating = this.animator.tick();
|
|
2899
3051
|
// 脏标记正常由微任务刷掉;这里再兜一次底,保证「本帧画出来的」一定是最新样式
|
|
@@ -2944,7 +3096,11 @@ export class S3MViewer {
|
|
|
2944
3096
|
this._updateLineResolution(canvas);
|
|
2945
3097
|
this.renderer.setViewport(0, 0, canvas.width, canvas.height);
|
|
2946
3098
|
this.renderer.resetState();
|
|
2947
|
-
|
|
3099
|
+
// 完全透明时什么都不画:透明物体照样写深度,画了会在底图上挖出一个洞。
|
|
3100
|
+
// 这里跳过的是「本层的绘制」,宿主自己的清屏 / 底图照旧。
|
|
3101
|
+
if ((this.uniforms.uOpacity?.value ?? 1) > OPACITY_EPS) {
|
|
3102
|
+
this.renderer.render(this.scene, this.camera);
|
|
3103
|
+
}
|
|
2948
3104
|
this._frame++;
|
|
2949
3105
|
const now = performance.now();
|
|
2950
3106
|
if (now - this._fps.last > 500) {
|
|
@@ -2954,11 +3110,16 @@ export class S3MViewer {
|
|
|
2954
3110
|
}
|
|
2955
3111
|
// 动态着色 / 动画进行中 / 瓦片加载中 / 瓦片层级或可见性改变,都需要宿主继续驱动下一帧。
|
|
2956
3112
|
// 动画结束那一帧必须精确回到 false,否则 MapLibre 会永远重绘(耗电、发热)。
|
|
2957
|
-
|
|
3113
|
+
const needsNextFrame = animating ||
|
|
2958
3114
|
this.style.mode === STYLE.DYNAMIC ||
|
|
2959
3115
|
gpuPathActive ||
|
|
2960
3116
|
this.stats.loading > 0 ||
|
|
2961
|
-
tilesetChanged
|
|
3117
|
+
tilesetChanged;
|
|
3118
|
+
// 首帧画完、场上有材质了,再把另一种 shading 变体编出来(异步,不挡当前帧)
|
|
3119
|
+
if (this._prewarmShadingEnabled && !this._shadingPrewarmed && this._pickables.size > 0) {
|
|
3120
|
+
void this.prewarmShading();
|
|
3121
|
+
}
|
|
3122
|
+
return needsNextFrame;
|
|
2962
3123
|
}
|
|
2963
3124
|
/* ---------------------------------------------------------------- 只读属性 */
|
|
2964
3125
|
get fps() {
|