s3m-three 0.4.25 → 0.4.26

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 (73) hide show
  1. package/dist/browser/assets/{s3mWorker-jc9Oly5K.js → s3mWorker-DHOVIrAU.js} +1 -1
  2. package/dist/browser/s3m-three.iife.ext-three.js +107 -36
  3. package/dist/browser/s3m-three.iife.js +107 -36
  4. package/dist/core/CustomAttributes.d.ts +7 -0
  5. package/dist/core/CustomAttributes.d.ts.map +1 -0
  6. package/dist/core/CustomAttributes.js +48 -0
  7. package/dist/core/CustomAttributes.js.map +1 -0
  8. package/dist/core/Draco.d.ts.map +1 -1
  9. package/dist/core/Draco.js +149 -2
  10. package/dist/core/Draco.js.map +1 -1
  11. package/dist/core/MeshOpt.d.ts.map +1 -1
  12. package/dist/core/MeshOpt.js +2 -0
  13. package/dist/core/MeshOpt.js.map +1 -1
  14. package/dist/core/S3M1Parser.d.ts.map +1 -1
  15. package/dist/core/S3M1Parser.js +114 -27
  16. package/dist/core/S3M1Parser.js.map +1 -1
  17. package/dist/core/S3MBParser.d.ts +5 -2
  18. package/dist/core/S3MBParser.d.ts.map +1 -1
  19. package/dist/core/S3MBParser.js +27 -12
  20. package/dist/core/S3MBParser.js.map +1 -1
  21. package/dist/core/TileGeometry.d.ts +1 -0
  22. package/dist/core/TileGeometry.d.ts.map +1 -1
  23. package/dist/core/TileGeometry.js +25 -7
  24. package/dist/core/TileGeometry.js.map +1 -1
  25. package/dist/core/types.d.ts +1 -0
  26. package/dist/core/types.d.ts.map +1 -1
  27. package/dist/index.d.ts +4 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +2 -1
  30. package/dist/index.js.map +1 -1
  31. package/dist/render/GeometryBuilder.d.ts.map +1 -1
  32. package/dist/render/GeometryBuilder.js +36 -2
  33. package/dist/render/GeometryBuilder.js.map +1 -1
  34. package/dist/render/MotionBlurPass.d.ts +130 -0
  35. package/dist/render/MotionBlurPass.d.ts.map +1 -0
  36. package/dist/render/MotionBlurPass.js +394 -0
  37. package/dist/render/MotionBlurPass.js.map +1 -0
  38. package/dist/render/S3MViewer.d.ts +27 -0
  39. package/dist/render/S3MViewer.d.ts.map +1 -1
  40. package/dist/render/S3MViewer.js +76 -4
  41. package/dist/render/S3MViewer.js.map +1 -1
  42. package/dist/render/viewer/types.d.ts +22 -0
  43. package/dist/render/viewer/types.d.ts.map +1 -1
  44. package/dist/render/viewer/types.js.map +1 -1
  45. package/dist/tileset/S3MTileset.d.ts +19 -0
  46. package/dist/tileset/S3MTileset.d.ts.map +1 -1
  47. package/dist/tileset/S3MTileset.js +71 -4
  48. package/dist/tileset/S3MTileset.js.map +1 -1
  49. package/dist/worker/S3MWorkerClient.d.ts +1 -1
  50. package/dist/worker/S3MWorkerClient.d.ts.map +1 -1
  51. package/dist/worker/S3MWorkerClient.js +2 -2
  52. package/dist/worker/S3MWorkerClient.js.map +1 -1
  53. package/dist/worker/s3mWorker.js +791 -635
  54. package/dist/worker/s3mWorker.js.map +1 -1
  55. package/docs/API.md +18 -5
  56. package/docs/FORMAT-NOTES.md +10 -0
  57. package/package.json +1 -1
  58. package/src/core/CustomAttributes.ts +53 -0
  59. package/src/core/Draco.ts +168 -6
  60. package/src/core/MeshOpt.ts +2 -0
  61. package/src/core/S3M1Parser.ts +102 -22
  62. package/src/core/S3MBParser.ts +33 -14
  63. package/src/core/TileGeometry.ts +34 -7
  64. package/src/core/types.ts +1 -0
  65. package/src/index.ts +4 -2
  66. package/src/render/GeometryBuilder.ts +37 -2
  67. package/src/render/MotionBlurPass.ts +453 -0
  68. package/src/render/S3MViewer.ts +78 -4
  69. package/src/render/viewer/types.ts +22 -0
  70. package/src/tileset/S3MTileset.ts +74 -3
  71. package/src/vendor/draco_decoder.module.d.ts +15 -1
  72. package/src/worker/S3MWorkerClient.ts +7 -2
  73. package/src/worker/s3mWorker.ts +6 -2
