@needle-tools/engine 3.2.14-alpha → 3.3.0-alpha

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 (101) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/needle-engine.js +42139 -35749
  3. package/dist/needle-engine.min.js +694 -516
  4. package/dist/needle-engine.umd.cjs +696 -518
  5. package/lib/engine/codegen/register_types.js +4 -2
  6. package/lib/engine/codegen/register_types.js.map +1 -1
  7. package/lib/engine/engine_addressables.d.ts +3 -3
  8. package/lib/engine/engine_addressables.js +30 -9
  9. package/lib/engine/engine_addressables.js.map +1 -1
  10. package/lib/engine/engine_element.js +1 -1
  11. package/lib/engine/engine_element.js.map +1 -1
  12. package/lib/engine/engine_gameobject.d.ts +2 -1
  13. package/lib/engine/engine_gameobject.js +17 -0
  14. package/lib/engine/engine_gameobject.js.map +1 -1
  15. package/lib/engine/engine_input.js +10 -0
  16. package/lib/engine/engine_input.js.map +1 -1
  17. package/lib/engine/engine_license.js +11 -24
  18. package/lib/engine/engine_license.js.map +1 -1
  19. package/lib/engine/engine_math.d.ts +4 -0
  20. package/lib/engine/engine_math.js +6 -0
  21. package/lib/engine/engine_math.js.map +1 -1
  22. package/lib/engine-components/AnimatorController.js +7 -2
  23. package/lib/engine-components/AnimatorController.js.map +1 -1
  24. package/lib/engine-components/OrbitControls.js +13 -4
  25. package/lib/engine-components/OrbitControls.js.map +1 -1
  26. package/lib/engine-components/SceneSwitcher.d.ts +1 -1
  27. package/lib/engine-components/SceneSwitcher.js +25 -1
  28. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  29. package/lib/engine-components/TransformGizmo.d.ts +8 -4
  30. package/lib/engine-components/TransformGizmo.js +62 -63
  31. package/lib/engine-components/TransformGizmo.js.map +1 -1
  32. package/lib/engine-components/codegen/components.d.ts +2 -1
  33. package/lib/engine-components/codegen/components.js +2 -1
  34. package/lib/engine-components/codegen/components.js.map +1 -1
  35. package/lib/engine-components/export/usdz/USDZExporter.d.ts +1 -0
  36. package/lib/engine-components/export/usdz/USDZExporter.js +10 -2
  37. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  38. package/lib/engine-components/ui/Button.js +9 -5
  39. package/lib/engine-components/ui/Button.js.map +1 -1
  40. package/lib/engine-components/ui/Canvas.d.ts +13 -6
  41. package/lib/engine-components/ui/Canvas.js +101 -37
  42. package/lib/engine-components/ui/Canvas.js.map +1 -1
  43. package/lib/engine-components/ui/EventSystem.d.ts +6 -0
  44. package/lib/engine-components/ui/EventSystem.js +4 -4
  45. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  46. package/lib/engine-components/ui/Graphic.d.ts +5 -2
  47. package/lib/engine-components/ui/Graphic.js +38 -7
  48. package/lib/engine-components/ui/Graphic.js.map +1 -1
  49. package/lib/engine-components/ui/Image.d.ts +1 -0
  50. package/lib/engine-components/ui/Image.js +10 -1
  51. package/lib/engine-components/ui/Image.js.map +1 -1
  52. package/lib/engine-components/ui/InputField.d.ts +1 -0
  53. package/lib/engine-components/ui/InputField.js +8 -0
  54. package/lib/engine-components/ui/InputField.js.map +1 -1
  55. package/lib/engine-components/ui/Interfaces.d.ts +8 -0
  56. package/lib/engine-components/ui/Outline.d.ts +7 -0
  57. package/lib/engine-components/ui/Outline.js +21 -0
  58. package/lib/engine-components/ui/Outline.js.map +1 -0
  59. package/lib/engine-components/ui/RectTransform.d.ts +29 -11
  60. package/lib/engine-components/ui/RectTransform.js +178 -46
  61. package/lib/engine-components/ui/RectTransform.js.map +1 -1
  62. package/lib/engine-components/ui/Text.d.ts +13 -10
  63. package/lib/engine-components/ui/Text.js +177 -246
  64. package/lib/engine-components/ui/Text.js.map +1 -1
  65. package/lib/engine-components/utils/LookAt.d.ts +7 -0
  66. package/lib/engine-components/utils/LookAt.js +29 -0
  67. package/lib/engine-components/utils/LookAt.js.map +1 -0
  68. package/lib/engine-components/webxr/WebXRImageTracking.d.ts +8 -0
  69. package/lib/engine-components/webxr/WebXRImageTracking.js +79 -3
  70. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  71. package/lib/tsconfig.tsbuildinfo +1 -1
  72. package/package.json +2 -2
  73. package/src/engine/codegen/register_types.js +4 -2
  74. package/src/engine/engine_addressables.ts +28 -10
  75. package/src/engine/engine_element.ts +1 -1
  76. package/src/engine/engine_gameobject.ts +18 -1
  77. package/src/engine/engine_input.ts +11 -0
  78. package/src/engine/engine_license.ts +12 -25
  79. package/src/engine/engine_math.ts +10 -0
  80. package/src/engine-components/AnimatorController.ts +7 -1
  81. package/src/engine-components/OrbitControls.ts +14 -6
  82. package/src/engine-components/SceneSwitcher.ts +28 -3
  83. package/src/engine-components/TransformGizmo.ts +64 -70
  84. package/src/engine-components/codegen/components.ts +2 -1
  85. package/src/engine-components/export/usdz/USDZExporter.ts +7 -2
  86. package/src/engine-components/ui/Button.ts +14 -9
  87. package/src/engine-components/ui/Canvas.ts +104 -40
  88. package/src/engine-components/ui/EventSystem.ts +16 -9
  89. package/src/engine-components/ui/Graphic.ts +44 -8
  90. package/src/engine-components/ui/Image.ts +10 -1
  91. package/src/engine-components/ui/InputField.ts +9 -1
  92. package/src/engine-components/ui/Interfaces.ts +12 -0
  93. package/src/engine-components/ui/Outline.ts +13 -0
  94. package/src/engine-components/ui/RectTransform.ts +203 -60
  95. package/src/engine-components/ui/Text.ts +284 -265
  96. package/src/engine-components/utils/LookAt.ts +21 -0
  97. package/src/engine-components/webxr/WebXRImageTracking.ts +85 -10
  98. package/lib/engine-components/ui/Keyboard.d.ts +0 -31
  99. package/lib/engine-components/ui/Keyboard.js +0 -178
  100. package/lib/engine-components/ui/Keyboard.js.map +0 -1
  101. package/src/engine-components/ui/Keyboard.ts +0 -204
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/engine",
3
- "version": "3.2.14-alpha",
3
+ "version": "3.3.0-alpha",
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.umd.cjs",
6
6
  "type": "module",
