@gg-web-engine/core 0.0.48 → 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/2d/models/shapes.d.ts +3 -1
- package/dist/3d/entities/controllers/input/car-keyboard-handling.controller.d.ts +1 -1
- package/dist/3d/entities/controllers/input/free-camera.controller.d.ts +32 -13
- package/dist/3d/entities/controllers/input/free-camera.controller.js +99 -45
- package/dist/3d/entities/controllers/input/gg-car-keyboard-handling.controller.d.ts +1 -1
- package/dist/3d/entities/controllers/input/orbit-camera.controller.d.ts +29 -9
- package/dist/3d/entities/controllers/input/orbit-camera.controller.js +64 -33
- 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/3d/models/shapes.d.ts +3 -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/index.d.ts +1 -0
- package/dist/base/index.js +1 -0
- 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.d.ts +1 -0
- package/dist/base/inputs/mouse.input.js +6 -2
- package/dist/base/pipes/gg-elastic.pipe.d.ts +5 -0
- package/dist/base/pipes/gg-elastic.pipe.js +44 -0
- 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/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();
|
package/dist/base/index.d.ts
CHANGED
package/dist/base/index.js
CHANGED
|
@@ -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
|
+
];
|
|
@@ -59,6 +59,7 @@ export declare class MouseInput extends IInput<[], [unlockPointer?: boolean]> {
|
|
|
59
59
|
private _wheel$;
|
|
60
60
|
get wheel$(): Observable<number>;
|
|
61
61
|
get isPointerLocked(): boolean;
|
|
62
|
+
get isPointerLocked$(): Observable<boolean>;
|
|
62
63
|
private readonly options;
|
|
63
64
|
private _delta$;
|
|
64
65
|
private _position$;
|
|
@@ -67,6 +67,9 @@ export class MouseInput extends IInput {
|
|
|
67
67
|
get isPointerLocked() {
|
|
68
68
|
return !!document.pointerLockElement;
|
|
69
69
|
}
|
|
70
|
+
get isPointerLocked$() {
|
|
71
|
+
return fromEvent(document, 'pointerlockchange').pipe(map(() => this.isPointerLocked));
|
|
72
|
+
}
|
|
70
73
|
get state() {
|
|
71
74
|
return this._state$.getValue();
|
|
72
75
|
}
|
|
@@ -153,7 +156,7 @@ export class MouseInput extends IInput {
|
|
|
153
156
|
if (this.options.canvas) {
|
|
154
157
|
this.options.canvas.releasePointerCapture(event.pointerId);
|
|
155
158
|
}
|
|
156
|
-
|
|
159
|
+
window.removeEventListener('pointerup', onPointerUp);
|
|
157
160
|
this._element.removeEventListener('pointercancel', onPointerUp);
|
|
158
161
|
}
|
|
159
162
|
this._state$.next(pointerLengthsStateMap[Math.min(pointers.length, 2)]);
|
|
@@ -166,7 +169,8 @@ export class MouseInput extends IInput {
|
|
|
166
169
|
if (this.options.canvas) {
|
|
167
170
|
this.options.canvas.setPointerCapture(event.pointerId);
|
|
168
171
|
}
|
|
169
|
-
this._element
|
|
172
|
+
// use window instead of this._element to handle case when mouse was released over other element
|
|
173
|
+
window.addEventListener('pointerup', onPointerUp);
|
|
170
174
|
this._element.addEventListener('pointercancel', onPointerUp);
|
|
171
175
|
}
|
|
172
176
|
catch (err) {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Observable, OperatorFunction } from 'rxjs';
|
|
2
|
+
export declare function ggElastic<T>(tick$: Observable<[number, number]>, // [elapsed, delta]
|
|
3
|
+
elasticity: number, // Elasticity parameter
|
|
4
|
+
mix: (a: T, b: T, factor: number) => T, // Mixing function
|
|
5
|
+
equals: (a: T, b: T) => boolean): OperatorFunction<T, T>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { filter, map, scan, switchMap } from 'rxjs';
|
|
2
|
+
export function ggElastic(tick$, // [elapsed, delta]
|
|
3
|
+
elasticity, // Elasticity parameter
|
|
4
|
+
mix, // Mixing function
|
|
5
|
+
equals) {
|
|
6
|
+
return (source$) => {
|
|
7
|
+
return source$.pipe(
|
|
8
|
+
// Track the target value
|
|
9
|
+
scan((acc, newValue) => {
|
|
10
|
+
var _a;
|
|
11
|
+
return ({
|
|
12
|
+
targetValue: newValue,
|
|
13
|
+
currentValue: (_a = acc.currentValue) !== null && _a !== void 0 ? _a : newValue, // Initialize to the first value on startup
|
|
14
|
+
hasReachedTarget: false, // Reset reach status on new target
|
|
15
|
+
});
|
|
16
|
+
}, { targetValue: null, currentValue: null, hasReachedTarget: false }),
|
|
17
|
+
// Switch to using tick$ for timing, emitting interpolated values with each tick
|
|
18
|
+
switchMap(state => tick$.pipe(map(([_, delta]) => {
|
|
19
|
+
if (state.targetValue === null || state.currentValue === null) {
|
|
20
|
+
return state.currentValue; // If we have no values, output nothing
|
|
21
|
+
}
|
|
22
|
+
// Skip processing if we’ve reached the target and stop emitting
|
|
23
|
+
if (state.hasReachedTarget) {
|
|
24
|
+
return undefined; // Stop emitting by returning `undefined`
|
|
25
|
+
}
|
|
26
|
+
// Adjust factor based on elasticity and delta time
|
|
27
|
+
const factor = 1 - Math.exp(-delta / elasticity);
|
|
28
|
+
// Blend halfway towards the target on each tick
|
|
29
|
+
const newInterpolatedValue = mix(state.currentValue, state.targetValue, factor);
|
|
30
|
+
// Update the currentValue to the new interpolated position
|
|
31
|
+
state.currentValue = newInterpolatedValue;
|
|
32
|
+
// Check if the interpolated value is "close enough" to the target
|
|
33
|
+
if (equals(newInterpolatedValue, state.targetValue)) {
|
|
34
|
+
state.hasReachedTarget = true; // Mark as reached
|
|
35
|
+
return state.targetValue; // Emit exact target value
|
|
36
|
+
}
|
|
37
|
+
return newInterpolatedValue;
|
|
38
|
+
}))),
|
|
39
|
+
// Filter out `undefined` emissions (to stop emitting when settled)
|
|
40
|
+
map(value => (value !== undefined ? value : null)),
|
|
41
|
+
// Remove `null` values (after stopping)
|
|
42
|
+
filter(value => value !== null));
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -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;
|
|
@@ -69,7 +69,7 @@ export class GgDebuggerUI {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
this.currentWorld = selectedWorld;
|
|
72
|
-
if (value) {
|
|
72
|
+
if (selectedWorld && value) {
|
|
73
73
|
const stats = new Stats();
|
|
74
74
|
this.ui.stats = stats;
|
|
75
75
|
stats.dom.style.left = 'unset';
|
|
@@ -139,9 +139,10 @@ export class GgDebuggerUI {
|
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
makeSnapshot() {
|
|
142
|
+
var _a, _b;
|
|
142
143
|
const renderers = [];
|
|
143
144
|
let performanceMeter = null;
|
|
144
|
-
for (const e of this.currentWorld.children) {
|
|
145
|
+
for (const e of ((_a = this.currentWorld) === null || _a === void 0 ? void 0 : _a.children) || []) {
|
|
145
146
|
if (e instanceof IRendererEntity) {
|
|
146
147
|
renderers.push(e);
|
|
147
148
|
}
|
|
@@ -150,7 +151,7 @@ export class GgDebuggerUI {
|
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
153
|
return {
|
|
153
|
-
timeScale: this.currentWorld.worldClock.timeScale,
|
|
154
|
+
timeScale: ((_b = this.currentWorld) === null || _b === void 0 ? void 0 : _b.worldClock.timeScale) || NaN,
|
|
154
155
|
renderers: renderers.map(r => ({
|
|
155
156
|
name: r.name,
|
|
156
157
|
entity: r,
|
|
@@ -160,9 +161,10 @@ export class GgDebuggerUI {
|
|
|
160
161
|
};
|
|
161
162
|
}
|
|
162
163
|
makePerformanceStatsSnapshot() {
|
|
164
|
+
var _a;
|
|
163
165
|
if (!this.snapshot.performanceStatsEnabled)
|
|
164
166
|
return null;
|
|
165
|
-
let performanceMeter = this.currentWorld.children.find(e => e instanceof PerformanceMeterEntity);
|
|
167
|
+
let performanceMeter = (((_a = this.currentWorld) === null || _a === void 0 ? void 0 : _a.children) || []).find(e => e instanceof PerformanceMeterEntity);
|
|
166
168
|
if (performanceMeter) {
|
|
167
169
|
if (this.perfStatsMode === 'AVG') {
|
|
168
170
|
return performanceMeter.avgReport;
|
|
@@ -178,19 +180,19 @@ export class GgDebuggerUI {
|
|
|
178
180
|
for (const { entity, physicsDebugViewActive } of this.snapshot.renderers) {
|
|
179
181
|
html += `
|
|
180
182
|
<div ${this.css}>
|
|
181
|
-
<input type='checkbox' name='checkbox' id='physics_debugger_checkbox_id_${entity.name}' value='1'${physicsDebugViewActive ? ' checked' : ''}>
|
|
183
|
+
<input type='checkbox' name='checkbox' id='physics_debugger_checkbox_id_${entity.name}' onkeydown='event.preventDefault()' value='1'${physicsDebugViewActive ? ' checked' : ''}>
|
|
182
184
|
<label for='physics_debugger_checkbox_id_${entity.name}' style='user-select: none;'>Physics debugger${this.snapshot.renderers.length > 1 ? ' (' + entity.name + ')' : ''}</label>
|
|
183
185
|
</div>`;
|
|
184
186
|
}
|
|
185
187
|
html += `
|
|
186
188
|
<div ${this.css}>
|
|
187
|
-
<input id='time_scale_slider' type='range' min='0' max='5' step='0.01' style='flex-grow:1' value='${this.snapshot.timeScale}'/>
|
|
189
|
+
<input id='time_scale_slider' type='range' min='0' max='5' step='0.01' onkeydown='event.preventDefault()' style='flex-grow:1' value='${this.snapshot.timeScale}'/>
|
|
188
190
|
<label for='time_scale_slider' style='user-select: none;'>Time scale</label>
|
|
189
191
|
</div>`;
|
|
190
192
|
html +=
|
|
191
193
|
`
|
|
192
194
|
<div ${this.css}>
|
|
193
|
-
<input type='checkbox' name='checkbox' id='perf_stats_checkbox_id' value='1'${this.snapshot.performanceStatsEnabled ? ' checked' : ''}>
|
|
195
|
+
<input type='checkbox' name='checkbox' id='perf_stats_checkbox_id' onkeydown='event.preventDefault()' value='1'${this.snapshot.performanceStatsEnabled ? ' checked' : ''}>
|
|
194
196
|
<label for='perf_stats_checkbox_id' style='user-select: none;'>Entities performance distribution</label>
|
|
195
197
|
</div>
|
|
196
198
|
` +
|
|
@@ -233,12 +235,13 @@ export class GgDebuggerUI {
|
|
|
233
235
|
fromEvent(document.getElementById('perf_stats_checkbox_id'), 'change')
|
|
234
236
|
.pipe(takeUntil(this.debugControlsRemoved$), takeUntil(this.viewUpdated$))
|
|
235
237
|
.subscribe(e => {
|
|
236
|
-
|
|
238
|
+
var _a;
|
|
239
|
+
const entity = (((_a = this.currentWorld) === null || _a === void 0 ? void 0 : _a.children) || []).find(x => x instanceof PerformanceMeterEntity);
|
|
237
240
|
if (e.target.checked == !entity) {
|
|
238
241
|
if (entity) {
|
|
239
242
|
this.currentWorld.removeEntity(entity);
|
|
240
243
|
}
|
|
241
|
-
else {
|
|
244
|
+
else if (this.currentWorld) {
|
|
242
245
|
this.currentWorld.addEntity(new PerformanceMeterEntity());
|
|
243
246
|
}
|
|
244
247
|
}
|
|
@@ -265,7 +268,7 @@ export class GgDebuggerUI {
|
|
|
265
268
|
html += `
|
|
266
269
|
<div ${pCss}>
|
|
267
270
|
<label for='stat_progress' ${labelCss}>${total.toFixed(2)}ms</label>
|
|
268
|
-
<progress id=
|
|
271
|
+
<progress id='stat_progress' ${progressCss} max='16' value='${total}'></progress>
|
|
269
272
|
<span ${spanCss}>Total frame time</span>
|
|
270
273
|
</div>`;
|
|
271
274
|
for (const [i, [name, value]] of this.performanceStatsSnapshot.entries.entries()) {
|
package/dist/dev/gg-static.d.ts
CHANGED
|
@@ -7,12 +7,17 @@ export declare class GgStatic {
|
|
|
7
7
|
get devConsoleEnabled(): boolean;
|
|
8
8
|
consoleKeyPressEventListener: (event: KeyboardEvent) => void;
|
|
9
9
|
set devConsoleEnabled(value: boolean);
|
|
10
|
+
toggleDevConsole(value: boolean): void;
|
|
11
|
+
private showStats$;
|
|
10
12
|
get showStats(): boolean;
|
|
11
13
|
set showStats(value: boolean);
|
|
14
|
+
private showDebugControls$;
|
|
12
15
|
get showDebugControls(): boolean;
|
|
13
16
|
set showDebugControls(value: boolean);
|
|
14
|
-
private _selectedWorld
|
|
17
|
+
private _selectedWorld$;
|
|
15
18
|
get selectedWorld(): GgWorld<any, any> | null;
|
|
19
|
+
private set selectedWorld(value);
|
|
20
|
+
private autoAssignSelectedWorld;
|
|
16
21
|
get availableCommands(): [string, {
|
|
17
22
|
handler: (...args: string[]) => Promise<string>;
|
|
18
23
|
doc?: string;
|