s3m-three 0.3.0 → 0.3.1

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 (109) hide show
  1. package/README.md +1 -1
  2. package/dist/browser/s3m-three.iife.ext-three.js +163 -15
  3. package/dist/browser/s3m-three.iife.js +190 -42
  4. package/dist/core/AtlasAlpha.d.ts +31 -0
  5. package/dist/core/AtlasAlpha.d.ts.map +1 -0
  6. package/dist/core/AtlasAlpha.js +82 -0
  7. package/dist/core/AtlasAlpha.js.map +1 -0
  8. package/dist/core/AtlasPack.d.ts +57 -0
  9. package/dist/core/AtlasPack.d.ts.map +1 -0
  10. package/dist/core/AtlasPack.js +81 -0
  11. package/dist/core/AtlasPack.js.map +1 -0
  12. package/dist/core/AtlasUpgrade.d.ts +68 -0
  13. package/dist/core/AtlasUpgrade.d.ts.map +1 -0
  14. package/dist/core/AtlasUpgrade.js +154 -0
  15. package/dist/core/AtlasUpgrade.js.map +1 -0
  16. package/dist/core/DXTTextureDecode.d.ts.map +1 -1
  17. package/dist/core/DXTTextureDecode.js +28 -6
  18. package/dist/core/DXTTextureDecode.js.map +1 -1
  19. package/dist/core/Draco.d.ts +52 -0
  20. package/dist/core/Draco.d.ts.map +1 -0
  21. package/dist/core/Draco.js +323 -0
  22. package/dist/core/Draco.js.map +1 -0
  23. package/dist/core/MeshOpt.d.ts +14 -3
  24. package/dist/core/MeshOpt.d.ts.map +1 -1
  25. package/dist/core/MeshOpt.js +101 -18
  26. package/dist/core/MeshOpt.js.map +1 -1
  27. package/dist/core/S3M1Parser.d.ts +16 -0
  28. package/dist/core/S3M1Parser.d.ts.map +1 -0
  29. package/dist/core/S3M1Parser.js +367 -0
  30. package/dist/core/S3M1Parser.js.map +1 -0
  31. package/dist/core/S3MBParser.d.ts +43 -1
  32. package/dist/core/S3MBParser.d.ts.map +1 -1
  33. package/dist/core/S3MBParser.js +101 -23
  34. package/dist/core/S3MBParser.js.map +1 -1
  35. package/dist/core/S3MBlockParser.d.ts +209 -0
  36. package/dist/core/S3MBlockParser.d.ts.map +1 -0
  37. package/dist/core/S3MBlockParser.js +789 -0
  38. package/dist/core/S3MBlockParser.js.map +1 -0
  39. package/dist/core/TextureFormat.d.ts +0 -1
  40. package/dist/core/TextureFormat.d.ts.map +1 -1
  41. package/dist/core/TextureFormat.js +19 -2
  42. package/dist/core/TextureFormat.js.map +1 -1
  43. package/dist/core/types.d.ts +65 -0
  44. package/dist/core/types.d.ts.map +1 -1
  45. package/dist/index.d.ts +2 -1
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +2 -1
  48. package/dist/index.js.map +1 -1
  49. package/dist/render/DatasetManager.d.ts.map +1 -1
  50. package/dist/render/DatasetManager.js +5 -3
  51. package/dist/render/DatasetManager.js.map +1 -1
  52. package/dist/render/GeometryBuilder.d.ts +2 -0
  53. package/dist/render/GeometryBuilder.d.ts.map +1 -1
  54. package/dist/render/GeometryBuilder.js +185 -16
  55. package/dist/render/GeometryBuilder.js.map +1 -1
  56. package/dist/render/GroundManager.d.ts +57 -1
  57. package/dist/render/GroundManager.d.ts.map +1 -1
  58. package/dist/render/GroundManager.js +149 -21
  59. package/dist/render/GroundManager.js.map +1 -1
  60. package/dist/render/Picker.d.ts.map +1 -1
  61. package/dist/render/Picker.js +12 -4
  62. package/dist/render/Picker.js.map +1 -1
  63. package/dist/render/PointCloudMaterial.d.ts +36 -0
  64. package/dist/render/PointCloudMaterial.d.ts.map +1 -0
  65. package/dist/render/PointCloudMaterial.js +159 -0
  66. package/dist/render/PointCloudMaterial.js.map +1 -0
  67. package/dist/render/S3MViewer.d.ts +19 -0
  68. package/dist/render/S3MViewer.d.ts.map +1 -1
  69. package/dist/render/S3MViewer.js +143 -22
  70. package/dist/render/S3MViewer.js.map +1 -1
  71. package/dist/render/StyleMaterial.d.ts +6 -0
  72. package/dist/render/StyleMaterial.d.ts.map +1 -1
  73. package/dist/render/StyleMaterial.js +125 -2
  74. package/dist/render/StyleMaterial.js.map +1 -1
  75. package/dist/render/TextureFactory.d.ts.map +1 -1
  76. package/dist/render/TextureFactory.js +64 -10
  77. package/dist/render/TextureFactory.js.map +1 -1
  78. package/dist/tileset/S3MTileset.d.ts +152 -1
  79. package/dist/tileset/S3MTileset.d.ts.map +1 -1
  80. package/dist/tileset/S3MTileset.js +669 -53
  81. package/dist/tileset/S3MTileset.js.map +1 -1
  82. package/dist/vendor/crunch_decoder.module.js +12 -18
  83. package/dist/vendor/draco_decoder.module.js +3178 -0
  84. package/docs/API.md +110 -9
  85. package/package.json +1 -1
  86. package/src/core/AtlasAlpha.ts +82 -0
  87. package/src/core/AtlasPack.ts +127 -0
  88. package/src/core/AtlasUpgrade.ts +182 -0
  89. package/src/core/DXTTextureDecode.ts +28 -6
  90. package/src/core/Draco.ts +409 -0
  91. package/src/core/MeshOpt.ts +124 -18
  92. package/src/core/S3M1Parser.ts +430 -0
  93. package/src/core/S3MBParser.ts +112 -21
  94. package/src/core/S3MBlockParser.ts +953 -0
  95. package/src/core/TextureFormat.ts +20 -2
  96. package/src/core/types.ts +63 -0
  97. package/src/index.ts +2 -1
  98. package/src/render/DatasetManager.ts +5 -5
  99. package/src/render/GeometryBuilder.ts +207 -15
  100. package/src/render/GroundManager.ts +174 -20
  101. package/src/render/Picker.ts +13 -4
  102. package/src/render/PointCloudMaterial.ts +174 -0
  103. package/src/render/S3MViewer.ts +140 -24
  104. package/src/render/StyleMaterial.ts +135 -2
  105. package/src/render/TextureFactory.ts +67 -11
  106. package/src/tileset/S3MTileset.ts +701 -58
  107. package/src/vendor/crunch_decoder.module.js +12 -18
  108. package/src/vendor/draco_decoder.module.d.ts +128 -0
  109. package/src/vendor/draco_decoder.module.js +3178 -0
