@needle-tools/engine 2.65.2-pre → 2.66.1-pre
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/CHANGELOG.md +23 -0
- package/dist/needle-engine.js +7866 -7756
- package/dist/needle-engine.umd.cjs +223 -223
- package/lib/engine/debug/debug_overlay.js +4 -1
- package/lib/engine/debug/debug_overlay.js.map +1 -1
- package/lib/engine/engine_addressables.js +2 -2
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_element.d.ts +1 -0
- package/lib/engine/engine_element.js +4 -1
- package/lib/engine/engine_element.js.map +1 -1
- package/lib/engine/engine_element_loading.d.ts +3 -2
- package/lib/engine/engine_element_loading.js +18 -14
- package/lib/engine/engine_element_loading.js.map +1 -1
- package/lib/engine/engine_gameobject.js +6 -3
- package/lib/engine/engine_gameobject.js.map +1 -1
- package/lib/engine/engine_gizmos.js +3 -1
- package/lib/engine/engine_gizmos.js.map +1 -1
- package/lib/engine/engine_networking.d.ts +3 -1
- package/lib/engine/engine_networking.js +10 -8
- package/lib/engine/engine_networking.js.map +1 -1
- package/lib/engine/engine_physics.d.ts +29 -1
- package/lib/engine/engine_physics.js +99 -10
- package/lib/engine/engine_physics.js.map +1 -1
- package/lib/engine/engine_setup.js +3 -0
- package/lib/engine/engine_setup.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_render_objects.js +9 -0
- package/lib/engine/extensions/NEEDLE_render_objects.js.map +1 -1
- package/lib/engine-components/Animator.js +0 -1
- package/lib/engine-components/Animator.js.map +1 -1
- package/lib/engine-components/CharacterController.d.ts +1 -0
- package/lib/engine-components/CharacterController.js +14 -9
- package/lib/engine-components/CharacterController.js.map +1 -1
- package/lib/engine-components/Collider.js +14 -1
- package/lib/engine-components/Collider.js.map +1 -1
- package/lib/engine-components/ParticleSystem.d.ts +5 -1
- package/lib/engine-components/ParticleSystem.js +41 -6
- package/lib/engine-components/ParticleSystem.js.map +1 -1
- package/lib/engine-components/ParticleSystemModules.d.ts +2 -0
- package/lib/engine-components/ParticleSystemModules.js +26 -0
- package/lib/engine-components/ParticleSystemModules.js.map +1 -1
- package/lib/engine-components/ParticleSystemSubEmitter.js +5 -2
- package/lib/engine-components/ParticleSystemSubEmitter.js.map +1 -1
- package/lib/engine-components/Renderer.js +9 -5
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/ScreenCapture.js +3 -3
- package/lib/engine-components/ScreenCapture.js.map +1 -1
- package/lib/engine-components/SpectatorCamera.js +3 -3
- package/lib/engine-components/SpectatorCamera.js.map +1 -1
- package/lib/engine-components/SyncedCamera.js +1 -1
- package/lib/engine-components/SyncedCamera.js.map +1 -1
- package/lib/engine-components/SyncedTransform.js +2 -2
- package/lib/engine-components/SyncedTransform.js.map +1 -1
- package/lib/engine-components/TestRunner.js +1 -1
- package/lib/engine-components/TestRunner.js.map +1 -1
- package/lib/engine-components/WebARSessionRoot.js +3 -2
- package/lib/engine-components/WebARSessionRoot.js.map +1 -1
- package/lib/engine-components/WebXRAvatar.js.map +1 -1
- package/lib/engine-components/WebXRGrabRendering.js +2 -2
- package/lib/engine-components/WebXRGrabRendering.js.map +1 -1
- package/lib/engine-components/WebXRSync.js +2 -2
- package/lib/engine-components/WebXRSync.js.map +1 -1
- package/lib/engine-components-experimental/networking/PlayerSync.js +1 -1
- package/lib/engine-components-experimental/networking/PlayerSync.js.map +1 -1
- package/package.json +1 -1
- package/plugins/vite/meta.js +3 -0
- package/plugins/vite/poster-client.js +6 -4
- package/src/engine/debug/debug_overlay.ts +4 -1
- package/src/engine/engine_addressables.ts +2 -2
- package/src/engine/engine_element.ts +8 -1
- package/src/engine/engine_element_loading.ts +18 -14
- package/src/engine/engine_gameobject.ts +584 -583
- package/src/engine/engine_gizmos.ts +3 -2
- package/src/engine/engine_networking.ts +10 -8
- package/src/engine/engine_physics.ts +113 -10
- package/src/engine/engine_setup.ts +4 -0
- package/src/engine/extensions/NEEDLE_render_objects.ts +10 -1
- package/src/engine-components/Animator.ts +0 -1
- package/src/engine-components/CharacterController.ts +12 -9
- package/src/engine-components/Collider.ts +16 -2
- package/src/engine-components/ParticleSystem.ts +45 -9
- package/src/engine-components/ParticleSystemModules.ts +28 -1
- package/src/engine-components/ParticleSystemSubEmitter.ts +5 -3
- package/src/engine-components/Renderer.ts +14 -11
- package/src/engine-components/ScreenCapture.ts +3 -3
- package/src/engine-components/SpectatorCamera.ts +3 -3
- package/src/engine-components/SyncedCamera.ts +1 -1
- package/src/engine-components/SyncedTransform.ts +2 -2
- package/src/engine-components/TestRunner.ts +1 -1
- package/src/engine-components/WebARSessionRoot.ts +3 -2
- package/src/engine-components/WebXRAvatar.ts +0 -1
- package/src/engine-components/WebXRGrabRendering.ts +2 -2
- package/src/engine-components/WebXRSync.ts +2 -2
- package/src/engine-components-experimental/networking/PlayerSync.ts +1 -1
|
@@ -145,10 +145,9 @@ class Internal {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
static getSphere(radius: number, duration: number, wireframe: boolean): Mesh {
|
|
148
|
-
|
|
149
148
|
let sphere = this.spheresCache.pop();
|
|
150
149
|
if (!sphere) {
|
|
151
|
-
sphere = new Mesh(new SphereGeometry(
|
|
150
|
+
sphere = new Mesh(new SphereGeometry(1, 8, 8));
|
|
152
151
|
}
|
|
153
152
|
sphere.scale.set(radius, radius, radius);
|
|
154
153
|
sphere.material["wireframe"] = wireframe;
|
|
@@ -176,6 +175,8 @@ class Internal {
|
|
|
176
175
|
this.contextPostRenderCallbacks.set(context, cb);
|
|
177
176
|
context.post_render_callbacks.push(cb);
|
|
178
177
|
}
|
|
178
|
+
object.layers.disableAll();
|
|
179
|
+
object.layers.enable(2);
|
|
179
180
|
object[$cacheSymbol] = cache;
|
|
180
181
|
this.timedObjectsBuffer.push(object);
|
|
181
182
|
this.timesBuffer.push(Context.Current.time.time + duration);
|
|
@@ -147,7 +147,7 @@ export class OwnershipModel {
|
|
|
147
147
|
// console.log(res);
|
|
148
148
|
if (res.guid === this.guid) {
|
|
149
149
|
if (this._isWaitingForOwnershipResponseCallback) {
|
|
150
|
-
this.connection.
|
|
150
|
+
this.connection.stopListen(OwnershipEvent.ResponseHasOwner, this._isWaitingForOwnershipResponseCallback);
|
|
151
151
|
this._isWaitingForOwnershipResponseCallback = null;
|
|
152
152
|
}
|
|
153
153
|
this._isOwned = res.value;
|
|
@@ -185,18 +185,18 @@ export class OwnershipModel {
|
|
|
185
185
|
// TODO: abort "requestOwnershipIfNotOwned"
|
|
186
186
|
this.connection.send(OwnershipEvent.RemoveOwnership, { guid: this.guid });
|
|
187
187
|
if (this._isWaitingForOwnershipResponseCallback) {
|
|
188
|
-
this.connection.
|
|
188
|
+
this.connection.stopListen(OwnershipEvent.ResponseHasOwner, this._isWaitingForOwnershipResponseCallback);
|
|
189
189
|
this._isWaitingForOwnershipResponseCallback = null;
|
|
190
190
|
}
|
|
191
191
|
return this;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
public destroy() {
|
|
195
|
-
this.connection.
|
|
196
|
-
this.connection.
|
|
197
|
-
this.connection.
|
|
195
|
+
this.connection.stopListen(OwnershipEvent.GainedOwnership, this._gainSubscription);
|
|
196
|
+
this.connection.stopListen(OwnershipEvent.LostOwnership, this._lostSubscription);
|
|
197
|
+
this.connection.stopListen(OwnershipEvent.ResponseHasOwner, this._hasOwnerResponse);
|
|
198
198
|
if (this._isWaitingForOwnershipResponseCallback) {
|
|
199
|
-
this.connection.
|
|
199
|
+
this.connection.stopListen(OwnershipEvent.ResponseHasOwner, this._isWaitingForOwnershipResponseCallback);
|
|
200
200
|
this._isWaitingForOwnershipResponseCallback = null;
|
|
201
201
|
}
|
|
202
202
|
}
|
|
@@ -368,7 +368,9 @@ export class NetworkConnection implements INetworkConnection {
|
|
|
368
368
|
return callback;
|
|
369
369
|
}
|
|
370
370
|
|
|
371
|
-
|
|
371
|
+
/**@deprecated please use stopListen instead (2.65.2-pre) */
|
|
372
|
+
public stopListening(key: string | OwnershipEvent, callback: Function | null) { return this.stopListen(key, callback); }
|
|
373
|
+
public stopListen(key: string | OwnershipEvent, callback: Function | null) {
|
|
372
374
|
if (!callback) return;
|
|
373
375
|
if (!this._listeners[key]) return;
|
|
374
376
|
const index = this._listeners[key].indexOf(callback);
|
|
@@ -377,7 +379,7 @@ export class NetworkConnection implements INetworkConnection {
|
|
|
377
379
|
}
|
|
378
380
|
}
|
|
379
381
|
|
|
380
|
-
public
|
|
382
|
+
public beginListenBinary(identifier: string, callback: BinaryCallback): BinaryCallback {
|
|
381
383
|
if (!this._listenersBinary[identifier])
|
|
382
384
|
this._listenersBinary[identifier] = [];
|
|
383
385
|
this._listenersBinary[identifier].push(callback);
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import { InstancingUtil } from './engine_instancing';
|
|
16
16
|
import { foreachComponent } from './engine_gameobject';
|
|
17
17
|
|
|
18
|
-
import RAPIER, { ActiveEvents, CoefficientCombineRule, Collider, ColliderDesc, EventQueue, JointData, RigidBody, RigidBodyType, World } from '@dimforge/rapier3d-compat';
|
|
18
|
+
import RAPIER, { ActiveEvents, CoefficientCombineRule, Collider, ColliderDesc, EventQueue, JointData, QueryFilterFlags, RigidBody, RigidBodyType, ShapeColliderTOI, World } from '@dimforge/rapier3d-compat';
|
|
19
19
|
import { CollisionDetectionMode, PhysicsMaterialCombine } from '../engine/engine_physics.types';
|
|
20
20
|
import { Gizmos } from './engine_gizmos';
|
|
21
21
|
import { Mathf } from './engine_math';
|
|
@@ -83,12 +83,22 @@ export class SphereIntersection implements Intersection {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
export class SphereOverlapResult {
|
|
87
|
+
object: Object3D;
|
|
88
|
+
collider: ICollider;
|
|
89
|
+
constructor(object: Object3D, collider: ICollider) {
|
|
90
|
+
this.object = object;
|
|
91
|
+
this.collider = collider;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
86
95
|
declare type PhysicsRaycastResult = {
|
|
87
96
|
point: Vector3,
|
|
88
97
|
normal?: Vector3,
|
|
89
98
|
collider?: ICollider
|
|
90
99
|
}
|
|
91
100
|
|
|
101
|
+
|
|
92
102
|
export class Physics {
|
|
93
103
|
|
|
94
104
|
// raycasting
|
|
@@ -107,6 +117,12 @@ export class Physics {
|
|
|
107
117
|
|
|
108
118
|
private sphereResults: Array<Intersection> = new Array<Intersection>();
|
|
109
119
|
private sphereMask: Layers = new Layers();
|
|
120
|
+
/** Test overlapping of a sphere with the threejs geometry. This does not use colliders. This does not return an exact intersection point (intersections returned contain the object and the world position of the object that is being hit)
|
|
121
|
+
* For a more accurate test use the physics engine's collider overlap test (see sphereOverlapPhysics)
|
|
122
|
+
* @param spherePos the center of the sphere in world space
|
|
123
|
+
* @param radius the radius of the sphere
|
|
124
|
+
* @param traverseChildsAfterHit if false it will stop after the first hit. If true it will continue to traverse and add all hits to the result array
|
|
125
|
+
*/
|
|
110
126
|
public sphereOverlap(spherePos: Vector3, radius: number, traverseChildsAfterHit: boolean = true): Array<Intersection> {
|
|
111
127
|
this.sphereResults.length = 0;
|
|
112
128
|
if (!this.context.scene) return this.sphereResults;
|
|
@@ -130,10 +146,9 @@ export class Physics {
|
|
|
130
146
|
if (mesh.matrixWorldNeedsUpdate) mesh.updateMatrixWorld();
|
|
131
147
|
const test = this.tempBoundingBox.copy(geo.boundingBox).applyMatrix4(mesh.matrixWorld);
|
|
132
148
|
if (sp.intersectsBox(test)) {
|
|
133
|
-
// console.log(obj, obj.layers.test(mask), obj.layers.mask, mask.mask);
|
|
134
149
|
const wp = getWorldPosition(obj);
|
|
135
150
|
const dist = wp.distanceTo(sp.center);
|
|
136
|
-
const int = new SphereIntersection(obj, dist,
|
|
151
|
+
const int = new SphereIntersection(obj, dist, wp);
|
|
137
152
|
results.push(int);
|
|
138
153
|
if (!traverseChildsAfterHit) return;
|
|
139
154
|
}
|
|
@@ -157,6 +172,8 @@ export class Physics {
|
|
|
157
172
|
/** raycast against rendered three objects. This might be very slow depending on your scene complexity.
|
|
158
173
|
* We recommend setting objects to IgnoreRaycast layer (2) when you don't need them to be raycasted.
|
|
159
174
|
* Raycasting SkinnedMeshes is specially expensive.
|
|
175
|
+
* Use raycastPhysics for raycasting against physic colliders only. Depending on your scenario this might be faster.
|
|
176
|
+
* @param options raycast options. If null, default options will be used.
|
|
160
177
|
*/
|
|
161
178
|
public raycast(options: RaycastOptions | null = null): Array<Intersection> {
|
|
162
179
|
if (!options) options = this.defaultRaycastOptions;
|
|
@@ -222,8 +239,12 @@ export class Physics {
|
|
|
222
239
|
|
|
223
240
|
private rapierRay = new RAPIER.Ray({ x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 1 });
|
|
224
241
|
private raycastVectorsBuffer = new CircularBuffer(() => new Vector3(), 10);
|
|
225
|
-
|
|
226
|
-
|
|
242
|
+
/** Fast raycast against physics colliders
|
|
243
|
+
* @param origin ray origin in screen or worldspace
|
|
244
|
+
* @param direction ray direction in worldspace
|
|
245
|
+
* @param maxDistance max distance to raycast
|
|
246
|
+
* @param solid if true it will also hit the collider if origin is already inside it
|
|
247
|
+
*/
|
|
227
248
|
public raycastPhysicsFast(origin: Vec2 | Vec3, direction: Vec3 | undefined = undefined, maxDistance: number = Infinity, solid: boolean = true)
|
|
228
249
|
: null | { point: Vector3, collider: ICollider } {
|
|
229
250
|
|
|
@@ -285,6 +306,72 @@ export class Physics {
|
|
|
285
306
|
return ray;
|
|
286
307
|
}
|
|
287
308
|
|
|
309
|
+
|
|
310
|
+
private rapierSphere: RAPIER.Ball | null = null;
|
|
311
|
+
private rapierColliderArray: Array<SphereOverlapResult> = [];
|
|
312
|
+
private readonly rapierIdentityRotation = { x: 0, y: 0, z: 0, w: 1 };
|
|
313
|
+
private readonly rapierForwardVector = { x: 0, y: 0, z: 1 };
|
|
314
|
+
/** Precice sphere overlap detection using rapier against colliders
|
|
315
|
+
* @param point center of the sphere in worldspace
|
|
316
|
+
* @param radius radius of the sphere
|
|
317
|
+
* @returns array of colliders that overlap with the sphere. Note: they currently only contain the collider and the gameobject
|
|
318
|
+
*/
|
|
319
|
+
public sphereOverlapPhysics(point: Vector3, radius: number): Array<SphereOverlapResult> {
|
|
320
|
+
this.rapierColliderArray.length = 0;
|
|
321
|
+
if (!this.world) return this.rapierColliderArray;
|
|
322
|
+
if (!this.rapierSphere)
|
|
323
|
+
this.rapierSphere = new RAPIER.Ball(radius);
|
|
324
|
+
this.rapierSphere.radius = radius;
|
|
325
|
+
|
|
326
|
+
this.world.intersectionsWithShape(point, this.rapierIdentityRotation, this.rapierSphere, col => {
|
|
327
|
+
const collider = col[$componentKey] as ICollider
|
|
328
|
+
// if (collider.gameObject.layers.isEnabled(2)) return true;
|
|
329
|
+
const intersection = new SphereOverlapResult(collider.gameObject, collider);
|
|
330
|
+
this.rapierColliderArray.push(intersection);
|
|
331
|
+
return true; // Return `false` instead if we want to stop searching for other colliders that contain this point.
|
|
332
|
+
}, QueryFilterFlags.EXCLUDE_SENSORS, undefined, undefined, undefined,
|
|
333
|
+
col => {
|
|
334
|
+
const collider = col[$componentKey] as ICollider
|
|
335
|
+
return collider.gameObject.layers.isEnabled(2) == false
|
|
336
|
+
}
|
|
337
|
+
);
|
|
338
|
+
return this.rapierColliderArray;
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
// TODO: this only returns one hit
|
|
342
|
+
// let filterGroups = 0xffffffff;
|
|
343
|
+
// filterGroups &= ~(1 << 2);
|
|
344
|
+
// const hit: ShapeColliderTOI | null = this.world.castShape(point,
|
|
345
|
+
// this.rapierIdentityRotation,
|
|
346
|
+
// this.rapierForwardVector,
|
|
347
|
+
// this.rapierSphere,
|
|
348
|
+
// 0,
|
|
349
|
+
// QueryFilterFlags.EXCLUDE_SENSORS,
|
|
350
|
+
// // filterGroups,
|
|
351
|
+
// );
|
|
352
|
+
// // console.log(hit);
|
|
353
|
+
// if (hit) {
|
|
354
|
+
// const collider = hit.collider[$componentKey] as ICollider
|
|
355
|
+
// const intersection = new SphereOverlapResult(collider.gameObject);
|
|
356
|
+
// this.rapierColliderArray.push(intersection);
|
|
357
|
+
// // const localpt = hit.witness2;
|
|
358
|
+
// // // const normal = hit.normal2;
|
|
359
|
+
// // const hitPoint = new Vector3(localpt.x, localpt.y, localpt.z);
|
|
360
|
+
// // // collider.gameObject.localToWorld(hitPoint);
|
|
361
|
+
// // // const normalPt = new Vector3(normal.x, normal.y, normal.z);
|
|
362
|
+
// // // const mat = new Matrix4().setPosition(point).scale(new Vector3(radius, radius, radius));
|
|
363
|
+
// // // hitPoint.applyMatrix4(mat);
|
|
364
|
+
// // console.log(hit.witness2)
|
|
365
|
+
// // // hitPoint.add(point);
|
|
366
|
+
// // const dist = hitPoint.distanceTo(point);
|
|
367
|
+
// }
|
|
368
|
+
|
|
369
|
+
// return this.rapierColliderArray;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
|
|
288
375
|
// physics simulation
|
|
289
376
|
|
|
290
377
|
enabled: boolean = true;
|
|
@@ -352,42 +439,58 @@ export class Physics {
|
|
|
352
439
|
|
|
353
440
|
addBoxCollider(collider: ICollider, center: Vector3, size: Vector3) {
|
|
354
441
|
if (!this.enabled) {
|
|
355
|
-
if(debugPhysics) console.warn("Physics
|
|
442
|
+
if (debugPhysics) console.warn("Physics are disabled");
|
|
356
443
|
return;
|
|
357
444
|
}
|
|
358
445
|
const obj = collider.gameObject;
|
|
359
446
|
const scale = getWorldScale(obj, this._tempPosition).multiply(size);
|
|
360
447
|
scale.multiplyScalar(0.5);
|
|
448
|
+
|
|
449
|
+
// prevent negative scale
|
|
450
|
+
if (scale.x < 0)
|
|
451
|
+
scale.x = Math.abs(scale.x);
|
|
452
|
+
if (scale.y < 0)
|
|
453
|
+
scale.y = Math.abs(scale.y);
|
|
454
|
+
if (scale.z < 0)
|
|
455
|
+
scale.z = Math.abs(scale.z);
|
|
456
|
+
|
|
361
457
|
const desc = ColliderDesc.cuboid(scale.x, scale.y, scale.z);
|
|
458
|
+
// const objectLayerMask = collider.gameObject.layers.mask;
|
|
459
|
+
// const mask = objectLayerMask & ~2;
|
|
460
|
+
// desc.setCollisionGroups(objectLayerMask);
|
|
362
461
|
this.createCollider(collider, desc, center);
|
|
363
462
|
}
|
|
364
463
|
|
|
365
464
|
addSphereCollider(collider: ICollider, center: Vector3, radius: number) {
|
|
366
465
|
if (!this.enabled) {
|
|
367
|
-
if(debugPhysics) console.warn("Physics
|
|
466
|
+
if (debugPhysics) console.warn("Physics are disabled");
|
|
368
467
|
return;
|
|
369
468
|
}
|
|
370
469
|
const obj = collider.gameObject;
|
|
371
470
|
const scale = getWorldScale(obj, this._tempPosition).multiplyScalar(radius);
|
|
471
|
+
// Prevent negative scales
|
|
472
|
+
scale.x = Math.abs(scale.x);
|
|
372
473
|
const desc = ColliderDesc.ball(scale.x);
|
|
373
474
|
this.createCollider(collider, desc, center);
|
|
374
475
|
}
|
|
375
476
|
|
|
376
477
|
addCapsuleCollider(collider: ICollider, center: Vector3, height: number, radius: number) {
|
|
377
478
|
if (!this.enabled) {
|
|
378
|
-
if(debugPhysics) console.warn("Physics
|
|
479
|
+
if (debugPhysics) console.warn("Physics are disabled");
|
|
379
480
|
return;
|
|
380
481
|
}
|
|
381
482
|
const obj = collider.gameObject;
|
|
382
483
|
const scale = getWorldScale(obj, this._tempPosition);
|
|
383
|
-
|
|
484
|
+
// Prevent negative scales
|
|
485
|
+
scale.x = Math.abs(scale.x);
|
|
486
|
+
scale.y = Math.abs(scale.y);
|
|
384
487
|
const desc = ColliderDesc.capsule(height * .5 * scale.y - radius, radius * scale.x);
|
|
385
488
|
this.createCollider(collider, desc, center);
|
|
386
489
|
}
|
|
387
490
|
|
|
388
491
|
addMeshCollider(collider: ICollider, mesh: Mesh, convex: boolean, scale: Vector3) {
|
|
389
492
|
if (!this.enabled) {
|
|
390
|
-
if(debugPhysics) console.warn("Physics
|
|
493
|
+
if (debugPhysics) console.warn("Physics are disabled");
|
|
391
494
|
return;
|
|
392
495
|
}
|
|
393
496
|
const geo = mesh.geometry;
|
|
@@ -744,6 +744,10 @@ export class Context implements IContext {
|
|
|
744
744
|
this.connection.sendBufferedMessagesNow();
|
|
745
745
|
|
|
746
746
|
this._stats?.end();
|
|
747
|
+
|
|
748
|
+
if (this.time.frame === 1) {
|
|
749
|
+
this.domElement.dispatchEvent(new CustomEvent("ready"));
|
|
750
|
+
}
|
|
747
751
|
}
|
|
748
752
|
|
|
749
753
|
renderNow(camera?: Camera) {
|
|
@@ -23,7 +23,10 @@ import {
|
|
|
23
23
|
DecrementWrapStencilOp,
|
|
24
24
|
InvertStencilOp,
|
|
25
25
|
} from "three";
|
|
26
|
-
import { getParam } from "../engine_utils";
|
|
26
|
+
import { getParam, isDebugMode } from "../engine_utils";
|
|
27
|
+
import { showBalloonWarning } from "../debug";
|
|
28
|
+
import { isUsingInstancing } from "../engine_gameobject";
|
|
29
|
+
import { isLocalNetwork } from "../engine_networking_utils";
|
|
27
30
|
|
|
28
31
|
const debug = getParam("debugstencil");
|
|
29
32
|
|
|
@@ -62,6 +65,12 @@ export class NEEDLE_render_objects implements GLTFLoaderPlugin {
|
|
|
62
65
|
const stencil: StencilSettingsModel = settings[i];
|
|
63
66
|
if (matchesLayer(stencil.layer, obj)) {
|
|
64
67
|
if (debug) console.log(stencil);
|
|
68
|
+
setTimeout(() => {
|
|
69
|
+
if (isLocalNetwork() && isUsingInstancing(obj.gameObject)) {
|
|
70
|
+
showBalloonWarning("Stencil not supported on instanced objects");
|
|
71
|
+
console.warn("Stencil not supported on instanced objects", obj);
|
|
72
|
+
}
|
|
73
|
+
}, 500)
|
|
65
74
|
for (let i = 0; i < obj.sharedMaterials.length; i++) {
|
|
66
75
|
let mat = obj.sharedMaterials[i];
|
|
67
76
|
if (mat) {
|
|
@@ -73,7 +73,6 @@ export class Animator extends Behaviour {
|
|
|
73
73
|
/**@deprecated use getBool */
|
|
74
74
|
GetBool(name: string | number) { return this.getBool(name); }
|
|
75
75
|
getBool(name: string | number): boolean {
|
|
76
|
-
console.log("name", name);
|
|
77
76
|
return this.runtimeAnimatorController?.getBool(name) ?? false;
|
|
78
77
|
}
|
|
79
78
|
|
|
@@ -86,6 +86,9 @@ export class CharacterControllerInput extends Behaviour {
|
|
|
86
86
|
@serializable()
|
|
87
87
|
jumpForce: number = 1;
|
|
88
88
|
|
|
89
|
+
@serializable()
|
|
90
|
+
doubleJumpForce: number = 2;
|
|
91
|
+
|
|
89
92
|
@serializable(Animator)
|
|
90
93
|
animator?: Animator;
|
|
91
94
|
|
|
@@ -106,7 +109,7 @@ export class CharacterControllerInput extends Behaviour {
|
|
|
106
109
|
|
|
107
110
|
if (this.controller?.isGrounded) {
|
|
108
111
|
this._jumpCount = 0;
|
|
109
|
-
this.animator?.
|
|
112
|
+
if (this.doubleJumpForce > 0) this.animator?.setBool("doubleJump", false);
|
|
110
113
|
}
|
|
111
114
|
|
|
112
115
|
const forward = this.context.input.isKeyPressed("w");
|
|
@@ -121,8 +124,8 @@ export class CharacterControllerInput extends Behaviour {
|
|
|
121
124
|
this._currentSpeed.z += step * this.movementSpeed * this.context.time.deltaTime;
|
|
122
125
|
|
|
123
126
|
// if (!this.controller || this.controller.isGrounded)
|
|
124
|
-
this.animator?.
|
|
125
|
-
this.animator?.
|
|
127
|
+
this.animator?.setBool("running", step != 0);
|
|
128
|
+
this.animator?.setBool("jumping", this.controller?.isGrounded === true && jump);
|
|
126
129
|
|
|
127
130
|
this._temp.copy(this._currentSpeed);
|
|
128
131
|
this._temp.applyQuaternion(this.gameObject.quaternion);
|
|
@@ -146,9 +149,9 @@ export class CharacterControllerInput extends Behaviour {
|
|
|
146
149
|
|
|
147
150
|
if (this.controller && jump && this.jumpForce > 0) {
|
|
148
151
|
let canJump = this.controller?.isGrounded;
|
|
149
|
-
if (!this.controller?.isGrounded && this._jumpCount === 1) {
|
|
152
|
+
if (this.doubleJumpForce > 0 && !this.controller?.isGrounded && this._jumpCount === 1) {
|
|
150
153
|
canJump = true;
|
|
151
|
-
this.animator?.
|
|
154
|
+
this.animator?.setBool("doubleJump", true);
|
|
152
155
|
}
|
|
153
156
|
|
|
154
157
|
if (canJump) {
|
|
@@ -156,8 +159,8 @@ export class CharacterControllerInput extends Behaviour {
|
|
|
156
159
|
// TODO: factor in mass
|
|
157
160
|
const rb = this.controller.rigidbody;
|
|
158
161
|
// const fullJumpHoldLength = .1;
|
|
159
|
-
const factor = this._jumpCount === 2 ?
|
|
160
|
-
rb.applyImpulse(new Vector3(0, 1, 0).multiplyScalar(
|
|
162
|
+
const factor = this._jumpCount === 2 ? this.doubleJumpForce : this.jumpForce;// Mathf.clamp((this.context.time.time - this._jumpDownTime), 0, fullJumpHoldLength) / fullJumpHoldLength;
|
|
163
|
+
rb.applyImpulse(new Vector3(0, 1, 0).multiplyScalar(factor));
|
|
161
164
|
}
|
|
162
165
|
}
|
|
163
166
|
|
|
@@ -174,10 +177,10 @@ export class CharacterControllerInput extends Behaviour {
|
|
|
174
177
|
const hits = this.context.physics.raycast(this._raycastOptions);
|
|
175
178
|
this.gameObject.layers.set(currentLayer);
|
|
176
179
|
if ((hits.length && hits[0].distance > 2 || verticalSpeed < -10)) {
|
|
177
|
-
this.animator?.
|
|
180
|
+
this.animator?.setBool("falling", true);
|
|
178
181
|
}
|
|
179
182
|
}
|
|
180
|
-
else this.animator?.
|
|
183
|
+
else this.animator?.setBool("falling", false);
|
|
181
184
|
}
|
|
182
185
|
}
|
|
183
186
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Behaviour } from "./Component";
|
|
2
2
|
import { Rigidbody } from "./RigidBody";
|
|
3
3
|
import { serializable } from "../engine/engine_serialization_decorator";
|
|
4
|
-
import { Event, Mesh, Object3D, Vector3 } from "three"
|
|
4
|
+
import { Event, Group, Mesh, Object3D, Vector3 } from "three"
|
|
5
5
|
// import { IColliderProvider, registerColliderProvider } from "../engine/engine_physics";
|
|
6
6
|
import { ICollider } from "../engine/engine_types";
|
|
7
7
|
import { getWorldScale } from "../engine/engine_three_utils";
|
|
@@ -77,6 +77,7 @@ export class MeshCollider extends Collider {
|
|
|
77
77
|
|
|
78
78
|
|
|
79
79
|
sharedMesh?: Mesh;
|
|
80
|
+
|
|
80
81
|
@serializable()
|
|
81
82
|
convex: boolean = false;
|
|
82
83
|
|
|
@@ -88,8 +89,21 @@ export class MeshCollider extends Collider {
|
|
|
88
89
|
this.sharedMesh = this.gameObject;
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
|
-
if (this.sharedMesh?.isMesh)
|
|
92
|
+
if (this.sharedMesh?.isMesh) {
|
|
92
93
|
this.context.physics.addMeshCollider(this, this.sharedMesh, this.convex, getWorldScale(this.gameObject));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const group = this.sharedMesh as any as Group;
|
|
97
|
+
if (group?.isGroup) {
|
|
98
|
+
console.warn(`MeshCollider mesh is a group \"${this.sharedMesh?.name}\", adding all children as colliders. This is currently not fully supported (colliders can not be removed from world again)`, this)
|
|
99
|
+
for (const ch in group.children) {
|
|
100
|
+
const child = group.children[ch] as Mesh;
|
|
101
|
+
if (child.isMesh) {
|
|
102
|
+
this.context.physics.addMeshCollider(this, child, this.convex, getWorldScale(this.gameObject));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
93
107
|
}
|
|
94
108
|
}
|
|
95
109
|
|
|
@@ -330,7 +330,7 @@ class SizeBehaviour extends ParticleSystemBaseBehaviour {
|
|
|
330
330
|
}
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
-
const $particleLife = Symbol("particleLife");
|
|
333
|
+
export const $particleLife = Symbol("particleLife");
|
|
334
334
|
const $trailLifetime = Symbol("trailLifetime");
|
|
335
335
|
const $trailStartLength = Symbol("trailStartLength");
|
|
336
336
|
|
|
@@ -340,7 +340,6 @@ class TrailBehaviour extends ParticleSystemBaseBehaviour {
|
|
|
340
340
|
initialize(particle: Particle) {
|
|
341
341
|
if (particle instanceof TrailParticle) {
|
|
342
342
|
particle[$particleLife] = particle.life;
|
|
343
|
-
particle[$trailLifetime] = particle.life;
|
|
344
343
|
if (this.system.trails.enabled && this.system.trails.dieWithParticles === false) {
|
|
345
344
|
particle[$trailLifetime] = this.system.trails.lifetime.evaluate(Math.random(), Math.random());
|
|
346
345
|
particle.life += particle[$trailLifetime];
|
|
@@ -418,7 +417,7 @@ class VelocityBehaviour extends ParticleSystemBaseBehaviour {
|
|
|
418
417
|
if (gravityFactor !== 0) {
|
|
419
418
|
// gravityFactor *= -1;
|
|
420
419
|
temp3.copy(this._gravityDirection).multiplyScalar(gravityFactor);
|
|
421
|
-
if(debug) Gizmos.DrawDirection(particle.position, temp3, 0x0000ff, 0, false, 10);
|
|
420
|
+
if (debug) Gizmos.DrawDirection(particle.position, temp3, 0x0000ff, 0, false, 10);
|
|
422
421
|
baseVelocity.add(temp3);
|
|
423
422
|
}
|
|
424
423
|
particle.velocity.copy(baseVelocity);
|
|
@@ -747,6 +746,7 @@ export class ParticleSystem extends Behaviour implements IParticleSystem {
|
|
|
747
746
|
private _time: number = 0;
|
|
748
747
|
private _isPlaying: boolean = true;
|
|
749
748
|
private _isUsedAsSubsystem: boolean = false;
|
|
749
|
+
private _didPreWarm: boolean = false;
|
|
750
750
|
|
|
751
751
|
/** called from deserialization */
|
|
752
752
|
private set bursts(arr: ParticleBurst[]) {
|
|
@@ -779,7 +779,8 @@ export class ParticleSystem extends Behaviour implements IParticleSystem {
|
|
|
779
779
|
}
|
|
780
780
|
private _subEmitterSystems?: SubEmitterSystem[];
|
|
781
781
|
|
|
782
|
-
onAfterDeserialize(_){
|
|
782
|
+
onAfterDeserialize(_) {
|
|
783
|
+
// doing this here to get a chance to resolve the subemitter guid
|
|
783
784
|
if (this._subEmitterSystems && Array.isArray(this._subEmitterSystems)) {
|
|
784
785
|
for (const sub of this._subEmitterSystems) {
|
|
785
786
|
sub._deserialize(this.context, this.gameObject);
|
|
@@ -788,7 +789,6 @@ export class ParticleSystem extends Behaviour implements IParticleSystem {
|
|
|
788
789
|
}
|
|
789
790
|
|
|
790
791
|
awake(): void {
|
|
791
|
-
|
|
792
792
|
this._renderer = this.gameObject.getComponent(ParticleSystemRenderer) as ParticleSystemRenderer;
|
|
793
793
|
|
|
794
794
|
this._container = new Object3D();
|
|
@@ -837,16 +837,42 @@ export class ParticleSystem extends Behaviour implements IParticleSystem {
|
|
|
837
837
|
onEnable() {
|
|
838
838
|
if (this.inheritVelocity)
|
|
839
839
|
this.inheritVelocity.system = this;
|
|
840
|
+
if (this._batchSystem)
|
|
841
|
+
this._batchSystem.visible = true;
|
|
840
842
|
this.play();
|
|
841
843
|
}
|
|
842
844
|
|
|
843
845
|
onDisable() {
|
|
846
|
+
if (this._batchSystem)
|
|
847
|
+
this._batchSystem.visible = false;
|
|
844
848
|
}
|
|
845
849
|
|
|
846
850
|
onBeforeRender() {
|
|
851
|
+
if (this._didPreWarm === false && this.main?.prewarm === true) {
|
|
852
|
+
this._didPreWarm = true;
|
|
853
|
+
this.preWarm();
|
|
854
|
+
}
|
|
847
855
|
this.onUpdate();
|
|
848
|
-
|
|
856
|
+
this.onSimulate(this.deltaTime);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
private preWarm() {
|
|
860
|
+
const dt = 1 / 60;
|
|
861
|
+
const duration = this.main.duration;
|
|
862
|
+
const lifetime = this.main.startLifetime.getMax();
|
|
863
|
+
const maxDurationToPrewarm = 1000;
|
|
864
|
+
const timeToSimulate = Math.min(duration, lifetime, maxDurationToPrewarm);
|
|
865
|
+
const framesToSimulate = Math.ceil(timeToSimulate / dt);
|
|
866
|
+
if (debug)
|
|
867
|
+
console.log(`Particles ${this.name} - Prewarm for ${framesToSimulate} frames (${timeToSimulate} sec). Duration: ${duration}, Lifetime: ${lifetime}`);
|
|
868
|
+
for (let i = 0; i < framesToSimulate; i++) {
|
|
869
|
+
if (this.currentParticles >= this.maxParticles) break;
|
|
870
|
+
this.onUpdate();
|
|
871
|
+
this.onSimulate(dt);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
849
874
|
|
|
875
|
+
private onSimulate(dt: number) {
|
|
850
876
|
if (this._batchSystem) {
|
|
851
877
|
// Updating layers on batches
|
|
852
878
|
// TODO: figure out a better way to do this
|
|
@@ -942,10 +968,20 @@ export class SubEmitterSystem {
|
|
|
942
968
|
properties?: number;
|
|
943
969
|
type?: SubEmitterType;
|
|
944
970
|
|
|
945
|
-
_deserialize(
|
|
971
|
+
_deserialize(_context: Context, gameObject: GameObject) {
|
|
946
972
|
const ps = this.particleSystem;
|
|
947
|
-
if (ps
|
|
948
|
-
|
|
973
|
+
if (ps instanceof ParticleSystem) return;
|
|
974
|
+
|
|
975
|
+
let guid = "";
|
|
976
|
+
|
|
977
|
+
if (ps && typeof ps["guid"] === "string") {
|
|
978
|
+
guid = ps["guid"];
|
|
979
|
+
// subemitter MUST be a child of the particle system
|
|
980
|
+
this.particleSystem = GameObject.findByGuid(guid, gameObject) as ParticleSystem;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
if (debug && !(this.particleSystem instanceof ParticleSystem)) {
|
|
984
|
+
console.warn("Could not find particle system for sub emitter", guid, gameObject, this);
|
|
949
985
|
}
|
|
950
986
|
}
|
|
951
987
|
}
|
|
@@ -199,6 +199,33 @@ export class MinMaxCurve {
|
|
|
199
199
|
}
|
|
200
200
|
return 0;
|
|
201
201
|
}
|
|
202
|
+
|
|
203
|
+
getMax(): number {
|
|
204
|
+
switch (this.mode) {
|
|
205
|
+
case ParticleSystemCurveMode.Constant:
|
|
206
|
+
return this.constant;
|
|
207
|
+
case ParticleSystemCurveMode.Curve:
|
|
208
|
+
return this.getMaxFromCurve(this.curve!) * this.curveMultiplier!;
|
|
209
|
+
case ParticleSystemCurveMode.TwoCurves:
|
|
210
|
+
return Math.max(this.getMaxFromCurve(this.curveMin), this.getMaxFromCurve(this.curveMax)) * this.curveMultiplier!;
|
|
211
|
+
case ParticleSystemCurveMode.TwoConstants:
|
|
212
|
+
return Math.max(this.constantMin, this.constantMax);
|
|
213
|
+
default:
|
|
214
|
+
return 0;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private getMaxFromCurve(curve?: AnimationCurve) {
|
|
219
|
+
if (!curve) return 0;
|
|
220
|
+
let maxNumber = Number.MIN_VALUE;
|
|
221
|
+
for (let i = 0; i < curve!.keys.length; i++) {
|
|
222
|
+
const key = curve!.keys[i];
|
|
223
|
+
if (key.value > maxNumber) {
|
|
224
|
+
maxNumber = key.value;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return maxNumber;
|
|
228
|
+
}
|
|
202
229
|
}
|
|
203
230
|
|
|
204
231
|
export class MinMaxGradient {
|
|
@@ -571,7 +598,7 @@ export class ShapeModule implements EmitterShape {
|
|
|
571
598
|
if (this.enabled) {
|
|
572
599
|
switch (this.shapeType) {
|
|
573
600
|
case ParticleSystemShapeType.Box:
|
|
574
|
-
if(debug) Gizmos.DrawBox(this.position, this.scale, 0xdddddd, 1);
|
|
601
|
+
if (debug) Gizmos.DrawBox(this.position, this.scale, 0xdddddd, 1);
|
|
575
602
|
this._vector.x = Math.random() * this.scale.x - this.scale.x / 2;
|
|
576
603
|
this._vector.y = Math.random() * this.scale.y - this.scale.y / 2;
|
|
577
604
|
this._vector.z = Math.random() * this.scale.z - this.scale.z / 2;
|
|
@@ -2,7 +2,7 @@ import { Behavior, Particle, EmissionState, ParticleSystem, ParticleEmitter } fr
|
|
|
2
2
|
import { Vector3, Quaternion, Matrix4 } from "three";
|
|
3
3
|
import { IParticleSystem } from "./ParticleSystemModules";
|
|
4
4
|
import { CircularBuffer } from "../engine/engine_utils";
|
|
5
|
-
import { SubEmitterType } from "./ParticleSystem";
|
|
5
|
+
import { $particleLife, SubEmitterType } from "./ParticleSystem";
|
|
6
6
|
|
|
7
7
|
const VECTOR_ONE = new Vector3(1, 1, 1);
|
|
8
8
|
const VECTOR_Z = new Vector3(0, 0, 1);
|
|
@@ -73,13 +73,15 @@ export class ParticleSubEmitter implements Behavior {
|
|
|
73
73
|
if (!this.subParticleSystem || !particle.emissionState)
|
|
74
74
|
return;
|
|
75
75
|
|
|
76
|
-
if(this.emitterProbability && Math.random() > this.emitterProbability)
|
|
76
|
+
if (this.emitterProbability && Math.random() > this.emitterProbability)
|
|
77
77
|
return;
|
|
78
78
|
|
|
79
79
|
const delta = this.system.deltaTime;
|
|
80
80
|
|
|
81
81
|
if (this.emitterType === SubEmitterType.Death) {
|
|
82
|
-
|
|
82
|
+
let lifeTime = particle.life;
|
|
83
|
+
if (particle[$particleLife] !== undefined) lifeTime = particle[$particleLife];
|
|
84
|
+
const willDie = particle.age + delta * 1.2 >= lifeTime;
|
|
83
85
|
if (!willDie) return;
|
|
84
86
|
// Just emit all for now, we should probably add a way to get the amount from the subsystem emission module
|
|
85
87
|
const maxAmount = this.subSystem.main.maxParticles - this.subSystem.currentParticles;
|