@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,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,5 +1,5 @@
1
1
  import { IVisualSceneComponent, Point2, RendererOptions } from '../../../base';
2
2
  import { VisualTypeDocRepo2D } from '../../gg-2d-world';
3
3
  export interface IVisualScene2dComponent<TypeDoc extends VisualTypeDocRepo2D = VisualTypeDocRepo2D> extends IVisualSceneComponent<Point2, number, TypeDoc> {
4
- createRenderer(canvas?: HTMLCanvasElement, rendererOptions?: Partial<RendererOptions>): TypeDoc['renderer'];
4
+ createRenderer(canvas?: HTMLCanvasElement, rendererOptions?: Partial<RendererOptions & TypeDoc['rendererExtraOpts']>): TypeDoc['renderer'];
5
5
  }
@@ -1,4 +1,6 @@
1
1
  import { IRenderableEntity, Point2 } from '../../base';
2
- import { PhysicsTypeDocRepo2D, VisualTypeDocRepo2D } from '../gg-2d-world';
2
+ import { Gg2dWorld, PhysicsTypeDocRepo2D, VisualTypeDocRepo2D } from '../gg-2d-world';
3
3
  export declare abstract class IRenderable2dEntity<TypeDoc extends VisualTypeDocRepo2D = VisualTypeDocRepo2D, PTypeDoc extends PhysicsTypeDocRepo2D = PhysicsTypeDocRepo2D> extends IRenderableEntity<Point2, number, TypeDoc, PTypeDoc> {
4
+ protected _world: Gg2dWorld<TypeDoc, PTypeDoc> | null;
5
+ get world(): Gg2dWorld<TypeDoc, PTypeDoc> | null;
4
6
  }
@@ -1,3 +1,10 @@
1
1
  import { IRenderableEntity } from '../../base';
2
2
  export class IRenderable2dEntity extends IRenderableEntity {
3
+ constructor() {
4
+ super(...arguments);
5
+ this._world = null;
6
+ }
7
+ get world() {
8
+ return this._world;
9
+ }
3
10
  }
@@ -13,6 +13,7 @@ export declare type VisualTypeDocRepo2D = {
13
13
  factory: IDisplayObject2dComponentFactory;
14
14
  displayObject: IDisplayObject2dComponent;
15
15
  renderer: IRenderer2dComponent;
16
+ rendererExtraOpts: {};
16
17
  texture: unknown;
17
18
  };
18
19
  export declare type PhysicsTypeDocRepo2D = {
@@ -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
  }
@@ -2,5 +2,5 @@ import { IVisualSceneComponent, Point3, Point4, RendererOptions } from '../../..
2
2
  import { VisualTypeDocRepo3D } from '../../gg-3d-world';
3
3
  export interface IVisualScene3dComponent<TypeDoc extends VisualTypeDocRepo3D = VisualTypeDocRepo3D> extends IVisualSceneComponent<Point3, Point4, TypeDoc> {
4
4
  readonly loader: TypeDoc['loader'];
5
- createRenderer(camera: TypeDoc['camera'], canvas?: HTMLCanvasElement, rendererOptions?: Partial<RendererOptions>): TypeDoc['renderer'];
5
+ createRenderer(camera: TypeDoc['camera'], canvas?: HTMLCanvasElement, rendererOptions?: Partial<RendererOptions & TypeDoc['rendererExtraOpts']>): TypeDoc['renderer'];
6
6
  }
@@ -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,4 +1,6 @@
1
1
  import { IRenderableEntity, Point3, Point4 } from '../../base';
2
- import { PhysicsTypeDocRepo3D, VisualTypeDocRepo3D } from '../gg-3d-world';
2
+ import { Gg3dWorld, PhysicsTypeDocRepo3D, VisualTypeDocRepo3D } from '../gg-3d-world';
3
3
  export declare abstract class IRenderable3dEntity<TypeDoc extends VisualTypeDocRepo3D = VisualTypeDocRepo3D, PTypeDoc extends PhysicsTypeDocRepo3D = PhysicsTypeDocRepo3D> extends IRenderableEntity<Point3, Point4, TypeDoc, PTypeDoc> {
4
+ protected _world: Gg3dWorld<TypeDoc, PTypeDoc> | null;
5
+ get world(): Gg3dWorld<TypeDoc, PTypeDoc> | null;
4
6
  }
