@gg-web-engine/core 0.0.38 → 0.0.39

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 (34) 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/models/body-options.d.ts +5 -1
  4. package/dist/3d/components/physics/i-physics-world-3d.component.d.ts +7 -0
  5. package/dist/3d/components/physics/i-rigid-body-3d.component.d.ts +3 -0
  6. package/dist/3d/components/physics/i-trigger-3d.component.d.ts +3 -0
  7. package/dist/3d/entities/controllers/input/car-keyboard-handling.controller.js +2 -2
  8. package/dist/3d/entities/controllers/input/gg-car-keyboard-handling.controller.js +3 -3
  9. package/dist/3d/factories.d.ts +3 -3
  10. package/dist/3d/models/body-options.d.ts +5 -1
  11. package/dist/3d/models/shapes.d.ts +41 -1
  12. package/dist/base/clock/pausable-clock.d.ts +56 -1
  13. package/dist/base/clock/pausable-clock.js +88 -5
  14. package/dist/base/components/physics/i-body.component.d.ts +3 -1
  15. package/dist/base/components/physics/i-physics-world.component.d.ts +8 -6
  16. package/dist/base/components/rendering/i-renderer.component.d.ts +4 -0
  17. package/dist/base/components/rendering/i-visual-scene.component.d.ts +0 -4
  18. package/dist/base/entities/i-entity.d.ts +1 -1
  19. package/dist/base/entities/i-entity.js +3 -3
  20. package/dist/base/entities/i-renderer.entity.d.ts +4 -0
  21. package/dist/base/entities/i-renderer.entity.js +8 -0
  22. package/dist/base/gg-world.d.ts +0 -2
  23. package/dist/base/gg-world.js +26 -25
  24. package/dist/base/index.d.ts +0 -1
  25. package/dist/base/index.js +0 -1
  26. package/dist/base/models/body-options.d.ts +9 -0
  27. package/dist/dev/gg-console.ui.js +16 -8
  28. package/dist/dev/gg-debugger.ui.d.ts +4 -0
  29. package/dist/dev/gg-debugger.ui.js +83 -32
  30. package/dist/dev/gg-static.d.ts +0 -1
  31. package/dist/dev/gg-static.js +3 -4
  32. package/package.json +1 -1
  33. package/dist/base/interfaces/i-debug-physics-drawer.d.ts +0 -8
  34. package/dist/base/interfaces/i-debug-physics-drawer.js +0 -1
@@ -1,5 +1,8 @@
1
1
  import { IRigidBodyComponent, Point2 } from '../../../base';
2
2
  import { PhysicsTypeDocRepo2D } from '../../gg-2d-world';
3
+ import { DebugBody2DSettings } from '../../models/body-options';
3
4
  export interface IRigidBody2dComponent<TypeDoc extends PhysicsTypeDocRepo2D = PhysicsTypeDocRepo2D> extends IRigidBodyComponent<Point2, number, TypeDoc> {
4
5
  angularVelocity: number;
6
+ /** body info for physics debugger view */
7
+ readonly debugBodySettings: DebugBody2DSettings;
5
8
  }
@@ -1,7 +1,10 @@
1
1
  import { ITriggerComponent, Point2 } from '../../../base';
2
2
  import { Observable } from 'rxjs';
3
3
  import { PhysicsTypeDocRepo2D } from '../../gg-2d-world';
4
+ import { DebugBody2DSettings } from '../../models/body-options';
4
5
  export interface ITrigger2dComponent<TypeDoc extends PhysicsTypeDocRepo2D = PhysicsTypeDocRepo2D> extends ITriggerComponent<Point2, number, TypeDoc> {
6
+ /** body info for physics debugger view */
7
+ readonly debugBodySettings: DebugBody2DSettings;
5
8
  get onEntityEntered(): Observable<TypeDoc['rigidBody']>;
6
9
  get onEntityLeft(): Observable<TypeDoc['rigidBody'] | null>;
7
10
  }
@@ -1,3 +1,7 @@
1
- import { BodyOptions } from '../../base';
1
+ import { BodyOptions, DebugBodySettings } from '../../base';
2
+ import { Shape2DDescriptor } from './shapes';
2
3
  export interface Body2DOptions extends BodyOptions {
3
4
  }
5
+ export declare type DebugBody2DSettings = DebugBodySettings & {
6
+ shape: Shape2DDescriptor;
7
+ };
@@ -1,5 +1,12 @@
1
1
  import { IPhysicsWorldComponent, Point3, Point4 } from '../../../base';
2
2
  import { PhysicsTypeDocRepo3D } from '../../gg-3d-world';
3
+ import { Subject } from 'rxjs';
3
4
  export interface IPhysicsWorld3dComponent<TypeDoc extends PhysicsTypeDocRepo3D = PhysicsTypeDocRepo3D> extends IPhysicsWorldComponent<Point3, Point4, TypeDoc> {
4
5
  readonly loader: TypeDoc['loader'];
6
+ /** event emitter, emits newly added physics components */
7
+ readonly added$: Subject<TypeDoc['trigger'] | TypeDoc['rigidBody'] | TypeDoc['raycastVehicle'] | any>;
8
+ /** event emitter, emits just removed physics components */
9
+ readonly removed$: Subject<TypeDoc['trigger'] | TypeDoc['rigidBody'] | TypeDoc['raycastVehicle'] | any>;
10
+ /** list of currently added to world physics components */
11
+ readonly children: (TypeDoc['trigger'] | TypeDoc['rigidBody'] | TypeDoc['raycastVehicle'] | any)[];
5
12
  }
@@ -1,5 +1,8 @@
1
1
  import { IRigidBodyComponent, Point3, Point4 } from '../../../base';
