@gg-web-engine/core 0.0.49 → 0.0.56
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/2d/entities/entity-2d.d.ts +6 -3
- package/dist/2d/entities/entity-2d.js +20 -17
- package/dist/2d/gg-2d-world.d.ts +3 -0
- package/dist/2d/gg-2d-world.js +21 -13
- package/dist/3d/entities/controllers/input/free-camera.controller.d.ts +9 -1
- package/dist/3d/entities/controllers/input/free-camera.controller.js +45 -12
- package/dist/3d/entities/controllers/input/orbit-camera.controller.d.ts +9 -8
- package/dist/3d/entities/controllers/input/orbit-camera.controller.js +46 -32
- package/dist/3d/entities/entity-3d.d.ts +6 -3
- package/dist/3d/entities/entity-3d.js +20 -17
- package/dist/3d/entities/raycast-vehicle-3d.entity.js +5 -5
- package/dist/3d/gg-3d-world.d.ts +3 -0
- package/dist/3d/gg-3d-world.js +21 -13
- package/dist/3d/loader.js +1 -1
- package/dist/base/clock/global-clock.d.ts +2 -6
- package/dist/base/clock/global-clock.js +16 -16
- package/dist/base/clock/i-clock.d.ts +14 -5
- package/dist/base/clock/i-clock.js +43 -1
- package/dist/base/clock/pausable-clock.d.ts +3 -13
- package/dist/base/clock/pausable-clock.js +8 -16
- package/dist/base/gg-world.d.ts +6 -0
- package/dist/base/gg-world.js +102 -65
- package/dist/base/inputs/keyboard.input.d.ts +1 -1
- package/dist/base/inputs/keyboard.input.js +10 -10
- package/dist/base/inputs/mouse.input.js +3 -2
- package/dist/dev/gg-console.ui.js +14 -4
- package/dist/dev/gg-debugger.ui.d.ts +2 -2
- package/dist/dev/gg-debugger.ui.js +13 -10
- package/dist/dev/gg-static.d.ts +6 -1
- package/dist/dev/gg-static.js +104 -25
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +4 -3
package/dist/3d/loader.js
CHANGED
|
@@ -99,7 +99,7 @@ export class Gg3dLoader {
|
|
|
99
99
|
const loadOptions = Object.assign(Object.assign({}, defaultLoadOptions), options);
|
|
100
100
|
const { resources, meta } = yield this.loadGgGlbResources(path, loadOptions.cachingStrategy);
|
|
101
101
|
const result = {
|
|
102
|
-
entities: resources.map(
|
|
102
|
+
entities: resources.map(x => new Entity3d({ object3D: x.object3D, objectBody: x.body })),
|
|
103
103
|
meta,
|
|
104
104
|
};
|
|
105
105
|
if (loadOptions.loadProps) {
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import { Observable } from 'rxjs';
|
|
2
|
-
import { PausableClock } from './pausable-clock';
|
|
3
1
|
import { IClock } from './i-clock';
|
|
4
2
|
/**
|
|
5
3
|
* A singleton class, providing ability to track time, fire ticks, provide time elapsed + tick delta.
|
|
6
4
|
* Starts as soon as accessed and counts time from 01/01/1970
|
|
7
5
|
*/
|
|
8
|
-
export declare class GgGlobalClock
|
|
6
|
+
export declare class GgGlobalClock extends IClock {
|
|
9
7
|
private static _instance;
|
|
10
8
|
static get instance(): GgGlobalClock;
|
|
11
|
-
private readonly _tick$;
|
|
12
|
-
get tick$(): Observable<[number, number]>;
|
|
13
9
|
get elapsedTime(): number;
|
|
14
|
-
createChildClock(autoStart: boolean): PausableClock;
|
|
15
10
|
private constructor();
|
|
11
|
+
dispose(): void;
|
|
16
12
|
}
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { PausableClock } from './pausable-clock';
|
|
1
|
+
import { IClock } from './i-clock';
|
|
2
|
+
const now = typeof performance === 'undefined' ? () => Date.now() : () => performance.now();
|
|
4
3
|
/**
|
|
5
4
|
* A singleton class, providing ability to track time, fire ticks, provide time elapsed + tick delta.
|
|
6
5
|
* Starts as soon as accessed and counts time from 01/01/1970
|
|
7
6
|
*/
|
|
8
|
-
export class GgGlobalClock {
|
|
7
|
+
export class GgGlobalClock extends IClock {
|
|
9
8
|
static get instance() {
|
|
10
9
|
if (!GgGlobalClock._instance) {
|
|
11
10
|
GgGlobalClock._instance = new GgGlobalClock();
|
|
12
11
|
}
|
|
13
12
|
return GgGlobalClock._instance;
|
|
14
13
|
}
|
|
15
|
-
get tick$() {
|
|
16
|
-
return this._tick$.pipe(map(([oldTime, newTime]) => [newTime, newTime - oldTime]));
|
|
17
|
-
}
|
|
18
14
|
get elapsedTime() {
|
|
19
|
-
return
|
|
20
|
-
}
|
|
21
|
-
createChildClock(autoStart) {
|
|
22
|
-
return new PausableClock(autoStart, this);
|
|
15
|
+
return now();
|
|
23
16
|
}
|
|
24
17
|
constructor() {
|
|
25
|
-
|
|
18
|
+
super(null);
|
|
26
19
|
let oldRelativeTime = this.elapsedTime;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
const tick = () => {
|
|
21
|
+
requestAnimationFrame(tick);
|
|
22
|
+
const prev = oldRelativeTime;
|
|
23
|
+
const cur = this.elapsedTime;
|
|
24
|
+
oldRelativeTime = cur;
|
|
25
|
+
this._tick$.next([prev, cur - prev]);
|
|
26
|
+
};
|
|
27
|
+
requestAnimationFrame(tick);
|
|
28
|
+
}
|
|
29
|
+
dispose() {
|
|
30
|
+
throw new Error('Cannot dispose global clock');
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import { Observable } from 'rxjs';
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Observable, Subject } from 'rxjs';
|
|
2
|
+
export declare abstract class IClock {
|
|
3
|
+
readonly parent: IClock | null;
|
|
4
|
+
protected readonly _tick$: Subject<[number, number]>;
|
|
5
|
+
/**
|
|
6
|
+
* Observable stream of ticks, emitting an array containing the current time and the tick delta.
|
|
7
|
+
*/
|
|
4
8
|
get tick$(): Observable<[number, number]>;
|
|
5
|
-
get elapsedTime(): number;
|
|
6
|
-
|
|
9
|
+
abstract get elapsedTime(): number;
|
|
10
|
+
protected _children: IClock[];
|
|
11
|
+
get children(): IClock[];
|
|
12
|
+
protected constructor(parent: IClock | null);
|
|
13
|
+
addChild(clock: IClock): void;
|
|
14
|
+
removeChild(clock: IClock): void;
|
|
15
|
+
dispose(): void;
|
|
7
16
|
}
|
|
@@ -1 +1,43 @@
|
|
|
1
|
-
|
|
1
|
+
import { Subject } from 'rxjs';
|
|
2
|
+
export class IClock {
|
|
3
|
+
/**
|
|
4
|
+
* Observable stream of ticks, emitting an array containing the current time and the tick delta.
|
|
5
|
+
*/
|
|
6
|
+
get tick$() {
|
|
7
|
+
return this._tick$.asObservable();
|
|
8
|
+
}
|
|
9
|
+
get children() {
|
|
10
|
+
return [...this._children];
|
|
11
|
+
}
|
|
12
|
+
constructor(parent) {
|
|
13
|
+
this.parent = parent;
|
|
14
|
+
this._tick$ = new Subject();
|
|
15
|
+
this._children = [];
|
|
16
|
+
if (parent) {
|
|
17
|
+
parent.addChild(this);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
addChild(clock) {
|
|
21
|
+
if (clock.parent !== this) {
|
|
22
|
+
throw new Error('Incorrect child clock');
|
|
23
|
+
}
|
|
24
|
+
if (!this._children.includes(clock)) {
|
|
25
|
+
this._children.push(clock);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
removeChild(clock) {
|
|
29
|
+
if (clock.parent !== this) {
|
|
30
|
+
throw new Error('Incorrect child clock');
|
|
31
|
+
}
|
|
32
|
+
this._children = this._children.filter(c => c !== clock);
|
|
33
|
+
}
|
|
34
|
+
dispose() {
|
|
35
|
+
if (this.parent) {
|
|
36
|
+
this.parent.removeChild(this);
|
|
37
|
+
}
|
|
38
|
+
for (const c of this._children) {
|
|
39
|
+
c.dispose();
|
|
40
|
+
}
|
|
41
|
+
this._tick$.complete();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Subject } from 'rxjs';
|
|
2
2
|
import { IClock } from './i-clock';
|
|
3
3
|
/**
|
|
4
4
|
* A class providing the ability to track time, fire ticks, provide time elapsed, and tick delta with the ability to suspend/resume it.
|
|
5
5
|
*/
|
|
6
|
-
export declare class PausableClock
|
|
6
|
+
export declare class PausableClock extends IClock {
|
|
7
7
|
protected readonly parentClock: IClock;
|
|
8
8
|
private tickSub;
|
|
9
9
|
private readonly _internalTick$;
|
|
10
|
-
private readonly _tick$;
|
|
11
|
-
/**
|
|
12
|
-
* Observable stream of ticks, emitting an array containing the current time and the tick delta.
|
|
13
|
-
*/
|
|
14
|
-
get tick$(): Observable<[number, number]>;
|
|
15
10
|
/**
|
|
16
11
|
* Checks if the clock is currently running.
|
|
17
12
|
*/
|
|
@@ -54,12 +49,6 @@ export declare class PausableClock implements IClock {
|
|
|
54
49
|
* @param parentClock The parent clock to synchronize with. Defaults to GgGlobalClock instance.
|
|
55
50
|
*/
|
|
56
51
|
constructor(autoStart?: boolean, parentClock?: IClock);
|
|
57
|
-
/**
|
|
58
|
-
* Creates a child clock.
|
|
59
|
-
* @param autoStart Indicates whether the child clock should start automatically.
|
|
60
|
-
* @returns A new instance of PausableClock.
|
|
61
|
-
*/
|
|
62
|
-
createChildClock(autoStart: boolean): PausableClock;
|
|
63
52
|
/**
|
|
64
53
|
* Starts the clock.
|
|
65
54
|
*/
|
|
@@ -84,4 +73,5 @@ export declare class PausableClock implements IClock {
|
|
|
84
73
|
* Stops listening for ticks from the parent clock.
|
|
85
74
|
*/
|
|
86
75
|
protected stopListeningTicks(): void;
|
|
76
|
+
dispose(): void;
|
|
87
77
|
}
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import { filter, Subject } from 'rxjs';
|
|
2
2
|
import { map, tap } from 'rxjs/operators';
|
|
3
3
|
import { GgGlobalClock } from './global-clock';
|
|
4
|
+
import { IClock } from './i-clock';
|
|
4
5
|
/**
|
|
5
6
|
* A class providing the ability to track time, fire ticks, provide time elapsed, and tick delta with the ability to suspend/resume it.
|
|
6
7
|
*/
|
|
7
|
-
export class PausableClock {
|
|
8
|
-
/**
|
|
9
|
-
* Observable stream of ticks, emitting an array containing the current time and the tick delta.
|
|
10
|
-
*/
|
|
11
|
-
get tick$() {
|
|
12
|
-
return this._tick$.asObservable();
|
|
13
|
-
}
|
|
8
|
+
export class PausableClock extends IClock {
|
|
14
9
|
/**
|
|
15
10
|
* Checks if the clock is currently running.
|
|
16
11
|
*/
|
|
@@ -76,10 +71,10 @@ export class PausableClock {
|
|
|
76
71
|
* @param parentClock The parent clock to synchronize with. Defaults to GgGlobalClock instance.
|
|
77
72
|
*/
|
|
78
73
|
constructor(autoStart = false, parentClock = GgGlobalClock.instance) {
|
|
74
|
+
super(parentClock);
|
|
79
75
|
this.parentClock = parentClock;
|
|
80
76
|
this.tickSub = null;
|
|
81
77
|
this._internalTick$ = new Subject();
|
|
82
|
-
this._tick$ = new Subject();
|
|
83
78
|
/**
|
|
84
79
|
* Tick rate limiter. If set to 0 - tick rate is unlimited, 15 means "allow at most 15 ticks per second"
|
|
85
80
|
*/
|
|
@@ -103,14 +98,6 @@ export class PausableClock {
|
|
|
103
98
|
Math.floor((elapsed * this.tickRateLimit) / 1000)), tap(([elapsed]) => (this.lastFiredTickElapsed = elapsed)))
|
|
104
99
|
.subscribe(this._tick$);
|
|
105
100
|
}
|
|
106
|
-
/**
|
|
107
|
-
* Creates a child clock.
|
|
108
|
-
* @param autoStart Indicates whether the child clock should start automatically.
|
|
109
|
-
* @returns A new instance of PausableClock.
|
|
110
|
-
*/
|
|
111
|
-
createChildClock(autoStart) {
|
|
112
|
-
return new PausableClock(autoStart, this);
|
|
113
|
-
}
|
|
114
101
|
/**
|
|
115
102
|
* Starts the clock.
|
|
116
103
|
*/
|
|
@@ -170,4 +157,9 @@ export class PausableClock {
|
|
|
170
157
|
(_a = this.tickSub) === null || _a === void 0 ? void 0 : _a.unsubscribe();
|
|
171
158
|
this.tickSub = null;
|
|
172
159
|
}
|
|
160
|
+
dispose() {
|
|
161
|
+
this.stopListeningTicks();
|
|
162
|
+
this._internalTick$.complete();
|
|
163
|
+
super.dispose();
|
|
164
|
+
}
|
|
173
165
|
}
|
package/dist/base/gg-world.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export declare abstract class GgWorld<D, R, VTypeDoc extends VisualTypeDocRepo<D
|
|
|
16
16
|
readonly physicsWorld: PW;
|
|
17
17
|
private static default_name_counter;
|
|
18
18
|
private static _documentWorlds;
|
|
19
|
+
static readonly worldCreated$: Subject<GgWorld<any, any>>;
|
|
19
20
|
static get documentWorlds(): GgWorld<any, any>[];
|
|
20
21
|
readonly worldClock: PausableClock;
|
|
21
22
|
readonly keyboardInput: KeyboardInput;
|
|
@@ -26,6 +27,7 @@ export declare abstract class GgWorld<D, R, VTypeDoc extends VisualTypeDocRepo<D
|
|
|
26
27
|
readonly tickForwardTo$: Subject<IEntity | 'PHYSICS_WORLD'>;
|
|
27
28
|
readonly tickForwardedTo$: Subject<IEntity | 'PHYSICS_WORLD'>;
|
|
28
29
|
readonly paused$: Subject<boolean>;
|
|
30
|
+
readonly disposed$: Subject<void>;
|
|
29
31
|
protected constructor(visualScene: VS, physicsWorld: PW);
|
|
30
32
|
init(): Promise<void>;
|
|
31
33
|
start(): void;
|
|
@@ -40,4 +42,8 @@ export declare abstract class GgWorld<D, R, VTypeDoc extends VisualTypeDocRepo<D
|
|
|
40
42
|
position?: D, rotation?: R, material?: unknown): IPositionable<D, R> & IRenderableEntity<D, R, VTypeDoc>;
|
|
41
43
|
addEntity(entity: IEntity): void;
|
|
42
44
|
removeEntity(entity: IEntity, dispose?: boolean): void;
|
|
45
|
+
private onGgStaticInitialized;
|
|
46
|
+
protected registerConsoleCommands(ggstatic: {
|
|
47
|
+
registerConsoleCommand: (world: GgWorld<any, any> | null, command: string, handler: (...args: string[]) => Promise<string>, doc?: string) => void;
|
|
48
|
+
}): void;
|
|
43
49
|
}
|
package/dist/base/gg-world.js
CHANGED
|
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import {
|
|
10
|
+
import { IRendererEntity, KeyboardInput, PausableClock, TickOrder, } from '../base';
|
|
11
11
|
import { lastValueFrom, Subject, take } from 'rxjs';
|
|
12
12
|
import { PerformanceMeterEntity } from '../dev';
|
|
13
13
|
export class GgWorld {
|
|
@@ -17,7 +17,7 @@ export class GgWorld {
|
|
|
17
17
|
constructor(visualScene, physicsWorld) {
|
|
18
18
|
this.visualScene = visualScene;
|
|
19
19
|
this.physicsWorld = physicsWorld;
|
|
20
|
-
this.worldClock =
|
|
20
|
+
this.worldClock = new PausableClock(false);
|
|
21
21
|
this.keyboardInput = new KeyboardInput();
|
|
22
22
|
this.name = 'w0x' + (GgWorld.default_name_counter++).toString(16);
|
|
23
23
|
this.children = [];
|
|
@@ -28,72 +28,18 @@ export class GgWorld {
|
|
|
28
28
|
this.tickForwardTo$ = new Subject();
|
|
29
29
|
this.tickForwardedTo$ = new Subject();
|
|
30
30
|
this.paused$ = new Subject();
|
|
31
|
-
|
|
31
|
+
this.disposed$ = new Subject();
|
|
32
32
|
this.keyboardInput.start();
|
|
33
33
|
if (window.ggstatic) {
|
|
34
|
-
window.ggstatic
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
window.
|
|
39
|
-
return this.children
|
|
40
|
-
.filter(e => e instanceof IRendererEntity)
|
|
41
|
-
.map(r => r.name)
|
|
42
|
-
.join('\n');
|
|
43
|
-
}), 'no args; print all renderers in selected world');
|
|
44
|
-
window.ggstatic.registerConsoleCommand(this, 'dr_drawphysics', (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
45
|
-
const value = ['1', 'true', '+'].includes(args[0]);
|
|
46
|
-
const rendererName = args[1];
|
|
47
|
-
let renderer;
|
|
48
|
-
if (rendererName) {
|
|
49
|
-
renderer = this.children.find(x => x instanceof IRendererEntity && x.name === rendererName);
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
renderer = this.children.find(x => x instanceof IRendererEntity);
|
|
53
|
-
}
|
|
54
|
-
if (renderer) {
|
|
55
|
-
renderer.physicsDebugViewActive = value;
|
|
56
|
-
return '' + value;
|
|
57
|
-
}
|
|
58
|
-
return 'false';
|
|
59
|
-
}), 'args: [0 or 1] or [0 or 1, string]; turn on/off physics debug view. Second argument expects renderer ' +
|
|
60
|
-
'name, if not provided first renderer will be picked. Look up for renderer names using command "ls_renderers"');
|
|
61
|
-
window.ggstatic.registerConsoleCommand(this, 'performance_report', (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
62
|
-
let mode = 'avg';
|
|
63
|
-
let samples = 20;
|
|
64
|
-
for (let i = 0; i < 2; i++) {
|
|
65
|
-
if (['avg', 'peak'].includes(args[i])) {
|
|
66
|
-
mode = args[i];
|
|
67
|
-
}
|
|
68
|
-
else if (!isNaN(+args[i])) {
|
|
69
|
-
samples = +args[i];
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
const meter = new PerformanceMeterEntity(samples, 250);
|
|
73
|
-
this.addEntity(meter);
|
|
74
|
-
yield lastValueFrom(this.worldClock.tick$.pipe(take(samples)));
|
|
75
|
-
const report = mode === 'avg' ? meter.avgReport : meter.peakReport;
|
|
76
|
-
this.removeEntity(meter);
|
|
77
|
-
const renderItems = report.entries.map(([name, value]) => `<span style='color:lightgray;'>${name}:</span>` +
|
|
78
|
-
new Array(Math.max(0, 26 - name.length)).join(' ') +
|
|
79
|
-
`${value.toFixed(2)} ms` +
|
|
80
|
-
(mode === 'avg' ? ` (${((value * 100) / report.totalTime).toFixed(2)}%)` : ''));
|
|
81
|
-
let totalColor = 'lightgreen';
|
|
82
|
-
if (report.totalTime > 12) {
|
|
83
|
-
totalColor = report.totalTime < 16 ? 'yellow' : 'red';
|
|
84
|
-
}
|
|
85
|
-
const title = `${mode === 'avg' ? 'Average' : 'Peak'} Frame time`;
|
|
86
|
-
renderItems.unshift(title +
|
|
87
|
-
':' +
|
|
88
|
-
new Array(Math.max(0, 26 - title.length)).join(' ') +
|
|
89
|
-
`<span style='color:${totalColor};'>${report.totalTime.toFixed(2)} ms</span>`);
|
|
90
|
-
renderItems.unshift(`Performance report (${samples} samples)`);
|
|
91
|
-
return renderItems.join('\n');
|
|
92
|
-
}), 'args: [int, avg|peak]; [avg|peak, int]; [avg|peak]; [int] or []; measure how much time was spent per ' +
|
|
93
|
-
'entity in world. Arguments are samples amount (20 by default) and "peak" or "avg" choice, both arguments are ' +
|
|
94
|
-
'optional. "avg" report sorts entities by average time consumed, "peak" records highest value for each entity');
|
|
34
|
+
this.registerConsoleCommands(window.ggstatic);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
this.onGgStaticInitialized = this.onGgStaticInitialized.bind(this);
|
|
38
|
+
window.addEventListener('ggstatic_added', this.onGgStaticInitialized);
|
|
95
39
|
}
|
|
96
40
|
this.worldClock.paused$.subscribe(this.paused$);
|
|
41
|
+
GgWorld._documentWorlds.push(this);
|
|
42
|
+
GgWorld.worldCreated$.next(this);
|
|
97
43
|
}
|
|
98
44
|
init() {
|
|
99
45
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -145,12 +91,15 @@ export class GgWorld {
|
|
|
145
91
|
return this.worldClock.elapsedTime;
|
|
146
92
|
}
|
|
147
93
|
createClock(autoStart) {
|
|
148
|
-
return this.worldClock
|
|
94
|
+
return new PausableClock(autoStart, this.worldClock);
|
|
149
95
|
}
|
|
150
96
|
dispose() {
|
|
151
97
|
if (window.ggstatic) {
|
|
152
98
|
window.ggstatic.deregisterWorldCommands(this);
|
|
153
99
|
}
|
|
100
|
+
else {
|
|
101
|
+
window.removeEventListener('ggstatic_added', this.onGgStaticInitialized);
|
|
102
|
+
}
|
|
154
103
|
this.worldClock.stop();
|
|
155
104
|
this.keyboardInput.stop();
|
|
156
105
|
this.tickStarted$.complete();
|
|
@@ -164,6 +113,9 @@ export class GgWorld {
|
|
|
164
113
|
this.tickListeners.splice(0, this.tickListeners.length);
|
|
165
114
|
this.physicsWorld.dispose();
|
|
166
115
|
this.visualScene.dispose();
|
|
116
|
+
GgWorld._documentWorlds.splice(GgWorld._documentWorlds.indexOf(this), 1);
|
|
117
|
+
this.disposed$.next();
|
|
118
|
+
this.disposed$.complete();
|
|
167
119
|
}
|
|
168
120
|
addEntity(entity) {
|
|
169
121
|
if (!!entity.world) {
|
|
@@ -188,6 +140,91 @@ export class GgWorld {
|
|
|
188
140
|
entity.dispose();
|
|
189
141
|
}
|
|
190
142
|
}
|
|
143
|
+
onGgStaticInitialized() {
|
|
144
|
+
window.removeEventListener('ggstatic_added', this.onGgStaticInitialized);
|
|
145
|
+
this.registerConsoleCommands(window.ggstatic);
|
|
146
|
+
}
|
|
147
|
+
registerConsoleCommands(ggstatic) {
|
|
148
|
+
ggstatic.registerConsoleCommand(this, 'timescale', (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
149
|
+
if (!isNaN(+args[0])) {
|
|
150
|
+
this.worldClock.timeScale = +args[0];
|
|
151
|
+
}
|
|
152
|
+
return this.worldClock.timeScale.toString();
|
|
153
|
+
}), 'args: [ float? ]; Get current time scale of selected world clock or set it.' +
|
|
154
|
+
' Default value is 1.0 (no time scale applied)');
|
|
155
|
+
ggstatic.registerConsoleCommand(this, 'fps_limit', (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
156
|
+
if (!isNaN(+args[0])) {
|
|
157
|
+
this.worldClock.tickRateLimit = +args[0];
|
|
158
|
+
}
|
|
159
|
+
return this.worldClock.tickRateLimit.toString();
|
|
160
|
+
}), 'args: [ int? ]; Get current tick rate limit of selected world clock or set it. 0 means no limit applied');
|
|
161
|
+
ggstatic.registerConsoleCommand(this, 'renderers', () => __awaiter(this, void 0, void 0, function* () {
|
|
162
|
+
return this.children
|
|
163
|
+
.filter(e => e instanceof IRendererEntity)
|
|
164
|
+
.map(r => r.name)
|
|
165
|
+
.join('\n');
|
|
166
|
+
}), 'no args; Print all renderers in selected world');
|
|
167
|
+
ggstatic.registerConsoleCommand(this, 'debug_view', (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
168
|
+
let value = 'toggle';
|
|
169
|
+
let rendererName = undefined;
|
|
170
|
+
for (let arg of args) {
|
|
171
|
+
if (['1', '0'].includes(arg)) {
|
|
172
|
+
value = arg === '1';
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
rendererName = arg;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
let renderer = this.children.find(x => x instanceof IRendererEntity && (!rendererName || x.name === rendererName));
|
|
179
|
+
if (renderer) {
|
|
180
|
+
renderer.physicsDebugViewActive = value === 'toggle' ? !renderer.physicsDebugViewActive : value;
|
|
181
|
+
return renderer.physicsDebugViewActive ? '1' : '0';
|
|
182
|
+
}
|
|
183
|
+
else if (rendererName) {
|
|
184
|
+
throw new Error(`Renderer with name "${rendererName}" not found`);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
throw new Error(`No renderer found`);
|
|
188
|
+
}
|
|
189
|
+
}), 'args: [ 0|1?, string? ]; Turn on/off physics debug view, skip first argument to toggle value.' +
|
|
190
|
+
' Second argument expects renderer name, if not provided first renderer will be picked.' +
|
|
191
|
+
' Use "renderers" to get list of renderers in the world');
|
|
192
|
+
ggstatic.registerConsoleCommand(this, 'performance', (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
193
|
+
let mode = 'avg';
|
|
194
|
+
let samples = 20;
|
|
195
|
+
for (let arg of args) {
|
|
196
|
+
if (['avg', 'peak'].includes(arg)) {
|
|
197
|
+
mode = arg;
|
|
198
|
+
}
|
|
199
|
+
else if (!isNaN(+arg)) {
|
|
200
|
+
samples = +arg;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const meter = new PerformanceMeterEntity(samples, 250);
|
|
204
|
+
this.addEntity(meter);
|
|
205
|
+
yield lastValueFrom(this.worldClock.tick$.pipe(take(samples)));
|
|
206
|
+
const report = mode === 'avg' ? meter.avgReport : meter.peakReport;
|
|
207
|
+
this.removeEntity(meter);
|
|
208
|
+
const renderItems = report.entries.map(([name, value]) => `<span style='color:lightgray;'>${name}:</span>` +
|
|
209
|
+
new Array(Math.max(0, 26 - name.length)).join(' ') +
|
|
210
|
+
`${value.toFixed(2)} ms` +
|
|
211
|
+
(mode === 'avg' ? ` (${((value * 100) / report.totalTime).toFixed(2)}%)` : ''));
|
|
212
|
+
let totalColor = 'lightgreen';
|
|
213
|
+
if (report.totalTime > 12) {
|
|
214
|
+
totalColor = report.totalTime < 16 ? 'yellow' : 'red';
|
|
215
|
+
}
|
|
216
|
+
const title = `${mode === 'avg' ? 'Average' : 'Peak'} Frame time`;
|
|
217
|
+
renderItems.unshift(title +
|
|
218
|
+
':' +
|
|
219
|
+
new Array(Math.max(0, 26 - title.length)).join(' ') +
|
|
220
|
+
`<span style='color:${totalColor};'>${report.totalTime.toFixed(2)} ms</span>`);
|
|
221
|
+
renderItems.unshift(`Performance report (${samples} samples)`);
|
|
222
|
+
return renderItems.join('\n');
|
|
223
|
+
}), 'args: [ int?, avg|peak? ]; Measure how much time was spent per ' +
|
|
224
|
+
'entity in world. Arguments are samples amount (20 by default) and "peak" or "avg" choice, both arguments are ' +
|
|
225
|
+
'optional. "avg" report sorts entities by average time consumed, "peak" records highest value for each entity');
|
|
226
|
+
}
|
|
191
227
|
}
|
|
192
228
|
GgWorld.default_name_counter = 0;
|
|
193
229
|
GgWorld._documentWorlds = [];
|
|
230
|
+
GgWorld.worldCreated$ = new Subject();
|
|
@@ -17,15 +17,6 @@ export class KeyboardInput extends IInput {
|
|
|
17
17
|
* Flag which disables handling key downs, when document has some "typeable" element focused
|
|
18
18
|
*/
|
|
19
19
|
this.skipKeyDownsOnExternalFocus = true;
|
|
20
|
-
/**
|
|
21
|
-
* Which element types should filter key downs when focused
|
|
22
|
-
*/
|
|
23
|
-
this.externalFocusBlacklist = [
|
|
24
|
-
HTMLInputElement,
|
|
25
|
-
HTMLTextAreaElement,
|
|
26
|
-
HTMLSelectElement,
|
|
27
|
-
HTMLButtonElement,
|
|
28
|
-
];
|
|
29
20
|
this.handleKeys = this.handleKeys.bind(this);
|
|
30
21
|
this.resetAllKeys = this.resetAllKeys.bind(this);
|
|
31
22
|
this.onPointerLockChange = this.onPointerLockChange.bind(this);
|
|
@@ -127,7 +118,7 @@ export class KeyboardInput extends IInput {
|
|
|
127
118
|
}
|
|
128
119
|
const pressed = e.type == 'keydown';
|
|
129
120
|
if (pressed && this.skipKeyDownsOnExternalFocus && document.activeElement) {
|
|
130
|
-
for (const k of
|
|
121
|
+
for (const k of KeyboardInput.externalFocusBlacklist) {
|
|
131
122
|
if (document.activeElement instanceof k) {
|
|
132
123
|
return;
|
|
133
124
|
}
|
|
@@ -151,3 +142,12 @@ export class KeyboardInput extends IInput {
|
|
|
151
142
|
}
|
|
152
143
|
}
|
|
153
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Which element types should filter key downs when focused
|
|
147
|
+
*/
|
|
148
|
+
KeyboardInput.externalFocusBlacklist = [
|
|
149
|
+
HTMLInputElement,
|
|
150
|
+
HTMLTextAreaElement,
|
|
151
|
+
HTMLSelectElement,
|
|
152
|
+
HTMLButtonElement,
|
|
153
|
+
];
|
|
@@ -156,7 +156,7 @@ export class MouseInput extends IInput {
|
|
|
156
156
|
if (this.options.canvas) {
|
|
157
157
|
this.options.canvas.releasePointerCapture(event.pointerId);
|
|
158
158
|
}
|
|
159
|
-
|
|
159
|
+
window.removeEventListener('pointerup', onPointerUp);
|
|
160
160
|
this._element.removeEventListener('pointercancel', onPointerUp);
|
|
161
161
|
}
|
|
162
162
|
this._state$.next(pointerLengthsStateMap[Math.min(pointers.length, 2)]);
|
|
@@ -169,7 +169,8 @@ export class MouseInput extends IInput {
|
|
|
169
169
|
if (this.options.canvas) {
|
|
170
170
|
this.options.canvas.setPointerCapture(event.pointerId);
|
|
171
171
|
}
|
|
172
|
-
this._element
|
|
172
|
+
// use window instead of this._element to handle case when mouse was released over other element
|
|
173
|
+
window.addEventListener('pointerup', onPointerUp);
|
|
173
174
|
this._element.addEventListener('pointercancel', onPointerUp);
|
|
174
175
|
}
|
|
175
176
|
catch (err) {
|
|
@@ -7,6 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
import { VERSION } from '../version';
|
|
10
11
|
export class GgConsoleUI {
|
|
11
12
|
constructor() {
|
|
12
13
|
this.output = `
|
|
@@ -20,11 +21,12 @@ export class GgConsoleUI {
|
|
|
20
21
|
░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
|
|
21
22
|
░ ░ ░ ░ ░ ░
|
|
22
23
|
░
|
|
24
|
+
Version: ${VERSION}
|
|
23
25
|
>>> https://github.com/AndyGura/gg-web-engine <<<
|
|
24
26
|
Welcome to GG web engine UI console.
|
|
25
27
|
Enter command in input below.
|
|
26
28
|
|
|
27
|
-
List of available commands: `.replace(/ /g, ' ') + `<span style=
|
|
29
|
+
List of available commands: `.replace(/ /g, ' ') + `<span style='color:yellow'>commands</span>`;
|
|
28
30
|
this.commandHistory = [];
|
|
29
31
|
this.currentCommandIndex = 0; // for repeating command using up/down arrow keys
|
|
30
32
|
this.elements = null;
|
|
@@ -98,9 +100,14 @@ List of available commands: `.replace(/ /g, ' ') + `<span style="color:yell
|
|
|
98
100
|
if (value.trim() === '') {
|
|
99
101
|
return;
|
|
100
102
|
}
|
|
101
|
-
let autocompletion =
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
let autocompletion = null;
|
|
104
|
+
for (let [command] of window.ggstatic.availableCommands) {
|
|
105
|
+
if (command.startsWith(value) && (!autocompletion || autocompletion.length > command.length)) {
|
|
106
|
+
autocompletion = command;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (autocompletion && autocompletion.length > value.length) {
|
|
110
|
+
this.elements.input.value = autocompletion;
|
|
104
111
|
this.elements.input.setSelectionRange(value.length, this.elements.input.value.length);
|
|
105
112
|
}
|
|
106
113
|
};
|
|
@@ -135,6 +142,9 @@ List of available commands: `.replace(/ /g, ' ') + `<span style="color:yell
|
|
|
135
142
|
onInput() {
|
|
136
143
|
return __awaiter(this, void 0, void 0, function* () {
|
|
137
144
|
const command = this.elements.input.value;
|
|
145
|
+
if (command.length === 0) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
138
148
|
this.elements.input.value = '';
|
|
139
149
|
this.stdout('\n> ' + command);
|
|
140
150
|
this.stdout('\n' + (yield window.ggstatic.console(command)));
|
|
@@ -4,11 +4,11 @@ export declare class GgDebuggerUI {
|
|
|
4
4
|
private statsRemoved$;
|
|
5
5
|
get showStats(): boolean;
|
|
6
6
|
private currentWorld;
|
|
7
|
-
setShowStats(selectedWorld: GgWorld<any, any
|
|
7
|
+
setShowStats(selectedWorld: GgWorld<any, any> | null, value: boolean): void;
|
|
8
8
|
private debugControlsRemoved$;
|
|
9
9
|
private viewUpdated$;
|
|
10
10
|
get showDebugControls(): boolean;
|
|
11
|
-
setShowDebugControls(selectedWorld: GgWorld<any, any
|
|
11
|
+
setShowDebugControls(selectedWorld: GgWorld<any, any> | null, value: boolean): void;
|
|
12
12
|
private snapshot;
|
|
13
13
|
perfStatsMode: 'AVG' | 'PEAK';
|
|
14
14
|
private performanceStatsSnapshot;
|