@needle-tools/engine 2.31.0-pre → 2.32.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 (46) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/needle-engine.d.ts +148 -141
  3. package/dist/needle-engine.js +332 -332
  4. package/dist/needle-engine.js.map +4 -4
  5. package/dist/needle-engine.min.js +17 -17
  6. package/dist/needle-engine.min.js.map +4 -4
  7. package/lib/engine/debug/error_overlay.js +4 -4
  8. package/lib/engine/debug/error_overlay.js.map +1 -1
  9. package/lib/engine/engine_input.d.ts +87 -102
  10. package/lib/engine/engine_input.js +173 -99
  11. package/lib/engine/engine_input.js.map +1 -1
  12. package/lib/engine/engine_mainloop_utils.d.ts +2 -1
  13. package/lib/engine/engine_mainloop_utils.js +5 -2
  14. package/lib/engine/engine_mainloop_utils.js.map +1 -1
  15. package/lib/engine/engine_physics.d.ts +5 -1
  16. package/lib/engine/engine_physics.js +72 -22
  17. package/lib/engine/engine_physics.js.map +1 -1
  18. package/lib/engine/engine_setup.js +7 -3
  19. package/lib/engine/engine_setup.js.map +1 -1
  20. package/lib/engine/engine_utils.d.ts +9 -7
  21. package/lib/engine/engine_utils.js +35 -2
  22. package/lib/engine/engine_utils.js.map +1 -1
  23. package/lib/engine-components/Component.d.ts +10 -3
  24. package/lib/engine-components/Component.js +98 -40
  25. package/lib/engine-components/Component.js.map +1 -1
  26. package/lib/engine-components/OrbitControls.d.ts +2 -0
  27. package/lib/engine-components/OrbitControls.js +9 -0
  28. package/lib/engine-components/OrbitControls.js.map +1 -1
  29. package/lib/engine-components/Rigidbody.d.ts +12 -6
  30. package/lib/engine-components/Rigidbody.js +64 -17
  31. package/lib/engine-components/Rigidbody.js.map +1 -1
  32. package/lib/engine-components/XRFlag.js.map +1 -1
  33. package/lib/engine-components/ui/Text.js +2 -1
  34. package/lib/engine-components/ui/Text.js.map +1 -1
  35. package/package.json +1 -1
  36. package/src/engine/debug/error_overlay.ts +3 -3
  37. package/src/engine/engine_input.ts +179 -103
  38. package/src/engine/engine_mainloop_utils.ts +6 -2
  39. package/src/engine/engine_physics.ts +86 -24
  40. package/src/engine/engine_setup.ts +8 -3
  41. package/src/engine/engine_utils.ts +50 -4
  42. package/src/engine-components/Component.ts +109 -40
  43. package/src/engine-components/OrbitControls.ts +11 -1
  44. package/src/engine-components/RigidBody.ts +76 -20
  45. package/src/engine-components/XRFlag.ts +0 -2
  46. package/src/engine-components/ui/Text.ts +2 -1
