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.
- package/dist/browser/assets/{s3mWorker-jc9Oly5K.js → s3mWorker-DHOVIrAU.js} +1 -1
- package/dist/browser/s3m-three.iife.ext-three.js +107 -36
- package/dist/browser/s3m-three.iife.js +107 -36
- package/dist/core/CustomAttributes.d.ts +7 -0
- package/dist/core/CustomAttributes.d.ts.map +1 -0
- package/dist/core/CustomAttributes.js +48 -0
- package/dist/core/CustomAttributes.js.map +1 -0
- package/dist/core/Draco.d.ts.map +1 -1
- package/dist/core/Draco.js +149 -2
- package/dist/core/Draco.js.map +1 -1
- package/dist/core/MeshOpt.d.ts.map +1 -1
- package/dist/core/MeshOpt.js +2 -0
- package/dist/core/MeshOpt.js.map +1 -1
- package/dist/core/S3M1Parser.d.ts.map +1 -1
- package/dist/core/S3M1Parser.js +114 -27
- package/dist/core/S3M1Parser.js.map +1 -1
- package/dist/core/S3MBParser.d.ts +5 -2
- package/dist/core/S3MBParser.d.ts.map +1 -1
- package/dist/core/S3MBParser.js +27 -12
- package/dist/core/S3MBParser.js.map +1 -1
- package/dist/core/TileGeometry.d.ts +1 -0
- package/dist/core/TileGeometry.d.ts.map +1 -1
- package/dist/core/TileGeometry.js +25 -7
- package/dist/core/TileGeometry.js.map +1 -1
- package/dist/core/types.d.ts +1 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/render/GeometryBuilder.d.ts.map +1 -1
- package/dist/render/GeometryBuilder.js +36 -2
- package/dist/render/GeometryBuilder.js.map +1 -1
- package/dist/render/MotionBlurPass.d.ts +130 -0
- package/dist/render/MotionBlurPass.d.ts.map +1 -0
- package/dist/render/MotionBlurPass.js +394 -0
- package/dist/render/MotionBlurPass.js.map +1 -0
- package/dist/render/S3MViewer.d.ts +27 -0
- package/dist/render/S3MViewer.d.ts.map +1 -1
- package/dist/render/S3MViewer.js +76 -4
- package/dist/render/S3MViewer.js.map +1 -1
- package/dist/render/viewer/types.d.ts +22 -0
- package/dist/render/viewer/types.d.ts.map +1 -1
- package/dist/render/viewer/types.js.map +1 -1
- package/dist/tileset/S3MTileset.d.ts +19 -0
- package/dist/tileset/S3MTileset.d.ts.map +1 -1
- package/dist/tileset/S3MTileset.js +71 -4
- package/dist/tileset/S3MTileset.js.map +1 -1
- package/dist/worker/S3MWorkerClient.d.ts +1 -1
- package/dist/worker/S3MWorkerClient.d.ts.map +1 -1
- package/dist/worker/S3MWorkerClient.js +2 -2
- package/dist/worker/S3MWorkerClient.js.map +1 -1
- package/dist/worker/s3mWorker.js +791 -635
- package/dist/worker/s3mWorker.js.map +1 -1
- package/docs/API.md +18 -5
- package/docs/FORMAT-NOTES.md +10 -0
- package/package.json +1 -1
- package/src/core/CustomAttributes.ts +53 -0
- package/src/core/Draco.ts +168 -6
- package/src/core/MeshOpt.ts +2 -0
- package/src/core/S3M1Parser.ts +102 -22
- package/src/core/S3MBParser.ts +33 -14
- package/src/core/TileGeometry.ts +34 -7
- package/src/core/types.ts +1 -0
- package/src/index.ts +4 -2
- package/src/render/GeometryBuilder.ts +37 -2
- package/src/render/MotionBlurPass.ts +453 -0
- package/src/render/S3MViewer.ts +78 -4
- package/src/render/viewer/types.ts +22 -0
- package/src/tileset/S3MTileset.ts +74 -3
- package/src/vendor/draco_decoder.module.d.ts +15 -1
- package/src/worker/S3MWorkerClient.ts +7 -2
- package/src/worker/s3mWorker.ts +6 -2
package/src/core/S3MBParser.ts
CHANGED
|
@@ -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
|
-
):
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|
package/src/core/TileGeometry.ts
CHANGED
|
@@ -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(
|
|
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
|
-
//
|
|
589
|
-
// 用图集的哪个子区域、按 2
|
|
590
|
-
//
|
|
591
|
-
|
|
592
|
-
|
|
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
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
|
-
//
|
|
623
|
-
if (
|
|
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 的点云包不画点,画各向异性的高斯片。
|