@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.
Files changed (44) hide show
  1. package/dist/2d/entities/entity-2d.d.ts +6 -3
  2. package/dist/2d/entities/entity-2d.js +20 -17
  3. package/dist/2d/gg-2d-world.d.ts +3 -0
  4. package/dist/2d/gg-2d-world.js +21 -13
  5. package/dist/2d/models/shapes.d.ts +3 -1
  6. package/dist/3d/entities/controllers/input/car-keyboard-handling.controller.d.ts +1 -1
  7. package/dist/3d/entities/controllers/input/free-camera.controller.d.ts +32 -13
  8. package/dist/3d/entities/controllers/input/free-camera.controller.js +99 -45
  9. package/dist/3d/entities/controllers/input/gg-car-keyboard-handling.controller.d.ts +1 -1
  10. package/dist/3d/entities/controllers/input/orbit-camera.controller.d.ts +29 -9
  11. package/dist/3d/entities/controllers/input/orbit-camera.controller.js +64 -33
  12. package/dist/3d/entities/entity-3d.d.ts +6 -3
  13. package/dist/3d/entities/entity-3d.js +20 -17
  14. package/dist/3d/entities/raycast-vehicle-3d.entity.js +5 -5
  15. package/dist/3d/gg-3d-world.d.ts +3 -0
  16. package/dist/3d/gg-3d-world.js +21 -13
  17. package/dist/3d/loader.js +1 -1
  18. package/dist/3d/models/shapes.d.ts +3 -1
  19. package/dist/base/clock/global-clock.d.ts +2 -6
  20. package/dist/base/clock/global-clock.js +16 -16
  21. package/dist/base/clock/i-clock.d.ts +14 -5
  22. package/dist/base/clock/i-clock.js +43 -1
  23. package/dist/base/clock/pausable-clock.d.ts +3 -13
  24. package/dist/base/clock/pausable-clock.js +8 -16
  25. package/dist/base/gg-world.d.ts +6 -0
  26. package/dist/base/gg-world.js +102 -65
  27. package/dist/base/index.d.ts +1 -0
  28. package/dist/base/index.js +1 -0
  29. package/dist/base/inputs/keyboard.input.d.ts +1 -1
  30. package/dist/base/inputs/keyboard.input.js +10 -10
  31. package/dist/base/inputs/mouse.input.d.ts +1 -0
  32. package/dist/base/inputs/mouse.input.js +6 -2
  33. package/dist/base/pipes/gg-elastic.pipe.d.ts +5 -0
  34. package/dist/base/pipes/gg-elastic.pipe.js +44 -0
  35. package/dist/dev/gg-console.ui.js +14 -4
  36. package/dist/dev/gg-debugger.ui.d.ts +2 -2
  37. package/dist/dev/gg-debugger.ui.js +13 -10
  38. package/dist/dev/gg-static.d.ts +6 -1
  39. package/dist/dev/gg-static.js +104 -25
  40. package/dist/index.d.ts +2 -0
  41. package/dist/index.js +3 -0
  42. package/dist/version.d.ts +1 -0
  43. package/dist/version.js +1 -0
  44. package/package.json +4 -3
@@ -7,72 +7,103 @@ 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 { IEntity, MouseInput, MouseInputState, Pnt3, Qtrn, TickOrder, } from '../../../../base';
11
- import { filter, takeUntil } from 'rxjs';
10
+ import { ggElastic, IEntity, MouseInput, MouseInputState, Pnt2, Pnt3, Qtrn, TickOrder, } from '../../../../base';
11
+ import { BehaviorSubject, filter, Subject, takeUntil } from 'rxjs';
12
12
  import { map } from 'rxjs/operators';
13
13
  const DEFAULT_OPTIONS = {
14
14
  mouseOptions: {},
15
- orbiting: { sensitivityX: 1, sensitivityY: 1 }, // sensitivity units: rotation degree per 5 pixels movement
15
+ orbiting: { sensitivityX: 1, sensitivityY: 1 },
16
+ orbitingElasticity: 0,
16
17
  zooming: { sensitivity: 1 },
17
18
  panning: { sensitivityX: 1, sensitivityY: 1 },
18
19
  dollying: { sensitivity: 1 },
19
20
  };