@@ -79,9 +79,12 @@ abstract class GameObject extends THREE.Object3D implements THREE.Object3D {
79
79
 
80
80
  guid: string | undefined;
81
81
 
82
- public static setActive(go: THREE.Object3D, active: boolean) {
82
+ public static setActive(go: THREE.Object3D, active: boolean, processStart: boolean = true) {
83
83
  if (!go) return;
84
84
  go.visible = active;
85
+ main.updateActiveInHierarchyWithoutEventCall(go);
86
+ if (active && processStart)
87
+ main.processStart(Context.Current, go);
85
88
  }
86
89
 
87
90
  public static isActiveSelf(go: THREE.Object3D): boolean {
@@ -838,8 +841,13 @@ class Component implements EventTarget {
838
841
 
839
842
  onCollisionEnter?(col: Collision);
840
843
  onCollisionExit?(col: Collision);
844
+ onCollisionExitRaw?(col: Collision);
841
845
  onCollisionStay?(col: Collision);
842
846
 
847
+ onTriggerEnter?(col: Collision);
848
+ onTriggerStay?(col: Collision);
849
+ onTriggerExit?(col: Collision);
850
+
843
851
  startCoroutine(routine: Generator, evt: FrameEvent = FrameEvent.Update): Generator {
844
852
  return this.context.registerCoroutineUpdate(this, routine, evt);
845
853
  }
@@ -900,6 +908,7 @@ class Component implements EventTarget {
900
908
  this.__didEnable = false;
901
909
  this._collisionExitRoutine = undefined;
902
910
  this.onDisable();
911
+ this._collisions?.clear();
903
912
  }
904
913
 
905
914
  constructor() {
@@ -907,55 +916,115 @@ class Component implements EventTarget {
907
916
  }
908
917
 
909
918
  private _collisionExitRoutine: any;
910
- private _collisions: Map<Object3D, { col: Collision, frame: number }> | null = null;
919
+ private _collisions: Map<Object3D, { col: Collision, frame: number, exitFrame?: number }> | null = null;
920
+
921
+ private _triggerExitRoutine: any;
922
+ private _triggerCollisions: Map<Object3D, { col: Collision, frame: number, exitFrame?: number }> | null = null;
923
+
924
+ get collisionsCount() { return this._collisions?.size ?? 0; }
911
925
 
912
926
  private __internalResetsCachedPhysicsData() {
913
927
  this._collisionExitRoutine = null;
914
928
  this._collisions = null;
929
+ this._triggerExitRoutine = null;
930
+ this._triggerCollisions = null;
915
931
  }
916
932
 
917
- __internalHandleCollision(col: Collision) {
918
- if (this.onCollisionEnter || this.onCollisionExit || this.onCollisionStay) {
919
- const otherObject = col.gameObject;
920
- if (!this._collisions) this._collisions = new Map();
921
- if (this._collisions.has(otherObject)) {
922
- const cur = this._collisions.get(otherObject)!;
923
- cur.frame = this.context.time.frameCount;
924
- cur.col = col;
933
+ __internalHandleCollision(col: Collision, isTriggerCollision: boolean) {
934
+ if (isTriggerCollision) {
935
+ if (!this.onTriggerEnter && !this.onTriggerStay && !this.onTriggerExit) return;
936
+ }
937
+ else {
938
+ if (!this.onCollisionEnter && !this.onCollisionStay && !this.onCollisionExit) return;
939
+ }
940
+
941
+ const otherObject = col.gameObject;
942
+
943
+ // lazily create the maps
944
+ if (isTriggerCollision && !this._triggerCollisions) this._triggerCollisions = new Map();
945
+ else if (!this._collisions) this._collisions = new Map();
946
+
947
+ // select the correct map
948
+ const collection = isTriggerCollision ? this._triggerCollisions! : this._collisions!;
949
+
950
+ if (collection.has(otherObject)) {
951
+ const cur = collection.get(otherObject)!;
952
+ // console.log("STAY", this.name, this.context.time.frameCount)
953
+ // cur.exitFrame = undefined;
954
+ cur.frame = this.context.time.frameCount;
955
+ cur.col = col;
956
+ if (isTriggerCollision)
957
+ this.onTriggerStay?.(col);
958
+ else
925
959
  this.onCollisionStay?.call(this, col);
926
- }
927
- else {
928
- const entry = { col, frame: this.context.time.frameCount };
929
- this._collisions.set(otherObject, entry);
960
+ }
961
+ else {
962
+ // console.log("START", this.name);
963
+ const entry = { col, frame: this.context.time.frameCount };
964
+ collection.set(otherObject, entry);
965
+ if (isTriggerCollision)
966
+ this.onTriggerEnter?.(col);
967
+ else
930
968
  this.onCollisionEnter?.call(this, col);
931
- }
932
969
  }
933
970
  }
934
971
 
935
- sendExitCollisionEvent(obj : Object3D) {
936
- if(!this._collisions) return;
937
- const col = this._collisions.get(obj);
938
- if(!col) return;
939
- this.onCollisionExit?.call(this, col.col);
940
- this._collisions.delete(obj);
941
- }
942
-
943
- private __waitForCollisionExit() {
944
- if (this._collisionExitRoutine) return;
945
- // const self = this;
946
- // function* routine() {
947
- // while (self._collisions && self._collisions.size > 0) {
948
- // for (let ob of self._collisions.keys()) {
949
- // const entry = self._collisions!.get(ob)!;
950
- // if (self.context.time.frameCount - entry.frame > 1) {
951
- // self._collisions!.delete(ob);
952
- // self.onCollisionExit?.call(self, entry.col);
953
- // }
954
- // }
955
- // yield;
956
- // }
957
- // }
958
- // this._collisionExitRoutine = this.startCoroutine(routine(), FrameEvent.EarlyUpdate);
972
+ __internalHandleExitCollisionEvent(obj: Object3D, isTriggerCollision: boolean) {
973
+ if (isTriggerCollision) {
974
+ if (!this._triggerCollisions) return;
975
+ }
976
+ else {
977
+ if (!this._collisions) return;
978
+ }
979
+
980
+ const collection = isTriggerCollision ? this._triggerCollisions! : this._collisions!;
981
+
982
+ const collision = collection.get(obj);
983
+ if (!collision) return;
984
+ collision.exitFrame = this.context.time.frameCount;
985
+ // console.log("EXIT col", this.name, this.context.time.frameCount);
986
+ // if (this.onCollisionExit !== undefined)
987
+ this.__waitForCollisionExit(isTriggerCollision);
988
+ if (!isTriggerCollision)
989
+ this.onCollisionExitRaw?.call(this, collision.col);
990
+ // this._collisions.delete(obj);
991
+ }
992
+
993
+ private __waitForCollisionExit(isTriggerCollision: boolean) {
994
+ const routine = isTriggerCollision ? this._triggerExitRoutine : this._collisionExitRoutine;
995
+ if (routine) return;
996
+
997
+ const collection = isTriggerCollision ? this._triggerCollisions! : this._collisions!;
998
+ const self = this;
999
+ const frames = 10;
1000
+ function* delayedExitRoutine() {
1001
+ while (collection && collection.size > 0) {
1002
+ for (let other of collection.keys()) {
1003
+ const entry = collection!.get(other)!;
1004
+ if (entry.frame !== undefined && self.context.time.frameCount - entry.frame >= frames) {
1005
+ // console.log("EXIT real", self.name, entry.col.gameObject.name, collection);
1006
+ collection!.delete(other);
1007
+ if (isTriggerCollision)
1008
+ self.onTriggerExit?.(entry.col);
1009
+ else
1010
+ self.onCollisionExit?.call(self, entry.col);
1011
+ }
1012
+ }
1013
+ for (let i = 0; i < frames; i++)
1014
+ yield;
1015
+ }
1016
+ if (isTriggerCollision)
1017
+ self._triggerExitRoutine = undefined;
1018
+ else
1019
+ self._collisionExitRoutine = null;
1020
+ // console.log("DONE", self.name);
1021
+ }
1022
+
1023
+ const newRoutine = this.startCoroutine(delayedExitRoutine(), FrameEvent.OnAfterRender);
1024
+ if (isTriggerCollision)
1025
+ this._triggerExitRoutine = newRoutine;
1026
+ else
1027
+ this._collisionExitRoutine = newRoutine
959
1028
  }
960
1029
 
961
1030
 
@@ -1046,7 +1115,7 @@ class Component implements EventTarget {
1046
1115
  private static _forward: THREE.Vector3 = new THREE.Vector3(0, 0, 1);
1047
1116
  public get forward(): THREE.Vector3 {
1048
1117
 
1049
- return Component._forward.set(0, 0, 1).applyQuaternion(this.worldQuaternion);
1118
+ return Component._forward.set(0, 0, -1).applyQuaternion(this.worldQuaternion);
1050
1119
  }
1051
1120
 
1052
1121
 
@@ -53,9 +53,11 @@ export class OrbitControls extends Behaviour {
53
53
 
54
54
  private _inputs: number = 0;
55
55
  private _enableTime: number = 0; // use to disable double click when double clicking on UI
56
+ private _startedListeningToKeyEvents: boolean = false;
56
57
 
57
58
  awake(): void {
58
59
  this._lookTargetPosition = new THREE.Vector3();
60
+ this._startedListeningToKeyEvents = false;
59
61
  }
60
62
 
61
63
  onEnable() {
@@ -86,6 +88,10 @@ export class OrbitControls extends Behaviour {
86
88
  }
87
89
  this._controls.dampingFactor = this.dampingFactor;
88
90
  this._controls.enablePan = this.enablePan;
91
+ if (!this._startedListeningToKeyEvents) {
92
+ this._startedListeningToKeyEvents = true;
93
+ this._controls.listenToKeyEvents(window.document.body);
94
+ }
89
95
  }
90
96
  }
91
97
 
@@ -97,12 +103,16 @@ export class OrbitControls extends Behaviour {
97
103
  // this._controls.reset();
98
104
  }
99
105
  }
106
+
107
+ onDestroy(){
108
+ this._controls?.dispose();
109
+ }
100
110
 
101
111
  start() {
102
112
  if (this._controls) {
103
113
  const camGo = GameObject.getComponent(this.gameObject, Camera);
104
114
  if (camGo && !this.setFromTargetPosition()) {
105
- if(this.debugLog)
115
+ if (this.debugLog)
106
116
  console.log("NO TARGET");
107
117
  const forward = new THREE.Vector3(0, 0, -1).applyMatrix4(camGo.cam.matrixWorld);
108
118
  this.setTarget(forward, true);
@@ -3,14 +3,15 @@ import { BodyOptions } from '../engine/engine_physics'
3
3
  import * as CANNON from 'cannon-es'
4
4
  import * as THREE from 'three'
5
5
  import { getWorldPosition } from "../engine/engine_three_utils";
6
- import { onChange, RevocableProxy } from "./ui/Utils";
7
6
  import { serializeable } from "../engine/engine_serialization_decorator";
7
+ import { Watch } from "../engine/engine_utils";
8
+ import { Vector3 } from "three";
8
9
 
9
10
  export class Rigidbody extends Behaviour {
10
11
 
11
12
  @serializeable()
12
13
  mass: number = 1;
13
-
14
+
14
15
  @serializeable()
15
16
  public set isKinematic(kinematic: boolean) {
16
17
  this._isKinematic = kinematic;
@@ -47,19 +48,30 @@ export class Rigidbody extends Behaviour {
47
48
  private _smoothedVelocity!: THREE.Vector3;
48
49
  private lastWorldPosition!: THREE.Vector3;
49
50
 
50
- private dirty: boolean = true;
51
- private transformProxy? : RevocableProxy;
51
+ private _ignoreChange: boolean = false;
52
+ private _dirty: boolean = false;
53
+
54
+ private _positionWatch?: Watch;
55
+ private _matrixWatch?: Watch;
56
+ private _setMatrix: THREE.Matrix4;
57
+
58
+ constructor() {
59
+ super();
60
+ this._setMatrix = new THREE.Matrix4();
61
+ }
52
62
 
53
63
  awake() {
54
64
  this._body = null;
55
65
  this.currentVelocity = new THREE.Vector3();
56
66
  this._smoothedVelocity = new THREE.Vector3();
57
67
  this.lastWorldPosition = getWorldPosition(this.gameObject).clone();
68
+ this._matrixWatch = undefined;
69
+ this._positionWatch = undefined;
58
70
  }
59
71
 
60
- start(){
61
- this.setBodyFromGameObject();
62
- }
72
+ // start() {
73
+ // this.setBodyFromGameObject();
74
+ // }
63
75
 
64
76
  onEnable() {
65
77
  if (this._body) {
@@ -74,15 +86,18 @@ export class Rigidbody extends Behaviour {
74
86
  this.setBodyFromGameObject();
75
87
  }
76
88
 
77
- this.transformProxy = onChange(this.gameObject.matrix, "elements", () => {
78
- if (!this.context.physics.isUpdating) {
79
- this.dirty = true;
89
+ if (!this._positionWatch)
90
+ this._positionWatch = new Watch(this.gameObject.position, ["x", "y", "z"]);
91
+ this._positionWatch.subscribeWrite(_ => {
92
+ if (!this.context.physics.isUpdating && !this._ignoreChange) {
93
+ this._dirty = true;
94
+ this._setMatrix.copy(this.gameObject.matrix);
95
+ this._setMatrix.setPosition(this.gameObject.position);
80
96
  }
81
- });
97
+ })
82
98
  }
83
99
 
84
100
  onDisable(): void {
85
- this.transformProxy?.revoke();
86
101
  if (this._body)
87
102
  this.context.physics.removeBody(this.gameObject, this._body, false);
88
103
  }
@@ -92,15 +107,23 @@ export class Rigidbody extends Behaviour {
92
107
  this.context.physics.removeBody(this.gameObject, this._body);
93
108
  }
94
109
 
95
- earlyUpdate(){
96
- if(this.dirty) this.setBodyFromGameObject();
97
- }
98
110
  onBeforeRender() {
111
+ this._ignoreChange = true;
99
112
  this.updateVelocity();
113
+
114
+ if (this._dirty) {
115
+ this._dirty = false;
116
+ this._setMatrix.decompose(this.gameObject.position, this.gameObject.quaternion, this.gameObject.scale);
117
+ this.setBodyFromGameObject();
118
+ }
119
+
120
+ this._ignoreChange = false;
100
121
  }
101
122
 
102
123
  initialize() {
103
- if (this._body) return;
124
+ if (this._body) {
125
+ return;
126
+ }
104
127
  const options = new BodyOptions();
105
128
  options.drag = this.drag;
106
129
  options.angularDrag = this.angularDrag;
@@ -116,8 +139,14 @@ export class Rigidbody extends Behaviour {
116
139
  this.body?.wakeUp();
117
140
  }
118
141
 
119
- public applyForce(vec: THREE.Vector3, rel: THREE.Vector3 | undefined) {
120
- this.body?.applyForce(new CANNON.Vec3(vec.x, vec.y, vec.z), rel ? new CANNON.Vec3(rel.x, rel.y, rel.z) : undefined);
142
+ public applyForce(vec: THREE.Vector3, rel?: THREE.Vector3) {
143
+ const force = new CANNON.Vec3(vec.x, vec.y, vec.z);
144
+ force.scale(1 / this.context.time.deltaTime, force);
145
+ this.body?.applyForce(force, rel ? new CANNON.Vec3(rel.x, rel.y, rel.z) : undefined);
146
+ }
147
+
148
+ public setForce(x: number, y: number, z: number) {
149
+ this.body?.force.set(x, y, z);
121
150
  }
122
151
 
123
152
  public getVelocity(): THREE.Vector3 {
@@ -126,13 +155,39 @@ export class Rigidbody extends Behaviour {
126
155
  return Rigidbody.tempPosition.set(0, 0, 0);
127
156
  }
128
157
 
129
- public setVelocity(x: number, y: number, z: number) {
158
+ public setVelocity(x: number | Vector3, y: number, z: number) {
130
159
  if (!this.body) return;
160
+ if (x instanceof Vector3) {
161
+ const vec = x;
162
+ x = vec.x;
163
+ y = vec.y;
164
+ z = vec.z;
165
+ }
131
166
  this.body.velocity.x = x / this.context.time.deltaTime;
132
167
  this.body.velocity.y = y / this.context.time.deltaTime;
133
168
  this.body.velocity.z = z / this.context.time.deltaTime;
134
169
  }
135
170
 
171
+ public getTorque(): THREE.Vector3 {
172
+ if (this.body)
173
+ return Rigidbody.tempPosition.set(this.body?.torque.x, this.body?.torque.y, this.body?.torque.z);
174
+ return Rigidbody.tempPosition.set(0, 0, 0);
175
+ }
176
+
177
+ public setTorque(x: number | Vector3, y: number, z: number) {
178
+ if (!this.body) return;
179
+ if (x instanceof Vector3) {
180
+ const vec = x;
181
+ x = vec.x;
182
+ y = vec.y;
183
+ z = vec.z;
184
+ }
185
+ this.body.torque.x = x / this.context.time.deltaTime;
186
+ this.body.torque.y = y / this.context.time.deltaTime;
187
+ this.body.torque.z = z / this.context.time.deltaTime;
188
+
189
+ }
190
+
136
191
  public get smoothedVelocity() { return this._smoothedVelocity; }
137
192
 
138
193
  public setAngularVelocity(x: number, y: number, z: number) {
@@ -146,8 +201,8 @@ export class Rigidbody extends Behaviour {
146
201
 
147
202
 
148
203
  public setBodyFromGameObject(velocity: THREE.Vector3 | null | { x: number, y: number, z: number } = null) {
149
- this.dirty = false;
150
204
  if (this.body && this.gameObject && !this.destroyed) {
205
+ this._ignoreChange = true;
151
206
  const wp = this.worldPosition;
152
207
  this.body.position.set(wp.x, wp.y, wp.z);
153
208
  const wr = this.worldQuaternion;
@@ -161,6 +216,7 @@ export class Rigidbody extends Behaviour {
161
216
  this.body.velocity.y = sm.y;
162
217
  this.body.velocity.z = sm.z;
163
218
  }
219
+ this._ignoreChange = false;
164
220
  }
165
221
  }
166
222
 
@@ -1,5 +1,3 @@
1
- import { BufferAttribute } from "three";
2
- import { registerComponent } from "../engine/engine_components";
3
1
  import { Behaviour, GameObject } from "./Component";
4
2
  import * as utils from "../engine/engine_utils";
5
3
  const debug = utils.getParam("debugflags");
@@ -7,7 +7,7 @@ import { FrameEvent } from '../../engine/engine_setup';
7
7
  import { updateRenderSettings } from './Utils';
8
8
  import { Canvas } from './Canvas';
9
9
  import { serializeable } from '../../engine/engine_serialization_decorator';
10
- import { getParam } from '../../engine/engine_utils';
10
+ import { getParam, getPath } from '../../engine/engine_utils';
11
11
 
12
12
  const debug = getParam("debugtext");
13
13
 
@@ -441,6 +441,7 @@ export class Text extends Graphic {
441
441
  // if (!this.font.includes("-bold-italic"))
442
442
  // return this.font + "-bold-italic";
443
443
  // }
444
+ this.font = getPath(this.sourceId, this.font);
444
445
  return this.font;
445
446
  }
446
447
  }