@@ -0,0 +1,52 @@
1
+ /**
2
+ * S3M v3 Draco 几何压缩(Skeleton 的 Draco 分支)
3
+ *
4
+ * 与 MeshOpt 一样,这里的布局不是照规范写的 —— 规范里的 Draco 分支描述和实际数据
5
+ * 对不上,字段顺序是从真实瓦片逐字节标定出来的(两个瓦片 5 个骨架完全一致,
6
+ * 且用 payloadSize / dracoSize / "DRACO" magic / skeletonStreamSize 四重自校验)。
7
+ *
8
+ * 二进制布局(v3.01 实测,偏移以 tag 字段之后为 0):
9
+ * ```
10
+ * u32 payloadSize // 覆盖 [本字段之后 .. 条目末尾 OBB 之前]
11
+ * u32 unknown = 32 // 实测恒 32,语义未知
12
+ * i32 posUniqueId // draco 属性 uid,POSITION;-1 = 无
13
+ * i32 normalUniqueId // -1 = 无
14
+ * i32 colorUniqueId // -1 = 无
15
+ * i32 secondColorUniqueId
16
+ * u32 texCoordCount // 自定义属性个数,实测 3
17
+ * i32 texCoordUniqueIds[texCoordCount] // 各自定义属性的 draco uid
18
+ * u32 vertexAttrCount // 实测恒 0
19
+ * u32 dracoSize
20
+ * byte draco[dracoSize] // 标准 draco 比特流,开头 5 字节是 "DRACO" magic
21
+ * pad4
22
+ * String descript // {"PointCloudClassCode":2,"PointCloudIntensity":1,"VertexWeight":0}
23
+ * pad4
24
+ * f64 obb[12] // ← 由调用方 parseSkeletonEntries 统一按条目末尾读
25
+ * ```
26
+ *
27
+ * descript 的 value 是「自定义属性在 texCoordUniqueIds 里的下标」:
28
+ * VertexWeight→uid2(float 高度)、PointCloudIntensity→uid3、PointCloudClassCode→uid4。
29
+ * uid1 没被 descript 引用(实测是 0~29 的 uint32,用途不明)。
30
+ *
31
+ * 官方 SDK 的 `parseDracoSkeleton` 字段顺序不一样(多了 materialCount,且
32
+ * `version === 3` 的判定对 3.01 瓦片不成立),实际解析不了这个服务,所以以实测为准。
33
+ */
34
+ import type { BinaryReader } from './BinaryReader.js';
35
+ import type { IndexPackage, VertexPackage } from './types.js';
36
+ import type { DracoModule } from '../vendor/draco_decoder.module.js';
37
+ /**
38
+ * 懒加载 Draco 解码器(wasm 内联,约 266KB)。
39
+ * 只有真正遇到 Draco 瓦片时才会编译,标准 / MeshOpt 瓦片不受影响。
40
+ */
41
+ export declare function loadDracoDecoder(): Promise<DracoModule>;
42
+ /**
43
+ * 读取一个 Draco 压缩的 Skeleton。
44
+ *
45
+ * 点云没有索引包(顶点即点),但渲染层是「有 indexPackages 才建几何」,
46
+ * 所以这里合成一个 primitiveType = 1 的空点包,让下游走 THREE.Points 路径。
47
+ *
48
+ * 包围盒不在这里读:调用方 parseSkeletonEntries 统一按 skeletonStreamSize
49
+ * 定位到条目末尾的 OBB,各分支行为保持一致。
50
+ */
51
+ export declare function parseDracoGeometry(r: BinaryReader, vp: VertexPackage): Promise<IndexPackage[]>;
52
+ //# sourceMappingURL=Draco.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Draco.d.ts","sourceRoot":"","sources":["../../src/core/Draco.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAa,YAAY,EAAmB,aAAa,EAAE,MAAM,YAAY,CAAC;AAC1F,OAAO,KAAK,EAAE,WAAW,EAAwC,MAAM,mCAAmC,CAAC;AAgC3G;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAAC,WAAW,CAAC,CAKvD;AAmGD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAgLpG"}
@@ -0,0 +1,323 @@
1
+ /* ------------------------------------------------------------ draco 枚举(本地副本) */
2
+ /** draco::GeometryAttribute::Type */
3
+ const ATTR_POSITION = 0;
4
+ const ATTR_NORMAL = 1;
5
+ const ATTR_COLOR = 2;
6
+ const ATTR_GENERIC = 4;
7
+ /** draco::DataType(DT_INVALID = 0 占位) */
8
+ const DT_UINT8 = 2;
9
+ const DT_UINT16 = 4;
10
+ const DT_UINT32 = 6;
11
+ const DT_FLOAT32 = 9;
12
+ /** draco::EncodedGeometryType */
13
+ const GEOM_POINT_CLOUD = 0;
14
+ const GEOM_TRIANGULAR_MESH = 1;
15
+ const DRACO_MAGIC = 'DRACO';
16
+ /** primitiveType = 1:点云包(渲染层据此走 THREE.Points) */
17
+ const POINT_PRIMITIVE = 1;
18
+ /** 单个 skeleton 的点数上限,防头读错时开出天文数字的缓冲 */
19
+ const MAX_POINTS = 64 * 1024 * 1024;
20
+ /* ------------------------------------------------------------ 解码器 */
21
+ let decoderPromise = null;
22
+ /**
23
+ * 懒加载 Draco 解码器(wasm 内联,约 266KB)。
24
+ * 只有真正遇到 Draco 瓦片时才会编译,标准 / MeshOpt 瓦片不受影响。
25
+ */
26
+ export function loadDracoDecoder() {
27
+ if (!decoderPromise) {
28
+ decoderPromise = import('../vendor/draco_decoder.module.js').then((mod) => mod.default());
29
+ }
30
+ return decoderPromise;
31
+ }
32
+ /**
33
+ * 把 draco 的一个属性读成 JS 数组。
34
+ *
35
+ * 不能直接把 JS TypedArray 传给 `GetAttribute*ForAllPoints`:embind 会把期望
36
+ * 对象的参数原样透传,TypedArray 没有 `.ptr` 就变成 undefined,wasm 侧写不进
37
+ * 数据,函数却照样返回 true(实测取出来全是 0)。只能逐个 GetValue ——
38
+ * 6 千点 × 3 分量实测 3.5ms,可以接受。
39
+ *
40
+ * 量化 / 八面体变换由 draco 解码时自动还原,这里拿到的是最终值。
41
+ */
42
+ function readAttribute(draco, decoder, geometry, attr) {
43
+ const comps = Math.max(1, attr.num_components());
44
+ const n = geometry.num_points() * comps;
45
+ let src;
46
+ switch (attr.data_type()) {
47
+ case DT_FLOAT32:
48
+ src = new draco.DracoFloat32Array();
49
+ if (!decoder.GetAttributeFloatForAllPoints(geometry, attr, src))
50
+ return null;
51
+ break;
52
+ case DT_UINT8:
53
+ src = new draco.DracoUInt8Array();
54
+ if (!decoder.GetAttributeUInt8ForAllPoints(geometry, attr, src))
55
+ return null;
56
+ break;
57
+ case DT_UINT16:
58
+ src = new draco.DracoUInt16Array();
59
+ if (!decoder.GetAttributeUInt16ForAllPoints(geometry, attr, src))
60
+ return null;
61
+ break;
62
+ case DT_UINT32:
63
+ src = new draco.DracoUInt32Array();
64
+ if (!decoder.GetAttributeUInt32ForAllPoints(geometry, attr, src))
65
+ return null;
66
+ break;
67
+ default:
68
+ // int8/int16/int32/float64:真实点云里没见过,对应取值接口也存在,
69
+ // 需要时按上面同款补一行即可
70
+ return null;
71
+ }
72
+ const size = src.size();
73
+ const isFloat = attr.data_type() === DT_FLOAT32;
74
+ let out;
75
+ if (isFloat)
76
+ out = new Float32Array(n);
77
+ else if (attr.data_type() === DT_UINT16)
78
+ out = new Uint16Array(n);
79
+ else if (attr.data_type() === DT_UINT32)
80
+ out = new Uint32Array(n);
81
+ else
82
+ out = new Uint8Array(n);
83
+ for (let i = 0; i < size && i < n; i++)
84
+ out[i] = src.GetValue(i);
85
+ // Draco*Array 是 new 出来的,占 wasm 堆;不解引用会一直涨
86
+ draco.destroy(src);
87
+ return out;
88
+ }
89
+ /* ------------------------------------------------------------ 分类着色 */
90
+ /**
91
+ * LAS / ASPRS 标准分类色带(1~18,其余走兜底灰)。
92
+ *
93
+ * 点云分类服务(3D-PointCloudClassific 这类)本身不带顶点色,颜色全靠分类码。
94
+ * 渲染层的点云路径只认 `color` 属性,所以这里直接把分类码烘成 RGBA8 顶点色,
95
+ * 下游零改动。实测该服务的分类分布是 {5: 高植被, 14: 导线, 15: 输电塔, 18: 高噪声}。
96
+ */
97
+ const CLASS_COLORS = [
98
+ [190, 190, 190], // 0 未分类
99
+ [166, 166, 166], // 1 未分类(旧)
100
+ [139, 119, 101], // 2 地面
101
+ [35, 190, 35], // 3 低植被
102
+ [60, 179, 60], // 4 中植被
103
+ [30, 160, 30], // 5 高植被
104
+ [255, 170, 60], // 6 建筑
105
+ [255, 110, 190], // 7 低噪点
106
+ [190, 190, 190], // 8 关键点
107
+ [60, 120, 255], // 9 水面
108
+ [255, 235, 80], // 10 铁轨
109
+ [255, 200, 120], // 11 路面
110
+ [200, 120, 255], // 12 保留
111
+ [255, 120, 255], // 13 线缆
112
+ [255, 235, 60], // 14 导线
113
+ [255, 200, 40], // 15 输电塔
114
+ [120, 200, 255], // 16 桥面
115
+ [200, 160, 255], // 17 桥墩
116
+ [255, 60, 60], // 18 高噪声
117
+ ];
118
+ /* ------------------------------------------------------------ 骨架 */
119
+ /**
120
+ * 读取一个 Draco 压缩的 Skeleton。
121
+ *
122
+ * 点云没有索引包(顶点即点),但渲染层是「有 indexPackages 才建几何」,
123
+ * 所以这里合成一个 primitiveType = 1 的空点包,让下游走 THREE.Points 路径。
124
+ *
125
+ * 包围盒不在这里读:调用方 parseSkeletonEntries 统一按 skeletonStreamSize
126
+ * 定位到条目末尾的 OBB,各分支行为保持一致。
127
+ */
128
+ export async function parseDracoGeometry(r, vp) {
129
+ const draco = await loadDracoDecoder();
130
+ /* ---- 1. 容器头 ---- */
131
+ const payloadSize = r.u32();
132
+ const payloadStart = r.offset;
133
+ r.u32(); // 实测恒 32,语义未知
134
+ const posUniqueId = r.i32();
135
+ const normalUniqueId = r.i32();
136
+ const colorUniqueId = r.i32();
137
+ r.i32(); // secondColorUniqueId
138
+ const texCoordCount = r.u32();
139
+ if (texCoordCount > 16) {
140
+ throw new Error(`[s3m] Draco 自定义属性个数异常:texCoordCount=${texCoordCount}`);
141
+ }
142
+ const texCoordUniqueIds = [];
143
+ for (let i = 0; i < texCoordCount; i++)
144
+ texCoordUniqueIds.push(r.i32());
145
+ r.u32(); // vertexAttrCount,实测恒 0
146
+ const dracoSize = r.u32();
147
+ if (dracoSize < 16 || dracoSize > r.remaining) {
148
+ throw new Error(`[s3m] Draco 流长度异常:dracoSize=${dracoSize},剩余 ${r.remaining} 字节`);
149
+ }
150
+ const data = r.bytes(dracoSize);
151
+ // 头读错时这里必然对不上,比让 wasm 报一句看不懂的错有用
152
+ if (String.fromCharCode(...data.subarray(0, 5)) !== DRACO_MAGIC) {
153
+ throw new Error('[s3m] Draco 流缺少 "DRACO" magic,容器头解析错位');
154
+ }
155
+ r.align4();
156
+ const descript = r.string();
157
+ r.align4();
158
+ // 自定义属性 uid → 语义名:{"PointCloudClassCode":2,...} 的 value 是 texCoordUniqueIds 下标
159
+ const customNames = new Map();
160
+ try {
161
+ const map = JSON.parse(descript);
162
+ for (const [name, idx] of Object.entries(map)) {
163
+ const uid = texCoordUniqueIds[idx];
164
+ if (uid !== undefined)
165
+ customNames.set(uid, name);
166
+ }
167
+ }
168
+ catch {
169
+ // descript 不是 JSON 也不影响渲染,只是自定义属性没名字
170
+ }
171
+ vp.customVertexAttribute = descript ? safeJson(descript) : undefined;
172
+ if (payloadSize > 0) {
173
+ const consumed = r.offset - payloadStart;
174
+ if (consumed !== payloadSize) {
175
+ // 条目末尾由 streamSize 兜着,错位不会波及后面的骨架,只提示
176
+ console.warn(`[s3m] Draco 载荷长度不符:声明 ${payloadSize},实读 ${consumed}`);
177
+ }
178
+ }
179
+ /* ---- 2. 解码 ---- */
180
+ const decoder = new draco.Decoder();
181
+ const geometry = new draco.PointCloud();
182
+ try {
183
+ const geomType = decoder.GetEncodedGeometryType(data);
184
+ if (geomType !== GEOM_POINT_CLOUD) {
185
+ throw new Error(`[s3m] Draco ${geomType === GEOM_TRIANGULAR_MESH ? '三角网格' : '未知'} 几何暂未实现(仅支持点云)`);
186
+ }
187
+ const status = decoder.DecodeArrayToPointCloud(data, data.length, geometry);
188
+ if (!status.ok())
189
+ throw new Error(`[s3m] Draco 解码失败:${status.error_msg()}`);
190
+ const numPoints = geometry.num_points();
191
+ if (!Number.isInteger(numPoints) || numPoints < 0 || numPoints > MAX_POINTS) {
192
+ throw new Error(`[s3m] Draco 点数异常:${numPoints}`);
193
+ }
194
+ if (numPoints === 0) {
195
+ vp.verticesCount = 0;
196
+ return [pointPack()];
197
+ }
198
+ /* ---- 3. 属性装配 ---- */
199
+ let classCode = null;
200
+ for (let i = 0; i < geometry.num_attributes(); i++) {
201
+ const attr = decoder.GetAttribute(geometry, i);
202
+ const uid = attr.unique_id();
203
+ const comps = Math.max(1, attr.num_components());
204
+ if (uid === posUniqueId || attr.attribute_type() === ATTR_POSITION) {
205
+ if (attr.data_type() !== DT_FLOAT32) {
206
+ throw new Error(`[s3m] Draco 位置属性不是 float32(dt=${attr.data_type()})`);
207
+ }
208
+ const raw = readAttribute(draco, decoder, geometry, attr);
209
+ if (!raw)
210
+ throw new Error('[s3m] Draco 位置属性读取失败');
211
+ // draco 偶尔按 3 分量以上的格式存位置,取前 3 个
212
+ const out = new Float32Array(numPoints * 3);
213
+ for (let p = 0; p < numPoints; p++) {
214
+ out[p * 3] = raw[p * comps] ?? 0;
215
+ out[p * 3 + 1] = raw[p * comps + 1] ?? 0;
216
+ out[p * 3 + 2] = raw[p * comps + 2] ?? 0;
217
+ }
218
+ pushAttr(vp, 'position', new Uint8Array(out.buffer), 3, 12, 0, undefined, 'f32');
219
+ continue;
220
+ }
221
+ if (uid === colorUniqueId || attr.attribute_type() === ATTR_COLOR) {
222
+ const raw = readAttribute(draco, decoder, geometry, attr);
223
+ if (raw) {
224
+ const out = new Uint8Array(numPoints * 4);
225
+ const isFloat = raw instanceof Float32Array;
226
+ for (let p = 0; p < numPoints; p++) {
227
+ const o = p * comps;
228
+ if (isFloat) {
229
+ out[p * 4] = clamp255(raw[o] ?? 1);
230
+ out[p * 4 + 1] = clamp255(raw[o + 1] ?? 1);
231
+ out[p * 4 + 2] = clamp255(raw[o + 2] ?? 1);
232
+ }
233
+ else {
234
+ out[p * 4] = raw[o] ?? 255;
235
+ out[p * 4 + 1] = raw[o + 1] ?? 255;
236
+ out[p * 4 + 2] = raw[o + 2] ?? 255;
237
+ }
238
+ out[p * 4 + 3] = comps >= 4 && !isFloat ? (raw[o + 3] ?? 255) : 255;
239
+ }
240
+ pushAttr(vp, 'color', out, 4, 4, 0, undefined, 'u8');
241
+ }
242
+ continue;
243
+ }
244
+ if (uid === normalUniqueId || attr.attribute_type() === ATTR_NORMAL) {
245
+ // 点云不需要法线,八面体编码由 draco 解码时自动还原,这里直接跳过
246
+ continue;
247
+ }
248
+ // 自定义属性:只挑分类码,其余(高度 / 强度)渲染层用不上
249
+ const name = customNames.get(uid);
250
+ if (name === 'PointCloudClassCode' && comps === 1) {
251
+ const raw = readAttribute(draco, decoder, geometry, attr);
252
+ if (raw && !(raw instanceof Float32Array)) {
253
+ classCode = new Uint8Array(numPoints);
254
+ for (let p = 0; p < numPoints; p++)
255
+ classCode[p] = raw[p] ?? 0;
256
+ }
257
+ }
258
+ }
259
+ if (vp.attrLocation.position === undefined) {
260
+ throw new Error('[s3m] Draco 点云缺少 position 属性');
261
+ }
262
+ // 数据自带顶点色优先;没有色但有分类码时,用分类色带烘一份 RGBA8
263
+ if (vp.attrLocation.color === undefined && classCode) {
264
+ const out = new Uint8Array(numPoints * 4);
265
+ for (let p = 0; p < numPoints; p++) {
266
+ const c = CLASS_COLORS[classCode[p]] ?? CLASS_COLORS[0];
267
+ out[p * 4] = c[0];
268
+ out[p * 4 + 1] = c[1];
269
+ out[p * 4 + 2] = c[2];
270
+ out[p * 4 + 3] = 255;
271
+ }
272
+ pushAttr(vp, 'color', out, 4, 4, 0, undefined, 'u8');
273
+ }
274
+ vp.verticesCount = numPoints;
275
+ return [pointPack()];
276
+ }
277
+ finally {
278
+ // embind 对象占的是 wasm 堆,不显式释放会一直涨
279
+ try {
280
+ draco.destroy(decoder);
281
+ draco.destroy(geometry);
282
+ }
283
+ catch {
284
+ // 某些构建没有 destroy,忽略
285
+ }
286
+ }
287
+ }
288
+ /* ------------------------------------------------------------ 工具 */
289
+ /** 点云包:索引只是点序号,交给渲染层走 drawRange */
290
+ function pointPack() {
291
+ return {
292
+ indexCount: 0,
293
+ indexType: 0,
294
+ useIndex: 0,
295
+ primitiveType: POINT_PRIMITIVE,
296
+ indices: null,
297
+ passNames: [],
298
+ };
299
+ }
300
+ function pushAttr(vp, semantic, data, components, stride, offset = 0, divisor, dtype = 'f32') {
301
+ vp.attrLocation[semantic] = vp.vertexAttributes.length;
302
+ vp.vertexAttributes.push({
303
+ semantic,
304
+ data,
305
+ components,
306
+ stride,
307
+ offsetInBytes: offset,
308
+ divisor,
309
+ dtype,
310
+ });
311
+ }
312
+ function clamp255(v) {
313
+ return v <= 0 ? 0 : v >= 1 ? 255 : Math.round(v * 255);
314
+ }
315
+ function safeJson(s) {
316
+ try {
317
+ return JSON.parse(s);
318
+ }
319
+ catch {
320
+ return s;
321
+ }
322
+ }
323
+ //# sourceMappingURL=Draco.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Draco.js","sourceRoot":"","sources":["../../src/core/Draco.ts"],"names":[],"mappings":"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"}
@@ -55,12 +55,13 @@ export interface MeshOptBlockInfo {
55
55
  }>;
