@gg-web-engine/core 0.0.56 → 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.
@@ -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
+ }
@@ -0,0 +1,96 @@
1
+ import { BodyOptions, IEntity, Point3, Point4 } from '../../base';
2
+ import { Gg3dWorld, PhysicsTypeDocRepo3D, VisualTypeDocRepo3D } from '../gg-3d-world';
3
+ import { Shape3DDescriptor } from '../models/shapes';
4
+ /**
5
+ * Represents a function that calculates the surface position and normal
6
+ * for a given point in 3D space.
7
+ */
8
+ export type SurfaceFollowFunc = (p: Point3) => {
9
+ position: Point3;
10
+ normal: Point3;
11
+ };
12
+ /**
13
+ * Represents an entity that follows a surface dynamically by adjusting
14
+ * its position and orientation based on a given surface function.
15
+ *
16
+ * @template PTypeDoc - The physics type document repository.
17
+ */
18
+ export declare class SurfaceFollowingEntity<PTypeDoc extends PhysicsTypeDocRepo3D = PhysicsTypeDocRepo3D> extends IEntity<Point3, Point4, VisualTypeDocRepo3D, PTypeDoc> {
19
+ /** Function that determines surface position and normal. */
20
+ followFunc: SurfaceFollowFunc;
21
+ /** Optional body configuration. */
22
+ protected bodyOptions: Partial<Omit<BodyOptions, 'dynamic' | 'mass' | 'ownCollisionGroups' | 'interactWithCollisionGroups'>>;
23
+ /**
24
+ * Determines the execution order for physics simulation.
25
+ */
26
+ readonly tickOrder: number;
27
+ /**
28
+ * Debugging settings for the surface-following entity.
29
+ */
30
+ readonly debugBodySettings: SurfaceFollowingEntityDebugSettings;
31
+ constructor(
32
+ /** Function that determines surface position and normal. */
33
+ followFunc: SurfaceFollowFunc,
34
+ /** Optional body configuration. */
35
+ bodyOptions?: Partial<Omit<BodyOptions, 'dynamic' | 'mass' | 'ownCollisionGroups' | 'interactWithCollisionGroups'>>);
36
+ /**
37
+ * Stores colliders and their associated metadata.
38
+ */
39
+ private colliders;
40
+ /**
41
+ * Sets up a collider by assigning it a collision group and a dynamic plane.
42
+ *
43
+ * @param collider - The physics rigid body to configure.
44
+ */
45
+ private setupCollider;
46
+ /**
47
+ * Adds a collider to the entity, registering it immediately if the entity
48
+ * is already in a world.
49
+ *
50
+ * @param collider - The physics rigid body to add.
51
+ */
52
+ addCollider(collider: PTypeDoc['rigidBody']): void;
53
+ /**
54
+ * Removes a collider and deregisters its associated collision group.
55
+ *
56
+ * @param collider - The physics rigid body to remove.
57
+ */
58
+ removeCollider(collider: PTypeDoc['rigidBody']): void;
59
+ /**
60
+ * Called when the entity is added to a world.
61
+ *
62
+ * @param world - The game world instance.
63
+ */
64
+ onSpawned(world: Gg3dWorld<VisualTypeDocRepo3D, PTypeDoc>): void;
65
+ private customGlobalDebugDummyBody;
66
+ /**
67
+ * Updates debug visualization if active.
68
+ */
69
+ private updateDebugView;
70
+ /**
71
+ * Called when the entity is removed from the world.
72
+ */
73
+ onRemoved(): void;
74
+ /**
75
+ * Positions planes based on the surface-follow function.
76
+ */
77
+ protected positionPlanes(): void;
78
+ }
79
+ /**
80
+ * Stores debug settings for a SurfaceFollowingEntity, allowing customization
81
+ * of the debug mesh.
82
+ */
83
+ export declare class SurfaceFollowingEntityDebugSettings {
84
+ private _hexMeshStepDistance;
85
+ private _hexMeshDepth;
86
+ private _customGlobalShape;
87
+ private _revision;
88
+ get revision(): number;
89
+ get hexMeshStepDistance(): number;
90
+ set hexMeshStepDistance(value: number);
91
+ get hexMeshDepth(): number;
92
+ set hexMeshDepth(value: number);
93
+ get customGlobalShape(): Shape3DDescriptor | null;
94
+ set customGlobalShape(value: Shape3DDescriptor | null);
95
+ constructor(_hexMeshStepDistance?: number, _hexMeshDepth?: number, _customGlobalShape?: Shape3DDescriptor | null);
96
+ }
@@ -0,0 +1,296 @@
1
+ import { takeUntil } from 'rxjs';
2
+ import { IEntity, Pnt2, Pnt3, Qtrn, TickOrder } from '../../base';
3
+ /**
4
+ * Represents an entity that follows a surface dynamically by adjusting
5
+ * its position and orientation based on a given surface function.
6
+ *
7
+ * @template PTypeDoc - The physics type document repository.
8
+ */
9
+ export class SurfaceFollowingEntity extends IEntity {
10
+ constructor(
11
+ /** Function that determines surface position and normal. */
12
+ followFunc,
13
+ /** Optional body configuration. */
14
+ bodyOptions = {}) {
15
+ super();
16
+ this.followFunc = followFunc;
17
+ this.bodyOptions = bodyOptions;
18
+ /**
19
+ * Determines the execution order for physics simulation.
20
+ */
21
+ this.tickOrder = TickOrder.PHYSICS_SIMULATION - 1;
22
+ /**
23
+ * Debugging settings for the surface-following entity.
24
+ */
25
+ this.debugBodySettings = new SurfaceFollowingEntityDebugSettings();
26
+ /**
27
+ * Stores colliders and their associated metadata.
28
+ */
29
+ this.colliders = new Map();
30
+ this.customGlobalDebugDummyBody = null;
31
+ }
32
+ /**
33
+ * Sets up a collider by assigning it a collision group and a dynamic plane.
34
+ *
35
+ * @param collider - The physics rigid body to configure.
36
+ */
37
+ setupCollider(collider) {
38
+ if (!this.world) {
39
+ throw new Error('Cannot setup collider if not added to the world');
40
+ }
41
+ let cg = this.world.physicsWorld.registerCollisionGroup();
42
+ collider.ownCollisionGroups = [...collider.ownCollisionGroups, cg];
43
+ collider.interactWithCollisionGroups = [...collider.ownCollisionGroups, cg];
44
+ let plane = this.world.physicsWorld.factory.createRigidBody({
45
+ shape: { shape: 'PLANE' },
46
+ body: Object.assign(Object.assign({}, this.bodyOptions), { dynamic: false, ownCollisionGroups: [cg], interactWithCollisionGroups: [cg] }),
47
+ });
48
+ if (this.debugBodySettings.customGlobalShape) {
49
+ plane.debugBodySettings.shape = { shape: 'SPHERE', radius: 0.25 };
50
+ }
51
+ else {
52
+ plane.debugBodySettings.ignoreTransform = true;
53
+ }
54
+ this.addComponents(plane);
55
+ this.colliders.set(collider, [cg, plane, {}]);
56
+ }
57
+ /**
58
+ * Adds a collider to the entity, registering it immediately if the entity
59
+ * is already in a world.
60
+ *
61
+ * @param collider - The physics rigid body to add.
62
+ */
63
+ addCollider(collider) {
64
+ if (this.world) {
65
+ this.setupCollider(collider);
66
+ }
67
+ else {
68
+ this.colliders.set(collider, null);
69
+ }
70
+ }
71
+ /**
72
+ * Removes a collider and deregisters its associated collision group.
73
+ *
74
+ * @param collider - The physics rigid body to remove.
75
+ */
76
+ removeCollider(collider) {
77
+ if (!this.colliders.has(collider)) {
78
+ return;
79
+ }
80
+ const item = this.colliders.get(collider);
81
+ if (item) {
82
+ const [cg, plane] = item;
83
+ this.removeComponents([plane], true);
84
+ collider.ownCollisionGroups = collider.ownCollisionGroups.filter(g => g !== cg);
85
+ collider.interactWithCollisionGroups = collider.interactWithCollisionGroups.filter(g => g !== cg);
86
+ this.world.physicsWorld.deregisterCollisionGroup(cg);
87
+ }
88
+ this.colliders.delete(collider);
89
+ }
90
+ /**
91
+ * Called when the entity is added to a world.
92
+ *
93
+ * @param world - The game world instance.
94
+ */
95
+ onSpawned(world) {
96
+ super.onSpawned(world);
97
+ for (const [collider] of this.colliders.entries()) {
98
+ this.setupCollider(collider);
99
+ }
100
+ world.physicsWorld.removed$.pipe(takeUntil(this.onRemoved$)).subscribe(c => {
101
+ if (this.colliders.has(c)) {
102
+ this.removeCollider(c);
103
+ }
104
+ });
105
+ this.tick$.pipe(takeUntil(this.onRemoved$)).subscribe(() => {
106
+ this.positionPlanes();
107
+ this.updateDebugView();
108
+ });
109
+ this.positionPlanes();
110
+ this.updateDebugView();
111
+ }
112
+ /**
113
+ * Updates debug visualization if active.
114
+ */
115
+ updateDebugView() {
116
+ const updateDebugView = !!this.world.renderers.find(r => r.physicsDebugViewActive);
117
+ if (updateDebugView) {
118
+ if (this.debugBodySettings.customGlobalShape) {
119
+ if (!this.customGlobalDebugDummyBody) {
120
+ this.customGlobalDebugDummyBody = this.world.physicsWorld.factory.createRigidBody({
121
+ shape: { shape: 'BOX', dimensions: Pnt3.O },
122
+ body: { dynamic: false },
123
+ });
124
+ this.customGlobalDebugDummyBody.ownCollisionGroups = [];
125
+ this.customGlobalDebugDummyBody.interactWithCollisionGroups = [];
126
+ this.customGlobalDebugDummyBody.debugBodySettings.type = { type: 'RIGID_STATIC' };
127
+ this.customGlobalDebugDummyBody.debugBodySettings.shape = this.debugBodySettings.customGlobalShape;
128
+ this.addComponents(this.customGlobalDebugDummyBody);
129
+ for (const item of this.colliders.values()) {
130
+ if (item) {
131
+ let [_, plane] = item;
132
+ plane.debugBodySettings.shape = { shape: 'SPHERE', radius: 0.25 };
133
+ plane.debugBodySettings.ignoreTransform = false;
134
+ }
135
+ }
136
+ }
137
+ else {
138
+ this.customGlobalDebugDummyBody.debugBodySettings.shape = this.debugBodySettings.customGlobalShape;
139
+ }
140
+ }
141
+ else {
142
+ if (this.customGlobalDebugDummyBody) {
143
+ this.removeComponents([this.customGlobalDebugDummyBody], true);
144
+ this.customGlobalDebugDummyBody = null;
145
+ // reset caches
146
+ for (const item of this.colliders.values()) {
147
+ if (item) {
148
+ item[2] = {};
149
+ }
150
+ }
151
+ }
152
+ for (const [collider, item] of this.colliders.entries()) {
153
+ let [_, plane, debugCache] = item;
154
+ const { position, normal } = this.followFunc(collider.position);
155
+ let xThreshold = this.debugBodySettings.hexMeshStepDistance;
156
+ let yThreshold = this.debugBodySettings.hexMeshStepDistance * Math.sqrt(3);
157
+ let zThreshold = this.debugBodySettings.hexMeshStepDistance * 2;
158
+ let hexMeshStartPosition = {
159
+ x: Math.round(position.x / xThreshold) * xThreshold,
160
+ y: Math.round(position.y / yThreshold) * yThreshold,
161
+ z: Math.round(position.z / zThreshold) * zThreshold,
162
+ };
163
+ if (debugCache.lastDebugSettingsRev != this.debugBodySettings.revision ||
164
+ !debugCache.lastDebugStartPos ||
165
+ Pnt3.dist(debugCache.lastDebugStartPos, hexMeshStartPosition) > 1) {
166
+ let { vertices, faces } = buildHexMeshAlongSurface(hexMeshStartPosition, this.followFunc, this.debugBodySettings.hexMeshDepth, this.debugBodySettings.hexMeshStepDistance);
167
+ plane.debugBodySettings.shape = {
168
+ shape: 'MESH',
169
+ vertices,
170
+ faces,
171
+ };
172
+ plane.debugBodySettings.ignoreTransform = true;
173
+ debugCache.lastDebugStartPos = hexMeshStartPosition;
174
+ debugCache.lastDebugSettingsRev = this.debugBodySettings.revision;
175
+ }
176
+ }
177
+ }
178
+ }
179
+ }
180
+ /**
181
+ * Called when the entity is removed from the world.
182
+ */
183
+ onRemoved() {
184
+ for (const [collider, item] of this.colliders) {
185
+ if (item) {
186
+ const [cg, plane] = item;
187
+ this.removeComponents([plane], true);
188
+ collider.ownCollisionGroups = collider.ownCollisionGroups.filter(g => g !== cg);
189
+ collider.interactWithCollisionGroups = collider.interactWithCollisionGroups.filter(g => g !== cg);
190
+ this.world.physicsWorld.deregisterCollisionGroup(cg);
191
+ }
192
+ }
193
+ this.colliders.clear();
194
+ super.onRemoved();
195
+ }
196
+ /**
197
+ * Positions planes based on the surface-follow function.
198
+ */
199
+ positionPlanes() {
200
+ if (!this.world) {
201
+ return;
202
+ }
203
+ for (const [collider, item] of this.colliders.entries()) {
204
+ let [_, plane] = item;
205
+ const { position, normal } = this.followFunc(collider.position);
206
+ plane.position = position;
207
+ plane.rotation = Qtrn.lookAt(normal, Pnt3.O);
208
+ }
209
+ }
210
+ }
211
+ /**
212
+ * Stores debug settings for a SurfaceFollowingEntity, allowing customization
213
+ * of the debug mesh.
214
+ */
215
+ export class SurfaceFollowingEntityDebugSettings {
216
+ get revision() {
217
+ return this._revision;
218
+ }
219
+ get hexMeshStepDistance() {
220
+ return this._hexMeshStepDistance;
221
+ }
222
+ set hexMeshStepDistance(value) {
223
+ this._hexMeshStepDistance = value;
224
+ this._revision++;
225
+ }
226
+ get hexMeshDepth() {
227
+ return this._hexMeshDepth;
228
+ }
229
+ set hexMeshDepth(value) {
230
+ this._hexMeshDepth = value;
231
+ this._revision++;
232
+ }
233
+ get customGlobalShape() {
234
+ return this._customGlobalShape;
235
+ }
236
+ set customGlobalShape(value) {
237
+ this._customGlobalShape = value;
238
+ this._revision++;
239
+ }
240
+ constructor(_hexMeshStepDistance = 4, _hexMeshDepth = 6, _customGlobalShape = null) {
241
+ this._hexMeshStepDistance = _hexMeshStepDistance;
242
+ this._hexMeshDepth = _hexMeshDepth;
243
+ this._customGlobalShape = _customGlobalShape;
244
+ this._revision = 0;
245
+ }
246
+ }
247
+ const buildHexMeshAlongSurface = (start, surfaceFunc, depth, stepDistance) => {
248
+ let startPoint = surfaceFunc(start);
249
+ const vertices = [startPoint.position];
250
+ const faces = [];
251
+ let lastLoop = [
252
+ {
253
+ position: startPoint.position,
254
+ normal: startPoint.normal,
255
+ vertexIndex: 0,
256
+ },
257
+ ];
258
+ const traverse = (point, angle) => {
259
+ let pnt2 = Pnt2.rot(Pnt2.scalarMult(Pnt2.X, stepDistance), angle);
260
+ let snorm = Pnt3.toSpherical(point.normal);
261
+ let spnt = Pnt3.toSpherical(Object.assign(Object.assign({}, pnt2), { z: 0 }));
262
+ return Pnt3.add(point.position, Pnt3.fromSpherical(Object.assign(Object.assign({}, spnt), { phi: spnt.phi + Math.cos(snorm.theta - spnt.theta) * snorm.phi })));
263
+ };
264
+ for (let i = 0; i < depth; i++) {
265
+ let newLoop = [];
266
+ if (i == 0) {
267
+ for (let j = 0; j < 6; j++) {
268
+ let point = surfaceFunc(traverse(startPoint, (j * Math.PI) / 3));
269
+ newLoop.push(Object.assign(Object.assign({}, point), { vertexIndex: j + 1 }));
270
+ faces.push([0, j + 1, j < 5 ? j + 2 : 1]);
271
+ }
272
+ }
273
+ else {
274
+ for (let j = 0; j < lastLoop.length; j++) {
275
+ let startPoint = lastLoop[j];
276
+ let angle = (Math.floor((j * 6) / lastLoop.length) * Math.PI) / 3;
277
+ let rightNeighbour = lastLoop[j < lastLoop.length - 1 ? j + 1 : 0];
278
+ if (j % (lastLoop.length / 6) == 0) {
279
+ // hex corner vertex, add new vertex in this loop. Each next loop has +6 vertices
280
+ let pointF = surfaceFunc(traverse(startPoint, angle));
281
+ let vi = vertices.length + newLoop.length;
282
+ newLoop.push(Object.assign(Object.assign({}, pointF), { vertexIndex: vi }));
283
+ faces.push([startPoint.vertexIndex, j > 0 ? vi - 1 : vi + lastLoop.length + 5, vi]);
284
+ }
285
+ let pointR = surfaceFunc(Pnt3.avg(traverse(startPoint, angle + Math.PI / 3), traverse(rightNeighbour, angle)));
286
+ let vi = vertices.length + newLoop.length;
287
+ newLoop.push(Object.assign(Object.assign({}, pointR), { vertexIndex: vi }));
288
+ faces.push([startPoint.vertexIndex, vi - 1, vi]);
289
+ faces.push([rightNeighbour.vertexIndex, startPoint.vertexIndex, vi]);
290
+ }
291
+ }
292
+ vertices.push(...newLoop.map(({ position }) => position));
293
+ lastLoop = newLoop;
294
+ }
295
+ return { vertices, faces };
296
+ };
@@ -19,6 +19,7 @@ export * from './entities/controllers/input/car-keyboard-handling.controller';
19
19
  export * from './entities/controllers/input/gg-car-keyboard-handling.controller';
