@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.
- package/dist/2d/entities/entity-2d.d.ts +6 -3
- package/dist/2d/entities/entity-2d.js +20 -17
- package/dist/2d/gg-2d-world.d.ts +3 -0
- package/dist/2d/gg-2d-world.js +21 -13
- package/dist/2d/models/body-options.d.ts +4 -4
- package/dist/2d/models/body-options.js +6 -1
- package/dist/3d/entities/controllers/input/free-camera.controller.d.ts +9 -1
- package/dist/3d/entities/controllers/input/free-camera.controller.js +45 -12
- package/dist/3d/entities/controllers/input/orbit-camera.controller.d.ts +9 -8
- package/dist/3d/entities/controllers/input/orbit-camera.controller.js +46 -32
- package/dist/3d/entities/entity-3d.d.ts +6 -3
- package/dist/3d/entities/entity-3d.js +20 -17
- package/dist/3d/entities/raycast-vehicle-3d.entity.js +5 -5
- package/dist/3d/entities/surface-following.entity.d.ts +96 -0
- package/dist/3d/entities/surface-following.entity.js +296 -0
- package/dist/3d/gg-3d-world.d.ts +3 -0
- package/dist/3d/gg-3d-world.js +21 -13
- package/dist/3d/index.d.ts +1 -0
- package/dist/3d/index.js +1 -0
- package/dist/3d/loader.js +1 -1
- package/dist/3d/models/body-options.d.ts +4 -4
- package/dist/3d/models/body-options.js +6 -1
- package/dist/base/clock/global-clock.d.ts +2 -6
- package/dist/base/clock/global-clock.js +16 -16
- package/dist/base/clock/i-clock.d.ts +14 -5
- package/dist/base/clock/i-clock.js +43 -1
- package/dist/base/clock/pausable-clock.d.ts +3 -13
- package/dist/base/clock/pausable-clock.js +8 -16
- package/dist/base/components/physics/i-body.component.d.ts +5 -5
- package/dist/base/components/physics/i-physics-world.component.d.ts +43 -5
- package/dist/base/data-structures/bitmask.d.ts +5 -4
- package/dist/base/data-structures/bitmask.js +4 -3
- package/dist/base/gg-world.d.ts +8 -1
- package/dist/base/gg-world.js +102 -65
- package/dist/base/inputs/keyboard.input.d.ts +1 -1
- package/dist/base/inputs/keyboard.input.js +10 -10
- package/dist/base/inputs/mouse.input.js +3 -2
- package/dist/base/math/point2.d.ts +2 -0
- package/dist/base/math/point2.js +6 -3
- package/dist/base/math/point3.d.ts +2 -0
- package/dist/base/math/point3.js +6 -3
- package/dist/base/models/body-options.d.ts +27 -9
- package/dist/base/models/body-options.js +65 -1
- package/dist/dev/gg-console.ui.js +14 -4
- package/dist/dev/gg-debugger.ui.d.ts +2 -2
- package/dist/dev/gg-debugger.ui.js +15 -20
- package/dist/dev/gg-static.d.ts +6 -1
- package/dist/dev/gg-static.js +104 -25
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +4 -3
|
@@ -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
|
+
};
|
package/dist/3d/gg-3d-world.d.ts
CHANGED
|
@@ -36,4 +36,7 @@ export declare class Gg3dWorld<VTypeDoc extends VisualTypeDocRepo3D = VisualType
|
|
|
36
36
|
constructor(visualScene: VS, physicsWorld: PW);
|
|
37
37
|
addPrimitiveRigidBody(descr: BodyShape3DDescriptor, position?: Point3, rotation?: Point4, material?: DisplayObject3dOpts<VTypeDoc['texture']>): Entity3d<VTypeDoc, PTypeDoc>;
|
|
38
38
|
addRenderer(camera: VTypeDoc['camera'], canvas?: HTMLCanvasElement, rendererOptions?: Partial<RendererOptions & VTypeDoc['rendererExtraOpts']>): Renderer3dEntity<VTypeDoc>;
|
|
39
|
+
protected registerConsoleCommands(ggstatic: {
|
|
40
|
+
registerConsoleCommand: (world: GgWorld<any, any> | null, command: string, handler: (...args: string[]) => Promise<string>, doc?: string) => void;
|
|
41
|
+
}): void;
|
|
39
42
|
}
|
package/dist/3d/gg-3d-world.js
CHANGED
|
@@ -17,21 +17,12 @@ export class Gg3dWorld extends GgWorld {
|
|
|
17
17
|
this.visualScene = visualScene;
|
|
18
18
|
this.physicsWorld = physicsWorld;
|
|
19
19
|
this.loader = new Gg3dLoader(this);
|
|
20
|
-
if (window.ggstatic) {
|
|
21
|
-
window.ggstatic.registerConsoleCommand(this, 'ph_gravity', (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
22
|
-
if (args.length == 1) {
|
|
23
|
-
args = ['0', '0', '' + -+args[0]]; // mean -Z axis
|
|
24
|
-
}
|
|
25
|
-
if (args.length > 0) {
|
|
26
|
-
this.physicsWorld.gravity = { x: +args[0], y: +args[1], z: +args[2] };
|
|
27
|
-
}
|
|
28
|
-
return JSON.stringify(this.physicsWorld.gravity);
|
|
29
|
-
}), 'args: [float] or [float float float]; change 3D world gravity vector. 1 argument means ' +
|
|
30
|
-
'{x: 0, y: 0, z: -value}, 3 arguments set the whole vector. Default value is "9.82" or "0 0 -9.82"');
|
|
31
|
-
}
|
|
32
20
|
}
|
|
33
21
|
addPrimitiveRigidBody(descr, position = Pnt3.O, rotation = Qtrn.O, material = {}) {
|
|
34
|
-
const entity = new Entity3d(
|
|
22
|
+
const entity = new Entity3d({
|
|
23
|
+
object3D: this.visualScene.factory.createPrimitive(descr.shape, material),
|
|
24
|
+
objectBody: this.physicsWorld.factory.createRigidBody(descr),
|
|
25
|
+
});
|
|
35
26
|
entity.position = position;
|
|
36
27
|
entity.rotation = rotation;
|
|
37
28
|
this.addEntity(entity);
|
|
@@ -42,4 +33,21 @@ export class Gg3dWorld extends GgWorld {
|
|
|
42
33
|
this.addEntity(entity);
|
|
43
34
|
return entity;
|
|
44
35
|
}
|
|
36
|
+
registerConsoleCommands(ggstatic) {
|
|
37
|
+
super.registerConsoleCommands(ggstatic);
|
|
38
|
+
ggstatic.registerConsoleCommand(this, 'gravity', (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
39
|
+
if (args.length == 1) {
|
|
40
|
+
args = ['0', '0', '' + -+args[0]]; // mean -Z axis
|
|
41
|
+
}
|
|
42
|
+
if (args.length > 0) {
|
|
43
|
+
if (isNaN(+args[0]) || isNaN(+args[1]) || isNaN(+args[2])) {
|
|
44
|
+
throw new Error('Wrong arguments');
|
|
45
|
+
}
|
|
46
|
+
this.physicsWorld.gravity = { x: +args[0], y: +args[1], z: +args[2] };
|
|
47
|
+
}
|
|
48
|
+
return JSON.stringify(this.physicsWorld.gravity);
|
|
49
|
+
}), 'args: [ ?float, ?float, ?float ]; Get or set 3D world gravity vector. 1 argument sets ' +
|
|
50
|
+
'vector {x: 0, y: 0, z: -value}, 3 arguments set the whole vector.' +
|
|
51
|
+
' Default value is "9.82" or "0 0 -9.82"');
|
|
52
|
+
}
|
|
45
53
|
}
|
package/dist/3d/index.d.ts
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';
|
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';
|
package/dist/3d/loader.js
CHANGED
|
@@ -99,7 +99,7 @@ export class Gg3dLoader {
|
|
|
99
99
|
const loadOptions = Object.assign(Object.assign({}, defaultLoadOptions), options);
|
|
100
100
|
const { resources, meta } = yield this.loadGgGlbResources(path, loadOptions.cachingStrategy);
|
|
101
101
|
const result = {
|
|
102
|
-
entities: resources.map(
|
|
102
|
+
entities: resources.map(x => new Entity3d({ object3D: x.object3D, objectBody: x.body })),
|
|
103
103
|
meta,
|
|
104
104
|
};
|
|
105
105
|
if (loadOptions.loadProps) {
|
|
@@ -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
|
|
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,16 +1,12 @@
|
|
|
1
|
-
import { Observable } from 'rxjs';
|
|
2
|
-
import { PausableClock } from './pausable-clock';
|
|
3
1
|
import { IClock } from './i-clock';
|
|
4
2
|
/**
|
|
5
3
|
* A singleton class, providing ability to track time, fire ticks, provide time elapsed + tick delta.
|
|
6
4
|
* Starts as soon as accessed and counts time from 01/01/1970
|
|
7
5
|
*/
|
|
8
|
-
export declare class GgGlobalClock
|
|
6
|
+
export declare class GgGlobalClock extends IClock {
|
|
9
7
|
private static _instance;
|
|
10
8
|
static get instance(): GgGlobalClock;
|
|
11
|
-
private readonly _tick$;
|
|
12
|
-
get tick$(): Observable<[number, number]>;
|
|
13
9
|
get elapsedTime(): number;
|
|
14
|
-
createChildClock(autoStart: boolean): PausableClock;
|
|
15
10
|
private constructor();
|
|
11
|
+
dispose(): void;
|
|
16
12
|
}
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { PausableClock } from './pausable-clock';
|
|
1
|
+
import { IClock } from './i-clock';
|
|
2
|
+
const now = typeof performance === 'undefined' ? () => Date.now() : () => performance.now();
|
|
4
3
|
/**
|
|
5
4
|
* A singleton class, providing ability to track time, fire ticks, provide time elapsed + tick delta.
|
|
6
5
|
* Starts as soon as accessed and counts time from 01/01/1970
|
|
7
6
|
*/
|
|
8
|
-
export class GgGlobalClock {
|
|
7
|
+
export class GgGlobalClock extends IClock {
|
|
9
8
|
static get instance() {
|
|
10
9
|
if (!GgGlobalClock._instance) {
|
|
11
10
|
GgGlobalClock._instance = new GgGlobalClock();
|
|
12
11
|
}
|
|
13
12
|
return GgGlobalClock._instance;
|
|
14
13
|
}
|
|
15
|
-
get tick$() {
|
|
16
|
-
return this._tick$.pipe(map(([oldTime, newTime]) => [newTime, newTime - oldTime]));
|
|
17
|
-
}
|
|
18
14
|
get elapsedTime() {
|
|
19
|
-
return
|
|
20
|
-
}
|
|
21
|
-
createChildClock(autoStart) {
|
|
22
|
-
return new PausableClock(autoStart, this);
|
|
15
|
+
return now();
|
|
23
16
|
}
|
|
24
17
|
constructor() {
|
|
25
|
-
|
|
18
|
+
super(null);
|
|
26
19
|
let oldRelativeTime = this.elapsedTime;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
const tick = () => {
|
|
21
|
+
requestAnimationFrame(tick);
|
|
22
|
+
const prev = oldRelativeTime;
|
|
23
|
+
const cur = this.elapsedTime;
|
|
24
|
+
oldRelativeTime = cur;
|
|
25
|
+
this._tick$.next([prev, cur - prev]);
|
|
26
|
+
};
|
|
27
|
+
requestAnimationFrame(tick);
|
|
28
|
+
}
|
|
29
|
+
dispose() {
|
|
30
|
+
throw new Error('Cannot dispose global clock');
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import { Observable } from 'rxjs';
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Observable, Subject } from 'rxjs';
|
|
2
|
+
export declare abstract class IClock {
|
|
3
|
+
readonly parent: IClock | null;
|
|
4
|
+
protected readonly _tick$: Subject<[number, number]>;
|
|
5
|
+
/**
|
|
6
|
+
* Observable stream of ticks, emitting an array containing the current time and the tick delta.
|
|
7
|
+
*/
|
|
4
8
|
get tick$(): Observable<[number, number]>;
|
|
5
|
-
get elapsedTime(): number;
|
|
6
|
-
|
|
9
|
+
abstract get elapsedTime(): number;
|
|
10
|
+
protected _children: IClock[];
|
|
11
|
+
get children(): IClock[];
|
|
12
|
+
protected constructor(parent: IClock | null);
|
|
13
|
+
addChild(clock: IClock): void;
|
|
14
|
+
removeChild(clock: IClock): void;
|
|
15
|
+
dispose(): void;
|
|
7
16
|
}
|
|
@@ -1 +1,43 @@
|
|
|
1
|
-
|
|
1
|
+
import { Subject } from 'rxjs';
|
|
2
|
+
export class IClock {
|
|
3
|
+
/**
|
|
4
|
+
* Observable stream of ticks, emitting an array containing the current time and the tick delta.
|
|
5
|
+
*/
|
|
6
|
+
get tick$() {
|
|
7
|
+
return this._tick$.asObservable();
|
|
8
|
+
}
|
|
9
|
+
get children() {
|
|
10
|
+
return [...this._children];
|
|
11
|
+
}
|
|
12
|
+
constructor(parent) {
|
|
13
|
+
this.parent = parent;
|
|
14
|
+
this._tick$ = new Subject();
|
|
15
|
+
this._children = [];
|
|
16
|
+
if (parent) {
|
|
17
|
+
parent.addChild(this);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
addChild(clock) {
|
|
21
|
+
if (clock.parent !== this) {
|
|
22
|
+
throw new Error('Incorrect child clock');
|
|
23
|
+
}
|
|
24
|
+
if (!this._children.includes(clock)) {
|
|
25
|
+
this._children.push(clock);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
removeChild(clock) {
|
|
29
|
+
if (clock.parent !== this) {
|
|
30
|
+
throw new Error('Incorrect child clock');
|
|
31
|
+
}
|
|
32
|
+
this._children = this._children.filter(c => c !== clock);
|
|
33
|
+
}
|
|
34
|
+
dispose() {
|
|
35
|
+
if (this.parent) {
|
|
36
|
+
this.parent.removeChild(this);
|
|
37
|
+
}
|
|
38
|
+
for (const c of this._children) {
|
|
39
|
+
c.dispose();
|
|
40
|
+
}
|
|
41
|
+
this._tick$.complete();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Subject } from 'rxjs';
|
|
2
2
|
import { IClock } from './i-clock';
|
|
3
3
|
/**
|
|
4
4
|
* A class providing the ability to track time, fire ticks, provide time elapsed, and tick delta with the ability to suspend/resume it.
|
|
5
5
|
*/
|
|
6
|
-
export declare class PausableClock
|
|
6
|
+
export declare class PausableClock extends IClock {
|
|
7
7
|
protected readonly parentClock: IClock;
|
|
8
8
|
private tickSub;
|
|
9
9
|
private readonly _internalTick$;
|
|
10
|
-
private readonly _tick$;
|
|
11
|
-
/**
|
|
12
|
-
* Observable stream of ticks, emitting an array containing the current time and the tick delta.
|
|
13
|
-
*/
|
|
14
|
-
get tick$(): Observable<[number, number]>;
|
|
15
10
|
/**
|
|
16
11
|
* Checks if the clock is currently running.
|
|
17
12
|
*/
|
|
@@ -54,12 +49,6 @@ export declare class PausableClock implements IClock {
|
|
|
54
49
|
* @param parentClock The parent clock to synchronize with. Defaults to GgGlobalClock instance.
|
|
55
50
|
*/
|
|
56
51
|
constructor(autoStart?: boolean, parentClock?: IClock);
|
|
57
|
-
/**
|
|
58
|
-
* Creates a child clock.
|
|
59
|
-
* @param autoStart Indicates whether the child clock should start automatically.
|
|
60
|
-
* @returns A new instance of PausableClock.
|
|
61
|
-
*/
|
|
62
|
-
createChildClock(autoStart: boolean): PausableClock;
|
|
63
52
|
/**
|
|
64
53
|
* Starts the clock.
|
|
65
54
|
*/
|
|
@@ -84,4 +73,5 @@ export declare class PausableClock implements IClock {
|
|
|
84
73
|
* Stops listening for ticks from the parent clock.
|
|
85
74
|
*/
|
|
86
75
|
protected stopListeningTicks(): void;
|
|
76
|
+
dispose(): void;
|
|
87
77
|
}
|