20
21
  export class OrbitCameraController extends IEntity {
21
- get radius() {
22
- return this.spherical.radius;
22
+ get active() {
23
+ return super.active;
23
24
  }
24
- set radius(value) {
25
- this.spherical.radius = value;
25
+ set active(value) {
26
+ if (!super.active && value) {
27
+ this.reset();
28
+ }
29
+ super.active = value;
26
30
  }
27
- get phi() {
28
- return this.spherical.phi;
31
+ get spherical() {
32
+ return this._spherical;
29
33
  }
30
- set phi(value) {
31
- this.spherical.phi = Math.max(0.000001, Math.min(Math.PI - 0.000001, value));
32
- }
33
- get theta() {
34
- return this.spherical.theta;
35
- }
36
- set theta(value) {
37
- this.spherical.theta = value;
34
+ set spherical(value) {
35
+ this._spherical.phi = Math.max(0.000001, Math.min(Math.PI - 0.000001, value.phi));
36
+ this._spherical.theta = value.theta;
37
+ this._spherical.radius = value.radius;
38
+ this.resetMotion$.next();
38
39
  }
39
40
  constructor(camera, options = {}) {
40
41
  super();
41
42
  this.camera = camera;
42
43
  this.tickOrder = TickOrder.INPUT_CONTROLLERS;
43
- this.spherical = { phi: 0, radius: 10, theta: 0 };
44
+ this._spherical = { phi: 0, radius: 10, theta: 0 };
44
45
  this.target = Pnt3.O;
46
+ this.resetMotion$ = new Subject();
45
47
  this.options = Object.assign(Object.assign({}, DEFAULT_OPTIONS), options);
46
48
  this.mouseInput = new MouseInput(this.options.mouseOptions);
47
49
  }
50
+ reset() {
51
+ let targetDistance = Pnt3.dist(this.target, this.camera.position);
52
+ this.target = Pnt3.add(this.camera.position, Pnt3.rot({ x: 0, y: 0, z: -targetDistance }, this.camera.rotation));
53
+ this._spherical = Pnt3.toSpherical(Pnt3.sub(this.camera.position, this.target));
54
+ this.resetMotion$.next();
55
+ }
48
56
  onSpawned(world) {
49
57
  const _super = Object.create(null, {
50
58
  onSpawned: { get: () => super.onSpawned }
51
59
  });
52
60
  return __awaiter(this, void 0, void 0, function* () {
53
61
  yield _super.onSpawned.call(this, world);
54
- this.spherical = Pnt3.toSpherical(Pnt3.sub(this.camera.position, this.target));
62
+ this._spherical = Pnt3.toSpherical(Pnt3.sub(this.camera.position, this.target));
55
63
  if (this.options.orbiting) {
56
- this.mouseInput.delta$
57
- .pipe(takeUntil(this._onRemoved$), filter(() => this.mouseInput.state == MouseInputState.DRAG))
58
- .subscribe(delta => {
59
- this.spherical.theta -= (delta.x * this.options.orbiting.sensitivityX * Math.PI) / 900;
60
- this.spherical.phi -= (delta.y * this.options.orbiting.sensitivityY * Math.PI) / 900;
61
- this.spherical.phi = Math.max(0.000001, Math.min(Math.PI - 0.000001, this.spherical.phi));
62
- });
64
+ let mouseDelta$ = this.mouseInput.delta$.pipe(takeUntil(this._onRemoved$), filter(() => this.active && this.mouseInput.state == MouseInputState.DRAG));
65
+ if (this.options.orbitingElasticity > 0) {
66
+ const s$ = new BehaviorSubject(this._spherical);
67
+ mouseDelta$.subscribe(delta => {
68
+ const s = s$.getValue();
69
+ s$.next({
70
+ phi: Math.max(0.000001, Math.min(Math.PI - 0.000001, s.phi - (delta.y * this.options.orbiting.sensitivityY) / 1000)),
71
+ theta: s.theta - (delta.x * this.options.orbiting.sensitivityX) / 1000,
72
+ radius: 1,
73
+ });
74
+ });
75
+ const startElasticMotion = () => {
76
+ s$.pipe(takeUntil(this._onRemoved$), ggElastic(this.tick$, this.options.orbitingElasticity, (a, b, f) => ({ phi: a.phi + f * (b.phi - a.phi), theta: a.theta + f * (b.theta - a.theta), radius: 1 }), (a, b) => Pnt2.dist({ x: a.phi, y: a.theta }, { x: b.phi, y: b.theta }) < 0.0001), takeUntil(this.resetMotion$)).subscribe(s => {
77
+ this._spherical.theta = s.theta;
78
+ this._spherical.phi = s.phi;
79
+ });
80
+ };
81
+ this.resetMotion$.pipe(takeUntil(this._onRemoved$)).subscribe(() => {
82
+ s$.next(this._spherical);
83
+ startElasticMotion();
84
+ });
85
+ startElasticMotion();
86
+ }
87
+ else {
88
+ mouseDelta$.subscribe(delta => {
89
+ this._spherical.theta -= (delta.x * this.options.orbiting.sensitivityX) / 1000;
90
+ this._spherical.phi -= (delta.y * this.options.orbiting.sensitivityY) / 1000;
91
+ this._spherical.phi = Math.max(0.000001, Math.min(Math.PI - 0.000001, this._spherical.phi));
92
+ });
93
+ }
63
94
  }
64
95
  if (this.options.zooming) {
65
96
  this.mouseInput.wheel$.pipe(takeUntil(this._onRemoved$)).subscribe(delta => {
66
97
  if (delta != 0) {
67
- this.spherical.radius *= Math.pow(0.95, this.options.zooming.sensitivity * (delta > 0 ? -1 : 1));
98
+ this._spherical.radius *= Math.pow(0.95, this.options.zooming.sensitivity * (delta > 0 ? -1 : 1));
68
99
  }
69
100
  });
70
101
  }
71
102
  const performPan = (delta) => {
72
- const targetCameraVector = Pnt3.fromSpherical(this.spherical);
103
+ const targetCameraVector = Pnt3.fromSpherical(this._spherical);
73
104
  const viewUp = Pnt3.rotAround(targetCameraVector, {
74
- x: -Math.sin(this.spherical.theta),
75
- y: Math.cos(this.spherical.theta),
105
+ x: -Math.sin(this._spherical.theta),
106
+ y: Math.cos(this._spherical.theta),
76
107
  z: 0,
77
108
  }, Math.PI / 2);
78
109
  const viewRight = Pnt3.rotAround(targetCameraVector, Pnt3.norm(viewUp), Math.PI / 2);
@@ -89,14 +120,14 @@ export class OrbitCameraController extends IEntity {
89
120
  this.mouseInput.delta$
90
121
  .pipe(takeUntil(this._onRemoved$), filter(() => this.mouseInput.state == MouseInputState.DRAG_MIDDLE_BUTTON))
91
122
  .subscribe(delta => {
92
- this.spherical.radius *= Math.pow(0.95, (-this.options.dollying.sensitivity * delta.y) / 10);
123
+ this._spherical.radius *= Math.pow(0.95, (-this.options.dollying.sensitivity * delta.y) / 10);
93
124
  });
94
125
  }
95
126
  if (MouseInput.isTouchDevice() && (this.options.dollying || this.options.panning)) {
96
127
  this.mouseInput.twoTouchGestureDelta$.pipe(takeUntil(this._onRemoved$)).subscribe(delta => {
97
128
  // dolly on fingers pitch
98
129
  if (this.options.dollying) {
99
- this.spherical.radius *= Math.pow(0.95, (this.options.dollying.sensitivity * delta.distanceDelta) / 10);
130
+ this._spherical.radius *= Math.pow(0.95, (this.options.dollying.sensitivity * delta.distanceDelta) / 10);
100
131
  }
101
132
  // pan on fingers move
102
133
  if (this.options.panning) {
@@ -106,7 +137,7 @@ export class OrbitCameraController extends IEntity {
106
137
  }
107
138
  // Setup updating camera position and rotation based on input
108
139
  this.camera.tick$
109
- .pipe(takeUntil(this._onRemoved$), map(() => this.spherical))
140
+ .pipe(takeUntil(this._onRemoved$), filter(() => this.active), map(() => this._spherical))
110
141
  .subscribe(spherical => {
111
142
  this.camera.position = Pnt3.add(this.target, Pnt3.fromSpherical(spherical));
112
143
  this.camera.rotation = Qtrn.lookAt(this.camera.position, this.target);
@@ -5,8 +5,6 @@ import { IPositionable3d } from '../interfaces/i-positionable-3d';
5
5
  import { IRenderable3dEntity } from './i-renderable-3d.entity';
6
6
  import { PhysicsTypeDocRepo3D, VisualTypeDocRepo3D } from '../gg-3d-world';
7
7
  export declare class Entity3d<VTypeDoc extends VisualTypeDocRepo3D = VisualTypeDocRepo3D, PTypeDoc extends PhysicsTypeDocRepo3D = PhysicsTypeDocRepo3D> extends IRenderable3dEntity<VTypeDoc, PTypeDoc> implements IPositionable3d {
8
- readonly object3D: VTypeDoc['displayObject'] | null;
9
- readonly objectBody: PTypeDoc['rigidBody'] | null;
10
8
  readonly tickOrder = TickOrder.OBJECTS_BINDING;
11
9
  private _position;
12
10
  get position(): Point3;
@@ -14,10 +12,15 @@ export declare class Entity3d<VTypeDoc extends VisualTypeDocRepo3D = VisualTypeD
14
12
  private _rotation;
15
13
  get rotation(): Point4;
16
14
  set rotation(value: Point4);
15
+ readonly object3D: VTypeDoc['displayObject'] | null;
16
+ readonly objectBody: PTypeDoc['rigidBody'] | null;
17
17
  updateVisibility(): void;
18
18
  /**
19
19
  * Synchronize physics body transform with entity (and mesh if defined)
20
20
  * */
21
21
  protected runTransformBinding(objectBody: IRigidBody3dComponent, object3D: IDisplayObject3dComponent | null): void;
22
- constructor(object3D: VTypeDoc['displayObject'] | null, objectBody?: PTypeDoc['rigidBody'] | null);
22
+ constructor(options: {
23
+ object3D?: VTypeDoc['displayObject'] | null;
24
+ objectBody?: PTypeDoc['rigidBody'] | null;
25
+ });
23
26
  }
@@ -44,29 +44,32 @@ export class Entity3d extends IRenderable3dEntity {
44
44
  this._position = pos;
45
45
  this._rotation = quat;
46
46
  }
47
- constructor(object3D, objectBody = null) {
47
+ constructor(options) {
48
48
  super();
49
- this.object3D = object3D;
50
- this.objectBody = objectBody;
51
49
  this.tickOrder = TickOrder.OBJECTS_BINDING;
52
50
  this._position = Pnt3.O;
53
51
  this._rotation = Qtrn.O;
54
- if (objectBody) {
55
- this.tick$.subscribe(() => {
56
- this.runTransformBinding(objectBody, object3D);
57
- });
58
- this.runTransformBinding(objectBody, object3D);
59
- this.name = objectBody.name;
52
+ this.object3D = null;
53
+ this.objectBody = null;
54
+ if (options.objectBody) {
55
+ this.objectBody = options.objectBody;
56
+ this.name = this.objectBody.name;
57
+ this.addComponents(this.objectBody);
60
58
  }
61
- else if (object3D) {
62
- this._position = object3D.position;
63
- this._rotation = object3D.rotation;
64
- this.name = object3D.name;
59
+ if (options.object3D) {
60
+ this.object3D = options.object3D;
61
+ if (!options.objectBody) {
62
+ this._position = this.object3D.position;
63
+ this._rotation = this.object3D.rotation;
64
+ this.name = this.object3D.name;
65
+ }
66
+ this.addComponents(this.object3D);
65
67
  }
66
- else {
67
- throw new Error('Cannot create entity without a mesh and a body');
68
+ if (this.objectBody) {
69
+ this.tick$.subscribe(() => {
70
+ this.runTransformBinding(this.objectBody, this.object3D);
71
+ });
72
+ this.runTransformBinding(this.objectBody, this.object3D);
68
73
  }
69
- objectBody && this.addComponents(objectBody);
70
- object3D && this.addComponents(object3D);
71
74
  }
72
75
  }
@@ -56,7 +56,7 @@ export class RaycastVehicle3dEntity extends Entity3d {
56
56
  }
57
57
  /** car mesh and physics body direction has to be pointing: y front, z up*/
58
58
  constructor(carProperties, chassis3D, vehicleComponent) {
59
- super(chassis3D, vehicleComponent);
59
+ super({ object3D: chassis3D, objectBody: vehicleComponent });
60
60
  this.carProperties = carProperties;
61
61
  this.chassis3D = chassis3D;
62
62
  this.vehicleComponent = vehicleComponent;
@@ -104,9 +104,9 @@ export class RaycastVehicle3dEntity extends Entity3d {
104
104
  this.wheelLocalRotation.push(null);
105
105
  continue;
106
106
  }
107
- const entity = display.displayObject.clone();
107
+ const displayObj = display.displayObject.clone();
108
108
  if (display.autoScaleMesh) {
109
- const boundingBox = Box.expandByPoint(entity.getBoundings(), Pnt3.O);
109
+ const boundingBox = Box.expandByPoint(displayObj.getBoundings(), Pnt3.O);
110
110
  const scale = Object.assign({}, Pnt3.O);
111
111
  const wheelObjectDirection = display.wheelObjectDirection || 'x';
112
112
  for (const dir of ['x', 'y', 'z']) {
@@ -115,7 +115,7 @@ export class RaycastVehicle3dEntity extends Entity3d {
115
115
  ? options.tyreWidth / (boundingBox.max[dir] - boundingBox.min[dir])
116
116
  : (options.tyreRadius * 2) / (boundingBox.max[dir] - boundingBox.min[dir]);
117
117
  }
118
- entity.scale = scale;
118
+ displayObj.scale = scale;
119
119
  }
120
120
  const direction = display.wheelObjectDirection || 'x';
121
121
  const flip = direction.includes('-') ? options.isLeft : !options.isLeft;
@@ -134,7 +134,7 @@ export class RaycastVehicle3dEntity extends Entity3d {
134
134
  localRotation = { x: 0, y: 0.707107 * (flip ? 1 : -1), z: 0, w: 0.707107 };
135
135
  }
136
136
  this.wheelLocalRotation.push(localRotation);
137
- const wheelEntity = new Entity3d(entity);
137
+ const wheelEntity = new Entity3d({ object3D: displayObj });
138
138
  wheelEntity.name = this.name + '__wheel_' + (options.isFront ? 'f' : 'r') + (options.isLeft ? 'l' : 'r');
139
139
  this.wheels.push(wheelEntity);
140
140
  }
@@ -36,4 +36,7 @@ export declare class Gg3dWorld<VTypeDoc extends VisualTypeDocRepo3D = VisualType
36
36
  constructor(visualScene: VS, physicsWorld: PW);
37
37
  addPrimitiveRigidBody(descr: BodyShape3DDescriptor, position?: Point3, rotation?: Point4, material?: DisplayObject3dOpts<VTypeDoc['texture']>): Entity3d<VTypeDoc, PTypeDoc>;
38
38
  addRenderer(camera: VTypeDoc['camera'], canvas?: HTMLCanvasElement, rendererOptions?: Partial<RendererOptions & VTypeDoc['rendererExtraOpts']>): Renderer3dEntity<VTypeDoc>;
39
+ protected registerConsoleCommands(ggstatic: {
40
+ registerConsoleCommand: (world: GgWorld<any, any> | null, command: string, handler: (...args: string[]) => Promise<string>, doc?: string) => void;
41
+ }): void;
39
42
  }
@@ -17,21 +17,12 @@ export class Gg3dWorld extends GgWorld {
17
17
  this.visualScene = visualScene;
18
18
  this.physicsWorld = physicsWorld;
19
19
  this.loader = new Gg3dLoader(this);
20
- if (window.ggstatic) {
21
- window.ggstatic.registerConsoleCommand(this, 'ph_gravity', (...args) => __awaiter(this, void 0, void 0, function* () {
22
- if (args.length == 1) {
23
- args = ['0', '0', '' + -+args[0]]; // mean -Z axis
24
- }
25
- if (args.length > 0) {
26
- this.physicsWorld.gravity = { x: +args[0], y: +args[1], z: +args[2] };
27
- }
28
- return JSON.stringify(this.physicsWorld.gravity);
29
- }), 'args: [float] or [float float float]; change 3D world gravity vector. 1 argument means ' +
30
- '{x: 0, y: 0, z: -value}, 3 arguments set the whole vector. Default value is "9.82" or "0 0 -9.82"');
31
- }
32
20
  }
33
21
  addPrimitiveRigidBody(descr, position = Pnt3.O, rotation = Qtrn.O, material = {}) {
34
- const entity = new Entity3d(this.visualScene.factory.createPrimitive(descr.shape, material), this.physicsWorld.factory.createRigidBody(descr));
22
+ const entity = new Entity3d({
23
+ object3D: this.visualScene.factory.createPrimitive(descr.shape, material),
24
+ objectBody: this.physicsWorld.factory.createRigidBody(descr),
25
+ });
35
26
  entity.position = position;
36
27
  entity.rotation = rotation;
37
28
  this.addEntity(entity);
@@ -42,4 +33,21 @@ export class Gg3dWorld extends GgWorld {
42
33
  this.addEntity(entity);
43
34
  return entity;
44
35
  }
36
+ registerConsoleCommands(ggstatic) {
37
+ super.registerConsoleCommands(ggstatic);
38
+ ggstatic.registerConsoleCommand(this, 'gravity', (...args) => __awaiter(this, void 0, void 0, function* () {
39
+ if (args.length == 1) {
40
+ args = ['0', '0', '' + -+args[0]]; // mean -Z axis
41
+ }
42
+ if (args.length > 0) {
43
+ if (isNaN(+args[0]) || isNaN(+args[1]) || isNaN(+args[2])) {
44
+ throw new Error('Wrong arguments');
45
+ }
46
+ this.physicsWorld.gravity = { x: +args[0], y: +args[1], z: +args[2] };
47
+ }
48
+ return JSON.stringify(this.physicsWorld.gravity);
49
+ }), 'args: [ ?float, ?float, ?float ]; Get or set 3D world gravity vector. 1 argument sets ' +
50
+ 'vector {x: 0, y: 0, z: -value}, 3 arguments set the whole vector.' +
51
+ ' Default value is "9.82" or "0 0 -9.82"');
52
+ }
45
53
  }
package/dist/3d/loader.js CHANGED
@@ -99,7 +99,7 @@ export class Gg3dLoader {
99
99
  const loadOptions = Object.assign(Object.assign({}, defaultLoadOptions), options);
100
100
  const { resources, meta } = yield this.loadGgGlbResources(path, loadOptions.cachingStrategy);
101
101
  const result = {
102
- entities: resources.map(({ object3D, body }) => new Entity3d(object3D, body)),
102
+ entities: resources.map(x => new Entity3d({ object3D: x.object3D, objectBody: x.body })),
103
103
  meta,
104
104
  };
105
105
  if (loadOptions.loadProps) {
@@ -1,6 +1,8 @@
1
1
  import { Point2, Point3, Point4 } from '../../base';
2
2
  import { Body3DOptions } from './body-options';
3
3
  export type Shape3DDescriptor = {
4
+ collisionMargin?: number;
5
+ } & ({
4
6
  shape: 'PLANE';
5
7
  } | {
6
8
  shape: 'BOX';
@@ -30,7 +32,7 @@ export type Shape3DDescriptor = {
30
32
  shape: 'MESH';
31
33
  vertices: Point3[];
32
34
  faces: [number, number, number][];
33
- };
35
+ });
34
36
  export type Shape3DMeshDescriptor = {
35
37
  shape: 'PLANE';
36
38
  dimensions?: Point2;
@@ -1,16 +1,12 @@
1
- import { Observable } from 'rxjs';
2
- import { PausableClock } from './pausable-clock';
3
1
  import { IClock } from './i-clock';
4
2
  /**
5
3
  * A singleton class, providing ability to track time, fire ticks, provide time elapsed + tick delta.
6
4
  * Starts as soon as accessed and counts time from 01/01/1970
7
5
  */
8
- export declare class GgGlobalClock implements IClock {
6
+ export declare class GgGlobalClock extends IClock {
9
7
  private static _instance;
10
8
  static get instance(): GgGlobalClock;
11
- private readonly _tick$;
12
- get tick$(): Observable<[number, number]>;
13
9
  get elapsedTime(): number;
14
- createChildClock(autoStart: boolean): PausableClock;
15
10
  private constructor();
11
+ dispose(): void;
16
12
  }
@@ -1,32 +1,32 @@
1
- import { animationFrameScheduler, of, Subject } from 'rxjs';
2
- import { map, repeat, tap } from 'rxjs/operators';
3
- import { PausableClock } from './pausable-clock';
1
+ import { IClock } from './i-clock';
2
+ const now = typeof performance === 'undefined' ? () => Date.now() : () => performance.now();
4
3
  /**
5
4
  * A singleton class, providing ability to track time, fire ticks, provide time elapsed + tick delta.
6
5
  * Starts as soon as accessed and counts time from 01/01/1970
7
6
  */
8
- export class GgGlobalClock {
7
+ export class GgGlobalClock extends IClock {
9
8
  static get instance() {
10
9
  if (!GgGlobalClock._instance) {
11
10
  GgGlobalClock._instance = new GgGlobalClock();
12
11
  }
13
12
  return GgGlobalClock._instance;
14
13
  }
15
- get tick$() {
16
- return this._tick$.pipe(map(([oldTime, newTime]) => [newTime, newTime - oldTime]));
17
- }
18
14
  get elapsedTime() {
19
- return (typeof performance === 'undefined' ? Date : performance).now();
20
- }
21
- createChildClock(autoStart) {
22
- return new PausableClock(autoStart, this);
15
+ return now();
23
16
  }
24
17
  constructor() {
25
- this._tick$ = new Subject();
18
+ super(null);
26
19
  let oldRelativeTime = this.elapsedTime;
27
- of(undefined, animationFrameScheduler)
28
- .pipe(repeat())
29
- .pipe(map(() => [oldRelativeTime, this.elapsedTime]), tap(([_, cur]) => (oldRelativeTime = cur)))
30
- .subscribe(this._tick$);
20
+ const tick = () => {
21
+ requestAnimationFrame(tick);
22
+ const prev = oldRelativeTime;
23
+ const cur = this.elapsedTime;
24
+ oldRelativeTime = cur;
25
+ this._tick$.next([prev, cur - prev]);
26
+ };
27
+ requestAnimationFrame(tick);
28
+ }
29
+ dispose() {
30
+ throw new Error('Cannot dispose global clock');
31
31
  }
32
32
  }
@@ -1,7 +1,16 @@
1
- import { Observable } from 'rxjs';
2
- import { PausableClock } from './pausable-clock';
3
- export interface IClock {
1
+ import { Observable, Subject } from 'rxjs';
2
+ export declare abstract class IClock {
3
+ readonly parent: IClock | null;
4
+ protected readonly _tick$: Subject<[number, number]>;
5
+ /**
6
+ * Observable stream of ticks, emitting an array containing the current time and the tick delta.
7
+ */
4
8
  get tick$(): Observable<[number, number]>;
5
- get elapsedTime(): number;
6
- createChildClock(autoStart: boolean): PausableClock;
9
+ abstract get elapsedTime(): number;
10
+ protected _children: IClock[];
11
+ get children(): IClock[];
12
+ protected constructor(parent: IClock | null);
13
+ addChild(clock: IClock): void;
14
+ removeChild(clock: IClock): void;
15
+ dispose(): void;
7
16
  }
@@ -1 +1,43 @@
1
- export {};
1
+ import { Subject } from 'rxjs';
2
+ export class IClock {
3
+ /**
4
+ * Observable stream of ticks, emitting an array containing the current time and the tick delta.
5
+ */
6
+ get tick$() {
7
+ return this._tick$.asObservable();
8
+ }
9
+ get children() {
10
+ return [...this._children];
11
+ }
12
+ constructor(parent) {
13
+ this.parent = parent;
14
+ this._tick$ = new Subject();
15
+ this._children = [];
16
+ if (parent) {
17
+ parent.addChild(this);
18
+ }
19
+ }
20
+ addChild(clock) {
21
+ if (clock.parent !== this) {
22
+ throw new Error('Incorrect child clock');
23
+ }
24
+ if (!this._children.includes(clock)) {
25
+ this._children.push(clock);
26
+ }
27
+ }
28
+ removeChild(clock) {
29
+ if (clock.parent !== this) {
30
+ throw new Error('Incorrect child clock');
31
+ }
32
+ this._children = this._children.filter(c => c !== clock);
33
+ }
34
+ dispose() {
35
+ if (this.parent) {
36
+ this.parent.removeChild(this);
37
+ }
38
+ for (const c of this._children) {
39
+ c.dispose();
40
+ }
41
+ this._tick$.complete();
42
+ }
43
+ }
@@ -1,17 +1,12 @@
1
- import { Observable, Subject } from 'rxjs';
1
+ import { Subject } from 'rxjs';
2
2
  import { IClock } from './i-clock';
3
3
  /**
4
4
  * A class providing the ability to track time, fire ticks, provide time elapsed, and tick delta with the ability to suspend/resume it.
5
5
  */
6
- export declare class PausableClock implements IClock {
6
+ export declare class PausableClock extends IClock {
7
7
  protected readonly parentClock: IClock;
8
8
  private tickSub;
9
9
  private readonly _internalTick$;
10
- private readonly _tick$;
11
- /**
12
- * Observable stream of ticks, emitting an array containing the current time and the tick delta.
13
- */
14
- get tick$(): Observable<[number, number]>;
15
10
  /**
16
11
  * Checks if the clock is currently running.
17
12
  */
@@ -54,12 +49,6 @@ export declare class PausableClock implements IClock {
54
49
  * @param parentClock The parent clock to synchronize with. Defaults to GgGlobalClock instance.
55
50
  */
56
51
  constructor(autoStart?: boolean, parentClock?: IClock);
57
- /**
58
- * Creates a child clock.
59
- * @param autoStart Indicates whether the child clock should start automatically.
60
- * @returns A new instance of PausableClock.
61
- */
62
- createChildClock(autoStart: boolean): PausableClock;
63
52
  /**
64
53
  * Starts the clock.
65
54
  */
@@ -84,4 +73,5 @@ export declare class PausableClock implements IClock {
84
73
  * Stops listening for ticks from the parent clock.
85
74
  */
86
75
  protected stopListeningTicks(): void;
76
+ dispose(): void;
87
77
  }
@@ -1,16 +1,11 @@
1
1
  import { filter, Subject } from 'rxjs';
2
2
  import { map, tap } from 'rxjs/operators';
3
3
  import { GgGlobalClock } from './global-clock';
4
+ import { IClock } from './i-clock';
4
5
  /**
5
6
  * A class providing the ability to track time, fire ticks, provide time elapsed, and tick delta with the ability to suspend/resume it.
6
7
  */
7
- export class PausableClock {
8
- /**
9
- * Observable stream of ticks, emitting an array containing the current time and the tick delta.
10
- */
11
- get tick$() {
12
- return this._tick$.asObservable();
13
- }
8
+ export class PausableClock extends IClock {
14
9
  /**
15
10
  * Checks if the clock is currently running.
16
11
  */
@@ -76,10 +71,10 @@ export class PausableClock {
76
71
  * @param parentClock The parent clock to synchronize with. Defaults to GgGlobalClock instance.
77
72
  */
78
73
  constructor(autoStart = false, parentClock = GgGlobalClock.instance) {
74
+ super(parentClock);
79
75
  this.parentClock = parentClock;
80
76
  this.tickSub = null;
81
77
  this._internalTick$ = new Subject();
82
- this._tick$ = new Subject();
83
78
  /**
84
79
  * Tick rate limiter. If set to 0 - tick rate is unlimited, 15 means "allow at most 15 ticks per second"
85
80
  */
@@ -103,14 +98,6 @@ export class PausableClock {
103
98
  Math.floor((elapsed * this.tickRateLimit) / 1000)), tap(([elapsed]) => (this.lastFiredTickElapsed = elapsed)))
104
99
  .subscribe(this._tick$);
105
100
  }
106
- /**
107
- * Creates a child clock.
108
- * @param autoStart Indicates whether the child clock should start automatically.
109
- * @returns A new instance of PausableClock.
110
- */
111
- createChildClock(autoStart) {
112
- return new PausableClock(autoStart, this);
113
- }
114
101
  /**
115
102
  * Starts the clock.
116
103
  */
@@ -170,4 +157,9 @@ export class PausableClock {
170
157
  (_a = this.tickSub) === null || _a === void 0 ? void 0 : _a.unsubscribe();
171
158
  this.tickSub = null;
172
159
  }
160
+ dispose() {
161
+ this.stopListeningTicks();
162
+ this._internalTick$.complete();
163
+ super.dispose();
164
+ }
173
165
  }
@@ -16,6 +16,7 @@ export declare abstract class GgWorld<D, R, VTypeDoc extends VisualTypeDocRepo<D
16
16
  readonly physicsWorld: PW;
17
17
  private static default_name_counter;
18
18
  private static _documentWorlds;
19
+ static readonly worldCreated$: Subject<GgWorld<any, any>>;
19
20
  static get documentWorlds(): GgWorld<any, any>[];
20
21
  readonly worldClock: PausableClock;
21
22
  readonly keyboardInput: KeyboardInput;
@@ -26,6 +27,7 @@ export declare abstract class GgWorld<D, R, VTypeDoc extends VisualTypeDocRepo<D
26
27
  readonly tickForwardTo$: Subject<IEntity | 'PHYSICS_WORLD'>;
27
28
  readonly tickForwardedTo$: Subject<IEntity | 'PHYSICS_WORLD'>;
28
29
  readonly paused$: Subject<boolean>;
30
+ readonly disposed$: Subject<void>;
29
31
  protected constructor(visualScene: VS, physicsWorld: PW);
30
32
  init(): Promise<void>;
31
33
  start(): void;
@@ -40,4 +42,8 @@ export declare abstract class GgWorld<D, R, VTypeDoc extends VisualTypeDocRepo<D
40
42
  position?: D, rotation?: R, material?: unknown): IPositionable<D, R> & IRenderableEntity<D, R, VTypeDoc>;
41
43
  addEntity(entity: IEntity): void;
42
44
  removeEntity(entity: IEntity, dispose?: boolean): void;
45
+ private onGgStaticInitialized;
46
+ protected registerConsoleCommands(ggstatic: {
47
+ registerConsoleCommand: (world: GgWorld<any, any> | null, command: string, handler: (...args: string[]) => Promise<string>, doc?: string) => void;
48
+ }): void;
43
49
  }