20
20
  export * from './entities/controllers/input/free-camera.controller';
21
21
  export * from './entities/controllers/input/orbit-camera.controller';
22
+ export * from './entities/surface-following.entity';
22
23
  export * from './interfaces/i-positionable-3d';
23
24
  export * from './models/body-options';
24
25
  export * from './models/gg-meta';
package/dist/3d/index.js CHANGED
@@ -19,6 +19,7 @@ export * from './entities/controllers/input/car-keyboard-handling.controller';
19
19
  export * from './entities/controllers/input/gg-car-keyboard-handling.controller';
20
20
  export * from './entities/controllers/input/free-camera.controller';
21
21
  export * from './entities/controllers/input/orbit-camera.controller';
22
+ export * from './entities/surface-following.entity';
22
23
  export * from './interfaces/i-positionable-3d';
23
24
  export * from './models/body-options';
24
25
  export * from './models/gg-meta';
@@ -1,7 +1,7 @@
1
- import { BodyOptions, DebugBodySettings } from '../../base';
1
+ import { BodyOptions, DebugBodySettings, DebugBodyType } from '../../base';
2
2
  import { Shape3DDescriptor } from './shapes';
3
3
  export interface Body3DOptions extends BodyOptions {
4
4
  }
5
- export type DebugBody3DSettings = DebugBodySettings & {
6
- shape: Shape3DDescriptor;
7
- };
5
+ export declare class DebugBody3DSettings extends DebugBodySettings<Shape3DDescriptor> {
6
+ constructor(type: DebugBodyType, shape: Shape3DDescriptor, ignoreTransform?: boolean, color?: number | undefined);
7
+ }
@@ -1 +1,6 @@
1
- export {};
1
+ import { DebugBodySettings } from '../../base';
2
+ export class DebugBody3DSettings extends DebugBodySettings {
3
+ constructor(type, shape, ignoreTransform = false, color = undefined) {
4
+ super(type, shape, ignoreTransform, color);
5
+ }
6
+ }
@@ -7,12 +7,12 @@ export interface IBodyComponent<D, R, TypeDoc extends PhysicsTypeDocRepo<D, R> =
7
7
  position: D;
