@gg-web-engine/core 0.0.1 → 0.0.2
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.
- package/package.json +1 -1
- package/src/2d/entities/gg-2d-entity.ts +0 -86
- package/src/2d/entities/gg-2d-trigger.entity.ts +0 -17
- package/src/2d/entities/gg-positionable-2d-entity.ts +0 -16
- package/src/2d/factories.ts +0 -20
- package/src/2d/gg-2d-world.ts +0 -32
- package/src/2d/interfaces.ts +0 -31
- package/src/2d/models/body-options.ts +0 -3
- package/src/2d/models/shapes.ts +0 -7
- package/src/3d/controllers/car-keyboard.controller.ts +0 -128
- package/src/3d/controllers/free-camera.controller.ts +0 -86
- package/src/3d/entities/controllers/entity-motion.controller.ts +0 -122
- package/src/3d/entities/gg-3d-camera.entity.ts +0 -8
- package/src/3d/entities/gg-3d-entity.ts +0 -116
- package/src/3d/entities/gg-3d-map-graph.entity.ts +0 -204
- package/src/3d/entities/gg-3d-raycast-vehicle.entity.ts +0 -468
- package/src/3d/entities/gg-3d-trigger.entity.ts +0 -22
- package/src/3d/entities/gg-positionable-3d-entity.ts +0 -31
- package/src/3d/factories.ts +0 -29
- package/src/3d/gg-3d-world.ts +0 -36
- package/src/3d/interfaces.ts +0 -96
- package/src/3d/loader.ts +0 -165
- package/src/3d/models/body-options.ts +0 -3
- package/src/3d/models/gg-meta.ts +0 -12
- package/src/3d/models/shapes.ts +0 -13
- package/src/base/clock.ts +0 -97
- package/src/base/controllers/common.ts +0 -37
- package/src/base/controllers/i-controller.ts +0 -5
- package/src/base/controllers/keyboard.controller.ts +0 -109
- package/src/base/controllers/mouse.controller.ts +0 -73
- package/src/base/data-structures/graph.ts +0 -125
- package/src/base/entities/base-gg-renderer.ts +0 -77
- package/src/base/entities/gg-entity.ts +0 -61
- package/src/base/entities/gg-positionable-entity.ts +0 -64
- package/src/base/entities/inline-controller.ts +0 -31
- package/src/base/entities/interfaces/i-tick-listener.ts +0 -15
- package/src/base/gg-static.ts +0 -30
- package/src/base/gg-viewport-manager.ts +0 -122
- package/src/base/gg-viewport.ts +0 -159
- package/src/base/gg-world.ts +0 -198
- package/src/base/interfaces/gg-body.ts +0 -23
- package/src/base/interfaces/gg-debug-physics-drawer.ts +0 -10
- package/src/base/interfaces/gg-object.ts +0 -26
- package/src/base/interfaces/gg-physics-world.ts +0 -24
- package/src/base/interfaces/gg-trigger.ts +0 -10
- package/src/base/interfaces/gg-visual-scene.ts +0 -11
- package/src/base/math/box.ts +0 -19
- package/src/base/math/matrix4.ts +0 -29
- package/src/base/math/numbers.ts +0 -10
- package/src/base/math/point2.ts +0 -43
- package/src/base/math/point3.ts +0 -119
- package/src/base/math/quaternion.ts +0 -172
- package/src/base/models/axis-directions.ts +0 -3
- package/src/base/models/body-options.ts +0 -6
- package/src/base/models/geometry-nodes.ts +0 -5
- package/src/base/models/points.ts +0 -3
- package/src/base/ui/gg-console.ui.ts +0 -165
- package/src/base/ui/gg-debugger.ui.ts +0 -69
- package/src/index.ts +0 -53
- package/test/base/data-structures/graph.spec.ts +0 -76
package/package.json
CHANGED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { Subject, Subscription } from 'rxjs';
|
|
2
|
-
import { GgPositionable2dEntity } from './gg-positionable-2d-entity';
|
|
3
|
-
import { ITickListener } from '../../base/entities/interfaces/i-tick-listener';
|
|
4
|
-
import { Point2 } from '../../base/models/points';
|
|
5
|
-
import { IGg2dBody, IGg2dObject } from '../interfaces';
|
|
6
|
-
import { Gg2dWorld } from '../gg-2d-world';
|
|
7
|
-
|
|
8
|
-
export class Gg2dEntity extends GgPositionable2dEntity implements ITickListener {
|
|
9
|
-
public readonly tick$: Subject<[number, number]> = new Subject<[number, number]>();
|
|
10
|
-
public readonly tickOrder = 750;
|
|
11
|
-
private tickSub: Subscription | null = null;
|
|
12
|
-
|
|
13
|
-
set position(value: Point2) {
|
|
14
|
-
if (this.object2D) {
|
|
15
|
-
this.object2D.position = value;
|
|
16
|
-
}
|
|
17
|
-
if (this.objectBody) {
|
|
18
|
-
this.objectBody.position = value;
|
|
19
|
-
}
|
|
20
|
-
super.position = value;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
set rotation(value: number) {
|
|
24
|
-
if (this.object2D) {
|
|
25
|
-
this.object2D.rotation = value;
|
|
26
|
-
}
|
|
27
|
-
if (this.objectBody) {
|
|
28
|
-
this.objectBody.rotation = value;
|
|
29
|
-
}
|
|
30
|
-
super.rotation = value;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
set scale(value: Point2) {
|
|
34
|
-
if (this.object2D) {
|
|
35
|
-
this.object2D.scale = value;
|
|
36
|
-
}
|
|
37
|
-
if (this.objectBody) {
|
|
38
|
-
this.objectBody.scale = value;
|
|
39
|
-
}
|
|
40
|
-
super.scale = value;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
constructor(public readonly object2D: IGg2dObject | null, public readonly objectBody: IGg2dBody | null) {
|
|
44
|
-
super();
|
|
45
|
-
if (objectBody && object2D) {
|
|
46
|
-
this.tickSub = this.tick$.subscribe(() => {
|
|
47
|
-
// bind physics body transform to object transform
|
|
48
|
-
const pos = objectBody.position;
|
|
49
|
-
const rot = objectBody.rotation;
|
|
50
|
-
object2D.position = pos;
|
|
51
|
-
object2D.rotation = rot;
|
|
52
|
-
this._position$.next(pos);
|
|
53
|
-
this._rotation$.next(rot);
|
|
54
|
-
});
|
|
55
|
-
} else if (!objectBody && !object2D) {
|
|
56
|
-
throw new Error('Cannot create entity without a sprite and a body');
|
|
57
|
-
}
|
|
58
|
-
if (objectBody) {
|
|
59
|
-
objectBody.entity = this;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
onSpawned(world: Gg2dWorld) {
|
|
64
|
-
super.onSpawned(world);
|
|
65
|
-
this.object2D?.addToWorld(world.visualScene);
|
|
66
|
-
this.objectBody?.addToWorld(world.physicsWorld);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
onRemoved() {
|
|
70
|
-
this.object2D?.removeFromWorld(this.world!.visualScene);
|
|
71
|
-
this.objectBody?.removeFromWorld(this.world!.physicsWorld);
|
|
72
|
-
super.onRemoved();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
dispose(): void {
|
|
76
|
-
super.dispose();
|
|
77
|
-
this.tickSub?.unsubscribe();
|
|
78
|
-
this.tickSub = null;
|
|
79
|
-
if (this.object2D) {
|
|
80
|
-
this.object2D.dispose();
|
|
81
|
-
}
|
|
82
|
-
if (this.objectBody) {
|
|
83
|
-
this.objectBody.dispose();
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { IGg2dTrigger } from '../interfaces';
|
|
2
|
-
import { Gg2dEntity } from './gg-2d-entity';
|
|
3
|
-
import { Observable } from 'rxjs';
|
|
4
|
-
import { GgPositionable2dEntity } from './gg-positionable-2d-entity';
|
|
5
|
-
|
|
6
|
-
export class Gg2dTriggerEntity extends Gg2dEntity {
|
|
7
|
-
get onEntityEntered(): Observable<GgPositionable2dEntity> {
|
|
8
|
-
return this.objectBody.onEntityEntered;
|
|
9
|
-
}
|
|
10
|
-
get onEntityLeft(): Observable<GgPositionable2dEntity | null> {
|
|
11
|
-
return this.objectBody.onEntityLeft;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
constructor(public readonly objectBody: IGg2dTrigger) {
|
|
15
|
-
super(null, objectBody);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Point2 } from '../../base/models/points';
|
|
2
|
-
import { GgPositionableEntity } from '../../base/entities/gg-positionable-entity';
|
|
3
|
-
|
|
4
|
-
export abstract class GgPositionable2dEntity extends GgPositionableEntity<Point2, number> {
|
|
5
|
-
getDefaultPosition(): Point2 {
|
|
6
|
-
return { x: 0, y: 0 };
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
getDefaultRotation(): number {
|
|
10
|
-
return 0;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
getDefaultScale(): Point2 {
|
|
14
|
-
return { x: 1, y: 1 };
|
|
15
|
-
}
|
|
16
|
-
}
|
package/src/2d/factories.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { BodyShape2DDescriptor, Shape2DDescriptor } from './models/shapes';
|
|
2
|
-
import { Point2 } from '../base/models/points';
|
|
3
|
-
import { IGg2dBody, IGg2dObject, IGg2dTrigger } from './interfaces';
|
|
4
|
-
|
|
5
|
-
export abstract class IGg2dObjectFactory<T extends IGg2dObject = IGg2dObject> {
|
|
6
|
-
abstract createPrimitive(descriptor: Shape2DDescriptor): T;
|
|
7
|
-
|
|
8
|
-
// shortcuts
|
|
9
|
-
createSquare(dimensions: Point2): T {
|
|
10
|
-
return this.createPrimitive({ shape: 'SQUARE', dimensions });
|
|
11
|
-
}
|
|
12
|
-
createCircle(radius: number): T {
|
|
13
|
-
return this.createPrimitive({ shape: 'CIRCLE', radius });
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface IGg2dBodyFactory<T extends IGg2dBody = IGg2dBody, K extends IGg2dTrigger = IGg2dTrigger> {
|
|
18
|
-
createRigidBody(descriptor: BodyShape2DDescriptor, transform?: { position?: Point2; rotation?: number }): T;
|
|
19
|
-
createTrigger(descriptor: Shape2DDescriptor, transform?: { position?: Point2; rotation?: number }): K;
|
|
20
|
-
}
|
package/src/2d/gg-2d-world.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { GgWorld } from '../base/gg-world';
|
|
2
|
-
import { Point2 } from '../base/models/points';
|
|
3
|
-
import { IGg2dPhysicsWorld, IGg2dVisualScene } from './interfaces';
|
|
4
|
-
|
|
5
|
-
export class Gg2dWorld<
|
|
6
|
-
V extends IGg2dVisualScene = IGg2dVisualScene,
|
|
7
|
-
P extends IGg2dPhysicsWorld = IGg2dPhysicsWorld,
|
|
8
|
-
> extends GgWorld<Point2, number, V, P> {
|
|
9
|
-
constructor(
|
|
10
|
-
public readonly visualScene: V,
|
|
11
|
-
public readonly physicsWorld: P,
|
|
12
|
-
protected readonly consoleEnabled: boolean = false,
|
|
13
|
-
) {
|
|
14
|
-
super(visualScene, physicsWorld, consoleEnabled);
|
|
15
|
-
if (consoleEnabled) {
|
|
16
|
-
this.registerConsoleCommand(
|
|
17
|
-
'ph_gravity',
|
|
18
|
-
async (...args: string[]) => {
|
|
19
|
-
if (args.length == 1) {
|
|
20
|
-
args = ['0', args[0]]; // mean Y axis
|
|
21
|
-
}
|
|
22
|
-
if (args.length > 0) {
|
|
23
|
-
this.physicsWorld.gravity = { x: +args[0], y: +args[1] };
|
|
24
|
-
}
|
|
25
|
-
return JSON.stringify(this.physicsWorld.gravity);
|
|
26
|
-
},
|
|
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
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
package/src/2d/interfaces.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { GgBody } from '../base/interfaces/gg-body';
|
|
2
|
-
import { Point2 } from '../base/models/points';
|
|
3
|
-
import { GgObject } from '../base/interfaces/gg-object';
|
|
4
|
-
import { GgPhysicsWorld } from '../base/interfaces/gg-physics-world';
|
|
5
|
-
import { GgVisualScene } from '../base/interfaces/gg-visual-scene';
|
|
6
|
-
import { BaseGgRenderer } from '../base/entities/base-gg-renderer';
|
|
7
|
-
import { GgTrigger } from '../base/interfaces/gg-trigger';
|
|
8
|
-
import { Observable } from 'rxjs';
|
|
9
|
-
import { GgPositionable2dEntity } from './entities/gg-positionable-2d-entity';
|
|
10
|
-
import { IGg2dBodyFactory, IGg2dObjectFactory } from './factories';
|
|
11
|
-
|
|
12
|
-
// These interfaces have to be implemented for a particular 2D physics engine
|
|
13
|
-
export interface IGg2dPhysicsWorld extends GgPhysicsWorld<Point2, number> {
|
|
14
|
-
readonly factory: IGg2dBodyFactory;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export abstract class Gg2dRenderer extends BaseGgRenderer {}
|
|
18
|
-
|
|
19
|
-
export interface IGg2dBody extends GgBody<Point2, number> {}
|
|
20
|
-
|
|
21
|
-
export interface IGg2dTrigger extends GgTrigger<Point2, number> {
|
|
22
|
-
get onEntityEntered(): Observable<GgPositionable2dEntity>;
|
|
23
|
-
get onEntityLeft(): Observable<GgPositionable2dEntity | null>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// These interfaces have to be implemented for a particular 2D rendering engine
|
|
27
|
-
export interface IGg2dVisualScene extends GgVisualScene<Point2, number> {
|
|
28
|
-
readonly factory: IGg2dObjectFactory;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface IGg2dObject extends GgObject<Point2, number> {}
|
package/src/2d/models/shapes.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { Point2 } from '../../base/models/points';
|
|
2
|
-
import { Body2DOptions } from './body-options';
|
|
3
|
-
|
|
4
|
-
export type Shape2DDescriptor = { shape: 'SQUARE'; dimensions: Point2 } | { shape: 'CIRCLE'; radius: number };
|
|
5
|
-
// TODO add more shapes
|
|
6
|
-
|
|
7
|
-
export type BodyShape2DDescriptor = { shape: Shape2DDescriptor; body: Partial<Body2DOptions> };
|
|
@@ -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
|
-
}
|