@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.
Files changed (54) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/needle-engine.d.ts +118 -35
  3. package/dist/needle-engine.js +352 -352
  4. package/dist/needle-engine.js.map +3 -3
  5. package/dist/needle-engine.min.js +59 -59
  6. package/dist/needle-engine.min.js.map +3 -3
  7. package/lib/engine/api.d.ts +1 -0
  8. package/lib/engine/api.js +1 -0
  9. package/lib/engine/api.js.map +1 -1
  10. package/lib/engine/engine_gizmos.d.ts +24 -0
  11. package/lib/engine/engine_gizmos.js +97 -5
  12. package/lib/engine/engine_gizmos.js.map +1 -1
  13. package/lib/engine/engine_input.d.ts +2 -0
  14. package/lib/engine/engine_input.js +9 -2
  15. package/lib/engine/engine_input.js.map +1 -1
  16. package/lib/engine/engine_networking.d.ts +1 -0
  17. package/lib/engine/engine_networking.js +9 -0
  18. package/lib/engine/engine_networking.js.map +1 -1
  19. package/lib/engine/engine_physics.d.ts +20 -2
  20. package/lib/engine/engine_physics.js +133 -16
  21. package/lib/engine/engine_physics.js.map +1 -1
  22. package/lib/engine/engine_three_utils.js +1 -22
  23. package/lib/engine/engine_three_utils.js.map +1 -1
  24. package/lib/engine/engine_types.d.ts +12 -6
  25. package/lib/engine/engine_types.js +22 -5
  26. package/lib/engine/engine_types.js.map +1 -1
  27. package/lib/engine/engine_utils.d.ts +8 -0
  28. package/lib/engine/engine_utils.js +22 -0
  29. package/lib/engine/engine_utils.js.map +1 -1
  30. package/lib/engine/extensions/NEEDLE_lightmaps.js +1 -1
  31. package/lib/engine/extensions/NEEDLE_lightmaps.js.map +1 -1
  32. package/lib/engine-components/Component.d.ts +18 -0
  33. package/lib/engine-components/Component.js +15 -2
  34. package/lib/engine-components/Component.js.map +1 -1
  35. package/lib/engine-components/Joints.d.ts +5 -1
  36. package/lib/engine-components/Joints.js +17 -7
  37. package/lib/engine-components/Joints.js.map +1 -1
  38. package/lib/engine-components/codegen/components.d.ts +1 -0
  39. package/lib/engine-components/codegen/components.js +1 -0
  40. package/lib/engine-components/codegen/components.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/engine/api.ts +2 -1
  43. package/src/engine/codegen/register_types.js +4 -0
  44. package/src/engine/engine_gizmos.ts +117 -5
  45. package/src/engine/engine_input.ts +12 -2
  46. package/src/engine/engine_networking.ts +10 -0
  47. package/src/engine/engine_physics.ts +159 -17
  48. package/src/engine/engine_three_utils.ts +1 -29
  49. package/src/engine/engine_types.ts +33 -14
  50. package/src/engine/engine_utils.ts +27 -0
  51. package/src/engine/extensions/NEEDLE_lightmaps.ts +1 -1
  52. package/src/engine-components/Component.ts +30 -4
  53. package/src/engine-components/Joints.ts +19 -7
  54. 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
- addFixedJoint(body1: IRigidbody, body2: IRigidbody, rel: { x: number, y: number, z: number }) {
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
- const rot = body1.worldQuaternion.multiply(body2.worldQuaternion.invert());
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
- rel, rot
757
+ this._tempPosition, this._tempQuaternion,
675
758
  );
676
759
  const joint = this.world.createImpulseJoint(params, b1, b2, true);
677
- console.log("ADD JOINT", joint)
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.numContacts(); i++) {
747
- const pt1 = manifold.localContactPoint1(i);
748
- const dist = manifold.contactDist(i);
749
- if (pt1) {
750
- const contact = new ContactPoint(pt1, dist, normal);
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
- c.onCollisionEnter.call(c, collision);
757
- this.activeCollisions.push({ collider: self, component: c, collision });
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.activeCollisions) {
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() : string;
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() : boolean;
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
- export type Vec2 = {
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 localPoint: Vec3;
167
+ private readonly _point: Vec3;
168
+ private readonly _normal: Vec3;
169
+
164
170
  readonly distance: number;
165
- readonly normal: Vec3;
171
+ readonly impulse: number;
172
+ readonly friction: number;
166
173
 
167
- constructor(localPt: Vec3, dist: number, normal: Vec3) {
168
- this.localPoint = localPt;
169
- this.distance = dist;
170
- this.normal = normal;
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(() => {
@@ -51,7 +51,7 @@ export class NEEDLE_lightmaps implements GLTFLoaderPlugin {
51
51
  const ext: LightmapExtension = extensions[EXTENSION_NAME];
52
52
  if (ext) {
53
53
  const arr = ext.textures;
54
- if (!arr.length) {
54
+ if (!arr?.length) {
55
55
  return null;
56
56
  }
57
57
  if (debug)
@@ -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
- // TODO: can we not have these and then check in update loop if they are implemented (magick methods) ?
312
+ /** called once when the component becomes active for the first time */
313
313
  awake() { }
314
- // update() { }
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
- if (this.connectedAnchor)
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";