8
8
  rotation: R;
9
9
  name: string;
10
- get ownCollisionGroups(): CollisionGroup[];
11
- set ownCollisionGroups(value: CollisionGroup[] | 'all');
12
- get interactWithCollisionGroups(): CollisionGroup[];
13
- set interactWithCollisionGroups(value: CollisionGroup[] | 'all');
10
+ get ownCollisionGroups(): ReadonlyArray<CollisionGroup>;
11
+ set ownCollisionGroups(value: ReadonlyArray<CollisionGroup> | 'all');
12
+ get interactWithCollisionGroups(): ReadonlyArray<CollisionGroup>;
13
+ set interactWithCollisionGroups(value: ReadonlyArray<CollisionGroup> | 'all');
14
14
  /** body info for physics debugger view */
15
- readonly debugBodySettings: DebugBodySettings;
15
+ readonly debugBodySettings: DebugBodySettings<any>;
16
16
  clone(): IBodyComponent<D, R, TypeDoc>;
17
17
  addToWorld(world: GgWorld<D, R, VisualTypeDocRepo<D, R>, TypeDoc>): void;
18
18
  removeFromWorld(world: GgWorld<D, R, VisualTypeDocRepo<D, R>, TypeDoc>): void;
@@ -2,22 +2,60 @@ import { PhysicsTypeDocRepo } from '../../gg-world';
2
2
  import { IComponent } from '../i-component';