@@ -62,7 +62,7 @@
62
62
  "simplex-noise": "^4.0.1",
63
63
  "stats.js": "^0.17.0",
64
64
  "three": "npm:@needle-tools/three@^0.146.8",
65
- "three-mesh-ui": "^6.4.5",
65
+ "three-mesh-ui": "npm:@needle-tools/three-mesh-ui@^7.1.4",
66
66
  "three.quarks": "^0.7.3",
67
67
  "uuid": "^9.0.0",
68
68
  "websocket-ts": "^1.1.1"
@@ -76,13 +76,13 @@ import { Image } from "../../engine-components/ui/Image";
76
76
  import { InheritVelocityModule } from "../../engine-components/ParticleSystemModules";
77
77
  import { InputField } from "../../engine-components/ui/InputField";
78
78
  import { Interactable } from "../../engine-components/Interactable";
79
- import { Keyboard } from "../../engine-components/ui/Keyboard";
80
79
  import { LayoutGroup } from "../../engine-components/ui/Layout";
81
80
  import { Light } from "../../engine-components/Light";
82
81
  import { LimitVelocityOverLifetimeModule } from "../../engine-components/ParticleSystemModules";
83
82
  import { LODGroup } from "../../engine-components/LODGroup";
84
83
  import { LODModel } from "../../engine-components/LODGroup";
85
84
  import { LogStats } from "../../engine-components/debug/LogStats";
85
+ import { LookAt } from "../../engine-components/utils/LookAt";
86
86
  import { LookAtConstraint } from "../../engine-components/LookAtConstraint";
87
87
  import { MainModule } from "../../engine-components/ParticleSystemModules";
88
88
  import { MaskableGraphic } from "../../engine-components/ui/Graphic";
@@ -97,6 +97,7 @@ import { ObjectRaycaster } from "../../engine-components/ui/Raycaster";
97
97
  import { OffsetConstraint } from "../../engine-components/OffsetConstraint";
98
98
  import { OpenURL } from "../../engine-components/utils/OpenURL";
99
99
  import { OrbitControls } from "../../engine-components/OrbitControls";
100
+ import { Outline } from "../../engine-components/ui/Outline";
100
101
  import { ParticleBurst } from "../../engine-components/ParticleSystemModules";
101
102
  import { ParticleSubEmitter } from "../../engine-components/ParticleSystemSubEmitter";
