@gg-web-engine/core 0.0.38 → 0.0.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/2d/components/physics/i-rigid-body-2d.component.d.ts +3 -0
  2. package/dist/2d/components/physics/i-trigger-2d.component.d.ts +3 -0
  3. package/dist/2d/components/rendering/i-visual-scene-2d.component.d.ts +1 -1
  4. package/dist/2d/entities/i-renderable-2d.entity.d.ts +3 -1
  5. package/dist/2d/entities/i-renderable-2d.entity.js +7 -0
  6. package/dist/2d/gg-2d-world.d.ts +1 -0
  7. package/dist/2d/models/body-options.d.ts +5 -1
  8. package/dist/3d/components/physics/i-physics-world-3d.component.d.ts +7 -0
  9. package/dist/3d/components/physics/i-rigid-body-3d.component.d.ts +3 -0
  10. package/dist/3d/components/physics/i-trigger-3d.component.d.ts +3 -0
  11. package/dist/3d/components/rendering/i-visual-scene-3d.component.d.ts +1 -1
  12. package/dist/3d/entities/controllers/input/car-keyboard-handling.controller.js +2 -2
  13. package/dist/3d/entities/controllers/input/gg-car-keyboard-handling.controller.js +3 -3
  14. package/dist/3d/entities/i-renderable-3d.entity.d.ts +3 -1
  15. package/dist/3d/entities/i-renderable-3d.entity.js +7 -0
  16. package/dist/3d/entities/map-graph-3d.entity.d.ts +2 -3
  17. package/dist/3d/entities/map-graph-3d.entity.js +10 -7
  18. package/dist/3d/entities/raycast-vehicle-3d.entity.d.ts +2 -0
  19. package/dist/3d/entities/raycast-vehicle-3d.entity.js +15 -1
  20. package/dist/3d/factories.d.ts +3 -3
  21. package/dist/3d/gg-3d-world.d.ts +1 -0
  22. package/dist/3d/models/body-options.d.ts +5 -1
  23. package/dist/3d/models/shapes.d.ts +41 -1
  24. package/dist/base/clock/pausable-clock.d.ts +62 -1
  25. package/dist/base/clock/pausable-clock.js +102 -8
  26. package/dist/base/components/physics/i-body.component.d.ts +3 -1
  27. package/dist/base/components/physics/i-physics-world.component.d.ts +8 -6
  28. package/dist/base/components/rendering/i-renderer.component.d.ts +6 -2
  29. package/dist/base/components/rendering/i-visual-scene.component.d.ts +0 -4
  30. package/dist/base/entities/controllers/inline-controller.d.ts +1 -1
  31. package/dist/base/entities/controllers/inline-controller.js +4 -1
  32. package/dist/base/entities/i-entity.d.ts +1 -1
  33. package/dist/base/entities/i-entity.js +3 -3
  34. package/dist/base/entities/i-renderer.entity.d.ts +4 -0
  35. package/dist/base/entities/i-renderer.entity.js +8 -0
  36. package/dist/base/gg-world.d.ts +8 -5
  37. package/dist/base/gg-world.js +81 -39
  38. package/dist/base/index.d.ts +0 -1
  39. package/dist/base/index.js +0 -1
  40. package/dist/base/inputs/mouse.input.js +9 -4
  41. package/dist/base/models/body-options.d.ts +9 -0
  42. package/dist/dev/gg-console.ui.js +16 -8
  43. package/dist/dev/gg-debugger.ui.d.ts +9 -0
  44. package/dist/dev/gg-debugger.ui.js +206 -34
  45. package/dist/dev/gg-static.d.ts +0 -1
  46. package/dist/dev/gg-static.js +15 -8
  47. package/dist/dev/index.d.ts +1 -0
  48. package/dist/dev/index.js +1 -0
  49. package/dist/dev/performance-meter.entity.d.ts +21 -0
  50. package/dist/dev/performance-meter.entity.js +120 -0
  51. package/package.json +1 -1
  52. package/dist/base/interfaces/i-debug-physics-drawer.d.ts +0 -8
  53. package/dist/base/interfaces/i-debug-physics-drawer.js +0 -1
