Needle Engine

Changes between version 3.2.13-alpha and 3.2.14-alpha
Files changed (5) hide show
  1. src/engine/codegen/register_types.js +2 -0
  2. src/engine-components/codegen/components.ts +1 -0
  3. src/engine-components/ui/EventSystem.ts +11 -5
  4. src/engine-components/export/usdz/USDZExporter.ts +3 -4
  5. src/engine-components/utils/OpenURL.ts +119 -0
src/engine/codegen/register_types.js CHANGED
@@ -95,6 +95,7 @@
95
95
  import { NoiseModule } from "../../engine-components/ParticleSystemModules";
96
96
  import { ObjectRaycaster } from "../../engine-components/ui/Raycaster";
97
97
  import { OffsetConstraint } from "../../engine-components/OffsetConstraint";
98
+ import { OpenURL } from "../../engine-components/utils/OpenURL";
98
99
  import { OrbitControls } from "../../engine-components/OrbitControls";
99
100
  import { ParticleBurst } from "../../engine-components/ParticleSystemModules";
100
101
  import { ParticleSubEmitter } from "../../engine-components/ParticleSystemSubEmitter";
@@ -284,6 +285,7 @@
284
285
  TypeStore.add("NoiseModule", NoiseModule);
285
286
  TypeStore.add("ObjectRaycaster", ObjectRaycaster);
286
287
  TypeStore.add("OffsetConstraint", OffsetConstraint);
288
+ TypeStore.add("OpenURL", OpenURL);
287
289
  TypeStore.add("OrbitControls", OrbitControls);
288
290
  TypeStore.add("ParticleBurst", ParticleBurst);
289
291
  TypeStore.add("ParticleSubEmitter", ParticleSubEmitter);
src/engine-components/codegen/components.ts CHANGED
@@ -93,6 +93,7 @@
93
93
  export { NoiseModule } from "../ParticleSystemModules";
94
94
  export { ObjectRaycaster } from "../ui/Raycaster";
95
95
  export { OffsetConstraint } from "../OffsetConstraint";
96
+ export { OpenURL } from "../utils/OpenURL";
96
97
  export { OrbitControls } from "../OrbitControls";
97
98
  export { ParticleBurst } from "../ParticleSystemModules";
98
99
  export { ParticleSubEmitter } from "../ParticleSystemSubEmitter";
src/engine-components/ui/EventSystem.ts CHANGED
@@ -6,14 +6,14 @@
6
6
  import { Context } from "../../engine/engine_setup";
7
7
  import { OrbitControls } from "../OrbitControls";
8
8
  import { IPointerEventHandler, PointerEventData } from "./PointerEvents";
9
- import { Raycaster } from "./Raycaster";
9
+ import { ObjectRaycaster, Raycaster } from "./Raycaster";
10
10
  import { InputEvents } from "../../engine/engine_input";
11
11
  import { Object3D } from "three";
12
12
  import { ICanvasGroup, IGraphic } from "./Interfaces";
13
13
  import { getParam } from "../../engine/engine_utils";
14
14
  import { UIRaycastUtils } from "./RaycastUtils";
15
15
  import { $shadowDomOwner } from "./BaseUIComponent";
16
- import { showBalloonMessage, showBalloonWarning } from "../../engine/debug";
16
+ import { isDevEnvironment, showBalloonMessage, showBalloonWarning } from "../../engine/debug";
17
17
 
18
18
  const debug = getParam("debugeventsystem");
19
19
 
@@ -86,9 +86,15 @@
86
86
  }
87
87
 
88
88
  start() {
89
- // const res = GameObject.findObjectsOfType(Raycaster, this.context);
90
- // if (res)
91
- // this.raycaster = [...res];
89
+ if (this.raycaster.length <= 0) {
90
+ const res = GameObject.findObjectOfType(Raycaster, this.context);
91
+ if (!res) {
92
+ const rc = GameObject.addNewComponent(this.context.scene, ObjectRaycaster);
93
+ this.raycaster.push(rc);
94
+ if (isDevEnvironment())
95
+ console.warn("Added an ObjectRaycaster to the scene because no raycaster was found", this);
96
+ }
97
+ }
92
98
  }
93
99
 