@@ -1,3 +1,10 @@
1
1
  import { IRenderableEntity } from '../../base';
2
2
  export class IRenderable3dEntity extends IRenderableEntity {
3
+ constructor() {
4
+ super(...arguments);
5
+ this._world = null;
6
+ }
7
+ get world() {
8
+ return this._world;
9
+ }
3
10
  }
@@ -1,5 +1,5 @@
1
1
  import { BehaviorSubject, Observable, Subject } from 'rxjs';
2
- import { Graph, IEntity, Point3, Point4, TickOrder } from '../../base';
2
+ import { Graph, IEntity, PausableClock, Point3, Point4, TickOrder } from '../../base';
3
3
  import { Gg3dWorld, PhysicsTypeDocRepo3D, VisualTypeDocRepo3D } from '../gg-3d-world';
4
4
  import { Entity3d } from './entity-3d';
5
5
  import { LoadOptions, LoadResultWithProps } from '../loader';
@@ -60,10 +60,9 @@ export declare class MapGraph3dEntity<VTypeDoc extends VisualTypeDocRepo3D = Vis
60
60
  rotation: Point4;
61
61
  }
62
62
  ]>;
63
- get world(): Gg3dWorld<VTypeDoc, PTypeDoc> | null;
64
- protected _world: Gg3dWorld<VTypeDoc, PTypeDoc> | null;
65
63
  protected readonly mapGraphNodes: MapGraph[];
66
64
  protected readonly options: Gg3dMapGraphEntityOptions;
65
+ protected loadClock: PausableClock | null;
67
66
  constructor(mapGraph: MapGraph, options?: Partial<Gg3dMapGraphEntityOptions>);
68
67
  onSpawned(world: Gg3dWorld<VTypeDoc, PTypeDoc>): void;
69
68
  onRemoved(): void;
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { BehaviorSubject, NEVER, startWith, Subject, switchMap, takeUntil } from 'rxjs';
11
- import { distinctUntilChanged, map, tap, throttleTime } from 'rxjs/operators';
11
+ import { distinctUntilChanged, map, tap } from 'rxjs/operators';
12
12
  import { Graph, Qtrn, TickOrder } from '../../base';
13
13
  import { IRenderable3dEntity } from './i-renderable-3d.entity';
