s3m-three 0.3.5 → 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.
Files changed (69) hide show
  1. package/dist/browser/assets/s3mWorker-D7AV6O30.js +1 -0
  2. package/dist/browser/s3m-three.iife.ext-three.js +26 -20
  3. package/dist/browser/s3m-three.iife.js +26 -20
  4. package/dist/core/TextureDecode.d.ts +39 -0
  5. package/dist/core/TextureDecode.d.ts.map +1 -0
  6. package/dist/core/TextureDecode.js +174 -0
  7. package/dist/core/TextureDecode.js.map +1 -0
  8. package/dist/core/TextureFormat.d.ts +22 -2
  9. package/dist/core/TextureFormat.d.ts.map +1 -1
  10. package/dist/core/TextureFormat.js +22 -2
  11. package/dist/core/TextureFormat.js.map +1 -1
  12. package/dist/core/TileGeometry.d.ts +92 -0
  13. package/dist/core/TileGeometry.d.ts.map +1 -0
  14. package/dist/core/TileGeometry.js +607 -0
  15. package/dist/core/TileGeometry.js.map +1 -0
  16. package/dist/render/DatasetManager.d.ts +2 -0
  17. package/dist/render/DatasetManager.d.ts.map +1 -1
  18. package/dist/render/DatasetManager.js +1 -0
  19. package/dist/render/DatasetManager.js.map +1 -1
  20. package/dist/render/FeatureStyleManager.d.ts +4 -2
  21. package/dist/render/FeatureStyleManager.d.ts.map +1 -1
  22. package/dist/render/FeatureStyleManager.js +33 -1
  23. package/dist/render/FeatureStyleManager.js.map +1 -1
  24. package/dist/render/GeometryBuilder.d.ts +8 -10
  25. package/dist/render/GeometryBuilder.d.ts.map +1 -1
  26. package/dist/render/GeometryBuilder.js +77 -555
  27. package/dist/render/GeometryBuilder.js.map +1 -1
  28. package/dist/render/S3MViewer.d.ts +4 -0
  29. package/dist/render/S3MViewer.d.ts.map +1 -1
  30. package/dist/render/S3MViewer.js +13 -2
  31. package/dist/render/S3MViewer.js.map +1 -1
  32. package/dist/render/StyleMaterial.d.ts.map +1 -1
  33. package/dist/render/StyleMaterial.js +6 -0
  34. package/dist/render/StyleMaterial.js.map +1 -1
  35. package/dist/render/TextureFactory.d.ts +9 -19
  36. package/dist/render/TextureFactory.d.ts.map +1 -1
  37. package/dist/render/TextureFactory.js +62 -115
  38. package/dist/render/TextureFactory.js.map +1 -1
  39. package/dist/tileset/S3MTileset.d.ts +21 -0
  40. package/dist/tileset/S3MTileset.d.ts.map +1 -1
  41. package/dist/tileset/S3MTileset.js +41 -2
  42. package/dist/tileset/S3MTileset.js.map +1 -1
  43. package/dist/worker/S3MWorkerClient.d.ts +48 -0
  44. package/dist/worker/S3MWorkerClient.d.ts.map +1 -0
  45. package/dist/worker/S3MWorkerClient.js +124 -0
  46. package/dist/worker/S3MWorkerClient.js.map +1 -0
  47. package/dist/worker/s3mWorker.d.ts +2 -0
  48. package/dist/worker/s3mWorker.d.ts.map +1 -0
  49. package/dist/worker/s3mWorker.js +56 -0
  50. package/dist/worker/s3mWorker.js.map +1 -0
  51. package/dist/worker/transferables.d.ts +14 -0
  52. package/dist/worker/transferables.d.ts.map +1 -0
  53. package/dist/worker/transferables.js +61 -0
  54. package/dist/worker/transferables.js.map +1 -0
  55. package/docs/API.md +6 -0
  56. package/package.json +1 -1
  57. package/src/core/TextureDecode.ts +224 -0
  58. package/src/core/TextureFormat.ts +23 -2
  59. package/src/core/TileGeometry.ts +745 -0
  60. package/src/render/DatasetManager.ts +3 -0
  61. package/src/render/FeatureStyleManager.ts +31 -2
  62. package/src/render/GeometryBuilder.ts +91 -617
  63. package/src/render/S3MViewer.ts +18 -4
  64. package/src/render/StyleMaterial.ts +6 -0
  65. package/src/render/TextureFactory.ts +78 -123
  66. package/src/tileset/S3MTileset.ts +55 -3
  67. package/src/worker/S3MWorkerClient.ts +159 -0
  68. package/src/worker/s3mWorker.ts +76 -0
  69. package/src/worker/transferables.ts +58 -0
