@gg-web-engine/core 0.0.49 → 0.0.57

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/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/body-options.d.ts +4 -4
  6. package/dist/2d/models/body-options.js +6 -1
  7. package/dist/3d/entities/controllers/input/free-camera.controller.d.ts +9 -1
  8. package/dist/3d/entities/controllers/input/free-camera.controller.js +45 -12
  9. package/dist/3d/entities/controllers/input/orbit-camera.controller.d.ts +9 -8
  10. package/dist/3d/entities/controllers/input/orbit-camera.controller.js +46 -32
  11. package/dist/3d/entities/entity-3d.d.ts +6 -3
  12. package/dist/3d/entities/entity-3d.js +20 -17
  13. package/dist/3d/entities/raycast-vehicle-3d.entity.js +5 -5
  14. package/dist/3d/entities/surface-following.entity.d.ts +96 -0
  15. package/dist/3d/entities/surface-following.entity.js +296 -0
  16. package/dist/3d/gg-3d-world.d.ts +3 -0
  17. package/dist/3d/gg-3d-world.js +21 -13
  18. package/dist/3d/index.d.ts +1 -0
  19. package/dist/3d/index.js +1 -0
  20. package/dist/3d/loader.js +1 -1
  21. package/dist/3d/models/body-options.d.ts +4 -4
  22. package/dist/3d/models/body-options.js +6 -1
  23. package/dist/base/clock/global-clock.d.ts +2 -6
  24. package/dist/base/clock/global-clock.js +16 -16
  25. package/dist/base/clock/i-clock.d.ts +14 -5
  26. package/dist/base/clock/i-clock.js +43 -1
  27. package/dist/base/clock/pausable-clock.d.ts +3 -13
  28. package/dist/base/clock/pausable-clock.js +8 -16
  29. package/dist/base/components/physics/i-body.component.d.ts +5 -5
  30. package/dist/base/components/physics/i-physics-world.component.d.ts +43 -5
  31. package/dist/base/data-structures/bitmask.d.ts +5 -4
  32. package/dist/base/data-structures/bitmask.js +4 -3
  33. package/dist/base/gg-world.d.ts +8 -1
  34. package/dist/base/gg-world.js +102 -65
  35. package/dist/base/inputs/keyboard.input.d.ts +1 -1
  36. package/dist/base/inputs/keyboard.input.js +10 -10
  37. package/dist/base/inputs/mouse.input.js +3 -2
  38. package/dist/base/math/point2.d.ts +2 -0
  39. package/dist/base/math/point2.js +6 -3
  40. package/dist/base/math/point3.d.ts +2 -0
  41. package/dist/base/math/point3.js +6 -3
  42. package/dist/base/models/body-options.d.ts +27 -9
  43. package/dist/base/models/body-options.js +65 -1
  44. package/dist/dev/gg-console.ui.js +14 -4
  45. package/dist/dev/gg-debugger.ui.d.ts +2 -2
  46. package/dist/dev/gg-debugger.ui.js +15 -20
  47. package/dist/dev/gg-static.d.ts +6 -1
  48. package/dist/dev/gg-static.js +104 -25
  49. package/dist/index.d.ts +2 -0
  50. package/dist/index.js +3 -0
  51. package/dist/version.d.ts +1 -0
  52. package/dist/version.js +1 -0
  53. package/package.json +4 -3
@@ -3,8 +3,6 @@ import { IPositionable2d } from '../interfaces/i-positionable-2d';
3
3
  import { IRenderable2dEntity } from './i-renderable-2d.entity';
4
4
  import { PhysicsTypeDocRepo2D, VisualTypeDocRepo2D } from '../gg-2d-world';