@@ -12,6 +12,7 @@
12
12
  import { BinaryReader } from './BinaryReader.js';
13
13
  import { parseDracoGeometry } from './Draco.js';
14
14
  import { parseMeshOptGeometry } from './MeshOpt.js';
15
+ import { applyCustomAttributeSemantics } from './CustomAttributes.js';
15
16
  import { isS3M1Buffer, parseS3M1 } from './S3M1Parser.js';
16
17
  import type { DegradationKind } from './degradations.js';
17
18
  import type {
@@ -53,6 +54,8 @@ export interface ParseOptions {
53
54
  inflate?: InflateFn;
54
55
  /** 逐段追踪偏移,调试解析用 */
55
56
  trace?: (section: string, detail?: unknown, offset?: number) => void;
57
+ /** config.json 声明的顶点属性描述清单(用于语义重映射与维度判定) */
58
+ vertexAttributeDescript?: unknown;
56
59
  }
57
60
 
58
61
  /** 默认解压:zlib(文件头 78 9C / 78 DA)。浏览器与 Node 18+ 均可用。 */
@@ -73,9 +76,8 @@ function pushAttr(
73
76
  offset = 0,
74
77
  divisor?: number,
75
78
  dtype: AttrDType = 'f32'
76
- ): void {
77
- vp.attrLocation[semantic] = vp.vertexAttributes.length;
78
- vp.vertexAttributes.push({
79
+ ): VertexAttribute {
80
+ const attr: VertexAttribute = {
79
81
  semantic,
80
82
  data,
81
83
  components,
@@ -83,7 +85,10 @@ function pushAttr(
83
85
  offsetInBytes: offset,
84
86
  divisor,
85
87
  dtype,
86
- });
88
+ };
89
+ vp.attrLocation[semantic] = vp.vertexAttributes.length;
90
+ vp.vertexAttributes.push(attr);
91
+ return attr;
87
92
  }
88
93
 
89
94
  function newVertexPackage(): VertexPackage {
@@ -248,15 +253,19 @@ function parseInstanceInfo(r: BinaryReader, vp: VertexPackage, version: number):
248
253
  }
249
254
  }
250
255
 
251
- function parseVertexAttrExtension(r: BinaryReader, vp: VertexPackage): void {
256
+ export { applyCustomAttributeSemantics } from './CustomAttributes.js';
257
+
258
+ function parseVertexAttrExtension(r: BinaryReader, vp: VertexPackage): VertexAttribute[] {
252
259
  const n = r.u32();
260
+ const attrs: VertexAttribute[] = [];
253
261
  for (let i = 0; i < n; i++) {
254
262
  const count = r.u32();
255
263
  const dim = r.u16();
256
264
  const type = r.u16();
257
265
  const size = AttrTypeSize[type] ?? 4;
258
- pushAttr(vp, `custom${i}`, r.bytes(count * dim * size), dim, dim * size);
266
+ attrs.push(pushAttr(vp, `custom${i}`, r.bytes(count * dim * size), dim, dim * size));
259
267
  }
268
+ return attrs;
260
269
  }
261
270
 
262
271
  function parseTangent(r: BinaryReader): void {
@@ -267,7 +276,7 @@ function parseTangent(r: BinaryReader): void {
267
276
  r.skip(n * dim * 4);
268
277
  }
269
278
 
270
- function parseStandardSkeleton(r: BinaryReader, vp: VertexPackage, version: number): void {
279
+ function parseStandardSkeleton(r: BinaryReader, vp: VertexPackage, version: number, options?: ParseOptions): void {
271
280
  if (version >= 3) r.u32(); // streamSize
272
281
 
273
282
  parseVertex(r, vp);
@@ -278,9 +287,17 @@ function parseStandardSkeleton(r: BinaryReader, vp: VertexPackage, version: numb
278
287
  parseInstanceInfo(r, vp, version);
279
288
 
280
289
  if (version >= 3) {
281
- parseVertexAttrExtension(r, vp);
290
+ const extAttrs = parseVertexAttrExtension(r, vp);
282
291
  const descript = r.string();
283
- vp.customVertexAttribute = descript ? JSON.parse(descript) : undefined;
292
+ try {
293
+ vp.customVertexAttribute = descript ? JSON.parse(descript) : undefined;
294
+ } catch {
295
+ vp.customVertexAttribute = descript;
296
+ }
297
+ if (!vp.customVertexAttribute && options?.vertexAttributeDescript) {
298
+ vp.customVertexAttribute = options.vertexAttributeDescript;
299
+ }
300
+ applyCustomAttributeSemantics(vp, extAttrs, options?.vertexAttributeDescript);
284
301
  r.align4();
285
302
  parseTangent(r);
286
303
  }
@@ -386,7 +403,8 @@ function isFiniteBox(box: { center: Vec3; xExtent: Vec3; yExtent: Vec3; zExtent:
386
403
  export async function parseSkeletonEntries(
387
404
  r: BinaryReader,
388
405
  version: number,
389
- trace?: ParseOptions['trace']
406
+ trace?: ParseOptions['trace'],
407
+ options?: ParseOptions
390
408
  ): Promise<Record<string, GeoPackageEntry>> {
391
409
  const count = r.u32();
392
410
  const geoPackage: Record<string, GeoPackageEntry> = {};
@@ -419,7 +437,7 @@ export async function parseSkeletonEntries(
419
437
  let indexPackages: IndexPackage[] = [];
420
438
 
421
439
  if (tag === VertexTag.STANDARD) {
422
- parseStandardSkeleton(r, vp, version);
440
+ parseStandardSkeleton(r, vp, version, options);
423
441
  indexPackages = parseIndexPackages(r, version);
424
442
  } else if (tag === VertexTag.COMPRESSED) {
425
443
  if (version >= 3) {
@@ -462,10 +480,11 @@ export async function parseSkeletonEntries(
462
480
  export async function parseSkeletons(
463
481
  r: BinaryReader,
464
482
  version: number,
465
- trace?: ParseOptions['trace']
483
+ trace?: ParseOptions['trace'],
484
+ options?: ParseOptions
466
485
  ): Promise<Record<string, GeoPackageEntry>> {
467
486
  r.u32(); // streamSize
468
- const geoPackage = await parseSkeletonEntries(r, version, trace);
487
+ const geoPackage = await parseSkeletonEntries(r, version, trace, options);
469
488
 
470
489
  // v1/v2 在骨架数组之后还有一段附加流。官方写法是
471
490
  // bytesOffset += secColorSize; bytesOffset += 4;
@@ -649,7 +668,7 @@ export async function parseS3MB(arrayBuffer: ArrayBuffer, options: ParseOptions
649
668
  trace?.('fileOptions', fileOptions, t.offset);
650
669
  const groupNode = parseGroupNode(t, version);
651
670
  trace?.('groupNode', groupNode.pageLods.length, t.offset);
652
- const geoPackage = await parseSkeletons(t, version, trace);
671
+ const geoPackage = await parseSkeletons(t, version, trace, options);
653
672
  trace?.('skeletons', Object.keys(geoPackage).length, t.offset);
654
673
  const textures = parseTextures(t);
655
674
  trace?.('textures', Object.keys(textures).length, t.offset);
@@ -20,6 +20,7 @@ import type {
20
20
  ParsedTile,
21
21
  VertexAttribute,
22
22
  } from './types.js';
23
+ import { applyCustomAttributeSemantics } from './CustomAttributes.js';
23
24
 
24
25
  /**
25
26
  * S3M 规范里 `DrawPrimitiveType` 的两个特殊值(原住 GeometryBuilder,随组装层
@@ -297,6 +298,7 @@ export interface AssembleOptions {
297
298
  * `useWorker: false` 或 Worker 崩溃降级时它落在主线程上。
298
299
  */
299
300
  dedupeCoincident?: boolean;
301
+ vertexAttributeDescript?: unknown;
300
302
  }
301
303
 
302
304
  /* ------------------------------------------------------------------ 合批合并 */
@@ -556,8 +558,15 @@ interface SkeletonAsm {
556
558
  }
557
559
 
558
560
  /** 每骨架只解一次交错流(同一骨架被多个 pack / geode 引用时共享同一份数据) */
559
- function assembleSkeleton(name: string, geo: GeoPackageEntry, tile: ParsedTile, skipped: string[]): SkeletonAsm | null {
561
+ function assembleSkeleton(
562
+ name: string,
563
+ geo: GeoPackageEntry,
564
+ tile: ParsedTile,
565
+ skipped: string[],
566
+ options?: AssembleOptions
567
+ ): SkeletonAsm | null {
560
568
  const vp = geo.vertexPackage;
569
+ applyCustomAttributeSemantics(vp, undefined, (tile as any).vertexAttributeDescript ?? options?.vertexAttributeDescript);
561
570
  const positionAttr = vp.vertexAttributes.find((a) => a.semantic === 'position');
562
571
  if (!positionAttr) {
563
572
  skipped.push(`${name} 无 position`);
@@ -585,11 +594,29 @@ function assembleSkeleton(name: string, geo: GeoPackageEntry, tile: ParsedTile,
585
594
  else if (attr.semantic === 'normal') normalData = deinterleave(attr);
586
595
  else if (attr.semantic === 'uv') uvData = deinterleave(attr);
587
596
  else if (attr.semantic === 'uv2') uv1Data = deinterleave(attr);
588
- // custom0 = uv 的第三分量,官方 aTextureCoordMatrix:决定「这张贴图用不用、
589
- // 用图集的哪个子区域、按 2 的几次幂缩放」。BIM/OSGB 转出来的数据依赖它,
590
- // 丢掉就会把本该纯色的墙面贴上整张材质图。语义见 core/TexCoordZ.ts。
591
- else if (attr.semantic === 'custom0' && (attr.components ?? 1) >= 1) {
592
- texCoordZData = deinterleave(attr);
597
+ // uv 第三分量(官方 aTextureCoordMatrix):决定「这张贴图用不用、
598
+ // 用图集的哪个子区域、按 2 的几次幂缩放」。语义见 core/TexCoordZ.ts。
599
+ // 注意:按语义查槽(找 TextureCoordMatrix),不要固定取 custom0;
600
+ // 绝对不能把 VertexWeight(高度值)等其它语义属性当成贴图矩阵;
601
+ // 若全为哨兵(-99999)则视为无第三分量,跳过采样/图集选择逻辑。
602
+ else if (
603
+ (attr.semantic === 'TextureCoordMatrix' ||
604
+ attr.semantic === 'aTextureCoordMatrix' ||
605
+ (attr.semantic === 'custom0' && !vp.vertexAttributes.some((a) => a.semantic === 'TextureCoordMatrix' || a.semantic === 'VertexWeight'))) &&
606
+ (attr.components ?? 1) >= 1
607
+ ) {
608
+ const plain = deinterleave(attr);
609
+ const arr = plain.array as Float32Array;
610
+ let hasValid = false;
611
+ for (let i = 0; i < arr.length; i++) {
612
+ if (arr[i]! >= -99900) {
613
+ hasValid = true;
614
+ break;
615
+ }
616
+ }
617
+ if (hasValid) {
618
+ texCoordZData = plain;
619
+ }
593
620
  }
594
621
  }
595
622
 
@@ -752,7 +779,7 @@ export function assembleTileGeometry(tile: ParsedTile, options: AssembleOptions
752
779
 
753
780
  const skeletons = new Map<string, SkeletonAsm>();
754
781
  for (const [name, geo] of Object.entries(tile.geoPackage ?? {})) {
755
- const asm = assembleSkeleton(name, geo, tile, skipped);
782
+ const asm = assembleSkeleton(name, geo, tile, skipped, options);
756
783
  if (asm) {
757
784
  skeletons.set(name, asm);
758
785
  stats.vertices += geo.vertexPackage.verticesCount || 0;
package/src/core/types.ts CHANGED
@@ -201,6 +201,7 @@ export interface ParsedTile {
201
201
  materials: unknown;
202
202
  pickInfo: unknown;
203
203
  extensions?: unknown;
204
+ vertexAttributeDescript?: unknown;
204
205
  /**
205
206
  * 解析成功、但渲染能力不完整的项(机器可读)。
206
207
  *
package/src/index.ts CHANGED
@@ -60,8 +60,8 @@ export {
60
60
  export type { GeoAdapter } from './render/GeoAdapter.js';
61
61
 
62
62
  // —— 瓦片层
63
- export { S3MTileset } from './tileset/S3MTileset.js';
64
- export type { TileNode, BuildTileFn, S3MTilesetOptions } from './tileset/S3MTileset.js';
63
+ export { S3MTileset, parseConfigText, classifyDatasetKind } from './tileset/S3MTileset.js';
64
+ export type { TileNode, BuildTileFn, S3MTilesetOptions, DatasetKind } from './tileset/S3MTileset.js';
65
65
 
66
66
  // —— 渲染层
67
67
  export { S3MViewer } from './render/S3MViewer.js';
@@ -138,6 +138,8 @@ export { setMaterialQuality, getMaterialQuality } from './render/StyleMaterial.j
138
138
 
139
139
  export { Picker } from './render/Picker.js';
140
140
  export { GpuEdgePass } from './render/GpuEdgePass.js';
141
+ export { MotionBlurPass, normalizeMotionBlur, autoMotionBlurDamp } from './render/MotionBlurPass.js';
142
+ export type { MotionBlurOptions, MotionBlurConfig, MotionBlurParams } from './render/MotionBlurPass.js';
141
143
 
142
144
  /* 三维空间分析内核:纯数学,不依赖 WebGL,可单独用于服务端 / Worker */
143
145
  export * from './analysis/index.js';
@@ -619,8 +619,8 @@ export async function buildTileObjects(
619
619
  const matrix =
620
620
  prim.matrix && prim.matrix.length === 16 ? new THREE.Matrix4().fromArray(prim.matrix) : null;
621
621
 
622
- // 棱线包:线段,不是三角面。默认隐藏,等 setEdges(true) 再显示
623
- if (isLinePrimitive(prim.primitiveType)) {
622
+ // 棱线包(13):线段,不是三角面。默认隐藏,等 setEdges(true) 再显示
623
+ if (prim.primitiveType === LINE_PRIMITIVE) {
624
624
  const lines = new THREE.LineSegments(geometry, edgeLineMaterial);
625
625
  lines.visible = options.edges === true;
626
626
  lines.renderOrder = EDGE_LINES_RENDER_ORDER;
@@ -636,6 +636,41 @@ export async function buildTileObjects(
636
636
  continue;
637
637
  }
638
638
 
639
+ // 实体线段 / 折线几何(2=线段,3=折线,如 3D-pipe 管线):实体几何,默认可见并取材质色
640
+ if (prim.primitiveType === 2 || prim.primitiveType === 3) {
641
+ const entry = materialList.find((e) => materialName(unwrapMaterial(e)) === prim.passNames[0]);
642
+ const m = unwrapMaterial(entry);
643
+ const diff = m?.diffuse;
644
+ const lineColor = diff
645
+ ? new THREE.Color(diff.r ?? 1, diff.g ?? 1, diff.b ?? 1)
646
+ : new THREE.Color(0xffffff);
647
+ const hasVertColor = !!geometry.getAttribute('aColor') || !!geometry.getAttribute('color');
648
+ if (hasVertColor && !geometry.getAttribute('color')) {
649
+ geometry.setAttribute('color', geometry.getAttribute('aColor')!);
650
+ }
651
+ const lineMaterial = new THREE.LineBasicMaterial({
652
+ color: lineColor,
653
+ vertexColors: hasVertColor,
654
+ transparent: diff?.a !== undefined && diff.a < 1,
655
+ opacity: diff?.a ?? 1,
656
+ });
657
+ const lines = prim.primitiveType === 3
658
+ ? new THREE.Line(geometry, lineMaterial)
659
+ : new THREE.LineSegments(geometry, lineMaterial);
660
+ lines.visible = true;
661
+ lines.userData.skeletonName = prim.skeletonName;
662
+ lines.userData.geoRef = geoRef;
663
+ lines.userData.tileRef = tile;
664
+ lines.userData.primitiveType = prim.primitiveType;
665
+ if (matrix && !matrix.equals(IDENTITY)) {
666
+ matrix.decompose(lines.position, lines.quaternion, lines.scale);
667
+ }
668
+ group.add(lines);
669
+ objects.push(lines);
670
+ stats.meshes++;
671
+ continue;
672
+ }
673
+
639
674
  // 点云包:顶点即点,按 THREE.Points 渲染。索引只是点序号,直接走 drawRange。
640
675
  if (isPointPrimitive(prim.primitiveType)) {
641
676
  // 高斯泼溅:带 gsScale / gsRotation 的点云包不画点,画各向异性的高斯片。