s3m-three 0.3.13 → 0.3.14

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 (48) hide show
  1. package/dist/browser/assets/{s3mWorker-DLsGBSaV.js → s3mWorker-BjPOUgjg.js} +1 -1
  2. package/dist/browser/s3m-three.iife.ext-three.js +21 -21
  3. package/dist/browser/s3m-three.iife.js +21 -21
  4. package/dist/core/TextureDecode.d.ts +9 -0
  5. package/dist/core/TextureDecode.d.ts.map +1 -1
  6. package/dist/core/TextureDecode.js +66 -0
  7. package/dist/core/TextureDecode.js.map +1 -1
  8. package/dist/index.d.ts +2 -2
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +2 -2
  11. package/dist/index.js.map +1 -1
  12. package/dist/render/DatasetManager.d.ts +3 -1
  13. package/dist/render/DatasetManager.d.ts.map +1 -1
  14. package/dist/render/DatasetManager.js +2 -1
  15. package/dist/render/DatasetManager.js.map +1 -1
  16. package/dist/render/OrthoProjection.d.ts +22 -0
  17. package/dist/render/OrthoProjection.d.ts.map +1 -1
  18. package/dist/render/OrthoProjection.js +60 -0
  19. package/dist/render/OrthoProjection.js.map +1 -1
  20. package/dist/render/S3MViewer.d.ts +70 -0
  21. package/dist/render/S3MViewer.d.ts.map +1 -1
  22. package/dist/render/S3MViewer.js +100 -5
  23. package/dist/render/S3MViewer.js.map +1 -1
  24. package/dist/render/TextureFactory.d.ts +3 -2
  25. package/dist/render/TextureFactory.d.ts.map +1 -1
  26. package/dist/render/TextureFactory.js +26 -6
  27. package/dist/render/TextureFactory.js.map +1 -1
  28. package/dist/tileset/S3MTileset.d.ts +26 -1
  29. package/dist/tileset/S3MTileset.d.ts.map +1 -1
  30. package/dist/tileset/S3MTileset.js +29 -2
  31. package/dist/tileset/S3MTileset.js.map +1 -1
  32. package/dist/worker/S3MWorkerClient.d.ts +6 -3
  33. package/dist/worker/S3MWorkerClient.d.ts.map +1 -1
  34. package/dist/worker/S3MWorkerClient.js +8 -5
  35. package/dist/worker/S3MWorkerClient.js.map +1 -1
  36. package/dist/worker/s3mWorker.js +123 -71
  37. package/dist/worker/s3mWorker.js.map +1 -1
  38. package/docs/API.md +61 -0
  39. package/package.json +1 -1
  40. package/src/core/TextureDecode.ts +87 -0
  41. package/src/index.ts +2 -1
  42. package/src/render/DatasetManager.ts +5 -1
  43. package/src/render/OrthoProjection.ts +58 -0
  44. package/src/render/S3MViewer.ts +134 -7
  45. package/src/render/TextureFactory.ts +35 -8
  46. package/src/tileset/S3MTileset.ts +41 -2
  47. package/src/worker/S3MWorkerClient.ts +9 -5
  48. package/src/worker/s3mWorker.ts +2 -2
