@gg-web-engine/core 0.0.1 → 0.0.3

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 (103) hide show
  1. package/dist/2d/entities/controllers/entity-2d-positioning.animator.d.ts +16 -0
  2. package/dist/2d/entities/controllers/entity-2d-positioning.animator.js +26 -0
  3. package/dist/2d/entities/gg-2d-entity.d.ts +9 -1
  4. package/dist/2d/entities/gg-2d-entity.js +50 -17
  5. package/dist/2d/gg-2d-world.d.ts +3 -0
  6. package/dist/2d/gg-2d-world.js +11 -0
  7. package/dist/3d/controllers/car-keyboard.controller.d.ts +10 -10
  8. package/dist/3d/controllers/car-keyboard.controller.js +21 -20
  9. package/dist/3d/controllers/free-camera.controller.d.ts +8 -8
  10. package/dist/3d/entities/controllers/camera-3d.animator.d.ts +17 -0
  11. package/dist/3d/entities/controllers/camera-3d.animator.js +32 -0
  12. package/dist/3d/entities/controllers/entity-3d-positioning.animator.d.ts +16 -0
  13. package/dist/3d/entities/controllers/entity-3d-positioning.animator.js +26 -0
  14. package/dist/3d/entities/gg-3d-camera.entity.d.ts +2 -0
  15. package/dist/3d/entities/gg-3d-camera.entity.js +6 -0
  16. package/dist/3d/entities/gg-3d-entity.d.ts +4 -1
  17. package/dist/3d/entities/gg-3d-entity.js +19 -10
  18. package/dist/3d/entities/gg-3d-map-graph.entity.d.ts +7 -5
  19. package/dist/3d/entities/gg-3d-map-graph.entity.js +1 -1
  20. package/dist/3d/entities/gg-3d-raycast-vehicle.entity.d.ts +6 -5
  21. package/dist/3d/gg-3d-world.d.ts +3 -0
  22. package/dist/3d/gg-3d-world.js +12 -0
  23. package/dist/base/clock/clock.d.ts +25 -0
  24. package/dist/base/clock/clock.js +79 -0
  25. package/dist/base/clock/global-clock.d.ts +15 -0
  26. package/dist/base/clock/global-clock.js +36 -0
  27. package/dist/base/entities/controllers/animation-mixer.d.ts +83 -0
  28. package/dist/base/entities/controllers/animation-mixer.js +152 -0
  29. package/dist/base/entities/gg-entity.d.ts +4 -1
  30. package/dist/base/entities/gg-entity.js +7 -0
  31. package/dist/base/entities/inline-controller.js +0 -1
  32. package/dist/base/gg-viewport-manager.d.ts +4 -0
  33. package/dist/base/gg-world.d.ts +3 -1
  34. package/dist/base/gg-world.js +6 -5
  35. package/dist/base/math/matrix4.js +3 -3
  36. package/dist/base/math/point2.js +1 -1
  37. package/dist/base/math/point3.js +1 -1
  38. package/dist/base/math/quaternion.d.ts +83 -9
  39. package/dist/base/math/quaternion.js +93 -10
  40. package/dist/base/models/points.d.ts +9 -9
  41. package/dist/index.d.ts +6 -2
  42. package/dist/index.js +6 -2
  43. package/package.json +1 -1
  44. package/.prettierrc +0 -8
  45. package/src/2d/entities/gg-2d-entity.ts +0 -86
  46. package/src/2d/entities/gg-2d-trigger.entity.ts +0 -17
  47. package/src/2d/entities/gg-positionable-2d-entity.ts +0 -16
  48. package/src/2d/factories.ts +0 -20
  49. package/src/2d/gg-2d-world.ts +0 -32
  50. package/src/2d/interfaces.ts +0 -31
  51. package/src/2d/models/body-options.ts +0 -3
  52. package/src/2d/models/shapes.ts +0 -7
  53. package/src/3d/controllers/car-keyboard.controller.ts +0 -128
  54. package/src/3d/controllers/free-camera.controller.ts +0 -86
  55. package/src/3d/entities/controllers/entity-motion.controller.ts +0 -122
  56. package/src/3d/entities/gg-3d-camera.entity.ts +0 -8
  57. package/src/3d/entities/gg-3d-entity.ts +0 -116
  58. package/src/3d/entities/gg-3d-map-graph.entity.ts +0 -204
  59. package/src/3d/entities/gg-3d-raycast-vehicle.entity.ts +0 -468
  60. package/src/3d/entities/gg-3d-trigger.entity.ts +0 -22
  61. package/src/3d/entities/gg-positionable-3d-entity.ts +0 -31
  62. package/src/3d/factories.ts +0 -29
  63. package/src/3d/gg-3d-world.ts +0 -36
  64. package/src/3d/interfaces.ts +0 -96
  65. package/src/3d/loader.ts +0 -165
  66. package/src/3d/models/body-options.ts +0 -3
  67. package/src/3d/models/gg-meta.ts +0 -12
  68. package/src/3d/models/shapes.ts +0 -13
  69. package/src/base/clock.ts +0 -97
  70. package/src/base/controllers/common.ts +0 -37
  71. package/src/base/controllers/i-controller.ts +0 -5
  72. package/src/base/controllers/keyboard.controller.ts +0 -109
  73. package/src/base/controllers/mouse.controller.ts +0 -73
  74. package/src/base/data-structures/graph.ts +0 -125
  75. package/src/base/entities/base-gg-renderer.ts +0 -77
  76. package/src/base/entities/gg-entity.ts +0 -61
  77. package/src/base/entities/gg-positionable-entity.ts +0 -64
  78. package/src/base/entities/inline-controller.ts +0 -31
  79. package/src/base/entities/interfaces/i-tick-listener.ts +0 -15
  80. package/src/base/gg-static.ts +0 -30
  81. package/src/base/gg-viewport-manager.ts +0 -122
  82. package/src/base/gg-viewport.ts +0 -159
  83. package/src/base/gg-world.ts +0 -198
  84. package/src/base/interfaces/gg-body.ts +0 -23
  85. package/src/base/interfaces/gg-debug-physics-drawer.ts +0 -10
  86. package/src/base/interfaces/gg-object.ts +0 -26
  87. package/src/base/interfaces/gg-physics-world.ts +0 -24
  88. package/src/base/interfaces/gg-trigger.ts +0 -10
  89. package/src/base/interfaces/gg-visual-scene.ts +0 -11
  90. package/src/base/math/box.ts +0 -19
  91. package/src/base/math/matrix4.ts +0 -29
  92. package/src/base/math/numbers.ts +0 -10
  93. package/src/base/math/point2.ts +0 -43
  94. package/src/base/math/point3.ts +0 -119
  95. package/src/base/math/quaternion.ts +0 -172
  96. package/src/base/models/axis-directions.ts +0 -3
  97. package/src/base/models/body-options.ts +0 -6
  98. package/src/base/models/geometry-nodes.ts +0 -5
  99. package/src/base/models/points.ts +0 -3
  100. package/src/base/ui/gg-console.ui.ts +0 -165
  101. package/src/base/ui/gg-debugger.ui.ts +0 -69
  102. package/src/index.ts +0 -53
  103. package/test/base/data-structures/graph.spec.ts +0 -76