5
5
  export declare class Entity2d<VTypeDoc extends VisualTypeDocRepo2D = VisualTypeDocRepo2D, PTypeDoc extends PhysicsTypeDocRepo2D = PhysicsTypeDocRepo2D> extends IRenderable2dEntity<VTypeDoc, PTypeDoc> implements IPositionable2d {
6
- readonly object2D: VTypeDoc['displayObject'] | null;
7
- readonly objectBody: PTypeDoc['rigidBody'] | null;
8
6
  readonly tickOrder = TickOrder.OBJECTS_BINDING;
9
7
  private _position;
10
8
  get position(): Point2;
@@ -12,10 +10,15 @@ export declare class Entity2d<VTypeDoc extends VisualTypeDocRepo2D = VisualTypeD
12
10
  private _rotation;
13
11
  get rotation(): number;
14
12
  set rotation(value: number);
13
+ readonly object2D: VTypeDoc['displayObject'] | null;
14
+ readonly objectBody: PTypeDoc['rigidBody'] | null;
15
15
  updateVisibility(): void;
16
16
  /**
17
17
  * Synchronize physics body transform with entity (and object2d if defined)
18
18
  * */
19
19
  protected runTransformBinding(objectBody: PTypeDoc['rigidBody'], object2D: VTypeDoc['displayObject'] | null): void;
20
- constructor(object2D: VTypeDoc['displayObject'] | null, objectBody: PTypeDoc['rigidBody'] | null);
20
+ constructor(options: {
21
+ object2D?: VTypeDoc['displayObject'];
22
+ objectBody?: PTypeDoc['rigidBody'];
23
+ });
21
24
  }
@@ -44,29 +44,32 @@ export class Entity2d extends IRenderable2dEntity {
44
44
  this._position = pos;
45
45
  this._rotation = quat;
46
46
  }