3
3
  import { CollisionGroup } from '../../models/body-options';
4
4
  import { Subject } from 'rxjs';
5
+ /**
6
+ * Interface representing a physics world component.
7
+ *
8
+ * @template D - Data type used for representing physics properties.
9
+ * @template R - Type representing the physics engine's rigid body.
10
+ * @template TypeDoc - Physics typings repository.
11
+ */
5
12
  export interface IPhysicsWorldComponent<D, R, TypeDoc extends PhysicsTypeDocRepo<D, R> = PhysicsTypeDocRepo<D, R>> extends IComponent {
13
+ /**
14
+ * Factory function for creating physics-related objects.
15
+ */
6
16
  readonly factory: TypeDoc['factory'];
17
+ /**
18
+ * The gravity vector affecting the physics world.
19
+ */
7
20
  gravity: D;
8
- /** event emitter, emits newly added physics components */
21
+ /**
22
+ * Event emitter that emits newly added physics components.
23
+ */
9
24
  readonly added$: Subject<TypeDoc['rigidBody'] | TypeDoc['trigger'] | any>;
10
- /** event emitter, emits just removed physics components */
25
+ /**
26
+ * Event emitter that emits just removed physics components.
27
+ */
11
28
  readonly removed$: Subject<TypeDoc['rigidBody'] | TypeDoc['trigger'] | any>;
12
- /** list of currently added to world physics components */
29
+ /**
30
+ * List of currently added physics components in the world.
31
+ */
13
32
  readonly children: (TypeDoc['rigidBody'] | TypeDoc['trigger'] | any)[];
33
+ /**
34
+ * The main collision group. All physics bodies have this collision group set by default.
35
+ */
36
+ readonly mainCollisionGroup: CollisionGroup;
37
+ /**
38
+ * Initializes the physics world component.
39
+ *
40
+ * @returns A promise that resolves when initialization is complete.
41
+ */
14
42
  init(): Promise<void>;
15
43
  /**
16
- * Runs simulation of the physics world.
44
+ * Runs the simulation of the physics world for the given time step.
17
45
  *
18
- * @param delta delta time from last tick in milliseconds.
46
+ * @param delta - The time step in milliseconds since the last update.
19
47
  */
20
48
  simulate(delta: number): void;
49
+ /**
50
+ * Registers and returns a new collision group.
51
+ *
52
+ * @returns A newly registered collision group.
53
+ */
21
54
  registerCollisionGroup(): CollisionGroup;
55
+ /**
56
+ * Deregisters a previously registered collision group.
57
+ *
58
+ * @param group - The collision group to be removed.
59
+ */
22
60
  deregisterCollisionGroup(group: CollisionGroup): void;
23
61
  }
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * BitMask class provides static methods to manipulate bits in a number.
3
- */ /**
4
- * BitMask class provides static methods to manipulate bits in a number.
5
- */
3
+ */
4
+ /**
5
+ * BitMask class provides static methods to manipulate bits in a number.
6
+ */
6
7
  export declare class BitMask {
7
8
  /**
8
9
  * Generates a bitmask with all bits set to 1 upto the provided bit count
@@ -20,7 +21,7 @@ export declare class BitMask {
20
21
  * @param bits - The maximum bit index that can be used
21
22
  * @throws Will throw an Error if a bit index in `value` is larger than `bits`
22
23
  */
23
- static pack(value: number[], bits: number): number;
24
+ static pack(value: number[] | ReadonlyArray<number>, bits: number): number;
24
25
  /**
25
26
  * Unpacks a bitmask into an array of bit indices
26
27
  * @param mask - The bit mask to be unpacked
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * BitMask class provides static methods to manipulate bits in a number.
3
- */ /**
4
- * BitMask class provides static methods to manipulate bits in a number.
5
- */
3
+ */
4
+ /**
5
+ * BitMask class provides static methods to manipulate bits in a number.
6
+ */
6
7
  export class BitMask {
7
8
  /**
8
9
  * Generates a bitmask with all bits set to 1 upto the provided bit count
@@ -1,4 +1,4 @@
1
- import { IDisplayObjectComponent, IEntity, IPhysicsWorldComponent, IPositionable, IRenderableEntity, IRendererComponent, IRigidBodyComponent, ITriggerComponent, IVisualSceneComponent, KeyboardInput, PausableClock } from '../base';
1
+ import { IDisplayObjectComponent, IEntity, IPhysicsWorldComponent, IPositionable, IRenderableEntity, IRendererComponent, IRendererEntity, IRigidBodyComponent, ITriggerComponent, IVisualSceneComponent, KeyboardInput, PausableClock } from '../base';
2
2
  import { Subject } from 'rxjs';
3
3
  export type VisualTypeDocRepo<D, R> = {
4
4
  factory: unknown;
@@ -23,6 +23,7 @@ export declare abstract class GgWorld<D, R, VTypeDoc extends VisualTypeDocRepo<D
23
23
  name: string;
24
24
  readonly children: IEntity[];
25
25
  protected readonly tickListeners: IEntity[];
26
+ get renderers(): IRendererEntity<D, R>[];
26
27
  readonly tickStarted$: Subject<void>;
27
28
  readonly tickForwardTo$: Subject<IEntity | 'PHYSICS_WORLD'>;
28
29
  readonly tickForwardedTo$: Subject<IEntity | 'PHYSICS_WORLD'>;
@@ -14,6 +14,9 @@ export class GgWorld {
14
14
  static get documentWorlds() {
15
15
  return [...GgWorld._documentWorlds];
16
16
  }
17
+ get renderers() {
18
+ return this.tickListeners.filter(e => e instanceof IRendererEntity);
19
+ }
17
20
  constructor(visualScene, physicsWorld) {
18
21
  this.visualScene = visualScene;
19
22
  this.physicsWorld = physicsWorld;
@@ -159,10 +162,7 @@ export class GgWorld {
159
162
  return this.worldClock.tickRateLimit.toString();
160
163
  }), 'args: [ int? ]; Get current tick rate limit of selected world clock or set it. 0 means no limit applied');
161
164
  ggstatic.registerConsoleCommand(this, 'renderers', () => __awaiter(this, void 0, void 0, function* () {
162
- return this.children
163
- .filter(e => e instanceof IRendererEntity)
164
- .map(r => r.name)
165
- .join('\n');
165
+ return this.renderers.map(r => r.name).join('\n');
166
166
  }), 'no args; Print all renderers in selected world');
167
167
  ggstatic.registerConsoleCommand(this, 'debug_view', (...args) => __awaiter(this, void 0, void 0, function* () {
168
168
  let value = 'toggle';
@@ -175,7 +175,7 @@ export class GgWorld {
175
175
  rendererName = arg;
176
176
  }
177
177
  }
178
- let renderer = this.children.find(x => x instanceof IRendererEntity && (!rendererName || x.name === rendererName));
178
+ let renderer = rendererName ? this.renderers.find(x => x.name === rendererName) : this.renderers[0];
179
179
  if (renderer) {
180
180
  renderer.physicsDebugViewActive = value === 'toggle' ? !renderer.physicsDebugViewActive : value;
181
181
  return renderer.physicsDebugViewActive ? '1' : '0';
@@ -36,6 +36,8 @@ export declare class Pnt2 {
36
36
  static norm(p: Point2): Point2;
37
37
  /** scalar multiplication */
38
38
  static scalarMult(p: Point2, m: number): Point2;
39
+ /** dot multiplication */
40
+ static dot(a: Point2, b: Point2): number;
39
41
  /** linear interpolation */
40
42
  static lerp(a: Point2, b: Point2, t: number): Point2;
41
43
  /** angle between vectors in radians */
@@ -78,6 +78,10 @@ export class Pnt2 {
78
78
  y: p.y * m,
79
79
  };
80
80
  }
81
+ /** dot multiplication */
82
+ static dot(a, b) {
83
+ return a.x * b.x + a.y * b.y;
84
+ }
81
85
  /** linear interpolation */
82
86
  static lerp(a, b, t) {
83
87
  return {
@@ -87,9 +91,8 @@ export class Pnt2 {
87
91
  }
88
92
  /** angle between vectors in radians */
89
93
  static angle(a, b) {
90
- const dotProduct = a.x * b.x + a.y * b.y;
91
- const magnitudeProduct = Math.sqrt(a.x ** 2 + a.y ** 2) * Math.sqrt(b.x ** 2 + b.y ** 2);
92
- return Math.acos(dotProduct / magnitudeProduct);
94
+ const magnitudeProduct = Pnt2.len(a) * Pnt2.len(b);
95
+ return Math.acos(Pnt2.dot(a, b) / magnitudeProduct);
93
96
  }
94
97
  /** rotate point around zero by provided angle */
95
98
  static rot(p, angle) {
@@ -42,6 +42,8 @@ export declare class Pnt3 {
42
42
  static norm(p: Point3): Point3;
43
43
  /** scalar multiplication */
44
44
  static scalarMult(p: Point3, m: number): Point3;
45
+ /** dot multiplication */
46
+ static dot(a: Point3, b: Point3): number;
45
47
  /** linear interpolation */
46
48
  static lerp(a: Point3, b: Point3, t: number): Point3;
47
49
  /** angle between vectors in radians */
@@ -102,6 +102,10 @@ export class Pnt3 {
102
102
  z: p.z * m,
103
103
  };
104
104
  }
105
+ /** dot multiplication */
106
+ static dot(a, b) {
107
+ return a.x * b.x + a.y * b.y + a.z * b.z;
108
+ }
105
109
  /** linear interpolation */
106
110
  static lerp(a, b, t) {
107
111
  return {
@@ -112,9 +116,8 @@ export class Pnt3 {
112
116
  }
113
117
  /** angle between vectors in radians */
114
118
  static angle(a, b) {
115
- const dotProduct = a.x * b.x + a.y * b.y + a.z * b.z;
116
- const magnitudeProduct = Math.sqrt(a.x ** 2 + a.y ** 2 + a.z ** 2) * Math.sqrt(b.x ** 2 + b.y ** 2 + b.z ** 2);
117
- return Math.acos(dotProduct / magnitudeProduct);
119
+ const magnitudeProduct = Pnt3.len(a) * Pnt3.len(b);
120
+ return Math.acos(Pnt3.dot(a, b) / magnitudeProduct);
118
121
  }
119
122
  /** rotate point a with quaternion q */
120
123
  static rot(p, q) {
@@ -4,15 +4,33 @@ export interface BodyOptions {
4
4
  mass: number;
5
5
  restitution: number;
6
6
  friction: number;
7
- ownCollisionGroups: CollisionGroup[] | 'all';
8
- interactWithCollisionGroups: CollisionGroup[] | 'all';
7
+ ownCollisionGroups: ReadonlyArray<CollisionGroup> | 'all';
8
+ interactWithCollisionGroups: ReadonlyArray<CollisionGroup> | 'all';
9
9
  }
10
- export type DebugBodySettings = {
11
- shape: any;
12
- ignoreTransform?: boolean;
13
- } & ({
14
- type: 'RIGID_STATIC' | 'TRIGGER';
10
+ export type DebugBodyType = {
11
+ type: 'RIGID_STATIC';
12
+ } | {
13
+ type: 'TRIGGER';
14
+ activated: () => boolean;
15
15
  } | {
16
16
  type: 'RIGID_DYNAMIC';
17
- sleeping: boolean;
18
- });
17
+ sleeping: () => boolean;
18
+ };
19
+ export declare abstract class DebugBodySettings<S> {
20
+ private _type;
21
+ private _shape;
22
+ private _ignoreTransform;
23
+ private _color;
24
+ private _revision;
25
+ get revision(): number;
26
+ get type(): DebugBodyType;
27
+ set type(value: DebugBodyType);
28
+ get shape(): S;
29
+ set shape(value: S);
30
+ get ignoreTransform(): boolean;
31
+ set ignoreTransform(value: boolean);
32
+ private lastRetrievedColorCache;
33
+ get color(): number;
34
+ set color(value: number | undefined);
35
+ protected constructor(_type: DebugBodyType, _shape: S, _ignoreTransform?: boolean, _color?: number | undefined);
36
+ }
@@ -1 +1,65 @@
1
- export {};
1
+ export class DebugBodySettings {
2
+ get revision() {
3
+ // force check of color
4
+ let _ = this.color;
5
+ return this._revision;
6
+ }
7
+ get type() {
8
+ return this._type;
9
+ }
10
+ set type(value) {
11
+ this._type = value;
12
+ this._revision++;
13
+ }
14
+ get shape() {
15
+ return this._shape;
16
+ }
17
+ set shape(value) {
18
+ this._shape = value;
19
+ this._revision++;
20
+ }
21
+ get ignoreTransform() {
22
+ return this._ignoreTransform;
23
+ }
24
+ set ignoreTransform(value) {
25
+ this._ignoreTransform = value;
26
+ this._revision++;
27
+ }
28
+ get color() {
29
+ let color = 0;
30
+ if (this._color !== undefined) {
31
+ color = this._color;
32
+ }
33
+ else {
34
+ switch (this.type.type) {
35
+ case 'RIGID_DYNAMIC':
36
+ color = this.type.sleeping() ? 0x0000ff : 0xff0000;
37
+ break;
38
+ case 'RIGID_STATIC':
39
+ color = 0x00ff00;
40
+ break;
41
+ case 'TRIGGER':
42
+ color = this.type.activated() ? 0xff9900 : 0xffff00;
43
+ break;
44
+ }
45
+ }
46
+ if (color !== this.lastRetrievedColorCache) {
47
+ this._revision++;
48
+ this.lastRetrievedColorCache = color;
49
+ }
50
+ return color;
51
+ }
52
+ set color(value) {
53
+ this._color = value;
54
+ }
55
+ constructor(_type, _shape, _ignoreTransform = false, _color = undefined) {
56
+ this._type = _type;
57
+ this._shape = _shape;
58
+ this._ignoreTransform = _ignoreTransform;
59
+ this._color = _color;
60
+ // this value changes on each change inside the settings.
61
+ // Debug view skips updating anything if value stays the same
62
+ this._revision = 0;
63
+ this.lastRetrievedColorCache = 0;
64
+ }
65
+ }
@@ -1,4 +1,4 @@
1
- import { createInlineTickController, IRendererEntity } from '../base';
1
+ import { createInlineTickController } from '../base';
2
2
  import { animationFrameScheduler, fromEvent, of, Subject, takeUntil } from 'rxjs';
3
3
  import Stats from 'stats.js';
4
4
  import { repeat } from 'rxjs/operators';
@@ -139,19 +139,11 @@ export class GgDebuggerUI {
139
139
  }
140
140
  }
141
141
  makeSnapshot() {
142
- var _a, _b;
143
- const renderers = [];
144
- let performanceMeter = null;
145
- for (const e of ((_a = this.currentWorld) === null || _a === void 0 ? void 0 : _a.children) || []) {
146
- if (e instanceof IRendererEntity) {
147
- renderers.push(e);
148
- }
149
- else if (e instanceof PerformanceMeterEntity) {
150
- performanceMeter = e;
151
- }
152
- }
142
+ var _a, _b, _c;
143
+ const renderers = ((_a = this.currentWorld) === null || _a === void 0 ? void 0 : _a.renderers) || [];
144
+ let performanceMeter = (((_b = this.currentWorld) === null || _b === void 0 ? void 0 : _b.children) || []).find(e => e instanceof PerformanceMeterEntity);
153
145
  return {
154
- timeScale: ((_b = this.currentWorld) === null || _b === void 0 ? void 0 : _b.worldClock.timeScale) || NaN,
146
+ timeScale: ((_c = this.currentWorld) === null || _c === void 0 ? void 0 : _c.worldClock.timeScale) || NaN,
155
147
  renderers: renderers.map(r => ({
156
148
  name: r.name,
157
149
  entity: r,
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.0.56";
1
+ export declare const VERSION = "0.0.57";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.0.56';
1
+ export const VERSION = '0.0.57';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gg-web-engine/core",
3
- "version": "0.0.56",
3
+ "version": "0.0.57",
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",