@needle-tools/engine 5.0.5 → 5.0.6

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.
@@ -266,19 +266,28 @@ export class CursorFollow extends Behaviour {
266
266
  this.gameObject.worldPosition = newPosition;
267
267
  }
268
268
  if (this.snapToSurface) {
269
- ray.origin = _position;
270
- ray.direction = rayDirection.multiplyScalar(-1);
271
- const hits = this.context.physics.raycastFromRay(ray);
272
- if (hits?.length) {
273
- const hit = hits[0];
274
- if (this.damping > 0) {
275
- this.gameObject.worldPosition = _position.lerp(hit.point, this.context.time.deltaTime / this.damping);
276
- }
277
- else {
278
- this.gameObject.worldPosition = hit.point;
269
+ ray.origin = cameraPosition;
270
+ ray.direction = rayDirection;
271
+ const hits = this.context.physics.raycastFromRay(ray, {
272
+ testObject: obj => {
273
+ return obj !== this.gameObject && !this.gameObject.contains(obj) ? true : false;
279
274
  }
280
- if (debug) {
281
- Gizmos.DrawLine(hit.point, hit.normal.add(hit.point), 0x00FF00);
275
+ });
276
+ if (hits?.length) {
277
+ // Get the first hit that is not a child of *this* object. Because we do not want to lerp to a surface that is part of the object itself, since that would result in *this* object moving closer and closer to the camera as it tries to snap to itself
278
+ const hit = hits[0]; //.find(h => !this.gameObject.contains(h.object));
279
+ if (hit) {
280
+ if (this.damping > 0) {
281
+ this.gameObject.worldPosition = _position.lerp(hit.point, this.context.time.deltaTime / this.damping);
282
+ // this._distance = this.gameObject.worldPosition.distanceTo(cameraPosition);
283
+ }
284
+ else {
285
+ this.gameObject.worldPosition = hit.point;
286
+ // this._distance = hit.point.distanceTo(cameraPosition);
287
+ }
288
+ if (debug) {
289
+ Gizmos.DrawLine(hit.point, hit.normal.add(hit.point), 0x00FF00);
290
+ }
282
291
  }
283
292
  }
284
293
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CursorFollow.js","sourceRoot":"","sources":["../../../src/engine-components/web/CursorFollow.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAE5B,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,gDAAgD,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgFG;AACH,MAAM,OAAO,YAAa,SAAQ,SAAS;IAEvC,+BAA+B;IAC/B,MAAM,CAAU,IAAI,GAAG,cAAc,CAAC;IAEtC;;;;;;;;;;;;;;;;OAgBG;IAEH,OAAO,GAAW,CAAC,CAAC;IAEpB;;;;;;;;;;;;;;;;;;;;OAoBG;IAEH,WAAW,GAAY,IAAI,CAAC;IAE5B;;;;;;;;;;;;;;;;;;;;;OAqBG;IAEH,YAAY,GAAY,IAAI,CAAC;IAE7B;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IAEH,aAAa,GAAY,KAAK,CAAC;IAGvB,SAAS,GAAW,CAAC,CAAC,CAAC;IAE/B;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,cAAc,CAAC,QAAiB,KAAK;QACjC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACzD,OAAO;QACX,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACrG,CAAC;IAED,gBAAgB;IAChB,KAAK;QACD,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IACxB,CAAC;IAED,gBAAgB;IAChB,QAAQ;QACJ,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;QACpB,MAAM,CAAC,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAChE,CAAC;IACD,gBAAgB;IAChB,SAAS;QACL,MAAM,CAAC,mBAAmB,CAAC,aAAa,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IACnE,CAAC;IAEO,MAAM,GAAG,CAAC,CAAC;IACX,MAAM,GAAG,CAAC,CAAC;IAEX,cAAc,GAAG,CAAC,CAAe,EAAE,EAAE;QACzC,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAC9B,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;QACpB,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;QACpC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,GAAG,CAAE,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9C,CAAC,CAAA;IAGD,gBAAgB;IAChB,UAAU;QACN,8DAA8D;QAC9D,IAAI,CAAC,cAAc,EAAE,CAAC;QAEtB,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;QAChF,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;QAEhF,yEAAyE;QACzE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;QACvC,MAAM,cAAc,GAAG,MAAM,CAAC,aAAa,CAAC;QAE5C,iDAAiD;QACjD,MAAM,YAAY,GAAG,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC9D,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,SAAS,EAAE,CAAC;QAE7C,oDAAoD;QACpD,MAAM,WAAW,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACnG,IAAI,SAAS,GAAG,WAAW,CAAC;QAG5B,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;YACnB,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;YAC1C,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;YAClE,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,GAAG,CAAC;YACpC,SAAS,GAAG,GAAG,CAAC;QACpB,CAAC;aACI,CAAC;YACF,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,WAAW,CAAC;QAChD,CAAC;QAGD,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,GAAG,CAAC,MAAM,GAAG,SAAS,CAAC;YACvB,GAAG,CAAC,SAAS,GAAG,YAAY,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;YAChD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;YACtD,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;gBACf,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;gBACpB,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;oBACnB,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;gBAC1G,CAAC;qBACI,CAAC;oBACF,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,GAAG,CAAC,KAAK,CAAC;gBAC9C,CAAC;gBAED,IAAG,KAAK,EAAE,CAAC;oBACP,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC;gBACrE,CAAC;YACL,CAAC;QACL,CAAC;IAEL,CAAC;;AAlMD;IADC,YAAY,EAAE;6CACK;AAwBpB;IADC,YAAY,EAAE;iDACa;AAyB5B;IADC,YAAY,EAAE;kDACc;AA6B7B;IADC,YAAY,EAAE;mDACgB;AAwHnC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC"}
1
+ {"version":3,"file":"CursorFollow.js","sourceRoot":"","sources":["../../../src/engine-components/web/CursorFollow.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAE5B,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,gDAAgD,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgFG;AACH,MAAM,OAAO,YAAa,SAAQ,SAAS;IAEvC,+BAA+B;IAC/B,MAAM,CAAU,IAAI,GAAG,cAAc,CAAC;IAEtC;;;;;;;;;;;;;;;;OAgBG;IAEH,OAAO,GAAW,CAAC,CAAC;IAEpB;;;;;;;;;;;;;;;;;;;;OAoBG;IAEH,WAAW,GAAY,IAAI,CAAC;IAE5B;;;;;;;;;;;;;;;;;;;;;OAqBG;IAEH,YAAY,GAAY,IAAI,CAAC;IAE7B;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IAEH,aAAa,GAAY,KAAK,CAAC;IAGvB,SAAS,GAAW,CAAC,CAAC,CAAC;IAE/B;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,cAAc,CAAC,QAAiB,KAAK;QACjC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACzD,OAAO;QACX,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACrG,CAAC;IAED,gBAAgB;IAChB,KAAK;QACD,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IACxB,CAAC;IAED,gBAAgB;IAChB,QAAQ;QACJ,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;QACpB,MAAM,CAAC,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAChE,CAAC;IACD,gBAAgB;IAChB,SAAS;QACL,MAAM,CAAC,mBAAmB,CAAC,aAAa,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IACnE,CAAC;IAEO,MAAM,GAAG,CAAC,CAAC;IACX,MAAM,GAAG,CAAC,CAAC;IAEX,cAAc,GAAG,CAAC,CAAe,EAAE,EAAE;QACzC,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAC9B,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;QACpB,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;QACpC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,GAAG,CAAE,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9C,CAAC,CAAA;IAGD,gBAAgB;IAChB,UAAU;QACN,8DAA8D;QAC9D,IAAI,CAAC,cAAc,EAAE,CAAC;QAEtB,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;QAChF,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;QAEhF,yEAAyE;QACzE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;QACvC,MAAM,cAAc,GAAG,MAAM,CAAC,aAAa,CAAC;QAE5C,iDAAiD;QACjD,MAAM,YAAY,GAAG,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC9D,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,SAAS,EAAE,CAAC;QAE7C,oDAAoD;QACpD,MAAM,WAAW,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACnG,IAAI,SAAS,GAAG,WAAW,CAAC;QAG5B,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;YACnB,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;YAC1C,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;YAClE,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,GAAG,CAAC;YACpC,SAAS,GAAG,GAAG,CAAC;QACpB,CAAC;aACI,CAAC;YACF,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,WAAW,CAAC;QAChD,CAAC;QAGD,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,GAAG,CAAC,MAAM,GAAG,cAAc,CAAC;YAC5B,GAAG,CAAC,SAAS,GAAG,YAAY,CAAC;YAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,GAAG,EAAE;gBAClD,UAAU,EAAE,GAAG,CAAC,EAAE;oBACd,OAAO,GAAG,KAAK,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAA;gBACnF,CAAC;aACJ,CAAC,CAAC;YACH,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;gBACf,uPAAuP;gBACvP,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA,kDAAkD;gBACtE,IAAI,GAAG,EAAE,CAAC;oBACN,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;wBACnB,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;wBACtG,6EAA6E;oBACjF,CAAC;yBACI,CAAC;wBACF,IAAI,CAAC,UAAU,CAAC,aAAa,GAAG,GAAG,CAAC,KAAK,CAAC;wBAC1C,yDAAyD;oBAC7D,CAAC;oBACD,IAAI,KAAK,EAAE,CAAC;wBACR,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC;oBACrE,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC;IAEL,CAAC;;AA1MD;IADC,YAAY,EAAE;6CACK;AAwBpB;IADC,YAAY,EAAE;iDACa;AAyB5B;IADC,YAAY,EAAE;kDACc;AA6B7B;IADC,YAAY,EAAE;mDACgB;AAgInC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/engine",
3
- "version": "5.0.5",
3
+ "version": "5.0.6",
4
4
  "description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.",
5
5
  "main": "dist/needle-engine.min.js",
6
6
  "exports": {
@@ -12,6 +12,7 @@ import { getParam } from "../engine/engine_utils.js";
12
12
  import { NeedleXRSession } from "../engine/engine_xr.js";
13
13
  import { Avatar_POI } from "./avatar/Avatar_Brain_LookAt.js";
14
14
  import { Behaviour, GameObject } from "./Component.js";
15
+ import { EventList } from "./EventList.js";
15
16
  import { UsageMarker } from "./Interactable.js";
16
17
  import { Rigidbody } from "./RigidBody.js";
17
18
  import { SyncedTransform } from "./SyncedTransform.js";
@@ -155,7 +156,19 @@ export class DragControls extends Behaviour implements IPointerEventHandler {
155
156
  @serializable()
156
157
  public showGizmo: boolean = false;
157
158
 
158
- /**
159
+ /** Invoked once when a drag begins (after the minimum drag distance threshold is met). */
160
+ @serializable(EventList)
161
+ dragStarted: EventList = new EventList();
162
+
163
+ /** Invoked every frame while the object is being dragged. */
164
+ @serializable(EventList)
165
+ dragUpdated: EventList = new EventList();
166
+
167
+ /** Invoked once when the last pointer is released and the drag ends. */
168
+ @serializable(EventList)
169
+ dragEnded: EventList = new EventList();
170
+
171
+ /**
159
172
  * Returns the object currently being dragged by this DragControls component, if any.
160
173
  * @returns The object being dragged or null if no object is currently dragged
161
174
  */
@@ -457,6 +470,7 @@ export class DragControls extends Behaviour implements IPointerEventHandler {
457
470
  if (!object) return;
458
471
 
459
472
  this._isDragging = true;
473
+ this.dragStarted?.invoke();
460
474
 
461
475
  const sync = GameObject.getComponentInChildren(object, SyncedTransform);
462
476
  if (debug) console.log("DRAG START", sync, object);
@@ -497,9 +511,10 @@ export class DragControls extends Behaviour implements IPointerEventHandler {
497
511
  const object = this._targetObject || this.gameObject;
498
512
 
499
513
  InstancingUtil.markDirty(object);
514
+ this.dragUpdated?.invoke();
500
515
  }
501
516
 
502
- /**
517
+ /**
503
518
  * Called when the last pointer has been removed from this object.
504
519
  * Cleans up drag state and applies final velocities to rigidbodies.
505
520
  * @param evt Pointer event data for the last pointer that was lifted
@@ -507,6 +522,7 @@ export class DragControls extends Behaviour implements IPointerEventHandler {
507
522
  private onLastDragEnd(evt: PointerEventData | null) {
508
523
  if (!this || !this._isDragging) return;
509
524
  this._isDragging = false;
525
+ this.dragEnded?.invoke();
510
526
  for (const rb of this._draggingRigidbodies) {
511
527
  rb.setVelocity(rb.smoothedVelocity.multiplyScalar(this.context.time.deltaTime));
512
528
  }
@@ -758,19 +758,26 @@ export class OrbitControls extends Behaviour implements ICameraController {
758
758
 
759
759
  if (this.targetBounds) {
760
760
  // #region target bounds
761
- const targetVector = this._controls.target;
762
761
  const boundsCenter = this.targetBounds.worldPosition;
763
762
  const boundsHalfSize = getTempVector(this.targetBounds.worldScale).multiplyScalar(0.5);
764
763
  const min = getTempVector(boundsCenter).sub(boundsHalfSize);
765
764
  const max = getTempVector(boundsCenter).add(boundsHalfSize);
766
- const newTarget = getTempVector(this._controls.target).clamp(min, max);
767
- const duration = .1;
768
- if (duration <= 0) targetVector.copy(newTarget);
769
- else targetVector.lerp(newTarget, this.context.time.deltaTime / duration);
765
+
770
766
  if (this._lookTargetLerpActive) {
771
- if (duration <= 0) this._lookTargetEndPosition.copy(newTarget);
772
- else this._lookTargetEndPosition.lerp(newTarget, this.context.time.deltaTime / (duration * 5));
767
+ // During a programmatic transition (fitCamera / setLookTargetPosition with immediate: false),
768
+ // only clamp the destination. The look-target lerp (above) handles moving _controls.target
769
+ // towards the endpoint — we must not fight it by also lerping _controls.target here.
770
+ this._lookTargetEndPosition.clamp(min, max);
771
+ }
772
+ else {
773
+ // Interactive use (pan/orbit): smoothly push the target back into bounds
774
+ const targetVector = this._controls.target;
775
+ const newTarget = getTempVector(targetVector).clamp(min, max);
776
+ const duration = .1;
777
+ if (duration <= 0) targetVector.copy(newTarget);
778
+ else targetVector.lerp(newTarget, Math.min(1, this.context.time.deltaTime / duration));
773
779
  }
780
+
774
781
  if (debug) {
775
782
  Gizmos.DrawWireBox(boundsCenter, boundsHalfSize.multiplyScalar(2), 0xffaa00);
776
783
  }
@@ -847,7 +854,8 @@ export class OrbitControls extends Behaviour implements ICameraController {
847
854
  this._controls.update(this.context.time.deltaTime);
848
855
 
849
856
  if (debug) {
850
- Gizmos.DrawWireSphere(this._controls.target, 0.1, 0x00ff00);
857
+ const distance = this._controls.getDistance();
858
+ Gizmos.DrawWireSphere(this._controls.target, 0.01 * distance, 0x00ff00);
851
859
  }
852
860
  }
853
861
  }
@@ -1022,7 +1030,8 @@ export class OrbitControls extends Behaviour implements ICameraController {
1022
1030
 
1023
1031
  if (debug) {
1024
1032
  console.warn("OrbitControls: setLookTargetPosition", position, immediateOrDuration);
1025
- Gizmos.DrawWireSphere(this._lookTargetEndPosition, .2, 0xff0000, 2);
1033
+ const distance = this._controls.getDistance();
1034
+ Gizmos.DrawWireSphere(this._lookTargetEndPosition, 0.01 * distance, 0xff5500, 2);
1026
1035
  }
1027
1036
 
1028
1037
  if (immediateOrDuration === true) {
@@ -81,7 +81,24 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
81
81
  @serializable()
82
82
  supportRichText: boolean = false;
83
83
  @serializable(URL)
84
- font?: string;
84
+ set font(val: string | null) {
85
+ if (val !== this._font) {
86
+ this._font = val;
87
+ // If the font is assigned during deserialization it means the font URL MUST be resolved with the style suffix. If it's assigned at runtime by a user invocation and is absolute then we assume the user is prividing a full path to the font asset json or png
88
+ this._assignedAtRuntime = this.__didAwake;
89
+ }
90
+ }
91
+ get font(): string | null {
92
+ return this._font;
93
+ }
94
+ private _font: string | null = null;
95
+ private _assignedAtRuntime: boolean = true;
96
+
97
+
98
+ /**
99
+ * Whether to support basic rich text tags in the `text` property. Supported tags include `<b>`, `<i>`, and `<color=hex>`. For example: `Hello <b>World</b>` or `Score: <color=#ff0000>100</color>`
100
+ * @default false
101
+ */
85
102
  @serializable()
86
103
  fontStyle: FontStyle = FontStyle.Normal;
87
104
 
@@ -224,8 +241,15 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
224
241
  // fontSize /= this.canvas?.scaleFactor;
225
242
  // }
226
243
 
227
-
228
- const textOpts = {
244
+ const textOpts: {
245
+ color: Color,
246
+ fontOpacity: number,
247
+ fontSize: number,
248
+ fontKerning: string,
249
+ whiteSpace?: string,
250
+ overflow?: string,
251
+ lineHeight?: number
252
+ } = {
229
253
  color: this.color,
230
254
  fontOpacity: this.color.alpha,
231
255
  fontSize: fontSize,
@@ -486,15 +510,16 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
486
510
  * @private
487
511
  */
488
512
  private setFont(opts: ThreeMeshUIEveryOptions, fontStyle: FontStyle) {
513
+
514
+ if (!this.font) {
515
+ if(debug) console.warn("No font set for Text component, skipping font setup");
516
+ return;
517
+ }
489
518
 
490
- // @TODO : THH could be useful to uniformize font family name :
491
- // This would ease possible html/vr matching
492
- // - Arial instead of assets/arial
493
- // - Arial should stay Arial instead of arial
494
- if (!this.font) return;
495
519
  const fontName = this.font;
496
520
  const familyName = this.getFamilyNameWithCorrectSuffix(fontName, fontStyle);
497
- if (debug) console.log("Selected font family:" + familyName);
521
+
522
+ if (debug) console.log("Selected font family:" + familyName, this.font);
498
523
 
499
524
  // ensure a font family is register under this name
500
525
  let fontFamily = ThreeMeshUI.FontLibrary.getFontFamily(familyName as string);
@@ -527,9 +552,7 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
527
552
  opts.fontWeight = 400;
528
553
  }
529
554
 
530
-
531
555
  // Ensure a fontVariant is registered
532
- //@TODO: @swingingtom add type for fontWeight
533
556
  let fontVariant = fontFamily.getVariant(opts.fontWeight as any as string, opts.fontStyle);
534
557
  if (!fontVariant) {
535
558
  let jsonPath = familyName;
@@ -550,13 +573,7 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
550
573
 
551
574
  private getFamilyNameWithCorrectSuffix(familyName: string, style: FontStyle): string {
552
575
 
553
-
554
- // the URL decorator resolves the URL to absolute URLs - we need to remove the domain part since we're only interested in the path
555
- if (familyName.startsWith("https:") || familyName.startsWith("http:")) {
556
- const url = new URL(familyName);
557
- familyName = url.pathname;
558
- }
559
-
576
+ const isAbsolute = familyName.startsWith("https:") || familyName.startsWith("http:");
560
577
 
561
578
  // we can only change the style for the family if the name has a suffix (e.g. Arial-Bold)
562
579
  const styleSeparator = familyName.lastIndexOf('-');
@@ -580,8 +597,16 @@ export class Text extends Graphic implements IHasAlphaFactor, ICanvasEventReceiv
580
597
  }
581
598
  const isUpperCase = fontBaseName[0] === fontBaseName[0].toUpperCase();
582
599
  const fontNameWithoutSuffix = familyName.substring(0, styleSeparator > pathSeparatorIndex ? styleSeparator : familyName.length);
600
+
583
601
  if (debug) console.log("Select font: ", familyName, FontStyle[style], fontBaseName, isUpperCase, fontNameWithoutSuffix);
584
602
 
603
+ /**
604
+ * If a user provides a font with an absolute URL AND the font name does not end with "-msdf.json" or ".png" (e.g. "https://example.com/fonts/Arial-Bold"), we will assume that the user is providing the full path to the font files and we will not try to modify the font name based on the style. This allows users to have more control over the font files they are using, especially if they are hosting their own fonts or using a custom font provider that does not follow the same naming conventions as our default fonts.
605
+ */
606
+ if (isAbsolute && this._assignedAtRuntime && !(familyName.endsWith("-msdf.json") || familyName.endsWith(".png"))) {
607
+ return fontNameWithoutSuffix;
608
+ }
609
+
585
610
  switch (style) {
586
611
  case FontStyle.Normal:
587
612
  if (isUpperCase) return fontNameWithoutSuffix + "-Regular";
@@ -289,20 +289,28 @@ export class CursorFollow extends Behaviour {
289
289
 
290
290
 
291
291
  if (this.snapToSurface) {
292
- ray.origin = _position;
293
- ray.direction = rayDirection.multiplyScalar(-1);
294
- const hits = this.context.physics.raycastFromRay(ray);
295
- if (hits?.length) {
296
- const hit = hits[0];
297
- if (this.damping > 0) {
298
- this.gameObject.worldPosition = _position.lerp(hit.point, this.context.time.deltaTime / this.damping);
299
- }
300
- else {
301
- this.gameObject.worldPosition = hit.point;
292
+ ray.origin = cameraPosition;
293
+ ray.direction = rayDirection;
294
+ const hits = this.context.physics.raycastFromRay(ray, {
295
+ testObject: obj => {
296
+ return obj !== this.gameObject && !this.gameObject.contains(obj) ? true : false
302
297
  }
303
-
304
- if(debug) {
305
- Gizmos.DrawLine(hit.point, hit.normal!.add(hit.point), 0x00FF00);
298
+ });
299
+ if (hits?.length) {
300
+ // Get the first hit that is not a child of *this* object. Because we do not want to lerp to a surface that is part of the object itself, since that would result in *this* object moving closer and closer to the camera as it tries to snap to itself
301
+ const hit = hits[0];//.find(h => !this.gameObject.contains(h.object));
302
+ if (hit) {
303
+ if (this.damping > 0) {
304
+ this.gameObject.worldPosition = _position.lerp(hit.point, this.context.time.deltaTime / this.damping);
305
+ // this._distance = this.gameObject.worldPosition.distanceTo(cameraPosition);
306
+ }
307
+ else {
308
+ this.gameObject.worldPosition = hit.point;
309
+ // this._distance = hit.point.distanceTo(cameraPosition);
310
+ }
311
+ if (debug) {
312
+ Gizmos.DrawLine(hit.point, hit.normal!.add(hit.point), 0x00FF00);
313
+ }
306
314
  }
307
315
  }
308
316
  }