47
- constructor(object2D, objectBody) {
47
+ constructor(options) {
48
48
  super();
49
- this.object2D = object2D;
50
- this.objectBody = objectBody;
51
49
  this.tickOrder = TickOrder.OBJECTS_BINDING;
52
50
  this._position = Pnt2.O;
53
51
  this._rotation = 0;
54
- if (objectBody) {
55
- this.tick$.subscribe(() => {
56
- this.runTransformBinding(objectBody, object2D);
57
- });
58
- this.runTransformBinding(objectBody, object2D);
59
- this.name = objectBody.name;
52
+ this.object2D = 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 (object2D) {
62
- this._position = object2D.position;
63
- this._rotation = object2D.rotation;
64
- this.name = object2D.name;
59
+ if (options.object2D) {
60
+ this.object2D = options.object2D;
61
+ if (!options.objectBody) {
62
+ this._position = this.object2D.position;
63
+ this._rotation = this.object2D.rotation;
64
+ this.name = this.object2D.name;
65
+ }
66
+ this.addComponents(this.object2D);
65
67
  }
66
- else {
67
- throw new Error('Cannot create entity without an object2D and a body');
68
+ if (this.objectBody) {
69
+ this.tick$.subscribe(() => {
70
+ this.runTransformBinding(this.objectBody, this.object2D);
71
+ });
72
+ this.runTransformBinding(this.objectBody, this.object2D);
68
73
  }
69
- objectBody && this.addComponents(objectBody);
70
- object2D && this.addComponents(object2D);
71
74
  }
72
75
  }
@@ -27,4 +27,7 @@ export declare class Gg2dWorld<VTypeDoc extends VisualTypeDocRepo2D = VisualType
27
27
  constructor(visualScene: VS, physicsWorld: PW);
28
28
  addPrimitiveRigidBody(descr: BodyShape2DDescriptor, position?: Point2, rotation?: number, material?: DisplayObject2dOpts<VTypeDoc['texture']>): Entity2d<VTypeDoc, PTypeDoc>;
29
29
  addRenderer(canvas?: HTMLCanvasElement, rendererOptions?: Partial<RendererOptions & VTypeDoc['rendererExtraOpts']>): Renderer2dEntity<VTypeDoc>;
30
+ protected registerConsoleCommands(ggstatic: {
31
+ registerConsoleCommand: (world: GgWorld<any, any> | null, command: string, handler: (...args: string[]) => Promise<string>, doc?: string) => void;
32
+ }): void;
30
33
  }
@@ -15,21 +15,12 @@ export class Gg2dWorld extends GgWorld {
15
15
  super(visualScene, physicsWorld);
16
16
  this.visualScene = visualScene;
17
17
  this.physicsWorld = physicsWorld;
18
- if (window.ggstatic) {
19
- window.ggstatic.registerConsoleCommand(this, 'ph_gravity', (...args) => __awaiter(this, void 0, void 0, function* () {
20
- if (args.length == 1) {
21
- args = ['0', args[0]]; // mean Y axis
22
- }
23
- if (args.length > 0) {
24
- this.physicsWorld.gravity = { x: +args[0], y: +args[1] };
25
- }
26
- return JSON.stringify(this.physicsWorld.gravity);
27
- }), 'args: [float] or [float float]; change 2D world gravity vector. 1 argument means ' +
28
- '{x: 0, y: value}, 2 arguments set the whole vector. Default value is "9.82" or "0 9.82"');
29
- }
30
18
  }
31
19
  addPrimitiveRigidBody(descr, position = Pnt2.O, rotation = 0, material = {}) {
32
- const entity = new Entity2d(this.visualScene.factory.createPrimitive(descr.shape, material), this.physicsWorld.factory.createRigidBody(descr));
20
+ const entity = new Entity2d({
21
+ object2D: this.visualScene.factory.createPrimitive(descr.shape, material),
22
+ objectBody: this.physicsWorld.factory.createRigidBody(descr),
23
+ });
33
24
  entity.position = position;
34
25
  entity.rotation = rotation;
35
26
  this.addEntity(entity);
@@ -40,4 +31,21 @@ export class Gg2dWorld extends GgWorld {
40
31
  this.addEntity(entity);
41
32
  return entity;
42
33
  }
34
+ registerConsoleCommands(ggstatic) {
35
+ super.registerConsoleCommands(ggstatic);
36
+ ggstatic.registerConsoleCommand(this, 'gravity', (...args) => __awaiter(this, void 0, void 0, function* () {
37
+ if (args.length == 1) {
38
+ args = ['0', args[0]]; // mean Y axis
39
+ }
40
+ if (args.length > 0) {
41
+ if (isNaN(+args[0]) || isNaN(+args[1])) {
42
+ throw new Error('Wrong arguments');
43
+ }
44
+ this.physicsWorld.gravity = { x: +args[0], y: +args[1] };
45
+ }
46
+ return JSON.stringify(this.physicsWorld.gravity);
47
+ }), 'args: [ ?float, ?float ]; Get or set 2D world gravity vector. 1 argument sets' +
48
+ ' vector {x: 0, y: value}, 2 arguments sets the whole vector.' +
49
+ ' Default value is "9.82" or "0 9.82"');
50
+ }
43
51
  }
@@ -1,7 +1,7 @@
1
- import { BodyOptions, DebugBodySettings } from '../../base';
1
+ import { BodyOptions, DebugBodySettings, DebugBodyType } from '../../base';
2
2
  import { Shape2DDescriptor } from './shapes';
3
3
  export interface Body2DOptions extends BodyOptions {
4
4
  }
5
- export type DebugBody2DSettings = DebugBodySettings & {
6
- shape: Shape2DDescriptor;
7
- };
5
+ export declare class DebugBody2DSettings extends DebugBodySettings<Shape2DDescriptor> {
6
+ constructor(type: DebugBodyType, shape: Shape2DDescriptor, ignoreTransform?: boolean, color?: number | undefined);
7
+ }
@@ -1 +1,6 @@
1
- export {};
1
+ import { DebugBodySettings } from '../../base';
2
+ export class DebugBody2DSettings extends DebugBodySettings {
3
+ constructor(type, shape, ignoreTransform = false, color = undefined) {
4
+ super(type, shape, ignoreTransform, color);
5
+ }
6
+ }
@@ -1,4 +1,5 @@
1
- import { DirectionKeyboardInput, DirectionKeyboardKeymap, GgWorld, IEntity, KeyboardInput, MouseInput, MouseInputOptions, TickOrder } from '../../../../base';
1
+ import { Subject } from 'rxjs';
2
+ import { DirectionKeyboardInput, DirectionKeyboardKeymap, GgWorld, IEntity, KeyboardInput, MouseInput, MouseInputOptions, MutableSpherical, Spherical, TickOrder } from '../../../../base';
2
3
  import { Renderer3dEntity } from '../../renderer-3d.entity';
3
4
  /**
4
5
  * Options for configuring a FreeCameraInput controller.
@@ -57,6 +58,12 @@ export declare class FreeCameraController extends IEntity {
57
58
  * The keyboard input controller used for camera movement.
58
59
  */
59
60
  readonly directionsInput: DirectionKeyboardInput;
61
+ protected _spherical: MutableSpherical;
62
+ get active(): boolean;
63
+ set active(value: boolean);
64
+ get spherical(): Spherical;
65
+ set spherical(value: Spherical);
66
+ protected resetMotion$: Subject<void>;
60
67
  /**
61
68
  * Creates a new FreeCameraInput instance.
62
69
  * @param keyboard The keyboard input controller to use for camera movement.
@@ -64,6 +71,7 @@ export declare class FreeCameraController extends IEntity {
64
71
  * @param options Optional configuration options for the controller.
65
72
  */
66
73
  constructor(keyboard: KeyboardInput, camera: Renderer3dEntity, options?: Partial<FreeCameraControllerOptions>);
74
+ reset(): void;
67
75
  onSpawned(world: GgWorld<any, any>): Promise<void>;
68
76
  onRemoved(): Promise<void>;
69
77
  }
@@ -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 { BehaviorSubject, combineLatest, filter, takeUntil } from 'rxjs';
10
+ import { BehaviorSubject, combineLatest, filter, Subject, takeUntil } from 'rxjs';
11
11
  import { DirectionKeyboardInput, ggElastic, IEntity, MouseInput, Pnt2, Pnt3, Qtrn, TickOrder, } from '../../../../base';
12
12
  import { map } from 'rxjs/operators';
13
13
  const DEFAULT_FREE_CAMERA_CONTROLLER_OPTIONS = {
@@ -25,6 +25,23 @@ const DEFAULT_FREE_CAMERA_CONTROLLER_OPTIONS = {
25
25
  * A controller for a free-moving camera.
26
26
  */
27
27
  export class FreeCameraController extends IEntity {
28
+ get active() {
29
+ return super.active;
30
+ }
31
+ set active(value) {
32
+ if (!super.active && value) {
33
+ this.reset();
34
+ }
35
+ super.active = value;
36
+ }
37
+ get spherical() {
38
+ return this._spherical;
39
+ }
40
+ set spherical(value) {
41
+ this._spherical.phi = Math.max(0.000001, Math.min(Math.PI - 0.000001, value.phi));
42
+ this._spherical.theta = value.theta;
43
+ this.resetMotion$.next();
44
+ }
28
45
  /**
29
46
  * Creates a new FreeCameraInput instance.
30
47
  * @param keyboard The keyboard input controller to use for camera movement.
@@ -36,6 +53,8 @@ export class FreeCameraController extends IEntity {
36
53
  this.keyboard = keyboard;
37
54
  this.camera = camera;
38
55
  this.tickOrder = TickOrder.INPUT_CONTROLLERS;
56
+ this._spherical = { phi: 0, radius: 1, theta: 0 };
57
+ this.resetMotion$ = new Subject();
39
58
  this.options = Object.assign(Object.assign({}, DEFAULT_FREE_CAMERA_CONTROLLER_OPTIONS), options);
40
59
  if (options.mouseOptions) {
41
60
  this.options.mouseOptions = Object.assign(Object.assign({}, DEFAULT_FREE_CAMERA_CONTROLLER_OPTIONS.mouseOptions), options.mouseOptions);
@@ -43,12 +62,17 @@ export class FreeCameraController extends IEntity {
43
62
  this.mouseInput = new MouseInput(this.options.mouseOptions);
44
63
  this.directionsInput = new DirectionKeyboardInput(keyboard, this.options.keymap);
45
64
  }
65
+ reset() {
66
+ this._spherical = Pnt3.toSpherical(Pnt3.rot({ x: 0, y: 0, z: -1 }, this.camera.rotation));
67
+ this.resetMotion$.next();
68
+ }
46
69
  onSpawned(world) {
47
70
  const _super = Object.create(null, {
48
71
  onSpawned: { get: () => super.onSpawned }
49
72
  });
50
73
  return __awaiter(this, void 0, void 0, function* () {
51
74
  yield _super.onSpawned.call(this, world);
75
+ this._spherical = Pnt3.toSpherical(Pnt3.rot({ x: 0, y: 0, z: -1 }, this.camera.rotation));
52
76
  // Subscribe to keyboard input for movement controls
53
77
  const keys = ['KeyE', 'KeyQ'];
54
78
  if (this.camera.camera.supportsFov) {
@@ -87,11 +111,11 @@ export class FreeCameraController extends IEntity {
87
111
  cameraFovInc = f;
88
112
  });
89
113
  // Subscribe to mouse input for camera rotation
90
- const spherical = Pnt3.toSpherical(Pnt3.rot({ x: 0, y: 0, z: -1 }, this.camera.rotation));
91
114
  let isTouchScreen = MouseInput.isTouchDevice();
92
- let mouseDelta$ = this.mouseInput.delta$.pipe(takeUntil(this._onRemoved$), filter(() => isTouchScreen || !this.options.ignoreMouseUnlessPointerLocked || this.mouseInput.isPointerLocked));
115
+ let mouseDelta$ = this.mouseInput.delta$.pipe(takeUntil(this._onRemoved$), filter(() => this.active &&
116
+ (isTouchScreen || !this.options.ignoreMouseUnlessPointerLocked || this.mouseInput.isPointerLocked)));
93
117
  if (this.options.cameraRotationElasticity > 0) {
94
- const s$ = new BehaviorSubject(spherical);
118
+ const s$ = new BehaviorSubject(this._spherical);
95
119
  mouseDelta$.subscribe(delta => {
96
120
  const s = s$.getValue();
97
121
  s$.next({
@@ -100,23 +124,32 @@ export class FreeCameraController extends IEntity {
100
124
  radius: 1,
101
125
  });
102
126
  });
103
- s$.pipe(takeUntil(this._onRemoved$), ggElastic(this.tick$, this.options.cameraRotationElasticity, (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)).subscribe(s => {
104
- spherical.theta = s.theta;
105
- spherical.phi = s.phi;
127
+ const startElasticMotion = () => {
128
+ s$.pipe(takeUntil(this._onRemoved$), ggElastic(this.tick$, this.options.cameraRotationElasticity, (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 => {
129
+ this._spherical.theta = s.theta;
130
+ this._spherical.phi = s.phi;
131
+ });
132
+ };
133
+ this.resetMotion$.pipe(takeUntil(this._onRemoved$)).subscribe(() => {
134
+ s$.next(this._spherical);
135
+ startElasticMotion();
106
136
  });
137
+ startElasticMotion();
107
138
  }
108
139
  else {
109
140
  mouseDelta$.subscribe(delta => {
110
- spherical.theta -= (delta.x * this.options.cameraRotationSensitivity) / 1000;
111
- spherical.phi += (delta.y * this.options.cameraRotationSensitivity) / 1000;
112
- spherical.phi = Math.max(0.000001, Math.min(Math.PI - 0.000001, spherical.phi));
141
+ this._spherical.theta -= (delta.x * this.options.cameraRotationSensitivity) / 1000;
142
+ this._spherical.phi += (delta.y * this.options.cameraRotationSensitivity) / 1000;
143
+ this._spherical.phi = Math.max(0.000001, Math.min(Math.PI - 0.000001, this._spherical.phi));
113
144
  });
114
145
  }
115
146
  // Setup updating camera position and rotation based on input
116
- this.camera.tick$.pipe(takeUntil(this._onRemoved$)).subscribe(([_, delta]) => {
147
+ this.camera.tick$
148
+ .pipe(takeUntil(this._onRemoved$), filter(() => this.active))
149
+ .subscribe(([_, delta]) => {
117
150
  this.camera.camera.fov += cameraFovInc;
118
151
  this.camera.position = Pnt3.add(this.camera.position, Pnt3.rot(Pnt3.scalarMult(translateVector, (this.options.cameraLinearSpeed * delta) / 1000), this.camera.rotation));
119
- this.camera.rotation = Qtrn.lookAt(this.camera.position, Pnt3.add(this.camera.position, Pnt3.fromSpherical(spherical)));
152
+ this.camera.rotation = Qtrn.lookAt(this.camera.position, Pnt3.add(this.camera.position, Pnt3.fromSpherical(this._spherical)));
120
153
  });
121
154
  // start input
122
155
  this.mouseInput.start();
@@ -1,4 +1,5 @@
1
- import { GgWorld, IEntity, MouseInput, MouseInputOptions, MutableSpherical, Point3, TickOrder } from '../../../../base';
1
+ import { GgWorld, IEntity, MouseInput, MouseInputOptions, MutableSpherical, Point3, Spherical, TickOrder } from '../../../../base';
2
+ import { Subject } from 'rxjs';
2
3
  import { Renderer3dEntity } from '../../renderer-3d.entity';
3
4
  export type OrbitCameraControllerOptions = {
4
5
  /**
@@ -41,15 +42,15 @@ export declare class OrbitCameraController extends IEntity {
41
42
  readonly tickOrder = TickOrder.INPUT_CONTROLLERS;
42
43
  protected readonly options: OrbitCameraControllerOptions;
43
44
  readonly mouseInput: MouseInput;
44
- protected spherical: MutableSpherical;
45
+ protected _spherical: MutableSpherical;
45
46
  target: Point3;
46
- get radius(): number;
47
- set radius(value: number);
48
- get phi(): number;
49
- set phi(value: number);
50
- get theta(): number;
51
- set theta(value: number);
47
+ get active(): boolean;
48
+ set active(value: boolean);
49
+ get spherical(): Spherical;
50
+ set spherical(value: Spherical);
51
+ protected resetMotion$: Subject<void>;
52
52
  constructor(camera: Renderer3dEntity, options?: Partial<OrbitCameraControllerOptions>);
53
+ reset(): void;
53
54
  onSpawned(world: GgWorld<any, any>): Promise<void>;
54
55
  onRemoved(): Promise<void>;
55
56
  }
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { ggElastic, IEntity, MouseInput, MouseInputState, Pnt2, Pnt3, Qtrn, TickOrder, } from '../../../../base';
11
- import { BehaviorSubject, filter, takeUntil } from 'rxjs';
11
+ import { BehaviorSubject, filter, Subject, takeUntil } from 'rxjs';
12
12
  import { map } from 'rxjs/operators';
13
13
  const DEFAULT_OPTIONS = {
14
14
  mouseOptions: {},
@@ -19,44 +19,51 @@ const DEFAULT_OPTIONS = {
19
19
  dollying: { sensitivity: 1 },
20
20
  };
21
21
  export class OrbitCameraController extends IEntity {
22
- get radius() {
23
- return this.spherical.radius;
22
+ get active() {
23
+ return super.active;
24
24
  }
25
- set radius(value) {
26
- this.spherical.radius = value;
25
+ set active(value) {
26
+ if (!super.active && value) {
27
+ this.reset();
28
+ }
29
+ super.active = value;
27
30
  }
28
- get phi() {
29
- return this.spherical.phi;
31
+ get spherical() {
32
+ return this._spherical;
30
33
  }
31
- set phi(value) {
32
- this.spherical.phi = Math.max(0.000001, Math.min(Math.PI - 0.000001, value));
33
- }
34
- get theta() {
35
- return this.spherical.theta;
36
- }
37
- set theta(value) {
38
- 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();
39
39
  }
40
40
  constructor(camera, options = {}) {
41
41
  super();
42
42
  this.camera = camera;
43
43
  this.tickOrder = TickOrder.INPUT_CONTROLLERS;
44
- this.spherical = { phi: 0, radius: 10, theta: 0 };
44
+ this._spherical = { phi: 0, radius: 10, theta: 0 };
45
45
  this.target = Pnt3.O;
46
+ this.resetMotion$ = new Subject();
46
47
  this.options = Object.assign(Object.assign({}, DEFAULT_OPTIONS), options);
47
48
  this.mouseInput = new MouseInput(this.options.mouseOptions);
48
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
+ }
49
56
  onSpawned(world) {
50
57
  const _super = Object.create(null, {
51
58
  onSpawned: { get: () => super.onSpawned }
52
59
  });
53
60
  return __awaiter(this, void 0, void 0, function* () {
54
61
  yield _super.onSpawned.call(this, world);
55
- this.spherical = Pnt3.toSpherical(Pnt3.sub(this.camera.position, this.target));
62
+ this._spherical = Pnt3.toSpherical(Pnt3.sub(this.camera.position, this.target));
56
63
  if (this.options.orbiting) {
57
- let mouseDelta$ = this.mouseInput.delta$.pipe(takeUntil(this._onRemoved$), filter(() => this.mouseInput.state == MouseInputState.DRAG));
64
+ let mouseDelta$ = this.mouseInput.delta$.pipe(takeUntil(this._onRemoved$), filter(() => this.active && this.mouseInput.state == MouseInputState.DRAG));
58
65
  if (this.options.orbitingElasticity > 0) {
59
- const s$ = new BehaviorSubject(this.spherical);
66
+ const s$ = new BehaviorSubject(this._spherical);
60
67
  mouseDelta$.subscribe(delta => {
61
68
  const s = s$.getValue();
62
69
  s$.next({
@@ -65,31 +72,38 @@ export class OrbitCameraController extends IEntity {
65
72
  radius: 1,
66
73
  });
67
74
  });
68
- 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)).subscribe(s => {
69
- this.spherical.theta = s.theta;
70
- this.spherical.phi = s.phi;
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();
71
84
  });
85
+ startElasticMotion();
72
86
  }
73
87
  else {
74
88
  mouseDelta$.subscribe(delta => {
75
- this.spherical.theta -= (delta.x * this.options.orbiting.sensitivityX) / 1000;
76
- this.spherical.phi -= (delta.y * this.options.orbiting.sensitivityY) / 1000;
77
- this.spherical.phi = Math.max(0.000001, Math.min(Math.PI - 0.000001, this.spherical.phi));
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));
78
92
  });
79
93
  }
80
94
  }
81
95
  if (this.options.zooming) {
82
96
  this.mouseInput.wheel$.pipe(takeUntil(this._onRemoved$)).subscribe(delta => {
83
97
  if (delta != 0) {
84
- 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));
85
99
  }
86
100
  });
87
101
  }
88
102
  const performPan = (delta) => {
89
- const targetCameraVector = Pnt3.fromSpherical(this.spherical);
103
+ const targetCameraVector = Pnt3.fromSpherical(this._spherical);
90
104
  const viewUp = Pnt3.rotAround(targetCameraVector, {
91
- x: -Math.sin(this.spherical.theta),
92
- y: Math.cos(this.spherical.theta),
105
+ x: -Math.sin(this._spherical.theta),
106
+ y: Math.cos(this._spherical.theta),
93
107
  z: 0,
94
108
  }, Math.PI / 2);
95
109
  const viewRight = Pnt3.rotAround(targetCameraVector, Pnt3.norm(viewUp), Math.PI / 2);
@@ -106,14 +120,14 @@ export class OrbitCameraController extends IEntity {
106
120
  this.mouseInput.delta$
107
121
  .pipe(takeUntil(this._onRemoved$), filter(() => this.mouseInput.state == MouseInputState.DRAG_MIDDLE_BUTTON))
108
122
  .subscribe(delta => {
109
- 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);
110
124
  });
111
125
  }
112
126
  if (MouseInput.isTouchDevice() && (this.options.dollying || this.options.panning)) {
113
127
  this.mouseInput.twoTouchGestureDelta$.pipe(takeUntil(this._onRemoved$)).subscribe(delta => {
114
128
  // dolly on fingers pitch
115
129
  if (this.options.dollying) {
116
- 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);
117
131
  }
118
132
  // pan on fingers move
119
133
  if (this.options.panning) {
@@ -123,7 +137,7 @@ export class OrbitCameraController extends IEntity {
123
137
  }
124
138
  // Setup updating camera position and rotation based on input
125
139
  this.camera.tick$
126
- .pipe(takeUntil(this._onRemoved$), map(() => this.spherical))
140
+ .pipe(takeUntil(this._onRemoved$), filter(() => this.active), map(() => this._spherical))
127
141
  .subscribe(spherical => {
128
142
  this.camera.position = Pnt3.add(this.target, Pnt3.fromSpherical(spherical));
129
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
  }