@gg-web-engine/core 0.0.18 → 0.0.20

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 (46) hide show
  1. package/dist/2d/entities/gg-2d-entity.d.ts +5 -3
  2. package/dist/2d/entities/gg-2d-entity.js +6 -12
  3. package/dist/2d/entities/gg-positionable-2d-entity.js +2 -1
  4. package/dist/2d/gg-2d-world.js +2 -4
  5. package/dist/3d/entities/controllers/animators/camera-3d.animator.js +1 -1
  6. package/dist/3d/entities/controllers/input/car-keyboard-handling.controller.d.ts +1 -0
  7. package/dist/3d/entities/controllers/input/car-keyboard-handling.controller.js +20 -6
  8. package/dist/3d/entities/controllers/input/free-camera.controller.d.ts +8 -0
  9. package/dist/3d/entities/controllers/input/free-camera.controller.js +22 -11
  10. package/dist/3d/entities/controllers/input/orbit-camera.controller.d.ts +7 -1
  11. package/dist/3d/entities/controllers/input/orbit-camera.controller.js +24 -6
  12. package/dist/3d/entities/gg-3d-entity.d.ts +5 -3
  13. package/dist/3d/entities/gg-3d-entity.js +6 -12
  14. package/dist/3d/entities/gg-3d-map-graph.entity.d.ts +4 -1
  15. package/dist/3d/entities/gg-3d-map-graph.entity.js +6 -6
  16. package/dist/3d/entities/gg-3d-raycast-vehicle.entity.d.ts +9 -1
  17. package/dist/3d/entities/gg-3d-raycast-vehicle.entity.js +24 -11
  18. package/dist/3d/entities/gg-3d-trigger.entity.js +1 -0
  19. package/dist/3d/entities/gg-positionable-3d-entity.js +3 -2
  20. package/dist/3d/gg-3d-world.js +3 -5
  21. package/dist/3d/loader.js +2 -2
  22. package/dist/base/entities/base-gg-renderer.js +4 -3
  23. package/dist/base/entities/gg-entity.d.ts +2 -0
  24. package/dist/base/entities/gg-entity.js +14 -3
  25. package/dist/base/entities/gg-positionable-entity.d.ts +1 -1
  26. package/dist/base/entities/inline-controller.js +1 -1
  27. package/dist/base/entities/mixins/renderable-entity.mixin.d.ts +10 -0
  28. package/dist/base/entities/mixins/renderable-entity.mixin.js +57 -0
  29. package/dist/base/gg-world.d.ts +2 -1
  30. package/dist/base/gg-world.js +4 -3
  31. package/dist/base/inputs/direction.keyboard-input.d.ts +1 -1
  32. package/dist/base/inputs/direction.keyboard-input.js +34 -45
  33. package/dist/base/inputs/input.d.ts +8 -10
  34. package/dist/base/inputs/input.js +15 -31
  35. package/dist/base/inputs/keyboard.input.d.ts +14 -2
  36. package/dist/base/inputs/keyboard.input.js +47 -20
  37. package/dist/base/inputs/mouse.input.d.ts +3 -3
  38. package/dist/base/inputs/mouse.input.js +87 -103
  39. package/dist/base/math/point2.d.ts +8 -0
  40. package/dist/base/math/point2.js +17 -1
  41. package/dist/base/math/point3.d.ts +10 -0
  42. package/dist/base/math/point3.js +21 -1
  43. package/dist/base/math/quaternion.d.ts +3 -1
  44. package/dist/base/math/quaternion.js +5 -1
  45. package/package.json +5 -3
  46. package/tsconfig.json +1 -1
@@ -1,15 +1,6 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { Input } from './input';
11
- import { BehaviorSubject, filter, fromEvent, NEVER, Subject, takeUntil } from 'rxjs';
12
- import { map, pairwise, switchMap } from 'rxjs/operators';
2
+ import { BehaviorSubject, filter, finalize, fromEvent, NEVER, share, Subject, takeUntil } from 'rxjs';
3
+ import { map, pairwise, switchMap, tap } from 'rxjs/operators';
13
4
  import { Pnt2 } from '../math/point2';