56
56
  }
57
57
  /**
58
- * 读取一个 MeshOpt 压缩的 Skeleton。
58
+ * 读取一个 MeshOpt 压缩的 Skeleton,以及**内嵌在块之间的索引包**。
59
59
  *
60
60
  * 二进制布局(v3.01 实测):
61
61
  * ```
62
62
  * uint32 compressOptions // 0x13 = Position | Normal | TexCoord
63
- * uint32 blockCount // 实测恒为 1
63
+ * uint32 blockCount // 实测 1 或 2
64
+ * ↑ 下面整段重复 blockCount 次:
64
65
  * uint32 vertexCount
65
66
  * float32 vertCompressConstant
66
67
  * float64 minVerticesValue.xyz
@@ -76,9 +77,19 @@ export interface MeshOptBlockInfo {
76
77
  * pad4
77
78
  * String descript // {"TextureCoordMatrix":1,"VertexWeight":0}
78
79
  * pad4
80
+ * IndexPackage[] // ← 每块各带一份,不是所有块之后再统一放
79
81
  * ```
82
+ *
83
+ * 注意最后一行:blockCount > 1 时索引包夹在块与块之间。之前按「读完所有块再读索引包」
84
+ * 处理,会把第一块的索引包当成第二块的块头,读出天文数字的 compressedSize。
85
+ *
86
+ * 多块是同一份几何的拆分,这里把各块属性按顺序拼成一条流,并把各块索引加上
87
+ * 顶点基数偏移,对外仍是一份顶点缓冲 + 多组索引包,下游无需改动。
80
88
  */