2
2
  import { PhysicsTypeDocRepo3D } from '../../gg-3d-world';
3
+ import { DebugBody3DSettings } from '../../models/body-options';
3
4
  export interface IRigidBody3dComponent<TypeDoc extends PhysicsTypeDocRepo3D = PhysicsTypeDocRepo3D> extends IRigidBodyComponent<Point3, Point4, TypeDoc> {
4
5
  angularVelocity: Point3;
6
+ /** body info for physics debugger view */
7
+ readonly debugBodySettings: DebugBody3DSettings;
5
8
  }
@@ -1,7 +1,10 @@
1
1
  import { ITriggerComponent, Point3, Point4 } from '../../../base';
2
2
  import { Observable } from 'rxjs';
3
3
  import { PhysicsTypeDocRepo3D } from '../../gg-3d-world';
4
+ import { DebugBody3DSettings } from '../../models/body-options';
4
5
  export interface ITrigger3dComponent<TypeDoc extends PhysicsTypeDocRepo3D = PhysicsTypeDocRepo3D> extends ITriggerComponent<Point3, Point4, TypeDoc> {
6
+ /** body info for physics debugger view */
7
+ readonly debugBodySettings: DebugBody3DSettings;
5
8
  get onEntityEntered(): Observable<TypeDoc['rigidBody']>;
6
9
  get onEntityLeft(): Observable<TypeDoc['rigidBody'] | null>;
7
10
  }
@@ -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 { combineLatest, Subject, takeUntil } from 'rxjs';
10
+ import { combineLatest, filter, Subject, takeUntil } from 'rxjs';
11
11
  import { DirectionKeyboardInput, IEntity, TickOrder, } from '../../../../base';
