s3m-three 0.4.20 → 0.4.22

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.
Files changed (33) hide show
  1. package/dist/browser/s3m-three.iife.ext-three.js +30 -30
  2. package/dist/browser/s3m-three.iife.js +30 -30
  3. package/dist/render/DatasetManager.d.ts +20 -1
  4. package/dist/render/DatasetManager.d.ts.map +1 -1
  5. package/dist/render/DatasetManager.js +23 -1
  6. package/dist/render/DatasetManager.js.map +1 -1
  7. package/dist/render/GeometryBuilder.d.ts +8 -0
  8. package/dist/render/GeometryBuilder.d.ts.map +1 -1
  9. package/dist/render/GeometryBuilder.js +25 -0
  10. package/dist/render/GeometryBuilder.js.map +1 -1
  11. package/dist/render/S3MViewer.d.ts +23 -6
  12. package/dist/render/S3MViewer.d.ts.map +1 -1
  13. package/dist/render/S3MViewer.js +53 -12
  14. package/dist/render/S3MViewer.js.map +1 -1
  15. package/dist/render/StyleMaterial.d.ts +13 -1
  16. package/dist/render/StyleMaterial.d.ts.map +1 -1
  17. package/dist/render/StyleMaterial.js +26 -4
  18. package/dist/render/StyleMaterial.js.map +1 -1
  19. package/dist/render/viewer/types.d.ts +10 -2
  20. package/dist/render/viewer/types.d.ts.map +1 -1
  21. package/dist/render/viewer/types.js.map +1 -1
  22. package/dist/tileset/S3MTileset.d.ts +44 -2
  23. package/dist/tileset/S3MTileset.d.ts.map +1 -1
  24. package/dist/tileset/S3MTileset.js +110 -4
  25. package/dist/tileset/S3MTileset.js.map +1 -1
  26. package/docs/API.md +11 -30
  27. package/package.json +1 -1
  28. package/src/render/DatasetManager.ts +31 -1
  29. package/src/render/GeometryBuilder.ts +30 -0
  30. package/src/render/S3MViewer.ts +53 -12
  31. package/src/render/StyleMaterial.ts +32 -4
  32. package/src/render/viewer/types.ts +10 -2
  33. package/src/tileset/S3MTileset.ts +125 -5
@@ -233,7 +233,7 @@ export interface TileNode {
233
233
  /** 该瓦片引用的纹理 key 清单,用于引用计数与显存释放 */
234
234
  textureKeys?: string[];
235
235
  /** 瓦片构建产生的统计信息,用于淘汰时准确扣减 stats */
236
- stats?: { meshes: number; vertices: number; triangles: number };
236
+ stats?: { meshes: number; vertices: number; triangles: number; memory?: number };
237
237
  }
238
238
 
239
239
  /** 瓦片构建器可注入,用于测试 / 服务端渲染(此时不需要 three) */
@@ -246,6 +246,8 @@ export type BuildTileFn = (
246
246
  assembled?: TileGeometryData;
247
247
  /** 深度偏移配置(透传给 buildTileObjects),见 BuildTileOptions.polygonOffset */
248
248
  polygonOffset?: { factor: number; units: number };
249
+ /** 光照模型档位(透传给 buildTileObjects),见 BuildTileOptions.materialQuality */
250
+ materialQuality?: 'pbr' | 'lambert';
249
251
  }
250
252
  ) => Promise<{ group: Group; stats: Partial<Stats> }>;
251
253
 