@@ -261,6 +261,16 @@ export interface S3MTilesetOptions {
261
261
  maxRetries?: number;
262
262
  /** LOD 切换范围缩放系数(默认 1.0),> 1 更倾向保留粗模,< 1 提前换细模 */
263
263
  lodRangeScale?: number;
264
+ /**
265
+ * 本数据集**驻留三角形总数**的软上限(默认不限制)。
266
+ *
267
+ * `maxTiles` 只数瓦片个数:一块精模瓦片几十万面,64 块照样能把差设备压垮,
268
+ * 所以在瓦片数之外再给一条按面数的刹车。超了走同一套 make-before-break 淘汰
269
+ * (只踢「当前不显示」或「位置已被可见子瓦接管」的瓦片),不在画面上开洞。
270
+ *
271
+ * 不传 = 不限制,行为与旧版完全一致。运行时可用 `setMaxTriangles()` 调整。
272
+ */
273
+ maxTriangles?: number;
264
274
  /**
265
275
  * 是否请求 `.texblock` 子图集(S3MBLOCK 根图集的清晰版)。**默认关**。
266
276
  *
@@ -315,6 +325,11 @@ export class S3MTileset {
315
325
 
316
326
  maxConcurrent: number;
317
327
  maxTiles: number;
328
+ /**
329
+ * 驻留三角形总数的软上限。`Infinity` = 不限制(默认),此时 `_trimBudget` 的
330
+ * 判据与旧版逐字一致,不会多算一次面数。
331
+ */
332
+ maxTriangles: number;
318
333
  lodRangeScale: number;
319
334
 
320
335
  /** 数据集配置声明的高度范围(米),若配置未声明则返回 null */
@@ -369,6 +384,7 @@ export class S3MTileset {
369
384
  // 并发与总量限制:iServer 公开服务上别把人家打爆
370
385
  this.maxConcurrent = options.maxConcurrent ?? 4;
371
386
  this.maxTiles = options.maxTiles ?? 64;
387
+ this.maxTriangles = options.maxTriangles ?? Infinity;
372
388
  this.maxImageCache = options.maxImageCache ?? 128;
373
389
  // 硬上限 = 软上限 + 余量。淘汰只在「新瓦片已经落地并接管显示」之后做(make-before-break),
374
390
  // 所以中间态允许临时超出 maxTiles;硬上限只用来挡住失控的请求量,不用于挤掉正在显示的内容。
@@ -566,17 +582,40 @@ export class S3MTileset {
566
582
  return true;
567
583
  }
568
584
 
585
+ /** 当前驻留瓦片的三角形总数(只算已落地的;在途瓦片还没构建,统计不到) */
586
+ private _residentTriangles(): number {
587
+ let total = 0;
588
+ for (const node of this.nodes.values()) total += node.stats?.triangles ?? 0;
589
+ return total;
590
+ }
591
+
592
+ /**
593
+ * 运行时调整面数预算。`Infinity` = 不限制(默认)。
594
+ * 收紧后不会立刻掉内容,要等下一次 `_trimBudget`(下一帧 update)才生效。
595
+ */
596
+ setMaxTriangles(n: number): void {
597
+ this.maxTriangles = n > 0 ? n : Infinity;
598
+ }
599
+
569
600
  /**
570
601
  * 预算裁剪 —— 必须在可见性算完之后调用(make-before-break):
571
602
  * 新瓦片已经落地、也已经决定由谁负责显示,这时候才把没人依赖的那块裁掉。
572
603
  * 只踢「当前不显示」或「位置已被可见子瓦接手」的瓦片;挑不出安全候选时允许
573
- * 暂时超出 maxTiles,等下一帧再裁 —— 宁可多占一会儿内存,也不在画面上开洞。
604
+ * 暂时超出预算,等下一帧再裁 —— 宁可多占一会儿内存,也不在画面上开洞。
605
+ *
606
+ * 两条预算并行:瓦片数(`maxTiles`)与三角形总数(`maxTriangles`,默认不限制)。
607
+ * `maxTriangles` 为 Infinity 时短路,行为与只有瓦片数预算时完全一致。
574
608
  */
575
609
  private _trimBudget(): boolean {
576
610
  let removed = false;
577
611
  let guard = 0;
578
612
  // 在途的也算进"预期驻留数":不然一批瓦片同时落地时会先把画面撑爆再疯狂裁
579
- while (this.nodes.size + this.pending.size > this.maxTiles && guard++ <= this.maxTiles + 8) {
613
+ const overTiles = () => this.nodes.size + this.pending.size > this.maxTiles;
614
+ const overTriangles = () =>
615
+ Number.isFinite(this.maxTriangles) && this._residentTriangles() > this.maxTriangles;
616
+ // 面数预算生效时可能要连踢多块,迭代上限跟着驻留规模走;不生效时根本进不了循环
617
+ const iterationCap = this.nodes.size + this.maxTiles + 16;
618
+ while ((overTiles() || overTriangles()) && guard++ <= iterationCap) {
580
619
  if (!this._evictOne(this.nodes.size + this.pending.size > this.hardCap)) break;
581
620
  removed = true;
582
621
  }
@@ -97,13 +97,16 @@ export class S3MWorkerClient {
97
97
  });
98
98
  }
99
99
 
100
- /** 解码一张纹理(DXT / CRN / WEBP / RGB 系)。tex.data 走 structured clone,主线程保留原数据 */
101
- decodeTexture(tex: S3MTexture, compressed = false): Promise<DecodedTextureData> {
100
+ /**
101
+ * 解码一张纹理(DXT / CRN / WEBP / RGB 系)。tex.data 走 structured clone,主线程保留原数据。
102
+ * `maxSize` 交给 Worker 侧收口——缩放本身也是 CPU 活,放在 Worker 里主线程零成本。
103
+ */
104
+ decodeTexture(tex: S3MTexture, compressed = false, maxSize?: number): Promise<DecodedTextureData> {
102
105
  if (this.dead) return Promise.reject(new WorkerDeadError('s3m 解析 Worker 已终止'));
103
106
  const id = ++this._seq;
104
107
  return new Promise<DecodedTextureData>((resolve, reject) => {
105
108
  this._pending.set(id, { resolve: resolve as (v: unknown) => void, reject });
106
- this._worker.postMessage({ type: 'tex', id, tex, compressed });
109
+ this._worker.postMessage({ type: 'tex', id, tex, compressed, maxSize });
107
110
  });
108
111
  }
109
112
 
@@ -147,12 +150,13 @@ export function getSharedS3MWorker(): S3MWorkerClient | null {
147
150
  */
148
151
  export async function decodeTextureWithWorker(
149
152
  tex: S3MTexture,
150
- compressed: boolean
153
+ compressed: boolean,
154
+ maxSize?: number
151
155
  ): Promise<DecodedTextureData | null> {
152
156
  const client = getSharedS3MWorker();
153
157
  if (!client) return null;
154
158
  try {
155
- return await client.decodeTexture(tex, compressed);
159
+ return await client.decodeTexture(tex, compressed, maxSize);
156
160
  } catch {
157
161
  return null;
158
162
  }
@@ -29,7 +29,7 @@ type ParseRequest = {
29
29
  assemble?: boolean;
30
30
  batching?: boolean;
31
31
  };
32
- type TexRequest = { type: 'tex'; id: number; tex: S3MTexture; compressed?: boolean };
32
+ type TexRequest = { type: 'tex'; id: number; tex: S3MTexture; compressed?: boolean; maxSize?: number };
33
33
  type Request = ParseRequest | TexRequest;
34
34
 
35
35
  type ParseResponse =
@@ -65,7 +65,7 @@ ctx.onmessage = async (e: MessageEvent<Request>) => {
65
65
  ctx.postMessage({ id, ok: true, tile } satisfies ParseResponse, collectTransferables(tile));
66
66
  }
67
67
  } else {
68
- const data = await decodeTextureData(msg.tex, { compressed: msg.compressed });
68
+ const data = await decodeTextureData(msg.tex, { compressed: msg.compressed, maxSize: msg.maxSize });
69
69
  ctx.postMessage({ id, ok: true, data } satisfies TexResponse, texTransferList(data));
70
70
  }
71
71
  } catch (err) {