@galacean/cli 0.0.1-alpha.6 → 0.0.1-alpha.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.
@@ -1,1065 +0,0 @@
1
- const debugChannel = new BroadcastChannel('Galacean_Editor_Channel');
2
-
3
- debugChannel.addEventListener("message", (e) => {
4
- if (!(e.data && typeof e.data === 'object')) return;
5
- const type = e.data.type;
6
- });
7
-
8
- import "./scripts";
9
- import {
10
- Camera, AssetType, Scene, WebGLEngine, Loader, Logger, WebGLEngineConfiguration, ParserContext, HierarchyParser, Component
11
- } from "@galacean/engine";
12
-
13
- <% if(features.spine) { %>
14
- import { SpineAnimationRenderer } from "@galacean/engine-spine";
15
- <% } %>
16
- <% if(features.lottie) { %>
17
- import { LottieAnimation } from "@galacean/engine-lottie";
18
- <% } %>
19
- <% if(features.physicsLite) { %>
20
- import { LitePhysics } from "@galacean/engine-physics-lite";
21
- <% } %>
22
- <% if(features.physx) { %>
23
- import { PhysXPhysics } from "@galacean/engine-physics-physx";
24
- <% } %>
25
-
26
- <% if(features.xr) { %>
27
- import { WebXRDevice } from "@galacean/engine-xr-webxr";
28
- import { XROrigin, XRAnchorManager, XRImageManager, XRPlaneManager } from "@galacean/engine-toolkit-xr";
29
- Loader.registerClass("XR Origin", XROrigin);
30
- Loader.registerClass("XR Anchor Manager", XRAnchorManager);
31
- Loader.registerClass("XR Image Manager", XRImageManager);
32
- Loader.registerClass("XR Plane Manager", XRPlaneManager);
33
- <% } %>
34
-
35
- <% if(features.shaderlab) { %>
36
- import { ShaderLab } from "@galacean/engine-shaderlab";
37
- import { registerIncludes } from "@galacean/engine-shader";
38
- <% } %>
39
-
40
- <% if(features.gui) { %>
41
- import { registerGUI } from "@galacean/engine-ui";
42
- registerGUI();
43
- <% } %>
44
-
45
- <% if(debug.orbitControl) { %>
46
- import { OrbitControl as ToolkitOrbitControl } from "@galacean/engine-toolkit";
47
- <% } %>
48
-
49
- <% if(debug.stats) { %>
50
- import { Stats } from "@galacean/engine-toolkit";
51
-
52
- Stats.hookRequest();
53
-
54
- debugChannel.addEventListener("message", (e) => {
55
- if (!(e.data && typeof e.data === 'object')) return;
56
-
57
- const type = e.data.type;
58
- if (type === 'toggle-stats-visible') {
59
- if (!_statsComponent) {
60
- _statsComponent = engine.sceneManager.activeScene._componentsManager._activeCameras.get(0).entity.addComponent(Stats);
61
- }
62
- const value = e.data.payload;
63
- if(value) {
64
- _statsComponent.enabled = true;
65
- } else {
66
- _statsComponent.enabled = false;
67
- }
68
- }
69
- });
70
- <% } %>
71
-
72
- <% if(debug.godMode) { %>
73
- import {
74
- Canvas,
75
- Entity,
76
- InputManager,
77
- Keys,
78
- PointerButton,
79
- Script,
80
- Transform,
81
- Vector2,
82
- Vector3,
83
- MathUtil,
84
- Matrix
85
- } from "@galacean/engine";
86
- <% } %>
87
- import projectInfo from "../project.json";
88
-
89
-
90
- <% if(debug.eventDebugger) { %>
91
- function eventDebugger() {
92
- const originOnFunc = WebGLEngine.prototype.on;
93
- const message = {
94
- type: "collect-preview-runtime-event",
95
- payload: {},
96
- };
97
-
98
- WebGLEngine.prototype.on = function(type, listener) {
99
- originOnFunc.call(this, type, listener);
100
- message.payload.inputEvents = this.eventNames();
101
- debugChannel.postMessage(message);
102
- };
103
-
104
- WebGLEngine.prototype._dispatch = WebGLEngine.prototype.dispatch;
105
-
106
- WebGLEngine.prototype.dispatch = function(event, data) {
107
- this._dispatch(event, data);
108
- // message.payload.outputEvent = event;
109
- debugChannel.postMessage(message, "*");
110
- };
111
-
112
- debugChannel.addEventListener("message", (e) => {
113
- if (!(e.data && typeof e.data === 'object')) return;
114
- const type = e.data.type;
115
- if (type === 'trigger-preview-runtime-event') {
116
- engine.dispatch(e.data.payload.eventName, e.data.payload.data);
117
- }
118
- });
119
- }
120
-
121
- eventDebugger();
122
- <% } %>
123
-
124
- <% if(debug.hotReload) { %>
125
- function engineEnhancement(): void {
126
- // @ts-ignore
127
- WebGLEngine.prototype._engineObjectMap = new Map<string, string>();
128
-
129
- // @ts-ignore
130
- HierarchyParser.prototype._addComponentPlugin = function (
131
- componentId: string,
132
- component: Component
133
- ) {
134
- this._engine._engineObjectMap.set(componentId, component);
135
- };
136
-
137
- // @ts-ignore
138
- HierarchyParser.prototype._addEntityPlugin = function (
139
- entityId: string,
140
- entity: Entity
141
- ) {
142
- this._engine._engineObjectMap.set(entityId, entity);
143
- };
144
- }
145
- engineEnhancement();
146
-
147
- debugChannel.addEventListener("message", (e) => {
148
- if (!(e.data && typeof e.data === 'object')) return;
149
- const type = e.data.type;
150
- if (type === 'script-inspector-value-change') {
151
- const component = engine._componentsMap.get(e.data.payload.instanceId);
152
- if (component) {
153
- component[e.data.payload.property] = e.data.payload.value;
154
- }
155
- }
156
-
157
- if (type === 'scene-inspector-value-change') {
158
- const { property, value } = e.data.payload;
159
- engine.sceneManager.activeScene[property] = value;
160
- }
161
-
162
- })
163
- <% } %>
164
-
165
- export async function init(canvas: HTMLCanvasElement) {
166
- const config: WebGLEngineConfiguration = {
167
- canvas,
168
- physics: undefined,
169
- shaderLab: undefined,
170
- graphicDeviceOptions: projectInfo.runtimeOptions.webGLRendererOptions
171
- };
172
-
173
- <% if(features.xr) { %>
174
- config.xrDevice = new WebXRDevice();
175
- <% } %>
176
- <% if(features.shaderlab) { %>
177
- config.shaderLab = new ShaderLab();
178
- Logger.enable();
179
- registerIncludes();
180
- <% } %>
181
- <% if(features.physicsLite) { %>
182
- config.physics = new LitePhysics();
183
- <% } %>
184
- <% if(features.physx) { %>
185
- config.physics = new PhysXPhysics();
186
- <% } %>
187
- <% if(debug.stats) { %>
188
- <% } %>
189
-
190
- const engine = await WebGLEngine.create(config);
191
- document.oncontextmenu = (e) => {
192
- e.preventDefault();
193
- };
194
- engine.canvas.resizeByClientSize(projectInfo.runtimeOptions.devicePixelRatioMode === "Fixed" ? projectInfo.runtimeOptions.devicePixelRatio : undefined);
195
-
196
- <% if(features.lottie) { %>
197
- Loader.registerClass("LottieAnimation", LottieAnimation);
198
- <% } %>
199
- <% if(features.spine) { %>
200
- Loader.registerClass("SpineAnimationRenderer", SpineAnimationRenderer);
201
- <% } %>
202
-
203
- await engine.resourceManager
204
- .load({
205
- url: projectInfo.url,
206
- type: AssetType.Project,
207
- })
208
- .onProgress(
209
- (loaded: number, total: number) => {
210
- const percentage = Math.round((loaded / total) * 100);
211
- console.log(`Overall progress: ${percentage}% (${loaded}/${total} files loaded)`);
212
- console.log(`Files loaded: project config + ${loaded - 1} project files`);
213
-
214
- window.parent.postMessage({ loaded, total }, "*");
215
- // Update your loading ui here
216
- },
217
- // Track detailed progress (individual file loading)
218
- (url: string, loaded: number, total: number) => {
219
- const percentage = Math.round((loaded / total) * 100);
220
- console.log(`Loading ${url}: ${percentage}% (${loaded}/${total} bytes)`);
221
-
222
- // Update detailed progress display here
223
- }
224
- )
225
- .catch((e) => {
226
- throw e;
227
- });
228
-
229
- <% if(debug.orbitControl) { %>
230
- try {
231
- const cameras = [];
232
- const rootEntities = engine.sceneManager.activeScene.rootEntities;
233
- for (let i = 0, n = rootEntities.length; i < n; i++) {
234
- cameras.push(...rootEntities[i].getComponentsIncludeChildren(Camera, []));
235
- }
236
- for (let j = 0, n = cameras.length; j < n; j++) {
237
- cameras[0].entity.addComponent(ToolkitOrbitControl);
238
- }
239
- } catch (e) {
240
- console.log(e);
241
- }
242
- <% } %>
243
-
244
- window.engine = engine;
245
-
246
- <% if(features.xr) { %>
247
- function addXRButton(content: string): HTMLButtonElement {
248
- const button = document.createElement("button");
249
- button.textContent = content;
250
- const { style } = button;
251
- style.position = "absolute";
252
- style.bottom = "20px";
253
- style.padding = "12px 6px";
254
- style.border = "1px solid rgb(255, 255, 255)";
255
- style.borderRadius = "4px";
256
- style.background = "rgba(0, 0, 0, 0.1)";
257
- style.color = "rgb(255, 255, 255)";
258
- style.font = "13px sans-serif";
259
- style.textAlign = "center";
260
- style.opacity = "0.5";
261
- style.outline = "none";
262
- style.zIndex = "999";
263
- style.cursor = "pointer";
264
- style.left = "calc(50% - 50px)";
265
- style.width = "100px";
266
- document.body.appendChild(button);
267
- return button;
268
- }
269
- const xrManager = engine.xrManager;
270
- let xrMode = 0;
271
- if (xrManager.origin) {
272
- const inputManager = xrManager.inputManager;
273
- const camera = inputManager.getTrackedDevice(3);
274
- const leftCamera = inputManager.getTrackedDevice(4);
275
- const rightCamera = inputManager.getTrackedDevice(5);
276
- if(leftCamera._camera && rightCamera._camera) {
277
- xrMode = 2;
278
- } else if(camera._camera) {
279
- xrMode = 1;
280
- }
281
- }
282
- if(xrMode !== 0) {
283
- xrManager.sessionManager.isSupportedMode(xrMode).then(
284
- () => {
285
- addXRButton(xrMode === 1 ? "进入 AR" : "进入 VR").onclick = () => {
286
- xrManager.enterXR(xrMode);
287
- };
288
- },
289
- (error) => {
290
- addXRButton("not support")
291
- console.error(error);
292
- }
293
- );
294
- }
295
- <% } %>
296
-
297
- <% if(debug.autoResize) { %>
298
- // auto resize canvas
299
- const _canvas = document.getElementById('canvas');
300
- const ob = new ResizeObserver(() => {
301
- engine.canvas.resizeByClientSize();
302
- });
303
- ob.observe(_canvas);
304
- <% } %>
305
-
306
- <% if(debug.godMode) { %>
307
-
308
- enum ControlEvent {
309
- Default = "control-default",
310
- Rotate = "control-rotating",
311
- Zoom = "control-zoom",
312
- Pan = "control-paning",
313
- Around = "control-around",
314
- Fly = "control-fly",
315
- Enter = "editorControl-enter",
316
- Leave = "editorControl-leave",
317
- SpeedChange = "flySpeedChange",
318
- PanOn = "editorControl-pan"
319
- }
320
-
321
- // Prevent gimbal lock.
322
- const ESP = MathUtil.zeroTolerance;
323
-
324
- class Spherical {
325
- private static _xAxis: Vector3 = new Vector3();
326
- private static _yAxis: Vector3 = new Vector3();
327
- private static _zAxis: Vector3 = new Vector3();
328
- private _matrix: Matrix = new Matrix();
329
- private _matrixInv: Matrix = new Matrix();
330
- constructor(public radius?: number, public phi?: number, public theta?: number) {
331
- this.radius = radius !== undefined ? radius : 1.0;
332
- this.phi = phi !== undefined ? phi : 0;
333
- this.theta = theta !== undefined ? theta : 0;
334
- }
335
-
336
- makeSafe(): Spherical {
337
- const count = Math.floor(this.phi / Math.PI);
338
- this.phi = MathUtil.clamp(this.phi, count * Math.PI + ESP, (count + 1) * Math.PI - ESP);
339
- return this;
340
- }
341
-
342
- set(radius: number, phi: number, theta: number): Spherical {
343
- this.radius = radius;
344
- this.phi = phi;
345
- this.theta = theta;
346
- return this;
347
- }
348
-
349
- setYAxis(up: Vector3): void {
350
- const { _xAxis: xAxis, _yAxis: yAxis, _zAxis: zAxis } = Spherical;
351
- if (Vector3.equals(xAxis.set(1, 0, 0), yAxis.copyFrom(up).normalize())) {
352
- xAxis.set(0, 1, 0);
353
- }
354
- Vector3.cross(xAxis, yAxis, zAxis);
355
- zAxis.normalize();
356
- Vector3.cross(yAxis, zAxis, xAxis);
357
- const { elements: es } = this._matrix;
358
- (es[0] = xAxis.x), (es[1] = xAxis.y), (es[2] = xAxis.z);
359
- (es[4] = yAxis.x), (es[5] = yAxis.y), (es[6] = yAxis.z);
360
- (es[8] = zAxis.x), (es[9] = zAxis.y), (es[10] = zAxis.z);
361
-
362
- const { elements: eInv } = this._matrixInv;
363
- (eInv[0] = xAxis.x), (eInv[4] = xAxis.y), (eInv[8] = xAxis.z);
364
- (eInv[1] = yAxis.x), (eInv[5] = yAxis.y), (eInv[9] = yAxis.z);
365
- (eInv[2] = zAxis.x), (eInv[6] = zAxis.y), (eInv[10] = zAxis.z);
366
- }
367
-
368
- setFromVec3(value: Vector3, atTheBack = false): Spherical {
369
- value.transformNormal(this._matrixInv);
370
- this.radius = value.length();
371
- if (this.radius === 0) {
372
- this.theta = 0;
373
- this.phi = 0;
374
- } else {
375
- if (atTheBack) {
376
- this.phi = 2 * Math.PI - Math.acos(MathUtil.clamp(value.y / this.radius, -1, 1));
377
- this.theta = Math.atan2(-value.x, -value.z);
378
- } else {
379
- this.phi = Math.acos(MathUtil.clamp(value.y / this.radius, -1, 1));
380
- this.theta = Math.atan2(value.x, value.z);
381
- }
382
- }
383
- return this;
384
- }
385
-
386
- setToVec3(value: Vector3): boolean {
387
- const { radius, phi, theta } = this;
388
- const sinPhiRadius = Math.sin(phi) * radius;
389
- this.phi -= Math.floor(this.phi / Math.PI / 2) * Math.PI * 2;
390
- value.set(sinPhiRadius * Math.sin(theta), radius * Math.cos(phi), sinPhiRadius * Math.cos(theta));
391
- value.transformNormal(this._matrix);
392
- return this.phi > Math.PI;
393
- }
394
- }
395
-
396
- class OrbitControl extends Script {
397
- canvas: Canvas;
398
- input: InputManager;
399
- camera: Camera;
400
- cameraTransform: Transform;
401
-
402
- /** Whether to automatically rotate the camera, the default is false. */
403
- autoRotate = false;
404
- /** The radian of automatic rotation per second. */
405
- autoRotateSpeed: number = Math.PI;
406
- /** Whether to enable camera damping, the default is true. */
407
- enableDamping = true;
408
- /** Rotation speed, default is 1.0 . */
409
- rotateSpeed = 1.0;
410
- /** Camera zoom speed, the default is 1.0. */
411
- zoomSpeed = 1.0;
412
- /** Keyboard translation speed, the default is 7.0 . */
413
- keyPanSpeed = 7.0;
414
- /** Rotation damping parameter, default is 0.1 . */
415
- dampingFactor = 0.1;
416
- /** Zoom damping parameter, default is 0.2 . */
417
- zoomFactor = 0.2;
418
- /** The minimum distance, the default is 0.1, should be greater than 0. */
419
- minDistance = 0.000001;
420
- /** The maximum distance, the default is infinite, should be greater than the minimum distance. */
421
- maxDistance = Infinity;
422
- /** Minimum zoom speed, the default is 0.0. */
423
- minZoom = 0.0;
424
- /** Maximum zoom speed, the default is positive infinity. */
425
- maxZoom = Infinity;
426
- /** The minimum radian in the vertical direction, the default is 1 degree. */
427
- minPolarAngle = 1;
428
- /** The maximum radian in the vertical direction, the default is 179 degree. */
429
- maxPolarAngle: number = (179 / 180) * Math.PI;
430
- /** The minimum radian in the horizontal direction, the default is negative infinity. */
431
- minAzimuthAngle = -Infinity;
432
- /** The maximum radian in the horizontal direction, the default is positive infinity. */
433
- maxAzimuthAngle = Infinity;
434
- /** Movement distance per second, the unit is the unit before MVP conversion. */
435
- movementSpeed = 2;
436
- /** factor of movement speed */
437
- speedFactor = 1.0;
438
-
439
- private _up: Vector3 = new Vector3(0, 1, 0);
440
- private _target: Vector3 = new Vector3();
441
- private _atTheBack = false;
442
- private _spherical: Spherical = new Spherical();
443
- private _sphericalDelta: Spherical = new Spherical();
444
- private _sphericalDump: Spherical = new Spherical();
445
- private _sphericalFree: Spherical = new Spherical();
446
- private _zoomFrag = 0;
447
- private _scale = 1;
448
- private _panOffset: Vector3 = new Vector3();
449
- private _tempVec3: Vector3 = new Vector3();
450
- private _tempVec2: Vector3 = new Vector3();
451
- private _tempVec: Vector3 = new Vector3();
452
- private _topVec: Vector3 = new Vector3(0, 1, 0);
453
- private _bottomVec: Vector3 = new Vector3(0, -1, 0);
454
- private _tempForward: Vector3 = new Vector3();
455
- private _tempTarget: Vector3 = new Vector3();
456
- private _tempVecDelta: Vector2 = new Vector2();
457
-
458
- private _radius = 0;
459
- private _isAltDown = false;
460
- private _transformDirty = false;
461
- private _inEditing = false;
462
- private _lastFlySpeed = 0;
463
- private _flyDirection = new Vector3();
464
- private _damping = 0.8;
465
- private _dampingThreshold = 0.01;
466
-
467
- /*
468
- * Return up vector.
469
- */
470
- get up(): Vector3 {
471
- return this._up;
472
- }
473
-
474
- set up(value: Vector3) {
475
- this._up.copyFrom(value);
476
- this._spherical.setYAxis(value);
477
- this._atTheBack = false;
478
- }
479
-
480
- /**
481
- * Return target position.
482
- * */
483
- get target(): Vector3 {
484
- return this._target;
485
- }
486
-
487
- set target(value: Vector3) {
488
- this._target.copyFrom(value);
489
- this._atTheBack = false;
490
- }
491
-
492
- set inEditing(value: boolean) {
493
- if (this._inEditing !== value) {
494
- this._inEditing = value;
495
- }
496
-
497
- this.engine.dispatch(value ? ControlEvent.Enter : ControlEvent.Leave);
498
- }
499
-
500
- onAwake(): void {
501
- const { engine, entity } = this;
502
- this.canvas = engine.canvas;
503
- this.input = engine.inputManager;
504
- this.camera = entity.getComponent(Camera);
505
- this.cameraTransform = entity.transform;
506
- this._spherical.setYAxis(this._up);
507
- this._atTheBack = false;
508
-
509
- /** Init spherical. */
510
- Vector3.transformByQuat(this._tempVec3.set(0, 0, -1), this.cameraTransform.rotationQuaternion, this._tempVec3);
511
- this._sphericalFree.setFromVec3(this._tempVec3, this._atTheBack);
512
-
513
- // alt key
514
- // 需要拿到全局 alt down, 而inputManager为防止与其他面板快捷键冲突需要挂在canvas上
515
- document.addEventListener("keydown", (event) => {
516
- this._isAltDown = event.altKey;
517
- });
518
- document.addEventListener("keyup", (event) => {
519
- this._isAltDown = event.altKey;
520
- });
521
-
522
- // 此处重写了 pointerManager 的事件监听
523
- // 在 viewport 编辑态时,需要阻拦触控点所有的默认行为
524
- // @ts-ignore
525
- const pointerManager = this.engine.inputManager._pointerManager;
526
- pointerManager._removeEventListener();
527
- const { _nativeEvents: nativeEvents } = pointerManager;
528
- // @ts-ignore
529
- const { _webCanvas: webCanvas } = engine.canvas;
530
- pointerManager._onPointerEvent = (evt: PointerEvent) => {
531
- // @ts-ignore
532
- if (evt.type === "pointerdown" && evt.target !== webCanvas) return;
533
- this._inEditing && evt.preventDefault();
534
- nativeEvents.push(evt);
535
- };
536
- pointerManager._addEventListener();
537
- }
538
-
539
- onUpdate(deltaTime: number): void {
540
- const inputManager = this.input;
541
- // 维护编辑态
542
- // 1. 若触控按下,则进入编辑态
543
- if (inputManager.isPointerDown() || inputManager.isKeyDown()) {
544
- this.inEditing = true;
545
- }
546
- // 2. 若当前没有触控,则退出编辑态
547
- if (!inputManager.isPointerHeldDown() && !inputManager.isKeyHeldDown()) {
548
- this.inEditing = false;
549
- }
550
- const { _inEditing: inEditing } = this;
551
- const primaryDown = inputManager.isPointerHeldDown(PointerButton.Primary);
552
- const secondaryDown = inputManager.isPointerHeldDown(PointerButton.Secondary);
553
- const auxiliaryDown = inputManager.isPointerHeldDown(PointerButton.Auxiliary);
554
- // 判断滚轮
555
- if (secondaryDown) {
556
- this._updateSpeed(inputManager.wheelDelta);
557
- } else {
558
- this._zoom(inputManager.wheelDelta);
559
- }
560
-
561
- // 判断 fly
562
- const { movementSpeed } = this;
563
- const delta = this._tempVec2.set(0, 0, 0);
564
-
565
- if ((secondaryDown && inputManager.isKeyHeldDown(Keys.KeyA)) || inputManager.isKeyHeldDown(Keys.ArrowLeft)) {
566
- delta.x -= movementSpeed;
567
- }
568
- if ((secondaryDown && inputManager.isKeyHeldDown(Keys.KeyD)) || inputManager.isKeyHeldDown(Keys.ArrowRight)) {
569
- delta.x += movementSpeed;
570
- }
571
- if (secondaryDown && inputManager.isKeyHeldDown(Keys.KeyQ)) {
572
- delta.y -= movementSpeed;
573
- }
574
- if (secondaryDown && inputManager.isKeyHeldDown(Keys.KeyE)) {
575
- delta.y += movementSpeed;
576
- }
577
- if ((secondaryDown && inputManager.isKeyHeldDown(Keys.KeyS)) || inputManager.isKeyHeldDown(Keys.ArrowDown)) {
578
- delta.z += movementSpeed;
579
- }
580
- if ((secondaryDown && inputManager.isKeyHeldDown(Keys.KeyW)) || inputManager.isKeyHeldDown(Keys.ArrowUp)) {
581
- delta.z -= movementSpeed;
582
- }
583
- this._fly(delta, deltaTime);
584
-
585
- let controlType = ControlEvent.Default;
586
-
587
- // 判断 panning, around, zoom 和 rotating
588
- if (inEditing) {
589
- const onlyPrimaryDown = primaryDown && !secondaryDown;
590
- const onlySecondaryDown = !primaryDown && secondaryDown;
591
- const bothDown = primaryDown && secondaryDown;
592
- const deltaPosition = inputManager.pointers[0]?.deltaPosition || this._tempVecDelta.set(0, 0);
593
- const altDown = this._isAltDown;
594
- const ctrlDown = inputManager.isKeyHeldDown(Keys.ControlLeft) || inputManager.isKeyHeldDown(Keys.ControlRight);
595
- const metaDown = inputManager.isKeyHeldDown(Keys.MetaLeft) || inputManager.isKeyHeldDown(Keys.MetaRight);
596
-
597
- // change cursor for next move implication
598
- if (altDown && !metaDown) {
599
- controlType = ControlEvent.Rotate;
600
- }
601
- if (altDown && ctrlDown) {
602
- controlType = ControlEvent.Zoom;
603
- }
604
- if (auxiliaryDown || (altDown && metaDown)) {
605
- controlType = ControlEvent.Pan;
606
- }
607
-
608
- if (bothDown || (altDown && ctrlDown && onlyPrimaryDown)) {
609
- controlType = ControlEvent.Zoom;
610
- this._zoom(deltaPosition);
611
- } else if (altDown && onlyPrimaryDown && !metaDown) {
612
- controlType = ControlEvent.Rotate;
613
- this._rotate(deltaPosition);
614
- } else if (onlySecondaryDown) {
615
- controlType = ControlEvent.Around;
616
- this._around(deltaPosition);
617
- } else if (onlyPrimaryDown || auxiliaryDown || (altDown && metaDown && onlyPrimaryDown)) {
618
- controlType = ControlEvent.Pan;
619
- this._pan(deltaPosition);
620
- }
621
- }
622
- this._transformDirty && this._updateTransform();
623
- }
624
-
625
- private _rotate(delta: Vector2): void {
626
- if (delta.x === 0 && delta.y === 0) {
627
- return;
628
- }
629
- const radianLeft = ((2 * Math.PI * delta.x) / this.canvas.width) * this.rotateSpeed;
630
- this._sphericalDelta.theta -= radianLeft;
631
- const radianUp = ((2 * Math.PI * delta.y) / this.canvas.height) * this.rotateSpeed;
632
- this._sphericalDelta.phi -= radianUp;
633
- if (this.enableDamping) {
634
- this._sphericalDump.theta = -radianLeft;
635
- this._sphericalDump.phi = -radianUp;
636
- }
637
- this._transformDirty = true;
638
- }
639
-
640
- private _zoom(delta: Vector2 | Vector3): void {
641
- if (delta.y === 0) {
642
- return;
643
- }
644
- const factor = this._easeOutExpo(this._getDistToTarget());
645
- if (delta.y > 0) {
646
- this._scale /= Math.pow(0.95, this.zoomSpeed * factor);
647
- } else if (delta.y < 0) {
648
- this._scale *= Math.pow(0.95, this.zoomSpeed * factor);
649
- }
650
-
651
- this._transformDirty = true;
652
- }
653
-
654
- private _pan(delta: Vector2): void {
655
- if (delta.x === 0 && delta.y === 0) {
656
- return;
657
- }
658
- const { cameraTransform } = this;
659
- const { elements } = cameraTransform.worldMatrix;
660
- const { height } = this.canvas;
661
- const targetDistance =
662
- Vector3.distance(cameraTransform.position, this.target) * (this.camera.fieldOfView / 2) * (Math.PI / 180);
663
- const distanceLeft = -2 * delta.x * (targetDistance / height);
664
- const distanceUp = 2 * delta.y * (targetDistance / height);
665
- this._panOffset.x += elements[0] * distanceLeft + elements[4] * distanceUp;
666
- this._panOffset.y += elements[1] * distanceLeft + elements[5] * distanceUp;
667
- this._panOffset.z += elements[2] * distanceLeft + elements[6] * distanceUp;
668
- this._transformDirty = true;
669
- }
670
-
671
- private _fly(moveDelta: Vector3, delta: number): void {
672
- if (moveDelta.x === 0 && moveDelta.y === 0 && moveDelta.z === 0) {
673
- if (this._lastFlySpeed > this._dampingThreshold) {
674
- this._lastFlySpeed *= this._damping;
675
- const length = delta * this.movementSpeed * this._easeOutExpo(this._lastFlySpeed);
676
- this._flyUpdate(length);
677
- } else {
678
- this._lastFlySpeed = 0;
679
- }
680
- return;
681
- }
682
-
683
- const factor = this._getDistToTarget() * 0.024154589371980676;
684
- this._lastFlySpeed += delta;
685
- const length = delta * this.movementSpeed * factor * (1 + this._lastFlySpeed * this._lastFlySpeed * 10);
686
-
687
- this._flyDirection.copyFrom(moveDelta);
688
- this._flyUpdate(length);
689
- }
690
-
691
- private _around(moveDelta: Vector2): void {
692
- if (moveDelta.x === 0 && moveDelta.y === 0) {
693
- return;
694
- }
695
- this._tempVec3.copyFrom(this.cameraTransform.worldUp);
696
- this._atTheBack = this._tempVec3.y <= 0;
697
-
698
- Vector3.transformByQuat(this._tempVec3.set(0, 0, -1), this.cameraTransform.rotationQuaternion, this._tempVec3);
699
- this._sphericalFree.setFromVec3(this._tempVec3, this._atTheBack);
700
-
701
- const canvas = this.engine.canvas;
702
- const deltaAlpha = (-moveDelta.x * 180) / canvas.width;
703
- const deltaPhi = (moveDelta.y * 180) / canvas.height;
704
- this._sphericalFree.theta += MathUtil.degreeToRadian(deltaAlpha);
705
- this._sphericalFree.phi += MathUtil.degreeToRadian(deltaPhi);
706
- this._sphericalFree.makeSafe();
707
- this._atTheBack = this._sphericalFree.setToVec3(this._tempVec2);
708
- Vector3.add(this.cameraTransform.position, this._tempVec2, this._tempVec2);
709
- this._atTheBack
710
- ? this.cameraTransform.lookAt(this._tempVec2, this._bottomVec)
711
- : this.cameraTransform.lookAt(this._tempVec2, this._topVec);
712
-
713
- this._updateTarget();
714
- this._transformDirty = true;
715
- }
716
-
717
- private _updateSpeed(delta: Vector3) {
718
- if (delta.y === 0) return;
719
- this.speedFactor = MathUtil.clamp(this.speedFactor - delta.y / 500, 0.001, 50);
720
- this.engine.dispatch(ControlEvent.SpeedChange, this.speedFactor);
721
- this.movementSpeed = 10.0 * this.speedFactor;
722
- }
723
-
724
- private _updateTarget() {
725
- this._radius = Vector3.distance(this.cameraTransform.worldPosition, this.target);
726
- const currentPos = this.cameraTransform.worldPosition;
727
- this._tempForward = this.cameraTransform.worldForward.clone();
728
- this._tempForward.normalize().scale(this._radius);
729
- Vector3.add(currentPos, this._tempForward, this._tempTarget);
730
-
731
- this._tempVec.copyFrom(this._atTheBack ? this._bottomVec : this._topVec);
732
-
733
- this._spherical.setYAxis(this._tempVec);
734
- this._target = this._tempTarget;
735
- this._up.copyFrom(this._tempVec);
736
- }
737
-
738
- private _updateTransform(): void {
739
- const { cameraTransform, target, _spherical, _tempVec2: _tempVec31, _sphericalDelta, _panOffset } = this;
740
-
741
- _tempVec31.copyFrom(cameraTransform.worldUp);
742
- this._atTheBack = _tempVec31.y <= 0;
743
-
744
- Vector3.subtract(cameraTransform.position, target, _tempVec31);
745
- _spherical.setFromVec3(_tempVec31, this._atTheBack);
746
- _spherical.theta += _sphericalDelta.theta;
747
- _spherical.phi += _sphericalDelta.phi;
748
- _spherical.theta = Math.max(this.minAzimuthAngle, Math.min(this.maxAzimuthAngle, _spherical.theta));
749
- _spherical.phi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, _spherical.phi));
750
- _spherical.makeSafe();
751
- if (this._scale !== 1) {
752
- this._zoomFrag = _spherical.radius * (this._scale - 1);
753
- }
754
- _spherical.radius += this._zoomFrag;
755
-
756
- _spherical.radius = Math.max(this.minDistance, Math.min(this.maxDistance, _spherical.radius));
757
- this._atTheBack = _spherical.setToVec3(_tempVec31);
758
-
759
- // Camera move
760
- Vector3.add(target.add(_panOffset), _tempVec31, cameraTransform.worldPosition);
761
- cameraTransform.lookAt(target, this._atTheBack ? this._bottomVec : this._topVec);
762
-
763
- // Reset cache value.
764
- this._zoomFrag = 0;
765
- this._scale = 1;
766
- _sphericalDelta.set(0, 0, 0);
767
- _panOffset.set(0, 0, 0);
768
- this._transformDirty = false;
769
- }
770
-
771
- private _flyUpdate(length: number) {
772
- const dist = this._flyDirection.normalize().scale(length);
773
- this.cameraTransform.translate(dist, true);
774
- Vector3.transformByQuat(dist, this.entity.transform.worldRotationQuaternion, this._tempVec3);
775
- this._target.add(this._tempVec3);
776
- }
777
-
778
- private _easeOutExpo(t: number): number {
779
- return t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
780
- }
781
-
782
- private _getDistToTarget(): number {
783
- Vector3.subtract(this.target, this.cameraTransform.worldPosition, this._tempVec3);
784
- return this._tempVec3.length();
785
- }
786
- }
787
-
788
- class OrthoControl extends Script {
789
- canvas: Canvas;
790
- input: InputManager;
791
- camera: Camera;
792
- cameraTransform: Transform;
793
-
794
- /** Target position. */
795
- target: Vector3 = new Vector3();
796
- /** Up vector */
797
- up: Vector3 = new Vector3(0, 1, 0);
798
- /** Camera zoom speed, the default is 1.0. */
799
- zoomSpeed = 1.0;
800
- /** Minimum zoom speed, the default is 0.0. */
801
- minZoom = 0.0;
802
- /** Maximum zoom speed, the default is positive infinity. */
803
- maxZoom = Infinity;
804
- /** Movement distance per second, the unit is the unit before MVP conversion. */
805
- movementSpeed = 10.0;
806
-
807
- private _zoomScaleUnit = 3;
808
- private _scale = 1;
809
- private _realScale = 1;
810
-
811
- private _transformDirty = false;
812
-
813
- private _panOffset: Vector2 = new Vector2();
814
- private _tempVec2: Vector2 = new Vector2();
815
- private _tempVec3: Vector3 = new Vector3();
816
-
817
- private _isAltDown = false;
818
- private _inEditing = false;
819
-
820
- set inEditing(value: boolean) {
821
- if (this._inEditing !== value) {
822
- this._inEditing = value;
823
- }
824
-
825
- this.engine.dispatch(value ? ControlEvent.Enter : ControlEvent.Leave);
826
- }
827
-
828
- constructor(entity: Entity) {
829
- super(entity);
830
- }
831
-
832
- checkAltPressed = (event: KeyboardEvent) => {
833
- this._isAltDown = event.altKey;
834
- }
835
-
836
- onAwake(): void {
837
- const { engine, entity } = this;
838
- this.canvas = engine.canvas;
839
- this.input = engine.inputManager;
840
- this.camera = entity.getComponent(Camera);
841
- this.cameraTransform = entity.transform;
842
-
843
- document.addEventListener("keydown", this.checkAltPressed);
844
- document.addEventListener("keyup", this.checkAltPressed);
845
-
846
- // 此处重写了 pointerManager 的事件监听
847
- // 在 viewport 编辑态时,需要阻拦触控点所有的默认行为
848
- // @ts-ignore
849
- const pointerManager = this.engine.inputManager._pointerManager;
850
- pointerManager._removeEventListener();
851
- const { _nativeEvents: nativeEvents } = pointerManager;
852
- // @ts-ignore
853
- const { _webCanvas: webCanvas } = engine.canvas;
854
- pointerManager._onPointerEvent = (evt: PointerEvent) => {
855
- // @ts-ignore
856
- if (evt.type === "pointerdown" && evt.target !== webCanvas) return;
857
- this._inEditing && evt.preventDefault();
858
- nativeEvents.push(evt);
859
- };
860
- pointerManager._addEventListener();
861
- }
862
-
863
- onUpdate(): void {
864
- const inputManager = this._engine.inputManager; // 维护编辑态
865
- // 1. 若触控按下,则进入编辑态
866
- //@ts-ignore
867
- if (inputManager.isPointerDown() || inputManager.isKeyDown()) {
868
- this.inEditing = true;
869
- }
870
- // 2. 若当前没有触控,则退出编辑态
871
- if (!inputManager.isPointerHeldDown() && !inputManager.isKeyHeldDown()) {
872
- this.inEditing = false;
873
- }
874
- const { _inEditing: inEditing } = this;
875
- const { _tempVec2: delta } = this;
876
- let controlEvent = ControlEvent.Default;
877
-
878
- // wheel
879
- const wheelDelta = inputManager.wheelDelta;
880
- if (wheelDelta.x !== 0 || wheelDelta.y !== 0 || wheelDelta.z !== 0) {
881
- controlEvent = ControlEvent.Zoom;
882
- this._zoom(wheelDelta);
883
- }
884
-
885
- // keyboard event
886
- delta.x = delta.y = 0;
887
- if (inputManager.isKeyHeldDown(Keys.ArrowLeft)) {
888
- delta.x += this.movementSpeed;
889
- controlEvent = ControlEvent.Pan;
890
- this._pan(delta);
891
- } else if (inputManager.isKeyHeldDown(Keys.ArrowRight)) {
892
- delta.x -= this.movementSpeed;
893
- controlEvent = ControlEvent.Pan;
894
- this._pan(delta);
895
- }
896
-
897
- if (inputManager.isKeyHeldDown(Keys.ArrowDown)) {
898
- delta.y -= this.movementSpeed;
899
- controlEvent = ControlEvent.Pan;
900
- this._pan(delta);
901
- } else if (inputManager.isKeyHeldDown(Keys.ArrowUp)) {
902
- delta.y += this.movementSpeed;
903
- controlEvent = ControlEvent.Pan;
904
- this._pan(delta);
905
- }
906
-
907
- // pointer event
908
- if (inEditing) {
909
- const primaryDown = inputManager.isPointerHeldDown(PointerButton.Primary);
910
- const secondaryDown = inputManager.isPointerHeldDown(PointerButton.Secondary);
911
- const auxiliaryDown = inputManager.isPointerHeldDown(PointerButton.Auxiliary);
912
-
913
- const altDown = this._isAltDown;
914
- const metaDown = inputManager.isKeyHeldDown(Keys.MetaLeft) || inputManager.isKeyHeldDown(Keys.MetaRight);
915
- const onlyPrimaryDown = primaryDown && !secondaryDown;
916
- const bothDown = primaryDown && secondaryDown;
917
-
918
- const deltaPosition = inputManager.pointers[0]?.deltaPosition || delta;
919
-
920
- if ((altDown && metaDown && onlyPrimaryDown) || bothDown) {
921
- controlEvent = ControlEvent.Zoom;
922
- this._zoom(deltaPosition);
923
- } else if (onlyPrimaryDown || (!altDown && metaDown && onlyPrimaryDown) || auxiliaryDown) {
924
- controlEvent = ControlEvent.Pan;
925
- this._pan(deltaPosition);
926
- }
927
- }
928
-
929
- this._transformDirty && this._updateTransform();
930
- }
931
-
932
- private _zoom(delta: Vector2 | Vector3): void {
933
- const scaleFactor = Math.pow(0.96, this.zoomSpeed);
934
- if (delta.y > 0) {
935
- this._scale /= scaleFactor;
936
- this._realScale++;
937
- } else if (delta.y < 0) {
938
- this._scale *= scaleFactor;
939
- this._realScale--;
940
- }
941
- this._updateZoomScaleUnit();
942
- this._transformDirty = true;
943
- }
944
-
945
- private _updateZoomScaleUnit(): void {
946
- const realScale = Math.abs(this._realScale);
947
- if (realScale < 25) {
948
- this._zoomScaleUnit = 3;
949
- } else if (realScale < 50) {
950
- this._zoomScaleUnit = 2.8;
951
- } else {
952
- this._zoomScaleUnit = 2.6;
953
- }
954
- }
955
-
956
- private _pan(moveDelta: Vector2): void {
957
- this._panOffset.copyFrom(moveDelta);
958
-
959
- this._transformDirty = true;
960
- }
961
-
962
- private _updateTransform(): void {
963
- const { cameraTransform, camera, _panOffset } = this;
964
-
965
- // Update Zoom
966
- const sizeDiff = this._zoomScaleUnit * Math.log1p(camera.orthographicSize) * (this._scale - 1);
967
- const size = camera.orthographicSize + sizeDiff;
968
- camera.orthographicSize = Math.max(this.minZoom, Math.min(this.maxZoom, size));
969
-
970
- // Update X and Y
971
- const { width, height } = this.canvas;
972
- const { x, y } = _panOffset;
973
- const doubleOrthographicSize = camera.orthographicSize * 2;
974
- const width3D = doubleOrthographicSize * camera.aspectRatio;
975
- const height3D = doubleOrthographicSize;
976
- const cameraPosition = cameraTransform.position;
977
- const curPosition = this._tempVec3;
978
- curPosition.x = cameraPosition.x - (x * width3D) / width;
979
- curPosition.y = cameraPosition.y + (y * height3D) / height;
980
- curPosition.z = cameraPosition.z;
981
-
982
- // Update camera transform
983
- cameraTransform.position = curPosition;
984
- /** Reset cache value. */
985
- this._scale = 1;
986
- _panOffset.set(0, 0);
987
- this._transformDirty = false;
988
- }
989
-
990
- onDestroy() {
991
- document.removeEventListener("keydown", this.checkAltPressed);
992
- document.removeEventListener("keyup", this.checkAltPressed);
993
- }
994
- }
995
- <% } %>
996
-
997
- <% if(debug.stats || debug.godMode) { %>
998
- // toggle stats display
999
- let _statsComponent = null;
1000
- let _inGodMode = false;
1001
- const debugChannel = new BroadcastChannel('Galacean_Editor_Channel');
1002
-
1003
- const __defaultClipPlane__ = {
1004
- near: 0,
1005
- far: 0,
1006
- };
1007
-
1008
- const __debugClipPlane__ = {
1009
- near: 0.03,
1010
- far: 10000,
1011
- };
1012
-
1013
- const onGodModeChange = (inGodMode: boolean) => {
1014
- const _activeCameras = engine.sceneManager.activeScene._componentsManager._activeCameras;
1015
- if (_activeCameras.length === 0) return;
1016
-
1017
- const defaultActiveCamera = _activeCameras.get(0);
1018
- const defaultActiveCameraEntity = defaultActiveCamera.entity;
1019
-
1020
- __defaultClipPlane__.near = defaultActiveCamera.nearClipPlane;
1021
- __defaultClipPlane__.far = defaultActiveCamera.farClipPlane;
1022
-
1023
- if (!defaultActiveCameraEntity) return;
1024
- const controlType = defaultActiveCamera.isOrthographic ? OrthoControl : OrbitControl;
1025
- const control = defaultActiveCameraEntity.getComponent(controlType);
1026
- if(inGodMode && !control) {
1027
- defaultActiveCameraEntity.addComponent(controlType);
1028
- defaultActiveCamera.nearClipPlane = __debugClipPlane__.near;
1029
- defaultActiveCamera.farClipPlane = __debugClipPlane__.far;
1030
- } else if(!inGodMode && control) {
1031
- control.destroy(true);
1032
- defaultActiveCamera.nearClipPlane = __defaultClipPlane__.near;
1033
- defaultActiveCamera.farClipPlane = __defaultClipPlane__.far;
1034
- }
1035
- }
1036
-
1037
- onGodModeChange(_inGodMode);
1038
-
1039
- debugChannel.addEventListener("message", (e) => {
1040
- if (!(e.data && typeof e.data === 'object')) return;
1041
-
1042
- const type = e.data.type;
1043
-
1044
- if (type === 'toggle-stats-visible') {
1045
- if (!_statsComponent) {
1046
- _statsComponent = engine.sceneManager.activeScene._componentsManager._activeCameras.get(0).entity.addComponent(Stats);
1047
- }
1048
- const value = e.data.payload;
1049
- if(value) {
1050
- _statsComponent.enabled = true;
1051
- } else {
1052
- _statsComponent.enabled = false;
1053
- }
1054
- }
1055
-
1056
- if(type === 'toggle-god-mode') {
1057
- onGodModeChange(_inGodMode = e.data.payload);
1058
- }
1059
- });
1060
- <% } %>
1061
-
1062
-
1063
- engine.run();
1064
- return engine;
1065
- };