81
- export declare function parseMeshOptSkeleton(r: BinaryReader, vp: VertexPackage): Promise<MeshOptBlockInfo>;
89
+ export declare function parseMeshOptGeometry(r: BinaryReader, vp: VertexPackage): Promise<{
90
+ info: MeshOptBlockInfo;
91
+ indexPackages: IndexPackage[];
92
+ }>;
82
93
  /**
83
94
  * MeshOpt 骨架的索引包。
84
95
  * 与标准骨架相比只多了一个 `compressedSize`,其余字段顺序一致;
@@ -1 +1 @@
1
- {"version":3,"file":"MeshOpt.d.ts","sourceRoot":"","sources":["../../src/core/MeshOpt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAmB,aAAa,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAE1E,sBAAsB;AACtB,eAAO,MAAM,eAAe;;;;;;;;;;;;;CAalB,CAAC;AAOX;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,cAAc,CAAC,CAU5D;AAuFD,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB;IACjB,aAAa,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACvC,cAAc,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACxC,qBAAqB;IACrB,OAAO,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrE;AAiBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA0JxG;AAID;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA0CxF"}
1
+ {"version":3,"file":"MeshOpt.d.ts","sourceRoot":"","sources":["../../src/core/MeshOpt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAmB,aAAa,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAE1E,sBAAsB;AACtB,eAAO,MAAM,eAAe;;;;;;;;;;;;;CAalB,CAAC;AAOX;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,cAAc,CAAC,CAU5D;AAuFD,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB;IACjB,aAAa,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACvC,cAAc,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACxC,qBAAqB;IACrB,OAAO,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrE;AA4DD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,oBAAoB,CACxC,CAAC,EAAE,YAAY,EACf,EAAE,EAAE,aAAa,GAChB,OAAO,CAAC;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,aAAa,EAAE,YAAY,EAAE,CAAA;CAAE,CAAC,CA8MpE;AAID;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA0CxF"}
@@ -116,13 +116,41 @@ const SEMANTIC_NAME = {
116
116
  [MeshOptSemantic.GS_SCALE]: 'gsScale',
117
117
  [MeshOptSemantic.GS_ROTATION]: 'gsRotation',
118
118
  };
119
+ function pushSink(sinks, semantic, dtype, components, chunk) {
120
+ let sink = sinks.get(semantic);
121
+ if (!sink) {
122
+ sink = { dtype, components, chunks: [], length: 0 };
123
+ sinks.set(semantic, sink);
124
+ }
125
+ sink.chunks.push(chunk);
126
+ sink.length += chunk.length;
127
+ }
119
128
  /**
120
- * 读取一个 MeshOpt 压缩的 Skeleton。
129
+ * 索引是块内局部编号(实测 max index 恰为所在块的 vertexCount - 1),
130
+ * 多块合并成一份顶点缓冲后要整体加上该块的顶点基数。
131
+ *
132
+ * 用 DataView 而不是 Uint32Array:未压缩索引那段是底层 buffer 的 subarray,
133
+ * byteOffset 未必 4 字节对齐,直接建 Uint32Array 会抛 RangeError。
134
+ */
135
+ function offsetIndices(packs, base) {
136
+ for (const pack of packs) {
137
+ const buf = pack.indices;
138
+ if (!buf || pack.indexCount <= 0 || buf.byteLength < pack.indexCount * 4)
139
+ continue;
140
+ const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
141
+ for (let i = 0; i < pack.indexCount; i++) {
142
+ dv.setUint32(i * 4, dv.getUint32(i * 4, true) + base, true);
143
+ }
144
+ }
145
+ }
146
+ /**
147
+ * 读取一个 MeshOpt 压缩的 Skeleton,以及**内嵌在块之间的索引包**。
121
148
  *
122
149
  * 二进制布局(v3.01 实测):
123
150
  * ```
124
151
  * uint32 compressOptions // 0x13 = Position | Normal | TexCoord
125
- * uint32 blockCount // 实测恒为 1
152
+ * uint32 blockCount // 实测 1 或 2
153
+ * ↑ 下面整段重复 blockCount 次:
126
154
  * uint32 vertexCount
127
155
  * float32 vertCompressConstant
128
156
  * float64 minVerticesValue.xyz
@@ -138,13 +166,23 @@ const SEMANTIC_NAME = {
138
166
  * pad4
139
167
  * String descript // {"TextureCoordMatrix":1,"VertexWeight":0}
140
168
  * pad4
169
+ * IndexPackage[] // ← 每块各带一份,不是所有块之后再统一放
141
170
  * ```
171
+ *
172
+ * 注意最后一行:blockCount > 1 时索引包夹在块与块之间。之前按「读完所有块再读索引包」
173
+ * 处理,会把第一块的索引包当成第二块的块头,读出天文数字的 compressedSize。
174
+ *
175
+ * 多块是同一份几何的拆分,这里把各块属性按顺序拼成一条流,并把各块索引加上
176
+ * 顶点基数偏移,对外仍是一份顶点缓冲 + 多组索引包,下游无需改动。
142
177
  */