14
5
  const DEFAULT_MOUSE_INPUT_OPTIONS = {
15
6
  pointerLock: false,
@@ -50,10 +41,13 @@ export class MouseInput extends Input {
50
41
  */
51
42
  constructor(options = {}) {
52
43
  super();
44
+ /**
45
+ An observable of the wheel scrolling.
46
+ */
47
+ this._wheel$ = null;
53
48
  this._delta$ = new Subject();
54
- this._position$ = new BehaviorSubject({ x: 0, y: 0 });
49
+ this._position$ = new BehaviorSubject(Pnt2.O);
55
50
  this._multiTouchPositions$ = new BehaviorSubject([]);
56
- this._wheel$ = new Subject();
57
51
  this.stopped$ = new Subject();
58
52
  this._state$ = new BehaviorSubject(MouseInputState.NONE);
59
53
  this.options = Object.assign(Object.assign({}, DEFAULT_MOUSE_INPUT_OPTIONS), options);
@@ -82,11 +76,11 @@ export class MouseInput extends Input {
82
76
  get position$() {
83
77
  return this._position$.asObservable();
84
78
  }
85
- /**
86
- An observable of the wheel scrolling.
87
- */
88
79
  get wheel$() {
89
- return this._wheel$.asObservable();
80
+ if (!this._wheel$) {
81
+ this._wheel$ = fromEvent(this._element, 'wheel', { passive: false }).pipe(takeUntil(this.stopped$), finalize(() => (this._wheel$ = null)), tap(e => e.preventDefault()), map(e => e.deltaY), share());
82
+ }
83
+ return this._wheel$;
90
84
  }
91
85
  get isPointerLocked() {
92
86
  return !!document.pointerLockElement;
@@ -102,7 +96,7 @@ export class MouseInput extends Input {
102
96
  }
103
97
  get twoTouchGestureDelta$() {
104
98
  return this.state$.pipe(switchMap((s) => s == MouseInputState.DRAG_TOUCH_TWO_FINGERS ? this.multiTouchPositions$ : NEVER), map((p) => p.map(v => ({ x: v.x, y: v.y }))), pairwise(), filter(([prev, cur]) => prev.length > 1 && cur.length > 1), map(([prev, cur]) => ({
105
- centerPointDelta: Pnt2.sub(Pnt2.scalarMult(cur.reduce((p, c) => Pnt2.add(p, c), { x: 0, y: 0 }), 1 / cur.length), Pnt2.scalarMult(prev.reduce((p, c) => Pnt2.add(p, c), { x: 0, y: 0 }), 1 / cur.length)),
99
+ centerPointDelta: Pnt2.sub(Pnt2.scalarMult(cur.reduce((p, c) => Pnt2.add(p, c), Pnt2.O), 1 / cur.length), Pnt2.scalarMult(prev.reduce((p, c) => Pnt2.add(p, c), Pnt2.O), 1 / cur.length)),
106
100
  angleDelta: Pnt2.angle(cur[1], cur[0]) - Pnt2.angle(prev[1], prev[0]),
107
101
  distanceDelta: Pnt2.dist(cur[1], cur[0]) - Pnt2.dist(prev[1], prev[0]),
108
102
  })));
@@ -111,90 +105,82 @@ export class MouseInput extends Input {
111
105
  return this.options.canvas || window;
112
106
  }
113
107
  startInternal() {
114
- return __awaiter(this, void 0, void 0, function* () {
115
- if (this.options.canvas) {
116
- this.options.canvas.style.touchAction = 'none';
117
- }
118
- this._state$.next(MouseInputState.NONE);
119
- const mouseButtonStateMap = [
120
- MouseInputState.DRAG,
121
- MouseInputState.DRAG_MIDDLE_BUTTON,
122
- MouseInputState.DRAG_RIGHT_BUTTON,
123
- ];
124
- const pointerLengthsStateMap = [MouseInputState.NONE, MouseInputState.DRAG, MouseInputState.DRAG_TOUCH_TWO_FINGERS];
125
- const pointers = [];
126
- const pointerPositions = {};
127
- fromEvent(this._element, 'mousemove')
128
- .pipe(takeUntil(this.stopped$))
129
- .subscribe((event) => {
130
- this._delta$.next({ x: event.movementX, y: event.movementY });
131
- });
132
- fromEvent(this._element, 'pointermove')
133
- .pipe(takeUntil(this.stopped$))
134
- .subscribe((event) => {
135
- if (event instanceof PointerEvent) {
136
- if (event.pointerType === 'touch') {
137
- pointerPositions[event.pointerId] = { x: event.pageX, y: event.pageY };
138
- }
139
- const newPosition = { x: event.pageX, y: event.pageY };
140
- this._position$.next(newPosition);
141
- this._multiTouchPositions$.next(Object.values(pointerPositions));
108
+ if (this.options.canvas) {
109
+ this.options.canvas.style.touchAction = 'none';
110
+ }
111
+ this._state$.next(MouseInputState.NONE);
112
+ const mouseButtonStateMap = [
113
+ MouseInputState.DRAG,
114
+ MouseInputState.DRAG_MIDDLE_BUTTON,
115
+ MouseInputState.DRAG_RIGHT_BUTTON,
116
+ ];
117
+ const pointerLengthsStateMap = [MouseInputState.NONE, MouseInputState.DRAG, MouseInputState.DRAG_TOUCH_TWO_FINGERS];
118
+ const pointers = [];
119
+ const pointerPositions = {};
120
+ fromEvent(this._element, 'mousemove')
121
+ .pipe(takeUntil(this.stopped$))
122
+ .subscribe((event) => {
123
+ this._delta$.next({ x: event.movementX, y: event.movementY });
124
+ });
125
+ fromEvent(this._element, 'pointermove')
126
+ .pipe(takeUntil(this.stopped$))
127
+ .subscribe((event) => {
128
+ if (event instanceof PointerEvent) {
129
+ if (event.pointerType === 'touch') {
130
+ pointerPositions[event.pointerId] = { x: event.pageX, y: event.pageY };
142
131
  }
143
- else {
144
- this._position$.next({ x: event.clientX, y: event.clientY });
132
+ const newPosition = { x: event.pageX, y: event.pageY };
133
+ this._position$.next(newPosition);
134
+ this._multiTouchPositions$.next(Object.values(pointerPositions));
135
+ }
136
+ else {
137
+ this._position$.next({ x: event.clientX, y: event.clientY });
138
+ }
139
+ this._delta$.next({ x: event.movementX, y: event.movementY });
140
+ });
141
+ if (!MouseInput.isTouchDevice() && this.options.pointerLock && this.options.canvas) {
142
+ this.options.canvas.addEventListener('click', this.canvasClickListener);
143
+ }
144
+ const onPointerUp = (event) => {
145
+ delete pointerPositions[event.pointerId];
146
+ for (let i = 0; i < pointers.length; i++) {
147
+ if (pointers[i].pointerId == event.pointerId) {
148
+ pointers.splice(i, 1);
149
+ break;
145
150
  }
146
- this._delta$.next({ x: event.movementX, y: event.movementY });
147
- });
148
- if (!MouseInput.isTouchDevice() && this.options.pointerLock && this.options.canvas) {
149
- this.options.canvas.addEventListener('click', this.canvasClickListener);
150
151
  }
151
- const onPointerUp = (event) => {
152
- delete pointerPositions[event.pointerId];
153
- for (let i = 0; i < pointers.length; i++) {
154
- if (pointers[i].pointerId == event.pointerId) {
155
- pointers.splice(i, 1);
156
- break;
157
- }
152
+ if (pointers.length === 0) {
153
+ if (this.options.canvas) {
154
+ this.options.canvas.releasePointerCapture(event.pointerId);
158
155
  }
159
- if (pointers.length === 0) {
160
- if (this.options.canvas) {
161
- this.options.canvas.releasePointerCapture(event.pointerId);
162
- }
163
- this._element.removeEventListener('pointerup', onPointerUp);
164
- this._element.removeEventListener('pointercancel', onPointerUp);
156
+ this._element.removeEventListener('pointerup', onPointerUp);
157
+ this._element.removeEventListener('pointercancel', onPointerUp);
158
+ }
159
+ this._state$.next(pointerLengthsStateMap[Math.min(pointers.length, 2)]);
160
+ };
161
+ fromEvent(this._element, 'pointerdown')
162
+ .pipe(takeUntil(this.stopped$))
163
+ .subscribe((event) => {
164
+ if (pointers.length === 0) {
165
+ if (this.options.canvas) {
166
+ this.options.canvas.setPointerCapture(event.pointerId);
165
167
  }
168
+ this._element.addEventListener('pointerup', onPointerUp);
169
+ this._element.addEventListener('pointercancel', onPointerUp);
170
+ }
171
+ pointers.push(event);
172
+ if (event.pointerType === 'touch') {
173
+ pointerPositions[event.pointerId] = { x: event.pageX, y: event.pageY };
166
174
  this._state$.next(pointerLengthsStateMap[Math.min(pointers.length, 2)]);
167
- };
168
- fromEvent(this._element, 'pointerdown')
169
- .pipe(takeUntil(this.stopped$))
170
- .subscribe((event) => {
171
- if (pointers.length === 0) {
172
- if (this.options.canvas) {
173
- this.options.canvas.setPointerCapture(event.pointerId);
174
- }
175
- this._element.addEventListener('pointerup', onPointerUp);
176
- this._element.addEventListener('pointercancel', onPointerUp);
177
- }
178
- pointers.push(event);
179
- if (event.pointerType === 'touch') {
180
- pointerPositions[event.pointerId] = { x: event.pageX, y: event.pageY };
181
- this._state$.next(pointerLengthsStateMap[Math.min(pointers.length, 2)]);
182
- }
183
- else {
184
- this._state$.next(mouseButtonStateMap[event.button] || MouseInputState.NONE);
185
- }
186
- });
187
- fromEvent(this._element, 'contextmenu')
188
- .pipe(takeUntil(this.stopped$))
189
- .subscribe(event => {
190
- event.preventDefault();
191
- });
192
- fromEvent(this._element, 'wheel', { passive: false })
193
- .pipe(takeUntil(this.stopped$))
194
- .subscribe(e => {
195
- e.preventDefault();
196
- this._wheel$.next(e.deltaY);
197
- });
175
+ }
176
+ else {
177
+ this._state$.next(mouseButtonStateMap[event.button] || MouseInputState.NONE);
178
+ }
179
+ });
180
+ fromEvent(this._element, 'contextmenu')
181
+ .pipe(takeUntil(this.stopped$))
182
+ .subscribe(event => {
183
+ event.preventDefault();
198
184
  });
199
185
  }
200
186
  /**
@@ -202,13 +188,11 @@ export class MouseInput extends Input {
202
188
  @param {boolean} [unlockPointer=true] - Whether to exit pointer lock.
203
189
  */
204
190
  stopInternal(unlockPointer = true) {
205
- return __awaiter(this, void 0, void 0, function* () {
206
- this.stopped$.next();
207
- if (unlockPointer && !!this.options.canvas) {
208
- this.options.canvas.removeEventListener('click', this.canvasClickListener);
209
- document.exitPointerLock();
210
- }
211
- });
191
+ this.stopped$.next();
192
+ if (unlockPointer && !!this.options.canvas) {
193
+ this.options.canvas.removeEventListener('click', this.canvasClickListener);
194
+ document.exitPointerLock();
195
+ }
212
196
  }
213
197
  /**
214
198
  Request pointer lock on the canvas element.
@@ -1,11 +1,19 @@
1
1
  import { Point2 } from '../models/points';
2
2
  export declare class Pnt2 {
3
+ /** empty vector */
4
+ static get O(): Point2;
5
+ /** basis X vector */
6
+ static get X(): Point2;
7
+ /** basis Y vector */
8
+ static get Y(): Point2;
3
9
  /** clone point */
4
10
  static clone(p: Point2): Point2;
5
11
  /** add point b to point a */
6
12
  static add(a: Point2, b: Point2): Point2;
7
13
  /** subtract point b from point a */
8
14
  static sub(a: Point2, b: Point2): Point2;
15
+ /** average point between a and b */
16
+ static avg(a: Point2, b: Point2): Point2;
9
17
  /** calculate vector length (squared) */
10
18
  static lenSq(v: Point2): number;
11
19
  /** calculate vector length */
@@ -1,4 +1,16 @@
1
1
  export class Pnt2 {
2
+ /** empty vector */
3
+ static get O() {
4
+ return { x: 0, y: 0 };
5
+ }
6
+ /** basis X vector */
7
+ static get X() {
8
+ return { x: 1, y: 0 };
9
+ }
10
+ /** basis Y vector */
11
+ static get Y() {
12
+ return { x: 0, y: 1 };
13
+ }
2
14
  /** clone point */
3
15
  static clone(p) {
4
16
  return { x: p.x, y: p.y };
@@ -11,6 +23,10 @@ export class Pnt2 {
11
23
  static sub(a, b) {
12
24
  return { x: a.x - b.x, y: a.y - b.y };
13
25
  }
26
+ /** average point between a and b */
27
+ static avg(a, b) {
28
+ return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
29
+ }
14
30
  /** calculate vector length (squared) */
15
31
  static lenSq(v) {
16
32
  return v.x * v.x + v.y * v.y;
@@ -25,7 +41,7 @@ export class Pnt2 {
25
41
  }
26
42
  /** normalize */
27
43
  static norm(p) {
28
- const length = Math.sqrt(p.x ** 2 + p.y ** 2);
44
+ const length = Pnt2.len(p);
29
45
  return {
30
46
  x: p.x / length,
31
47
  y: p.y / length,
@@ -1,11 +1,21 @@
1
1
  import { Point3, Point4, Spherical } from '../models/points';
2
2
  export declare class Pnt3 {
3
+ /** empty vector */
4
+ static get O(): Point3;
5
+ /** basis X vector */
6
+ static get X(): Point3;
7
+ /** basis Y vector */
8
+ static get Y(): Point3;
9
+ /** basis Z vector */
10
+ static get Z(): Point3;
3
11
  /** clone point */
4
12
  static clone(p: Point3): Point3;
5
13
  /** add point b to point a */
6
14
  static add(a: Point3, b: Point3): Point3;
7
15
  /** subtract point b from point a */
8
16
  static sub(a: Point3, b: Point3): Point3;
17
+ /** average point between a and b */
18
+ static avg(a: Point3, b: Point3): Point3;
9
19
  /** calculate vector length (squared) */
10
20
  static lenSq(v: Point3): number;
11
21
  /** calculate vector length */
@@ -1,5 +1,21 @@
1
1
  import { Qtrn } from './quaternion';
2
2
  export class Pnt3 {
3
+ /** empty vector */
4
+ static get O() {
5
+ return { x: 0, y: 0, z: 0 };
6
+ }
7
+ /** basis X vector */
8
+ static get X() {
9
+ return { x: 1, y: 0, z: 0 };
10
+ }
11
+ /** basis Y vector */
12
+ static get Y() {
13
+ return { x: 0, y: 1, z: 0 };
14
+ }
15
+ /** basis Z vector */
16
+ static get Z() {
17
+ return { x: 0, y: 0, z: 1 };
18
+ }
3
19
  /** clone point */
4
20
  static clone(p) {
5
21
  return { x: p.x, y: p.y, z: p.z };
@@ -12,6 +28,10 @@ export class Pnt3 {
12
28
  static sub(a, b) {
13
29
  return { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
14
30
  }
31
+ /** average point between a and b */
32
+ static avg(a, b) {
33
+ return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2, z: (a.z + b.z) / 2 };
34
+ }
15
35
  /** calculate vector length (squared) */
16
36
  static lenSq(v) {
17
37
  return v.x * v.x + v.y * v.y + v.z * v.z;
@@ -36,7 +56,7 @@ export class Pnt3 {
36
56
  }
37
57
  /** normalize */
38
58
  static norm(p) {
39
- const length = Math.sqrt(p.x ** 2 + p.y ** 2 + p.z ** 2);
59
+ const length = Pnt3.len(p);
40
60
  if (length === 0) {
41
61
  return p;
42
62
  }
@@ -10,6 +10,8 @@ import { Point3, Point4 } from '../models/points';
10
10
  * Euler angles (such as gimbal lock).
11
11
  */
12
12
  export declare class Qtrn {
13
+ /** empty rotation */
14
+ static get O(): Point4;
13
15
  /**
14
16
  * Returns a new quaternion instance with the same values as the given quaternion object.
15
17
  * @param q The Point4 object to clone.
@@ -89,7 +91,7 @@ export declare class Qtrn {
89
91
  * @param up - The up direction of the object
90
92
  * @returns A new quaternion representing the rotation required to face towards the target point.
91
93
  */
92
- static lookAt(eye: Point3, target: Point3, up: Point3): Point4;
94
+ static lookAt(eye: Point3, target: Point3, up?: Point3): Point4;
93
95
  /**
94
96
  * Returns a quaternion that represents the input quaternion, rotated around provided axis vector by provided angle.
95
97
  * Assumes that axis vector is already normalized
@@ -11,6 +11,10 @@ import { Mtrx4 } from './matrix4';
11
11
  * Euler angles (such as gimbal lock).
12
12
  */
13
13
  export class Qtrn {
14
+ /** empty rotation */
15
+ static get O() {
16
+ return { x: 0, y: 0, z: 0, w: 1 };
17
+ }
14
18
  /**
15
19
  * Returns a new quaternion instance with the same values as the given quaternion object.
16
20
  * @param q The Point4 object to clone.
@@ -200,7 +204,7 @@ export class Qtrn {
200
204
  * @param up - The up direction of the object
201
205
  * @returns A new quaternion representing the rotation required to face towards the target point.
202
206
  */
203
- static lookAt(eye, target, up) {
207
+ static lookAt(eye, target, up = Pnt3.Z) {
204
208
  return this.fromMatrix4(Mtrx4.lookAt(eye, target, up));
205
209
  }
206
210
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gg-web-engine/core",
3
- "version": "0.0.18",
3
+ "version": "0.0.20",
4
4
  "description": "An attempt to create open source game engine for browser",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -28,12 +28,14 @@
28
28
  "homepage": "https://github.com/AndyGura/gg-web-engine#readme",
29
29
  "dependencies": {
30
30
  "rxjs": "~7.8.0",
31
- "stats.js": "^0.17.0"
31
+ "stats.js": "^0.17.0",
32
+ "ts-mixer": "^6.0.3"
32
33
  },
33
34
  "devDependencies": {
34
35
  "@types/jest": "^29.5.0",
35
36
  "@types/stats.js": "^0.17.0",
36
37
  "jest": "^29.5.0",
38
+ "jest-environment-jsdom": "^29.5.0",
37
39
  "prettier": "^2.8.6",
38
40
  "ts-jest": "^29.0.5",
39
41
  "typescript": "~4.7.2"
@@ -59,6 +61,6 @@
59
61
  "<rootDir>/test/"
60
62
  ],
61
63
  "coverageDirectory": "./coverage",
62
- "testEnvironment": "node"
64
+ "testEnvironment": "jsdom"
63
65
  }
64
66
  }
package/tsconfig.json CHANGED
@@ -5,5 +5,5 @@
5
5
  "outDir": "./dist/"
6
6
  },
7
7
  "include": ["*.ts", "**/*.ts"],
8
- "exclude": ["**/*.spec.ts", "node_modules", "dist/**/*"]
8
+ "exclude": ["**/*.spec.ts", "node_modules", "dist/**/*", "test/**/*"]
9
9
  }