102
103
  import { ParticleSystem } from "../../engine-components/ParticleSystem";
@@ -266,13 +267,13 @@ TypeStore.add("Image", Image);
266
267
  TypeStore.add("InheritVelocityModule", InheritVelocityModule);
267
268
  TypeStore.add("InputField", InputField);
268
269
  TypeStore.add("Interactable", Interactable);
269
- TypeStore.add("Keyboard", Keyboard);
270
270
  TypeStore.add("LayoutGroup", LayoutGroup);
271
271
  TypeStore.add("Light", Light);
272
272
  TypeStore.add("LimitVelocityOverLifetimeModule", LimitVelocityOverLifetimeModule);
273
273
  TypeStore.add("LODGroup", LODGroup);
274
274
  TypeStore.add("LODModel", LODModel);
275
275
  TypeStore.add("LogStats", LogStats);
276
+ TypeStore.add("LookAt", LookAt);
276
277
  TypeStore.add("LookAtConstraint", LookAtConstraint);
277
278
  TypeStore.add("MainModule", MainModule);
278
279
  TypeStore.add("MaskableGraphic", MaskableGraphic);
@@ -287,6 +288,7 @@ TypeStore.add("ObjectRaycaster", ObjectRaycaster);
287
288
  TypeStore.add("OffsetConstraint", OffsetConstraint);
288
289
  TypeStore.add("OpenURL", OpenURL);
289
290
  TypeStore.add("OrbitControls", OrbitControls);
291
+ TypeStore.add("Outline", Outline);
290
292
  TypeStore.add("ParticleBurst", ParticleBurst);
291
293
  TypeStore.add("ParticleSubEmitter", ParticleSubEmitter);
292
294
  TypeStore.add("ParticleSystem", ParticleSystem);
