@needle-tools/engine 2.37.0-pre → 2.38.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 (82) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/needle-engine.d.ts +80 -13
  3. package/dist/needle-engine.js +349 -349
  4. package/dist/needle-engine.js.map +4 -4
  5. package/dist/needle-engine.min.js +25 -25
  6. package/dist/needle-engine.min.js.map +4 -4
  7. package/lib/engine/engine_addressables.d.ts +3 -1
  8. package/lib/engine/engine_addressables.js +12 -5
  9. package/lib/engine/engine_addressables.js.map +1 -1
  10. package/lib/engine/engine_element.js +3 -2
  11. package/lib/engine/engine_element.js.map +1 -1
  12. package/lib/engine/engine_element_overlay.js +4 -3
  13. package/lib/engine/engine_element_overlay.js.map +1 -1
  14. package/lib/engine/engine_input.d.ts +2 -0
  15. package/lib/engine/engine_input.js +14 -3
  16. package/lib/engine/engine_input.js.map +1 -1
  17. package/lib/engine/engine_physics.d.ts +2 -4
  18. package/lib/engine/engine_physics.js +75 -30
  19. package/lib/engine/engine_physics.js.map +1 -1
  20. package/lib/engine/engine_physics.types.d.ts +7 -0
  21. package/lib/engine/engine_physics.types.js +8 -0
  22. package/lib/engine/engine_physics.types.js.map +1 -1
  23. package/lib/engine/engine_setup.d.ts +7 -1
  24. package/lib/engine/engine_setup.js +10 -2
  25. package/lib/engine/engine_setup.js.map +1 -1
  26. package/lib/engine/engine_types.d.ts +1 -1
  27. package/lib/engine/engine_types.js.map +1 -1
  28. package/lib/engine-components/Animation.d.ts +1 -0
  29. package/lib/engine-components/Animation.js +7 -0
  30. package/lib/engine-components/Animation.js.map +1 -1
  31. package/lib/engine-components/AnimatorController.js +14 -7
  32. package/lib/engine-components/AnimatorController.js.map +1 -1
  33. package/lib/engine-components/Camera.d.ts +1 -0
  34. package/lib/engine-components/Camera.js +20 -5
  35. package/lib/engine-components/Camera.js.map +1 -1
  36. package/lib/engine-components/CharacterController.d.ts +31 -0
  37. package/lib/engine-components/CharacterController.js +167 -0
  38. package/lib/engine-components/CharacterController.js.map +1 -0
  39. package/lib/engine-components/Collider.d.ts +10 -4
  40. package/lib/engine-components/Collider.js +18 -8
  41. package/lib/engine-components/Collider.js.map +1 -1
  42. package/lib/engine-components/Light.d.ts +2 -0
  43. package/lib/engine-components/Light.js +13 -2
  44. package/lib/engine-components/Light.js.map +1 -1
  45. package/lib/engine-components/Renderer.js +4 -0
  46. package/lib/engine-components/Renderer.js.map +1 -1
  47. package/lib/engine-components/RigidBody.d.ts +6 -1
  48. package/lib/engine-components/RigidBody.js +62 -25
  49. package/lib/engine-components/RigidBody.js.map +1 -1
  50. package/lib/engine-components/SmoothFollow.d.ts +2 -1
  51. package/lib/engine-components/SmoothFollow.js +25 -17
  52. package/lib/engine-components/SmoothFollow.js.map +1 -1
  53. package/lib/engine-components/WebXR.js +3 -4
  54. package/lib/engine-components/WebXR.js.map +1 -1
  55. package/lib/engine-components/codegen/components.d.ts +3 -0
  56. package/lib/engine-components/codegen/components.js +3 -0
  57. package/lib/engine-components/codegen/components.js.map +1 -1
  58. package/package.json +1 -1
  59. package/src/engine/codegen/register_types.js +12 -0
  60. package/src/engine/dist/engine_physics.js +739 -0
  61. package/src/engine/dist/engine_setup.js +777 -0
  62. package/src/engine/engine_addressables.ts +18 -8
  63. package/src/engine/engine_element.ts +3 -2
  64. package/src/engine/engine_element_overlay.ts +4 -3
  65. package/src/engine/engine_input.ts +12 -3
  66. package/src/engine/engine_physics.ts +82 -47
  67. package/src/engine/engine_physics.types.ts +9 -0
  68. package/src/engine/engine_setup.ts +26 -17
  69. package/src/engine/engine_types.ts +2 -1
  70. package/src/engine-components/Animation.ts +8 -0
  71. package/src/engine-components/AnimatorController.ts +16 -11
  72. package/src/engine-components/Camera.ts +21 -4
  73. package/src/engine-components/CharacterController.ts +171 -0
  74. package/src/engine-components/Collider.ts +22 -12
  75. package/src/engine-components/Light.ts +17 -3
  76. package/src/engine-components/Renderer.ts +5 -1
  77. package/src/engine-components/RigidBody.ts +63 -29
  78. package/src/engine-components/SmoothFollow.ts +21 -18
  79. package/src/engine-components/WebXR.ts +3 -4
  80. package/src/engine-components/codegen/components.ts +3 -0
  81. package/src/engine-components/dist/CharacterController.js +123 -0
  82. package/src/engine-components/dist/RigidBody.js +458 -0
