s3m-maplibre 0.1.0 → 0.2.4

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 (38) hide show
  1. package/README.md +2 -2
  2. package/dist/core/types.d.ts +27 -0
  3. package/dist/core/types.d.ts.map +1 -1
  4. package/dist/index.d.ts +2 -2
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/render/GeometryBuilder.d.ts +7 -0
  9. package/dist/render/GeometryBuilder.d.ts.map +1 -1
  10. package/dist/render/GeometryBuilder.js +310 -18
  11. package/dist/render/GeometryBuilder.js.map +1 -1
  12. package/dist/render/S3MLayer.d.ts +84 -4
  13. package/dist/render/S3MLayer.d.ts.map +1 -1
  14. package/dist/render/S3MLayer.js +264 -9
  15. package/dist/render/S3MLayer.js.map +1 -1
  16. package/dist/render/StyleMaterial.d.ts +38 -0
  17. package/dist/render/StyleMaterial.d.ts.map +1 -1
  18. package/dist/render/StyleMaterial.js +150 -8
  19. package/dist/render/StyleMaterial.js.map +1 -1
  20. package/dist/vendor/meshopt_decoder.module.js +112 -112
  21. package/docs/API.md +0 -2
  22. package/package.json +87 -78
  23. package/src/core/S3MBParser.ts +586 -586
  24. package/src/core/types.ts +9 -0
  25. package/src/index.ts +4 -1
  26. package/src/render/GeometryBuilder.ts +354 -19
  27. package/src/render/S3MLayer.ts +328 -10
  28. package/src/render/StyleMaterial.ts +164 -8
  29. package/src/vendor/meshopt_decoder.module.js +112 -112
  30. package/tools/copy-assets.mjs +28 -0
  31. package/tools/e2e.mjs +78 -0
  32. package/tools/lod-probe.mjs +60 -0
  33. package/tools/meshopt-check.mjs +242 -0
  34. package/tools/official-oracle.mjs +54 -0
  35. package/tools/parse-sample.mjs +87 -0
  36. package/tools/serve.mjs +51 -0
  37. package/tools/test-batch-texmatrix.mjs +221 -0
  38. package/tools/trace-one.mjs +93 -0
package/README.md CHANGED
@@ -45,6 +45,8 @@ iServer / 本地目录 → .scp 配置 + .s3mb 瓦片
45
45
  | 全局棱线 | ✅ | 优先用数据自带棱线包,没有才 CPU 现算 |
46
46
  | 单体级点选 + 属性 | ✅ | GPU 拾取,代价与面数无关 |
47
47
  | 实例化几何 | ✅ | `instanceMode` 16 / 17 |
48
+ | 瓦片同材质合批 | ✅ | 倾斜摄影大瓦片自动合批,大幅降低 DrawCall |
49
+ | 纹理变换与寻址 | ✅ | 支持 `texmodmatrix` 仿射变换与 `addressmode` |
48
50
  | DXT / WebP 纹理 | ✅ | |
49
51
  | Draco 压缩几何 | ❌ | 遇到会抛明确错误 |
50
52
  | KTX2 / CRN | ❌ | |
@@ -228,8 +230,6 @@ S3M 顶点(数据集局部 ENU,米)
228
230
 
229
231
  - **Draco 压缩几何**未实现。MeshOpt 已支持;遇到 Draco 会抛带说明的错误。
230
232
  - **地形遮挡**未实现:MapLibre 的地形深度没进 custom layer 的深度缓冲,建筑不会被地形裁掉。
231
- - 每个 `indexPackage` 一个 `Mesh`,倾斜摄影大瓦片需要合批。
232
- - `texmodmatrix`(纹理变换)未应用。
233
233
  - 跨城市量级的数据集叠加会重新引入 float32 精度问题。
234
234
 
235
235
  ---