@@ -1,128 +0,0 @@
1
- import { BehaviorSubject, combineLatest, interval, pipe, Subject, takeUntil, filter } from 'rxjs';
2
- import { distinctUntilChanged, map, pairwise, startWith, switchMap, take, tap } from 'rxjs/operators';
3
- import { IController } from '../../base/controllers/i-controller';
4
- import { KeyboardController } from '../../base/controllers/keyboard.controller';
5
- import { Gg3dRaycastVehicleEntity } from '../entities/gg-3d-raycast-vehicle.entity';
6
- import { bindDirectionKeys, DirectionKeymap, DirectionOutput } from '../../base/controllers/common';
7
-
8
- // TODO pass as settings
9
- // TODO smooth y?
10
- const TICKER_INTERVAL = 16;
11
- const TICKER_MAX_STEPS = 10;
12
-
13
- type CarKeyboardControllerOptions = {
14
- keymap: DirectionKeymap;
15
- gearUpDownKeys: [string, string];
16
- };
17
-
18
- export class CarKeyboardController implements IController {
19
- // emits values -1 - 1; -1 = full turn left; 1 = full turn right
20
- private readonly x$: BehaviorSubject<number> = new BehaviorSubject<number>(0);
21
- // emits values -1 - 1; -1 = full brake; 1 = full acceleration
22
- private readonly y$: BehaviorSubject<number> = new BehaviorSubject<number>(0);
23
-
24
- private lastX: number = 0;
25
- private readonly stop$: Subject<void> = new Subject<void>();
26
-
27
- protected readonly car$: BehaviorSubject<Gg3dRaycastVehicleEntity> = new BehaviorSubject<Gg3dRaycastVehicleEntity>(
28
- null!,
29
- );
30
-
31
- pairTickerPipe = pipe(
32
- startWith(0),
33
- pairwise<number>(),
34
- map(([beforeValue, afterValue]) => [this.lastX, afterValue] as [number, number]),
35
- switchMap(([beforeValue, afterValue]: [number, number]) =>
36
- interval(TICKER_INTERVAL).pipe(
37
- take(TICKER_MAX_STEPS),
38
- map(count => ++count),
39
- map(
40
- count =>
41
- beforeValue * ((TICKER_MAX_STEPS - count) / TICKER_MAX_STEPS) + afterValue * (count / TICKER_MAX_STEPS),
42
- ),
43
- tap(x => {
44
- this.lastX = x;
45
- }),
46
- ),
47
- ),
48
- );
49
-
50
- public set car(value: Gg3dRaycastVehicleEntity) {
51
- this.car$.next(value);
52
- }
53
-
54
- public get car(): Gg3dRaycastVehicleEntity {
55
- return this.car$.getValue();
56
- }
57
-
58
- constructor(
59
- private readonly keyboardController: KeyboardController,
60
- car: Gg3dRaycastVehicleEntity,
61
- private readonly options: CarKeyboardControllerOptions = { keymap: 'arrows', gearUpDownKeys: ['KeyA', 'KeyZ'] },
62
- ) {
63
- this.car$.next(car);
64
- }
65
-
66
- async start(): Promise<void> {
67
- let moveDirection: DirectionOutput = {};
68
- bindDirectionKeys(this.keyboardController, this.options.keymap)
69
- .pipe(takeUntil(this.stop$))
70
- .subscribe(d => {
71
- moveDirection = d;
72
- });
73
- this.car$
74
- .pipe(
75
- takeUntil(this.stop$),
76
- switchMap(c => c.tick$),
77
- takeUntil(this.stop$),
78
- map(() => {
79
- const direction = [0, 0];
80
- if (moveDirection.leftRight !== undefined) direction[0] = moveDirection.leftRight ? -1 : 1;
81
- if (moveDirection.upDown !== undefined) direction[1] = moveDirection.upDown ? 1 : -1;
82
- return direction;
83
- }),
84
- )
85
- .subscribe(([x, y]) => {
86
- this.x$.next(x);
87
- this.y$.next(y);
88
- });
89
- combineLatest([
90
- this.x$.pipe(takeUntil(this.stop$), distinctUntilChanged(), this.pairTickerPipe),
91
- this.y$.pipe(takeUntil(this.stop$), distinctUntilChanged()),
92
- ]).subscribe(([x, y]) => {
93
- this.car$.getValue().setXAxisControlValue(x);
94
- this.car$.getValue().setYAxisControlValue(y);
95
- });
96
- this.keyboardController
97
- .bind(this.options.gearUpDownKeys[0])
98
- .pipe(
99
- takeUntil(this.stop$),
100
- filter(x => !!x),
101
- )
102
- .subscribe(() => {
103
- if (
104
- this.car$.getValue() &&
105
- (!this.car$.getValue().carProperties.transmission.isAuto || this.car$.getValue().gear <= 0)
106
- ) {
107
- this.car$.getValue().gear++;
108
- }
109
- });
110
- this.keyboardController
111
- .bind(this.options.gearUpDownKeys[1])
112
- .pipe(
113
- takeUntil(this.stop$),
114
- filter(x => !!x),
115
- )
116
- .subscribe(() => {
117
- if (this.car$.getValue().carProperties.transmission.isAuto && this.car.gear > 1) {
118
- this.car$.getValue().gear = 0;
119
- } else {
120
- this.car$.getValue().gear--;
121
- }
122
- });
123
- }
124
-
125
- async stop(): Promise<void> {
126
- this.stop$.next();
127
- }
128
- }
@@ -1,86 +0,0 @@
1
- import { IController } from '../../base/controllers/i-controller';
2
- import { combineLatest, Subject, takeUntil } from 'rxjs';
3
- import { MouseController, MouseControllerOptions } from '../../base/controllers/mouse.controller';
4
- import { Pnt3 } from '../../base/math/point3';
5
- import { Gg3dCameraEntity } from '../entities/gg-3d-camera.entity';
6
- import { KeyboardController } from '../../base/controllers/keyboard.controller';
7
- import { Pnt2 } from '../../base/math/point2';
8
- import { Point2 } from '../../base/models/points';
9
- import { Qtrn } from '../../base/math/quaternion';
10
- import { bindDirectionKeys, DirectionKeymap, DirectionOutput } from '../../base/controllers/common';
11
-
12
- type FreeCameraControllerOptions = {
13
- keymap: DirectionKeymap;
14
- movementOptions: {
15
- speed: number;
16
- };
17
- mouseOptions: MouseControllerOptions;
18
- };
19
-
20
- export class FreeCameraController implements IController {
21
- private readonly mController: MouseController;
22
- private readonly stop$: Subject<void> = new Subject<void>();
23
-
24
- constructor(
25
- private readonly keyboardController: KeyboardController,
26
- private readonly camera: Gg3dCameraEntity,
27
- private readonly options: FreeCameraControllerOptions = {
28
- keymap: 'wasd',
29
- movementOptions: { speed: 0.5 },
30
- mouseOptions: {},
31
- },
32
- ) {
33
- this.mController = new MouseController(options.mouseOptions);
34
- }
35
-
36
- async start(): Promise<void> {
37
- let controls: { direction: DirectionOutput; rest: boolean[] } = { direction: {}, rest: [] };
38
- const keys = ['KeyE', 'KeyQ'];
39
- if (this.camera.object3D.supportsFov) {
40
- keys.push('KeyZ', 'KeyC');
41
- }
42
- bindDirectionKeys(this.keyboardController, this.options.keymap)
43
- .pipe(takeUntil(this.stop$))
44
- .subscribe(d => {
45
- controls.direction = d;
46
- });
47
- combineLatest(keys.map(c => this.keyboardController.bind(c)))
48
- .pipe(takeUntil(this.stop$))
49
- .subscribe((d: boolean[]) => {
50
- controls.rest = d;
51
- });
52
-
53
- let rotationDelta: Point2 = { x: 0, y: 0 };
54
- this.mController.delta$.pipe(takeUntil(this.stop$)).subscribe(delta => {
55
- rotationDelta = Pnt2.add(rotationDelta, delta);
56
- });
57
-
58
- this.camera.tick$.pipe(takeUntil(this.stop$)).subscribe(() => {
59
- let translateVector = { x: 0, y: 0, z: 0 };
60
- const [u, d, zo, zi] = controls.rest;
61
- if (controls.direction.upDown !== undefined) translateVector.z = controls.direction.upDown ? -1 : 1;
62
- if (controls.direction.leftRight !== undefined) translateVector.x = controls.direction.leftRight ? -1 : 1;
63
- if (u != d) translateVector.y = d ? -1 : 1;
64
- if (zo != zi) this.camera.object3D.fov += zo ? 1 : -1;
65
- this.camera.position = Pnt3.add(
66
- this.camera.position,
67
- Pnt3.rot(Pnt3.scalarMult(Pnt3.norm(translateVector), this.options.movementOptions.speed), this.camera.rotation),
68
- );
69
- if (rotationDelta.x != 0 || rotationDelta.y != 0) {
70
- this.camera.rotation = Qtrn.combineRotations(
71
- Qtrn.fromAngle({ x: 0, y: 0, z: 1 }, -rotationDelta.x / 300),
72
- this.camera.rotation,
73
- Qtrn.fromAngle({ x: 1, y: 0, z: 0 }, -rotationDelta.y / 300),
74
- );
75
- rotationDelta = { x: 0, y: 0 };
76
- }
77
- });
78
-
79
- await this.mController.start();
80
- }
81
-
82
- async stop(unlockPointer: boolean = true): Promise<void> {
83
- this.stop$.next();
84
- await this.mController.stop(unlockPointer);
85
- }
86
- }
@@ -1,122 +0,0 @@
1
- import { GgEntity } from '../../../base/entities/gg-entity';
2
- import { ITickListener } from '../../../base/entities/interfaces/i-tick-listener';
3
- import { Subject, Subscription, takeUntil } from 'rxjs';
4
- import { Gg3dWorld } from '../../gg-3d-world';
5
- import { GgPositionable3dEntity } from '../gg-positionable-3d-entity';
6
- import { Point3, Point4 } from '../../../base/models/points';
7
- import { Pnt3 } from '../../../base/math/point3';
8
- import { Qtrn } from '../../../base/math/quaternion';
9
-
10
- export type MotionControlFuncReturn = { position: Point3; rotation: Point4; customParameters: { [key: string]: any } };
11
- export type MotionControlFunction = (delta: number) => MotionControlFuncReturn;
12
-
13
- export class EntityMotionController extends GgEntity implements ITickListener {
14
- get motionControlFunction(): MotionControlFunction {
15
- return this._motionControlFunction;
16
- }
17
-
18
- set motionControlFunction(value: MotionControlFunction) {
19
- if (this.currentTransition) {
20
- this.currentTransition.unsubscribe();
21
- this.currentTransition = null;
22
- }
23
- this._motionControlFunction = value;
24
- }
25
- public readonly tick$: Subject<[number, number]> = new Subject<[number, number]>();
26
-
27
- protected readonly removed$: Subject<void> = new Subject<void>();
28
- public readonly tickOrder: number = 750;
29
-
30
- constructor(
31
- public target: GgPositionable3dEntity,
32
- protected _motionControlFunction: MotionControlFunction,
33
- public customParametersHandleFunc: (target: GgPositionable3dEntity, params: any) => void = () => {},
34
- ) {
35
- super();
36
- }
37
-
38
- protected lastValue: MotionControlFuncReturn | undefined;
39
- private currentTransition: Subscription | null = null;
40
-
41
- // smoothly set controller function, animate from some determined state
42
- transitFromStaticState(
43
- state: MotionControlFuncReturn,
44
- newFunc: MotionControlFunction,
45
- transitionDuration: number,
46
- easing: (t: number) => number = x => x,
47
- customParametersLerpFunc: (a: any, b: any, t: number) => any = (a, b, t) => b,
48
- ) {
49
- this.lastValue = state;
50
- this._motionControlFunction = () => state;
51
- return this.transitControlFunction(newFunc, transitionDuration, easing, customParametersLerpFunc);
52
- }
53
-
54
- // smoothly change controller function
55
- transitControlFunction(
56
- newFunc: MotionControlFunction,
57
- transitionDuration: number,
58
- easing: (t: number) => number = x => x,
59
- customParametersLerpFunc: (a: any, b: any, t: number) => any = (a, b, t) => b,
60
- ) {
61
- let oldControlFunc: MotionControlFunction;
62
- if (this.currentTransition) {
63
- this.currentTransition.unsubscribe();
64
- this.currentTransition = null;
65
- if (this.lastValue) {
66
- oldControlFunc = () => this.lastValue!;
67
- } else {
68
- this._motionControlFunction = newFunc;
69
- return;
70
- }
71
- } else {
72
- oldControlFunc = this._motionControlFunction;
73
- }
74
- let k = 0;
75
- this._motionControlFunction = delta => {
76
- const oldRes = oldControlFunc(delta);
77
- const newRes = newFunc(delta);
78
- return {
79
- customParameters: customParametersLerpFunc(oldRes.customParameters, newRes.customParameters, k),
80
- position: Pnt3.lerp(oldRes.position, newRes.position, k),
81
- rotation: Qtrn.slerp(oldRes.rotation, newRes.rotation, k),
82
- };
83
- };
84
- let startTime: number | undefined;
85
- this.currentTransition = this.tick$.pipe(takeUntil(this.removed$)).subscribe(([elapsed, _]) => {
86
- if (startTime === undefined) {
87
- startTime = elapsed;
88
- } else {
89
- k = (elapsed - startTime) / transitionDuration;
90
- if (k >= 1) {
91
- k = 1;
92
- this.currentTransition!.unsubscribe();
93
- this.currentTransition = null;
94
- this._motionControlFunction = newFunc;
95
- } else {
96
- k = easing(k);
97
- }
98
- }
99
- });
100
- }
101
-
102
- onSpawned(world: Gg3dWorld) {
103
- super.onSpawned(world);
104
- this.tick$.pipe(takeUntil(this.removed$)).subscribe(([_, delta]) => {
105
- this.lastValue = this._motionControlFunction(delta);
106
- this.target.position = this.lastValue.position;
107
- this.target.rotation = this.lastValue.rotation;
108
- this.customParametersHandleFunc(this.target, this.lastValue.customParameters);
109
- });
110
- }
111
-
112
- onRemoved() {
113
- super.onRemoved();
114
- this.removed$.next();
115
- }
116
-
117
- dispose(): void {
118
- super.dispose();
119
- this.tick$.unsubscribe();
120
- this.tick$.complete();
121
- }
122
- }
@@ -1,8 +0,0 @@
1
- import { IGg3dBody, IGg3dCamera } from '../interfaces';
2
- import { Gg3dEntity } from './gg-3d-entity';
3
-
4
- export class Gg3dCameraEntity<T extends IGg3dCamera = IGg3dCamera> extends Gg3dEntity {
5
- constructor(public readonly object3D: T, public readonly objectBody: IGg3dBody | null = null) {
6
- super(object3D, objectBody);
7
- }
8
- }
@@ -1,116 +0,0 @@
1
- import { Subject } from 'rxjs';
2
- import { GgPositionable3dEntity } from './gg-positionable-3d-entity';
3
- import { ITickListener } from '../../base/entities/interfaces/i-tick-listener';
4
- import { Point3, Point4 } from '../../base/models/points';
5
- import { Gg3dWorld } from '../gg-3d-world';
6
- import { IGg3dBody, IGg3dObject } from '../interfaces';
7
-
8
- export class Gg3dEntity extends GgPositionable3dEntity implements ITickListener {
9
- public readonly tick$: Subject<[number, number]> = new Subject<[number, number]>();
10
- public readonly tickOrder = 750;
11
-
12
- public get position(): Point3 {
13
- return super.position;
14
- }
15
-
16
- set position(value: Point3) {
17
- if (this.object3D) {
18
- this.object3D.position = value;
19
- }
20
- if (this.objectBody) {
21
- this.objectBody.position = value;
22
- }
23
- super.position = value;
24
- }
25
-
26
- public get rotation(): Point4 {
27
- return super.rotation;
28
- }
29
-
30
- set rotation(value: Point4) {
31
- if (this.object3D) {
32
- this.object3D.rotation = value;
33
- }
34
- if (this.objectBody) {
35
- this.objectBody.rotation = value;
36
- }
37
- super.rotation = value;
38
- }
39
-
40
- public get scale(): Point3 {
41
- return super.scale;
42
- }
43
-
44
- set scale(value: Point3) {
45
- if (this.object3D) {
46
- this.object3D.scale = value;
47
- }
48
- if (this.objectBody) {
49
- this.objectBody.scale = value;
50
- }
51
- super.scale = value;
52
- }
53
-
54
- public get visible(): boolean {
55
- return !!this.object3D?.visible;
56
- }
57
-
58
- set visible(value: boolean) {
59
- if (this.object3D) {
60
- this.object3D.visible = value;
61
- }
62
- for (const entity of this._children) {
63
- if (entity instanceof Gg3dEntity && entity.object3D) {
64
- entity.object3D.visible = value;
65
- }
66
- }
67
- }
68
-
69
- protected runTransformBinding(objectBody: IGg3dBody, object3D: IGg3dObject): void {
70
- // bind physics body transform to mesh transform
71
- const pos = objectBody.position;
72
- const quat = objectBody.rotation;
73
- object3D.position = pos;
74
- object3D.rotation = quat;
75
- this._position$.next(pos);
76
- this._rotation$.next(quat);
77
- }
78
-
79
- constructor(public readonly object3D: IGg3dObject | null, public readonly objectBody: IGg3dBody | null = null) {
80
- super();
81
- if (objectBody && object3D) {
82
- this.tick$.subscribe(() => {
83
- this.runTransformBinding(objectBody, object3D);
84
- });
85
- this.runTransformBinding(objectBody, object3D);
86
- } else if (!objectBody && !object3D) {
87
- throw new Error('Cannot create entity without a mesh and a body');
88
- }
89
- if (objectBody) {
90
- objectBody.entity = this;
91
- }
92
- }
93
-
94
- onSpawned(world: Gg3dWorld) {
95
- super.onSpawned(world);
96
- this.object3D?.addToWorld(world.visualScene);
97
- this.objectBody?.addToWorld(world.physicsWorld);
98
- }
99
-
100
- onRemoved() {
101
- this.object3D?.removeFromWorld(this.world!.visualScene);
102
- this.objectBody?.removeFromWorld(this.world!.physicsWorld);
103
- super.onRemoved();
104
- }
105
-
106
- dispose(): void {
107
- super.dispose();
108
- this.tick$.unsubscribe();
109
- if (this.object3D) {
110
- this.object3D.dispose();
111
- }
112
- if (this.objectBody) {
113
- this.objectBody.dispose();
114
- }
115
- }
116
- }
@@ -1,204 +0,0 @@
1
- import { BehaviorSubject, NEVER, Observable, startWith, Subject, switchMap } from 'rxjs';
2
- import { distinctUntilChanged, map, tap, throttleTime } from 'rxjs/operators';
3
- import { Point3, Point4 } from '../../base/models/points';
4
- import { Graph } from '../../base/data-structures/graph';
5
- import { GgEntity } from '../../base/entities/gg-entity';
6
- import { ITickListener } from '../../base/entities/interfaces/i-tick-listener';
7
- import { GgPositionable3dEntity } from './gg-positionable-3d-entity';
8
- import { Gg3dWorld } from '../gg-3d-world';
9
- import { Gg3dEntity } from './gg-3d-entity';
10
- import { LoadOptions, LoadResultWithProps } from '../loader';
11
-
12
- type MapGraphNodeType = {
13
- path: string;
14
- position: Point3;
15
- rotation?: Point4;
16
- loadOptions: Partial<Omit<LoadOptions, 'position' | 'rotation'>>;
17
- };
18
-
19
- export class MapGraph extends Graph<MapGraphNodeType> {
20
- static fromMapArray(array: MapGraphNodeType[]): MapGraph {
21
- const root = new MapGraph(array[0]);
22
- let tail = root;
23
- for (let i = 1; i < array.length; i++) {
24
- const newTail = new MapGraph(array[i]);
25
- tail.addAdjacent(newTail);
26
- tail = newTail;
27
- }
28
- return root;
29
- }
30
- static fromMapSquareGrid(grid: MapGraphNodeType[][]): MapGraph {
31
- const nodes = grid.map(sgrid => sgrid.map(item => new MapGraph(item)));
32
- // bind them
33
- for (let j = 0; j < nodes.length; j++) {
34
- for (let i = 0; i < nodes.length; i++) {
35
- if (i > 0) {
36
- nodes[j][i].addAdjacent(nodes[j][i - 1]);
37
- }
38
- if (j > 0) {
39
- nodes[j][i].addAdjacent(nodes[j - 1][i]);
40
- }
41
- }
42
- }
43
- return nodes[0][0];
44
- }
45
- public getNearestDummy(thisNodes: Graph<MapGraphNodeType>[], cursor: Point3): Graph<MapGraphNodeType> {
46
- let min = Infinity;
47
- let node: Graph<MapGraphNodeType> = this;
48
- // TODO should be heavily optimized with kd-tree-javascript, but building spatial index takes too much time. Serialize to file?
49
- thisNodes.forEach(n => {
50
- let dist = Math.sqrt(
51
- Math.pow(cursor.x - n.data.position.x, 2) +
52
- Math.pow(cursor.y - n.data.position.y, 2) +
53
- Math.pow(cursor.z - n.data.position.z, 2),
54
- );
55
- if (dist < min) {
56
- min = dist;
57
- node = n;
58
- }
59
- });
60
- return node;
61
- }
62
-
63
- // TODO return iterable?
64
- nodes(): MapGraph[] {
65
- return Array.from(this.walkRead(-1)) as MapGraph[];
66
- }
67
- }
68
-
69
- export type Gg3dMapGraphEntityOptions = {
70
- // depth in tree to load. 0 means load only the nearest node, 1 means nearest + all of it's neighbours etc.
71
- loadDepth: number;
72
- // additional depth, means unload delay. Nodes with this depth won't load, but if already loaded, will not be destroyed
73
- inertia: number;
74
- };
75
-
76
- const defaultOptions: Gg3dMapGraphEntityOptions = {
77
- loadDepth: 5,
78
- inertia: 0,
79
- };
80
-
81
- export class Gg3dMapGraphEntity extends GgEntity implements ITickListener {
82
- public readonly tick$: Subject<[number, number]> = new Subject<[number, number]>();
83
- public readonly tickOrder = 1500;
84
-
85
- public readonly loaderCursorEntity$: BehaviorSubject<GgPositionable3dEntity | null> =
86
- new BehaviorSubject<GgPositionable3dEntity | null>(null);
87
- readonly loaded: Map<MapGraphNodeType, GgPositionable3dEntity[]> = new Map();
88
-
89
- private _initialLoadComplete$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
90
- public get initialLoadComplete$(): Observable<boolean> {
91
- return this._initialLoadComplete$.asObservable();
92
- }
93
-
94
- private _nearestDummy$: BehaviorSubject<Graph<MapGraphNodeType> | null> =
95
- new BehaviorSubject<Graph<MapGraphNodeType> | null>(null);
96
- public get nearestDummy(): Graph<MapGraphNodeType> | null {
97
- return this._nearestDummy$.getValue();
98
- }
99
-
100
- private _chunkLoaded$: Subject<[LoadResultWithProps, { position: Point3; rotation: Point4 }]> = new Subject<
101
- [LoadResultWithProps, { position: Point3; rotation: Point4 }]
102
- >();
103
- public get chunkLoaded$(): Observable<[LoadResultWithProps, { position: Point3; rotation: Point4 }]> {
104
- return this._chunkLoaded$.asObservable();
105
- }
106
-
107
- get world(): Gg3dWorld | null {
108
- return this._world;
109
- }
110
- protected _world: Gg3dWorld | null = null;
111
- private readonly mapGraphNodes: MapGraph[];
112
-
113
- protected readonly options: Gg3dMapGraphEntityOptions;
114
-
115
- constructor(public readonly mapGraph: MapGraph, options: Partial<Gg3dMapGraphEntityOptions> = {}) {
116
- super();
117
- this.options = { ...defaultOptions, ...options };
118
- this.mapGraphNodes = mapGraph.nodes();
119
- }
120
-
121
- onSpawned(world: Gg3dWorld) {
122
- super.onSpawned(world);
123
- // TODO takeUntil removed from world?
124
- this.loaderCursorEntity$
125
- .pipe(
126
- switchMap(entity =>
127
- entity
128
- ? this.tick$.pipe(
129
- startWith(null), // map will perform initial loading even if world not started yet. Handy to preload map
130
- throttleTime(1000),
131
- map(() => entity.position),
132
- )
133
- : NEVER,
134
- ),
135
- map(pos => this.mapGraph.getNearestDummy(this.mapGraphNodes, pos)),
136
- tap(node => this._nearestDummy$.next(node)),
137
- distinctUntilChanged(),
138
- )
139
- .subscribe(currentChunk => {
140
- let haveToBeLoaded: Set<MapGraphNodeType> = new Set();
141
- let canBeLoaded: Set<MapGraphNodeType>;
142
- if (this.options.inertia > 0) {
143
- canBeLoaded = new Set();
144
- const nodes = currentChunk.walkReadPreserveDepth(this.options.loadDepth + this.options.inertia);
145
- for (let distance = 0; distance < nodes.length; distance++) {
146
- nodes[distance].forEach(node => canBeLoaded.add(node.data));
147
- if (distance <= this.options.loadDepth) {
148
- nodes[distance].forEach(node => haveToBeLoaded.add(node.data));
149
- }
150
- }
151
- } else {
152
- currentChunk.walkRead(this.options.loadDepth).forEach(node => haveToBeLoaded.add(node.data));
153
- canBeLoaded = haveToBeLoaded;
154
- }
155
- for (const loadedNode of this.loaded.keys()) {
156
- if (!canBeLoaded.has(loadedNode)) {
157
- this.disposeChunk(loadedNode);
158
- } else {
159
- haveToBeLoaded.delete(loadedNode);
160
- }
161
- }
162
- Promise.all(Array.from(haveToBeLoaded.keys()).map(n => this.loadChunk(n))).then(() =>
163
- this._initialLoadComplete$.next(true),
164
- );
165
- });
166
- }
167
-
168
- onRemoved() {
169
- super.onRemoved();
170
- this.loaderCursorEntity$.next(null);
171
- }
172
-
173
- protected async loadChunk(node: MapGraphNodeType): Promise<Gg3dEntity[]> {
174
- const loaded = await this.world!.loader.loadGgGlb(node.path, {
175
- position: node.position,
176
- rotation: node.rotation || { x: 0, y: 0, z: 0, w: 1 },
177
- ...node.loadOptions,
178
- });
179
- const entities = [
180
- ...loaded.entities,
181
- ...(loaded.props || [])
182
- .map(p => p.entities)
183
- .reduce((p, c) => {
184
- p.push(...c);
185
- return p;
186
- }, []),
187
- ];
188
- this.loaded.set(node, entities);
189
- this.addChildren(...entities);
190
- this._chunkLoaded$.next([
191
- loaded,
192
- { position: node.position, rotation: node.rotation || { x: 0, y: 0, z: 0, w: 1 } },
193
- ]);
194
- return entities;
195
- }
196
-
197
- protected disposeChunk(node: MapGraphNodeType) {
198
- if (!this.loaded.has(node)) {
199
- return;
200
- }
201
- this.removeChildren(this.loaded.get(node)!, true);
202
- this.loaded.delete(node);
203
- }
204
- }