@@ -1,6 +1,42 @@
1
- import { createInlineTickController } from '../base';
2
- import { fromEvent, Subject, takeUntil } from 'rxjs';
1
+ import { createInlineTickController, IRendererEntity } from '../base';
2
+ import { animationFrameScheduler, fromEvent, of, Subject, takeUntil } from 'rxjs';
3
3
  import Stats from 'stats.js';
4
+ import { repeat } from 'rxjs/operators';
5
+ import { PerformanceMeterEntity } from './performance-meter.entity';
6
+ const snapshotEqual = (a, b) => {
7
+ if (a.timeScale !== b.timeScale)
8
+ return false;
9
+ if (a.performanceStatsEnabled !== b.performanceStatsEnabled)
10
+ return false;
11
+ if (a.renderers.length !== b.renderers.length)
12
+ return false;
13
+ for (let i = 0; i < a.renderers.length; i++) {
14
+ if (a.renderers[i].name !== b.renderers[i].name)
15
+ return false;
16
+ if (a.renderers[i].entity !== b.renderers[i].entity)
17
+ return false;
18
+ if (a.renderers[i].physicsDebugViewActive !== b.renderers[i].physicsDebugViewActive)
19
+ return false;
20
+ }
21
+ return true;
22
+ };
23
+ const performanceStatsSnapshotEqual = (a, b) => {
24
+ if (!!a !== !!b)
25
+ return false;
26
+ if (a && b) {
27
+ if (a.totalTime !== b.totalTime)
28
+ return false;
29
+ if (a.entries.length !== b.entries.length)
30
+ return false;
31
+ for (let i = 0; i < a.entries.length; i++) {
32
+ if (a.entries[i][0] !== b.entries[i][0])
33
+ return false;
34
+ if (a.entries[i][1] !== b.entries[i][1])
35
+ return false;
36
+ }
37
+ }
38
+ return true;
39
+ };
4
40
  export class GgDebuggerUI {
5
41
  constructor() {
6
42
  this.ui = {
@@ -10,6 +46,15 @@ export class GgDebuggerUI {
10
46
  this.statsRemoved$ = new Subject();
11
47
  this.currentWorld = null;
12
48
  this.debugControlsRemoved$ = new Subject();
49
+ this.viewUpdated$ = new Subject();
50
+ this.snapshot = {
51
+ timeScale: 1,
52
+ renderers: [],
53
+ performanceStatsEnabled: false,
54
+ };
55
+ this.perfStatsMode = 'AVG';
56
+ this.performanceStatsSnapshot = null;
57
+ this.css = "style='display:flex;align-items:center;margin:0.25rem;'";
13
58
  }
14
59
  get showStats() {
15
60
  return !!this.ui.stats;
@@ -31,12 +76,12 @@ export class GgDebuggerUI {
31
76
  stats.dom.style.right = '0';
32
77
  stats.showPanel(0); // 0: fps, 1: ms, 2: mb
33
78
  document.body.appendChild(stats.dom);
34
- createInlineTickController(selectedWorld, -1)
79
+ createInlineTickController(selectedWorld, -1, 'fps_meter_init')
35
80
  .pipe(takeUntil(this.statsRemoved$))
36
81
  .subscribe(() => {
37
82
  stats === null || stats === void 0 ? void 0 : stats.begin();
38
83
  });
39
- createInlineTickController(selectedWorld, 10000)
84
+ createInlineTickController(selectedWorld, 10000, 'fps_meter')
40
85
  .pipe(takeUntil(this.statsRemoved$))
41
86
  .subscribe(() => {
42
87
  stats === null || stats === void 0 ? void 0 : stats.end();
@@ -64,48 +109,175 @@ export class GgDebuggerUI {
64
109
  this.currentWorld = selectedWorld;
65
110
  if (value) {
66
111
  const debugControlsContainer = document.createElement('div');
112
+ document.body.appendChild(debugControlsContainer);
67
113
  this.ui.debugControlsContainer = debugControlsContainer;
68
- const debugLabelCss = "style='display:flex;align-items:center;margin:0.25rem;'";
69
114
  debugControlsContainer.style.cssText =
70
115
  'position:fixed;top:48px;right:0;opacity:0.9;z-index:9999;background-color:#333;color:white;display:flex;flex-direction:column';
71
- debugControlsContainer.innerHTML = `
72
- <div ${debugLabelCss}>
73
- <input type='checkbox' name='checkbox' id='physics_debugger_checkbox_id' value='1'${this.currentWorld.physicsWorld.physicsDebugViewActive ? ' checked' : ''}>
74
- <label for='physics_debugger_checkbox_id' style='user-select: none;'>Show physics bodies in scene</label>
116
+ this.snapshot = this.makeSnapshot();
117
+ this.performanceStatsSnapshot = this.makePerformanceStatsSnapshot();
118
+ this.renderControls(debugControlsContainer);
119
+ this.renderPerformanceStats();
120
+ of(undefined, animationFrameScheduler)
121
+ .pipe(repeat(), takeUntil(this.debugControlsRemoved$))
122
+ .subscribe(() => {
123
+ const newSnapshot = this.makeSnapshot();
124
+ if (!snapshotEqual(this.snapshot, newSnapshot)) {
125
+ this.snapshot = newSnapshot;
126
+ this.renderControls(debugControlsContainer);
127
+ }
128
+ const newPerformanceStats = this.makePerformanceStatsSnapshot();
129
+ if (!performanceStatsSnapshotEqual(this.performanceStatsSnapshot, newPerformanceStats)) {
130
+ this.performanceStatsSnapshot = newPerformanceStats;
131
+ this.renderPerformanceStats();
132
+ }
133
+ });
134
+ }
135
+ else {
136
+ this.debugControlsRemoved$.next();
137
+ document.body.removeChild(this.ui.debugControlsContainer);
138
+ this.ui.debugControlsContainer = null;
139
+ }
140
+ }
141
+ makeSnapshot() {
142
+ const renderers = [];
143
+ let performanceMeter = null;
144
+ for (const e of this.currentWorld.children) {
145
+ if (e instanceof IRendererEntity) {
146
+ renderers.push(e);
147
+ }
148
+ else if (e instanceof PerformanceMeterEntity) {
149
+ performanceMeter = e;
150
+ }
151
+ }
152
+ return {
153
+ timeScale: this.currentWorld.worldClock.timeScale,
154
+ renderers: renderers.map(r => ({
155
+ name: r.name,
156
+ entity: r,
157
+ physicsDebugViewActive: r.physicsDebugViewActive,
158
+ })),
159
+ performanceStatsEnabled: !!performanceMeter,
160
+ };
161
+ }
162
+ makePerformanceStatsSnapshot() {
163
+ if (!this.snapshot.performanceStatsEnabled)
164
+ return null;
165
+ let performanceMeter = this.currentWorld.children.find(e => e instanceof PerformanceMeterEntity);
166
+ if (performanceMeter) {
167
+ if (this.perfStatsMode === 'AVG') {
168
+ return performanceMeter.avgReport;
169
+ }
170
+ else if (this.perfStatsMode === 'PEAK') {
171
+ return performanceMeter.peakReport;
172
+ }
173
+ }
174
+ return null;
175
+ }
176
+ renderControls(debugControlsContainer) {
177
+ let html = '';
178
+ for (const { entity, physicsDebugViewActive } of this.snapshot.renderers) {
179
+ html += `
180
+ <div ${this.css}>
181
+ <input type='checkbox' name='checkbox' id='physics_debugger_checkbox_id_${entity.name}' value='1'${physicsDebugViewActive ? ' checked' : ''}>
182
+ <label for='physics_debugger_checkbox_id_${entity.name}' style='user-select: none;'>Physics debugger${this.snapshot.renderers.length > 1 ? ' (' + entity.name + ')' : ''}</label>
75
183
  </div>`;
76
- // <div ${debugLabelCss}>
77
- // <input id="time_scale_slider" type="range" min="0" max="10" step="0.1" style="flex-grow:1" value="${
78
- // this.currentWorld.physicsWorld.timeScale
79
- // }"/>
80
- // <label for="time_scale_slider" style="user-select: none;">Time scale</label>
81
- // </div>`;
82
- document.body.appendChild(debugControlsContainer);
83
- fromEvent(document.getElementById('physics_debugger_checkbox_id'), 'change')
84
- .pipe(takeUntil(this.debugControlsRemoved$))
184
+ }
185
+ html += `
186
+ <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}'/>
188
+ <label for='time_scale_slider' style='user-select: none;'>Time scale</label>
189
+ </div>`;
190
+ html +=
191
+ `
192
+ <div ${this.css}>
193
+ <input type='checkbox' name='checkbox' id='perf_stats_checkbox_id' value='1'${this.snapshot.performanceStatsEnabled ? ' checked' : ''}>
194
+ <label for='perf_stats_checkbox_id' style='user-select: none;'>Entities performance distribution</label>
195
+ </div>
196
+ ` +
197
+ (this.snapshot.performanceStatsEnabled
198
+ ? `<div ${this.css}>
199
+ <label for='per_mode'>Mode:</label>
200
+ <select id='per_mode'>
201
+ <option value='AVG' ${this.perfStatsMode === 'AVG' ? 'selected' : ''}>Average</option>
202
+ <option value='PEAK' ${this.perfStatsMode === 'PEAK' ? 'selected' : ''}>Peak</option>
203
+ </select>
204
+ </div>`
205
+ : '') +
206
+ `
207
+ <div style='display: contents' id='perf_stats_container'></div>`;
208
+ debugControlsContainer.innerHTML = html;
209
+ debugControlsContainer.style.minWidth = '25rem';
210
+ this.viewUpdated$.next();
211
+ for (const { entity } of this.snapshot.renderers) {
212
+ fromEvent(document.getElementById('physics_debugger_checkbox_id_' + entity.name), 'change')
213
+ .pipe(takeUntil(this.debugControlsRemoved$), takeUntil(this.viewUpdated$))
85
214
  .subscribe(e => {
86
215
  try {
87
- this.currentWorld.physicsDebugViewActive = e.target.checked;
216
+ entity.physicsDebugViewActive = e.target.checked;
88
217
  }
89
218
  catch (err) {
90
219
  console.error(err);
91
220
  }
92
- e.target.checked = this.currentWorld.physicsDebugViewActive;
93
221
  });
94
- // fromEvent(document.getElementById('time_scale_slider')! as HTMLInputElement, 'change')
95
- // .pipe(takeUntil(this.debugControlsRemoved$))
96
- // .subscribe(e => {
97
- // try {
98
- // this.currentWorld.physicsWorld.timeScale = +(e.target as HTMLInputElement).value;
99
- // } catch (err) {
100
- // console.error(err);
101
- // }
102
- // (e.target as HTMLInputElement).value = '' + ( this.currentWorld.physicsWorld.timeScale || 1);
103
- // });
104
222
  }
105
- else {
106
- this.debugControlsRemoved$.next();
107
- document.body.removeChild(this.ui.debugControlsContainer);
108
- this.ui.debugControlsContainer = null;
223
+ fromEvent(document.getElementById('time_scale_slider'), 'change')
224
+ .pipe(takeUntil(this.debugControlsRemoved$), takeUntil(this.viewUpdated$))
225
+ .subscribe(e => {
226
+ try {
227
+ this.currentWorld.worldClock.timeScale = +e.target.value;
228
+ }
229
+ catch (err) {
230
+ console.error(err);
231
+ }
232
+ });
233
+ fromEvent(document.getElementById('perf_stats_checkbox_id'), 'change')
234
+ .pipe(takeUntil(this.debugControlsRemoved$), takeUntil(this.viewUpdated$))
235
+ .subscribe(e => {
236
+ const entity = this.currentWorld.children.find(x => x instanceof PerformanceMeterEntity);
237
+ if (e.target.checked == !entity) {
238
+ if (entity) {
239
+ this.currentWorld.removeEntity(entity);
240
+ }
241
+ else {
242
+ this.currentWorld.addEntity(new PerformanceMeterEntity());
243
+ }
244
+ }
245
+ });
246
+ if (this.snapshot.performanceStatsEnabled) {
247
+ fromEvent(document.getElementById('per_mode'), 'change')
248
+ .pipe(takeUntil(this.debugControlsRemoved$), takeUntil(this.viewUpdated$))
249
+ .subscribe(e => {
250
+ this.perfStatsMode = e.target.value;
251
+ });
252
+ }
253
+ }
254
+ renderPerformanceStats() {
255
+ const pCss = "style='display:flex;align-items:center;margin:0.25rem;max-width:25rem'";
256
+ const em = document.getElementById('perf_stats_container');
257
+ if (!em)
258
+ return;
259
+ let html = '';
260
+ if (this.performanceStatsSnapshot) {
261
+ const total = this.performanceStatsSnapshot.totalTime;
262
+ const labelCss = `style='user-select:none;width:3.5rem;text-align:left;flex-shrink:0;'`;
263
+ const progressCss = `style='width:11.5rem;margin:4px;flex-shrink:0;'`;
264
+ const spanCss = `style='user-select:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;width:calc(9.5rem - 8px);'`;
265
+ html += `
266
+ <div ${pCss}>
267
+ <label for='stat_progress' ${labelCss}>${total.toFixed(2)}ms</label>
268
+ <progress id="stat_progress" ${progressCss} max="16" value="${total}"></progress>
269
+ <span ${spanCss}>Total frame time</span>
270
+ </div>`;
271
+ for (const [i, [name, value]] of this.performanceStatsSnapshot.entries.entries()) {
272
+ const strVal = this.perfStatsMode == 'AVG' ? ((value * 100) / total).toFixed(2) + '%' : value.toFixed(2) + 'ms';
273
+ html += `
274
+ <div ${pCss}>
275
+ <label for='stat_progress_${i}' ${labelCss}>${strVal}</label>
276
+ <progress id='stat_progress_${i}' ${progressCss} max='${total}' value='${value}'></progress>
277
+ <span ${spanCss}>${name}</span>
278
+ </div>`;
279
+ }
109
280
  }
281
+ em.innerHTML = html;
110
282
  }
111
283
  }
@@ -1,6 +1,5 @@
1
1
  import { GgWorld } from '../base';
2
2
  export declare class GgStatic {
3
- private static _instance;
4
3
  static get instance(): GgStatic;
5
4
  private readonly debuggerUI;
6
5
  private readonly consoleUI;
@@ -30,13 +30,13 @@ export class GgStatic {
30
30
  this.consoleCommands = new Map();
31
31
  this.registerConsoleCommand(null, 'ls_commands', () => __awaiter(this, void 0, void 0, function* () {
32
32
  return this.availableCommands
33
- .map(([key, value]) => `<span style="color:yellow">${key}</span>${value.doc ? '\t<span style="color:#aaa">// ' + value.doc + '</span>' : ''}`)
33
+ .map(([key, value]) => `<span style='color:yellow'>${key}</span>${value.doc ? '\t<span style="color:#aaa">// ' + value.doc + '</span>' : ''}`)
34
34
  .sort()
35
35
  .join('\n\n');
36
36
  }), 'no args; print all available commands');
37
37
  this.registerConsoleCommand(null, 'ls_worlds', () => __awaiter(this, void 0, void 0, function* () {
38
38
  return GgWorld.documentWorlds
39
- .map(w => (w === this.selectedWorld ? `<span style="color:green;">* ${w.name}</span>` : ` ${w.name}`))
39
+ .map(w => (w === this.selectedWorld ? `<span style='color:lightgreen;'>* ${w.name}</span>` : ` ${w.name}`))
40
40
  .join('\n');
41
41
  }), 'no args; print all available worlds');
42
42
  this.registerConsoleCommand(null, 'select_world', (...args) => __awaiter(this, void 0, void 0, function* () {
@@ -49,12 +49,20 @@ export class GgStatic {
49
49
  }
50
50
  return ((_a = this.selectedWorld) === null || _a === void 0 ? void 0 : _a.name) || 'null';
51
51
  }), 'args: [string]; select world by name');
52
+ this.registerConsoleCommand(null, 'show_debugger', (...args) => __awaiter(this, void 0, void 0, function* () {
53
+ this.showDebugControls = ['1', 'true', '+'].includes(args[0]);
54
+ return '' + this.showDebugControls;
55
+ }), 'args: [0 or 1]; turn on/off debug panel. Default value is 0');
56
+ this.registerConsoleCommand(null, 'show_stats', (...args) => __awaiter(this, void 0, void 0, function* () {
57
+ this.showStats = ['1', 'true', '+'].includes(args[0]);
58
+ return '' + this.showStats;
59
+ }), 'args: [0 or 1]; turn on/off stats. Default value is 0');
52
60
  }
53
61
  static get instance() {
54
- if (!GgStatic._instance) {
55
- GgStatic._instance = new GgStatic();
62
+ if (!window.ggstatic) {
63
+ window.ggstatic = new GgStatic();
56
64
  }
57
- return GgStatic._instance;
65
+ return window.ggstatic;
58
66
  }
59
67
  get devConsoleEnabled() {
60
68
  return this._devConsoleEnabled;
@@ -126,16 +134,15 @@ export class GgStatic {
126
134
  if (!action) {
127
135
  action = (this.consoleCommands.get(this.selectedWorld) || {})[command];
128
136
  if (!action) {
129
- return `<span style="color:red">Unrecognized command: ${command}</span>`;
137
+ return `<span style='color:red'>Unrecognized command: ${command}</span>`;
130
138
  }
131
139
  }
132
140
  try {
133
141
  return yield action.handler(...args);
134
142
  }
135
143
  catch (err) {
136
- return `<span style="color:red">${err}</span>`;
144
+ return `<span style='color:red'>${err}</span>`;
137
145
  }
138
146
  });
139
147
  }
140
148
  }
141
- window.ggstatic = GgStatic.instance;
@@ -1,3 +1,4 @@
1
1
  export * from './gg-console.ui';
2
2
  export * from './gg-debugger.ui';
3
3
  export * from './gg-static';
4
+ export * from './performance-meter.entity';
package/dist/dev/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './gg-console.ui';
2
2
  export * from './gg-debugger.ui';
3
3
  export * from './gg-static';
4
+ export * from './performance-meter.entity';
@@ -0,0 +1,21 @@
1
+ import { GgWorld, IEntity, TickOrder } from '../base';
2
+ export declare class PerformanceMeterEntity extends IEntity {
3
+ private readonly maxSamples;
4
+ private readonly maxRows;
5
+ readonly tickOrder: TickOrder | number;
6
+ private sampleIndex;
7
+ private collectedData;
8
+ private _avgReport;
9
+ get avgReport(): {
10
+ totalTime: number;
11
+ entries: [string, number][];
12
+ };
13
+ private _peakReport;
14
+ get peakReport(): {
15
+ totalTime: number;
16
+ entries: [string, number][];
17
+ };
18
+ constructor(maxSamples?: number, maxRows?: number);
19
+ onSpawned(world: GgWorld<unknown, unknown>): void;
20
+ onRemoved(): void;
21
+ }
@@ -0,0 +1,120 @@
1
+ import { IEntity } from '../base';
2
+ import { takeUntil } from 'rxjs';
3
+ export class PerformanceMeterEntity extends IEntity {
4
+ constructor(maxSamples = 60, maxRows = 15) {
5
+ super();
6
+ this.maxSamples = maxSamples;
7
+ this.maxRows = maxRows;
8
+ this.tickOrder = Number.MAX_SAFE_INTEGER;
9
+ this.sampleIndex = 0;
10
+ this.collectedData = new Map();
11
+ this._avgReport = { totalTime: 0, entries: [] };
12
+ this._peakReport = { totalTime: 0, entries: [] };
13
+ }
14
+ get avgReport() {
15
+ return this._avgReport;
16
+ }
17
+ get peakReport() {
18
+ return this._peakReport;
19
+ }
20
+ onSpawned(world) {
21
+ super.onSpawned(world);
22
+ this.sampleIndex = 0;
23
+ const now = () => (typeof performance === 'undefined' ? Date : performance).now();
24
+ this.world.tickForwardTo$.pipe(takeUntil(this.onRemoved$)).subscribe(e => {
25
+ if (e === this)
26
+ return;
27
+ if (!this.collectedData.has(e)) {
28
+ this.collectedData.set(e, [[this.sampleIndex, now(), 0]]);
29
+ }
30
+ else {
31
+ this.collectedData.get(e).push([this.sampleIndex, now(), 0]);
32
+ }
33
+ });
34
+ this.world.tickForwardedTo$.pipe(takeUntil(this.onRemoved$)).subscribe(e => {
35
+ if (e === this)
36
+ return;
37
+ const report = this.collectedData.get(e);
38
+ report[report.length - 1][2] = now();
39
+ });
40
+ this.tick$.pipe(takeUntil(this.onRemoved$)).subscribe(() => {
41
+ this.sampleIndex++;
42
+ // remove oldest samples
43
+ const entries = Array.from(this.collectedData.entries());
44
+ for (const [k, v] of entries) {
45
+ while (v.length > 0 && v[0][0] < this.sampleIndex - this.maxSamples) {
46
+ v.shift();
47
+ }
48
+ if (v.length === 0) {
49
+ this.collectedData.delete(k);
50
+ }
51
+ }
52
+ // helper functions
53
+ const label = (e) => {
54
+ if (e instanceof IEntity) {
55
+ if (e.name === '') {
56
+ return e.constructor.name;
57
+ }
58
+ else {
59
+ return e.name;
60
+ }
61
+ }
62
+ else if (e === 'PHYSICS_WORLD') {
63
+ return 'Physics simulation';
64
+ }
65
+ else {
66
+ return e;
67
+ }
68
+ };
69
+ const truncateReport = (report) => {
70
+ let restSum = 0;
71
+ let restAmount = 0;
72
+ for (restAmount; restAmount < report.length - 2; restAmount++) {
73
+ if (this.maxRows > report.length - restAmount + 1) {
74
+ restAmount--;
75
+ break;
76
+ }
77
+ restSum += report[report.length - restAmount - 1][1];
78
+ }
79
+ if (restAmount > 1) {
80
+ report = report.slice(0, report.length - restAmount);
81
+ report.push([`Rest (${restAmount})`, restSum]);
82
+ }
83
+ return report;
84
+ };
85
+ // build avg report
86
+ let totalTimeAvg = 0;
87
+ let avgReport = Array.from(this.collectedData.entries())
88
+ .map(([e, samples]) => {
89
+ const timeSpentAvg = samples.reduce((prev, [_, start, finish]) => prev + finish - start, 0) / this.maxSamples;
90
+ totalTimeAvg += timeSpentAvg;
91
+ return [label(e), timeSpentAvg];
92
+ })
93
+ .sort((a, b) => b[1] - a[1]);
94
+ avgReport = truncateReport(avgReport);
95
+ this._avgReport = { totalTime: totalTimeAvg, entries: avgReport };
96
+ // build peak report
97
+ let totalTimePeaks = new Map();
98
+ let peakReport = Array.from(this.collectedData.entries())
99
+ .map(([e, samples]) => {
100
+ let peakTime = 0;
101
+ for (const [sample, start, finish] of samples) {
102
+ const time = finish - start;
103
+ peakTime = Math.max(peakTime, time);
104
+ totalTimePeaks.set(sample, time + (totalTimePeaks.get(sample) || 0));
105
+ }
106
+ return [label(e), peakTime];
107
+ })
108
+ .sort((a, b) => b[1] - a[1]);
109
+ peakReport = truncateReport(peakReport);
110
+ let totalPeak = Array.from(totalTimePeaks.values()).reduce((prev, cur) => Math.max(prev, cur), 0);
111
+ this._peakReport = { totalTime: totalPeak, entries: peakReport };
112
+ });
113
+ }
114
+ onRemoved() {
115
+ super.onRemoved();
116
+ this.collectedData = new Map();
117
+ this._avgReport = { totalTime: 0, entries: [] };
118
+ this._peakReport = { totalTime: 0, entries: [] };
119
+ }
120
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gg-web-engine/core",
3
- "version": "0.0.38",
3
+ "version": "0.0.40",
4
4
  "description": "An attempt to create open source game engine for browser",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,8 +0,0 @@
1
- import { IDisplayObjectComponent } from '../components/rendering/i-display-object.component';
2
- import { Point3 } from '../models/points';
3
- import { VisualTypeDocRepo } from '../gg-world';
4
- export interface IDebugPhysicsDrawer<D, R, TypeDoc extends VisualTypeDocRepo<D, R> = VisualTypeDocRepo<D, R>> extends IDisplayObjectComponent<D, R, TypeDoc> {
5
- drawContactPoint(point: D, normal: D, color?: Point3): void;
6
- drawLine(from: D, to: D, color?: Point3): void;
7
- update(): void;
8
- }
@@ -1 +0,0 @@
1
- export {};