@@ -183,17 +183,44 @@ export interface StyleUniforms {
183
183
  uDim: {
184
184
  value: number;
185
185
  };
186
+ uDimFactor?: {
187
+ value: number;
188
+ };
186
189
  uSelColor: {
187
190
  value: Color;
188
191
  };
189
192
  uSelMix: {
190
193
  value: number;
191
194
  };
195
+ uSelMode?: {
196
+ value: number;
197
+ };
198
+ uSelOpacity?: {
199
+ value: number;
200
+ };
201
+ uSelEmissive?: {
202
+ value: number;
203
+ };
204
+ uSelElevation?: {
205
+ value: number;
206
+ };
207
+ uFilterMinZ?: {
208
+ value: number;
209
+ };
210
+ uFilterMaxZ?: {
211
+ value: number;
212
+ };
213
+ uFilterHeightActive?: {
214
+ value: number;
215
+ };
192
216
  uRoofThreshold?: {
193
217
  value: number;
194
218
  };
195
219
  uTexMix?: {
196
220
  value: number;
197
221
  };
222
+ [key: string]: {
223
+ value: unknown;
224
+ } | undefined;
198
225
  }
199
226
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -15,8 +15,8 @@ export { resolveFeature, measureSegments, buildAttributes, DEFAULT_FLOOR_HEIGHT
15
15
  export type { Vec3, BoundingBox, AttrDType, VertexAttribute, IndexPackage, VertexPackage, PickSegment, PickEntry, GeoPackageEntry, Geode, PageLOD, GroupNode, S3MTexture, ParsedTile, Anchor, Stats, FeatureAttributes, SegmentBox, StyleUniforms, } from './core/types.js';
16
16
  export { S3MTileset } from './tileset/S3MTileset.js';
17
17
  export type { TileNode, BuildTileFn, S3MTilesetOptions } from './tileset/S3MTileset.js';
18
- export { S3MLayer, DEFAULT_STYLE, STYLE, STYLE_LABELS } from './render/S3MLayer.js';
19
- export type { S3MLayerOptions, S3MStyle, S3MDatasetOptions, S3MDatasetEntry, PickResult, MapLike, MapMouseEventLike, } from './render/S3MLayer.js';
18
+ export { S3MLayer, DEFAULT_STYLE, STYLE, STYLE_LABELS, SELECT_MODE, SELECT_MODE_LABELS } from './render/S3MLayer.js';
19
+ export type { S3MLayerOptions, S3MStyle, SelectMode, FeatureStyle, S3MFilter, S3MDatasetOptions, S3MDatasetEntry, PickResult, MapLike, MapMouseEventLike, } from './render/S3MLayer.js';
20
20
  export { buildTileObjects } from './render/GeometryBuilder.js';
21
21
  export type { BuildTileOptions, BuildTileResult, TileBuildStats } from './render/GeometryBuilder.js';
22
22
  export { createTexture, PixelFormat, setMaxAnisotropy } from './render/TextureFactory.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACtF,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAG/G,YAAY,EACV,IAAI,EACJ,WAAW,EACX,SAAS,EACT,eAAe,EACf,YAAY,EACZ,aAAa,EACb,WAAW,EACX,SAAS,EACT,eAAe,EACf,KAAK,EACL,OAAO,EACP,SAAS,EACT,UAAU,EACV,UAAU,EACV,MAAM,EACN,KAAK,EACL,iBAAiB,EACjB,UAAU,EACV,aAAa,GACd,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAGxF,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpF,YAAY,EACV,eAAe,EACf,QAAQ,EACR,iBAAiB,EACjB,eAAe,EACf,UAAU,EACV,OAAO,EACP,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAErG,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC1F,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAC9G,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEnH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,YAAY,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,QAAQ,IAAI,OAAO,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACtF,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAG/G,YAAY,EACV,IAAI,EACJ,WAAW,EACX,SAAS,EACT,eAAe,EACf,YAAY,EACZ,aAAa,EACb,WAAW,EACX,SAAS,EACT,eAAe,EACf,KAAK,EACL,OAAO,EACP,SAAS,EACT,UAAU,EACV,UAAU,EACV,MAAM,EACN,KAAK,EACL,iBAAiB,EACjB,UAAU,EACV,aAAa,GACd,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAGxF,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrH,YAAY,EACV,eAAe,EACf,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,UAAU,EACV,OAAO,EACP,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAErG,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC1F,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAC9G,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEnH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,YAAY,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,QAAQ,IAAI,OAAO,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ export { resolveFeature, measureSegments, buildAttributes, DEFAULT_FLOOR_HEIGHT
15
15
  // —— 瓦片层
16
16
  export { S3MTileset } from './tileset/S3MTileset.js';
17
17
  // —— 渲染层
18
- export { S3MLayer, DEFAULT_STYLE, STYLE, STYLE_LABELS } from './render/S3MLayer.js';
18
+ export { S3MLayer, DEFAULT_STYLE, STYLE, STYLE_LABELS, SELECT_MODE, SELECT_MODE_LABELS } from './render/S3MLayer.js';
19
19
  export { buildTileObjects } from './render/GeometryBuilder.js';
20
20
  export { createTexture, PixelFormat, setMaxAnisotropy } from './render/TextureFactory.js';
21
21
  export { createSharedUniforms, createStyledMaterial, createOutlineMaterial } from './render/StyleMaterial.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,oCAAoC;AACpC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEtF,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAyB/G,SAAS;AACT,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAGrD,SAAS;AACT,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAWpF,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAG/D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAG1F,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAG9G,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAG5C,OAAO,EAAE,QAAQ,IAAI,OAAO,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,oCAAoC;AACpC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEtF,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAyB/G,SAAS;AACT,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAGrD,SAAS;AACT,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAcrH,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAG/D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAG1F,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAG9G,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAG5C,OAAO,EAAE,QAAQ,IAAI,OAAO,EAAE,MAAM,sBAAsB,CAAC"}
@@ -29,6 +29,8 @@ export interface TileBuildStats {
29
29
  instanced: number;
30
30
  /** 数据自带的棱线包(primitiveType 13)数量 */
31
31
  edgeLines: number;
32
+ /** 合批前原有独立网格数 */
33
+ originalMeshes?: number;
32
34
  /** 被跳过的东西,加载完可以直接在控制台看 */
33
35
  skipped: string[];
34
36
  }
@@ -39,6 +41,11 @@ export interface BuildTileOptions {
39
41
  /** 是否显示数据自带的棱线包 */
40
42
  edges?: boolean;
41
43
  edgeColor?: THREE.ColorRepresentation;
44
+ /**
45
+ * 是否开启瓦片内同材质网格合批(默认 true)。
46
+ * 对倾斜摄影、大网格切片瓦片可减少 90%+ DrawCall,显著提高渲染帧率。
47
+ */
48
+ batching?: boolean;
42
49
  }
43
50
  export interface BuildTileResult {
44
51
  group: THREE.Group;
@@ -1 +1 @@
1
- {"version":3,"file":"GeometryBuilder.d.ts","sourceRoot":"","sources":["../../src/render/GeometryBuilder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAIV,UAAU,EACV,aAAa,EAEd,MAAM,kBAAkB,CAAC;AAI1B;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,KAAK,CAAC;AA8JjC,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC1C,mBAAmB;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC;CACvC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC;IACnB,KAAK,EAAE,cAAc,CAAC;IACtB,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;CAC3B;AAYD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAmP1B"}
1
+ {"version":3,"file":"GeometryBuilder.d.ts","sourceRoot":"","sources":["../../src/render/GeometryBuilder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAIV,UAAU,EACV,aAAa,EAEd,MAAM,kBAAkB,CAAC;AAI1B;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,KAAK,CAAC;AA+KjC,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC1C,mBAAmB;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC;IACnB,KAAK,EAAE,cAAc,CAAC;IACtB,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;CAC3B;AAYD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CA0iB1B"}
@@ -56,21 +56,24 @@ function typedArrayOf(dtype, length) {
56
56
  }
57
57
  }
58
58
  function toBufferAttribute(attr) {
59
- const { data, components, stride, offsetInBytes, dtype = 'f32' } = attr;
59
+ const { data, stride, offsetInBytes, dtype = 'f32' } = attr;
60
+ const components = attr.components ?? attr.comp ?? 3;
60
61
  const bpe = bytesPerElement(dtype);
61
62
  const itemBytes = components * bpe;
63
+ const actualStride = stride ?? itemBytes;
64
+ const actualOffset = offsetInBytes ?? 0;
62
65
  const normalized = dtype === 'u8';
63
66
  // 非交错:整段直接铺开
64
- if (stride === itemBytes && offsetInBytes === 0) {
67
+ if (actualStride === itemBytes && actualOffset === 0) {
65
68
  const view = typedArray(dtype, data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
66
69
  return new THREE.BufferAttribute(view, components, normalized);
67
70
  }
68
71
  // 交错:逐元素抽取
69
- const count = Math.floor(data.byteLength / stride);
72
+ const count = Math.floor(data.byteLength / actualStride);
70
73
  const out = typedArrayOf(dtype, count * components);
71
74
  const dv = new DataView(data.buffer, data.byteOffset, data.byteLength);
72
75
  for (let i = 0; i < count; i++) {
73
- const base = i * stride + offsetInBytes;
76
+ const base = i * actualStride + actualOffset;
74
77
  for (let c = 0; c < components; c++) {
75
78
  const o = base + c * bpe;
76
79
  out[i * components + c] =
@@ -137,6 +140,15 @@ function materialUnits(m) {
137
140
  const raw = m?.textureStates ?? m?.textureunitstates ?? [];
138
141
  return raw.map((u) => u?.textureunitstate ?? u).filter((u) => !!u);
139
142
  }
143
+ /** 判断 4x4 矩阵是否为标准单位矩阵 */
144
+ function isIdentityMatrix4(m) {
145
+ if (m.length !== 16)
146
+ return false;
147
+ return (m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 0 &&
148
+ m[4] === 0 && m[5] === 1 && m[6] === 0 && m[7] === 0 &&
149
+ m[8] === 0 && m[9] === 0 && m[10] === 1 && m[11] === 0 &&
150
+ m[12] === 0 && m[13] === 0 && m[14] === 0 && m[15] === 1);
151
+ }
140
152
  /** cullMode:1.x 用 none/clockwise/backward,3.x 用 CULL_NONE / CULL_BACK / CULL_FRONT */
141
153
  function resolveSide(m) {
142
154
  const cull = String(m?.cullMode ?? '').toUpperCase();
@@ -181,19 +193,50 @@ export async function buildTileObjects(tile, options = {}) {
181
193
  stats.skipped.push(`texture ${code} → ${decoded}`);
182
194
  }
183
195
  }
184
- // 2) 材质描述表
196
+ // 2) 材质描述表与材质池缓存
185
197
  const materialList = resolveMaterialList(tile.materials);
198
+ const materialCache = new Map();
186
199
  const makeMaterial = (code) => {
200
+ const cacheKey = code ?? '__default__';
201
+ const cached = materialCache.get(cacheKey);
202
+ if (cached)
203
+ return cached;
187
204
  const entry = materialList.find((e) => materialName(unwrapMaterial(e)) === code);
188
205
  const m = unwrapMaterial(entry);
189
206
  const units = materialUnits(m);
190
- const map = units[0] ? (textureByCode.get(materialName(units[0]) ?? units[0].id ?? '') ?? null) : null;
207
+ const unit0 = units[0];
208
+ let map = unit0 ? (textureByCode.get(materialName(unit0) ?? unit0.id ?? '') ?? null) : null;
209
+ // 检查并应用 texmodmatrix 纹理变换矩阵
210
+ const texMod = unit0?.texmodmatrix ?? unit0?.matrix;
211
+ if (map && Array.isArray(texMod) && texMod.length === 16 && !isIdentityMatrix4(texMod)) {
212
+ map = map.clone();
213
+ map.matrixAutoUpdate = false;
214
+ // S3M texmodmatrix 为 4x4 列主序矩阵 [m0..m15],对应 2D 仿射变换:
215
+ // u' = m[0]*u + m[4]*v + m[12]
216
+ // v' = m[1]*u + m[5]*v + m[13]
217
+ // Three.js Matrix3.set 按行主序接收参数
218
+ map.matrix.set(texMod[0], texMod[4], texMod[12], texMod[1], texMod[5], texMod[13], 0, 0, 1);
219
+ }
220
+ // 纹理寻址模式(addressmode)
221
+ if (map && unit0?.addressmode) {
222
+ const u = unit0.addressmode.u;
223
+ const v = unit0.addressmode.v;
224
+ if (u !== undefined || v !== undefined) {
225
+ if (map.matrixAutoUpdate) {
226
+ map = map.clone();
227
+ }
228
+ if (u !== undefined)
229
+ map.wrapS = u === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
230
+ if (v !== undefined)
231
+ map.wrapT = v === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
232
+ }
233
+ }
191
234
  const diffuse = m?.diffuse ?? { r: 1, g: 1, b: 1, a: 1 };
192
235
  const alphaMode = m?.alphaMode;
193
236
  const transparent = alphaMode === 'AM_BLEND' || (diffuse.a ?? 1) < 0.999 || m?.transparentsorting === true;
194
237
  // S3M 的 shininess 是旧式 0~128 高光指数,粗略映射到粗糙度
195
238
  const roughness = typeof m?.shininess === 'number' && m.shininess > 0 ? Math.max(0.15, 1 - m.shininess / 128) : 0.95;
196
- return createStyledMaterial({
239
+ const mat = createStyledMaterial({
197
240
  uniforms,
198
241
  map,
199
242
  color: new THREE.Color(diffuse.r ?? 1, diffuse.g ?? 1, diffuse.b ?? 1),
@@ -202,6 +245,8 @@ export async function buildTileObjects(tile, options = {}) {
202
245
  opacity: diffuse.a ?? 1,
203
246
  roughness,
204
247
  });
248
+ materialCache.set(cacheKey, mat);
249
+ return mat;
205
250
  };
206
251
  // 3) 骨架 → 几何。顶点是「骨架局部坐标」,真正位置由引用它的 Geode 矩阵决定。
207
252
  const geoCache = new Map();
@@ -238,15 +283,6 @@ export async function buildTileObjects(tile, options = {}) {
238
283
  attributes.aBatchId = batchAttr
239
284
  ? toBufferAttribute(batchAttr)
240
285
  : new THREE.BufferAttribute(new Float32Array(vertexCount).fill(-1), 1);
241
- if (!attributes.normal) {
242
- const tmp = new THREE.BufferGeometry();
243
- tmp.setAttribute('position', attributes.position);
244
- tmp.computeVertexNormals();
245
- const normal = tmp.getAttribute('normal');
246
- if (normal)
247
- attributes.normal = normal;
248
- tmp.dispose();
249
- }
250
286
  const instanced = vp.instanceIndex !== -1 && (vp.instanceMode === 16 || vp.instanceMode === 17);
251
287
  // 注意:indexPackages 属于 Skeleton(geoPackage[name]),不在 vertexPackage 里
252
288
  skeletonInfo.set(name, { indexPackages: geo.indexPackages ?? [], instanced });
@@ -260,12 +296,212 @@ export async function buildTileObjects(tile, options = {}) {
260
296
  geometry.setIndex(idx);
261
297
  else
262
298
  geometry.setDrawRange(0, vertexCount);
299
+ // 如果数据本身没有法线且是三角网格,必须在绑定了 index 之后计算法线
300
+ // 绝不能在未索引的 attributes.position 上算,否则非连续顶点会导致法线朝向错乱甚至越界 NaN 导致死黑面
301
+ if (!isLinePrimitive(pack.primitiveType) && !geometry.getAttribute('normal')) {
302
+ geometry.computeVertexNormals();
303
+ }
263
304
  geometry.computeBoundingBox();
264
305
  geometry.computeBoundingSphere();
265
306
  geoCache.set(`${name}|${pi}`, { geometry, pack });
266
307
  });
267
308
  }
268
309
  const IDENTITY = new THREE.Matrix4();
310
+ const batchGroups = new Map();
311
+ const mergeBatchGeometries = (candidates, material, tileRef) => {
312
+ let totalVertices = 0;
313
+ let totalIndices = 0;
314
+ let hasNormal = false;
315
+ let hasUv = false;
316
+ let hasUv1 = false;
317
+ let hasAColor = false;
318
+ let hasABatchId = false;
319
+ for (const c of candidates) {
320
+ const g = c.cached.geometry;
321
+ const vCount = g.attributes.position?.count ?? 0;
322
+ totalVertices += vCount;
323
+ totalIndices += g.index ? g.index.count : vCount;
324
+ if (g.attributes.normal)
325
+ hasNormal = true;
326
+ if (g.attributes.uv)
327
+ hasUv = true;
328
+ if (g.attributes.uv1)
329
+ hasUv1 = true;
330
+ if (g.attributes.aColor)
331
+ hasAColor = true;
332
+ if (g.attributes.aBatchId)
333
+ hasABatchId = true;
334
+ }
335
+ const mergedPos = new Float32Array(totalVertices * 3);
336
+ const mergedNorm = hasNormal ? new Float32Array(totalVertices * 3) : null;
337
+ const mergedUv = hasUv ? new Float32Array(totalVertices * 2) : null;
338
+ const mergedUv1 = hasUv1 ? new Float32Array(totalVertices * 2) : null;
339
+ const mergedColor = hasAColor ? new Float32Array(totalVertices * 3) : null;
340
+ const mergedBatchId = hasABatchId ? new Float32Array(totalVertices) : null;
341
+ const IndexArrayType = totalVertices > 65535 ? Uint32Array : Uint16Array;
342
+ const mergedIndices = new IndexArrayType(totalIndices);
343
+ let vertexOffset = 0;
344
+ let indexOffset = 0;
345
+ const tmpVec = new THREE.Vector3();
346
+ const normalMatrix = new THREE.Matrix3();
347
+ const geoByBatchId = new Map();
348
+ for (const c of candidates) {
349
+ const g = c.cached.geometry;
350
+ const vCount = g.attributes.position.count;
351
+ const m = c.matrix;
352
+ const hasTransform = m && !m.equals(IDENTITY);
353
+ if (hasTransform && hasNormal) {
354
+ normalMatrix.getNormalMatrix(m);
355
+ }
356
+ if (c.geo.pickInfo) {
357
+ for (const entry of Object.values(c.geo.pickInfo)) {
358
+ if (typeof entry.batchId === 'number' && entry.batchId >= 0) {
359
+ geoByBatchId.set(entry.batchId, c.geo);
360
+ }
361
+ }
362
+ }
363
+ // 1. Position
364
+ const posArr = g.attributes.position.array;
365
+ for (let i = 0; i < vCount; i++) {
366
+ const srcIdx = i * 3;
367
+ const dstIdx = (vertexOffset + i) * 3;
368
+ if (hasTransform) {
369
+ tmpVec.set(posArr[srcIdx], posArr[srcIdx + 1], posArr[srcIdx + 2]).applyMatrix4(m);
370
+ mergedPos[dstIdx] = tmpVec.x;
371
+ mergedPos[dstIdx + 1] = tmpVec.y;
372
+ mergedPos[dstIdx + 2] = tmpVec.z;
373
+ }
374
+ else {
375
+ mergedPos[dstIdx] = posArr[srcIdx];
376
+ mergedPos[dstIdx + 1] = posArr[srcIdx + 1];
377
+ mergedPos[dstIdx + 2] = posArr[srcIdx + 2];
378
+ }
379
+ }
380
+ // 2. Normal
381
+ if (mergedNorm) {
382
+ const normAttr = g.attributes.normal;
383
+ if (normAttr) {
384
+ const normArr = normAttr.array;
385
+ for (let i = 0; i < vCount; i++) {
386
+ const srcIdx = i * 3;
387
+ const dstIdx = (vertexOffset + i) * 3;
388
+ if (hasTransform) {
389
+ tmpVec
390
+ .set(normArr[srcIdx], normArr[srcIdx + 1], normArr[srcIdx + 2])
391
+ .applyMatrix3(normalMatrix)
392
+ .normalize();
393
+ mergedNorm[dstIdx] = tmpVec.x;
394
+ mergedNorm[dstIdx + 1] = tmpVec.y;
395
+ mergedNorm[dstIdx + 2] = tmpVec.z;
396
+ }
397
+ else {
398
+ mergedNorm[dstIdx] = normArr[srcIdx];
399
+ mergedNorm[dstIdx + 1] = normArr[srcIdx + 1];
400
+ mergedNorm[dstIdx + 2] = normArr[srcIdx + 2];
401
+ }
402
+ }
403
+ }
404
+ }
405
+ // 3. UV
406
+ if (mergedUv) {
407
+ const uvAttr = g.attributes.uv;
408
+ if (uvAttr) {
409
+ const uvArr = uvAttr.array;
410
+ for (let i = 0; i < vCount; i++) {
411
+ mergedUv[(vertexOffset + i) * 2] = uvArr[i * 2];
412
+ mergedUv[(vertexOffset + i) * 2 + 1] = uvArr[i * 2 + 1];
413
+ }
414
+ }
415
+ }
416
+ // 4. UV1
417
+ if (mergedUv1) {
418
+ const uv1Attr = g.attributes.uv1;
419
+ if (uv1Attr) {
420
+ const uvArr = uv1Attr.array;
421
+ for (let i = 0; i < vCount; i++) {
422
+ mergedUv1[(vertexOffset + i) * 2] = uvArr[i * 2];
423
+ mergedUv1[(vertexOffset + i) * 2 + 1] = uvArr[i * 2 + 1];
424
+ }
425
+ }
426
+ }
427
+ // 5. aColor
428
+ if (mergedColor) {
429
+ const colAttr = g.attributes.aColor;
430
+ if (colAttr) {
431
+ const colArr = colAttr.array;
432
+ for (let i = 0; i < vCount; i++) {
433
+ mergedColor[(vertexOffset + i) * 3] = colArr[i * 3];
434
+ mergedColor[(vertexOffset + i) * 3 + 1] = colArr[i * 3 + 1];
435
+ mergedColor[(vertexOffset + i) * 3 + 2] = colArr[i * 3 + 2];
436
+ }
437
+ }
438
+ else {
439
+ for (let i = 0; i < vCount; i++) {
440
+ mergedColor[(vertexOffset + i) * 3] = 1;
441
+ mergedColor[(vertexOffset + i) * 3 + 1] = 1;
442
+ mergedColor[(vertexOffset + i) * 3 + 2] = 1;
443
+ }
444
+ }
445
+ }
446
+ // 6. aBatchId
447
+ if (mergedBatchId) {
448
+ const bAttr = g.attributes.aBatchId;
449
+ if (bAttr) {
450
+ const bArr = bAttr.array;
451
+ for (let i = 0; i < vCount; i++) {
452
+ mergedBatchId[vertexOffset + i] = bArr[i];
453
+ }
454
+ }
455
+ else {
456
+ for (let i = 0; i < vCount; i++) {
457
+ mergedBatchId[vertexOffset + i] = -1;
458
+ }
459
+ }
460
+ }
461
+ // 7. Indices
462
+ if (g.index) {
463
+ const idxArr = g.index.array;
464
+ const iCount = g.index.count;
465
+ for (let k = 0; k < iCount; k++) {
466
+ mergedIndices[indexOffset + k] = idxArr[k] + vertexOffset;
467
+ }
468
+ indexOffset += iCount;
469
+ }
470
+ else {
471
+ for (let k = 0; k < vCount; k++) {
472
+ mergedIndices[indexOffset + k] = k + vertexOffset;
473
+ }
474
+ indexOffset += vCount;
475
+ }
476
+ vertexOffset += vCount;
477
+ }
478
+ const mergedGeo = new THREE.BufferGeometry();
479
+ mergedGeo.setAttribute('position', new THREE.BufferAttribute(mergedPos, 3));
480
+ if (mergedNorm)
481
+ mergedGeo.setAttribute('normal', new THREE.BufferAttribute(mergedNorm, 3));
482
+ if (mergedUv)
483
+ mergedGeo.setAttribute('uv', new THREE.BufferAttribute(mergedUv, 2));
484
+ if (mergedUv1)
485
+ mergedGeo.setAttribute('uv1', new THREE.BufferAttribute(mergedUv1, 2));
486
+ if (mergedColor)
487
+ mergedGeo.setAttribute('aColor', new THREE.BufferAttribute(mergedColor, 3));
488
+ if (mergedBatchId)
489
+ mergedGeo.setAttribute('aBatchId', new THREE.BufferAttribute(mergedBatchId, 1));
490
+ mergedGeo.setIndex(new THREE.BufferAttribute(mergedIndices, 1));
491
+ mergedGeo.computeBoundingBox();
492
+ mergedGeo.computeBoundingSphere();
493
+ const mesh = new THREE.Mesh(mergedGeo, material);
494
+ mesh.userData.skeletonName = candidates[0].name;
495
+ mesh.userData.geoRef = candidates[0].geo;
496
+ mesh.userData.tileRef = tileRef;
497
+ mesh.userData.primitiveType = 4;
498
+ mesh.userData.isBatched = true;
499
+ mesh.userData.batchedCount = candidates.length;
500
+ if (geoByBatchId.size > 0) {
501
+ mesh.userData.geoByBatchId = geoByBatchId;
502
+ }
503
+ return mesh;
504
+ };
269
505
  const placeObject = (cached, name, geo, matrix) => {
270
506
  const { geometry, pack } = cached;
271
507
  const material = makeMaterial(pack.passNames?.[0]);
@@ -350,12 +586,32 @@ export async function buildTileObjects(tile, options = {}) {
350
586
  const geo = tile.geoPackage[name];
351
587
  if (!geo)
352
588
  continue;
589
+ const vp = geo.vertexPackage;
353
590
  for (let pi = 0; pi < info.indexPackages.length; pi++) {
354
591
  const cached = geoCache.get(`${name}|${pi}`);
355
- if (cached) {
592
+ if (!cached)
593
+ continue;
594
+ // 棱线包或实例化网格:保持单独摆放
595
+ if (isLinePrimitive(cached.pack.primitiveType) ||
596
+ (info.instanced && vp.instanceBuffer && vp.instanceCount > 0)) {
356
597
  placeObject(cached, name, geo, matrix);
357
598
  placed++;
599
+ continue;
358
600
  }
601
+ // 普通三角面网格:若开启合批,按材质进行分组收集
602
+ if (options.batching !== false && cached.pack.primitiveType === 4) {
603
+ const matKey = cached.pack.passNames?.[0] ?? '__default__';
604
+ let list = batchGroups.get(matKey);
605
+ if (!list) {
606
+ list = [];
607
+ batchGroups.set(matKey, list);
608
+ }
609
+ list.push({ cached, name, geo, matrix });
610
+ placed++;
611
+ continue;
612
+ }
613
+ placeObject(cached, name, geo, matrix);
614
+ placed++;
359
615
  }
360
616
  }
361
617
  }
@@ -366,10 +622,46 @@ export async function buildTileObjects(tile, options = {}) {
366
622
  const geo = tile.geoPackage[name];
367
623
  if (!geo)
368
624
  continue;
625
+ const vp = geo.vertexPackage;
369
626
  for (let pi = 0; pi < info.indexPackages.length; pi++) {
370
627
  const cached = geoCache.get(`${name}|${pi}`);
371
- if (cached)
628
+ if (!cached)
629
+ continue;
630
+ if (isLinePrimitive(cached.pack.primitiveType) ||
631
+ (info.instanced && vp.instanceBuffer && vp.instanceCount > 0)) {
372
632
  placeObject(cached, name, geo, null);
633
+ continue;
634
+ }
635
+ if (options.batching !== false && cached.pack.primitiveType === 4) {
636
+ const matKey = cached.pack.passNames?.[0] ?? '__default__';
637
+ let list = batchGroups.get(matKey);
638
+ if (!list) {
639
+ list = [];
640
+ batchGroups.set(matKey, list);
641
+ }
642
+ list.push({ cached, name, geo, matrix: null });
643
+ continue;
644
+ }
645
+ placeObject(cached, name, geo, null);
646
+ }
647
+ }
648
+ }
649
+ // 执行同材质几何合批
650
+ if (batchGroups.size > 0) {
651
+ for (const [matKey, list] of batchGroups) {
652
+ stats.originalMeshes = (stats.originalMeshes ?? 0) + list.length;
653
+ if (list.length === 1) {
654
+ placeObject(list[0].cached, list[0].name, list[0].geo, list[0].matrix);
655
+ }
656
+ else {
657
+ const material = makeMaterial(matKey === '__default__' ? undefined : matKey);
658
+ const batchedMesh = mergeBatchGeometries(list, material, tile);
659
+ group.add(batchedMesh);
660
+ objects.push(batchedMesh);
661
+ stats.meshes++;
662
+ for (const item of list) {
663
+ stats.triangles += (item.cached.pack.indexCount || 0) / 3;
664
+ }
373
665
  }
374
666
  }
375
667
  }