@@ -0,0 +1,224 @@
1
+ /**
2
+ * S3M 纹理解码(three 无关,主线程 / Worker 共用同一份实现)
3
+ *
4
+ * 职责到「解出像素数据」为止:DXT 族解块成 RGBA8、CRN 先用 crunch wasm 转码回 DXT
5
+ * 再解块、WEBP 交给 createImageBitmap、RGB 系逐像素重排。产出是纯数据
6
+ * (DecodedTextureData),包成 three 对象由渲染层的 TextureFactory 负责——
7
+ * 这样整份文件可以原样搬进 Worker 跑。
8
+ *
9
+ * 该走哪条解码路径由 compressType 判定(见 core/TextureFormat.ts):
10
+ * v3.00 的 CRN 纹理 pixelFormat 是 13(ABGR)、行标的 CRN 是 32856(GL_RGBA8),
11
+ * 都不是 26 / 27,所以判定只能看 compressType,不能看 pixelFormat。
12
+ */
13
+ import DXTTextureDecode from './DXTTextureDecode.js';
14
+ import { transcodeCRN } from './CRNDecode.js';
15
+ import { dxtPixelFormatOf, resolveTextureKind, PixelFormat } from './TextureFormat.js';
16
+ import type { S3MTexture } from './types.js';
17
+
18
+ /** 解码产物:rgba 与 bitmap 二选一,都为 null = 解码失败 / 格式不支持 */
19
+ export interface DecodedTextureData {
20
+ /** 实际走的解码路径('crn->dxt5' / 'dxt1' / 'raw' / 'webp' / 'crn(failed)' / 'unsupported(...)') */
21
+ kind: string;
22
+ width: number;
23
+ height: number;
24
+ /** RGBA8 像素(已按需翻行) */
25
+ rgba: Uint8Array | null;
26
+ /** WEBP 路径的解码结果(ImageBitmap 可 transfer,主线程直接 new THREE.Texture 包) */
27
+ bitmap: ImageBitmap | null;
28
+ hasAlpha: boolean;
29
+ /**
30
+ * 压缩直传模式(decodeTextureData 的 compressed 选项):不解码,原样返回 DXT 块,
31
+ * 主线程用 CompressedTexture 硬件解压上传。与 rgba 互斥。
32
+ */
33
+ blocks?: Uint8Array;
34
+ blockFormat?: 'dxt1' | 'dxt5';
35
+ /** CRN 转码失败时的原始错误(渲染层负责打 warn) */
36
+ error?: string;
37
+ }
38
+
39
+ export interface DecodeOptions {
40
+ /**
41
+ * 压缩直传:dxt1/dxt5(含 CRN 转码产物)不解码,原样返回 DXT 块。
42
+ * 仅在宿主支持 WEBGL_compressed_texture_s3tc(+srgb) 时开启;
43
+ * flipY 纹理走不了压缩上传(WebGL 对压缩纹理忽略 UNPACK_FLIP_Y),自动回退解码。
44
+ */
45
+ compressed?: boolean;
46
+ }
47
+
48
+ /** 行序翻转(原地)。S3MBLOCK 图集按 GL 方向存储(v=0 在最后一行),解码后要翻一次行 */
49
+ function flipRows(rgba: Uint8Array, width: number, height: number): void {
50
+ const row = width * 4;
51
+ const tmp = new Uint8Array(row);
52
+ for (let y = 0; y < height >> 1; y++) {
53
+ const a = y * row;
54
+ const b = (height - 1 - y) * row;
55
+ tmp.set(rgba.subarray(a, a + row));
56
+ rgba.copyWithin(a, b, b + row);
57
+ rgba.set(tmp, b);
58
+ }
59
+ }
60
+
61
+ /** 该像素格式自带 alpha 通道(DXT1 的 1bit alpha 不算,历史上没开过 alphaTest) */
62
+ function hasAlphaChannel(pixelFormat: number): boolean {
63
+ return (
64
+ pixelFormat === PixelFormat.DXT3 ||
65
+ pixelFormat === PixelFormat.DXT4 ||
66
+ pixelFormat === PixelFormat.DXT5 ||
67
+ pixelFormat === PixelFormat.ARGB ||
68
+ pixelFormat === PixelFormat.ABGR ||
69
+ pixelFormat === PixelFormat.BGRA ||
70
+ pixelFormat === PixelFormat.RGBA
71
+ );
72
+ }
73
+
74
+ /** 解 DXT 到 RGBA8。DXT5 是 1 字节/像素,DXT1 是 0.5 字节/像素。 */
75
+ /** DXT 解码只需要尺寸 / 格式 / 字节,CRN 转码出来的结果也走这条路 */
76
+ function decodeDXT(tex: Pick<S3MTexture, 'width' | 'height' | 'pixelFormat' | 'data' | 'flipY'>): {
77
+ rgba: Uint8Array;
78
+ hasAlpha: boolean;
79
+ } {
80
+ const { width, height, pixelFormat, data } = tex;
81
+ const out = new Uint8Array(width * height * 4);
82
+ DXTTextureDecode.decode(out, width, height, data, pixelFormat);
83
+ if (tex.flipY) flipRows(out, width, height);
84
+ return { rgba: out, hasAlpha: hasAlphaChannel(pixelFormat) };
85
+ }
86
+
87
+ function rawRGBA(tex: S3MTexture): { rgba: Uint8Array; hasAlpha: boolean } {
88
+ const { width, height, pixelFormat, data } = tex;
89
+ const out = new Uint8Array(width * height * 4);
90
+ // RGBA / BGRA / ARGB / ABGR 都是 4 字节,前两个是「尾字节是 alpha」,后两个是「首字节是 alpha」
91
+ const hasAlpha =
92
+ pixelFormat === PixelFormat.RGBA ||
93
+ pixelFormat === PixelFormat.BGRA ||
94
+ pixelFormat === PixelFormat.ARGB ||
95
+ pixelFormat === PixelFormat.ABGR;
96
+ const swapRB = pixelFormat === PixelFormat.BGR || pixelFormat === PixelFormat.BGRA;
97
+ const alphaFirst = pixelFormat === PixelFormat.ARGB || pixelFormat === PixelFormat.ABGR;
98
+ const src = hasAlpha ? 4 : 3;
99
+ for (let i = 0, s = 0, d = 0; i < width * height; i++, s += src, d += 4) {
100
+ const p = alphaFirst ? s + 1 : s;
101
+ out[d] = swapRB ? data[p + 2] : data[p];
102
+ out[d + 1] = data[p + 1];
103
+ out[d + 2] = swapRB ? data[p] : data[p + 2];
104
+ out[d + 3] = hasAlpha ? data[alphaFirst ? s : s + 3] : 255;
105
+ }
106
+ if (tex.flipY) flipRows(out, width, height);
107
+ return { rgba: out, hasAlpha: hasAlphaChannel(pixelFormat) };
108
+ }
109
+
110
+ /**
111
+ * DXT 块数据的期望字节数:4×4 对齐块 × 每块字节(DXT1=8,DXT5=16)。
112
+ * S3M 纹理宽高不总是 4 的倍数,驱动按声明尺寸算期望大小——对不上就是
113
+ * GL_INVALID_VALUE: glCompressedTexSubImage2D: The <imageSize> does not match。
114
+ * 压缩直传前必须用这个函数把关,不匹配就回退 RGBA 解码。
115
+ */
116
+ export function expectedDxtByteLength(width: number, height: number, format: 'dxt1' | 'dxt5'): number {
117
+ const bw = (width + 3) >> 2;
118
+ const bh = (height + 3) >> 2;
119
+ return bw * bh * (format === 'dxt1' ? 8 : 16);
120
+ }
121
+
122
+ /** 主线程 / Worker 共用的解码入口。不抛错:失败与不支持都体现在返回值里 */
123
+ export async function decodeTextureData(
124
+ tex: S3MTexture,
125
+ options: DecodeOptions = {}
126
+ ): Promise<DecodedTextureData> {
127
+ const empty: DecodedTextureData = {
128
+ kind: 'unsupported(0/0)',
129
+ width: 0,
130
+ height: 0,
131
+ rgba: null,
132
+ bitmap: null,
133
+ hasAlpha: false,
134
+ };
135
+ if (!tex || !tex.width || !tex.height) return empty;
136
+
137
+ const kind = resolveTextureKind(tex.compressType ?? 0, tex.pixelFormat);
138
+
139
+ // 压缩直传的公共前提:不是 GL 翻行图集(压缩上传无法 flipY)
140
+ const canCompress = options.compressed === true && !tex.flipY;
141
+
142
+ /** 块数据字节数与 4×4 对齐公式一致才允许直传,否则回退解码 */
143
+ const compressible = (data: Uint8Array, w: number, h: number, format: 'dxt1' | 'dxt5'): boolean =>
144
+ data.byteLength === expectedDxtByteLength(w, h, format);
145
+
146
+ if (kind === 'crn') {
147
+ try {
148
+ const crn = await transcodeCRN(tex.data);
149
+ const pf = crn.dxtFormat === 1 ? PixelFormat.DXT1 : PixelFormat.DXT5;
150
+ const fmt = crn.dxtFormat === 1 ? 'dxt1' : 'dxt5';
151
+ if (canCompress && compressible(crn.data, crn.width, crn.height, fmt)) {
152
+ // CRN→DXT 的转码免不了(wasm),但后续的 DXT→RGBA 解块省掉,块数据直传 GPU
153
+ return {
154
+ kind: `crn->dxt${crn.dxtFormat}(c)`,
155
+ width: crn.width,
156
+ height: crn.height,
157
+ rgba: null,
158
+ bitmap: null,
159
+ hasAlpha: pf === PixelFormat.DXT5,
160
+ blocks: crn.data,
161
+ blockFormat: fmt,
162
+ };
163
+ }
164
+ const decoded = decodeDXT({
165
+ width: crn.width,
166
+ height: crn.height,
167
+ pixelFormat: pf,
168
+ data: crn.data,
169
+ });
170
+ return {
171
+ kind: `crn->dxt${crn.dxtFormat}`,
172
+ width: crn.width,
173
+ height: crn.height,
174
+ rgba: decoded.rgba,
175
+ bitmap: null,
176
+ hasAlpha: decoded.hasAlpha,
177
+ };
178
+ } catch (e) {
179
+ // 一张纹理坏了不该带崩整个瓦片,如实上报并降级成无贴图
180
+ return {
181
+ kind: 'crn(failed)',
182
+ width: tex.width,
183
+ height: tex.height,
184
+ rgba: null,
185
+ bitmap: null,
186
+ hasAlpha: false,
187
+ error: String((e as Error | null)?.message ?? e),
188
+ };
189
+ }
190
+ }
191
+
192
+ if (kind === 'dxt1' || kind === 'dxt3' || kind === 'dxt4' || kind === 'dxt5') {
193
+ const pf = dxtPixelFormatOf(kind);
194
+ // dxt3/dxt4 少见,不进直传白名单;块长与 4×4 对齐公式不符的也回退解码
195
+ if (canCompress && (kind === 'dxt1' || kind === 'dxt5') && compressible(tex.data, tex.width, tex.height, kind)) {
196
+ return {
197
+ kind: `${kind}(c)`,
198
+ width: tex.width,
199
+ height: tex.height,
200
+ rgba: null,
201
+ bitmap: null,
202
+ hasAlpha: kind === 'dxt5',
203
+ blocks: tex.data,
204
+ blockFormat: kind,
205
+ };
206
+ }
207
+ const decoded = decodeDXT({ ...tex, pixelFormat: pf });
208
+ return { kind, width: tex.width, height: tex.height, rgba: decoded.rgba, bitmap: null, hasAlpha: decoded.hasAlpha };
209
+ }
210
+
211
+ if (kind === 'webp') {
212
+ const blob = new Blob([tex.data as BlobPart], { type: 'image/webp' });
213
+ const bitmap = await createImageBitmap(blob);
214
+ return { kind, width: bitmap.width, height: bitmap.height, rgba: null, bitmap, hasAlpha: true };
215
+ }
216
+
217
+ if (kind === 'raw') {
218
+ const decoded = rawRGBA(tex);
219
+ return { kind, width: tex.width, height: tex.height, rgba: decoded.rgba, bitmap: null, hasAlpha: decoded.hasAlpha };
220
+ }
221
+
222
+ // KTX2 / ETC 需要额外解码器,先如实上报而不是静默出错
223
+ return { ...empty, kind: `unsupported(${tex.compressType}/${tex.pixelFormat})` };
224
+ }
@@ -18,8 +18,29 @@
18
18
  * 真实服务里的 CRN 纹理会被当成未知格式整包丢掉(模型变成无贴图的灰模)。
19
19
  */
20
20
 
21
- /** S3M 3.01(行标)compressType 取值 */
22
- export const TextureCompress = {
21
+ /**
22
+ * S3M 像素 / 块格式枚举(iClient3D S3MPixelFormat)。
23
+ *
24
+ * 原本住在 render/TextureFactory,纹理解码下沉到 core(Worker 复用)后搬到这里;
25
+ * TextureFactory 保留同名 re-export,对外 API 不变。
26
+ */
27
+ export const PixelFormat = {
28
+ RGB: 10,
29
+ BGR: 11,
30
+ ARGB: 12,
31
+ ABGR: 13,
32
+ BGRA: 14,
33
+ RGBA: 28,
34
+ WEBP: 25,
35
+ DXT1: 17,
36
+ DXT3: 19,
37
+ DXT4: 20,
38
+ DXT5: 21,
39
+ CRN_DXT5: 26,
40
+ STANDARD_CRN: 27,
41
+ } as const;
42
+
43
+ /** S3M 3.01(行标)compressType 取值 */export const TextureCompress = {
23
44
  NONE: 0,
24
45
  DXT1_RGB: 33776,
25
46
  DXT1_RGBA: 33777,