s3m-three 0.1.0 → 0.2.0

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 (44) hide show
  1. package/LICENSE +202 -202
  2. package/dist/render/DatasetManager.d.ts +10 -3
  3. package/dist/render/DatasetManager.d.ts.map +1 -1
  4. package/dist/render/DatasetManager.js +14 -5
  5. package/dist/render/DatasetManager.js.map +1 -1
  6. package/dist/render/EdgeLines.js +46 -46
  7. package/dist/render/GroundManager.d.ts +9 -0
  8. package/dist/render/GroundManager.d.ts.map +1 -1
  9. package/dist/render/GroundManager.js +13 -1
  10. package/dist/render/GroundManager.js.map +1 -1
  11. package/dist/render/Picker.js +66 -66
  12. package/dist/render/S3MViewer.d.ts +11 -3
  13. package/dist/render/S3MViewer.d.ts.map +1 -1
  14. package/dist/render/S3MViewer.js +18 -3
  15. package/dist/render/S3MViewer.js.map +1 -1
  16. package/dist/render/StyleMaterial.js +265 -265
  17. package/dist/vendor/meshopt_decoder.module.js +118 -118
  18. package/docs/API.md +20 -0
  19. package/package.json +1 -1
  20. package/src/core/BinaryReader.ts +116 -116
  21. package/src/core/DXTTextureDecode.ts +260 -260
  22. package/src/core/FeatureInfo.ts +150 -150
  23. package/src/core/MeshOpt.ts +401 -401
  24. package/src/core/S3MBParser.ts +590 -590
  25. package/src/core/types.ts +222 -222
  26. package/src/render/DatasetManager.ts +17 -6
  27. package/src/render/EdgeLines.ts +343 -343
  28. package/src/render/GeometryBuilder.ts +840 -840
  29. package/src/render/GroundManager.ts +14 -1
  30. package/src/render/Picker.ts +293 -293
  31. package/src/render/S3MViewer.ts +18 -3
  32. package/src/render/StyleMaterial.ts +602 -602
  33. package/src/render/TextureFactory.ts +134 -134
  34. package/src/tileset/S3MTileset.ts +552 -552
  35. package/src/vendor/meshopt_decoder.module.d.ts +31 -31
  36. package/src/vendor/meshopt_decoder.module.js +118 -118
  37. package/dist/render/CoordinateHelper.d.ts +0 -90
  38. package/dist/render/CoordinateHelper.d.ts.map +0 -1
  39. package/dist/render/CoordinateHelper.js +0 -85
  40. package/dist/render/CoordinateHelper.js.map +0 -1
  41. package/dist/render/S3MLayer.d.ts +0 -457
  42. package/dist/render/S3MLayer.d.ts.map +0 -1
  43. package/dist/render/S3MLayer.js +0 -1860
  44. package/dist/render/S3MLayer.js.map +0 -1