12
12
  export class CarKeyboardHandlingController extends IEntity {
13
13
  constructor(keyboard, options = {
@@ -32,7 +32,7 @@ export class CarKeyboardHandlingController extends IEntity {
32
32
  _super.onSpawned.call(this, world);
33
33
  let input = { upDown: 0, leftRight: 0 };
34
34
  combineLatest([this.directionsInput.output$, this.tick$])
35
- .pipe(takeUntil(this._onRemoved$))
35
+ .pipe(filter(() => this.active), takeUntil(this._onRemoved$))
36
36
  .subscribe(([d, [_, dt]]) => {
37
37
  const direction = { upDown: 0, leftRight: 0 };
38
38
  if (d.leftRight !== undefined)
@@ -64,7 +64,7 @@ export class GgCarKeyboardHandlingController extends IEntity {
64
64
  });
65
65
  this.keyboard
66
66
  .bind(this.options.gearUpDownKeys[0])
67
- .pipe(takeUntil(this._onRemoved$), filter(x => this.switchingGearsEnabled && !!x))
67
+ .pipe(takeUntil(this._onRemoved$), filter(x => this.active && this.switchingGearsEnabled && !!x))
68
68
  .subscribe(() => {
69
69
  if (this.car && (!this.car.carProperties.transmission.isAuto || this.car.gear <= 0)) {
70
70
  this.car.gear++;
@@ -72,7 +72,7 @@ export class GgCarKeyboardHandlingController extends IEntity {
72
72
  });
73
73
  this.keyboard
74
74
  .bind(this.options.gearUpDownKeys[1])
75
- .pipe(takeUntil(this._onRemoved$), filter(x => this.switchingGearsEnabled && !!x))
75
+ .pipe(takeUntil(this._onRemoved$), filter(x => this.active && this.switchingGearsEnabled && !!x))
76
76
  .subscribe(() => {
77
77
  if (this.car) {
78
78
  if (this.car.carProperties.transmission.isAuto && this.car.gear > 1) {
@@ -85,7 +85,7 @@ export class GgCarKeyboardHandlingController extends IEntity {
85
85
  });
86
86
  this.keyboard
87
87
  .bind(this.options.handbrakeKey)
88
- .pipe(takeUntil(this._onRemoved$))
88
+ .pipe(takeUntil(this._onRemoved$), filter(() => this.active))
89
89
  .subscribe(isKeyDown => {
90
90
  if (this.car) {
91
91
  this.car.handBrake = isKeyDown;
@@ -1,15 +1,15 @@
1
- import { BodyShape3DDescriptor, Shape3DDescriptor } from './models/shapes';
1
+ import { BodyShape3DDescriptor, Shape3DDescriptor, Shape3DMeshDescriptor } from './models/shapes';
2
2
  import { Point3, Point4 } from '../base';
3
3
  import { PhysicsTypeDocRepo3D, VisualTypeDocRepo3D } from './gg-3d-world';
4
4
  export declare type DisplayObject3dOpts<Tex> = {
5
5
  color?: number;
6
- shading?: 'unlit' | 'standart' | 'phong';
6
+ shading?: 'unlit' | 'standart' | 'phong' | 'wireframe';
7
7
  diffuse?: Tex;
8
8
  castShadow?: boolean;
9
9
  receiveShadow?: boolean;
10
10
  };
11
11
  export declare abstract class IDisplayObject3dComponentFactory<TypeDoc extends VisualTypeDocRepo3D = VisualTypeDocRepo3D> {
12
- abstract createPrimitive(descriptor: Shape3DDescriptor, material?: DisplayObject3dOpts<TypeDoc['texture']>): TypeDoc['displayObject'];
12
+ abstract createPrimitive(descriptor: Shape3DMeshDescriptor, material?: DisplayObject3dOpts<TypeDoc['texture']>): TypeDoc['displayObject'];
13
13
  abstract createPerspectiveCamera(settings: {
14
14
  fov?: number;
15
15
  aspectRatio?: number;
@@ -1,3 +1,7 @@
1
- import { BodyOptions } from '../../base';
1
+ import { BodyOptions, DebugBodySettings } from '../../base';
2
+ import { Shape3DDescriptor } from './shapes';
2
3
  export interface Body3DOptions extends BodyOptions {
3
4
  }
5
+ export declare type DebugBody3DSettings = DebugBodySettings & {
6
+ shape: Shape3DDescriptor;
7
+ };
@@ -1,4 +1,4 @@
1
- import { Point3, Point4 } from '../../base';
1
+ import { Point2, Point3, Point4 } from '../../base';
2
2
  import { Body3DOptions } from './body-options';
3
3
  export declare type Shape3DDescriptor = {
4
4
  shape: 'PLANE';
@@ -31,6 +31,46 @@ export declare type Shape3DDescriptor = {
31
31
  vertices: Point3[];
32
32
  faces: [number, number, number][];
33
33
  };
34
+ export declare type Shape3DMeshDescriptor = {
35
+ shape: 'PLANE';
36
+ dimensions?: Point2;
37
+ segments?: Point2;
38
+ } | {
39
+ shape: 'BOX';
40
+ dimensions: Point3;
41
+ segments?: Point3;
42
+ } | {
43
+ shape: 'CONE' | 'CYLINDER';
44
+ radius: number;
45
+ height: number;
46
+ radialSegments?: number;
47
+ heightSegments?: number;
48
+ } | {
49
+ shape: 'CAPSULE';
50
+ radius: number;
51
+ centersDistance: number;
52
+ capSegments?: number;
53
+ radialSegments?: number;
54
+ } | {
55
+ shape: 'SPHERE';
56
+ radius: number;
57
+ widthSegments?: number;
58
+ heightSegments?: number;
59
+ } | {
60
+ shape: 'COMPOUND';
61
+ children: {
62
+ position?: Point3;
63
+ rotation?: Point4;
64
+ shape: Shape3DMeshDescriptor;
65
+ }[];
66
+ } | {
67
+ shape: 'CONVEX_HULL';
68
+ vertices: Point3[];
69
+ } | {
70
+ shape: 'MESH';
71
+ vertices: Point3[];
72
+ faces: [number, number, number][];
73
+ };
34
74
  export declare type BodyShape3DDescriptor = {
35
75
  shape: Shape3DDescriptor;
36
76
  body: Partial<Body3DOptions>;
@@ -1,25 +1,80 @@
1
1
  import { Observable } from 'rxjs';
2
2
  import { IClock } from './i-clock';
3
3
  /**
4
- * A class, providing ability to track time, fire ticks, provide time elapsed + tick delta with ability to suspend/resume it.
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
6
  export declare class PausableClock implements IClock {
7
7
  protected readonly parentClock: IClock;
8
8
  private tickSub;
9
9
  private readonly _tick$;
10
+ /**
11
+ * Observable stream of ticks, emitting an array containing the current time and the tick delta.
12
+ */
10
13
  get tick$(): Observable<[number, number]>;
14
+ /**
15
+ * Checks if the clock is currently running.
16
+ */
11
17
  get isRunning(): boolean;
18
+ /**
19
+ * Checks if the clock is currently paused.
20
+ */
12
21
  get isPaused(): boolean;
22
+ /**
23
+ * Checks if the clock is stopped.
24
+ */
25
+ get isStopped(): boolean;
26
+ /**
27
+ * Gets the time scale of the clock.
28
+ */
29
+ get timeScale(): number;
30
+ /**
31
+ * Sets the time scale of the clock.
32
+ */
33
+ set timeScale(value: number);
34
+ /**
35
+ * Gets the elapsed time since the clock started.
36
+ */
13
37
  get elapsedTime(): number;
14
38
  private startedAt;
15
39
  private oldRelativeTime;
16
40
  private pausedAt;
41
+ private lastStopElapsed;
42
+ private _timeScale;
43
+ private pausedByTimescale;
44
+ /**
45
+ * Constructs a new PausableClock instance.
46
+ * @param autoStart Indicates whether the clock should start automatically upon creation.
47
+ * @param parentClock The parent clock to synchronize with. Defaults to GgGlobalClock instance.
48
+ */
17
49
  constructor(autoStart?: boolean, parentClock?: IClock);
50
+ /**
51
+ * Creates a child clock.
52
+ * @param autoStart Indicates whether the child clock should start automatically.
53
+ * @returns A new instance of PausableClock.
54
+ */
18
55
  createChildClock(autoStart: boolean): PausableClock;
56
+ /**
57
+ * Starts the clock.
58
+ */
19
59
  start(): void;
60
+ /**
61
+ * Stops the clock.
62
+ */
20
63
  stop(): void;
64
+ /**
65
+ * Pauses the clock.
66
+ */
21
67
  pause(): void;
68
+ /**
69
+ * Resumes the clock.
70
+ */
22
71
  resume(): void;
72
+ /**
73
+ * Starts listening for ticks from the parent clock.
74
+ */
23
75
  protected startListeningTicks(): void;
76
+ /**
77
+ * Stops listening for ticks from the parent clock.
78
+ */
24
79
  protected stopListeningTicks(): void;
25
80
  }
@@ -2,39 +2,105 @@ import { Subject } from 'rxjs';
2
2
  import { map, tap } from 'rxjs/operators';
3
3
  import { GgGlobalClock } from './global-clock';
4
4
  /**
5
- * A class, providing ability to track time, fire ticks, provide time elapsed + tick delta with ability to suspend/resume it.
5
+ * A class providing the ability to track time, fire ticks, provide time elapsed, and tick delta with the ability to suspend/resume it.
6
6
  */
7
7
  export class PausableClock {
8
+ /**
9
+ * Constructs a new PausableClock instance.
10
+ * @param autoStart Indicates whether the clock should start automatically upon creation.
11
+ * @param parentClock The parent clock to synchronize with. Defaults to GgGlobalClock instance.
12
+ */
8
13
  constructor(autoStart = false, parentClock = GgGlobalClock.instance) {
9
14
  this.parentClock = parentClock;
10
15
  this.tickSub = null;
11
16
  this._tick$ = new Subject();
12
- // state
17
+ // State variables
13
18
  this.startedAt = -1;
14
19
  this.oldRelativeTime = 0; // "elapsed", emitted on last tick
15
20
  this.pausedAt = -1;
21
+ this.lastStopElapsed = 0;
22
+ this._timeScale = 1;
23
+ this.pausedByTimescale = false;
16
24
  if (autoStart) {
17
25
  this.start();
18
26
  }
19
27
  }
28
+ /**
29
+ * Observable stream of ticks, emitting an array containing the current time and the tick delta.
30
+ */
20
31
  get tick$() {
21
32
  return this._tick$.pipe(map(([oldTime, newTime]) => [newTime, newTime - oldTime]));
22
33
  }
34
+ /**
35
+ * Checks if the clock is currently running.
36
+ */
23
37
  get isRunning() {
24
38
  return !!this.tickSub;
25
39
  }
40
+ /**
41
+ * Checks if the clock is currently paused.
42
+ */
26
43
  get isPaused() {
27
44
  return this.pausedAt !== -1;
28
45
  }
46
+ /**
47
+ * Checks if the clock is stopped.
48
+ */
49
+ get isStopped() {
50
+ return this.startedAt === -1;
51
+ }
52
+ /**
53
+ * Gets the time scale of the clock.
54
+ */
55
+ get timeScale() {
56
+ return this._timeScale;
57
+ }
58
+ /**
59
+ * Sets the time scale of the clock.
60
+ */
61
+ set timeScale(value) {
62
+ if (value === this._timeScale && !(this.pausedByTimescale && value !== 0))
63
+ return;
64
+ if (value === 0) {
65
+ if (!this.isPaused) {
66
+ this.pause();
67
+ this.pausedByTimescale = true;
68
+ }
69
+ return;
70
+ }
71
+ if (this.isPaused && this.pausedByTimescale) {
72
+ this.resume();
73
+ this.pausedByTimescale = false;
74
+ }
75
+ if (!this.isStopped) {
76
+ const cur = this.isPaused ? this.pausedAt : this.parentClock.elapsedTime;
77
+ this.startedAt = cur - ((cur - this.startedAt) * this.timeScale) / value;
78
+ }
79
+ this._timeScale = value;
80
+ }
81
+ /**
82
+ * Gets the elapsed time since the clock started.
83
+ */
29
84
  get elapsedTime() {
85
+ if (this.isStopped) {
86
+ return this.lastStopElapsed;
87
+ }
30
88
  if (this.isPaused) {
31
- return this.pausedAt - this.startedAt;
89
+ return this._timeScale * (this.pausedAt - this.startedAt);
32
90
  }
33
- return this.parentClock.elapsedTime - this.startedAt;
91
+ return this._timeScale * (this.parentClock.elapsedTime - this.startedAt);
34
92
  }
93
+ /**
94
+ * Creates a child clock.
95
+ * @param autoStart Indicates whether the child clock should start automatically.
96
+ * @returns A new instance of PausableClock.
97
+ */
35
98
  createChildClock(autoStart) {
36
99
  return new PausableClock(autoStart, this);
37
100
  }
101
+ /**
102
+ * Starts the clock.
103
+ */
38
104
  start() {
39
105
  if (this.isRunning) {
40
106
  return;
@@ -43,14 +109,25 @@ export class PausableClock {
43
109
  this.startedAt = this.parentClock.elapsedTime;
44
110
  this.startListeningTicks();
45
111
  }
112
+ /**
113
+ * Stops the clock.
114
+ */
46
115
  stop() {
47
116
  this.stopListeningTicks();
117
+ this.lastStopElapsed = this.elapsedTime;
48
118
  this.startedAt = this.pausedAt = -1;
49
119
  }
120
+ /**
121
+ * Pauses the clock.
122
+ */
50
123
  pause() {
51
124
  this.stopListeningTicks();
52
125
  this.pausedAt = this.parentClock.elapsedTime;
126
+ this.pausedByTimescale = false;
53
127
  }
128
+ /**
129
+ * Resumes the clock.
130
+ */
54
131
  resume() {
55
132
  if (this.isRunning || this.pausedAt == -1) {
56
133
  return;
@@ -59,14 +136,20 @@ export class PausableClock {
59
136
  this.pausedAt = -1;
60
137
  this.startListeningTicks();
61
138
  }
139
+ /**
140
+ * Starts listening for ticks from the parent clock.
141
+ */
62
142
  startListeningTicks() {
63
143
  if (this.tickSub) {
64
144
  throw new Error('Clock is already ticking!');
65
145
  }
66
146
  this.tickSub = this.parentClock.tick$
67
- .pipe(map(([parentElapsed, _]) => [this.oldRelativeTime, parentElapsed - this.startedAt]), tap(([_, cur]) => (this.oldRelativeTime = cur)))
147
+ .pipe(map(([_, d]) => [this.oldRelativeTime, this.oldRelativeTime + d * this.timeScale]), tap(([_, cur]) => (this.oldRelativeTime = cur)))
68
148
  .subscribe(this._tick$);
69
149
  }
150
+ /**
151
+ * Stops listening for ticks from the parent clock.
152
+ */
70
153
  stopListeningTicks() {
71
154
  var _a;
72
155
  (_a = this.tickSub) === null || _a === void 0 ? void 0 : _a.unsubscribe();
@@ -1,7 +1,7 @@
1
1
  import { IEntity } from '../../entities/i-entity';
2
2
  import { IWorldComponent } from '../i-world-component';
3
3
  import { GgWorld, PhysicsTypeDocRepo, VisualTypeDocRepo } from '../../gg-world';
4
- import { CollisionGroup } from '../../models/body-options';
4
+ import { CollisionGroup, DebugBodySettings } from '../../models/body-options';
5
5
  export interface IBodyComponent<D, R, TypeDoc extends PhysicsTypeDocRepo<D, R> = PhysicsTypeDocRepo<D, R>> extends IWorldComponent<D, R, VisualTypeDocRepo<D, R>, TypeDoc> {
6
6
  entity: IEntity | null;
7
7
  position: D;
@@ -11,6 +11,8 @@ export interface IBodyComponent<D, R, TypeDoc extends PhysicsTypeDocRepo<D, R> =
11
11
  set ownCollisionGroups(value: CollisionGroup[] | 'all');
12
12
  get interactWithCollisionGroups(): CollisionGroup[];
13
13
  set interactWithCollisionGroups(value: CollisionGroup[] | 'all');
14
+ /** body info for physics debugger view */
15
+ readonly debugBodySettings: DebugBodySettings;
14
16
  clone(): IBodyComponent<D, R, TypeDoc>;
15
17
  addToWorld(world: GgWorld<D, R, VisualTypeDocRepo<D, R>, TypeDoc>): void;
16
18
  removeFromWorld(world: GgWorld<D, R, VisualTypeDocRepo<D, R>, TypeDoc>): void;
@@ -1,12 +1,16 @@
1
- import { GgWorld, PhysicsTypeDocRepo } from '../../gg-world';
2
- import { IDebugPhysicsDrawer } from '../../interfaces/i-debug-physics-drawer';
1
+ import { PhysicsTypeDocRepo } from '../../gg-world';
3
2
  import { IComponent } from '../i-component';
4
3
  import { CollisionGroup } from '../../models/body-options';
4
+ import { Subject } from 'rxjs';
5
5
  export interface IPhysicsWorldComponent<D, R, TypeDoc extends PhysicsTypeDocRepo<D, R> = PhysicsTypeDocRepo<D, R>> extends IComponent {
6
6
  readonly factory: TypeDoc['factory'];
7
7
  gravity: D;
8
- timeScale: number;
9
- get physicsDebugViewActive(): boolean;
8
+ /** event emitter, emits newly added physics components */
9
+ readonly added$: Subject<TypeDoc['rigidBody'] | TypeDoc['trigger'] | any>;
10
+ /** event emitter, emits just removed physics components */
11
+ readonly removed$: Subject<TypeDoc['rigidBody'] | TypeDoc['trigger'] | any>;
12
+ /** list of currently added to world physics components */
13
+ readonly children: (TypeDoc['rigidBody'] | TypeDoc['trigger'] | any)[];
10
14
  init(): Promise<void>;
11
15
  /**
12
16
  * Runs simulation of the physics world.
@@ -16,6 +20,4 @@ export interface IPhysicsWorldComponent<D, R, TypeDoc extends PhysicsTypeDocRepo
16
20
  simulate(delta: number): void;
17
21
  registerCollisionGroup(): CollisionGroup;
18
22
  deregisterCollisionGroup(group: CollisionGroup): void;
19
- startDebugger(world: GgWorld<D, R>, drawer: IDebugPhysicsDrawer<D, R>): void;
20
- stopDebugger(world: GgWorld<D, R>): void;
21
23
  }
@@ -26,6 +26,10 @@ export declare abstract class IRendererComponent<D, R, VTypeDoc extends VisualTy
26
26
  entity: IEntity | null;
27
27
  /** Specifies the options for the renderer. */
28
28
  readonly rendererOptions: RendererOptions;
29
+ /** get flag whether renderer shows physics debugger view */
30
+ abstract get physicsDebugViewActive(): boolean;
31
+ /** turns on/off physics debugger view for this renderer */
32
+ abstract set physicsDebugViewActive(value: boolean);
29
33
  protected constructor(scene: IVisualSceneComponent<D, R, VTypeDoc>, canvas?: HTMLCanvasElement | undefined, options?: Partial<RendererOptions>);
30
34
  /**
31
35
  * Renders the scene.
@@ -1,10 +1,6 @@
1
- import { IDebugPhysicsDrawer } from '../../interfaces/i-debug-physics-drawer';
2
1
  import { IComponent } from '../i-component';
3
2
  import { VisualTypeDocRepo } from '../../gg-world';
4
3
  export interface IVisualSceneComponent<D, R, TypeDoc extends VisualTypeDocRepo<D, R> = VisualTypeDocRepo<D, R>> extends IComponent {
5
4
  readonly factory: TypeDoc['factory'];
6
- readonly debugPhysicsDrawerClass?: {
7
- new (): IDebugPhysicsDrawer<D, R, TypeDoc>;
8
- };
9
5
  init(): Promise<void>;
10
6
  }
@@ -34,7 +34,7 @@ export declare abstract class IEntity<D = any, R = any, VTypeDoc extends VisualT
34
34
  /**
35
35
  * The flag whether entity should listen to ticks. If set to false, ticks will not be propagated to this entity
36
36
  * */
37
- protected _active: boolean;
37
+ protected _selfActive: boolean;
38
38
  get active(): boolean;
39
39
  set active(value: boolean);
40
40
  parent: IEntity | null;
@@ -26,7 +26,7 @@ export class IEntity {
26
26
  /**
27
27
  * The flag whether entity should listen to ticks. If set to false, ticks will not be propagated to this entity
28
28
  * */
29
- this._active = true;
29
+ this._selfActive = true;
30
30
  this.parent = null;
31
31
  this._children = [];
32
32
  this._components = [];
@@ -43,10 +43,10 @@ export class IEntity {
43
43
  this._name = value;
44
44
  }
45
45
  get active() {
46
- return this._active;
46
+ return this._selfActive && (!this.parent || this.parent.active);
47
47
  }
48
48
  set active(value) {
49
- this._active = value;
49
+ this._selfActive = value;
50
50
  }
51
51
  get children() {
52
52
  return [...this._children];
@@ -23,6 +23,10 @@ export declare abstract class IRendererEntity<D, R, TypeDoc extends VisualTypeDo
23
23
  */
24
24
  get rendererSize(): Point2 | null;
25
25
  get rendererOptions(): RendererOptions;
26
+ /** get flag whether renderer shows physics debugger view */
27
+ get physicsDebugViewActive(): boolean;
28
+ /** turns on/off physics debugger view for this renderer */
29
+ set physicsDebugViewActive(value: boolean);
26
30
  /**
27
31
  Initializes a new instance of the BaseGgRenderer class.
28
32
  * @param renderer
@@ -38,6 +38,14 @@ export class IRendererEntity extends IEntity {
38
38
  get rendererOptions() {
39
39
  return this.renderer.rendererOptions;
40
40
  }
41
+ /** get flag whether renderer shows physics debugger view */
42
+ get physicsDebugViewActive() {
43
+ return this.renderer.physicsDebugViewActive;
44
+ }
45
+ /** turns on/off physics debugger view for this renderer */
46
+ set physicsDebugViewActive(value) {
47
+ this.renderer.physicsDebugViewActive = value;
48
+ }
41
49
  onSpawned(world) {
42
50
  this._rendererSize$.next(null);
43
51
  if (this.rendererOptions.size == 'fullscreen' || typeof this.rendererOptions.size === 'function') {
@@ -34,6 +34,4 @@ export declare abstract class GgWorld<D, R, VTypeDoc extends VisualTypeDocRepo<D
34
34
  position?: D, rotation?: R, material?: unknown): IPositionable<D, R> & IRenderableEntity<D, R, VTypeDoc>;
35
35
  addEntity(entity: IEntity): void;
36
36
  removeEntity(entity: IEntity, dispose?: boolean): void;
37
- get physicsDebugViewActive(): boolean;
38
- set physicsDebugViewActive(value: boolean);
39
37
  }
@@ -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 { GgGlobalClock, KeyboardInput, TickOrder, } from '../base';
10
+ import { GgGlobalClock, IRendererEntity, KeyboardInput, TickOrder, } from '../base';
11
11
  export class GgWorld {
12
12
  constructor(visualScene, physicsWorld) {
13
13
  this.visualScene = visualScene;
@@ -30,13 +30,32 @@ export class GgWorld {
30
30
  return '' + window.ggstatic.showStats;
31
31
  }), 'args: [0 or 1]; turn on/off stats. Default value is 0');
32
32
  window.ggstatic.registerConsoleCommand(this, 'ph_timescale', (...args) => __awaiter(this, void 0, void 0, function* () {
33
- this.physicsWorld.timeScale = +args[0];
34
- return JSON.stringify(this.physicsWorld.timeScale);
33
+ this.worldClock.timeScale = +args[0];
34
+ return JSON.stringify(this.worldClock.timeScale);
35
35
  }), 'args: [float]; change time scale of physics engine. Default value is 1.0');
36
+ window.ggstatic.registerConsoleCommand(this, 'ls_renderers', () => __awaiter(this, void 0, void 0, function* () {
37
+ return this.children
38
+ .filter(e => e instanceof IRendererEntity)
39
+ .map(r => r.name)
40
+ .join('\n');
41
+ }), 'no args; print all renderers in selected world');
36
42
  window.ggstatic.registerConsoleCommand(this, 'dr_drawphysics', (...args) => __awaiter(this, void 0, void 0, function* () {
37
- this.physicsDebugViewActive = ['1', 'true', '+'].includes(args[0]);
38
- return '' + this.physicsWorld.physicsDebugViewActive;
39
- }), 'args: [0 or 1]; turn on/off physics debug view. Default value is 0');
43
+ const value = ['1', 'true', '+'].includes(args[0]);
44
+ const rendererName = args[1];
45
+ let renderer;
46
+ if (rendererName) {
47
+ renderer = this.children.find(x => x instanceof IRendererEntity && x.name === rendererName);
48
+ }
49
+ else {
50
+ renderer = this.children.find(x => x instanceof IRendererEntity);
51
+ }
52
+ if (renderer) {
53
+ renderer.physicsDebugViewActive = value;
54
+ return '' + value;
55
+ }
56
+ return 'false';
57
+ }), 'args: [0 or 1] or [0 or 1, string]; turn on/off physics debug view. Second argument expects renderer ' +
58
+ 'name, if not provided first renderer will be picked. Look up for renderer names using command "ls_renderers"');
40
59
  }
41
60
  }
42
61
  static get documentWorlds() {
@@ -56,7 +75,7 @@ export class GgWorld {
56
75
  this.tickListeners[i].tick$.next([elapsed, delta]);
57
76
  }
58
77
  }
59
- // run phycics simulation
78
+ // run physics simulation
60
79
  this.physicsWorld.simulate(delta);
61
80
  // emit tick to all remained entities
62
81
  for (i; i < this.tickListeners.length; i++) {
@@ -126,24 +145,6 @@ export class GgWorld {
126
145
  entity.dispose();
127
146
  }
128
147
  }
129
- get physicsDebugViewActive() {
130
- return this.physicsWorld.physicsDebugViewActive;
131
- }
132
- set physicsDebugViewActive(value) {
133
- if (this.physicsDebugViewActive === value) {
134
- return;
135
- }
136
- if (value) {
137
- const cls = this.visualScene.debugPhysicsDrawerClass;
138
- if (!cls) {
139
- throw new Error('Debug drawer is not available');
140
- }
141
- this.physicsWorld.startDebugger(this, new cls());
142
- }
143
- else {
144
- this.physicsWorld.stopDebugger(this);
145
- }
146
- }
147
148
  }
148
149
  GgWorld.default_name_counter = 0;
149
150
  GgWorld._documentWorlds = [];
@@ -21,7 +21,6 @@ export * from './inputs/direction.keyboard.input';
21
21
  export * from './inputs/i-input';
22
22
  export * from './inputs/keyboard.input';
23
23
  export * from './inputs/mouse.input';
24
- export * from './interfaces/i-debug-physics-drawer';
25
24
  export * from './interfaces/i-positionable';
26
25
  export * from './models/axis-directions';
27
26
  export * from './models/body-options';
@@ -21,7 +21,6 @@ export * from './inputs/direction.keyboard.input';
21
21
  export * from './inputs/i-input';
22
22
  export * from './inputs/keyboard.input';
23
23
  export * from './inputs/mouse.input';
24
- export * from './interfaces/i-debug-physics-drawer';
25
24
  export * from './interfaces/i-positionable';
26
25
  export * from './models/axis-directions';
27
26
  export * from './models/body-options';
@@ -7,3 +7,12 @@ export interface BodyOptions {
7
7
  ownCollisionGroups: CollisionGroup[] | 'all';
8
8
  interactWithCollisionGroups: CollisionGroup[] | 'all';
9
9
  }
10
+ export declare type DebugBodySettings = {
11
+ shape: any;
12
+ ignoreTransform?: boolean;
13
+ } & ({
14
+ type: 'RIGID_STATIC' | 'TRIGGER';
15
+ } | {
16
+ type: 'RIGID_DYNAMIC';
17
+ sleeping: boolean;
18
+ });
@@ -67,31 +67,39 @@ List of available commands: `.replace(/ /g, '&nbsp;') + `<span style="color:yell
67
67
  };
68
68
  document.getElementById('gg-console-close-icon').onmousedown = () => this.destroyUI();
69
69
  this.elements.input.onkeydown = event => {
70
- if ((event === null || event === void 0 ? void 0 : event.keyCode) === 13) {
70
+ var _a;
71
+ if ((event === null || event === void 0 ? void 0 : event.code) === 'Enter') {
71
72
  event.preventDefault();
72
73
  this.onInput().then();
73
74
  }
74
- else if ((event === null || event === void 0 ? void 0 : event.keyCode) === 38) {
75
+ else if ((event === null || event === void 0 ? void 0 : event.code) === 'ArrowUp') {
75
76
  event.preventDefault();
76
77
  this.onUsePreviousCommand();
77
78
  }
78
- else if ((event === null || event === void 0 ? void 0 : event.keyCode) === 40) {
79
+ else if ((event === null || event === void 0 ? void 0 : event.code) === 'ArrowDown') {
79
80
  event.preventDefault();
80
81
  this.onUseNextCommand();
81
82
  }
83
+ else if ((event === null || event === void 0 ? void 0 : event.code) === 'Backspace') {
84
+ let input = (_a = this.elements) === null || _a === void 0 ? void 0 : _a.input;
85
+ if (input) {
86
+ let value = input.value || '';
87
+ // backspace pressed while input had completion selected.
88
+ // Native logic will remove selected text (completion part), we remove one additional character
89
+ if ((input.selectionStart || value.length) < value.length && input.selectionEnd == value.length) {
90
+ this.elements.input.value = value.substring(0, input.selectionStart || value.length - 1);
91
+ }
92
+ }
93
+ }
82
94
  };
83
95
  this.elements.input.oninput = event => {
84
96
  var _a;
85
97
  let value = ((_a = this.elements) === null || _a === void 0 ? void 0 : _a.input.value) || '';
86
- // backspace
87
- if (value.length > 0 && event.data === null) {
88
- value = value.substring(0, value.length - 1);
89
- }
90
98
  if (value.trim() === '') {
91
99
  return;
92
100
  }
93
101
  let autocompletion = window.ggstatic.availableCommands.find((c) => c[0].startsWith(value));
94
- if (autocompletion) {
102
+ if (autocompletion && autocompletion[0].length > value.length) {
95
103
  this.elements.input.value = autocompletion[0];
96
104
  this.elements.input.setSelectionRange(value.length, this.elements.input.value.length);
97
105
  }
@@ -6,6 +6,10 @@ export declare class GgDebuggerUI {
6
6
  private currentWorld;
7
7
  setShowStats(selectedWorld: GgWorld<any, any>, value: boolean): void;
8
8
  private debugControlsRemoved$;
9
+ private viewUpdated$;
9
10
  get showDebugControls(): boolean;
10
11
  setShowDebugControls(selectedWorld: GgWorld<any, any>, value: boolean): void;
12
+ private snapshot;
13
+ private makeSnapshot;
14
+ private renderControls;
11
15
  }
@@ -1,6 +1,22 @@
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
+ const snapshotEqual = (a, b) => {
6
+ if (a.timeScale !== b.timeScale)
7
+ return false;
8
+ if (a.renderers.length !== b.renderers.length)
9
+ return false;
10
+ for (let i = 0; i < a.renderers.length; i++) {
11
+ if (a.renderers[i].name !== b.renderers[i].name)
12
+ return false;
13
+ if (a.renderers[i].entity !== b.renderers[i].entity)
14
+ return false;
15
+ if (a.renderers[i].physicsDebugViewActive !== b.renderers[i].physicsDebugViewActive)
16
+ return false;
17
+ }
18
+ return true;
19
+ };
4
20
  export class GgDebuggerUI {
5
21
  constructor() {
6
22
  this.ui = {
@@ -10,6 +26,11 @@ export class GgDebuggerUI {
10
26
  this.statsRemoved$ = new Subject();
11
27
  this.currentWorld = null;
12
28
  this.debugControlsRemoved$ = new Subject();
29
+ this.viewUpdated$ = new Subject();
30
+ this.snapshot = {
31
+ timeScale: 1,
32
+ renderers: [],
33
+ };
13
34
  }
14
35
  get showStats() {
15
36
  return !!this.ui.stats;
@@ -64,48 +85,78 @@ export class GgDebuggerUI {
64
85
  this.currentWorld = selectedWorld;
65
86
  if (value) {
66
87
  const debugControlsContainer = document.createElement('div');
88
+ document.body.appendChild(debugControlsContainer);
67
89
  this.ui.debugControlsContainer = debugControlsContainer;
68
- const debugLabelCss = "style='display:flex;align-items:center;margin:0.25rem;'";
69
90
  debugControlsContainer.style.cssText =
70
91
  '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 = `
92
+ this.snapshot = this.makeSnapshot();
93
+ this.renderControls(debugControlsContainer);
94
+ of(undefined, animationFrameScheduler)
95
+ .pipe(repeat(), takeUntil(this.debugControlsRemoved$))
96
+ .subscribe(() => {
97
+ const newSnapshot = this.makeSnapshot();
98
+ if (!snapshotEqual(this.snapshot, newSnapshot)) {
99
+ this.snapshot = newSnapshot;
100
+ this.renderControls(debugControlsContainer);
101
+ }
102
+ });
103
+ }
104
+ else {
105
+ this.debugControlsRemoved$.next();
106
+ document.body.removeChild(this.ui.debugControlsContainer);
107
+ this.ui.debugControlsContainer = null;
108
+ }
109
+ }
110
+ makeSnapshot() {
111
+ return {
112
+ timeScale: this.currentWorld.worldClock.timeScale,
113
+ renderers: this.currentWorld.children
114
+ .filter(x => x instanceof IRendererEntity)
115
+ .map(r => ({
116
+ name: r.name,
117
+ entity: r,
118
+ physicsDebugViewActive: r.physicsDebugViewActive,
119
+ })),
120
+ };
121
+ }
122
+ renderControls(debugControlsContainer) {
123
+ const debugLabelCss = "style='display:flex;align-items:center;margin:0.25rem;'";
124
+ let html = '';
125
+ for (const { entity, physicsDebugViewActive } of this.snapshot.renderers) {
126
+ html += `
72
127
  <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>
128
+ <input type='checkbox' name='checkbox' id='physics_debugger_checkbox_id_${entity.name}' value='1'${physicsDebugViewActive ? ' checked' : ''}>
129
+ <label for='physics_debugger_checkbox_id_${entity.name}' style='user-select: none;'>Physics debugger${this.snapshot.renderers.length > 1 ? ' (' + entity.name + ')' : ''}</label>
75
130
  </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$))
131
+ }
132
+ html += `
133
+ <div ${debugLabelCss}>
134
+ <input id='time_scale_slider' type='range' min='0' max='5' step='0.01' style='flex-grow:1' value='${this.snapshot.timeScale}'/>
135
+ <label for='time_scale_slider' style='user-select: none;'>Time scale</label>
136
+ </div>`;
137
+ debugControlsContainer.innerHTML = html;
138
+ this.viewUpdated$.next();
139
+ for (const { entity } of this.snapshot.renderers) {
140
+ fromEvent(document.getElementById('physics_debugger_checkbox_id_' + entity.name), 'change')
141
+ .pipe(takeUntil(this.debugControlsRemoved$), takeUntil(this.viewUpdated$))
85
142
  .subscribe(e => {
86
143
  try {
87
- this.currentWorld.physicsDebugViewActive = e.target.checked;
144
+ entity.physicsDebugViewActive = e.target.checked;
88
145
  }
89
146
  catch (err) {
90
147
  console.error(err);
91
148
  }
92
- e.target.checked = this.currentWorld.physicsDebugViewActive;
93
149
  });
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
- }
105
- else {
106
- this.debugControlsRemoved$.next();
107
- document.body.removeChild(this.ui.debugControlsContainer);
108
- this.ui.debugControlsContainer = null;
109
150
  }
151
+ fromEvent(document.getElementById('time_scale_slider'), 'change')
152
+ .pipe(takeUntil(this.debugControlsRemoved$), takeUntil(this.viewUpdated$))
153
+ .subscribe(e => {
154
+ try {
155
+ this.currentWorld.worldClock.timeScale = +e.target.value;
156
+ }
157
+ catch (err) {
158
+ console.error(err);
159
+ }
160
+ });
110
161
  }
111
162
  }
@@ -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;
@@ -51,10 +51,10 @@ export class GgStatic {
51
51
  }), 'args: [string]; select world by name');
52
52
  }
53
53
  static get instance() {
54
- if (!GgStatic._instance) {
55
- GgStatic._instance = new GgStatic();
54
+ if (!window.ggstatic) {
55
+ window.ggstatic = new GgStatic();
56
56
  }
57
- return GgStatic._instance;
57
+ return window.ggstatic;
58
58
  }
59
59
  get devConsoleEnabled() {
60
60
  return this._devConsoleEnabled;
@@ -138,4 +138,3 @@ export class GgStatic {
138
138
  });
139
139
  }
140
140
  }
141
- window.ggstatic = GgStatic.instance;
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.39",
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 {};