s3m-three 0.3.6 → 0.3.7
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-hZY5KqWF.js → s3mWorker-D7AV6O30.js} +1 -1
- package/dist/browser/s3m-three.iife.ext-three.js +20 -20
- package/dist/browser/s3m-three.iife.js +20 -20
- package/dist/core/TextureDecode.d.ts +22 -1
- package/dist/core/TextureDecode.d.ts.map +1 -1
- package/dist/core/TextureDecode.js +47 -3
- package/dist/core/TextureDecode.js.map +1 -1
- package/dist/core/TileGeometry.d.ts +92 -0
- package/dist/core/TileGeometry.d.ts.map +1 -0
- package/dist/core/TileGeometry.js +607 -0
- package/dist/core/TileGeometry.js.map +1 -0
- package/dist/render/GeometryBuilder.d.ts +8 -10
- package/dist/render/GeometryBuilder.d.ts.map +1 -1
- package/dist/render/GeometryBuilder.js +77 -555
- package/dist/render/GeometryBuilder.js.map +1 -1
- package/dist/render/S3MViewer.d.ts +2 -0
- package/dist/render/S3MViewer.d.ts.map +1 -1
- package/dist/render/S3MViewer.js +9 -1
- package/dist/render/S3MViewer.js.map +1 -1
- package/dist/render/TextureFactory.d.ts +2 -1
- package/dist/render/TextureFactory.d.ts.map +1 -1
- package/dist/render/TextureFactory.js +30 -6
- package/dist/render/TextureFactory.js.map +1 -1
- package/dist/tileset/S3MTileset.d.ts +6 -2
- package/dist/tileset/S3MTileset.d.ts.map +1 -1
- package/dist/tileset/S3MTileset.js +14 -6
- package/dist/tileset/S3MTileset.js.map +1 -1
- package/dist/worker/S3MWorkerClient.d.ts +12 -4
- package/dist/worker/S3MWorkerClient.d.ts.map +1 -1
- package/dist/worker/S3MWorkerClient.js +12 -8
- package/dist/worker/S3MWorkerClient.js.map +1 -1
- package/dist/worker/s3mWorker.js +11 -3
- package/dist/worker/s3mWorker.js.map +1 -1
- package/docs/API.md +2 -0
- package/package.json +1 -1
- package/src/core/TextureDecode.ts +69 -3
- package/src/core/TileGeometry.ts +745 -0
- package/src/render/GeometryBuilder.ts +91 -617
- package/src/render/S3MViewer.ts +11 -3
- package/src/render/TextureFactory.ts +42 -8
- package/src/tileset/S3MTileset.ts +22 -8
- package/src/worker/S3MWorkerClient.ts +18 -9
- package/src/worker/s3mWorker.ts +24 -6
package/src/render/S3MViewer.ts
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
import * as THREE from 'three';
|
|
24
24
|
import { S3MTileset, usableGeoBounds } from '../tileset/S3MTileset.js';
|
|
25
25
|
import type { BuildTileFn, TileNode } from '../tileset/S3MTileset.js';
|
|
26
|
-
import { setMaxAnisotropy, setTextureOffThreadDecode } from './TextureFactory.js';
|
|
26
|
+
import { setMaxAnisotropy, setCompressedTexturesSupported, setTextureOffThreadDecode } from './TextureFactory.js';
|
|
27
27
|
import { decodeTextureWithWorker } from '../worker/S3MWorkerClient.js';
|
|
28
28
|
import {
|
|
29
29
|
STYLE,
|
|
@@ -438,8 +438,7 @@ export class S3MViewer {
|
|
|
438
438
|
* MapLibre 宿主由 S3MLayer 在 onAdd 里调用;纯 three 宿主直接传自己的 canvas / gl。
|
|
439
439
|
*/
|
|
440
440
|
/** 创建并配置一个绑定到给定 gl 上下文的 three renderer(mount 与上下文恢复共用) */
|
|
441
|
-
private _createRenderer(canvas: HTMLCanvasElement, gl: WebGLRenderingContext | WebGL2RenderingContext): THREE.WebGLRenderer {
|
|
442
|
-
const r = new THREE.WebGLRenderer({
|
|
441
|
+
private _createRenderer(canvas: HTMLCanvasElement, gl: WebGLRenderingContext | WebGL2RenderingContext): THREE.WebGLRenderer { const r = new THREE.WebGLRenderer({
|
|
443
442
|
canvas,
|
|
444
443
|
context: gl as WebGLRenderingContext,
|
|
445
444
|
antialias: true,
|
|
@@ -452,11 +451,19 @@ export class S3MViewer {
|
|
|
452
451
|
return r;
|
|
453
452
|
}
|
|
454
453
|
|
|
454
|
+
/** 按 GL 能力开/关 DXT 压缩直传:两个 s3tc 扩展都要在(srgb 缺了贴图会发白失配) */
|
|
455
|
+
private _syncTextureCapabilities(gl: WebGLRenderingContext | WebGL2RenderingContext): void {
|
|
456
|
+
const s3tc = !!gl.getExtension('WEBGL_compressed_texture_s3tc');
|
|
457
|
+
const s3tcSrgb = !!gl.getExtension('WEBGL_compressed_texture_s3tc_srgb');
|
|
458
|
+
setCompressedTexturesSupported(s3tc && s3tcSrgb);
|
|
459
|
+
}
|
|
460
|
+
|
|
455
461
|
mount(canvas: HTMLCanvasElement, gl: WebGLRenderingContext | WebGL2RenderingContext): void {
|
|
456
462
|
this.canvas = canvas;
|
|
457
463
|
|
|
458
464
|
this.renderer = this._createRenderer(canvas, gl);
|
|
459
465
|
setMaxAnisotropy(this.renderer.capabilities.getMaxAnisotropy());
|
|
466
|
+
this._syncTextureCapabilities(gl);
|
|
460
467
|
|
|
461
468
|
this.scene = new THREE.Scene();
|
|
462
469
|
this.scene.add(this.groundManager.group);
|
|
@@ -498,6 +505,7 @@ export class S3MViewer {
|
|
|
498
505
|
}
|
|
499
506
|
this.renderer = this._createRenderer(this.canvas, gl);
|
|
500
507
|
setMaxAnisotropy(this.renderer.capabilities.getMaxAnisotropy());
|
|
508
|
+
this._syncTextureCapabilities(gl);
|
|
501
509
|
this.picker?.dispose();
|
|
502
510
|
this.picker = new Picker(this.renderer);
|
|
503
511
|
// 共享 uniform / 材质引用不变,three 重新编译着色器时会自然接管;
|
|
@@ -21,6 +21,18 @@ export function setMaxAnisotropy(n: number): void {
|
|
|
21
21
|
maxAnisotropy = Math.max(1, n | 0);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* DXT 压缩直传开关(S3MViewer 在 mount / 上下文恢复时按 GL 能力注入):
|
|
26
|
+
* 需要 WEBGL_compressed_texture_s3tc 与 s3tc_srgb 两个扩展同时可用 ——
|
|
27
|
+
* 纹理是 SRGBColorSpace,缺 srgb 变体会出现贴图发白的颜色失配。
|
|
28
|
+
* 移动端大多没有 s3tc,自动回退 Worker 解码成 RGBA 上传,行为同旧版。
|
|
29
|
+
*/
|
|
30
|
+
let compressedTexturesSupported = false;
|
|
31
|
+
|
|
32
|
+
export function setCompressedTexturesSupported(v: boolean): void {
|
|
33
|
+
compressedTexturesSupported = v;
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
export interface DecodedTexture {
|
|
25
37
|
texture: THREE.Texture | null;
|
|
26
38
|
/** 实际走的解码路径,`unsupported(26)` 这类值会进 stats.skipped */
|
|
@@ -31,20 +43,22 @@ export interface DecodedTexture {
|
|
|
31
43
|
* 注入「离线解码」实现(S3MViewer 装配:走共享解析 Worker)。
|
|
32
44
|
* 返回 null = Worker 不可用 / 崩溃,createTexture 自动退回主线程 decodeTextureData。
|
|
33
45
|
*/
|
|
34
|
-
let offThreadDecode:
|
|
46
|
+
let offThreadDecode:
|
|
47
|
+
| ((tex: S3MTexture, compressed: boolean) => Promise<DecodedTextureData | null>)
|
|
48
|
+
| null = null;
|
|
35
49
|
|
|
36
50
|
export function setTextureOffThreadDecode(
|
|
37
|
-
fn: ((tex: S3MTexture) => Promise<DecodedTextureData | null>) | null
|
|
51
|
+
fn: ((tex: S3MTexture, compressed: boolean) => Promise<DecodedTextureData | null>) | null
|
|
38
52
|
): void {
|
|
39
53
|
offThreadDecode = fn;
|
|
40
54
|
}
|
|
41
55
|
|
|
42
|
-
async function decodeTexture(tex: S3MTexture): Promise<DecodedTextureData> {
|
|
56
|
+
async function decodeTexture(tex: S3MTexture, compressed: boolean): Promise<DecodedTextureData> {
|
|
43
57
|
if (offThreadDecode) {
|
|
44
|
-
const viaWorker = await offThreadDecode(tex);
|
|
58
|
+
const viaWorker = await offThreadDecode(tex, compressed);
|
|
45
59
|
if (viaWorker) return viaWorker;
|
|
46
60
|
}
|
|
47
|
-
return decodeTextureData(tex);
|
|
61
|
+
return decodeTextureData(tex, { compressed });
|
|
48
62
|
}
|
|
49
63
|
|
|
50
64
|
function configure<T extends THREE.Texture>(tex: T, srgb = true): T {
|
|
@@ -87,9 +101,11 @@ export async function createTexture(tex: S3MTexture | null | undefined): Promise
|
|
|
87
101
|
// 关 GPU mip,层级由 StyleMaterial.s3mAtlasUnit 的内联金字塔自己算。
|
|
88
102
|
const isAtlas = tex.packed === true || tex.flipY === true || tex.composed === true;
|
|
89
103
|
|
|
90
|
-
|
|
104
|
+
// 压缩直传只在「宿主 GL 支持 + 非图集」时开:图集要 flipY / 无 mip,压缩上传做不了翻行
|
|
105
|
+
const wantCompressed = compressedTexturesSupported && !isAtlas;
|
|
106
|
+
const data = await decodeTexture(tex, wantCompressed);
|
|
91
107
|
|
|
92
|
-
if (!data.rgba && !data.bitmap) {
|
|
108
|
+
if (!data.rgba && !data.blocks && !data.bitmap) {
|
|
93
109
|
if (data.error) {
|
|
94
110
|
// 一张纹理坏了不该带崩整个瓦片,如实上报并降级成无贴图
|
|
95
111
|
console.warn('[s3m] CRN 转码失败:', data.error);
|
|
@@ -100,12 +116,30 @@ export async function createTexture(tex: S3MTexture | null | undefined): Promise
|
|
|
100
116
|
let texture: THREE.Texture;
|
|
101
117
|
if (data.bitmap) {
|
|
102
118
|
texture = new THREE.Texture(data.bitmap);
|
|
119
|
+
} else if (data.blocks && data.blockFormat) {
|
|
120
|
+
// DXT 块直传:GPU 硬件解压,省 4~8 倍上传带宽与显存。压缩纹理不能 generateMipmaps
|
|
121
|
+
// (WebGL 限制),远处靠 LinearFilter 兜底;s3mHasAlpha 走 alphaTest 的 DXT1 1bit
|
|
122
|
+
// alpha 历史上没开过,DXT5 的 hasAlpha 由 decodeTextureData 判定
|
|
123
|
+
const format =
|
|
124
|
+
data.blockFormat === 'dxt1' ? THREE.RGB_S3TC_DXT1_Format : THREE.RGBA_S3TC_DXT5_Format;
|
|
125
|
+
texture = new THREE.CompressedTexture(
|
|
126
|
+
[{ data: data.blocks, width: data.width, height: data.height }],
|
|
127
|
+
data.width,
|
|
128
|
+
data.height,
|
|
129
|
+
format,
|
|
130
|
+
THREE.UnsignedByteType
|
|
131
|
+
);
|
|
103
132
|
} else {
|
|
104
133
|
const dt = new THREE.DataTexture(data.rgba!, data.width, data.height, THREE.RGBAFormat, THREE.UnsignedByteType);
|
|
105
134
|
if (data.hasAlpha) dt.userData.s3mHasAlpha = true;
|
|
106
135
|
texture = dt;
|
|
107
136
|
}
|
|
108
137
|
texture.needsUpdate = true;
|
|
138
|
+
const configured = isAtlas ? configureAtlas(texture) : configure(texture);
|
|
139
|
+
if (data.blocks) {
|
|
140
|
+
configured.generateMipmaps = false;
|
|
141
|
+
configured.minFilter = THREE.LinearFilter;
|
|
142
|
+
}
|
|
109
143
|
// flipY 为真即 S3MBLOCK 图集(见 core/types.ts),必须无 mip
|
|
110
|
-
return { texture:
|
|
144
|
+
return { texture: configured, decoded: data.kind };
|
|
111
145
|
}
|
|
@@ -27,6 +27,7 @@ import type {
|
|
|
27
27
|
TexblockRequestPlan,
|
|
28
28
|
} from '../core/S3MBlockParser.js';
|
|
29
29
|
import type { Anchor, ParsedTile, Stats, StyleUniforms } from '../core/types.js';
|
|
30
|
+
import type { TileGeometryData } from '../core/TileGeometry.js';
|
|
30
31
|
import type { Group, Texture } from 'three';
|
|
31
32
|
|
|
32
33
|
function dirOf(url: string): string {
|
|
@@ -226,7 +227,12 @@ export interface TileNode {
|
|
|
226
227
|
/** 瓦片构建器可注入,用于测试 / 服务端渲染(此时不需要 three) */
|
|
227
228
|
export type BuildTileFn = (
|
|
228
229
|
tile: ParsedTile,
|
|
229
|
-
options: {
|
|
230
|
+
options: {
|
|
231
|
+
textureCache: Map<string, Texture>;
|
|
232
|
+
uniforms: StyleUniforms | null;
|
|
233
|
+
/** Worker 侧已完成的纯数据几何组装(经典 .s3mb 路径),构建器直接包装成 three 对象 */
|
|
234
|
+
assembled?: TileGeometryData;
|
|
235
|
+
}
|
|
230
236
|
) => Promise<{ group: Group; stats: Partial<Stats> }>;
|
|
231
237
|
|
|
232
238
|
export interface S3MTilesetOptions {
|
|
@@ -691,17 +697,18 @@ export class S3MTileset {
|
|
|
691
697
|
}
|
|
692
698
|
|
|
693
699
|
/**
|
|
694
|
-
* 经典 `.s3mb` 瓦片解析:优先走 Worker
|
|
695
|
-
*
|
|
700
|
+
* 经典 `.s3mb` 瓦片解析:优先走 Worker(inflate / Draco / MeshOpt / 几何组装
|
|
701
|
+
* 全在 Worker 做,一次往返带回 tile + 纯数据几何),Worker 不可用或已崩溃时
|
|
702
|
+
* 主线程兜底,两条路径输出完全一致。
|
|
696
703
|
*/
|
|
697
|
-
private async _parseClassicTile(buffer: ArrayBuffer): Promise<ParsedTile> {
|
|
704
|
+
private async _parseClassicTile(buffer: ArrayBuffer): Promise<{ tile: ParsedTile; geometry?: TileGeometryData }> {
|
|
698
705
|
if (this._useWorker && this._workerClient === undefined) {
|
|
699
706
|
// 与纹理解码共用进程级单例,避免每个数据集各起一个 Worker
|
|
700
707
|
this._workerClient = getSharedS3MWorker();
|
|
701
708
|
}
|
|
702
709
|
if (this._workerClient) {
|
|
703
710
|
try {
|
|
704
|
-
return await this._workerClient.parseTile(buffer);
|
|
711
|
+
return await this._workerClient.parseTile(buffer, true);
|
|
705
712
|
} catch (e) {
|
|
706
713
|
if (e instanceof WorkerDeadError) {
|
|
707
714
|
// 脚本加载失败 / 崩溃属于环境问题,永久退回主线程;瓦片本身的问题照常抛
|
|
@@ -711,7 +718,7 @@ export class S3MTileset {
|
|
|
711
718
|
}
|
|
712
719
|
}
|
|
713
720
|
}
|
|
714
|
-
return parseS3MB(buffer);
|
|
721
|
+
return { tile: await parseS3MB(buffer) };
|
|
715
722
|
}
|
|
716
723
|
|
|
717
724
|
/**
|
|
@@ -985,6 +992,7 @@ export class S3MTileset {
|
|
|
985
992
|
const isBlock = /\.s3mblock($|\?)/i.test(baseUrl);
|
|
986
993
|
|
|
987
994
|
let tile: ParsedTile;
|
|
995
|
+
let assembled: TileGeometryData | undefined;
|
|
988
996
|
let groupNames: string[] = [];
|
|
989
997
|
const cachedSet = this._groupCache.get(baseUrl);
|
|
990
998
|
if (cachedSet) {
|
|
@@ -1007,7 +1015,9 @@ export class S3MTileset {
|
|
|
1007
1015
|
groupNames = set.names;
|
|
1008
1016
|
tile = set.tiles[groupIndex] ?? set.tiles[0]!;
|
|
1009
1017
|
} else {
|
|
1010
|
-
|
|
1018
|
+
const parsed = await this._parseClassicTile(buffer);
|
|
1019
|
+
tile = parsed.tile;
|
|
1020
|
+
assembled = parsed.geometry;
|
|
1011
1021
|
}
|
|
1012
1022
|
}
|
|
1013
1023
|
if (isBlock) {
|
|
@@ -1017,7 +1027,11 @@ export class S3MTileset {
|
|
|
1017
1027
|
}
|
|
1018
1028
|
|
|
1019
1029
|
const build = await this._builder();
|
|
1020
|
-
const buildRes = await build(tile, {
|
|
1030
|
+
const buildRes = await build(tile, {
|
|
1031
|
+
textureCache: this.textureCache,
|
|
1032
|
+
uniforms: this._uniforms,
|
|
1033
|
+
assembled,
|
|
1034
|
+
});
|
|
1021
1035
|
const { group, stats } = buildRes;
|
|
1022
1036
|
const texKeys = (buildRes as any).textureKeys ?? (group.userData?.textureKeys as string[] | undefined) ?? Object.keys(tile.textures ?? {});
|
|
1023
1037
|
this.registerTileTextures(url, texKeys);
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { ParsedTile, S3MTexture } from '../core/types.js';
|
|
12
12
|
import type { DecodedTextureData } from '../core/TextureDecode.js';
|
|
13
|
+
import type { TileGeometryData } from '../core/TileGeometry.js';
|
|
13
14
|
|
|
14
15
|
/** Worker 崩溃 / 不可用 —— 与「瓦片数据本身有问题」区分开,调用方据此决定降级还是重试 */
|
|
15
16
|
export class WorkerDeadError extends Error {
|
|
@@ -23,6 +24,7 @@ interface ParseOkResponse {
|
|
|
23
24
|
id: number;
|
|
24
25
|
ok: true;
|
|
25
26
|
tile: ParsedTile;
|
|
27
|
+
geometry?: TileGeometryData;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
interface TexOkResponse {
|
|
@@ -73,7 +75,7 @@ export class S3MWorkerClient {
|
|
|
73
75
|
(err as { permanent?: boolean }).permanent = msg.permanent;
|
|
74
76
|
waiter.reject(err);
|
|
75
77
|
} else if ('tile' in msg) {
|
|
76
|
-
waiter.resolve(msg.tile);
|
|
78
|
+
waiter.resolve({ tile: msg.tile, geometry: msg.geometry });
|
|
77
79
|
} else {
|
|
78
80
|
waiter.resolve(msg.data);
|
|
79
81
|
}
|
|
@@ -82,23 +84,26 @@ export class S3MWorkerClient {
|
|
|
82
84
|
worker.onmessageerror = () => this._die('s3m 解析 Worker 消息无法反序列化');
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
+
/**
|
|
88
|
+
* 解析一块瓦片。buffer 会被 structured clone(不 transfer),主线程保留原 buffer 以便崩溃后降级重解。
|
|
89
|
+
* assemble = true 时顺带在 Worker 里做纯数据几何组装,返回 { tile, geometry } 一次带回。
|
|
90
|
+
*/
|
|
91
|
+
parseTile(buffer: ArrayBuffer, assemble = false, batching = true): Promise<{ tile: ParsedTile; geometry?: TileGeometryData }> {
|
|
87
92
|
if (this.dead) return Promise.reject(new WorkerDeadError('s3m 解析 Worker 已终止'));
|
|
88
93
|
const id = ++this._seq;
|
|
89
|
-
return new Promise<ParsedTile>((resolve, reject) => {
|
|
94
|
+
return new Promise<{ tile: ParsedTile; geometry?: TileGeometryData }>((resolve, reject) => {
|
|
90
95
|
this._pending.set(id, { resolve: resolve as (v: unknown) => void, reject });
|
|
91
|
-
this._worker.postMessage({ type: 'parse', id, buffer });
|
|
96
|
+
this._worker.postMessage({ type: 'parse', id, buffer, assemble, batching });
|
|
92
97
|
});
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
/** 解码一张纹理(DXT / CRN / WEBP / RGB 系)。tex.data 走 structured clone,主线程保留原数据 */
|
|
96
|
-
decodeTexture(tex: S3MTexture): Promise<DecodedTextureData> {
|
|
101
|
+
decodeTexture(tex: S3MTexture, compressed = false): Promise<DecodedTextureData> {
|
|
97
102
|
if (this.dead) return Promise.reject(new WorkerDeadError('s3m 解析 Worker 已终止'));
|
|
98
103
|
const id = ++this._seq;
|
|
99
104
|
return new Promise<DecodedTextureData>((resolve, reject) => {
|
|
100
105
|
this._pending.set(id, { resolve: resolve as (v: unknown) => void, reject });
|
|
101
|
-
this._worker.postMessage({ type: 'tex', id, tex });
|
|
106
|
+
this._worker.postMessage({ type: 'tex', id, tex, compressed });
|
|
102
107
|
});
|
|
103
108
|
}
|
|
104
109
|
|
|
@@ -138,12 +143,16 @@ export function getSharedS3MWorker(): S3MWorkerClient | null {
|
|
|
138
143
|
* 纹理解码的「Worker 优先」入口,供 TextureFactory.setTextureOffThreadDecode 装配。
|
|
139
144
|
* 永不 reject:Worker 不可用 / 崩溃 / 单张解码失败都返回 null,调用方主线程兜底
|
|
140
145
|
* (解码失败本身在 decodeTextureData 里也不抛错,走 crn(failed) 之类的标记)。
|
|
146
|
+
* compressed 由 TextureFactory 按宿主 GL 能力传入,Worker 端据此跳过 DXT 解码。
|
|
141
147
|
*/
|
|
142
|
-
export async function decodeTextureWithWorker(
|
|
148
|
+
export async function decodeTextureWithWorker(
|
|
149
|
+
tex: S3MTexture,
|
|
150
|
+
compressed: boolean
|
|
151
|
+
): Promise<DecodedTextureData | null> {
|
|
143
152
|
const client = getSharedS3MWorker();
|
|
144
153
|
if (!client) return null;
|
|
145
154
|
try {
|
|
146
|
-
return await client.decodeTexture(tex);
|
|
155
|
+
return await client.decodeTexture(tex, compressed);
|
|
147
156
|
} catch {
|
|
148
157
|
return null;
|
|
149
158
|
}
|
package/src/worker/s3mWorker.ts
CHANGED
|
@@ -16,15 +16,24 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { parseS3MB } from '../core/S3MBParser.js';
|
|
18
18
|
import { decodeTextureData } from '../core/TextureDecode.js';
|
|
19
|
+
import { assembleTileGeometry } from '../core/TileGeometry.js';
|
|
19
20
|
import type { ParsedTile, S3MTexture } from '../core/types.js';
|
|
21
|
+
import type { TileGeometryData } from '../core/TileGeometry.js';
|
|
20
22
|
import { collectTransferables } from './transferables.js';
|
|
21
23
|
|
|
22
|
-
type ParseRequest = {
|
|
23
|
-
type
|
|
24
|
+
type ParseRequest = {
|
|
25
|
+
type: 'parse';
|
|
26
|
+
id: number;
|
|
27
|
+
buffer: ArrayBuffer;
|
|
28
|
+
/** 解析后顺带做纯数据几何组装(解交错 / 法线 / 合批),一次往返带回 */
|
|
29
|
+
assemble?: boolean;
|
|
30
|
+
batching?: boolean;
|
|
31
|
+
};
|
|
32
|
+
type TexRequest = { type: 'tex'; id: number; tex: S3MTexture; compressed?: boolean };
|
|
24
33
|
type Request = ParseRequest | TexRequest;
|
|
25
34
|
|
|
26
35
|
type ParseResponse =
|
|
27
|
-
| { id: number; ok: true; tile: ParsedTile }
|
|
36
|
+
| { id: number; ok: true; tile: ParsedTile; geometry?: TileGeometryData }
|
|
28
37
|
| { id: number; ok: false; message: string; permanent: boolean };
|
|
29
38
|
|
|
30
39
|
type TexResponse = { id: number; ok: true; data: Awaited<ReturnType<typeof decodeTextureData>> };
|
|
@@ -44,10 +53,19 @@ ctx.onmessage = async (e: MessageEvent<Request>) => {
|
|
|
44
53
|
try {
|
|
45
54
|
if (msg.type === 'parse') {
|
|
46
55
|
const tile = await parseS3MB(msg.buffer);
|
|
47
|
-
|
|
48
|
-
|
|
56
|
+
if (msg.assemble) {
|
|
57
|
+
// 解析 + 组装一起做,避免 tile 回主线程后再整块克隆回来一次
|
|
58
|
+
const geometry = assembleTileGeometry(tile, { batching: msg.batching });
|
|
59
|
+
ctx.postMessage(
|
|
60
|
+
{ id, ok: true, tile, geometry } satisfies ParseResponse,
|
|
61
|
+
[...collectTransferables(tile), ...collectTransferables(geometry)]
|
|
62
|
+
);
|
|
63
|
+
} else {
|
|
64
|
+
// 响应整体过线(零拷贝);请求 buffer 是主线程 structured clone 过来的副本,无需回传
|
|
65
|
+
ctx.postMessage({ id, ok: true, tile } satisfies ParseResponse, collectTransferables(tile));
|
|
66
|
+
}
|
|
49
67
|
} else {
|
|
50
|
-
const data = await decodeTextureData(msg.tex);
|
|
68
|
+
const data = await decodeTextureData(msg.tex, { compressed: msg.compressed });
|
|
51
69
|
ctx.postMessage({ id, ok: true, data } satisfies TexResponse, texTransferList(data));
|
|
52
70
|
}
|
|
53
71
|
} catch (err) {
|