@@ -27,8 +27,8 @@ export class Addressables {
27
27
 
28
28
  private _assetReferences: { [key: string]: AssetReference } = {};
29
29
 
30
- findAssetReference(uri: string): AssetReference | null {
31
- return this._assetReferences[uri] || null;
30
+ findAssetReference(key: string): AssetReference | null {
31
+ return this._assetReferences[key] || null;
32
32
  }
33
33
 
34
34
  registerAssetReference(ref: AssetReference): AssetReference {
@@ -48,9 +48,9 @@ export type ProgressCallback = (asset: AssetReference, prog: ProgressEvent) => v
48
48
 
49
49
  export class AssetReference {
50
50
 
51
- static getOrCreate(sourceId: SourceIdentifier, uri: string, context: Context): AssetReference {
52
- const fullPath = resolveUrl(sourceId, uri);
53
- if (debug) console.log("GetOrCreate Addressable from", sourceId, uri, "FinalPath=", fullPath);
51
+ static getOrCreate(sourceId: SourceIdentifier, url: string, context: Context): AssetReference {
52
+ const fullPath = resolveUrl(sourceId, url);
53
+ if (debug) console.log("GetOrCreate Addressable from", sourceId, url, "FinalPath=", fullPath);
54
54
  const addressables = context.addressables;
55
55
  const existing = addressables.findAssetReference(fullPath);
56
56
  if (existing) return existing;
@@ -89,19 +89,19 @@ export class AssetReference {
89
89
  private _isLoadingRawBinary: boolean = false;
90
90
  private _rawBinary?: ArrayBuffer | null;
91
91
 
92
- constructor(uri: string, hash?: string) {
92
+ constructor(uri: string, hash?: string, asset: any = null) {
93
93
  this._url = uri;
94
94
  this._hash = hash;
95
95
  if (uri.includes("?v="))
96
96
  this._hashedUri = uri;
97
97
  else
98
98
  this._hashedUri = hash ? uri + "?v=" + hash : uri;
99
-
99
+ if (asset !== null) this.asset = asset;
100
100
  registerPrefabProvider(this._url, this.onResolvePrefab.bind(this));
101
101
  }
102
102
 
103
- private async onResolvePrefab(uri: string): Promise<IGameObject | null> {
104
- if (uri === this.uri) {
103
+ private async onResolvePrefab(url: string): Promise<IGameObject | null> {
104
+ if (url === this.uri) {
105
105
  if (this.mustLoad) await this.loadAssetAsync();
106
106
  if (this.asset) {
107
107
  return this.asset;
@@ -320,12 +320,30 @@ class AddressableSerializer extends TypeSerializer {
320
320
  return null;
321
321
  }
322
322
  if (!context.gltfId) {
323
- console.error("Missing spurce id");
323
+ console.error("Missing source id");
324
324
  return null;
325
325
  }
326
326
  const ref = AssetReference.getOrCreate(context.gltfId, data, context.context);
327
327
  return ref;
328
328
  }
329
+ else if (data instanceof Object3D) {
330
+ if (!context.context) {
331
+ console.error("Missing context");
332
+ return null;
333
+ }
334
+ if (!context.gltfId) {
335
+ console.error("Missing source id");
336
+ return null;
337
+ }
338
+ const obj = data;
339
+ const ctx = context.context;
340
+ const guid = obj["guid"] ?? obj.uuid;
341
+ const existing = ctx.addressables.findAssetReference(guid);
342
+ if (existing) return existing;
343
+ const ref = new AssetReference(guid, undefined, obj);
344
+ ctx.addressables.registerAssetReference(ref);
345
+ return ref;
346
+ }
329
347
  return null;
330
348
  }
331
349
 
@@ -448,5 +448,5 @@ function getNameFromUrl(str: string) {
448
448
  let index = name.indexOf("?")
449
449
  if (index > 0)
450
450
  name = name.substring(0, index);
451
- return name;
451
+ return decodeURIComponent(name);
452
452
  }
@@ -3,7 +3,7 @@ import { processNewScripts } from "./engine_mainloop_utils";
3
3
  import { InstantiateIdProvider } from "./engine_networking_instantiate";
4
4
  import { Context, registerComponent } from "./engine_setup";
5
5
  import { logHierarchy, setWorldPosition, setWorldQuaternion } from "./engine_three_utils";
6
- import { GuidsMap, IComponent as Component, IComponent, IGameObject as GameObject, UIDProvider } from "./engine_types";
6
+ import { GuidsMap, IComponent as Component, IComponent, IGameObject as GameObject, UIDProvider, Constructor } from "./engine_types";
7
7
  import { getParam, tryFindObject } from "./engine_utils";
8
8
  import { apply } from "../engine-components/js-extensions/Object3D";
9
9
  import { $isUsingInstancing, InstancingUtil } from "./engine_instancing";
@@ -163,6 +163,23 @@ export function foreachComponent(instance: Object3D, cb: ForEachComponentCallbac
163
163
  return internalForEachComponent(instance, cb, recursive);
164
164
  }
165
165
 
166
+ export function* foreachComponentEnumerator<T extends IComponent>(instance: Object3D, type?: Constructor<T>, includeChildren: boolean = false): Generator<T> {
167
+ if (!instance?.userData.components) return;
168
+ for (const comp of instance.userData.components) {
169
+ if (type && comp?.isComponent === true && comp instanceof type) {
170
+ yield comp;
171
+ }
172
+ else {
173
+ yield comp;
174
+ }
175
+ }
176
+ if (includeChildren === true) {
177
+ for (const ch of instance.children) {
178
+ yield* foreachComponentEnumerator(ch, type, true);
179
+ }
180
+ }
181
+ }
182
+
166
183
  function internalForEachComponent(instance: Object3D, cb: ForEachComponentCallback, recursive: boolean, level: number = 0): any {
167
184
  if (!instance) return;
168
185
  if (!instance.isObject3D) {
@@ -512,6 +512,17 @@ export class Input extends EventTarget {
512
512
  private onDown(evt: PointerEventArgs) {
513
513
  if (debug) console.log(evt.pointerType, "DOWN", evt.button);
514
514
  if (!this.isInRect(evt)) return;
515
+
516
+ // check if we received an mouse UP event for a touch (for some reason we get a mouse.down for touch.up)
517
+ if (evt.pointerType === PointerType.Mouse) {
518
+ const upTime = this._pointerUpTimestamp[evt.button];
519
+ if (upTime > 0 && upTime === evt.source?.timeStamp) {
520
+ // we received an UP event for a touch, ignore this DOWN event
521
+ if(debug) console.log("Ignoring mouse.down for touch.up");
522
+ return;
523
+ }
524
+ }
525
+
515
526
  this.setPointerState(evt.button, this._pointerPressed, true);
516
527
  this.setPointerState(evt.button, this._pointerDown, true);
517
528
  this.setPointerStateT(evt.button, this._pointerEvent, evt.source);
@@ -1,4 +1,4 @@
1
- import { getParam } from "./engine_utils";
1
+ import { getParam, isMobileDevice } from "./engine_utils";
2
2
  import { ContextEvent, ContextRegistry } from "./engine_context_registry";
3
3
  import { IContext } from "./engine_types";
4
4
  import { logoSVG } from "./assets";
@@ -31,8 +31,8 @@ async function showLicenseInfo(ctx: IContext) {
31
31
 
32
32
 
33
33
  const licenseElementIdentifier = "needle-license-element";
34
- const licenseDuration = 15000;
35
- const licenseDelay = 500;
34
+ const licenseDuration = 5000;
35
+ const licenseDelay = 200;
36
36
 
37
37
  function onNonCommercialVersionDetected(ctx: IContext) {
38
38
  setTimeout(() => insertNonCommercialUseHint(ctx), 2000);
@@ -62,7 +62,8 @@ function insertNonCommercialUseHint(ctx: IContext) {
62
62
 
63
63
  const textElement = document.createElement("div");
64
64
  textElement.classList.add("text");
65
- textElement.innerHTML = "Needle Engine<br/><span class=\"non-commercial\">Non Commercial</span>";
65
+ // if (!isMobileDevice())
66
+ // textElement.innerHTML = "Needle Engine<br/><span class=\"non-commercial\">Non Commercial</span>";
66
67
  licenseElement.appendChild(textElement);
67
68
 
68
69
  licenseElement.title = "Needle Engine — non commercial version";
@@ -157,11 +158,11 @@ function createLicenseStyle() {
157
158
  animation-delay: ${licenseDelay / 1000}s;
158
159
  animation-easing: ease-in-out;
159
160
  mix-blend-mode: difference;
160
- color: rgb(40, 40, 40);
161
+ color: rgb(0, 0, 0);
161
162
  mix-blend-mode: difference;
162
163
  line-height: 1em;
163
164
  margin-left: -3px;
164
- text-shadow: 0 0 2px rgba(200,200,200, .3);
165
+ text-shadow: 0 0 2px rgba(200,200,200, .5);
165
166
  }
166
167
 
167
168
  ${selector} .text .non-commercial {
@@ -175,30 +176,16 @@ function createLicenseStyle() {
175
176
  transform: translate(0px, 10px);
176
177
  pointer-events: none;
177
178
  }
178
- 1% {
179
- transform: translate(0, -5px);
180
- opacity: 1;
181
- }
182
- 2% {
183
- transform: translate(0, 2.5px);
184
- }
185
- 3% {
179
+ 8% {
186
180
  transform: translate(0, 0px);
187
181
  pointer-events: all;
182
+ opacity: 1;
183
+ transform: scale(1.1)
188
184
  }
189
- 4% {
190
- transform: scale(1)
191
- }
192
- 4.5% {
193
- transform: scale(1.3)
194
- }
195
- 6% {
196
- transform: scale(1.32)
197
- }
198
- 7% {
185
+ 20% {
199
186
  transform: scale(1)
200
187
  }
201
- 98% {
188
+ 90% {
202
189
  opacity: 1;
203
190
  pointer-events: all;
204
191
  transform: scale(1)
@@ -49,10 +49,20 @@ class MathHelper {
49
49
  return radians * 180 / Math.PI;
50
50
  }
51
51
 
52
+ readonly Rad2Deg = 180 / Math.PI;
53
+
52
54
  toRadians(degrees: number) {
53
55
  return degrees * Math.PI / 180;
54
56
  }
55
57
 
58
+ readonly Deg2Rad = Math.PI / 180;
59
+
60
+ readonly Epsilon = 0.00001;
61
+
62
+ tan(radians: number) {
63
+ return Math.tan(radians);
64
+ }
65
+
56
66
  gammaToLinear(gamma: number) {
57
67
  return Math.pow(gamma, 2.2);
58
68
  }
@@ -7,6 +7,7 @@ import { TypeStore } from "../engine/engine_typestore";
7
7
  import { assign } from "../engine/engine_serialization_core";
8
8
  import { Mathf } from "../engine/engine_math";
9
9
  import { isAnimationAction } from "../engine/engine_three_utils";
10
+ import { isDevEnvironment } from "../engine/debug";
10
11
 
11
12
  const debug = getParam("debuganimatorcontroller");
12
13
  const debugRootMotion = getParam("debugrootmotion");
@@ -407,7 +408,12 @@ export class AnimatorController {
407
408
  }
408
409
  }
409
410
  }
410
- else console.warn("No action", state.motion, this);
411
+ else if (isDevEnvironment()) {
412
+ if (!state["__warned_no_motion"]) {
413
+ state["__warned_no_motion"] = true;
414
+ console.warn("No action", state.motion, this);
415
+ }
416
+ }
411
417
 
412
418
  if (debug)
413
419
  console.log("TRANSITION FROM " + prev?.name + " TO " + state.name, durationInSec, prevAction, action, action?.getEffectiveTimeScale(), action?.getEffectiveWeight(), action?.isRunning(), action?.isScheduled(), action?.paused);
@@ -8,7 +8,7 @@ import { getParam, isMobileDevice } from "../engine/engine_utils";
8
8
 
9
9
  import { Camera as ThreeCamera, Box3, Object3D, PerspectiveCamera, Vector2, Vector3 } from "three";
10
10
  import { OrbitControls as ThreeOrbitControls } from "three/examples/jsm/controls/OrbitControls";
11
- import { EventSystem, EventSystemEvents } from "./ui/EventSystem";
11
+ import { AfterHandleInputEvent, EventSystem, EventSystemEvents } from "./ui/EventSystem";
12
12
  import { ICameraController } from "../engine/engine_types";
13
13
  import { setCameraController } from "../engine/engine_camera";
14
14
  import { SyncedTransform } from "./SyncedTransform";
@@ -176,15 +176,22 @@ export class OrbitControls extends Behaviour implements ICameraController {
176
176
  }
177
177
 
178
178
  private onControlsChangeStarted() {
179
- if(this._syncedTransform) {
179
+ if (this._syncedTransform) {
180
180
  this._syncedTransform.requestOwnership();
181
181
  }
182
182
  }
183
183
 
184
184
  private _shouldDisable: boolean = false;
185
- private afterHandleInput() {
186
- if (this._controls && this._eventSystem) {
187
- this._shouldDisable = this._eventSystem.hasActiveUI;
185
+ private afterHandleInput(evt: CustomEvent<AfterHandleInputEvent>) {
186
+ if (evt.detail.args.pointerId === 0) {
187
+ if (evt.detail.args.isDown) {
188
+ if (this._controls && this._eventSystem) {
189
+ this._shouldDisable = this._eventSystem.hasActiveUI;
190
+ }
191
+ }
192
+ else if (!evt.detail.args.isPressed || evt.detail.args.isUp) {
193
+ this._shouldDisable = false;
194
+ }
188
195
  }
189
196
  }
190
197
 
@@ -205,6 +212,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
205
212
 
206
213
  onBeforeRender() {
207
214
  if (!this._controls) return;
215
+ if (this._cameraObject !== this.context.mainCamera) return;
208
216
 
209
217
  if (this.context.input.getPointerDown(0) || this.context.input.getPointerDown(1) || this.context.input.getPointerDown(2)) {
210
218
  this._inputs += 1;
@@ -267,7 +275,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
267
275
  if (this._controls && !this.context.isInXR) {
268
276
  if (this.debugLog)
269
277
  this._controls.domElement = this.context.renderer.domElement;
270
- this._controls.enabled = !this._shouldDisable;
278
+ this._controls.enabled = !this._shouldDisable && this._camera === this.context.mainCameraComponent;
271
279
  this._controls.update();
272
280
  }
273
281
  }
@@ -186,8 +186,33 @@ export class SceneSwitcher extends Behaviour {
186
186
  return this.select(this._currentIndex - 1);
187
187
  }
188
188
 
189
- select(index: number): Promise<boolean> {
190
- if (debug) console.log("select", index)
189
+ select(index: number | string): Promise<boolean> {
190
+ if (debug) console.log("select", index);
191
+
192
+ if(typeof index === "object"){
193
+ // If a user tries to reference a scene object in a UnityEvent and invoke select(obj)
194
+ // Then the object will be serialized as a object { guid : ... } or with the index json pointer
195
+ // This case is not supported right now. Object references in the editor must not be scene references
196
+ console.warn("Switching to \"" + index + "\" might not work. Please either use an index or a AssetReference (not a scene reference)");
197
+ }
198
+
199
+ if (typeof index === "string") {
200
+ // If the parameter is a string we try to resolve the scene by its uri
201
+ // it's either already known in the scenes array
202
+ // or we create/get a new AssetReference and try to switch to that
203
+ const scene = this.scenes?.find(s => s.uri === index);
204
+ if (!scene) {
205
+ // Ok the scene is unknown to the scene switcher
206
+ // we create a new asset reference (or get an existing one)
207
+ // And switch to that. With this we can not modify the history
208
+ // Until the scene switcher can store the uri in the history instead of the index
209
+ const reference = AssetReference.getOrCreate(this.sourceId ?? "", index, this.context);
210
+ return this.switchScene(reference);
211
+ }
212
+ if (scene) index = this.scenes.indexOf(scene);
213
+ else return couldNotLoadScenePromise;
214
+ }
215
+
191
216
  if (!this.scenes?.length) return couldNotLoadScenePromise;
192
217
  if (index < 0) {
193
218
  if (this.clamp) return couldNotLoadScenePromise;
@@ -225,7 +250,7 @@ export class SceneSwitcher extends Behaviour {
225
250
  GameObject.add(scene.asset, this.gameObject);
226
251
  if (this.useSceneLighting)
227
252
  this.context.sceneLighting.enable(scene)
228
- if (this.useHistory) {
253
+ if (this.useHistory && index >= 0) {
229
254
  // save the loaded scene as an url parameter
230
255
  if (this.queryParameterName?.length)
231
256
  setParamWithoutReload(this.queryParameterName, index.toString(), this.useHistory);
@@ -9,57 +9,78 @@ import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
9
9
  export class TransformGizmo extends Behaviour {
10
10
 
11
11
  @serializable()
12
- public isGizmo: boolean = true;
12
+ public isGizmo: boolean = false;
13
+
14
+ @serializable()
15
+ public translationSnap: number = 1;
16
+
17
+ @serializable()
18
+ public rotationSnapAngle: number = 15;
19
+
20
+ @serializable()
21
+ public scaleSnap: number = .25;
13
22
 
14
23
  private control?: TransformControls;
15
24
  private orbit?: OrbitControls;
16
25
 
17
- awake() {
26
+ onEnable() {
18
27
  if (this.isGizmo && !params.showGizmos) return;
28
+
19
29
  if (!this.context.mainCamera) return;
20
- this.control = new TransformControls(this.context.mainCamera, this.context.renderer.domElement);
21
- this.control.visible = true;
22
- this.control.enabled = true;
23
- this.control.getRaycaster().layers.set(2);
24
-
25
- this.control.size = 0.6;
26
- this.control.traverse(x => {
27
- const mesh = x as Mesh;
28
- mesh.layers.set(2);
29
- if (mesh) {
30
- const gizmoMat = mesh.material as THREE.MeshBasicMaterial;
31
- if (gizmoMat) {
32
- gizmoMat.opacity = 0.3;
33
- }
34
- }
35
- });
36
- }
37
30
 
38
- start() {
39
- if (this.context.mainCamera) {
40
- const orbit = GameObject.getComponentInParent(this.context.mainCamera, OrbitControls) ?? undefined;
41
- this.orbit = orbit;
31
+ if (!this.control) {
32
+ this.control = new TransformControls(this.context.mainCamera, this.context.domElement);
33
+ this.control.visible = true;
34
+ this.control.enabled = true;
35
+ this.control.getRaycaster().layers.set(2);
36
+ this.control.size = 1;
37
+ this.control.traverse(x => {
38
+ const mesh = x as Mesh;
39
+ mesh.layers.set(2);
40
+ if (mesh) {
41
+ const gizmoMat = mesh.material as THREE.MeshBasicMaterial;
42
+ if (gizmoMat) {
43
+ gizmoMat.opacity = 0.3;
44
+ }
45
+ }
46
+ });
47
+ this.orbit = GameObject.getComponentInParent(this.context.mainCamera, OrbitControls) ?? undefined;
42
48
  }
43
- }
44
49
 
45
- private changeEventListener?: any;
46
- private windowKeyDownListener?: any;
47
- private windowKeyUpListener?: any;
48
-
49
- onEnable() {
50
50
  if (this.control) {
51
51
  this.context.scene.add(this.control);
52
52
  this.control.attach(this.gameObject);
53
+ this.changeEventListener = this.onControlChangedEvent.bind(this);
54
+ this.control?.addEventListener('dragging-changed', this.changeEventListener);
55
+ this.addWindowEvents();
53
56
  }
54
- this.changeEventListener = this.onControlChangedEvent.bind(this);
55
- this.control?.addEventListener('dragging-changed', this.changeEventListener);
56
- this.attachWindowEvents();
57
57
  }
58
58
 
59
+ private changeEventListener?: any;
60
+ private windowKeyDownListener?: any;
61
+ private windowKeyUpListener?: any;
62
+
59
63
  onDisable() {
60
64
  this.control?.removeFromParent();
61
65
  if (this.changeEventListener)
62
66
  this.control?.removeEventListener('dragging-changed', this.changeEventListener);
67
+ this.removeWindowEvents();
68
+ }
69
+
70
+ enableSnapping() {
71
+ if (this.control) {
72
+ this.control.setTranslationSnap(this.translationSnap);
73
+ this.control.setRotationSnap(MathUtils.degToRad(this.rotationSnapAngle));
74
+ this.control.setScaleSnap(this.scaleSnap);
75
+ }
76
+ }
77
+
78
+ disableSnapping() {
79
+ if (this.control) {
80
+ this.control.setTranslationSnap(null);
81
+ this.control.setRotationSnap(null);
82
+ this.control.setScaleSnap(null);
83
+ }
63
84
  }
64
85
 
65
86
  private onControlChangedEvent(event) {
@@ -74,12 +95,13 @@ export class TransformGizmo extends Behaviour {
74
95
  }
75
96
  }
76
97
 
77
- private attachWindowEvents() {
98
+ private addWindowEvents() {
78
99
  const control = this.control;
79
100
  if (!control) return;
80
101
 
81
102
  if (!this.windowKeyDownListener) {
82
103
  this.windowKeyDownListener = (event) => {
104
+ if (!this.enabled) return;
83
105
  switch (event.keyCode) {
84
106
 
85
107
  case 81: // Q
@@ -87,9 +109,7 @@ export class TransformGizmo extends Behaviour {
87
109
  break;
88
110
 
89
111
  case 16: // Shift
90
- control.setTranslationSnap(100);
91
- control.setRotationSnap(MathUtils.degToRad(15));
92
- control.setScaleSnap(0.25);
112
+ this.enableSnapping();
93
113
  break;
94
114
 
95
115
  case 87: // W
@@ -103,34 +123,6 @@ export class TransformGizmo extends Behaviour {
103
123
  case 82: // R
104
124
  control.setMode('scale');
105
125
  break;
106
-
107
- /*
108
- case 67: // C
109
- const position = currentCamera.position.clone();
110
-
111
- currentCamera = currentCamera.isPerspectiveCamera ? cameraOrtho : cameraPersp;
112
- currentCamera.position.copy( position );
113
-
114
- orbit.object = currentCamera;
115
- control.camera = currentCamera;
116
-
117
- currentCamera.lookAt( orbit.target.x, orbit.target.y, orbit.target.z );
118
- onWindowResize();
119
- break;
120
-
121
- case 86: // V
122
- const randomFoV = Math.random() + 0.1;
123
- const randomZoom = Math.random() + 0.1;
124
-
125
- cameraPersp.fov = randomFoV * 160;
126
- cameraOrtho.bottom = - randomFoV * 500;
127
- cameraOrtho.top = randomFoV * 500;
128
-
129
- cameraPersp.zoom = randomZoom * 5;
130
- cameraOrtho.zoom = randomZoom * 5;
131
- onWindowResize();
132
- break;
133
- */
134
126
  case 187:
135
127
  case 107: // +, =, num+
136
128
  control.setSize(control.size + 0.1);
@@ -164,13 +156,10 @@ export class TransformGizmo extends Behaviour {
164
156
 
165
157
  if (!this.windowKeyUpListener) {
166
158
  this.windowKeyUpListener = (event) => {
167
-
159
+ if (!this.enabled) return;
168
160
  switch (event.keyCode) {
169
-
170
161
  case 16: // Shift
171
- control.setTranslationSnap(null);
172
- control.setRotationSnap(null);
173
- control.setScaleSnap(null);
162
+ this.disableSnapping();
174
163
  break;
175
164
 
176
165
  }
@@ -178,8 +167,13 @@ export class TransformGizmo extends Behaviour {
178
167
  };
179
168
  }
180
169
 
181
-
170
+
182
171
  window.addEventListener('keydown', this.windowKeyDownListener);
183
172
  window.addEventListener('keyup', this.windowKeyUpListener);
184
173
  }
174
+
175
+ private removeWindowEvents() {
176
+ window.removeEventListener('keydown', this.windowKeyDownListener);
177
+ window.removeEventListener('keyup', this.windowKeyUpListener);
178
+ }
185
179
  }
@@ -74,13 +74,13 @@ export { Image } from "../ui/Image";
74
74
  export { InheritVelocityModule } from "../ParticleSystemModules";
75
75
  export { InputField } from "../ui/InputField";
76
76
  export { Interactable } from "../Interactable";
77
- export { Keyboard } from "../ui/Keyboard";
78
77
  export { LayoutGroup } from "../ui/Layout";
79
78
  export { Light } from "../Light";
80
79
  export { LimitVelocityOverLifetimeModule } from "../ParticleSystemModules";
81
80
  export { LODGroup } from "../LODGroup";
82
81
  export { LODModel } from "../LODGroup";
83
82
  export { LogStats } from "../debug/LogStats";
83
+ export { LookAt } from "../utils/LookAt";
84
84
  export { LookAtConstraint } from "../LookAtConstraint";
85
85
  export { MainModule } from "../ParticleSystemModules";
86
86
  export { MaskableGraphic } from "../ui/Graphic";
@@ -95,6 +95,7 @@ export { ObjectRaycaster } from "../ui/Raycaster";
95
95
  export { OffsetConstraint } from "../OffsetConstraint";
96
96
  export { OpenURL } from "../utils/OpenURL";
97
97
  export { OrbitControls } from "../OrbitControls";
98
+ export { Outline } from "../ui/Outline";
98
99
  export { ParticleBurst } from "../ParticleSystemModules";
99
100
  export { ParticleSubEmitter } from "../ParticleSystemSubEmitter";
100
101
  export { ParticleSystem } from "../ParticleSystem";