@needle-tools/engine 2.39.0-pre → 2.40.0-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 +10 -0
- package/dist/needle-engine.d.ts +118 -35
- package/dist/needle-engine.js +352 -352
- package/dist/needle-engine.js.map +3 -3
- package/dist/needle-engine.min.js +59 -59
- package/dist/needle-engine.min.js.map +3 -3
- package/lib/engine/api.d.ts +1 -0
- package/lib/engine/api.js +1 -0
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/engine_gizmos.d.ts +24 -0
- package/lib/engine/engine_gizmos.js +97 -5
- package/lib/engine/engine_gizmos.js.map +1 -1
- package/lib/engine/engine_input.d.ts +2 -0
- package/lib/engine/engine_input.js +9 -2
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_networking.d.ts +1 -0
- package/lib/engine/engine_networking.js +9 -0
- package/lib/engine/engine_networking.js.map +1 -1
- package/lib/engine/engine_physics.d.ts +20 -2
- package/lib/engine/engine_physics.js +133 -16
- package/lib/engine/engine_physics.js.map +1 -1
- package/lib/engine/engine_three_utils.js +1 -22
- package/lib/engine/engine_three_utils.js.map +1 -1
- package/lib/engine/engine_types.d.ts +12 -6
- package/lib/engine/engine_types.js +22 -5
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine/engine_utils.d.ts +8 -0
- package/lib/engine/engine_utils.js +22 -0
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_lightmaps.js +1 -1
- package/lib/engine/extensions/NEEDLE_lightmaps.js.map +1 -1
- package/lib/engine-components/Component.d.ts +18 -0
- package/lib/engine-components/Component.js +15 -2
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/Joints.d.ts +5 -1
- package/lib/engine-components/Joints.js +17 -7
- package/lib/engine-components/Joints.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +1 -0
- package/lib/engine-components/codegen/components.js +1 -0
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/api.ts +2 -1
- package/src/engine/codegen/register_types.js +4 -0
- package/src/engine/engine_gizmos.ts +117 -5
- package/src/engine/engine_input.ts +12 -2
- package/src/engine/engine_networking.ts +10 -0
- package/src/engine/engine_physics.ts +159 -17
- package/src/engine/engine_three_utils.ts +1 -29
- package/src/engine/engine_types.ts +33 -14
- package/src/engine/engine_utils.ts +27 -0
- package/src/engine/extensions/NEEDLE_lightmaps.ts +1 -1
- package/src/engine-components/Component.ts +30 -4
- package/src/engine-components/Joints.ts +19 -7
- package/src/engine-components/codegen/components.ts +1 -0
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { BasicDepthPacking, Box3, BufferAttribute, BufferGeometry, Camera, Intersection, Layers, LineBasicMaterial, LineSegments, Matrix4, Mesh, NormalAnimationBlendMode, NumberKeyframeTrack, Object3D, Quaternion, Ray, Raycaster, Sphere, Vector2, Vector3 } from 'three'
|
|
2
2
|
import { Context } from './engine_setup';
|
|
3
|
-
import { getParam } from "./engine_utils"
|
|
4
|
-
import { getWorldPosition, getWorldQuaternion, getWorldScale, setWorldPositionXYZ, setWorldQuaternionXYZW } from "./engine_three_utils"
|
|
3
|
+
import { CircularBuffer, getParam } from "./engine_utils"
|
|
4
|
+
import { getWorldPosition, getWorldQuaternion, getWorldScale, setWorldPositionXYZ, setWorldQuaternion, setWorldQuaternionXYZW } from "./engine_three_utils"
|
|
5
5
|
import {
|
|
6
6
|
IComponent,
|
|
7
7
|
ICollider,
|
|
8
8
|
IRigidbody,
|
|
9
9
|
Collision,
|
|
10
10
|
ContactPoint,
|
|
11
|
-
Vec3
|
|
11
|
+
Vec3,
|
|
12
|
+
IGameObject,
|
|
13
|
+
Vec2,
|
|
12
14
|
} from './engine_types';
|
|
13
15
|
import { InstancingUtil } from './engine_instancing';
|
|
14
16
|
import { foreachComponent } from './engine_gameobject';
|
|
@@ -21,6 +23,7 @@ export type Rapier = typeof RAPIER;
|
|
|
21
23
|
const debugPhysics = getParam("debugphysics");
|
|
22
24
|
const debugColliderPlacement = getParam("debugphysicscolliders");
|
|
23
25
|
const debugCollisions = getParam("debugcollisions");
|
|
26
|
+
const showColliders = getParam("showcolliders");
|
|
24
27
|
|
|
25
28
|
|
|
26
29
|
declare type PhysicsBody = {
|
|
@@ -28,7 +31,9 @@ declare type PhysicsBody = {
|
|
|
28
31
|
rotation(): { x: number, y: number, z: number, w: number }
|
|
29
32
|
}
|
|
30
33
|
|
|
34
|
+
/** on physics body and references the needle component */
|
|
31
35
|
const $componentKey = Symbol("needle component");
|
|
36
|
+
/** on needle component and references physics body */
|
|
32
37
|
const $bodyKey = Symbol("physics body");
|
|
33
38
|
const $colliderRigidbody = Symbol("rigidbody");
|
|
34
39
|
// const $removed = Symbol("removed");
|
|
@@ -75,6 +80,12 @@ export class SphereIntersection implements Intersection {
|
|
|
75
80
|
}
|
|
76
81
|
}
|
|
77
82
|
|
|
83
|
+
declare type PhysicsRaycastResult = {
|
|
84
|
+
point: Vector3,
|
|
85
|
+
normal?: Vector3,
|
|
86
|
+
collider?: ICollider
|
|
87
|
+
}
|
|
88
|
+
|
|
78
89
|
export class Physics {
|
|
79
90
|
|
|
80
91
|
// raycasting
|
|
@@ -140,6 +151,10 @@ export class Physics {
|
|
|
140
151
|
return this.raycast(opts);
|
|
141
152
|
}
|
|
142
153
|
|
|
154
|
+
/** raycast against rendered three objects. This might be very slow depending on your scene complexity.
|
|
155
|
+
* We recommend setting objects to IgnoreRaycast layer (2) when you don't need them to be raycasted.
|
|
156
|
+
* Raycasting SkinnedMeshes is specially expensive.
|
|
157
|
+
*/
|
|
143
158
|
public raycast(options: RaycastOptions | null = null): Array<Intersection> {
|
|
144
159
|
if (!options) options = this.defaultRaycastOptions;
|
|
145
160
|
const mp = options.screenPoint ?? this.context.input.mousePositionRC;
|
|
@@ -194,6 +209,7 @@ export class Physics {
|
|
|
194
209
|
results.length = 0;
|
|
195
210
|
rc.intersectObjects(targets, options.recursive, results);
|
|
196
211
|
|
|
212
|
+
// TODO: instead of doing this we should temporerly set these objects to layer 2 during raycasting
|
|
197
213
|
const ignorelist = options.ignore;
|
|
198
214
|
if (ignorelist !== undefined && ignorelist.length > 0) {
|
|
199
215
|
results = results.filter(r => !ignorelist.includes(r.object));
|
|
@@ -201,8 +217,70 @@ export class Physics {
|
|
|
201
217
|
return results;
|
|
202
218
|
}
|
|
203
219
|
|
|
220
|
+
private rapierRay = new RAPIER.Ray({ x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 1 });
|
|
221
|
+
private raycastVectorsBuffer = new CircularBuffer(() => new Vector3(), 10);
|
|
204
222
|
|
|
223
|
+
/** raycast against colliders */
|
|
224
|
+
public raycastPhysicsFast(origin: Vec2 | Vec3, direction: Vec3 | undefined = undefined, maxDistance: number = Infinity, solid: boolean = true)
|
|
225
|
+
: null | { point: Vector3, collider:ICollider } {
|
|
205
226
|
|
|
227
|
+
const ray = this.getPhysicsRay(this.rapierRay, origin, direction);
|
|
228
|
+
if (!ray) return null;
|
|
229
|
+
|
|
230
|
+
const hit = this.world?.castRay(ray, maxDistance, solid);
|
|
231
|
+
if (hit) {
|
|
232
|
+
const point = ray.pointAt(hit.toi);
|
|
233
|
+
const vec = this.raycastVectorsBuffer.get();
|
|
234
|
+
vec.set(point.x, point.y, point.z);
|
|
235
|
+
return { point: vec, collider: hit.collider[$componentKey] };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private getPhysicsRay(ray: RAPIER.Ray, origin: Vec2 | Vec3, direction: Vec3 | undefined = undefined): RAPIER.Ray | null {
|
|
242
|
+
const cam = this.context.mainCamera;
|
|
243
|
+
// if we get origin in 2d space we need to project it to 3d space
|
|
244
|
+
if (origin["z"] === undefined) {
|
|
245
|
+
if (!cam) {
|
|
246
|
+
console.error("Can not perform raycast from 2d point - no main camera found");
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
const vec3 = this.raycastVectorsBuffer.get();
|
|
250
|
+
// if the origin is in screen space we need to convert it to raycaster space
|
|
251
|
+
if (origin.x > 1 || origin.y > 1 || origin.y < -1 || origin.x < -1) {
|
|
252
|
+
this.context.input.convertScreenspaceToRaycastSpace(origin);
|
|
253
|
+
}
|
|
254
|
+
vec3.set(origin.x, origin.y, -1);
|
|
255
|
+
vec3.unproject(cam);
|
|
256
|
+
origin = vec3;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const o = origin as Vec3;
|
|
260
|
+
|
|
261
|
+
ray.origin.x = o.x;
|
|
262
|
+
ray.origin.y = o.y;
|
|
263
|
+
ray.origin.z = o.z;
|
|
264
|
+
const vec = this.raycastVectorsBuffer.get();
|
|
265
|
+
if (direction)
|
|
266
|
+
vec.set(direction.x, direction.y, direction.z);
|
|
267
|
+
else {
|
|
268
|
+
if (!cam) {
|
|
269
|
+
console.error("Can not perform raycast - no camera found");
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
vec.set(ray.origin.x, ray.origin.y, ray.origin.z);
|
|
273
|
+
const camPosition = getWorldPosition(cam);
|
|
274
|
+
vec.sub(camPosition);
|
|
275
|
+
}
|
|
276
|
+
// we need to normalize the ray because our input is a max travel length and the direction may be not normalized
|
|
277
|
+
vec.normalize();
|
|
278
|
+
ray.dir.x = vec.x;
|
|
279
|
+
ray.dir.y = vec.y;
|
|
280
|
+
ray.dir.z = vec.z;
|
|
281
|
+
// Gizmos.DrawRay(ray.origin, ray.dir, 0xff0000, Infinity);
|
|
282
|
+
return ray;
|
|
283
|
+
}
|
|
206
284
|
|
|
207
285
|
// physics simulation
|
|
208
286
|
|
|
@@ -514,7 +592,7 @@ export class Physics {
|
|
|
514
592
|
}
|
|
515
593
|
|
|
516
594
|
private updateDebugRendering(world: World) {
|
|
517
|
-
if (debugPhysics || debugColliderPlacement) {
|
|
595
|
+
if (debugPhysics || debugColliderPlacement || showColliders) {
|
|
518
596
|
if (!this.lines) {
|
|
519
597
|
let material = new LineBasicMaterial({
|
|
520
598
|
color: 0xffffff,
|
|
@@ -661,22 +739,62 @@ export class Physics {
|
|
|
661
739
|
private static centerConnectionPos = { x: 0, y: 0, z: 0 };
|
|
662
740
|
private static centerConnectionRot = { x: 0, y: 0, z: 0, w: 1 };
|
|
663
741
|
|
|
664
|
-
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
addFixedJoint(body1: IRigidbody, body2: IRigidbody) {
|
|
665
745
|
if (!this.world) {
|
|
666
746
|
console.error("Physics world not initialized");
|
|
667
747
|
return;
|
|
668
748
|
}
|
|
669
749
|
const b1 = body1[$bodyKey] as RigidBody;
|
|
670
750
|
const b2 = body2[$bodyKey] as RigidBody;
|
|
671
|
-
|
|
751
|
+
|
|
752
|
+
this.calculateJointRelativeMatrices(body1.gameObject, body2.gameObject, this._tempMatrix);
|
|
753
|
+
this._tempMatrix.decompose(this._tempPosition, this._tempQuaternion, this._tempScale);
|
|
754
|
+
|
|
672
755
|
const params = JointData.fixed(
|
|
673
756
|
Physics.centerConnectionPos, Physics.centerConnectionRot,
|
|
674
|
-
|
|
757
|
+
this._tempPosition, this._tempQuaternion,
|
|
675
758
|
);
|
|
676
759
|
const joint = this.world.createImpulseJoint(params, b1, b2, true);
|
|
677
|
-
|
|
760
|
+
if (debugPhysics)
|
|
761
|
+
console.log("ADD FIXED JOINT", joint)
|
|
678
762
|
}
|
|
679
763
|
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
addHingeJoint(body1: IRigidbody, body2: IRigidbody, anchor: { x: number, y: number, z: number }, axis: { x: number, y: number, z: number }) {
|
|
767
|
+
if (!this.world) {
|
|
768
|
+
console.error("Physics world not initialized");
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
const b1 = body1[$bodyKey] as RigidBody;
|
|
772
|
+
const b2 = body2[$bodyKey] as RigidBody;
|
|
773
|
+
|
|
774
|
+
this.calculateJointRelativeMatrices(body1.gameObject, body2.gameObject, this._tempMatrix);
|
|
775
|
+
this._tempMatrix.decompose(this._tempPosition, this._tempQuaternion, this._tempScale);
|
|
776
|
+
|
|
777
|
+
let params = RAPIER.JointData.revolute(anchor, this._tempPosition, axis);
|
|
778
|
+
let joint = this.world.createImpulseJoint(params, b1, b2, true);
|
|
779
|
+
if (debugPhysics)
|
|
780
|
+
console.log("ADD HINGE JOINT", joint)
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
private calculateJointRelativeMatrices(body1: IGameObject, body2: IGameObject, mat: Matrix4) {
|
|
785
|
+
body1.updateWorldMatrix(true, false);
|
|
786
|
+
body2.updateWorldMatrix(true, false);
|
|
787
|
+
const world1 = body1.matrixWorld;
|
|
788
|
+
const world2 = body2.matrixWorld;
|
|
789
|
+
// set scale to 1
|
|
790
|
+
world1.elements[0] = 1;
|
|
791
|
+
world1.elements[5] = 1;
|
|
792
|
+
world1.elements[10] = 1;
|
|
793
|
+
world2.elements[0] = 1;
|
|
794
|
+
world2.elements[5] = 1;
|
|
795
|
+
world2.elements[10] = 1;
|
|
796
|
+
mat.copy(world2).premultiply(world1.invert()).invert();
|
|
797
|
+
}
|
|
680
798
|
}
|
|
681
799
|
|
|
682
800
|
|
|
@@ -693,6 +811,7 @@ class PhysicsCollisionHandler {
|
|
|
693
811
|
}
|
|
694
812
|
|
|
695
813
|
private activeCollisions: Array<{ collider: ICollider, component: IComponent, collision: Collision }> = [];
|
|
814
|
+
private activeCollisionsStay: Array<{ collider: ICollider, component: IComponent, collision: Collision }> = [];
|
|
696
815
|
private activeTriggers: Array<{ collider: ICollider, component: IComponent, otherCollider: ICollider }> = [];
|
|
697
816
|
|
|
698
817
|
handleCollisionEvents() {
|
|
@@ -739,22 +858,32 @@ class PhysicsCollisionHandler {
|
|
|
739
858
|
// TODO: we dont respect the flip value here!
|
|
740
859
|
this.world.contactPair(selfBody, otherBody, (manifold, _flipped) => {
|
|
741
860
|
foreachComponent(object, (c: IComponent) => {
|
|
742
|
-
if (c.onCollisionEnter) {
|
|
861
|
+
if (c.onCollisionEnter || c.onCollisionStay || c.onCollisionExit) {
|
|
743
862
|
if (!collision) {
|
|
744
863
|
const contacts: Array<ContactPoint> = [];
|
|
745
864
|
const normal = manifold.normal();
|
|
746
|
-
for (let i = 0; i < manifold.
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
865
|
+
for (let i = 0; i < manifold.numSolverContacts(); i++) {
|
|
866
|
+
// solver points are in world space
|
|
867
|
+
// https://rapier.rs/docs/user_guides/javascript/advanced_collision_detection_js#the-contact-graph
|
|
868
|
+
const pt = manifold.solverContactPoint(i);
|
|
869
|
+
const impulse = manifold.contactImpulse(i);
|
|
870
|
+
if (pt) {
|
|
871
|
+
const dist = manifold.contactDist(i);
|
|
872
|
+
const friction = manifold.solverContactFriction(i);
|
|
873
|
+
const contact = new ContactPoint(pt, dist, normal, impulse, friction);
|
|
751
874
|
contacts.push(contact);
|
|
752
875
|
}
|
|
753
876
|
}
|
|
754
877
|
collision = new Collision(object, other, contacts);
|
|
755
878
|
}
|
|
756
|
-
|
|
757
|
-
|
|
879
|
+
|
|
880
|
+
const info = { collider: self, component: c, collision };
|
|
881
|
+
this.activeCollisions.push(info);
|
|
882
|
+
if (c.onCollisionStay) {
|
|
883
|
+
this.activeCollisionsStay.push(info);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
c.onCollisionEnter?.call(c, collision);
|
|
758
887
|
}
|
|
759
888
|
});
|
|
760
889
|
});
|
|
@@ -762,7 +891,7 @@ class PhysicsCollisionHandler {
|
|
|
762
891
|
}
|
|
763
892
|
|
|
764
893
|
private onHandleCollisionStay() {
|
|
765
|
-
for (const active of this.
|
|
894
|
+
for (const active of this.activeCollisionsStay) {
|
|
766
895
|
const c = active.component;
|
|
767
896
|
if (c.activeAndEnabled && c.onCollisionStay) {
|
|
768
897
|
const arg = active.collision;
|
|
@@ -792,6 +921,19 @@ class PhysicsCollisionHandler {
|
|
|
792
921
|
}
|
|
793
922
|
}
|
|
794
923
|
}
|
|
924
|
+
for (let i = 0; i < this.activeCollisionsStay.length; i++) {
|
|
925
|
+
const active = this.activeCollisionsStay[i];
|
|
926
|
+
const collider = active.collider;
|
|
927
|
+
if (collider === self && active.collision.collider === other) {
|
|
928
|
+
const c = active.component;
|
|
929
|
+
this.activeCollisionsStay.splice(i, 1);
|
|
930
|
+
i--;
|
|
931
|
+
if (c.activeAndEnabled && c.onCollisionExit) {
|
|
932
|
+
const collision = active.collision;
|
|
933
|
+
c.onCollisionExit(collision);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
795
937
|
for (let i = 0; i < this.activeTriggers.length; i++) {
|
|
796
938
|
const active = this.activeTriggers[i];
|
|
797
939
|
const collider = active.collider;
|
|
@@ -1,31 +1,7 @@
|
|
|
1
1
|
import * as THREE from "three";
|
|
2
2
|
import { Mathf } from "./engine_math"
|
|
3
3
|
import { Vector3, Quaternion, Uniform } from "three";
|
|
4
|
-
|
|
5
|
-
class CircularBuffer<T> {
|
|
6
|
-
private _factory: () => T;
|
|
7
|
-
private _cache: T[] = [];
|
|
8
|
-
private _maxSize: number;
|
|
9
|
-
private _index: number = 0;
|
|
10
|
-
|
|
11
|
-
constructor(factory: () => T, maxSize: number) {
|
|
12
|
-
this._factory = factory;
|
|
13
|
-
this._maxSize = maxSize;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
get(): T {
|
|
17
|
-
let i = this._index++;
|
|
18
|
-
if (i >= this._cache.length) {
|
|
19
|
-
if (i >= this._maxSize) {
|
|
20
|
-
i = this._index = 0;
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
this._cache.push(this._factory());
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return this._cache[i];
|
|
27
|
-
}
|
|
28
|
-
}
|
|
4
|
+
import { CircularBuffer } from "./engine_utils";
|
|
29
5
|
|
|
30
6
|
|
|
31
7
|
|
|
@@ -39,10 +15,6 @@ export function lookAtInverse(obj: THREE.Object3D, target: Vector3) {
|
|
|
39
15
|
|
|
40
16
|
const _worldPositions = new CircularBuffer(() => new THREE.Vector3(), 100);
|
|
41
17
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
18
|
export function getWorldPosition(obj: THREE.Object3D, vec: THREE.Vector3 | null = null, updateParents: boolean = true): THREE.Vector3 {
|
|
47
19
|
const wp = vec ?? _worldPositions.get();
|
|
48
20
|
if (!obj) return wp.set(0, 0, 0);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Camera, Color, Material, Object3D, Vector3, Quaternion } from "three";
|
|
2
2
|
import { RGBAColor } from "../engine-components/js-extensions/RGBAColor";
|
|
3
3
|
import { CollisionDetectionMode, RigidbodyConstraints } from "./engine_physics.types";
|
|
4
|
+
import { getWorldPosition } from "./engine_three_utils";
|
|
5
|
+
import { CircularBuffer } from "./engine_utils";
|
|
4
6
|
|
|
5
7
|
/** used to find data registered via gltf files e.g. find lightmaps for a Renderer component that were shipped inside a gltf */
|
|
6
8
|
export declare type SourceIdentifier = string;
|
|
@@ -40,7 +42,7 @@ export interface IComponent {
|
|
|
40
42
|
get name(): string;
|
|
41
43
|
get layer(): number;
|
|
42
44
|
get destroyed(): boolean;
|
|
43
|
-
get tag()
|
|
45
|
+
get tag(): string;
|
|
44
46
|
|
|
45
47
|
context: any;
|
|
46
48
|
|
|
@@ -84,7 +86,7 @@ export interface IComponent {
|
|
|
84
86
|
|
|
85
87
|
|
|
86
88
|
export declare interface ICamera extends IComponent {
|
|
87
|
-
get isCamera()
|
|
89
|
+
get isCamera(): boolean;
|
|
88
90
|
applyClearFlagsIfIsActiveCamera(): unknown;
|
|
89
91
|
buildCamera();
|
|
90
92
|
get cam(): Camera;
|
|
@@ -147,34 +149,51 @@ export declare type ICollisionContext = {
|
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
|
|
152
|
+
export type Vec2 = {
|
|
153
|
+
x: number,
|
|
154
|
+
y: number
|
|
155
|
+
}
|
|
156
|
+
|
|
150
157
|
export type Vec3 = {
|
|
151
158
|
x: number,
|
|
152
159
|
y: number,
|
|
153
160
|
z: number
|
|
154
161
|
}
|
|
155
162
|
|
|
156
|
-
|
|
157
|
-
x: number,
|
|
158
|
-
y: number
|
|
159
|
-
}
|
|
163
|
+
const contactsVectorBuffer = new CircularBuffer(() => new Vector3(), 20);
|
|
160
164
|
|
|
161
165
|
export class ContactPoint {
|
|
162
166
|
|
|
163
|
-
readonly
|
|
167
|
+
private readonly _point: Vec3;
|
|
168
|
+
private readonly _normal: Vec3;
|
|
169
|
+
|
|
164
170
|
readonly distance: number;
|
|
165
|
-
readonly
|
|
171
|
+
readonly impulse: number;
|
|
172
|
+
readonly friction: number;
|
|
166
173
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
this.
|
|
174
|
+
/** worldspace point */
|
|
175
|
+
get point() {
|
|
176
|
+
const target = contactsVectorBuffer.get();
|
|
177
|
+
return target.set(this._point.x, this._point.y, this._point.z);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** worldspace normal */
|
|
181
|
+
get normal() {
|
|
182
|
+
const target = contactsVectorBuffer.get();
|
|
183
|
+
return target.set(this._normal.x, this._normal.y, this._normal.z);
|
|
171
184
|
}
|
|
172
185
|
|
|
186
|
+
constructor(point: Vec3, dist: number, normal: Vec3, impulse: number, friction: number) {
|
|
187
|
+
this._point = point;
|
|
188
|
+
this.distance = dist;
|
|
189
|
+
this._normal = normal;
|
|
190
|
+
this.impulse = impulse;
|
|
191
|
+
this.friction = friction;
|
|
192
|
+
}
|
|
173
193
|
}
|
|
174
194
|
|
|
175
195
|
/// all info in here must be readonly because the object is only created once per started collision
|
|
176
196
|
export class Collision {
|
|
177
|
-
|
|
178
197
|
readonly contacts: ContactPoint[];
|
|
179
198
|
|
|
180
199
|
constructor(obj: Object3D, otherCollider: ICollider, contacts: ContactPoint[]) {
|
|
@@ -203,7 +222,7 @@ export class Collision {
|
|
|
203
222
|
return this.collider?.attachedRigidbody;
|
|
204
223
|
}
|
|
205
224
|
|
|
206
|
-
|
|
225
|
+
|
|
207
226
|
|
|
208
227
|
// private _normal?: Vector3;
|
|
209
228
|
// get normal(): Vector3 {
|
|
@@ -14,6 +14,33 @@ export function isDebugMode(): boolean {
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
export class CircularBuffer<T> {
|
|
18
|
+
private _factory: () => T;
|
|
19
|
+
private _cache: T[] = [];
|
|
20
|
+
private _maxSize: number;
|
|
21
|
+
private _index: number = 0;
|
|
22
|
+
|
|
23
|
+
constructor(factory: () => T, maxSize: number) {
|
|
24
|
+
this._factory = factory;
|
|
25
|
+
this._maxSize = maxSize;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get(): T {
|
|
29
|
+
let i = this._index++;
|
|
30
|
+
if (i >= this._cache.length) {
|
|
31
|
+
if (i >= this._maxSize) {
|
|
32
|
+
i = this._index = 0;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
this._cache.push(this._factory());
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return this._cache[i];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
17
44
|
let saveParams: boolean = false;
|
|
18
45
|
const requestedParams: Array<string> = [];
|
|
19
46
|
setTimeout(() => {
|
|
@@ -309,14 +309,17 @@ class Component implements IComponent, EventTarget {
|
|
|
309
309
|
/** called on a component with a map of old to new guids (e.g. when instantiate generated new guids and e.g. timeline track bindings needs to remape them) */
|
|
310
310
|
resolveGuids?(guidsMap: GuidsMap): void;
|
|
311
311
|
|
|
312
|
-
|
|
312
|
+
/** called once when the component becomes active for the first time */
|
|
313
313
|
awake() { }
|
|
314
|
-
|
|
314
|
+
/** called every time when the component gets enabled (this is invoked after awake and before start) */
|
|
315
315
|
onEnable() { }
|
|
316
316
|
onDisable() { }
|
|
317
317
|
onDestroy() {
|
|
318
318
|
this.__destroyed = true;
|
|
319
319
|
}
|
|
320
|
+
/** called when you decorate fields with the @validate() decorator
|
|
321
|
+
* @param field the name of the field that was changed
|
|
322
|
+
*/
|
|
320
323
|
onValidate?(prop?: string): void;
|
|
321
324
|
start?(): void;
|
|
322
325
|
earlyUpdate?(): void;
|
|
@@ -350,20 +353,35 @@ class Component implements IComponent, EventTarget {
|
|
|
350
353
|
this.__internalDestroy();
|
|
351
354
|
}
|
|
352
355
|
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
/** @internal */
|
|
353
359
|
protected __didAwake: boolean = false;
|
|
360
|
+
|
|
361
|
+
/** @internal */
|
|
354
362
|
private __didStart: boolean = false;
|
|
363
|
+
|
|
364
|
+
/** @internal */
|
|
355
365
|
protected __didEnable: boolean = false;
|
|
366
|
+
|
|
367
|
+
/** @internal */
|
|
356
368
|
protected __isEnabled: boolean | undefined = undefined;
|
|
369
|
+
|
|
370
|
+
/** @internal */
|
|
357
371
|
private __destroyed: boolean = false;
|
|
358
|
-
|
|
372
|
+
|
|
373
|
+
/** @internal */
|
|
359
374
|
get __internalDidAwakeAndStart() { return this.__didAwake && this.__didStart; }
|
|
360
375
|
|
|
361
|
-
|
|
376
|
+
|
|
377
|
+
/** @internal */
|
|
362
378
|
constructor() {
|
|
363
379
|
// super();
|
|
364
380
|
this.__internalNewInstanceCreated();
|
|
365
381
|
}
|
|
366
382
|
|
|
383
|
+
|
|
384
|
+
/** @internal */
|
|
367
385
|
__internalNewInstanceCreated() {
|
|
368
386
|
this.__didAwake = false;
|
|
369
387
|
this.__didStart = false;
|
|
@@ -373,6 +391,8 @@ class Component implements IComponent, EventTarget {
|
|
|
373
391
|
// this.__internalResetsCachedPhysicsData();
|
|
374
392
|
}
|
|
375
393
|
|
|
394
|
+
|
|
395
|
+
/** @internal */
|
|
376
396
|
__internalAwake() {
|
|
377
397
|
if (this.__didAwake) return;
|
|
378
398
|
// console.log("__internalAwake");
|
|
@@ -382,12 +402,16 @@ class Component implements IComponent, EventTarget {
|
|
|
382
402
|
this.awake();
|
|
383
403
|
}
|
|
384
404
|
|
|
405
|
+
|
|
406
|
+
/** @internal */
|
|
385
407
|
__internalStart() {
|
|
386
408
|
if (this.__didStart) return;
|
|
387
409
|
this.__didStart = true;
|
|
388
410
|
if (this.start) this.start();
|
|
389
411
|
}
|
|
390
412
|
|
|
413
|
+
|
|
414
|
+
/** @internal */
|
|
391
415
|
__internalEnable(): boolean {
|
|
392
416
|
if (this.__didEnable) return false;
|
|
393
417
|
// console.trace("INTERNAL ENABLE");
|
|
@@ -398,6 +422,7 @@ class Component implements IComponent, EventTarget {
|
|
|
398
422
|
return true;
|
|
399
423
|
}
|
|
400
424
|
|
|
425
|
+
/** @internal */
|
|
401
426
|
__internalDisable() {
|
|
402
427
|
if (!this.__didEnable) return;
|
|
403
428
|
this.__didEnable = false;
|
|
@@ -408,6 +433,7 @@ class Component implements IComponent, EventTarget {
|
|
|
408
433
|
this.__isEnabled = false;
|
|
409
434
|
}
|
|
410
435
|
|
|
436
|
+
/** @internal */
|
|
411
437
|
__internalDestroy() {
|
|
412
438
|
if (this.__destroyed) return;
|
|
413
439
|
this.__destroyed = true;
|
|
@@ -13,9 +13,6 @@ export abstract class Joint extends Behaviour {
|
|
|
13
13
|
private _rigidBody: Rigidbody | null = null;
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
@serializeable(Vector3)
|
|
17
|
-
connectedAnchor?: Vector3;
|
|
18
|
-
|
|
19
16
|
onEnable() {
|
|
20
17
|
if (!this._rigidBody) this._rigidBody = this.gameObject.getComponent(Rigidbody);
|
|
21
18
|
if (this.rigidBody && this.connectedBody)
|
|
@@ -24,8 +21,9 @@ export abstract class Joint extends Behaviour {
|
|
|
24
21
|
|
|
25
22
|
private *create() {
|
|
26
23
|
yield;
|
|
27
|
-
if (this.rigidBody && this.connectedBody)
|
|
28
|
-
this.createJoint(this.rigidBody, this.connectedBody)
|
|
24
|
+
if (this.rigidBody && this.connectedBody) {
|
|
25
|
+
this.createJoint(this.rigidBody, this.connectedBody)
|
|
26
|
+
}
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
protected abstract createJoint(self: Rigidbody, other: Rigidbody);
|
|
@@ -34,7 +32,21 @@ export abstract class Joint extends Behaviour {
|
|
|
34
32
|
export class FixedJoint extends Joint {
|
|
35
33
|
|
|
36
34
|
protected createJoint(self: Rigidbody, other: Rigidbody) {
|
|
37
|
-
|
|
38
|
-
this.context.physics.addFixedJoint(self, other, this.connectedAnchor);
|
|
35
|
+
this.context.physics.addFixedJoint(self, other);
|
|
39
36
|
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class HingeJoint extends Joint {
|
|
40
|
+
|
|
41
|
+
@serializeable(Vector3)
|
|
42
|
+
anchor?: Vector3;
|
|
43
|
+
|
|
44
|
+
@serializeable(Vector3)
|
|
45
|
+
axis?: Vector3;
|
|
46
|
+
|
|
47
|
+
protected createJoint(self: Rigidbody, other: Rigidbody) {
|
|
48
|
+
if (this.axis && this.anchor)
|
|
49
|
+
this.context.physics.addHingeJoint(self, other, this.anchor, this.axis);
|
|
50
|
+
}
|
|
51
|
+
|
|
40
52
|
}
|
|
@@ -39,6 +39,7 @@ export { GroundProjectedEnv } from "../GroundProjection";
|
|
|
39
39
|
export { Interactable } from "../Interactable";
|
|
40
40
|
export { UsageMarker } from "../Interactable";
|
|
41
41
|
export { FixedJoint } from "../Joints";
|
|
42
|
+
export { HingeJoint } from "../Joints";
|
|
42
43
|
export { Light } from "../Light";
|
|
43
44
|
export { LODModel } from "../LODGroup";
|
|
44
45
|
export { LODGroup } from "../LODGroup";
|