143
- export async function parseMeshOptSkeleton(r, vp) {
178
+ export async function parseMeshOptGeometry(r, vp) {
144
179
  const decoder = await loadMeshoptDecoder();
145
180
  const compressOptions = r.u32();
146
181
  vp.compressOptions = compressOptions;
147
182
  const blockCount = r.u32();
183
+ if (blockCount > 64) {
184
+ throw new Error(`[s3m] MeshOpt 块数异常:blockCount=${blockCount}`);
185
+ }
148
186
  const info = {
149
187
  vertexCount: 0,
150
188
  compressOptions,
@@ -160,6 +198,10 @@ export async function parseMeshOptSkeleton(r, vp) {
160
198
  ],
161
199
  streams: [],
162
200
  };
201
+ const sinks = new Map();
202
+ const indexPackages = [];
203
+ let baseVertex = 0;
204
+ let hasCustomAttribute = false;
163
205
  for (let bi = 0; bi < blockCount; bi++) {
164
206
  const vertexCount = r.u32();
165
207
  const vertScale = r.f32();
@@ -178,14 +220,11 @@ export async function parseMeshOptSkeleton(r, vp) {
178
220
  texCoordOffset.push([a, b]);
179
221
  }
180
222
  const attrCount = r.i32();
181
- const decodeThisBlock = bi === 0;
182
- if (decodeThisBlock) {
183
- info.vertexCount = vertexCount;
184
- info.vertScale = vertScale;
223
+ if (bi === 0) {
185
224
  info.minVertices = [minX, minY, minZ];
225
+ info.vertScale = vertScale;
186
226
  info.texCoordScale = texCoordScale;
187
227
  info.texCoordOffset = texCoordOffset;
188
- vp.verticesCount = vertexCount;
189
228
  }
190
229
  for (let ai = 0; ai < attrCount; ai++) {
191
230
  const semantic = r.i32();
@@ -199,8 +238,6 @@ export async function parseMeshOptSkeleton(r, vp) {
199
238
  const compressedSize = r.i32();
200
239
  const data = r.bytes(compressedSize);
201
240
  r.align4();
202
- if (!decodeThisBlock)
203
- continue;
204
241
  // 切线流我们不用(渲染里没有法线贴图通道),解不出来也不该拖垮整个瓦片:
205
242
  // 真实服务里带 PBR 的数据(如 SubwayStationCombined)都带这条流,之前直接抛错导致整套数据不可见
206
243
  if (semantic === MeshOptSemantic.TANGENT) {
@@ -235,7 +272,7 @@ export async function parseMeshOptSkeleton(r, vp) {
235
272
  pos[i * 3 + 1] = minY + u16[i * 4 + 1] * vertScale;
236
273
  pos[i * 3 + 2] = minZ + u16[i * 4 + 2] * vertScale;
237
274
  }
238
- vp.vertexAttributes.push(f32Attribute('position', pos, 3));
275
+ pushSink(sinks, 'position', 'f32', 3, pos);
239
276
  }
240
277
  else if (semantic === MeshOptSemantic.NORMAL) {
241
278
  const i16 = new Int16Array(out.buffer, out.byteOffset, vertexCount * 2);
@@ -243,13 +280,13 @@ export async function parseMeshOptSkeleton(r, vp) {
243
280
  for (let i = 0; i < vertexCount; i++) {
244
281
  octDecode(i16[i * 2] / 32767, i16[i * 2 + 1] / 32767, nrm, i * 3);
245
282
  }
246
- vp.vertexAttributes.push(f32Attribute('normal', nrm, 3));
283
+ pushSink(sinks, 'normal', 'f32', 3, nrm);
247
284
  }
248
285
  else if (semantic === MeshOptSemantic.COLOR) {
249
- vp.vertexAttributes.push(u8Attribute('color', out, 4));
286
+ pushSink(sinks, 'color', 'u8', 4, out);
250
287
  }
251
288
  else if (semantic === MeshOptSemantic.COLOR1) {
252
- vp.vertexAttributes.push(u8Attribute('color2', out, 4));
289
+ pushSink(sinks, 'color2', 'u8', 4, out);
253
290
  }
254
291
  else if (semantic === MeshOptSemantic.TEXCOORD0 || semantic === MeshOptSemantic.TEXCOORD1) {
255
292
  const slot = semantic === MeshOptSemantic.TEXCOORD0 ? 0 : 1;
@@ -268,13 +305,23 @@ export async function parseMeshOptSkeleton(r, vp) {
268
305
  uv[i * 2 + 1] = offset[1] + u16[i * 2 + 1] * scale[1];
269
306
  }
270
307
  }
271
- vp.vertexAttributes.push(f32Attribute(slot === 0 ? 'uv' : 'uv2', uv, 2));
308
+ pushSink(sinks, slot === 0 ? 'uv' : 'uv2', 'f32', 2, uv);
309
+ }
310
+ else if (semantic === MeshOptSemantic.CUSTOM0 ||
311
+ semantic === MeshOptSemantic.CUSTOM1 ||
312
+ semantic === MeshOptSemantic.GS_SCALE ||
313
+ semantic === MeshOptSemantic.GS_ROTATION) {
314
+ // 自定义属性流 / 高斯泼溅的逐点参数:原样保留解码结果(每点 stride 字节),
315
+ // 语义由 vp.customVertexAttribute 的名字→槽位映射给出。
316
+ // 以前这里直接丢掉,导致 3DGaussianSplatting 数据只剩「点 + 颜色」,
317
+ // 上层永远拿不到泼溅尺寸与朝向。
318
+ pushSink(sinks, SEMANTIC_NAME[semantic], 'u8', stride, out);
272
319
  }
273
- // tangent / custom / GS 系列暂不使用
274
320
  }
275
321
  const descript = r.string();
276
322
  r.align4();
277
- if (decodeThisBlock && descript) {
323
+ if (descript && !hasCustomAttribute) {
324
+ hasCustomAttribute = true;
278
325
  try {
279
326
  vp.customVertexAttribute = JSON.parse(descript);
280
327
  }
@@ -282,12 +329,48 @@ export async function parseMeshOptSkeleton(r, vp) {
282
329
  vp.customVertexAttribute = descript;
283
330
  }
284
331
  }
332
+ // 每块后面紧跟自己的索引包(blockCount = 1 时等价于「骨架之后读索引包」)
333
+ const packs = await parseMeshOptIndexPackages(r);
334
+ if (baseVertex > 0)
335
+ offsetIndices(packs, baseVertex);
336
+ indexPackages.push(...packs);
337
+ baseVertex += vertexCount;
338
+ }
339
+ // blockCount = 0 的老数据:索引包仍然跟在骨架后面
340
+ if (blockCount === 0) {
341
+ indexPackages.push(...(await parseMeshOptIndexPackages(r)));
342
+ }
343
+ info.vertexCount = baseVertex;
344
+ vp.verticesCount = baseVertex;
345
+ // 留下原始流清单:诊断用,也是接自定义属性 / 高斯泼溅属性的入口
346
+ vp.meshOptInfo = info;
347
+ // 跨块拼接:属性流长度按理各块一致,缺块时补 0,宁可少一块颜色也不整块不可见
348
+ for (const [semantic, sink] of sinks) {
349
+ const expected = baseVertex * sink.components;
350
+ if (sink.dtype === 'f32') {
351
+ const out = new Float32Array(expected);
352
+ let off = 0;
353
+ for (const chunk of sink.chunks) {
354
+ out.set(chunk, off);
355
+ off += chunk.length;
356
+ }
357
+ vp.vertexAttributes.push(f32Attribute(semantic, out, sink.components));
358
+ }
359
+ else {
360
+ const out = new Uint8Array(expected);
361
+ let off = 0;
362
+ for (const chunk of sink.chunks) {
363
+ out.set(chunk, off);
364
+ off += chunk.length;
365
+ }
366
+ vp.vertexAttributes.push(u8Attribute(semantic, out, sink.components));
367
+ }
285
368
  }
286
369
  // attrLocation 供老接口按名字查索引
287
370
  for (let i = 0; i < vp.vertexAttributes.length; i++) {
288
371
  vp.attrLocation[`a${vp.vertexAttributes[i].semantic}`] = i;
289
372
  }
290
- return info;
373
+ return { info, indexPackages };
291
374
  }
292
375
  /* ------------------------------------------------------------------ 索引包 */
293
376
  /**