94
100
  register(rc: Raycaster) {
src/engine-components/export/usdz/USDZExporter.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { delay, getParam, isiOS, isMobileDevice, isSafari } from "../../../engine/engine_utils";
2
- import { Object3D, Color } from "three";
3
- import * as THREE from "three";
2
+ import { Object3D, Color, Mesh, Matrix4 } from "three";
4
3
  import { USDZExporter as ThreeUSDZExporter } from "three/examples/jsm/exporters/USDZExporter";
5
4
  import { AnimationExtension } from "./extensions/Animation"
6
5
  import { ensureQuicklookLinkIsCreated } from "./utils/quicklook";
@@ -72,7 +71,7 @@
72
71
  if (!this.objectToExport) this.objectToExport = this.gameObject;
73
72
 
74
73
 
75
- if (isDevEnvironment() && (!this.objectToExport || this.objectToExport.children.length <= 0)) {
74
+ if (isDevEnvironment() && (!this.objectToExport?.children?.length && !(this.objectToExport as Mesh)?.isMesh)) {
76
75
  showBalloonWarning("USDZ Exporter has nothing to export");
77
76
  console.warn("USDZExporter has no objects to export assigned:", this)
78
77
  }
@@ -114,7 +113,7 @@
114
113
  const scale = 1 / this.webARSessionRoot!.arScale;
115
114
  scene.matrix.makeScale(scale, scale, scale);
116
115
  if (this.webARSessionRoot.invertForward) {
117
- scene.matrix.multiply(new THREE.Matrix4().makeRotationY(Math.PI));
116
+ scene.matrix.multiply(new Matrix4().makeRotationY(Math.PI));
118
117
  }
119
118
  }
120
119
 
src/engine-components/utils/OpenURL.ts ADDED
@@ -0,0 +1,119 @@
1
+
2
+ import { IPointerClickHandler, PointerEventData } from "../ui";
3
+ import { Behaviour } from "../Component";
4
+ import { serializable } from "../../engine/engine_serialization";
5
+ import { isDevEnvironment, showBalloonMessage } from "../../engine/debug";
6
+ import { isSafari } from "../../engine/engine_utils";
7
+ import { ObjectRaycaster, Raycaster } from "../ui/Raycaster";
8
+ import { tryGetUIComponent } from "../ui/Utils";
9
+
10
+ export enum OpenURLMode {
11
+ NewTab = 0,
12
+ SameTab = 1,
13
+ NewWindow = 2
14
+ }
15
+
16
+ export class OpenURL extends Behaviour implements IPointerClickHandler {
17
+
18
+ @serializable()
19
+ clickable: boolean = true;
20
+
21
+ @serializable()
22
+ url?: string;
23
+
24
+ @serializable()
25
+ mode: OpenURLMode = OpenURLMode.NewTab;
26
+
27
+ async open() {
28
+ if (!this.url) {
29
+ console.error("URL is not set", this);
30
+ return;
31
+ }
32
+
33
+ this._validateUrl();
34
+
35
+ if (isDevEnvironment()) showBalloonMessage("Open URL: " + this.url)
36
+
37
+
38
+ switch (this.mode) {
39
+ case OpenURLMode.NewTab:
40
+ if (isSafari()) {
41
+ globalThis.open(this.url, "_blank");
42
+ }
43
+ else
44
+ globalThis.open(this.url, "_blank");
45
+ break;
46
+ case OpenURLMode.SameTab:
47
+ if (isSafari()) {
48
+ globalThis.open(this.url, "_top");
49
+ }
50
+ else globalThis.open(this.url, "_self");
51
+ break;
52
+ case OpenURLMode.NewWindow:
53
+ if (isSafari()) {
54
+ globalThis.open(this.url, "_top");
55
+ }
56
+ else globalThis.open(this.url, "_new");
57
+ break;
58
+
59
+ }
60
+ }
61
+
62
+ start(): void {
63
+ const raycaster = this.gameObject.getComponentInParent(ObjectRaycaster);
64
+ if (!raycaster) this.gameObject.addNewComponent(ObjectRaycaster);
65
+ }
66
+
67
+ onEnable(): void {
68
+ if (isSafari()) window.addEventListener("touchend", this._safariNewTabWorkaround);
69
+ }
70
+ onDisable(): void {
71
+ if (isSafari()) window.removeEventListener("touchend", this._safariNewTabWorkaround);
72
+ }
73
+
74
+ onPointerEnter(args) {
75
+ if (!args.used && this.clickable)
76
+ this.context.input.setCursorPointer();
77
+ }
78
+ onPointerExit() {
79
+ if (this.clickable)
80
+ this.context.input.setCursorNormal();
81
+ }
82
+ onPointerClick(args: PointerEventData) {
83
+ if (this.clickable && !args.used && this.url?.length)
84
+ this.open();
85
+ }
86
+
87
+ private _safariNewTabWorkaround = () => {
88
+ if (!this.clickable || !this.url?.length) return;
89
+ // we only need this workaround for opening a new tab
90
+ if (this.mode === OpenURLMode.SameTab) return;
91
+ // When we process the click directly in the browser event we can open a new tab
92
+ // by emitting a link attribute and calling onClick
93
+ const raycaster = this.gameObject.getComponentInParent(Raycaster);
94
+ if (raycaster) {
95
+ const hits = raycaster.performRaycast();
96
+ if (!hits) return;
97
+ for (const hit of hits) {
98
+ if (hit.object === this.gameObject || tryGetUIComponent(hit.object)?.gameObject === this.gameObject) {
99
+ this._validateUrl();
100
+ var a = document.createElement('a') as HTMLAnchorElement;
101
+ a.setAttribute("target", "_blank");
102
+ a.setAttribute("href", this.url);
103
+ a.click();
104
+ break;
105
+ }
106
+ }
107
+ }
108
+ }
109
+
110
+ private _validateUrl() {
111
+ if (!this.url) return;
112
+ if (this.url.startsWith("www.")) {
113
+ if (isDevEnvironment()) {
114
+ console.warn("URL is not valid, adding https:// to the start of the URL", this.url);
115
+ }
116
+ this.url = "https://" + this.url;
117
+ }
118
+ }
119
+ }