@@ -258,6 +260,11 @@ export interface S3MTilesetOptions {
258
260
  buildTile?: BuildTileFn | null;
259
261
  /** 三角面材质深度偏移(共面摩尔纹 / 重合面归属),透传给 buildTileObjects */
260
262
  polygonOffset?: { factor: number; units: number };
263
+ /**
264
+ * 光照模型档位,透传给 buildTileObjects。不传 = 读模块级默认。
265
+ * 由 DatasetManager 从图层 / 数据集设置带下来。
266
+ */
267
+ materialQuality?: 'pbr' | 'lambert';
261
268
  /**
262
269
  * 是否用 Web Worker 解析经典 `.s3mb` 瓦片(默认开)。
263
270
  *
@@ -287,6 +294,14 @@ export interface S3MTilesetOptions {
287
294
  * 不传 = 不限制,行为与旧版完全一致。运行时可用 `setMaxTriangles()` 调整。
288
295
  */
289
296
  maxTriangles?: number;
297
+ /**
298
+ * 本数据集**驻留显存/内存字节数**的软上限(默认不限制)。
299
+ * 包含几何顶点缓冲(精确字节)与纹理显存估计。超限时按同一套 make-before-break
300
+ * 主动淘汰非显示瓦片及其独占纹理。
301
+ *
302
+ * 不传 = 不限制(Infinity),行为与旧版完全一致。运行时可用 `setMaxMemory()` 调整。
303
+ */
304
+ maxMemory?: number;
290
305
  /**
291
306
  * 是否请求 `.texblock` 子图集(S3MBLOCK 根图集的清晰版)。**默认关**。
292
307
  *
@@ -315,6 +330,54 @@ export interface S3MTilesetOptions {
315
330
  unsupported?: UnsupportedPolicy;
316
331
  }
317
332
 
333
+ /** 估算单个纹理占用的显存字节数 */
334
+ export function estimateTextureBytes(tex: any): number {
335
+ if (!tex) return 0;
336
+ if (Array.isArray(tex.mipmaps) && tex.mipmaps.length > 0) {
337
+ let bytes = 0;
338
+ for (const m of tex.mipmaps) {
339
+ if (m?.data?.byteLength) bytes += m.data.byteLength;
340
+ }
341
+ if (bytes > 0) return bytes;
342
+ }
343
+ const img = tex.image;
344
+ if (!img) return 0;
345
+ if (img.data && typeof img.data.byteLength === 'number') {
346
+ return img.data.byteLength;
347
+ }
348
+ const w = img.width ?? 0;
349
+ const h = img.height ?? 0;
350
+ if (w > 0 && h > 0) {
351
+ const factor = tex.generateMipmaps ? 1.3333333333333333 : 1;
352
+ return Math.round(w * h * 4 * factor);
353
+ }
354
+ return 0;
355
+ }
356
+
357
+ /** 估算三维对象树中包含的几何缓冲字节数 */
358
+ export function estimateObjectGeometryBytes(obj: any): number {
359
+ let bytes = 0;
360
+ if (!obj) return 0;
361
+ obj.traverse?.((child: any) => {
362
+ const geo = child.geometry;
363
+ if (geo) {
364
+ if (geo.attributes) {
365
+ for (const attr of Object.values(geo.attributes) as any[]) {
366
+ if (attr?.array?.byteLength) bytes += attr.array.byteLength;
367
+ }
368
+ }
369
+ if (geo.index?.array?.byteLength) {
370
+ bytes += geo.index.array.byteLength;
371
+ }
372
+ }
373
+ if (child.isInstancedMesh) {
374
+ if (child.instanceMatrix?.array?.byteLength) bytes += child.instanceMatrix.array.byteLength;
375
+ if (child.instanceColor?.array?.byteLength) bytes += child.instanceColor.array.byteLength;
376
+ }
377
+ });
378
+ return bytes;
379
+ }
380
+
318
381
  export class S3MTileset {
319
382
  configUrl: string;
320
383
  config: Record<string, unknown> | null = null;
@@ -377,6 +440,10 @@ export class S3MTileset {
377
440
  * 判据与旧版逐字一致,不会多算一次面数。
378
441
  */
379
442
  maxTriangles: number;
443
+ /**
444
+ * 驻留显存/内存字节数的软上限。`Infinity` = 不限制(默认)。
445
+ */
446
+ maxMemory: number;
380
447
  lodRangeScale: number;
381
448
 
382
449
  /** 数据集配置声明的高度范围(米),若配置未声明则返回 null */
@@ -411,6 +478,7 @@ export class S3MTileset {
411
478
  private isS3MV1 = false;
412
479
  private _buildTile: BuildTileFn | null;
413
480
  private readonly _polygonOffset: { factor: number; units: number } | undefined;
481
+ private _materialQuality: 'pbr' | 'lambert' | undefined;
414
482
  private readonly _uniforms: StyleUniforms | null;
415
483
  /** 经典 .s3mb 的 Worker 解析客户端;undefined = 尚未尝试创建,null = 不可用/已降级 */
416
484
  private _workerClient: S3MWorkerClient | null | undefined; private readonly _useWorker: boolean;
@@ -431,6 +499,7 @@ export class S3MTileset {
431
499
  // 渲染层可注入:Node 环境下不需要 three,也能跑 URL / 解析链路
432
500
  this._buildTile = options.buildTile ?? null;
433
501
  this._polygonOffset = options.polygonOffset;
502
+ this._materialQuality = options.materialQuality;
434
503
  // 着色器共享 uniform,由 S3MLayer 持有并传进来
435
504
  this._uniforms = options.uniforms ?? null;
436
505
  this._useWorker = options.useWorker !== false;
@@ -439,6 +508,9 @@ export class S3MTileset {
439
508
  this.maxConcurrent = options.maxConcurrent ?? 4;
440
509
  this.maxTiles = options.maxTiles ?? 64;
441
510
  this.maxTriangles = options.maxTriangles ?? Infinity;
511
+ this.maxMemory = Number.isFinite(options.maxMemory) && (options.maxMemory as number) > 0
512
+ ? (options.maxMemory as number)
513
+ : Infinity;
442
514
  this.maxImageCache = options.maxImageCache ?? 128;
443
515
  // 硬上限 = 软上限 + 余量。淘汰只在「新瓦片已经落地并接管显示」之后做(make-before-break),
444
516
  // 所以中间态允许临时超出 maxTiles;硬上限只用来挡住失控的请求量,不用于挤掉正在显示的内容。
@@ -651,14 +723,58 @@ export class S3MTileset {
651
723
  this.maxTriangles = n > 0 ? n : Infinity;
652
724
  }
653
725
 
726
+ /**
727
+ * 运行时调整显存/内存字节预算。`Infinity` = 不限制(默认)。
728
+ * 收紧后不会立刻掉内容,要等下一次 `_trimBudget`(下一帧 update)才生效。
729
+ */
730
+ setMaxMemory(bytes: number): void {
731
+ this.maxMemory = Number.isFinite(bytes) && bytes > 0 ? bytes : Infinity;
732
+ }
733
+
734
+ /**
735
+ * 运行时改本数据集的光照模型档位。只影响**之后**构建的瓦片材质;
736
+ * 场上已有材质由 `S3MViewer.setMaterialQuality` 原位重建。
737
+ * `undefined` = 跟随模块级默认。
738
+ */
739
+ setMaterialQuality(q: 'pbr' | 'lambert' | undefined): void {
740
+ this._materialQuality = q;
741
+ }
742
+
743
+ /**
744
+ * 当前数据集驻留的显存与内存总字节数(几何缓冲精确大小 + 纹理显存估计)
745
+ */
746
+ residentMemory(): number {
747
+ return this._residentGeometryBytes() + this._residentTextureBytes();
748
+ }
749
+
750
+ private _residentGeometryBytes(): number {
751
+ let total = 0;
752
+ for (const node of this.nodes.values()) {
753
+ if (node.stats && typeof node.stats.memory === 'number') {
754
+ total += node.stats.memory;
755
+ } else if (node.group) {
756
+ total += estimateObjectGeometryBytes(node.group);
757
+ }
758
+ }
759
+ return total;
760
+ }
761
+
762
+ private _residentTextureBytes(): number {
763
+ let total = 0;
764
+ for (const tex of this.textureCache.values()) {
765
+ total += estimateTextureBytes(tex);
766
+ }
767
+ return total;
768
+ }
769
+
654
770
  /**
655
771
  * 预算裁剪 —— 必须在可见性算完之后调用(make-before-break):
656
772
  * 新瓦片已经落地、也已经决定由谁负责显示,这时候才把没人依赖的那块裁掉。
657
773
  * 只踢「当前不显示」或「位置已被可见子瓦接手」的瓦片;挑不出安全候选时允许
658
774
  * 暂时超出预算,等下一帧再裁 —— 宁可多占一会儿内存,也不在画面上开洞。
659
775
  *
660
- * 两条预算并行:瓦片数(`maxTiles`)与三角形总数(`maxTriangles`,默认不限制)。
661
- * `maxTriangles` 为 Infinity 时短路,行为与只有瓦片数预算时完全一致。
776
+ * 三条预算并行:瓦片数(`maxTiles`)、三角形总数(`maxTriangles`)与显存字节(`maxMemory`)。
777
+ * `maxTriangles` / `maxMemory` 为 Infinity 时短路,行为与只有瓦片数预算时完全一致。
662
778
  */
663
779
  private _trimBudget(): boolean {
664
780
  let removed = false;
@@ -667,9 +783,11 @@ export class S3MTileset {
667
783
  const overTiles = () => this.nodes.size + this.pending.size > this.maxTiles;
668
784
  const overTriangles = () =>
669
785
  Number.isFinite(this.maxTriangles) && this._residentTriangles() > this.maxTriangles;
670
- // 面数预算生效时可能要连踢多块,迭代上限跟着驻留规模走;不生效时根本进不了循环
786
+ const overMemory = () =>
787
+ Number.isFinite(this.maxMemory) && this.residentMemory() > this.maxMemory;
788
+ // 预算生效时可能要连踢多块,迭代上限跟着驻留规模走;不生效时根本进不了循环
671
789
  const iterationCap = this.nodes.size + this.maxTiles + 16;
672
- while ((overTiles() || overTriangles()) && guard++ <= iterationCap) {
790
+ while ((overTiles() || overTriangles() || overMemory()) && guard++ <= iterationCap) {
673
791
  if (!this._evictOne(this.nodes.size + this.pending.size > this.hardCap)) break;
674
792
  removed = true;
675
793
  }
@@ -1145,6 +1263,7 @@ export class S3MTileset {
1145
1263
  uniforms: this._uniforms,
1146
1264
  assembled,
1147
1265
  polygonOffset: this._polygonOffset,
1266
+ materialQuality: this._materialQuality,
1148
1267
  });
1149
1268
  const { group, stats } = buildRes;
1150
1269
  const texKeys = (buildRes as any).textureKeys ?? (group.userData?.textureKeys as string[] | undefined) ?? Object.keys(tile.textures ?? {});
@@ -1188,6 +1307,7 @@ export class S3MTileset {
1188
1307
  meshes: stats.meshes ?? 0,
1189
1308
  vertices: stats.vertices ?? 0,
1190
1309
  triangles: stats.triangles ?? 0,
1310
+ memory: (stats as any).memory ?? estimateObjectGeometryBytes(group),
1191
1311
  },
1192
1312
  };
1193
1313