s3m-three 0.3.6 → 0.3.8

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 (92) hide show
  1. package/dist/anim/Animator.d.ts +111 -0
  2. package/dist/anim/Animator.d.ts.map +1 -0
  3. package/dist/anim/Animator.js +382 -0
  4. package/dist/anim/Animator.js.map +1 -0
  5. package/dist/anim/Path.d.ts +30 -0
  6. package/dist/anim/Path.d.ts.map +1 -0
  7. package/dist/anim/Path.js +100 -0
  8. package/dist/anim/Path.js.map +1 -0
  9. package/dist/anim/easing.d.ts +70 -0
  10. package/dist/anim/easing.d.ts.map +1 -0
  11. package/dist/anim/easing.js +170 -0
  12. package/dist/anim/easing.js.map +1 -0
  13. package/dist/anim/index.d.ts +6 -0
  14. package/dist/anim/index.d.ts.map +1 -0
  15. package/dist/anim/index.js +4 -0
  16. package/dist/anim/index.js.map +1 -0
  17. package/dist/browser/assets/{s3mWorker-hZY5KqWF.js → s3mWorker-D7AV6O30.js} +1 -1
  18. package/dist/browser/s3m-three.iife.ext-three.js +60 -22
  19. package/dist/browser/s3m-three.iife.js +60 -22
  20. package/dist/core/TextureDecode.d.ts +22 -1
  21. package/dist/core/TextureDecode.d.ts.map +1 -1
  22. package/dist/core/TextureDecode.js +47 -3
  23. package/dist/core/TextureDecode.js.map +1 -1
  24. package/dist/core/TileGeometry.d.ts +92 -0
  25. package/dist/core/TileGeometry.d.ts.map +1 -0
  26. package/dist/core/TileGeometry.js +607 -0
  27. package/dist/core/TileGeometry.js.map +1 -0
  28. package/dist/core/types.d.ts +16 -0
  29. package/dist/core/types.d.ts.map +1 -1
  30. package/dist/index.d.ts +3 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +1 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/render/EdgeLines.d.ts +62 -14
  35. package/dist/render/EdgeLines.d.ts.map +1 -1
  36. package/dist/render/EdgeLines.js +97 -8
  37. package/dist/render/EdgeLines.js.map +1 -1
  38. package/dist/render/FeatureStyleManager.d.ts +17 -1
  39. package/dist/render/FeatureStyleManager.d.ts.map +1 -1
  40. package/dist/render/FeatureStyleManager.js +14 -4
  41. package/dist/render/FeatureStyleManager.js.map +1 -1
  42. package/dist/render/GeometryBuilder.d.ts +8 -10
  43. package/dist/render/GeometryBuilder.d.ts.map +1 -1
  44. package/dist/render/GeometryBuilder.js +77 -555
  45. package/dist/render/GeometryBuilder.js.map +1 -1
  46. package/dist/render/MergeVertices.d.ts +7 -0
  47. package/dist/render/MergeVertices.d.ts.map +1 -1
  48. package/dist/render/MergeVertices.js +10 -1
  49. package/dist/render/MergeVertices.js.map +1 -1
  50. package/dist/render/S3MViewer.d.ts +218 -9
  51. package/dist/render/S3MViewer.d.ts.map +1 -1
  52. package/dist/render/S3MViewer.js +772 -68
  53. package/dist/render/S3MViewer.js.map +1 -1
  54. package/dist/render/StyleMaterial.d.ts +19 -0
  55. package/dist/render/StyleMaterial.d.ts.map +1 -1
  56. package/dist/render/StyleMaterial.js +37 -0
  57. package/dist/render/StyleMaterial.js.map +1 -1
  58. package/dist/render/TextureFactory.d.ts +2 -1
  59. package/dist/render/TextureFactory.d.ts.map +1 -1
  60. package/dist/render/TextureFactory.js +30 -6
  61. package/dist/render/TextureFactory.js.map +1 -1
  62. package/dist/tileset/S3MTileset.d.ts +6 -2
  63. package/dist/tileset/S3MTileset.d.ts.map +1 -1
  64. package/dist/tileset/S3MTileset.js +17 -7
  65. package/dist/tileset/S3MTileset.js.map +1 -1
  66. package/dist/worker/S3MWorkerClient.d.ts +12 -4
  67. package/dist/worker/S3MWorkerClient.d.ts.map +1 -1
  68. package/dist/worker/S3MWorkerClient.js +12 -8
  69. package/dist/worker/S3MWorkerClient.js.map +1 -1
  70. package/dist/worker/s3mWorker.js +11 -3
  71. package/dist/worker/s3mWorker.js.map +1 -1
  72. package/docs/ANIMATION.md +534 -0
  73. package/docs/API.md +4 -0
  74. package/package.json +1 -1
  75. package/src/anim/Animator.ts +460 -0
  76. package/src/anim/Path.ts +107 -0
  77. package/src/anim/easing.ts +195 -0
  78. package/src/anim/index.ts +10 -0
  79. package/src/core/TextureDecode.ts +69 -3
  80. package/src/core/TileGeometry.ts +745 -0
  81. package/src/core/types.ts +8 -0
  82. package/src/index.ts +10 -0
  83. package/src/render/EdgeLines.ts +131 -15
  84. package/src/render/FeatureStyleManager.ts +12 -4
  85. package/src/render/GeometryBuilder.ts +91 -617
  86. package/src/render/MergeVertices.ts +11 -1
  87. package/src/render/S3MViewer.ts +866 -67
  88. package/src/render/StyleMaterial.ts +54 -1
  89. package/src/render/TextureFactory.ts +42 -8
  90. package/src/tileset/S3MTileset.ts +25 -9
  91. package/src/worker/S3MWorkerClient.ts +18 -9
  92. package/src/worker/s3mWorker.ts +24 -6
