u-space 0.0.2 → 0.0.5
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/index.cjs +1 -1
- package/dist/index.js +390 -351
- package/dist/plugins/u-manager/index.cjs +2 -2
- package/dist/plugins/u-manager/index.js +54 -43
- package/dist/src/objects/Model.d.ts +14 -1
- package/dist/src/objects/Poi.d.ts +4 -0
- package/dist/src/viewers/Viewer.d.ts +4 -0
- package/docs/api-objects.md +83 -3
- package/docs/api-plugins.md +9 -11
- package/docs/api-viewer.md +52 -4
- package/docs/examples-guide.md +14 -0
- package/docs/getting-started.md +1 -2
- package/docs/index.md +2 -0
- package/package.json +2 -2
|
@@ -3342,16 +3342,27 @@ function requireWhichTypedArray() {
|
|
|
3342
3342
|
var O = r(E);
|
|
3343
3343
|
S = p(O, Symbol.toStringTag);
|
|
3344
3344
|
}
|
|
3345
|
-
|
|
3345
|
+
if (S && S.get) {
|
|
3346
|
+
var C = b(S.get);
|
|
3347
|
+
s[
|
|
3348
|
+
/** @type {`$${import('.').TypedArrayName}`} */
|
|
3349
|
+
"$" + m
|
|
3350
|
+
] = C;
|
|
3351
|
+
}
|
|
3346
3352
|
}
|
|
3347
3353
|
}) : h(n, function(m) {
|
|
3348
3354
|
var A = new t[m](), E = A.slice || A.set;
|
|
3349
|
-
E
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
+
if (E) {
|
|
3356
|
+
var S = (
|
|
3357
|
+
/** @type {import('./types').BoundSlice | import('./types').BoundSet} */
|
|
3358
|
+
// @ts-expect-error TODO FIXME
|
|
3359
|
+
b(E)
|
|
3360
|
+
);
|
|
3361
|
+
s[
|
|
3362
|
+
/** @type {`$${import('.').TypedArrayName}`} */
|
|
3363
|
+
"$" + m
|
|
3364
|
+
] = S;
|
|
3365
|
+
}
|
|
3355
3366
|
});
|
|
3356
3367
|
var l = function(A) {
|
|
3357
3368
|
var E = !1;
|
|
@@ -4591,43 +4602,11 @@ function require_stream_duplex$1() {
|
|
|
4591
4602
|
}
|
|
4592
4603
|
}), _stream_duplex$1;
|
|
4593
4604
|
}
|
|
4594
|
-
var string_decoder = {},
|
|
4595
|
-
function requireSafeBuffer() {
|
|
4596
|
-
return hasRequiredSafeBuffer || (hasRequiredSafeBuffer = 1, (function(h, d) {
|
|
4597
|
-
var b = requireDist(), u = b.Buffer;
|
|
4598
|
-
function p(f, o) {
|
|
4599
|
-
for (var t in f)
|
|
4600
|
-
o[t] = f[t];
|
|
4601
|
-
}
|
|
4602
|
-
u.from && u.alloc && u.allocUnsafe && u.allocUnsafeSlow ? h.exports = b : (p(b, d), d.Buffer = r);
|
|
4603
|
-
function r(f, o, t) {
|
|
4604
|
-
return u(f, o, t);
|
|
4605
|
-
}
|
|
4606
|
-
p(u, r), r.from = function(f, o, t) {
|
|
4607
|
-
if (typeof f == "number")
|
|
4608
|
-
throw new TypeError("Argument must not be a number");
|
|
4609
|
-
return u(f, o, t);
|
|
4610
|
-
}, r.alloc = function(f, o, t) {
|
|
4611
|
-
if (typeof f != "number")
|
|
4612
|
-
throw new TypeError("Argument must be a number");
|
|
4613
|
-
var n = u(f);
|
|
4614
|
-
return o !== void 0 ? typeof t == "string" ? n.fill(o, t) : n.fill(o) : n.fill(0), n;
|
|
4615
|
-
}, r.allocUnsafe = function(f) {
|
|
4616
|
-
if (typeof f != "number")
|
|
4617
|
-
throw new TypeError("Argument must be a number");
|
|
4618
|
-
return u(f);
|
|
4619
|
-
}, r.allocUnsafeSlow = function(f) {
|
|
4620
|
-
if (typeof f != "number")
|
|
4621
|
-
throw new TypeError("Argument must be a number");
|
|
4622
|
-
return b.SlowBuffer(f);
|
|
4623
|
-
};
|
|
4624
|
-
})(safeBuffer, safeBuffer.exports)), safeBuffer.exports;
|
|
4625
|
-
}
|
|
4626
|
-
var hasRequiredString_decoder;
|
|
4605
|
+
var string_decoder = {}, hasRequiredString_decoder;
|
|
4627
4606
|
function requireString_decoder() {
|
|
4628
4607
|
if (hasRequiredString_decoder) return string_decoder;
|
|
4629
4608
|
hasRequiredString_decoder = 1;
|
|
4630
|
-
var h = requireSafeBuffer().Buffer, d = h.isEncoding || function(E) {
|
|
4609
|
+
var h = requireSafeBuffer$1().Buffer, d = h.isEncoding || function(E) {
|
|
4631
4610
|
switch (E = "" + E, E && E.toLowerCase()) {
|
|
4632
4611
|
case "hex":
|
|
4633
4612
|
case "utf8":
|
|
@@ -5688,6 +5667,38 @@ var streamBrowser, hasRequiredStreamBrowser;
|
|
|
5688
5667
|
function requireStreamBrowser() {
|
|
5689
5668
|
return hasRequiredStreamBrowser || (hasRequiredStreamBrowser = 1, streamBrowser = requireEvents().EventEmitter), streamBrowser;
|
|
5690
5669
|
}
|
|
5670
|
+
var safeBuffer = { exports: {} }, hasRequiredSafeBuffer;
|
|
5671
|
+
function requireSafeBuffer() {
|
|
5672
|
+
return hasRequiredSafeBuffer || (hasRequiredSafeBuffer = 1, (function(h, d) {
|
|
5673
|
+
var b = requireDist(), u = b.Buffer;
|
|
5674
|
+
function p(f, o) {
|
|
5675
|
+
for (var t in f)
|
|
5676
|
+
o[t] = f[t];
|
|
5677
|
+
}
|
|
5678
|
+
u.from && u.alloc && u.allocUnsafe && u.allocUnsafeSlow ? h.exports = b : (p(b, d), d.Buffer = r);
|
|
5679
|
+
function r(f, o, t) {
|
|
5680
|
+
return u(f, o, t);
|
|
5681
|
+
}
|
|
5682
|
+
p(u, r), r.from = function(f, o, t) {
|
|
5683
|
+
if (typeof f == "number")
|
|
5684
|
+
throw new TypeError("Argument must not be a number");
|
|
5685
|
+
return u(f, o, t);
|
|
5686
|
+
}, r.alloc = function(f, o, t) {
|
|
5687
|
+
if (typeof f != "number")
|
|
5688
|
+
throw new TypeError("Argument must be a number");
|
|
5689
|
+
var n = u(f);
|
|
5690
|
+
return o !== void 0 ? typeof t == "string" ? n.fill(o, t) : n.fill(o) : n.fill(0), n;
|
|
5691
|
+
}, r.allocUnsafe = function(f) {
|
|
5692
|
+
if (typeof f != "number")
|
|
5693
|
+
throw new TypeError("Argument must be a number");
|
|
5694
|
+
return u(f);
|
|
5695
|
+
}, r.allocUnsafeSlow = function(f) {
|
|
5696
|
+
if (typeof f != "number")
|
|
5697
|
+
throw new TypeError("Argument must be a number");
|
|
5698
|
+
return b.SlowBuffer(f);
|
|
5699
|
+
};
|
|
5700
|
+
})(safeBuffer, safeBuffer.exports)), safeBuffer.exports;
|
|
5701
|
+
}
|
|
5691
5702
|
var util = {}, hasRequiredUtil;
|
|
5692
5703
|
function requireUtil() {
|
|
5693
5704
|
if (hasRequiredUtil) return util;
|
|
@@ -9898,7 +9909,7 @@ function requireBn$1() {
|
|
|
9898
9909
|
var _ = 67108863 ^ 67108863 >>> g << g;
|
|
9899
9910
|
this.words[this.length - 1] &= _;
|
|
9900
9911
|
}
|
|
9901
|
-
return this.strip();
|
|
9912
|
+
return this.length === 0 && (this.words[0] = 0, this.length = 1), this.strip();
|
|
9902
9913
|
}, r.prototype.maskn = function(v) {
|
|
9903
9914
|
return this.clone().imaskn(v);
|
|
9904
9915
|
}, r.prototype.iaddn = function(v) {
|
|
@@ -11279,7 +11290,7 @@ function requireBn() {
|
|
|
11279
11290
|
var q = 67108863 ^ 67108863 >>> _ << _;
|
|
11280
11291
|
this.words[this.length - 1] &= q;
|
|
11281
11292
|
}
|
|
11282
|
-
return this._strip();
|
|
11293
|
+
return this.length === 0 && (this.words[0] = 0, this.length = 1), this._strip();
|
|
11283
11294
|
}, r.prototype.maskn = function(c) {
|
|
11284
11295
|
return this.clone().imaskn(c);
|
|
11285
11296
|
}, r.prototype.iaddn = function(c) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Object3D } from 'three/webgpu';
|
|
1
|
+
import { type Object3D, AnimationMixer, type AnimationAction, type AnimationClip } from 'three/webgpu';
|
|
2
2
|
import { type InteractionEventMap } from '../interactions';
|
|
3
3
|
import { BaseGroup } from './BaseGroup';
|
|
4
4
|
export interface ModelParameters {
|
|
@@ -6,12 +6,25 @@ export interface ModelParameters {
|
|
|
6
6
|
cache?: boolean;
|
|
7
7
|
persistent?: boolean;
|
|
8
8
|
}
|
|
9
|
+
export interface PlayAnimationOptions {
|
|
10
|
+
loop?: boolean;
|
|
11
|
+
repetitions?: number;
|
|
12
|
+
timeScale?: number;
|
|
13
|
+
clampWhenFinished?: boolean;
|
|
14
|
+
}
|
|
9
15
|
export declare class Model extends BaseGroup<InteractionEventMap> {
|
|
10
16
|
readonly isModel = true;
|
|
11
17
|
type: string;
|
|
12
18
|
private static memoryCache;
|
|
19
|
+
mixer: AnimationMixer | null;
|
|
20
|
+
animations: AnimationClip[];
|
|
21
|
+
private actions;
|
|
13
22
|
constructor();
|
|
14
23
|
loadAsync(parameters: ModelParameters): Promise<Object3D<import("three").Object3DEventMap> | null>;
|
|
24
|
+
playAnimation(nameOrIndex?: string | number, options?: PlayAnimationOptions): AnimationAction | null;
|
|
25
|
+
playAllAnimations(options?: PlayAnimationOptions): AnimationAction[];
|
|
26
|
+
stopAnimation(nameOrIndex?: string | number): void;
|
|
27
|
+
updateAnimation(delta: number): void;
|
|
15
28
|
static clearMemoryCache(): void;
|
|
16
29
|
static clearPersistentCache(): Promise<void>;
|
|
17
30
|
}
|
|
@@ -21,11 +21,15 @@ export interface PoiParameters {
|
|
|
21
21
|
borderRadius?: number;
|
|
22
22
|
/** Text position relative to the icon */
|
|
23
23
|
textPosition?: 'top' | 'bottom' | 'left' | 'right';
|
|
24
|
+
/** Scale factor for mapping canvas pixels to world units */
|
|
25
|
+
scaleFactor?: number;
|
|
24
26
|
}
|
|
25
27
|
export declare class Poi extends BaseSprite<InteractionEventMap> {
|
|
26
28
|
readonly isPoi = true;
|
|
27
29
|
type: string;
|
|
28
30
|
parameters: Required<PoiParameters>;
|
|
31
|
+
private _canvas;
|
|
32
|
+
private _lastParametersJSON;
|
|
29
33
|
constructor(parameters?: PoiParameters);
|
|
30
34
|
/**
|
|
31
35
|
* Update the POI with new parameters
|
|
@@ -14,15 +14,19 @@ export interface ViewerOptions {
|
|
|
14
14
|
export interface ViewerEventMap {
|
|
15
15
|
beforeControlsUpdate: {
|
|
16
16
|
time: number;
|
|
17
|
+
delta: number;
|
|
17
18
|
};
|
|
18
19
|
afterControlsUpdate: {
|
|
19
20
|
time: number;
|
|
21
|
+
delta: number;
|
|
20
22
|
};
|
|
21
23
|
beforeRender: {
|
|
22
24
|
time: number;
|
|
25
|
+
delta: number;
|
|
23
26
|
};
|
|
24
27
|
afterRender: {
|
|
25
28
|
time: number;
|
|
29
|
+
delta: number;
|
|
26
30
|
};
|
|
27
31
|
cameraChange: {
|
|
28
32
|
camera: PerspectiveCamera | OrthographicCamera;
|
package/docs/api-objects.md
CHANGED
|
@@ -66,6 +66,64 @@ const model3 = new Model();
|
|
|
66
66
|
model3.loadAsync({ url: 'model.glb', cache: true, persistent: true });
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
+
### 动画
|
|
70
|
+
|
|
71
|
+
`Model` 在加载含内置动画的模型(如 glTF/FBX)后,会自动初始化 `AnimationMixer` 并收集 `AnimationClip`。
|
|
72
|
+
|
|
73
|
+
**属性:**
|
|
74
|
+
|
|
75
|
+
| 属性 | 类型 | 说明 |
|
|
76
|
+
| :----------- | :-------------------------- | :----------------------------------------- |
|
|
77
|
+
| `mixer` | `AnimationMixer \| null` | 动画混合器,加载含动画模型后自动创建。 |
|
|
78
|
+
| `animations` | `AnimationClip[]` | 模型内置的动画片段列表。 |
|
|
79
|
+
|
|
80
|
+
#### `playAnimation(nameOrIndex?, options?)`
|
|
81
|
+
|
|
82
|
+
播放指定动画。不传参时播放第一个动画。
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// 按名称播放
|
|
86
|
+
model.playAnimation('Walk');
|
|
87
|
+
|
|
88
|
+
// 按索引播放
|
|
89
|
+
model.playAnimation(0);
|
|
90
|
+
|
|
91
|
+
// 带选项
|
|
92
|
+
model.playAnimation('Run', {
|
|
93
|
+
loop: true, // 是否循环(默认 true)
|
|
94
|
+
repetitions: 3, // 循环次数(默认 Infinity)
|
|
95
|
+
timeScale: 1.5, // 播放速度(默认 1)
|
|
96
|
+
clampWhenFinished: true, // 播放结束后停留在最后一帧(默认 false)
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### `playAllAnimations(options?)`
|
|
101
|
+
|
|
102
|
+
播放模型所有内置动画。参数同 `playAnimation`。
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
model.playAllAnimations({ timeScale: 2 });
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### `stopAnimation(nameOrIndex?)`
|
|
109
|
+
|
|
110
|
+
停止指定动画。不传参时停止所有动画。
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
model.stopAnimation('Walk'); // 停止指定动画
|
|
114
|
+
model.stopAnimation(); // 停止所有动画
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### `updateAnimation(delta)`
|
|
118
|
+
|
|
119
|
+
推进动画时间,需在渲染循环中调用。`delta` 为帧间隔时间(秒),可从 Viewer 事件的 `delta` 属性获取。
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
viewer.addEventListener('beforeRender', (e) => {
|
|
123
|
+
model.updateAnimation(e.delta);
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
69
127
|
### 清除缓存
|
|
70
128
|
|
|
71
129
|
可通过 `Model` 类的静态方法手动清除内部缓存:
|
|
@@ -186,7 +244,9 @@ const poi = new Poi({
|
|
|
186
244
|
backgroundColor: 'rgba(0,0,0,0.6)',
|
|
187
245
|
textPosition: 'right',
|
|
188
246
|
});
|
|
189
|
-
|
|
247
|
+
// 构造时会自动调用 updateAsync(),无需手动调用
|
|
248
|
+
// 如需更新参数,可再次调用:
|
|
249
|
+
// await poi.updateAsync({ text: '新文字' });
|
|
190
250
|
|
|
191
251
|
poi.position.set(10, 5, 10);
|
|
192
252
|
viewer.scene.add(poi);
|
|
@@ -203,15 +263,16 @@ viewer.scene.add(poi);
|
|
|
203
263
|
| `color` | `string` | `'#ffffff'` | 文字颜色。 |
|
|
204
264
|
| `iconSize` | `number` | `64` | 图标大小(像素)。 |
|
|
205
265
|
| `padding` | `number` | `10` | 内容四周的内边距。 |
|
|
206
|
-
| `backgroundColor` | `string` | `'rgba(0, 0, 0, 0
|
|
266
|
+
| `backgroundColor` | `string` | `'rgba(0, 0, 0, 0)'` | 背景填充颜色。 |
|
|
207
267
|
| `borderRadius` | `number` | `8` | 背景圆角半径。 |
|
|
208
268
|
| `textPosition` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'right'` | 文字相对图标的位置。 |
|
|
269
|
+
| `scaleFactor` | `number` | `0.01` | canvas 像素到世界单位的缩放系数。 |
|
|
209
270
|
|
|
210
271
|
### 方法
|
|
211
272
|
|
|
212
273
|
#### `updateAsync(parameters?)`
|
|
213
274
|
|
|
214
|
-
使用可选的参数覆盖重新渲染 canvas
|
|
275
|
+
使用可选的参数覆盖重新渲染 canvas 纹理。构造时会自动调用一次,后续参数未变化时会跳过重绘。
|
|
215
276
|
|
|
216
277
|
```typescript
|
|
217
278
|
await poi.updateAsync({ text: '更新后的标签', color: '#ffff00' });
|
|
@@ -306,6 +367,25 @@ viewer.render();
|
|
|
306
367
|
|
|
307
368
|
返回节点的邻接表(邻居 ID → 边权重)。
|
|
308
369
|
|
|
370
|
+
#### `exportData(): TopologyData`
|
|
371
|
+
|
|
372
|
+
将当前拓扑图(节点和边)导出为 JSON 可序列化对象。
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
const data = topo.exportData();
|
|
376
|
+
// { nodes: Record<string, {x,y,z}>, edges: Array<{from,to,weight}> }
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
双向边在导出时会去重,只保留一条记录。
|
|
380
|
+
|
|
381
|
+
#### `importData(data: TopologyData)`
|
|
382
|
+
|
|
383
|
+
从 `exportData()` 返回的数据中还原拓扑图,替换当前所有节点、边和路径网格,并重新渲染。
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
topo.importData(data);
|
|
387
|
+
```
|
|
388
|
+
|
|
309
389
|
#### `dispose()`
|
|
310
390
|
|
|
311
391
|
清除图和路径网格。
|
package/docs/api-plugins.md
CHANGED
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`u-space` 架构包含一套丰富的插件,提供从 GIS 瓦片加载到键盘控制和场景管理等高级功能。所有插件从 `u-space/plugins/*` 命名空间导入。
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
### `keyboard-controls`
|
|
5
|
+
## `keyboard-controls`
|
|
8
6
|
|
|
9
7
|
为激活相机提供 WASD 或方向键的平移和旋转控制。
|
|
10
8
|
|
|
@@ -45,7 +43,7 @@ import { ACTION } from 'u-space/plugins/keyboard-controls';
|
|
|
45
43
|
keyboardControls.keys['Space'] = ACTION.MOVE_UP;
|
|
46
44
|
```
|
|
47
45
|
|
|
48
|
-
|
|
46
|
+
## `minimap`
|
|
49
47
|
|
|
50
48
|
生成一个 2D 小地图叠加层,在 3D 场景中跟踪目标对象。渲染到注入 `viewer.el` 的 `<canvas>` 元素中。
|
|
51
49
|
|
|
@@ -82,7 +80,7 @@ minimap.dispose();
|
|
|
82
80
|
- `disable()` — 移除画布并停止渲染。
|
|
83
81
|
- `dispose()` — 禁用并清理所有资源。
|
|
84
82
|
|
|
85
|
-
|
|
83
|
+
## `tiles`
|
|
86
84
|
|
|
87
85
|
提供与 `3d-tiles-renderer` 和地理空间数据的集成。主要导出 `ArcgisTilesRenderer`,可流式加载 ArcGIS Online 3D 瓦片并将地球重新定向到指定地理位置。
|
|
88
86
|
|
|
@@ -109,7 +107,7 @@ arcgisTilesRenderer.enable();
|
|
|
109
107
|
| `disable()` | 从场景中移除瓦片并暂停更新循环。 |
|
|
110
108
|
| `dispose()` | 禁用并完全释放瓦片渲染器。 |
|
|
111
109
|
|
|
112
|
-
|
|
110
|
+
## `u-manager`
|
|
113
111
|
|
|
114
112
|
`u-manager` 是一套全面的加载器和解析器,用于从服务器路径流式传输、解密并显示结构化场景数据。支持场景、拓扑、动画、属性和相机视点。
|
|
115
113
|
|
|
@@ -233,7 +231,7 @@ const modelProps = properties.filter(p => p.modelId === myModel.userData.id);
|
|
|
233
231
|
| `value` | `string \| null` | 属性值。 |
|
|
234
232
|
| `label` | `string \| null` | 属性的显示标签。 |
|
|
235
233
|
|
|
236
|
-
|
|
234
|
+
## `curve-movement`
|
|
237
235
|
|
|
238
236
|
沿样条路径对相机或对象进行动画。提供两个具体子类:`CurveMovementCamera` 和 `CurveMovementObject`。
|
|
239
237
|
|
|
@@ -272,7 +270,7 @@ movement.addEventListener('complete', () => console.log('完成'));
|
|
|
272
270
|
| `positionOffset`| `Vector3` | `(0,0,0)` | 叠加到每个位置上的世界坐标偏移量。 |
|
|
273
271
|
| `direction` | `1 \| -1` | `1` | 当前行进方向。 |
|
|
274
272
|
|
|
275
|
-
|
|
273
|
+
## `tracking-controls`
|
|
276
274
|
|
|
277
275
|
使相机平滑跟随移动的 `Object3D` 目标。
|
|
278
276
|
|
|
@@ -296,7 +294,7 @@ tracking.enable();
|
|
|
296
294
|
| `type` | `'position' \| 'box3'` | `'position'` | 跟踪世界位置还是包围盒中心。 |
|
|
297
295
|
| `offset` | `Vector3` | `(0,0,0)` | 在移动相机前叠加到跟踪位置上的偏移量。 |
|
|
298
296
|
|
|
299
|
-
|
|
297
|
+
## `atmosphere`
|
|
300
298
|
|
|
301
299
|
天空和大气渲染插件。目前为占位实现,`enable()` / `disable()` / `dispose()` 方法可用,但尚未实现具体功能。
|
|
302
300
|
|
|
@@ -307,7 +305,7 @@ const atmosphere = new Atmosphere(viewer);
|
|
|
307
305
|
atmosphere.enable();
|
|
308
306
|
```
|
|
309
307
|
|
|
310
|
-
|
|
308
|
+
## `object-controls`
|
|
311
309
|
|
|
312
310
|
对场景中的 3D 对象进行交互式移动、旋转、缩放操作。继承自 Three.js `TransformControls`,集成了 u-space `Viewer` 生命周期管理。
|
|
313
311
|
|
|
@@ -375,7 +373,7 @@ controls.addEventListener('dragging-changed', ({ value }) => {
|
|
|
375
373
|
|
|
376
374
|
> 由于 `ObjectControls` 直接继承 `TransformControls`,所有原生属性(`translationSnap`、`rotationSnap`、`scaleSnap`、`showX/Y/Z` 等)和事件均可直接使用,参考 [Three.js TransformControls 文档](https://threejs.org/docs/#examples/en/controls/TransformControls)。
|
|
377
375
|
|
|
378
|
-
|
|
376
|
+
## `topology-drawer`
|
|
379
377
|
|
|
380
378
|
在 3D 场景中交互式绘制拓扑路径图。点击场景中的任意对象表面放置节点,自动连接成路径;点击空白处则落回不可见的地面平面。事件检测通过 `viewer.interactionManager` 实现(`scene.addEventListener` 冒泡)。
|
|
381
379
|
|
package/docs/api-viewer.md
CHANGED
|
@@ -100,6 +100,52 @@ const camera = viewer.createOrthographicCamera();
|
|
|
100
100
|
viewer.setCamera(camera);
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
+
## 调试工具
|
|
104
|
+
|
|
105
|
+
### Info
|
|
106
|
+
|
|
107
|
+
`viewer.info` 是一个轻量的渲染统计叠加层,显示当前帧的 GPU 诊断数据,适合开发调试阶段使用。
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
viewer.info.enable(); // 在画面左下角显示统计信息
|
|
111
|
+
viewer.info.disable(); // 隐藏统计信息
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
启用后将在 `viewer.el` 左下角叠加以下数据:
|
|
115
|
+
|
|
116
|
+
| 指标 | 说明 |
|
|
117
|
+
| :------------- | :---------------------------- |
|
|
118
|
+
| `draw calls` | 当前帧的绘制调用次数 |
|
|
119
|
+
| `frame calls` | 当前帧的帧调用次数 |
|
|
120
|
+
| `triangles` | 当前帧渲染的三角面数量 |
|
|
121
|
+
| `points` | 当前帧渲染的点数量 |
|
|
122
|
+
| `lines` | 当前帧渲染的线段数量 |
|
|
123
|
+
| `timestamp` | GPU 渲染耗时(ms,WebGPU 专属)|
|
|
124
|
+
|
|
125
|
+
> `timestamp` 指标仅在 WebGPU 后端可用,WebGL 回退模式下显示 `0`。
|
|
126
|
+
|
|
127
|
+
### ViewerHelper
|
|
128
|
+
|
|
129
|
+
`viewer.viewerHelper` 是一个方向指示器 gizmo(基于 Three.js `ViewHelper`),显示当前相机朝向的 XYZ 轴,渲染为画面角落的叠加层。
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
viewer.viewerHelper.enable(); // 显示方向 gizmo
|
|
133
|
+
viewer.viewerHelper.disable(); // 隐藏方向 gizmo
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**属性:**
|
|
137
|
+
|
|
138
|
+
| 属性 | 类型 | 说明 |
|
|
139
|
+
| :------------- | :---------- | :---------------------------------------- |
|
|
140
|
+
| `location` | `object` | gizmo 在画面中的位置,支持 `top`、`bottom`、`left`、`right` 偏移(像素)。默认右下角。 |
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// 调整位置到左下角
|
|
144
|
+
viewer.viewerHelper.location.left = 12;
|
|
145
|
+
viewer.viewerHelper.location.bottom = 12;
|
|
146
|
+
viewer.viewerHelper.location.right = null;
|
|
147
|
+
```
|
|
148
|
+
|
|
103
149
|
### `dispose()`
|
|
104
150
|
|
|
105
151
|
清理查看器,从 DOM 中移除 canvas,移除事件监听,并释放渲染器和环境贴图,以防止内存泄漏。
|
|
@@ -112,8 +158,10 @@ viewer.dispose();
|
|
|
112
158
|
|
|
113
159
|
Viewer 继承自 `EventDispatcher`,会触发以下事件:
|
|
114
160
|
|
|
115
|
-
- `beforeControlsUpdate`:在 `CameraControls` 更新之前触发,提供 `{ time: number }`。
|
|
116
|
-
- `afterControlsUpdate`:在 `CameraControls` 更新之后触发,提供 `{ time: number }`。
|
|
117
|
-
- `beforeRender`:在 `renderer.render` 调用之前立即触发,提供 `{ time: number }`。
|
|
118
|
-
- `afterRender`:在 `renderer.render` 完成之后立即触发,提供 `{ time: number }`。
|
|
161
|
+
- `beforeControlsUpdate`:在 `CameraControls` 更新之前触发,提供 `{ time: number; delta: number }`。
|
|
162
|
+
- `afterControlsUpdate`:在 `CameraControls` 更新之后触发,提供 `{ time: number; delta: number }`。
|
|
163
|
+
- `beforeRender`:在 `renderer.render` 调用之前立即触发,提供 `{ time: number; delta: number }`。
|
|
164
|
+
- `afterRender`:在 `renderer.render` 完成之后立即触发,提供 `{ time: number; delta: number }`。
|
|
119
165
|
- `cameraChange`:调用 `setCamera` 时触发,提供 `{ camera: Camera }`。
|
|
166
|
+
|
|
167
|
+
其中 `delta` 为上一帧到当前帧的时间间隔(秒),可用于动画更新等场景。
|
package/docs/examples-guide.md
CHANGED
|
@@ -46,3 +46,17 @@
|
|
|
46
46
|
- 流式传输结构化场景布局,解析场景描述(`SceneLoader`、`TopologiesLoader`)。
|
|
47
47
|
- 使用相机动画(`VisionsLoader` 和 `VisionsParser`)在场景中编排预定义的飞行路径。
|
|
48
48
|
- 获取复杂的节点材质,并自定义 WebGPU 专属的 TSL(Three Shading Language)节点,如 `TSLEffects.fluid()`。
|
|
49
|
+
|
|
50
|
+
## 5. `test_model_animation.html`:模型内置动画
|
|
51
|
+
|
|
52
|
+
[在线演示](https://u-space-phi.vercel.app/examples/test_model_animation.html)
|
|
53
|
+
|
|
54
|
+
该示例演示了如何加载含内置动画的 glTF 模型,并通过 `Model` 类的动画 API 控制播放。
|
|
55
|
+
|
|
56
|
+
### 核心要点:
|
|
57
|
+
|
|
58
|
+
- 使用 `Model.loadAsync()` 加载带动画的 `.glb` 文件,动画片段自动初始化。
|
|
59
|
+
- 使用 `viewer.frameloop = 'always'` 开启持续渲染以驱动动画。
|
|
60
|
+
- 监听 `beforeRender` 事件,通过 `model.updateAnimation(e.delta)` 推进动画时间。
|
|
61
|
+
- 使用 `model.playAnimation(name)` 按名称播放单个动画,`model.playAllAnimations()` 播放全部。
|
|
62
|
+
- 使用 `model.stopAnimation()` 停止动画播放。
|
package/docs/getting-started.md
CHANGED
|
@@ -93,8 +93,7 @@ pnpm install three camera-controls
|
|
|
93
93
|
`u-space` 内置了 `InteractionManager`,可以轻松为 3D 对象添加事件监听。
|
|
94
94
|
|
|
95
95
|
```javascript
|
|
96
|
-
//
|
|
97
|
-
viewer.interactionManager.targetObjects = [];
|
|
96
|
+
// 启用指针移动事件(pointerenter / pointerleave 需要此开关)
|
|
98
97
|
viewer.interactionManager.pointerMoveEventsEnabled = true;
|
|
99
98
|
|
|
100
99
|
// 添加点击事件
|
package/docs/index.md
CHANGED
|
@@ -48,3 +48,5 @@ features:
|
|
|
48
48
|
| [curve-movement](./api-plugins#curve-movement) | 沿样条曲线移动相机或对象 |
|
|
49
49
|
| [tracking-controls](./api-plugins#tracking-controls) | 相机跟随移动目标 |
|
|
50
50
|
| [atmosphere](./api-plugins#atmosphere) | 天空/大气渲染(开发中) |
|
|
51
|
+
| [object-controls](./api-plugins#object-controls) | 交互式移动、旋转、缩放 3D 对象 |
|
|
52
|
+
| [topology-drawer](./api-plugins#topology-drawer) | 在场景任意表面交互式绘制拓扑路径图 |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "u-space",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "dist/src/index.d.ts",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"docs:build": "vitepress build docs",
|
|
30
30
|
"docs:preview": "vitepress preview docs",
|
|
31
31
|
"docs:deploy": "pnpm docs:build && vercel --prod",
|
|
32
|
-
"release": "npm publish --access=public"
|
|
32
|
+
"release": "npm version patch && npm publish --access=public"
|
|
33
33
|
},
|
|
34
34
|
"files": [
|
|
35
35
|
"dist",
|