14
14
  export class MapGraph extends Graph {
@@ -90,7 +90,7 @@ export class MapGraph3dEntity extends IRenderable3dEntity {
90
90
  this._initialLoadComplete$ = new BehaviorSubject(false);
91
91
  this._nearestDummy$ = new BehaviorSubject(null);
92
92
  this._chunkLoaded$ = new Subject();
93
- this._world = null;
93
+ this.loadClock = null;
94
94
  this.options = Object.assign(Object.assign({}, defaultOptions), options);
95
95
  this.mapGraphNodes = mapGraph.nodes();
96
96
  }
@@ -103,15 +103,14 @@ export class MapGraph3dEntity extends IRenderable3dEntity {
103
103
  get chunkLoaded$() {
104
104
  return this._chunkLoaded$.asObservable();
105
105
  }
106
- get world() {
107
- return this._world;
108
- }
109
106
  onSpawned(world) {
110
107
  super.onSpawned(world);
108
+ this.loadClock = world.createClock(true);
109
+ this.loadClock.tickRateLimit = 1;
111
110
  this.loaderCursorEntity$
112
111
  .pipe(takeUntil(this._onRemoved$), switchMap(entity => entity
113
- ? this.tick$.pipe(startWith(null), // map will perform initial loading even if world not started yet. Handy to preload map
114
- takeUntil(entity.onRemoved$), throttleTime(1000), map(() => entity.position))
112
+ ? this.loadClock.tick$.pipe(startWith(null), // map will perform initial loading even if world not started yet. Handy to preload map
113
+ takeUntil(entity.onRemoved$), map(() => entity.position))
115
114
  : NEVER), map(pos => this.mapGraph.getNearestDummy(this.mapGraphNodes, pos)), tap(node => this._nearestDummy$.next(node)), distinctUntilChanged())
116
115
  .subscribe(currentChunk => {
117
116
  let haveToBeLoaded = new Set();
@@ -143,6 +142,10 @@ export class MapGraph3dEntity extends IRenderable3dEntity {
143
142
  }
144
143
  onRemoved() {
145
144
  super.onRemoved();
145
+ if (this.loadClock) {
146
+ this.loadClock.stop();
147
+ this.loadClock = null;
148
+ }
146
149
  this.loaderCursorEntity$.next(null);
147
150
  }
148
151
  loadChunk(node) {
@@ -51,6 +51,8 @@ export declare class RaycastVehicle3dEntity<VTypeDoc extends VisualTypeDocRepo3D
51
51
  protected readonly wheelLocalRotation: (Point4 | null)[];
52
52
  protected readonly frontWheelsIndices: number[];
53
53
  protected readonly rearWheelsIndices: number[];
54
+ get name(): string;
55
+ set name(value: string);
54
56
  getSpeed(): number;
55
57
  readonly tractionWheelRadius: number;
56
58
  private _steeringAngle;
@@ -93,11 +93,25 @@ export class RaycastVehicle3dEntity extends Entity3d {
93
93
  localRotation = { x: 0, y: 0.707107 * (flip ? 1 : -1), z: 0, w: 0.707107 };
94
94
  }
95
95
  this.wheelLocalRotation.push(localRotation);
96
- this.wheels.push(new Entity3d(entity));
96
+ const wheelEntity = new Entity3d(entity);
97
+ wheelEntity.name = this.name + '__wheel_' + (options.isFront ? 'f' : 'r') + (options.isLeft ? 'l' : 'r');
98
+ this.wheels.push(wheelEntity);
97
99
  }
98
100
  this.addChildren(...this.wheels.filter(x => !!x));
99
101
  this.vehicleComponent.entity = this;
100
102
  }
103
+ get name() {
104
+ return super.name;
105
+ }
106
+ set name(value) {
107
+ const oldName = super.name;
108
+ super.name = value;
109
+ for (const w of this.wheels || []) {
110
+ if (w) {
111
+ w.name = w.name.replace(oldName, value);
112
+ }
113
+ }
114
+ }
101
115
  // m/s
102
116
  getSpeed() {
103
117
  return this.vehicleComponent.wheelSpeed;
@@ -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;
@@ -18,6 +18,7 @@ export declare type VisualTypeDocRepo3D = {
18
18
  loader: IDisplayObject3dComponentLoader;
19
19
  displayObject: IDisplayObject3dComponent;
20
20
  renderer: IRenderer3dComponent;
21
+ rendererExtraOpts: {};
21
22
  camera: ICameraComponent;
22
23
  texture: unknown;
23
24
  };
@@ -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,86 @@
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
+ private readonly _internalTick$;
9
10
  private readonly _tick$;
11
+ /**
12
+ * Observable stream of ticks, emitting an array containing the current time and the tick delta.
13
+ */
10
14
  get tick$(): Observable<[number, number]>;
15
+ /**
16
+ * Checks if the clock is currently running.
17
+ */
11
18
  get isRunning(): boolean;
19
+ /**
20
+ * Checks if the clock is currently paused.
21
+ */
12
22
  get isPaused(): boolean;
23
+ /**
24
+ * Checks if the clock is stopped.
25
+ */
26
+ get isStopped(): boolean;
27
+ /**
28
+ * Gets the time scale of the clock.
29
+ */
30
+ get timeScale(): number;
31
+ /**
32
+ * Sets the time scale of the clock.
33
+ */
34
+ set timeScale(value: number);
35
+ /**
36
+ * Gets the elapsed time since the clock started.
37
+ */
13
38
  get elapsedTime(): number;
39
+ /**
40
+ * Tick rate limiter. If set to 0 - tick rate is unlimited, 15 means "allow at most 15 ticks per second"
41
+ */
42
+ tickRateLimit: number;
14
43
  private startedAt;
15
44
  private oldRelativeTime;
16
45
  private pausedAt;
46
+ private lastStopElapsed;
47
+ private _timeScale;
48
+ private pausedByTimescale;
49
+ private lastFiredTickElapsed;
50
+ /**
51
+ * Constructs a new PausableClock instance.
52
+ * @param autoStart Indicates whether the clock should start automatically upon creation.
53
+ * @param parentClock The parent clock to synchronize with. Defaults to GgGlobalClock instance.
54
+ */
17
55
  constructor(autoStart?: boolean, parentClock?: IClock);
56
+ /**
57
+ * Creates a child clock.
58
+ * @param autoStart Indicates whether the child clock should start automatically.
59
+ * @returns A new instance of PausableClock.
60
+ */
18
61
  createChildClock(autoStart: boolean): PausableClock;
62
+ /**
63
+ * Starts the clock.
64
+ */
19
65
  start(): void;
66
+ /**
67
+ * Stops the clock.
68
+ */
20
69
  stop(): void;
70
+ /**
71
+ * Pauses the clock.
72
+ */
21
73
  pause(): void;
74
+ /**
75
+ * Resumes the clock.
76
+ */
22
77
  resume(): void;
78
+ /**
79
+ * Starts listening for ticks from the parent clock.
80
+ */
23
81
  protected startListeningTicks(): void;
82
+ /**
83
+ * Stops listening for ticks from the parent clock.
84
+ */
24
85
  protected stopListeningTicks(): void;
25
86
  }
@@ -1,40 +1,117 @@
1
- import { Subject } from 'rxjs';
1
+ import { filter, 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;
16
+ this._internalTick$ = new Subject();
11
17
  this._tick$ = new Subject();
12
- // state
18
+ /**
19
+ * Tick rate limiter. If set to 0 - tick rate is unlimited, 15 means "allow at most 15 ticks per second"
20
+ */
21
+ this.tickRateLimit = 0;
22
+ // State variables
13
23
  this.startedAt = -1;
14
24
  this.oldRelativeTime = 0; // "elapsed", emitted on last tick
15
25
  this.pausedAt = -1;
26
+ this.lastStopElapsed = 0;
27
+ this._timeScale = 1;
28
+ this.pausedByTimescale = false;
29
+ this.lastFiredTickElapsed = 0;
16
30
  if (autoStart) {
17
31
  this.start();
18
32
  }
33
+ this._internalTick$
34
+ .pipe(map(([_, newTime]) => [newTime, newTime - this.lastFiredTickElapsed]), filter(([elapsed]) => !this.tickRateLimit ||
35
+ Math.floor((this.lastFiredTickElapsed * this.tickRateLimit) / 1000) <
36
+ Math.floor((elapsed * this.tickRateLimit) / 1000)), tap(([elapsed]) => (this.lastFiredTickElapsed = elapsed)))
37
+ .subscribe(this._tick$);
19
38
  }
39
+ /**
40
+ * Observable stream of ticks, emitting an array containing the current time and the tick delta.
41
+ */
20
42
  get tick$() {
21
- return this._tick$.pipe(map(([oldTime, newTime]) => [newTime, newTime - oldTime]));
43
+ return this._tick$.asObservable();
22
44
  }
45
+ /**
46
+ * Checks if the clock is currently running.
47
+ */
23
48
  get isRunning() {
24
49
  return !!this.tickSub;
25
50
  }
51
+ /**
52
+ * Checks if the clock is currently paused.
53
+ */
26
54
  get isPaused() {
27
55
  return this.pausedAt !== -1;
28
56
  }
57
+ /**
58
+ * Checks if the clock is stopped.
59
+ */
60
+ get isStopped() {
61
+ return this.startedAt === -1;
62
+ }
63
+ /**
64
+ * Gets the time scale of the clock.
65
+ */
66
+ get timeScale() {
67
+ return this._timeScale;
68
+ }
69
+ /**
70
+ * Sets the time scale of the clock.
71
+ */
72
+ set timeScale(value) {
73
+ if (value === this._timeScale && !(this.pausedByTimescale && value !== 0))
74
+ return;
75
+ if (value === 0) {
76
+ if (!this.isPaused) {
77
+ this.pause();
78
+ this.pausedByTimescale = true;
79
+ }
80
+ return;
81
+ }
82
+ if (this.isPaused && this.pausedByTimescale) {
83
+ this.resume();
84
+ this.pausedByTimescale = false;
85
+ }
86
+ if (!this.isStopped) {
87
+ const cur = this.isPaused ? this.pausedAt : this.parentClock.elapsedTime;
88
+ this.startedAt = cur - ((cur - this.startedAt) * this.timeScale) / value;
89
+ }
90
+ this._timeScale = value;
91
+ }
92
+ /**
93
+ * Gets the elapsed time since the clock started.
94
+ */
29
95
  get elapsedTime() {
96
+ if (this.isStopped) {
97
+ return this.lastStopElapsed;
98
+ }
30
99
  if (this.isPaused) {
31
- return this.pausedAt - this.startedAt;
100
+ return this._timeScale * (this.pausedAt - this.startedAt);
32
101
  }
33
- return this.parentClock.elapsedTime - this.startedAt;
102
+ return this._timeScale * (this.parentClock.elapsedTime - this.startedAt);
34
103
  }
104
+ /**
105
+ * Creates a child clock.
106
+ * @param autoStart Indicates whether the child clock should start automatically.
107
+ * @returns A new instance of PausableClock.
108
+ */
35
109
  createChildClock(autoStart) {
36
110
  return new PausableClock(autoStart, this);
37
111
  }
112
+ /**
113
+ * Starts the clock.
114
+ */
38
115
  start() {
39
116
  if (this.isRunning) {
40
117
  return;
@@ -43,14 +120,25 @@ export class PausableClock {
43
120
  this.startedAt = this.parentClock.elapsedTime;
44
121
  this.startListeningTicks();
45
122
  }
123
+ /**
124
+ * Stops the clock.
125
+ */
46
126
  stop() {
47
127
  this.stopListeningTicks();
128
+ this.lastStopElapsed = this.elapsedTime;
48
129
  this.startedAt = this.pausedAt = -1;
49
130
  }
131
+ /**
132
+ * Pauses the clock.
133
+ */
50
134
  pause() {
51
135
  this.stopListeningTicks();
52
136
  this.pausedAt = this.parentClock.elapsedTime;
137
+ this.pausedByTimescale = false;
53
138
  }
139
+ /**
140
+ * Resumes the clock.
141
+ */
54
142
  resume() {
55
143
  if (this.isRunning || this.pausedAt == -1) {
56
144
  return;
@@ -59,14 +147,20 @@ export class PausableClock {
59
147
  this.pausedAt = -1;
60
148
  this.startListeningTicks();
61
149
  }
150
+ /**
151
+ * Starts listening for ticks from the parent clock.
152
+ */
62
153
  startListeningTicks() {
63
154
  if (this.tickSub) {
64
155
  throw new Error('Clock is already ticking!');
65
156
  }
66
157
  this.tickSub = this.parentClock.tick$
67
- .pipe(map(([parentElapsed, _]) => [this.oldRelativeTime, parentElapsed - this.startedAt]), tap(([_, cur]) => (this.oldRelativeTime = cur)))
68
- .subscribe(this._tick$);
158
+ .pipe(map(([_, d]) => [this.oldRelativeTime, this.oldRelativeTime + d * this.timeScale]), tap(([_, cur]) => (this.oldRelativeTime = cur)))
159
+ .subscribe(this._internalTick$);
69
160
  }
161
+ /**
162
+ * Stops listening for ticks from the parent clock.
163
+ */
70
164
  stopListeningTicks() {
71
165
  var _a;
72
166
  (_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;