package/src/core/types.ts CHANGED
@@ -281,5 +281,13 @@ export interface StyleUniforms {
281
281
  uFilterHeightActive?: { value: number };
282
282
  uRoofThreshold?: { value: number };
283
283
  uTexMix?: { value: number };
284
+ /** 基础漫反射底色增益 / 直通自发光微调(0~1,默认 0.0) */
285
+ uAmbientGain?: { value: number };
286
+ /** 图层整体透明度(共享 uniform,透明度动画靠它做到 O(1)) */
287
+ uOpacity?: { value: number };
288
+ /** 棱线淡入淡出乘子(setEdges 过渡期间 0→1) */
289
+ uEdgeFade?: { value: number };
290
+ /** 整体压暗乘子(mode 切换的 fade-through) */
291
+ uFade?: { value: number };
284
292
  [key: string]: { value: unknown } | undefined;
285
293
  }
package/src/index.ts CHANGED
@@ -96,6 +96,7 @@ export type {
96
96
  GroundMode,
97
97
  GroundOptions,
98
98
  LightOptions,
99
+ PathFollowOptions,
99
100
  } from './render/S3MViewer.js';
100
101
 
101
102
  export { buildTileObjects } from './render/GeometryBuilder.js';
@@ -117,6 +118,15 @@ export type { StyleMode, SelectMode, StyledMaterial, StyledMaterialOptions, Outl
117
118
  export { Picker } from './render/Picker.js';
118
119
  export type { PickHit } from './render/Picker.js';
119
120
 
121
+ export { Animator, Path, easing, cubicBezier, steps, EASING_NAMES } from './anim/index.js';
122
+ export type {
123
+ Channel,
124
+ SpringSpec,
125
+ TransitionOptions,
126
+ EasingFn,
127
+ EasingName,
128
+ } from './anim/index.js';
129
+
120
130
  export {
121
131
  buildOrthoMVP,
122
132
  extractProjectionMatrix,
@@ -13,6 +13,7 @@
13
13
  * B 端算出来的法线正好和 A 端相反,反号一次才能拼成同一个四边形。
14
14
  */
15
15
  import * as THREE from 'three';
16
+ import { DUMMY_BATCH_TEX } from './StyleMaterial.js';
16
17
 
17
18
  /** 所有等宽线材质共享的分辨率 uniform(每帧更新一次即可) */
18
19
  export interface LineSharedUniforms {
@@ -20,14 +21,34 @@ export interface LineSharedUniforms {
20
21
  uDpr: { value: number };
21
22
  }
22
23
 
24
+ /**
25
+ * 等宽线的「外观」uniform(颜色 / 粗细 / 透明度 / 拔高)。
26
+ *
27
+ * 棱线材质是**逐网格一份**的(见 createThickLineMaterial 的 batchUniforms 参数),
28
+ * 但外观必须全局统一——所以这些 `{ value }` 对象由调用方持有并共享给每一份材质,
29
+ * 改一次全场景生效,棱线淡入淡出才能保持 O(1)。
30
+ */
31
+ export interface ThickLineStyleUniforms {
32
+ uColor: { value: THREE.Color };
33
+ uWidth: { value: number };
34
+ uOpacity: { value: number };
35
+ uElevation?: { value: number };
36
+ }
37
+
38
+ /** 等宽线的「单体状态」uniform:直接复用主材质 userData 里那几个对象,自动跟随 */
39
+ export interface ThickLineBatchUniforms {
40
+ uBatchTex: { value: THREE.Texture };
41
+ uBatchTexWidth: { value: number };
42
+ uBatchTexActive: { value: number };
43
+ }
44
+
23
45
  export interface ThickLineMaterial extends THREE.ShaderMaterial {
24
- uniforms: LineSharedUniforms & {
25
- uColor: { value: THREE.Color };
26
- uOpacity: { value: number };
27
- uWidth: { value: number };
28
- uElevation: { value: number };
29
- [uniform: string]: THREE.IUniform;
30
- };
46
+ uniforms: LineSharedUniforms &
47
+ ThickLineStyleUniforms &
48
+ Required<ThickLineBatchUniforms> & {
49
+ uTranslation: { value: THREE.Vector3 };
50
+ [uniform: string]: THREE.IUniform;
51
+ };
31
52
  }
32
53
 
33
54
  export function createLineSharedUniforms(): LineSharedUniforms {
@@ -40,18 +61,42 @@ export function createLineSharedUniforms(): LineSharedUniforms {
40
61
  const VERT = /* glsl */ `
41
62
  attribute vec3 aOther;
42
63
  attribute float aSide;
64
+ attribute float aBatchId;
43
65
 
44
66
  uniform vec2 uResolution;
45
67
  uniform float uDpr;
46
68
  uniform float uWidth;
47
69
  uniform float uElevation;
48
70
  uniform vec3 uTranslation;
71
+ uniform sampler2D uBatchTex;
72
+ uniform float uBatchTexWidth;
73
+ uniform int uBatchTexActive;
49
74
 
50
75
  void main() {
76
+ // 单体平移 / 拔高 / 隐藏:与主着色器同一份状态纹理、同一套口径。
77
+ // 不采样的话,单体一动棱线就留在原地——这正是「棱线跟随」要解决的。
78
+ vec3 s3mA = position;
79
+ vec3 s3mB = aOther;
80
+ if ( uBatchTexActive > 0 && aBatchId >= 0.0 ) {
81
+ vec4 bState = texture2D( uBatchTex, vec2( ( aBatchId + 0.5 ) / uBatchTexWidth, 0.25 ) );
82
+ if ( bState.a < -0.5 ) {
83
+ // 该单体被隐藏:顶点着色器不能 discard,扔到裁剪空间外
84
+ gl_Position = vec4( 2.0, 2.0, 2.0, 1.0 );
85
+ return;
86
+ }
87
+ vec4 bOffset = texture2D( uBatchTex, vec2( ( aBatchId + 0.5 ) / uBatchTexWidth, 0.75 ) );
88
+ vec3 s3mOffset = bOffset.xyz;
89
+ if ( s3mOffset.z == 0.0 && bState.a > 0.0 ) {
90
+ s3mOffset.z = bState.a;
91
+ }
92
+ s3mA += s3mOffset;
93
+ s3mB += s3mOffset;
94
+ }
95
+
51
96
  // 选中单体平移/拔高:与着色器里 transformed += uTranslation 保持同一口径(物体空间)
52
97
  vec3 offset = uTranslation + vec3( 0.0, 0.0, uElevation );
53
- vec4 clipA = projectionMatrix * modelViewMatrix * vec4( position + offset, 1.0 );
54
- vec4 clipB = projectionMatrix * modelViewMatrix * vec4( aOther + offset, 1.0 );
98
+ vec4 clipA = projectionMatrix * modelViewMatrix * vec4( s3mA + offset, 1.0 );
99
+ vec4 clipB = projectionMatrix * modelViewMatrix * vec4( s3mB + offset, 1.0 );
55
100
 
56
101
  if ( clipA.w <= 0.0 || clipB.w <= 0.0 ) {
57
102
  // 端点跑到相机后面,整段丢弃(裁到近平面的线段会有轻微截断,可接受)
@@ -87,19 +132,50 @@ const FRAG = /* glsl */ `
87
132
  }
88
133
  `;
89
134
 
135
+ /**
136
+ * 创建一份等宽线材质。
137
+ *
138
+ * @param styleUniforms 外观 uniform(颜色 / 粗细 / 透明度 / 拔高)。传入时**直接
139
+ * 复用这些 `{ value }` 对象**而不是新建——棱线材质逐网格一份,但外观必须全局统一,
140
+ * 共享对象才能让 `_syncEdgeStyle()` 的一次赋值作用到所有棱线上。
141
+ * @param batchUniforms 单体状态纹理 uniform。传入时同样直接复用主材质 userData 里
142
+ * 的那几个对象,主材质换纹理 / 开关 active 时棱线自动跟着走,零同步成本。
143
+ * 不传(或几何没有 aBatchId)时棱线不参与单体状态。
144
+ */
90
145
  export function createThickLineMaterial(
91
146
  shared: LineSharedUniforms,
92
- opts: { color?: THREE.ColorRepresentation; opacity?: number; width?: number; elevation?: number } = {}
147
+ opts: { color?: THREE.ColorRepresentation; opacity?: number; width?: number; elevation?: number } = {},
148
+ styleUniforms?: ThickLineStyleUniforms,
149
+ batchUniforms?: ThickLineBatchUniforms
93
150
  ): ThickLineMaterial {
151
+ const style: ThickLineStyleUniforms = styleUniforms ?? {
152
+ uColor: { value: new THREE.Color(opts.color ?? '#ffffff') },
153
+ uOpacity: { value: opts.opacity ?? 1 },
154
+ uWidth: { value: opts.width ?? 1.5 },
155
+ uElevation: { value: opts.elevation ?? 0 },
156
+ };
157
+ if (opts.color !== undefined && styleUniforms) style.uColor.value.set(opts.color);
158
+ if (opts.opacity !== undefined && styleUniforms) style.uOpacity.value = opts.opacity;
159
+ if (opts.width !== undefined && styleUniforms) style.uWidth.value = opts.width;
160
+
161
+ const batch: Required<ThickLineBatchUniforms> = batchUniforms ?? {
162
+ uBatchTex: { value: DUMMY_BATCH_TEX },
163
+ uBatchTexWidth: { value: 1 },
164
+ uBatchTexActive: { value: 0 },
165
+ };
166
+
94
167
  const mat = new THREE.ShaderMaterial({
95
168
  uniforms: {
96
169
  uResolution: shared.uResolution,
97
170
  uDpr: shared.uDpr,
98
- uColor: { value: new THREE.Color(opts.color ?? '#ffffff') },
99
- uOpacity: { value: opts.opacity ?? 1 },
100
- uWidth: { value: opts.width ?? 1.5 },
101
- uElevation: { value: opts.elevation ?? 0 },
171
+ uColor: style.uColor,
172
+ uOpacity: style.uOpacity,
173
+ uWidth: style.uWidth,
174
+ uElevation: style.uElevation ?? { value: opts.elevation ?? 0 },
102
175
  uTranslation: { value: new THREE.Vector3(0, 0, 0) },
176
+ uBatchTex: batch.uBatchTex,
177
+ uBatchTexWidth: batch.uBatchTexWidth,
178
+ uBatchTexActive: batch.uBatchTexActive,
103
179
  },
104
180
  vertexShader: VERT,
105
181
  fragmentShader: FRAG,
@@ -117,14 +193,22 @@ export function createThickLineMaterial(
117
193
 
118
194
  /**
119
195
  * 线段对(每 6 个 float:x1 y1 z1 x2 y2 z2)→ 可绘制等宽线的几何体。
196
+ *
197
+ * @param batchIds 每段所属的单体 batchId(长度 = 段数)。给了它,顶点着色器才能
198
+ * 按单体从状态纹理取平移/拔高——棱线跟随移动靠的就是这个。不传全部填 -1,
199
+ * 着色器会跳过采样(等于不参与单体状态)。
120
200
  */
121
- export function buildThickLineGeometry(segments: ArrayLike<number>): THREE.BufferGeometry {
201
+ export function buildThickLineGeometry(
202
+ segments: ArrayLike<number>,
203
+ batchIds?: ArrayLike<number>
204
+ ): THREE.BufferGeometry {
122
205
  const count = Math.floor(segments.length / 6);
123
206
  const vertexCount = count * 4;
124
207
 
125
208
  const position = new Float32Array(vertexCount * 3);
126
209
  const other = new Float32Array(vertexCount * 3);
127
210
  const side = new Float32Array(vertexCount);
211
+ const batch = new Float32Array(vertexCount);
128
212
  const index = vertexCount > 65535 ? new Uint32Array(count * 6) : new Uint16Array(count * 6);
129
213
 
130
214
  for (let i = 0; i < count; i++) {
@@ -135,6 +219,7 @@ export function buildThickLineGeometry(segments: ArrayLike<number>): THREE.Buffe
135
219
  const bx = segments[s + 3];
136
220
  const by = segments[s + 4];
137
221
  const bz = segments[s + 5];
222
+ const b = batchIds ? Number(batchIds[i] ?? -1) : -1;
138
223
 
139
224
  const v = i * 4;
140
225
  // A 端两枚
@@ -143,6 +228,7 @@ export function buildThickLineGeometry(segments: ArrayLike<number>): THREE.Buffe
143
228
  // B 端两枚(side 反号)
144
229
  setVertex(v + 2, bx, by, bz, ax, ay, az, 1);
145
230
  setVertex(v + 3, bx, by, bz, ax, ay, az, -1);
231
+ for (let k = 0; k < 4; k++) batch[v + k] = b;
146
232
 
147
233
  const o = i * 6;
148
234
  index[o] = v;
@@ -177,11 +263,41 @@ export function buildThickLineGeometry(segments: ArrayLike<number>): THREE.Buffe
177
263
  geometry.setAttribute('position', new THREE.BufferAttribute(position, 3));
178
264
  geometry.setAttribute('aOther', new THREE.BufferAttribute(other, 3));
179
265
  geometry.setAttribute('aSide', new THREE.BufferAttribute(side, 1));
266
+ geometry.setAttribute('aBatchId', new THREE.BufferAttribute(batch, 1));
180
267
  geometry.setIndex(new THREE.BufferAttribute(index, 1));
181
268
  geometry.computeBoundingSphere();
182
269
  return geometry;
183
270
  }
184
271
 
272
+ /**
273
+ * 从带 `aBatchId` 的几何里建「量化位置 → batchId」的查找表。
274
+ *
275
+ * `EdgesGeometry` 输出的线段只有坐标、没有顶点号,要知道每段棱线属于哪个单体,
276
+ * 只能拿端点坐标回来查。key 的量化方式必须和 `mergeVerticesByPosition` 完全一致,
277
+ * 否则焊过的顶点会对不上。返回 null 表示这个几何没有单体化信息。
278
+ */
279
+ export function buildPositionBatchMap(
280
+ geometry: THREE.BufferGeometry,
281
+ tolerance: number
282
+ ): Map<string, number> | null {
283
+ const pos = geometry.getAttribute('position') as THREE.BufferAttribute | undefined;
284
+ const bid = geometry.getAttribute('aBatchId') as THREE.BufferAttribute | undefined;
285
+ if (!pos || !bid || pos.count === 0 || pos.count !== bid.count) return null;
286
+
287
+ const inv = 1 / Math.max(tolerance, 1e-9);
288
+ const map = new Map<string, number>();
289
+ for (let i = 0; i < pos.count; i++) {
290
+ const key = positionQuantKey(pos.getX(i), pos.getY(i), pos.getZ(i), inv);
291
+ if (!map.has(key)) map.set(key, bid.getX(i));
292
+ }
293
+ return map.size > 0 ? map : null;
294
+ }
295
+
296
+ /** 位置量化 key:与 mergeVerticesByPosition 同一套网格,保证焊过的顶点对得上 */
297
+ export function positionQuantKey(x: number, y: number, z: number, inv: number): string {
298
+ return `${Math.round(x * inv)},${Math.round(y * inv)},${Math.round(z * inv)}`;
299
+ }
300
+
185
301
  /* ------------------------------------------------------------------ 单体棱线提取 */
186
302
 
187
303
  export interface EdgeVertexRange {
@@ -185,7 +185,15 @@ export class FeatureStyleManager {
185
185
  private readonly _triggerRepaint: () => void,
186
186
  private readonly _onStyleChanged: () => void,
187
187
  private readonly _getAllMeshes?: () => Iterable<THREE.Mesh>,
188
- private readonly _applyOverrideHook?: (mesh: THREE.Mesh) => void
188
+ private readonly _applyOverrideHook?: (mesh: THREE.Mesh) => void,
189
+ /**
190
+ * 图层自身是否处于半透明状态。
191
+ *
192
+ * 以前这里读 `material.opacity` 来判断「要不要把 transparent 还原」——自从全局
193
+ * 透明度改走共享 `uOpacity` 之后,`material.opacity` 不再等于图层透明度,
194
+ * 再读它就会在图层半透明时把材质错判成不透明、把整层透明效果抹掉。
195
+ */
196
+ private readonly _isLayerTransparent: () => boolean = () => false
189
197
  ) {}
190
198
 
191
199
  setFeatureStyle(smid: number, style: FeatureStyle, meshes?: Iterable<THREE.Mesh>): void {
@@ -565,7 +573,7 @@ export class FeatureStyleManager {
565
573
  mat.userData.uBatchTexActive.value = 0;
566
574
  }
567
575
  // 单体淡化清除后,把材质 transparent 还原成全局透明度语义
568
- if (mat.transparent && mat.opacity >= 0.999) mat.transparent = false;
576
+ if (mat.transparent && !this._isLayerTransparent()) mat.transparent = false;
569
577
  return;
570
578
  }
571
579
 
@@ -573,7 +581,7 @@ export class FeatureStyleManager {
573
581
  if (mat.userData.uBatchTexActive) {
574
582
  mat.userData.uBatchTexActive.value = 0;
575
583
  }
576
- if (mat.transparent && mat.opacity >= 0.999) mat.transparent = false;
584
+ if (mat.transparent && !this._isLayerTransparent()) mat.transparent = false;
577
585
  return;
578
586
  }
579
587
 
@@ -731,7 +739,7 @@ export class FeatureStyleManager {
731
739
 
732
740
  // 有淡化单体时整块材质要开混合才有透明效果;清除后还原为全局透明度语义
733
741
  if (hasFade && !mat.transparent) mat.transparent = true;
734
- else if (!hasFade && mat.transparent && mat.opacity >= 0.999) mat.transparent = false;
742
+ else if (!hasFade && mat.transparent && !this._isLayerTransparent()) mat.transparent = false;
735
743
 
736
744
  let conflicts = 0;
737
745
  for (const [bId, owners] of ownersOfBatch) {