@@ -1,134 +1,134 @@
1
- /**
2
- * S3M 纹理 → three.js Texture
3
- *
4
- * S3M 的纹理是「裸块」:没有容器头,只有 width/height/compressType/dataSize/pixelFormat + 原始字节。
5
- * 该走哪条解码路径由 compressType 决定(见 core/TextureFormat.ts):DXT 族直接解块,
6
- * CRN(26 / 27 / 38001 / 50000)先用 crunch wasm 转码回 DXT,WEBP 交给浏览器,
7
- * RGB 系逐像素拷贝。
8
- */
9
- import * as THREE from 'three';
10
- import DXTTextureDecode from '../core/DXTTextureDecode.js';
11
- import { transcodeCRN } from '../core/CRNDecode.js';
12
- import { dxtPixelFormatOf, resolveTextureKind } from '../core/TextureFormat.js';
13
- import type { S3MTexture } from '../core/types.js';
14
-
15
- export const PixelFormat = {
16
- RGB: 10,
17
- BGR: 11,
18
- ARGB: 12,
19
- ABGR: 13,
20
- BGRA: 14,
21
- RGBA: 28,
22
- WEBP: 25,
23
- DXT1: 17,
24
- DXT3: 19,
25
- DXT4: 20,
26
- DXT5: 21,
27
- CRN_DXT5: 26,
28
- STANDARD_CRN: 27,
29
- } as const;
30
-
31
- /** 由 S3MLayer 在拿到 GL 上下文后注入,用于斜视角下保持纹理清晰 */
32
- let maxAnisotropy = 1;
33
-
34
- export function setMaxAnisotropy(n: number): void {
35
- maxAnisotropy = Math.max(1, n | 0);
36
- }
37
-
38
- export interface DecodedTexture {
39
- texture: THREE.Texture | null;
40
- /** 实际走的解码路径,`unsupported(26)` 这类值会进 stats.skipped */
41
- decoded: string;
42
- }
43
-
44
- function configure<T extends THREE.Texture>(tex: T, srgb = true): T {
45
- tex.wrapS = THREE.RepeatWrapping;
46
- tex.wrapT = THREE.RepeatWrapping;
47
- tex.magFilter = THREE.LinearFilter;
48
- tex.minFilter = THREE.LinearMipmapLinearFilter;
49
- tex.generateMipmaps = true;
50
- // 建筑立面大多是斜视角,各向异性过滤对清晰度的提升比提高分辨率更直接
51
- tex.anisotropy = maxAnisotropy;
52
- if (srgb) tex.colorSpace = THREE.SRGBColorSpace;
53
- tex.flipY = false;
54
- tex.needsUpdate = true;
55
- return tex;
56
- }
57
-
58
- /** 解 DXT 到 RGBA8。DXT5 是 1 字节/像素,DXT1 是 0.5 字节/像素。 */
59
- /** DXT 解码只需要尺寸 / 格式 / 字节,CRN 转码出来的结果也走这条路 */
60
- function decodeDXT(tex: Pick<S3MTexture, 'width' | 'height' | 'pixelFormat' | 'data'>): THREE.DataTexture {
61
- const { width, height, pixelFormat, data } = tex;
62
- const out = new Uint8Array(width * height * 4);
63
- DXTTextureDecode.decode(out, width, height, data, pixelFormat);
64
- return new THREE.DataTexture(out, width, height, THREE.RGBAFormat, THREE.UnsignedByteType);
65
- }
66
-
67
- function fromRawRGB(tex: S3MTexture): THREE.DataTexture {
68
- const { width, height, pixelFormat, data } = tex;
69
- const out = new Uint8Array(width * height * 4);
70
- // RGBA / BGRA / ARGB / ABGR 都是 4 字节,前两个是「尾字节是 alpha」,后两个是「首字节是 alpha」
71
- const hasAlpha =
72
- pixelFormat === PixelFormat.RGBA ||
73
- pixelFormat === PixelFormat.BGRA ||
74
- pixelFormat === PixelFormat.ARGB ||
75
- pixelFormat === PixelFormat.ABGR;
76
- const swapRB = pixelFormat === PixelFormat.BGR || pixelFormat === PixelFormat.BGRA;
77
- const alphaFirst = pixelFormat === PixelFormat.ARGB || pixelFormat === PixelFormat.ABGR;
78
- const src = hasAlpha ? 4 : 3;
79
- for (let i = 0, s = 0, d = 0; i < width * height; i++, s += src, d += 4) {
80
- const p = alphaFirst ? s + 1 : s;
81
- out[d] = swapRB ? data[p + 2] : data[p];
82
- out[d + 1] = data[p + 1];
83
- out[d + 2] = swapRB ? data[p] : data[p + 2];
84
- out[d + 3] = hasAlpha ? data[alphaFirst ? s : s + 3] : 255;
85
- }
86
- return new THREE.DataTexture(out, width, height, THREE.RGBAFormat, THREE.UnsignedByteType);
87
- }
88
-
89
- export async function createTexture(tex: S3MTexture | null | undefined): Promise<DecodedTexture> {
90
- if (!tex || !tex.width || !tex.height) return { texture: null, decoded: 'empty' };
91
-
92
- // 判定只能看 compressType:v3.00 的 CRN 纹理 pixelFormat 是 13(ABGR)、
93
- // 行标的 CRN 是 32856(GL_RGBA8),都不是 26 / 27(见 core/TextureFormat.ts)
94
- const kind = resolveTextureKind(tex.compressType ?? 0, tex.pixelFormat);
95
-
96
- if (kind === 'crn') {
97
- try {
98
- const crn = await transcodeCRN(tex.data);
99
- const decoded = decodeDXT({
100
- width: crn.width,
101
- height: crn.height,
102
- pixelFormat: crn.dxtFormat === 1 ? 17 : 21,
103
- data: crn.data,
104
- });
105
- return { texture: configure(decoded), decoded: `crn->dxt${crn.dxtFormat}` };
106
- } catch (e) {
107
- // 一张纹理坏了不该带崩整个瓦片,如实上报并降级成无贴图
108
- console.warn('[s3m] CRN 转码失败:', e);
109
- return { texture: null, decoded: 'crn(failed)' };
110
- }
111
- }
112
-
113
- if (kind === 'dxt1' || kind === 'dxt3' || kind === 'dxt4' || kind === 'dxt5') {
114
- return {
115
- texture: configure(decodeDXT({ ...tex, pixelFormat: dxtPixelFormatOf(kind) })),
116
- decoded: kind,
117
- };
118
- }
119
-
120
- if (kind === 'webp') {
121
- const blob = new Blob([tex.data as BlobPart], { type: 'image/webp' });
122
- const bmp = await createImageBitmap(blob);
123
- const t = new THREE.Texture(bmp);
124
- t.needsUpdate = true;
125
- return { texture: configure(t), decoded: 'webp' };
126
- }
127
-
128
- if (kind === 'raw') {
129
- return { texture: configure(fromRawRGB(tex)), decoded: 'raw' };
130
- }
131
-
132
- // KTX2 / ETC 需要额外解码器,先如实上报而不是静默出错
133
- return { texture: null, decoded: `unsupported(${tex.compressType}/${tex.pixelFormat})` };
134
- }
1
+ /**
2
+ * S3M 纹理 → three.js Texture
3
+ *
4
+ * S3M 的纹理是「裸块」:没有容器头,只有 width/height/compressType/dataSize/pixelFormat + 原始字节。
5
+ * 该走哪条解码路径由 compressType 决定(见 core/TextureFormat.ts):DXT 族直接解块,
6
+ * CRN(26 / 27 / 38001 / 50000)先用 crunch wasm 转码回 DXT,WEBP 交给浏览器,
7
+ * RGB 系逐像素拷贝。
8
+ */
9
+ import * as THREE from 'three';
10
+ import DXTTextureDecode from '../core/DXTTextureDecode.js';
11
+ import { transcodeCRN } from '../core/CRNDecode.js';
12
+ import { dxtPixelFormatOf, resolveTextureKind } from '../core/TextureFormat.js';
13
+ import type { S3MTexture } from '../core/types.js';
14
+
15
+ export const PixelFormat = {
16
+ RGB: 10,
17
+ BGR: 11,
18
+ ARGB: 12,
19
+ ABGR: 13,
20
+ BGRA: 14,
21
+ RGBA: 28,
22
+ WEBP: 25,
23
+ DXT1: 17,
24
+ DXT3: 19,
25
+ DXT4: 20,
26
+ DXT5: 21,
27
+ CRN_DXT5: 26,
28
+ STANDARD_CRN: 27,
29
+ } as const;
30
+
31
+ /** 由 S3MLayer 在拿到 GL 上下文后注入,用于斜视角下保持纹理清晰 */
32
+ let maxAnisotropy = 1;
33
+
34
+ export function setMaxAnisotropy(n: number): void {
35
+ maxAnisotropy = Math.max(1, n | 0);
36
+ }
37
+
38
+ export interface DecodedTexture {
39
+ texture: THREE.Texture | null;
40
+ /** 实际走的解码路径,`unsupported(26)` 这类值会进 stats.skipped */
41
+ decoded: string;
42
+ }
43
+
44
+ function configure<T extends THREE.Texture>(tex: T, srgb = true): T {
45
+ tex.wrapS = THREE.RepeatWrapping;
46
+ tex.wrapT = THREE.RepeatWrapping;
47
+ tex.magFilter = THREE.LinearFilter;
48
+ tex.minFilter = THREE.LinearMipmapLinearFilter;
49
+ tex.generateMipmaps = true;
50
+ // 建筑立面大多是斜视角,各向异性过滤对清晰度的提升比提高分辨率更直接
51
+ tex.anisotropy = maxAnisotropy;
52
+ if (srgb) tex.colorSpace = THREE.SRGBColorSpace;
53
+ tex.flipY = false;
54
+ tex.needsUpdate = true;
55
+ return tex;
56
+ }
57
+
58
+ /** 解 DXT 到 RGBA8。DXT5 是 1 字节/像素,DXT1 是 0.5 字节/像素。 */
59
+ /** DXT 解码只需要尺寸 / 格式 / 字节,CRN 转码出来的结果也走这条路 */
60
+ function decodeDXT(tex: Pick<S3MTexture, 'width' | 'height' | 'pixelFormat' | 'data'>): THREE.DataTexture {
61
+ const { width, height, pixelFormat, data } = tex;
62
+ const out = new Uint8Array(width * height * 4);
63
+ DXTTextureDecode.decode(out, width, height, data, pixelFormat);
64
+ return new THREE.DataTexture(out, width, height, THREE.RGBAFormat, THREE.UnsignedByteType);
65
+ }
66
+
67
+ function fromRawRGB(tex: S3MTexture): THREE.DataTexture {
68
+ const { width, height, pixelFormat, data } = tex;
69
+ const out = new Uint8Array(width * height * 4);
70
+ // RGBA / BGRA / ARGB / ABGR 都是 4 字节,前两个是「尾字节是 alpha」,后两个是「首字节是 alpha」
71
+ const hasAlpha =
72
+ pixelFormat === PixelFormat.RGBA ||
73
+ pixelFormat === PixelFormat.BGRA ||
74
+ pixelFormat === PixelFormat.ARGB ||
75
+ pixelFormat === PixelFormat.ABGR;
76
+ const swapRB = pixelFormat === PixelFormat.BGR || pixelFormat === PixelFormat.BGRA;
77
+ const alphaFirst = pixelFormat === PixelFormat.ARGB || pixelFormat === PixelFormat.ABGR;
78
+ const src = hasAlpha ? 4 : 3;
79
+ for (let i = 0, s = 0, d = 0; i < width * height; i++, s += src, d += 4) {
80
+ const p = alphaFirst ? s + 1 : s;
81
+ out[d] = swapRB ? data[p + 2] : data[p];
82
+ out[d + 1] = data[p + 1];
83
+ out[d + 2] = swapRB ? data[p] : data[p + 2];
84
+ out[d + 3] = hasAlpha ? data[alphaFirst ? s : s + 3] : 255;
85
+ }
86
+ return new THREE.DataTexture(out, width, height, THREE.RGBAFormat, THREE.UnsignedByteType);
87
+ }
88
+
89
+ export async function createTexture(tex: S3MTexture | null | undefined): Promise<DecodedTexture> {
90
+ if (!tex || !tex.width || !tex.height) return { texture: null, decoded: 'empty' };
91
+
92
+ // 判定只能看 compressType:v3.00 的 CRN 纹理 pixelFormat 是 13(ABGR)、
93
+ // 行标的 CRN 是 32856(GL_RGBA8),都不是 26 / 27(见 core/TextureFormat.ts)
94
+ const kind = resolveTextureKind(tex.compressType ?? 0, tex.pixelFormat);
95
+
96
+ if (kind === 'crn') {
97
+ try {
98
+ const crn = await transcodeCRN(tex.data);
99
+ const decoded = decodeDXT({
100
+ width: crn.width,
101
+ height: crn.height,
102
+ pixelFormat: crn.dxtFormat === 1 ? 17 : 21,
103
+ data: crn.data,
104
+ });
105
+ return { texture: configure(decoded), decoded: `crn->dxt${crn.dxtFormat}` };
106
+ } catch (e) {
107
+ // 一张纹理坏了不该带崩整个瓦片,如实上报并降级成无贴图
108
+ console.warn('[s3m] CRN 转码失败:', e);
109
+ return { texture: null, decoded: 'crn(failed)' };
110
+ }
111
+ }
112
+
113
+ if (kind === 'dxt1' || kind === 'dxt3' || kind === 'dxt4' || kind === 'dxt5') {
114
+ return {
115
+ texture: configure(decodeDXT({ ...tex, pixelFormat: dxtPixelFormatOf(kind) })),
116
+ decoded: kind,
117
+ };
118
+ }
119
+
120
+ if (kind === 'webp') {
121
+ const blob = new Blob([tex.data as BlobPart], { type: 'image/webp' });
122
+ const bmp = await createImageBitmap(blob);
123
+ const t = new THREE.Texture(bmp);
124
+ t.needsUpdate = true;
125
+ return { texture: configure(t), decoded: 'webp' };
126
+ }
127
+
128
+ if (kind === 'raw') {
129
+ return { texture: configure(fromRawRGB(tex)), decoded: 'raw' };
130
+ }
131
+
132
+ // KTX2 / ETC 需要额外解码器,先如实上报而不是静默出错
133
+ return { texture: null, decoded: `unsupported(${tex.compressType}/${tex.pixelFormat})` };
134
+ }