@@ -7,6 +7,7 @@ import { RGBAColor } from "./js-extensions/RGBAColor";
7
7
  import { PerspectiveCamera } from "three";
8
8
  import { XRSessionMode } from "../engine/engine_setup";
9
9
  import { ICamera } from "../engine/engine_types"
10
+ import { showBalloonMessage } from "../engine/debug/debug";
10
11
 
11
12
  export enum ClearFlags {
12
13
  Skybox = 1,
@@ -127,6 +128,10 @@ export class Camera extends Behaviour implements ICamera {
127
128
  this.applyClearFlagsIfIsActiveCamera();
128
129
  }
129
130
 
131
+ onDisable() {
132
+ this.context.removeCamera(this);
133
+ }
134
+
130
135
  buildCamera() {
131
136
  if (this._cam) return;
132
137
 
@@ -168,6 +173,8 @@ export class Camera extends Behaviour implements ICamera {
168
173
  }
169
174
 
170
175
  applyClearFlagsIfIsActiveCamera() {
176
+ if (debug)
177
+ showBalloonMessage("apply Camera clear flags");
171
178
  if (this._cam && this.context.mainCameraComponent === this) {
172
179
  switch (this._clearFlags) {
173
180
  case ClearFlags.Skybox:
@@ -203,12 +210,22 @@ export class Camera extends Behaviour implements ICamera {
203
210
  const session = this.context.renderer.xr?.getSession();
204
211
  if (!session) return false;
205
212
  const environmentBlendMode = session.environmentBlendMode;
213
+ if (debug)
214
+ showBalloonMessage("Environment blend mode: " + environmentBlendMode + " on " + navigator.userAgent);
206
215
  const transparent = environmentBlendMode === 'additive' || environmentBlendMode === 'alpha-blend';
207
- // workaround for Quest 2 returning opaque when it should be alpha-blend
208
- // check user agent if this is the Quest browser and return true if so
209
216
 
210
- if (environmentBlendMode === "opaque" && navigator.userAgent?.includes("OculusBrowser")) {
211
- if (this.context.xrSessionMode === XRSessionMode.ImmersiveAR) return true;
217
+ if (this.context.xrSessionMode === XRSessionMode.ImmersiveAR) {
218
+ if (environmentBlendMode === "opaque") {
219
+ // workaround for Quest 2 returning opaque when it should be alpha-blend
220
+ // check user agent if this is the Quest browser and return true if so
221
+ if (navigator.userAgent?.includes("OculusBrowser")) {
222
+ return true;
223
+ }
224
+ // Mozilla WebXR Viewer
225
+ else if (navigator.userAgent?.includes("Mozilla") && navigator.userAgent?.includes("Mobile WebXRViewer/v2")) {
226
+ return true;
227
+ }
228
+ }
212
229
  }
213
230
  return transparent;
214
231
  }
@@ -0,0 +1,171 @@
1
+ import { Ray, Vector3 } from "three";
2
+ import { Mathf } from "../engine/engine_math";
3
+ import { serializeable } from "../engine/engine_serialization";
4
+ import { Collision } from "../engine/engine_types";
5
+ import { CapsuleCollider } from "./Collider";
6
+ import { Behaviour, GameObject } from "./Component";
7
+ import { Rigidbody } from "./RigidBody";
8
+ import { Animator } from "./Animator"
9
+ import { RaycastOptions } from "../engine/engine_physics";
10
+ import { getWorldPosition } from "../engine/engine_three_utils";
11
+
12
+ export class CharacterController extends Behaviour {
13
+
14
+ @serializeable(Vector3)
15
+ center: Vector3 = new Vector3(0, 0, 0);
16
+ @serializeable()
17
+ radius: number = .5;
18
+ @serializeable()
19
+ height: number = 2;
20
+
21
+ private _rigidbody: Rigidbody | null = null;
22
+ get rigidbody(): Rigidbody {
23
+ if (this._rigidbody) return this._rigidbody;
24
+ this._rigidbody = this.gameObject.getComponent(Rigidbody);
25
+ if (!this._rigidbody)
26
+ this._rigidbody = this.gameObject.addNewComponent(Rigidbody) as Rigidbody;
27
+ return this.rigidbody;
28
+ }
29
+
30
+ onEnable() {
31
+ let rb = this.rigidbody;
32
+ let collider = this.gameObject.getComponent(CapsuleCollider);
33
+ if (!collider)
34
+ collider = this.gameObject.addNewComponent(CapsuleCollider) as CapsuleCollider;
35
+ // rb.isKinematic = true;
36
+ collider.center.copy(this.center);
37
+ collider.radius = this.radius;
38
+ collider.height = this.height;
39
+ this.gameObject.rotation.x = 0;
40
+ this.gameObject.rotation.z = 0;
41
+ rb.lockRotationX = true;
42
+ rb.lockRotationY = true;
43
+ rb.lockRotationZ = true;
44
+
45
+ // TODO: this doesnt work yet
46
+ // setInterval(()=>{
47
+ // this.rigidbody.isKinematic = !this.rigidbody.isKinematic;
48
+ // console.log(this.rigidbody.isKinematic);
49
+ // }, 1000)
50
+ }
51
+
52
+ move(vec: Vector3) {
53
+ this.gameObject.position.add(vec);
54
+ }
55
+
56
+ private _activeGroundCollisions: Set<Collision> = new Set();
57
+
58
+ onCollisionEnter(col: Collision) {
59
+ for (const contact of col.contacts) {
60
+ // console.log(contact.normal);
61
+ if (contact.normal.y > .1) {
62
+ this._activeGroundCollisions.add(col);
63
+ break;
64
+ }
65
+ }
66
+ }
67
+
68
+ onCollisionExit(col: Collision) {
69
+ this._activeGroundCollisions.delete(col);
70
+ }
71
+
72
+ get isGrounded(): boolean { return this._activeGroundCollisions.size > 0; }
73
+ }
74
+
75
+ export class CharacterControllerInput extends Behaviour {
76
+
77
+ @serializeable(CharacterController)
78
+ controller?: CharacterController;
79
+
80
+ @serializeable()
81
+ movementSpeed: number = 2;
82
+
83
+ @serializeable()
84
+ rotationSpeed: number = 2;
85
+
86
+ @serializeable()
87
+ jumpForce: number = 1;
88
+
89
+ @serializeable(Animator)
90
+ animator?: Animator;
91
+
92
+ private _currentSpeed: Vector3 = new Vector3(0, 0, 0);
93
+ private _currentAngularSpeed: Vector3 = new Vector3(0, 0, 0);
94
+
95
+ private _temp: Vector3 = new Vector3(0, 0, 0);
96
+ private _jumpCount: number = 0;
97
+
98
+ update() {
99
+
100
+ if (this.controller?.isGrounded) {
101
+ this._jumpCount = 0;
102
+ this.animator?.SetBool("doubleJump", false);
103
+ }
104
+
105
+ const forward = this.context.input.isKeyPressed("w");
106
+ const backward = this.context.input.isKeyPressed("s");
107
+ const rotateLeft = this.context.input.isKeyPressed("a");
108
+ const rotateRight = this.context.input.isKeyPressed("d");
109
+ const jump = this.context.input.isKeyDown(" ");
110
+ // if (jumpDown) this._jumpDownTime = this.context.time.time;
111
+ // const jumpUp = this.context.input.isKeyUp(" ");
112
+
113
+ const step = forward ? 1 : 0 + backward ? -1 : 0;
114
+ this._currentSpeed.z += step * this.movementSpeed * this.context.time.deltaTime;
115
+
116
+ // if (!this.controller || this.controller.isGrounded)
117
+ this.animator?.SetBool("running", step != 0);
118
+ this.animator?.SetBool("jumping", this.controller?.isGrounded === true && jump);
119
+
120
+ this._temp.copy(this._currentSpeed);
121
+ this._temp.applyQuaternion(this.gameObject.quaternion);
122
+ if (this.controller) this.controller.move(this._temp);
123
+ else this.gameObject.position.add(this._temp);
124
+
125
+ const rotation = rotateLeft ? 1 : 0 + rotateRight ? -1 : 0;
126
+ this._currentAngularSpeed.y += Mathf.toRadians(rotation * this.rotationSpeed) * this.context.time.deltaTime;
127
+ this.gameObject.rotateY(this._currentAngularSpeed.y);
128
+
129
+
130
+ this._currentSpeed.multiplyScalar(1 - this.context.time.deltaTime * 10);
131
+ this._currentAngularSpeed.y *= 1 - this.context.time.deltaTime * 10;
132
+
133
+ if (this.controller && jump && this.jumpForce > 0) {
134
+ let canJump = this.controller?.isGrounded;
135
+ if (!this.controller?.isGrounded && this._jumpCount === 1) {
136
+ canJump = true;
137
+ this.animator?.SetBool("doubleJump", true);
138
+ }
139
+
140
+ if (canJump) {
141
+ this._jumpCount += 1;
142
+ // TODO: factor in mass
143
+ const rb = this.controller.rigidbody;
144
+ // const fullJumpHoldLength = .1;
145
+ const factor = this._jumpCount === 2 ? 2 : 1;// Mathf.clamp((this.context.time.time - this._jumpDownTime), 0, fullJumpHoldLength) / fullJumpHoldLength;
146
+ rb.applyImpulse(new Vector3(0, 1, 0).multiplyScalar(this.jumpForce * factor));
147
+ }
148
+ }
149
+
150
+ if (this.controller) {
151
+ // TODO: should probably raycast to the ground or check if we're still in the jump animation
152
+ const verticalSpeed = this.controller?.rigidbody.getVelocity().y;
153
+ if (verticalSpeed < -1) {
154
+ if (!this._raycastOptions.ray) this._raycastOptions.ray = new Ray();
155
+ this._raycastOptions.ray.origin.copy(getWorldPosition(this.gameObject));
156
+ this._raycastOptions.ray.direction.set(0, -1, 0);
157
+ const currentLayer = this.layer;
158
+ this.gameObject.layers.disableAll();
159
+ this.gameObject.layers.set(2);
160
+ const hits = this.context.physics.raycast(this._raycastOptions);
161
+ this.gameObject.layers.set(currentLayer);
162
+ if ((hits.length && hits[0].distance > 2 || verticalSpeed < -10)) {
163
+ this.animator?.SetBool("falling", true);
164
+ }
165
+ }
166
+ else this.animator?.SetBool("falling", false);
167
+ }
168
+ }
169
+
170
+ private _raycastOptions = new RaycastOptions();
171
+ }
@@ -2,16 +2,9 @@ import { Behaviour } from "./Component";
2
2
  import { Rigidbody } from "./RigidBody";
3
3
  import { serializeable } from "../engine/engine_serialization_decorator";
4
4
  import { Event, Mesh, Object3D, Vector3 } from "three"
5
- import { IColliderProvider, registerColliderProvider } from "../engine/engine_physics";
5
+ // import { IColliderProvider, registerColliderProvider } from "../engine/engine_physics";
6
6
  import { ICollider } from "../engine/engine_types";
7
- import { getComponentInChildren } from "../engine/engine_components";
8
7
 
9
- class ColliderProvider implements IColliderProvider {
10
- getCollider(obj: Object3D): ICollider {
11
- return getComponentInChildren<Collider>(obj, Collider);
12
- }
13
- }
14
- registerColliderProvider(new ColliderProvider());
15
8
 
16
9
  export class Collider extends Behaviour implements ICollider {
17
10
 
@@ -54,7 +47,7 @@ export class SphereCollider extends Collider {
54
47
  @serializeable()
55
48
  radius: number = .5;
56
49
  @serializeable(Vector3)
57
- center: THREE.Vector3 = new Vector3(0, 0, 0);
50
+ center: Vector3 = new Vector3(0, 0, 0);
58
51
 
59
52
  onEnable() {
60
53
  super.onEnable();
@@ -65,9 +58,9 @@ export class SphereCollider extends Collider {
65
58
  export class BoxCollider extends Collider {
66
59
 
67
60
  @serializeable(Vector3)
68
- size: THREE.Vector3 = new Vector3(1, 1, 1);
61
+ size: Vector3 = new Vector3(1, 1, 1);
69
62
  @serializeable(Vector3)
70
- center: THREE.Vector3 = new Vector3(0, 0, 0);
63
+ center: Vector3 = new Vector3(0, 0, 0);
71
64
 
72
65
  onEnable() {
73
66
  super.onEnable();
@@ -83,7 +76,7 @@ export class MeshCollider extends Collider {
83
76
  @serializeable()
84
77
  convex: boolean = false;
85
78
 
86
- awake(){
79
+ awake() {
87
80
  console.log(this);
88
81
  }
89
82
 
@@ -98,4 +91,21 @@ export class MeshCollider extends Collider {
98
91
  if (this.sharedMesh?.isMesh)
99
92
  this.context.physics.addMeshCollider(this, this.sharedMesh, this.convex);
100
93
  }
94
+ }
95
+
96
+
97
+ export class CapsuleCollider extends Collider {
98
+ @serializeable(Vector3)
99
+ center: Vector3 = new Vector3(0, 0, 0);
100
+
101
+ @serializeable()
102
+ radius: number = .5;
103
+ @serializeable()
104
+ height: number = 2;
105
+
106
+ onEnable() {
107
+ super.onEnable();
108
+ this.context.physics.addCapsuleCollider(this, this.center, this.height, this.radius);
109
+ }
110
+
101
111
  }
@@ -130,6 +130,9 @@ export class Light extends Behaviour implements ILight {
130
130
  get shadowNormalBias(): number { return this._shadowNormalBias; }
131
131
  private _shadowNormalBias: number = 1;
132
132
 
133
+ /** when enabled this will remove the multiplication when setting the shadow bias settings initially */
134
+ private _overrideShadowBiasSettings: boolean = false;
135
+
133
136
  @serializeable()
134
137
  set shadows(val: LightShadows) {
135
138
  this._shadows = val;
@@ -338,7 +341,7 @@ export class Light extends Behaviour implements ILight {
338
341
 
339
342
  if (this.light !== undefined) {
340
343
  this._intensity = this.light.intensity;
341
-
344
+
342
345
  if (this.shadows !== LightShadows.None) {
343
346
  this.light.castShadow = true;
344
347
  }
@@ -353,8 +356,19 @@ export class Light extends Behaviour implements ILight {
353
356
  this.light.shadow.mapSize.width = 2048;
354
357
  this.light.shadow.mapSize.height = 2048;
355
358
  }
356
- this.light.shadow.bias = this.shadowBias * 0.00001;
357
- this.light.shadow.normalBias = this.shadowNormalBias * .0001;
359
+
360
+ if (debug)
361
+ console.log("Override shadow bias?", this._overrideShadowBiasSettings, this.shadowBias, this.shadowNormalBias);
362
+
363
+ // shadow bias settings
364
+ if (this._overrideShadowBiasSettings === true) {
365
+ this.light.shadow.bias = this.shadowBias;
366
+ this.light.shadow.normalBias = this.shadowNormalBias;
367
+ }
368
+ else {
369
+ this.light.shadow.bias = this.shadowBias * 0.00001;
370
+ this.light.shadow.normalBias = this.shadowNormalBias * .0001;
371
+ }
358
372
 
359
373
  // console.log(this.light.shadow, this);
360
374
  const cam = this.light.shadow.camera as THREE.OrthographicCamera;
@@ -5,7 +5,7 @@ import { RendererLightmap } from "./RendererLightmap";
5
5
  import { Context } from "../engine/engine_setup";
6
6
  import { getParam } from "../engine/engine_utils";
7
7
  import { serializeable } from "../engine/engine_serialization_decorator";
8
- import { AxesHelper, Material, Mesh, Object3D, Texture, Vector4 } from "three";
8
+ import { AxesHelper, Material, Mesh, Object3D, SkinnedMesh, Texture, Vector4 } from "three";
9
9
  import { NEEDLE_render_objects } from "../engine/extensions/NEEDLE_render_objects";
10
10
  import { NEEDLE_deferred_texture } from "../engine/extensions/NEEDLE_deferred_texture";
11
11
  import { NEED_UPDATE_INSTANCE_KEY } from "../engine/engine_instancing";
@@ -67,6 +67,7 @@ class SharedMaterialArray implements ISharedMaterials {
67
67
  case "Group":
68
68
  this._targets = [...go.children];
69
69
  break;
70
+ case "SkinnedMesh":
70
71
  case "Mesh":
71
72
  this._targets.push(go);
72
73
  break;
@@ -155,6 +156,9 @@ export class Renderer extends Behaviour implements IRenderer {
155
156
  if (this.gameObject.type === "Mesh") {
156
157
  return this.gameObject as unknown as Mesh
157
158
  }
159
+ else if (this.gameObject.type === "SkinnesMesh") {
160
+ return this.gameObject as unknown as SkinnedMesh;
161
+ }
158
162
  else if (this.gameObject.type === "Group") {
159
163
  return this.gameObject.children[0] as unknown as Mesh;
160
164
  }
@@ -7,7 +7,7 @@ import { Object3D, Vector3 } from "three";
7
7
  import { IRigidbody } from "../engine/engine_types";
8
8
  import { CollisionDetectionMode, RigidbodyConstraints } from "../engine/engine_physics.types";
9
9
  import { validate } from "../engine/engine_util_decorator";
10
- import { Context } from "../engine/engine_setup";
10
+ import { Context, FrameEvent } from "../engine/engine_setup";
11
11
 
12
12
  class TransformWatch {
13
13
 
@@ -16,23 +16,48 @@ class TransformWatch {
16
16
  }
17
17
  positionChanged: boolean = false;
18
18
  rotationChanged: boolean = false;
19
- position?: Vector3;
20
- quaternion?: THREE.Quaternion;
21
19
 
22
- reset() {
20
+ position?: { x?: number, y?: number, z?: number };
21
+ quaternion?: { _x?: number, _y?: number, _z?: number, _w?: number };
22
+
23
+ private _positionKeys: string[] = ["x", "y", "z"];
24
+ private _quaternionKeys: string[] = ["_x", "_y", "_z", "_w"];
25
+
26
+ reset(clearPreviousValues: boolean = false) {
23
27
  this.positionChanged = false;
24
28
  this.rotationChanged = false;
25
29
  this.mute = false;
30
+
31
+ if (clearPreviousValues) {
32
+ if (this.position)
33
+ for (const key of this._positionKeys)
34
+ delete this.position[key];
35
+ if (this.quaternion)
36
+ for (const key of this._quaternionKeys)
37
+ delete this.quaternion[key];
38
+ }
26
39
  }
27
40
 
28
41
  mute: boolean = false;
29
42
 
30
43
  applyValues() {
31
- if (this.positionChanged) {
32
- this.obj.position.copy(this.position!)
44
+ // only apply the values that actually changed
45
+ // since we want to still control all the other values via physics
46
+ if (this.positionChanged && this.position) {
47
+ for (const key of this._positionKeys) {
48
+ const val = this.position[key];
49
+ if (val !== undefined)
50
+ this.obj.position[key] = val;
51
+ }
33
52
  }
34
53
  if (this.rotationChanged) {
35
- this.obj.quaternion.copy(this.quaternion!)
54
+ if (this.quaternion) {
55
+ for (const key of this._quaternionKeys) {
56
+ const val = this.quaternion[key];
57
+ if (val !== undefined)
58
+ this.obj.quaternion[key] = val;
59
+ }
60
+ }
36
61
  }
37
62
  }
38
63
 
@@ -52,26 +77,28 @@ class TransformWatch {
52
77
  if (!this._positionWatch)
53
78
  this._positionWatch = new Watch(this.obj.position, ["x", "y", "z"]);
54
79
  this._positionWatch.apply();
55
- this.position = this.obj.position.clone();
80
+ this.position = {};
81
+ // this.position = this.obj.position.clone();
56
82
  this._positionWatch.subscribeWrite((val, prop) => {
57
83
  if (this.context.physics.isUpdating || this.mute) return;
58
- // if (this.position![prop] !== val) {
84
+ const prev = this.position![prop];
85
+ if (Math.abs(prev - val) < .00001) return;
59
86
  this.position![prop] = val;
60
87
  this.positionChanged = true;
61
- // }
62
88
  })
63
89
  }
64
90
  if (rotation) {
65
91
  if (!this._rotationWatch)
66
92
  this._rotationWatch = new Watch(this.obj.quaternion, ["_x", "_y", "_z", "_w"]);
67
93
  this._rotationWatch.apply();
68
- this.quaternion = this.obj.quaternion.clone();
94
+ this.quaternion = {};
95
+ // this.quaternion = this.obj.quaternion.clone();
69
96
  this._rotationWatch.subscribeWrite((val, prop) => {
70
97
  if (this.context.physics.isUpdating || this.mute) return;
71
- // if (this.quaternion![prop] !== val) {
98
+ const prev = this.quaternion![prop];
99
+ if (Math.abs(prev - val) < .00001) return;
72
100
  this.quaternion![prop] = val;
73
101
  this.rotationChanged = true;
74
- // }
75
102
  })
76
103
  }
77
104
  }
@@ -188,6 +215,7 @@ export class Rigidbody extends Behaviour implements IRigidbody {
188
215
  this._watch = new TransformWatch(this.gameObject, this.context);
189
216
  }
190
217
  this._watch.start(true, true);
218
+ this.startCoroutine(this.beforePhysics(), FrameEvent.LateUpdate);
191
219
  }
192
220
 
193
221
  onDisable() {
@@ -203,29 +231,35 @@ export class Rigidbody extends Behaviour implements IRigidbody {
203
231
  this._propertiesChanged = true;
204
232
  }
205
233
 
206
- earlyUpdate() {
207
- if (this._propertiesChanged) {
208
- this._propertiesChanged = false;
209
- this.context.physics.updateProperties(this);
234
+ // need to do this right before updating physics to prevent rendered object glitching through physical bodies
235
+ *beforePhysics() {
236
+ while (true) {
237
+ if (this._propertiesChanged) {
238
+ this._propertiesChanged = false;
239
+ this.context.physics.updateProperties(this);
240
+ }
241
+ if (this._watch?.isDirty) {
242
+ this._watch.mute = true;
243
+ this._watch.applyValues();
244
+ this.context.physics.updateBody(this, this._watch.positionChanged, this._watch.rotationChanged);
245
+ this._watch.reset();
246
+ }
247
+ this.captureVelocity();
248
+ yield;
210
249
  }
211
- if (this._watch?.isDirty) {
212
- this._watch.mute = true;
213
- this._watch.applyValues();
214
- this.context.physics.updateBody(this, this._watch.positionChanged, this._watch.rotationChanged);
215
- this._watch.reset();
216
- }
217
-
218
- this.captureVelocity();
219
250
  }
220
251
 
221
252
  private get body() {
222
253
  return this.context.physics.internal_getRigidbody(this);
223
254
  }
224
255
 
225
- // TODO: we need to differentiate between setting a physics body to a new position and moving it where we also want to affect the velocity
226
- // public teleport(){
227
- // this.context.physics.updateBody(this);
228
- // }
256
+ public teleport(pt: { x: number, y: number, z: number }, localspace:boolean = true) {
257
+ this._watch?.reset(true);
258
+ if(localspace) this.gameObject.position.set(pt.x, pt.y, pt.z);
259
+ else this.setWorldPosition(pt.x, pt.y, pt.z);
260
+ this.resetForcesAndTorques();
261
+ this.resetVelocities();
262
+ }
229
263
 
230
264
  public resetForces() {
231
265
  this.body?.resetForces(true);
@@ -1,53 +1,56 @@
1
1
  import { Camera } from "./Camera";
2
2
  import { Behaviour, GameObject } from "./Component";
3
- import * as utils from "../engine/engine_three_utils";
4
3
  import * as THREE from "three";
5
4
  import { Mathf } from "../engine/engine_math";
6
5
  import { serializeable } from "../engine/engine_serialization_decorator";
7
6
  import { Object3D } from "three";
7
+ import { getWorldPosition, getWorldQuaternion } from "../engine/engine_three_utils";
8
+ import { Axes } from "../engine/engine_physics.types";
8
9
 
9
10
  export class SmoothFollow extends Behaviour {
10
11
 
11
12
  @serializeable(Object3D)
12
13
  target: THREE.Object3D | null = null;
13
14
 
15
+ @serializeable()
14
16
  followFactor = .1;
17
+ @serializeable()
15
18
  rotateFactor = .1;
16
19
 
17
- flipForward:boolean = false;
20
+ @serializeable()
21
+ positionAxes : Axes = Axes.All;
18
22
 
19
- private static _invertForward : THREE.Quaternion = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
20
- private _firstUpdate = true;
23
+ // @serializeable()
24
+ // rotationAxes : Axes = Axes.All;
21
25
 
26
+ flipForward: boolean = false;
27
+
28
+ private static _invertForward: THREE.Quaternion = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
29
+ private _firstUpdate = true;
22
30
 
23
- onEnable(): void {
24
- const cam = GameObject.getComponentInChildren(this.gameObject, Camera) as Camera;
25
- if (cam && cam.cam) {
26
- cam.gameObject.position.set(0, 0, 0);
27
- cam.cam.position.set(0, 0, 0);
28
- cam.gameObject.quaternion.identity();
29
- cam.cam.quaternion.setFromEuler(new THREE.Euler(0, Math.PI, 0));
30
- }
31
- }
32
31
 
33
32
  onBeforeRender(): void {
34
- if (this.followFactor <= 0) return;
35
33
  this.updateNow(false);
36
34
  }
37
35
 
38
36
  updateNow(hard: boolean) {
39
37
  if (!this.target || this.target === this.gameObject) return;
40
38
  if (this.followFactor > 0) {
41
- const wp = utils.getWorldPosition(this.target);
39
+ const wp = getWorldPosition(this.target);
42
40
  const fpos = this._firstUpdate || hard ? 1 : Mathf.clamp01(this.context.time.deltaTime * this.followFactor);
43
- this.worldPosition = this.worldPosition.lerp(wp, fpos);
41
+ const currentPosition = this.worldPosition;
42
+ if(this.positionAxes & Axes.X) currentPosition.x = Mathf.lerp(currentPosition.x, wp.x, fpos);
43
+ if(this.positionAxes & Axes.Y) currentPosition.y = Mathf.lerp(currentPosition.y, wp.y, fpos);
44
+ if(this.positionAxes & Axes.Z) currentPosition.z = Mathf.lerp(currentPosition.z, wp.z, fpos);
45
+ this.worldPosition = currentPosition;
44
46
  }
45
47
  if (this.rotateFactor > 0) {
46
- const wr = utils.getWorldQuaternion(this.target);
47
- if(this.flipForward){
48
+ const wr = getWorldQuaternion(this.target);
49
+ if (this.flipForward) {
48
50
  wr.premultiply(SmoothFollow._invertForward);
49
51
  }
50
52
  const frot = this._firstUpdate || hard ? 1 : Mathf.clamp01(this.context.time.deltaTime * this.rotateFactor);
53
+
51
54
  this.worldQuaternion = this.worldQuaternion.slerp(wr, frot);
52
55
  }
53
56
  this._firstUpdate = false;
@@ -505,6 +505,7 @@ export class WebAR {
505
505
  const context = this.webxr.context;
506
506
  this.reticleActive = true;
507
507
  this.didPlaceARSessionRoot = false;
508
+ this.getAROverlayContainer();
508
509
 
509
510
  const deviceType = navigator.userAgent?.includes("OculusBrowser") ? ControllerType.PhysicalDevice : ControllerType.Touch;
510
511
  const controllerCount = deviceType === ControllerType.Touch ? 4 : 2;
@@ -563,10 +564,8 @@ export class WebAR {
563
564
  }
564
565
  else console.warn("No WebARSessionRoot found in scene")
565
566
 
566
- if (this.arDomOverlay && this.arOverlayElement) {
567
- const el = this.arOverlayElement as INeedleEngineComponent;
568
- el.onEnterAR?.call(el, session, this.arOverlayElement);
569
- }
567
+ const eng = this.context.domElement as INeedleEngineComponent;
568
+ eng?.onEnterAR?.call(eng, session, this.arOverlayElement!);
570
569
 
571
570
  this.context.mainCameraComponent?.applyClearFlagsIfIsActiveCamera();
572
571
  }
@@ -13,10 +13,13 @@ export { AxesHelper } from "../AxesHelper";
13
13
  export { BasicIKConstraint } from "../BasicIKConstraint";
14
14
  export { BoxHelperComponent } from "../BoxHelperComponent";
15
15
  export { Camera } from "../Camera";
16
+ export { CharacterController } from "../CharacterController";
17
+ export { CharacterControllerInput } from "../CharacterController";
16
18
  export { Collider } from "../Collider";
17
19
  export { SphereCollider } from "../Collider";
18
20
  export { BoxCollider } from "../Collider";
19
21
  export { MeshCollider } from "../Collider";
22
+ export { CapsuleCollider } from "../Collider";
20
23
  export { DeleteBox } from "../DeleteBox";
21
24
  export { Deletable } from "../DeleteBox";
22
25
  export { DeviceFlag } from "../DeviceFlag";