@needle-tools/engine 2.62.1-pre → 2.63.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 (109) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/needle-engine.js +9983 -9692
  3. package/dist/needle-engine.umd.cjs +306 -223
  4. package/lib/engine/api.d.ts +1 -0
  5. package/lib/engine/api.js +1 -0
  6. package/lib/engine/api.js.map +1 -1
  7. package/lib/engine/codegen/license.d.ts +1 -0
  8. package/lib/engine/codegen/license.js +2 -0
  9. package/lib/engine/codegen/license.js.map +1 -0
  10. package/lib/engine/engine.js +0 -2
  11. package/lib/engine/engine.js.map +1 -1
  12. package/lib/engine/engine_camera.d.ts +4 -0
  13. package/lib/engine/engine_camera.js +13 -0
  14. package/lib/engine/engine_camera.js.map +1 -0
  15. package/lib/engine/engine_element.d.ts +1 -0
  16. package/lib/engine/engine_element.js +18 -2
  17. package/lib/engine/engine_element.js.map +1 -1
  18. package/lib/engine/engine_element_loading.js +28 -4
  19. package/lib/engine/engine_element_loading.js.map +1 -1
  20. package/lib/engine/engine_input.d.ts +2 -0
  21. package/lib/engine/engine_input.js +14 -0
  22. package/lib/engine/engine_input.js.map +1 -1
  23. package/lib/engine/engine_license.d.ts +1 -0
  24. package/lib/engine/engine_license.js +199 -0
  25. package/lib/engine/engine_license.js.map +1 -0
  26. package/lib/engine/engine_physics.d.ts +3 -0
  27. package/lib/engine/engine_physics.js +13 -2
  28. package/lib/engine/engine_physics.js.map +1 -1
  29. package/lib/engine/engine_setup.js +1 -1
  30. package/lib/engine/engine_setup.js.map +1 -1
  31. package/lib/engine/engine_types.d.ts +4 -0
  32. package/lib/engine/engine_types.js.map +1 -1
  33. package/lib/engine/extensions/extension_utils.js +1 -1
  34. package/lib/engine/extensions/extension_utils.js.map +1 -1
  35. package/lib/engine/js-extensions/Camera.d.ts +1 -0
  36. package/lib/engine/js-extensions/Camera.js +37 -0
  37. package/lib/engine/js-extensions/Camera.js.map +1 -0
  38. package/lib/engine/js-extensions/Layers.js +1 -0
  39. package/lib/engine/js-extensions/Layers.js.map +1 -1
  40. package/lib/engine/js-extensions/index.d.ts +2 -0
  41. package/lib/engine/js-extensions/index.js +3 -0
  42. package/lib/engine/js-extensions/index.js.map +1 -0
  43. package/lib/engine-components/CameraUtils.d.ts +1 -3
  44. package/lib/engine-components/CameraUtils.js +34 -17
  45. package/lib/engine-components/CameraUtils.js.map +1 -1
  46. package/lib/engine-components/Light.d.ts +4 -1
  47. package/lib/engine-components/Light.js +17 -2
  48. package/lib/engine-components/Light.js.map +1 -1
  49. package/lib/engine-components/OrbitControls.d.ts +4 -1
  50. package/lib/engine-components/OrbitControls.js +13 -2
  51. package/lib/engine-components/OrbitControls.js.map +1 -1
  52. package/lib/engine-components/ScreenCapture.d.ts +2 -2
  53. package/lib/engine-components/ScreenCapture.js +3 -1
  54. package/lib/engine-components/ScreenCapture.js.map +1 -1
  55. package/lib/engine-components/VideoPlayer.d.ts +2 -1
  56. package/lib/engine-components/VideoPlayer.js +9 -6
  57. package/lib/engine-components/VideoPlayer.js.map +1 -1
  58. package/lib/engine-components/timeline/TimelineTracks.js +7 -1
  59. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  60. package/lib/engine-components/ui/BaseUIComponent.d.ts +3 -0
  61. package/lib/engine-components/ui/BaseUIComponent.js +20 -10
  62. package/lib/engine-components/ui/BaseUIComponent.js.map +1 -1
  63. package/lib/engine-components/ui/Button.js +7 -3
  64. package/lib/engine-components/ui/Button.js.map +1 -1
  65. package/lib/engine-components/ui/EventSystem.js +21 -42
  66. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  67. package/lib/engine-components/ui/Graphic.d.ts +1 -0
  68. package/lib/engine-components/ui/Graphic.js +7 -0
  69. package/lib/engine-components/ui/Graphic.js.map +1 -1
  70. package/lib/engine-components/ui/Interfaces.d.ts +2 -1
  71. package/lib/engine-components/ui/RaycastUtils.js +2 -0
  72. package/lib/engine-components/ui/RaycastUtils.js.map +1 -1
  73. package/lib/engine-components/ui/Text.js +10 -1
  74. package/lib/engine-components/ui/Text.js.map +1 -1
  75. package/lib/needle-engine.d.ts +2 -0
  76. package/lib/needle-engine.js +2 -2
  77. package/lib/needle-engine.js.map +1 -1
  78. package/lib/tsconfig.tsbuildinfo +1 -1
  79. package/package.json +2 -2
  80. package/src/engine/api.ts +2 -1
  81. package/src/engine/assets/logo.svg +1 -0
  82. package/src/engine/codegen/license.js +1 -0
  83. package/src/engine/engine.ts +0 -3
  84. package/src/engine/engine_camera.ts +18 -0
  85. package/src/engine/engine_element.ts +18 -2
  86. package/src/engine/engine_element_loading.ts +33 -8
  87. package/src/engine/engine_input.ts +17 -5
  88. package/src/engine/engine_license.ts +216 -0
  89. package/src/engine/engine_physics.ts +16 -2
  90. package/src/engine/engine_setup.ts +2 -1
  91. package/src/engine/engine_types.ts +9 -4
  92. package/src/engine/extensions/extension_utils.ts +1 -1
  93. package/src/engine/js-extensions/Camera.ts +35 -0
  94. package/src/engine/js-extensions/Layers.ts +2 -1
  95. package/src/engine/js-extensions/index.ts +2 -0
  96. package/src/engine-components/CameraUtils.ts +42 -20
  97. package/src/engine-components/Light.ts +16 -3
  98. package/src/engine-components/OrbitControls.ts +18 -6
  99. package/src/engine-components/ScreenCapture.ts +3 -2
  100. package/src/engine-components/VideoPlayer.ts +10 -6
  101. package/src/engine-components/timeline/TimelineTracks.ts +9 -3
  102. package/src/engine-components/ui/BaseUIComponent.ts +21 -11
  103. package/src/engine-components/ui/Button.ts +7 -3
  104. package/src/engine-components/ui/EventSystem.ts +20 -42
  105. package/src/engine-components/ui/Graphic.ts +7 -0
  106. package/src/engine-components/ui/Interfaces.ts +3 -1
  107. package/src/engine-components/ui/RaycastUtils.ts +2 -1
  108. package/src/engine-components/ui/Text.ts +11 -2
  109. package/src/needle-engine.ts +6 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/engine",
3
- "version": "2.62.1-pre",
3
+ "version": "2.63.0-pre",
4
4
  "description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development, and can be deployed anywhere. It is flexible, extensible, and collaboration and XR come naturally.",
5
5
  "main": "dist/needle-engine.umd.cjs",
6
6
  "module": "dist/needle-engine.js",
@@ -60,7 +60,7 @@
60
60
  "@needle-tools/needle-component-compiler": "1.9.3",
61
61
  "@needle-tools/helper": "^0.2.1-pre",
62
62
  "@types/three": "0.146.0",
63
- "copy-files-from-to": "^3.3.0",
63
+ "copy-files-from-to": "^3.7.0",
64
64
  "esbuild": "^0.15.10",
65
65
  "esbuild-node-externals": "^1.5.0",
66
66
  "jsdoc-babel": "^0.5.0",
package/src/engine/api.ts CHANGED
@@ -8,4 +8,5 @@ export * from "./debug/debug";
8
8
  export { validate } from "./engine_util_decorator"
9
9
  export { Gizmos } from "./engine_gizmos"
10
10
  export * from "./engine_scenetools";
11
- export * from "./engine_math"
11
+ export * from "./engine_math"
12
+ export * from "./js-extensions"
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><svg id="Ebene_3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 160 187.74"><defs><style>.cls-1{fill:url(#Unbenannter_Verlauf_122);}.cls-2{fill:#f3e600;}.cls-3{fill:url(#Unbenannter_Verlauf_113);}.cls-4{fill:url(#gradient-6);}.cls-5{fill:url(#Unbenannter_Verlauf_110);}.cls-6{fill:url(#Unbenannter_Verlauf_101);}.cls-7{fill:#9c3;}.cls-8{fill:#ffe113;}.cls-9{fill:url(#gradient-5);}</style><linearGradient id="Unbenannter_Verlauf_113" x1="89.64" y1="184.81" x2="90.48" y2="21.85" gradientTransform="matrix(1, 0, 0, 1, 0, 0)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#62d399"/><stop offset=".51" stop-color="#acd842"/><stop offset=".9" stop-color="#d7db0a"/></linearGradient><linearGradient id="Unbenannter_Verlauf_110" x1="69.68" y1="178.9" x2="68.08" y2="16.77" gradientTransform="matrix(1, 0, 0, 1, 0, 0)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0ba398"/><stop offset=".5" stop-color="#4ca352"/><stop offset="1" stop-color="#76a30a"/></linearGradient><linearGradient id="Unbenannter_Verlauf_101" x1="36.6" y1="152.17" x2="34.7" y2="84.19" gradientTransform="matrix(1, 0, 0, 1, 0, 0)" gradientUnits="userSpaceOnUse"><stop offset=".19" stop-color="#36a382"/><stop offset=".54" stop-color="#49a459"/><stop offset="1" stop-color="#76a30b"/></linearGradient><linearGradient id="Unbenannter_Verlauf_122" x1="15.82" y1="153.24" x2="18" y2="90.86" gradientTransform="matrix(1, 0, 0, 1, 0, 0)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#267880"/><stop offset=".51" stop-color="#457a5c"/><stop offset="1" stop-color="#717516"/></linearGradient><linearGradient id="gradient-6" x1="135.08" y1="135.43" x2="148.93" y2="63.47" gradientTransform="matrix(1, 0, 0, 1, 0, 0)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#b0d939"/><stop offset="1" stop-color="#eadb04"/></linearGradient><linearGradient id="gradient-5" x1="-4163.25" y1="2285.12" x2="-4160.81" y2="2215.34" gradientTransform="translate(4801.15 -595.25) rotate(20)" gradientUnits="userSpaceOnUse"><stop offset=".17" stop-color="#74af52"/><stop offset=".48" stop-color="#99be32"/><stop offset="1" stop-color="#c0c40a"/></linearGradient><symbol id="needle-icon" viewBox="0 0 160 187.74"><g><polygon class="cls-3" points="79.32 36.98 79.32 187.74 95 174.54 101.59 18.23 79.32 36.98"/><polygon class="cls-5" points="79.32 36.98 57.05 18.23 63.64 174.54 79.32 187.74 79.32 36.98"/><polygon class="cls-6" points="25.19 104.83 33.82 153.87 46.32 138.92 43.86 82.5 25.19 104.83"/><polygon class="cls-1" points="25.19 104.83 0 90.24 16.97 144.1 33.82 153.87 25.19 104.83"/><polygon class="cls-7" points="43.86 82.5 18.69 67.98 0 90.24 25.18 104.83 43.86 82.5"/><polygon class="cls-4" points="134.82 78.69 124.85 135.19 140.43 126.15 160 64.1 134.82 78.69"/><polygon class="cls-9" points="134.82 78.69 116.14 56.36 113.28 121.36 124.85 135.19 134.82 78.69"/><polygon class="cls-8" points="160 64.1 141.31 41.84 116.14 56.36 134.81 78.69 160 64.1"/><polygon class="cls-2" points="101.59 18.23 79.32 0 57.05 18.23 79.32 36.98 101.59 18.23"/></g></symbol></defs><use width="160" height="187.74" xlink:href="#needle-icon"/></svg>
@@ -0,0 +1 @@
1
+ export const hasLicense = false;
@@ -1,8 +1,5 @@
1
1
  import "./engine_hot_reload"
2
2
 
3
- import * as layers from "./js-extensions/Layers";
4
- layers.patchLayers();
5
-
6
3
  import * as engine_setup from "./engine_setup";
7
4
  import * as engine_scenetools from "./engine_scenetools";
8
5
  import "./tests/test_utils";
@@ -0,0 +1,18 @@
1
+ import { ICameraController } from "./engine_types";
2
+ import { Camera } from "three";
3
+
4
+
5
+ const $cameraController = Symbol("cameraController");
6
+
7
+ export function getCameraController(cam: Camera): ICameraController | null {
8
+ return cam[$cameraController];
9
+ }
10
+
11
+ export function setCameraController(cam: Camera, cameraController: ICameraController, active: boolean) {
12
+ if (active)
13
+ cam[$cameraController] = cameraController;
14
+ else{
15
+ if(cam[$cameraController] === cameraController)
16
+ cam[$cameraController] = null;
17
+ }
18
+ }
@@ -79,6 +79,12 @@ export class EngineElement extends HTMLElement implements INeedleEngineComponent
79
79
  public get loadingProgress01(): number { return this._loadingProgress01; }
80
80
  public get loadingFinished(): boolean { return this.loadingProgress01 > .999; }
81
81
 
82
+ public get cameraControls(): boolean {
83
+ const attr = this.getAttribute("camera-controls");
84
+ if(attr === null || attr === "False" || attr === "false" || attr === "0") return false;
85
+ return true;
86
+ }
87
+
82
88
  public getContext(): Promise<Context> {
83
89
  return new Promise((res, _rej) => {
84
90
  if (this._context && this.loadingFinished) {
@@ -208,7 +214,7 @@ export class EngineElement extends HTMLElement implements INeedleEngineComponent
208
214
  }
209
215
  this._loadingProgress01 = 1;
210
216
  if (useDefaultLoading)
211
- this._loadingView?.onLoadingFinished("create scene");
217
+ this._loadingView?.onLoadingFinished("creating scene");
212
218
  this.classList.remove("loading");
213
219
  this.classList.add("loading-finished");
214
220
  if (debug)
@@ -237,7 +243,17 @@ export class EngineElement extends HTMLElement implements INeedleEngineComponent
237
243
  }
238
244
 
239
245
  static get observedAttributes() {
240
- return ["hash", "src", "loadstart", "progress", "loadfinished", "dracoDecoderPath", "dracoDecoderType", "ktx2DecoderPath"];
246
+ return [
247
+ "hash",
248
+ "src",
249
+ "camera-controls",
250
+ "loadstart",
251
+ "progress",
252
+ "loadfinished",
253
+ "dracoDecoderPath",
254
+ "dracoDecoderType",
255
+ "ktx2DecoderPath"
256
+ ];
241
257
  }
242
258
 
243
259
  attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
@@ -64,13 +64,13 @@ export class EngineLoadingView implements ILoadingViewHandler {
64
64
  }
65
65
 
66
66
  onLoadingBegin(message?: string) {
67
- if(debug) console.log("Begin Loading")
67
+ if (debug) console.log("Begin Loading")
68
68
  if (!this._loadingElement) {
69
69
  for (let i = 0; i < this.container.children.length; i++) {
70
70
  const el = this.container.children[i] as HTMLElement;
71
71
  if (el.classList.contains(EngineLoadingView.LoadingContainerClassName)) {
72
72
  if (!this._allowCustomLoadingElement) {
73
- if(debug) console.warn("Remove custom loading container")
73
+ if (debug) console.warn("Remove custom loading container")
74
74
  this.container.removeChild(el);
75
75
  continue;
76
76
  }
@@ -102,7 +102,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
102
102
  if ("index" in progress)
103
103
  total01 = calculateProgress01(progress);
104
104
  if (!message && "name" in progress)
105
- this.setMessage(progress.name);
105
+ this.setMessage("loading " + progress.name);
106
106
  }
107
107
  this.loadingProgress = total01;
108
108
  if (message) this.setMessage(message);
@@ -125,11 +125,14 @@ export class EngineLoadingView implements ILoadingViewHandler {
125
125
  if (this._progressLoop) return;
126
126
  let dt = 1 / 12;
127
127
  const max = 1 - .05;
128
- if(debugRendering) dt = 1 / 500;
128
+ if (debugRendering) {
129
+ dt = 1 / 500;
130
+ if (typeof debugRendering === "number") dt *= debugRendering;
131
+ }
129
132
  this._progressLoop = setInterval(() => {
130
133
  if (this.loadingProgress >= 1 && this._progress >= max) {
131
134
  if (this._loadingElement) {
132
- if(debug) console.log("Hiding loading element");
135
+ if (debug) console.log("Hiding loading element");
133
136
  this._loadingElement.style.display = "none";
134
137
  this._loadingElement.remove();
135
138
  }
@@ -154,7 +157,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
154
157
  }
155
158
 
156
159
  private createLoadingElement(existing?: HTMLElement): HTMLElement {
157
- if(debug && !existing) console.log("Creating loading element");
160
+ if (debug && !existing) console.log("Creating loading element");
158
161
  this._loadingElement = existing || document.createElement("div");
159
162
  if (!existing) {
160
163
  this._loadingElement.style.position = "fixed";
@@ -186,9 +189,29 @@ export class EngineLoadingView implements ILoadingViewHandler {
186
189
  loadingBarContainer.style.display = "flex";
187
190
  loadingBarContainer.style.width = maxWidth + "%";
188
191
  loadingBarContainer.style.height = "2px";
189
- loadingBarContainer.style.background = "rgba(255,255,255,.1)"
192
+ loadingBarContainer.style.background = "rgba(255,255,255,.2)"
193
+ // loadingBarContainer.style.alignItems = "center";
190
194
  this._loadingElement.appendChild(loadingBarContainer);
191
195
 
196
+ const needleLogo = document.createElement("img");
197
+ loadingBarContainer.appendChild(needleLogo);
198
+ const logoSize = 64;
199
+ needleLogo.style.width = `${logoSize}px`;
200
+ needleLogo.style.height = `${logoSize}px`;
201
+ needleLogo.style.position = "absolute";
202
+ needleLogo.style.left = "50%";
203
+ needleLogo.style.transform = `translate(-${logoSize * .5}px, -${logoSize + 32}px)`;
204
+ needleLogo.addEventListener("click", () => window.open("https://needle.tools", "_blank"));
205
+ needleLogo.style.cursor = "pointer";
206
+ needleLogo.style.pointerEvents = "all";
207
+ try {
208
+ //@ts-ignore
209
+ import("./assets/logo.svg").then(res => {
210
+ needleLogo.src = res.default;
211
+ })
212
+ }
213
+ catch { }
214
+
192
215
  this._loadingBar = document.createElement("div");
193
216
  loadingBarContainer.appendChild(this._loadingBar);
194
217
  const getGradientPos = function (t: number): string {
@@ -203,7 +226,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
203
226
  this._loadingTextContainer = document.createElement("div");
204
227
  this._loadingTextContainer.style.display = "flex";
205
228
  this._loadingTextContainer.style.justifyContent = "center";
206
- this._loadingTextContainer.style.marginTop = ".9em";
229
+ this._loadingTextContainer.style.marginTop = "1.2em";
207
230
  this._loadingElement.appendChild(this._loadingTextContainer);
208
231
 
209
232
  const messageContainer = document.createElement("div");
@@ -212,6 +235,8 @@ export class EngineLoadingView implements ILoadingViewHandler {
212
235
  messageContainer.style.fontSize = ".8em";
213
236
  messageContainer.style.paddingTop = ".5em";
214
237
  messageContainer.style.color = "rgba(255,255,255,.5)";
238
+ messageContainer.style.fontWeight = "200";
239
+ messageContainer.style.fontFamily = "Roboto, sans-serif";
215
240
  // messageContainer.style.border = "1px solid rgba(255,255,255,.1)";
216
241
  messageContainer.style.justifyContent = "center";
217
242
  this._loadingElement.appendChild(messageContainer);
@@ -220,6 +220,7 @@ export class Input extends EventTarget {
220
220
  private _pointerPositionDown: THREE.Vector2[] = [new THREE.Vector2()];
221
221
  private _pointerDownTime: number[] = [];
222
222
  private _pointerUpTime: number[] = [];
223
+ private _pointerUpTimestamp: number[] = [];
223
224
  private _pointerIds: number[] = [];
224
225
  private _pointerTypes: string[] = [""];
225
226
  private _mouseWheelChanged: boolean[] = [false];
@@ -411,13 +412,15 @@ export class Input extends EventTarget {
411
412
  }
412
413
  }
413
414
 
414
- private onTouchUp(evt) {
415
+ private onTouchUp(evt: TouchEvent) {
415
416
  if (evt.changedTouches.length <= 0) return;
416
417
  for (let i = 0; i < evt.changedTouches.length; i++) {
417
418
  const touch = evt.changedTouches[i];
418
- const id = this.getPointerIndex(touch.identifier)
419
- if (debug)
420
- showBalloonMessage(`touch up #${id}, identifier:${touch.identifier}`);
419
+ const id = this.getPointerIndex(touch.identifier);
420
+
421
+ if (!this.isNewEvent(evt.timeStamp, id, this._pointerUpTimestamp)) continue;
422
+
423
+ if (debug) showBalloonMessage(`touch up #${id}, identifier:${touch.identifier}`);
421
424
  const args: PointerEventArgs = { button: id, clientX: touch.clientX, clientY: touch.clientY, pointerType: PointerType.Touch, source: evt };
422
425
  this.onUp(args);
423
426
  }
@@ -440,11 +443,20 @@ export class Input extends EventTarget {
440
443
  private onMouseUp(evt: MouseEvent) {
441
444
  if (evt.defaultPrevented) return;
442
445
  let i = evt.button;
446
+ if (!this.isNewEvent(evt.timeStamp, i, this._pointerUpTimestamp)) return;
443
447
  this.onUp({ button: i, clientX: evt.clientX, clientY: evt.clientY, pointerType: PointerType.Mouse, source: evt });
444
448
  }
445
449
 
450
+ // Prevent the same event being handled twice (e.g. on touch we get a mouseUp and touchUp evt with the same timestamp)
451
+ private isNewEvent(timestamp: number, index: number, arr: number[]): boolean {
452
+ while (arr.length <= index) arr.push(-1);
453
+ if (timestamp === arr[index]) return false;
454
+ arr[index] = timestamp;
455
+ return true;
456
+ }
457
+
446
458
  private isInRect(e: { clientX: number, clientY: number }): boolean {
447
- if(this.context.isInXR) return true;
459
+ if (this.context.isInXR) return true;
448
460
  const rect = this.context.domElement.getBoundingClientRect();
449
461
  const px = e.clientX;
450
462
  const py = e.clientY;
@@ -0,0 +1,216 @@
1
+ import { getParam } from "./engine_utils";
2
+ import { ContextEvent, ContextRegistry } from "./engine_context_registry";
3
+ import { IContext } from "./engine_types";
4
+
5
+ const debug = getParam("debuglicense");
6
+
7
+ ContextRegistry.registerCallback(ContextEvent.ContextCreated, evt => {
8
+ showLicenseInfo(evt.context);
9
+ });
10
+
11
+ async function showLicenseInfo(ctx: IContext) {
12
+ try {
13
+ const res = await import(/* @vite-ignore */ "./codegen/license");
14
+ //@ts-ignore
15
+ if (!res || res.hasLicense !== true || debug) return onNonCommercialVersionDetected(ctx);
16
+ }
17
+ catch (err) {
18
+ if (debug) console.log("License check failed", err)
19
+ return onNonCommercialVersionDetected(ctx)
20
+ }
21
+ if (debug) onNonCommercialVersionDetected(ctx)
22
+ }
23
+
24
+
25
+ const _licenseText = "🌵 <span class=\"text\">Made with <a href=\"https://needle.tools\" target=\"_blank\">Needle</a></span>";
26
+ const licenseElementClass = "needle-license-element";
27
+ const licenseDuration = 30000;
28
+ const licenseDelay = 600;
29
+
30
+ function onNonCommercialVersionDetected(ctx: IContext) {
31
+ insertNonCommercialUseHint(ctx);
32
+ sendNonCommercialUsageMessageToAnalyticsBackend();
33
+ }
34
+
35
+ function insertNonCommercialUseHint(ctx: IContext) {
36
+
37
+ let licenseText = _licenseText;
38
+ const licenseElement = createLicenseElement();
39
+ licenseElement.innerHTML = licenseText;
40
+
41
+ const style = createLicenseStyle();
42
+
43
+ const interval = setInterval(() => {
44
+ if (!licenseElement) return;
45
+ if (licenseElement.parentElement !== ctx.domElement) {
46
+ ctx.domElement.appendChild(licenseElement);
47
+ if (style) ctx.domElement.appendChild(style);
48
+ }
49
+ }, 100);
50
+
51
+ logNonCommercialUse();
52
+
53
+ //@ts-ignore
54
+ import("./assets/logo.svg").then(res => {
55
+ const svgContent = res.default;
56
+ let svg = `<img class="logo" src="${svgContent}" style="width: 40px; height: 40px; margin-right: 2px; vertical-align: middle; margin-bottom: 2px;"/>`;
57
+ svg = "<a href=\"https://needle.tools\" target=\"_blank\">" + svg + "</a>";
58
+ licenseText = svg; //licenseText.replace("🌵", svg);
59
+ licenseElement.innerHTML = licenseText;
60
+ }).catch(err => {
61
+ if (debug) console.log("Failed to load logo", err);
62
+ });
63
+
64
+ const removeDelay = licenseDuration + licenseDelay;
65
+ setTimeout(() => {
66
+ clearInterval(interval);
67
+ licenseElement?.remove();
68
+ }, removeDelay);
69
+
70
+ }
71
+
72
+ async function logNonCommercialUse(_logo?: string) {
73
+ const logo = "data:image/webp;base64,UklGRrABAABXRUJQVlA4WAoAAAAQAAAAHwAAHwAAQUxQSKEAAAARN6CmbSM4WR7vdARON11EBDq3fLiNbVtVzpMCPlKAEzsx0Y/x+Ovuv4dn0EFE/ydAvz6YggXzgh5sVgXM/zOC/4sii7qgGvB5N7hmuQYwkvazWAu1JPW41FXSHq6pnaQWvqYH18Fc0j1hO/BFTtIeSBlJi5w6qIIO7IOrwhFsB2Yxukif0FTRLpXswHR8MxbslKe9VZsn/Ub5C7YFOpqSTABWUDgg6AAAAFAGAJ0BKiAAIAA+7VyoTqmkpCI3+qgBMB2JbACdMt69DwMIQBLhkTO6XwY00UEDK6cNIDnuNibPf0EgAP7Y1myuiQHLDsF/0h5unrGh6WAbv7aegg2ZMd3uRKfT/3SJztcaujYfTvMXspfCTmYcoO6a+vhC3ss4M8uM58t4siiu59I4aOl59e9Sr6xoxYlHf2v+NnBNpJYeJf8jABQAId/PXuBkLEFkiCucgSGEcfhvajql/j3reCGl0M5/9gQWy7ayNPs+wlvIxFnNfSlfuND4CZOCyxOHhRqOmHN4ULHo3tCSrUNvgAA=";
74
+ const style = `
75
+ font-size:18px;
76
+ background-size: contain;
77
+ background-position: left center;
78
+ background-repeat:no-repeat;
79
+ background-image:url('${logo}')
80
+ `;
81
+ let licenseText = "Needle Engine — non commercial version";
82
+ console.log("%c " + licenseText, style)
83
+ }
84
+
85
+ function createLicenseElement() {
86
+ const licenseElement = document.createElement("div");
87
+ licenseElement.classList.add(licenseElementClass);
88
+ licenseElement.setAttribute("data-needle_engine_license_element", "");
89
+ licenseElement.style.position = "fixed";
90
+ licenseElement.style.bottom = "12px";
91
+ licenseElement.style.right = "15px";
92
+
93
+ // licenseElement.style.textShadow = "0 0 2px rgba(200,200,200, 1)";
94
+ const maxPossibleZIndex = 2147483647;
95
+ licenseElement.style.zIndex = `${maxPossibleZIndex}`;
96
+ return licenseElement;
97
+ }
98
+
99
+ function createLicenseStyle() {
100
+ const licenseStyle = document.createElement("style");
101
+ const selector = "." + licenseElementClass;
102
+ licenseStyle.innerHTML = `
103
+ @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;500&display=swap');
104
+
105
+ ${selector} {
106
+ font-family: 'Roboto', sans-serif !important;
107
+ font-weight: 300;
108
+ }
109
+
110
+ ${selector} a {
111
+ color: black;
112
+ text-decoration: none;
113
+ font-weight: 500;
114
+ }
115
+
116
+ @keyframes append-animate {
117
+ 0% {
118
+ transform: translate(0px, 10px);
119
+ opacity: 0;
120
+ pointer-events: none;
121
+ }
122
+ 1% {
123
+ transform: translate(0, -5px);
124
+ opacity: .9;
125
+ }
126
+ 2% {
127
+ transform: translate(0, 3px);
128
+ }
129
+ 6% {
130
+ transform: translate(0, 0px);
131
+ pointer-events: all;
132
+ opacity: 1;
133
+ }
134
+ 50% {
135
+ transform: scale(1)
136
+ }
137
+ 51% {
138
+ transform: scale(1.2)
139
+ }
140
+ 53% {
141
+ transform: scale(1)
142
+ }
143
+ 90% {
144
+ opacity: 1;
145
+ pointer-events: all;
146
+ transform: scale(1)
147
+ }
148
+ 100% {
149
+ opacity: 0;
150
+ pointer-events: none;
151
+ }
152
+ }
153
+ ${selector} {
154
+ opacity: 0;
155
+ pointer-events: none;
156
+ animation: append-animate;
157
+ animation-iteration-count: 1;
158
+ animation-duration: ${(licenseDuration / 1000)}s;
159
+ animation-delay: ${licenseDelay / 1000}s;
160
+ animation-easing: ease-in-out;
161
+ }
162
+
163
+ ${selector} .text, ${selector} a {
164
+ color: rgb(255, 0, 0);
165
+ mix-blend-mode: difference;
166
+ }
167
+
168
+ ${selector} .logo {
169
+ border-radius: 50%;
170
+ background-color: transparent;
171
+ padding: 5px;
172
+ transition: all 0.1s ease-in-out;
173
+ }
174
+
175
+ ${selector} .logo:hover {
176
+ transform: scale(1.3);
177
+ cursor: pointer;
178
+ }
179
+ `
180
+ return licenseStyle;
181
+ }
182
+
183
+
184
+ async function sendNonCommercialUsageMessageToAnalyticsBackend() {
185
+ try {
186
+ const analyticsBackendUrlForward = "https://urls.needle.tools/analytics-endpoint";
187
+ const res = await fetch(analyticsBackendUrlForward);
188
+ let analyticsUrl = await res.text();
189
+ if (analyticsUrl) {
190
+ if (debug) console.log("Analytics backend url", analyticsUrl);
191
+
192
+ // analyticsUrl = "http://localhost:3000/";
193
+
194
+ const currentUrl = window.location.href;
195
+
196
+ let endpoint = "api/v1/register/web-request";
197
+ if (!analyticsUrl.endsWith("/")) endpoint = "/" + endpoint;
198
+ const finalUrl = `${analyticsUrl}${endpoint}?type=non-commercial&url=${encodeURIComponent(currentUrl)}&hostname=${encodeURIComponent(window.location.hostname)}&pathname=${encodeURIComponent(window.location.pathname)}&search=${encodeURIComponent(window.location.search)}&hash=${encodeURIComponent(window.location.hash)}`;
199
+ if (debug) console.log("Sending non-commercial usage message to analytics backend", finalUrl);
200
+
201
+
202
+ fetch(finalUrl,
203
+ {
204
+ mode: "no-cors"
205
+ })
206
+ .catch(err => {
207
+ if (debug)
208
+ console.log("Failed to send non-commercial usage message to analytics backend", err);
209
+ });
210
+ }
211
+ }
212
+ catch (err) {
213
+ if (debug)
214
+ console.log("Failed to send non-commercial usage message to analytics backend", err);
215
+ }
216
+ }
@@ -324,8 +324,22 @@ export class Physics {
324
324
  await RAPIER.init().then(() => RAPIER)
325
325
  Physics._didLoadPhysicsEngine = true;
326
326
  }
327
- const gravity = { x: 0.0, y: -9.81, z: 0.0 };
328
- this.world = new World(gravity);
327
+ this.world = new World(this._gravity);
328
+ }
329
+
330
+ private _gravity = { x: 0.0, y: -9.81, z: 0.0 };
331
+
332
+ get gravity() {
333
+ return this.world?.gravity ?? this._gravity;
334
+ }
335
+
336
+ set gravity(value: Vec3) {
337
+ if (this.world) {
338
+ this.world.gravity = value;
339
+ }
340
+ else {
341
+ this._gravity = value;
342
+ }
329
343
  }
330
344
 
331
345
  clearCaches() {
@@ -504,7 +504,6 @@ export class Context implements IContext {
504
504
  this.domElement.prepend(this.renderer.domElement);
505
505
 
506
506
  Context._current = this;
507
- ContextRegistry.dispatchCallback(ContextEvent.ContextCreated, this);
508
507
 
509
508
  // Setup
510
509
  Context._current = this;
@@ -591,6 +590,8 @@ export class Context implements IContext {
591
590
 
592
591
  if (debug)
593
592
  logHierarchy(this.scene, true);
593
+
594
+ ContextRegistry.dispatchCallback(ContextEvent.ContextCreated, this);
594
595
  }
595
596
 
596
597
  private render(_, frame) {
@@ -30,10 +30,11 @@ export declare type CoroutineData = {
30
30
  export interface IContext {
31
31
  alias?: string | null;
32
32
 
33
- scene : Scene;
34
- renderer : WebGLRenderer;
35
- mainCamera : Camera | null;
36
- domElement : HTMLElement;
33
+ scene: Scene;
34
+ renderer: WebGLRenderer;
35
+ mainCamera: Camera | null;
36
+ mainCameraComponent: ICamera | undefined;
37
+ domElement: HTMLElement;
37
38
 
38
39
  scripts: IComponent[];
39
40
  scripts_pausedChanged: IComponent[];
@@ -158,6 +159,10 @@ export declare interface ICamera extends IComponent {
158
159
  screenPointToRay(x: number, y: number, ray?: Ray): Ray;
159
160
  }
160
161
 
162
+ export declare interface ICameraController {
163
+ get isCameraController(): boolean;
164
+ }
165
+
161
166
  export declare interface ILight extends IComponent {
162
167
  intensity: number;
163
168
  color: Color;
@@ -43,7 +43,7 @@ function internalResolve(paths: DependencyInfo[], parser: GLTFParser, obj, promi
43
43
  // handle json pointer in string variable
44
44
  if (typeof val === "string") {
45
45
  const ext = resolveExtension(parser, val);
46
- if (ext !== null) {
46
+ if (ext !== null && ext !== undefined) {
47
47
  if (typeof ext.then === "function")
48
48
  promises.push(ext.then(res => obj[key] = res));
49
49
  else obj[key] = ext;
@@ -0,0 +1,35 @@
1
+ import { PerspectiveCamera } from "three";
2
+
3
+ // Wrap camera FOV to allow animation of fov
4
+ Object.defineProperty(PerspectiveCamera.prototype, "fov", {
5
+ get: function () {
6
+ return this._fov;;
7
+ },
8
+ set: function (val) {
9
+ const changed = val !== this._fov;
10
+ this._fov = val;
11
+ if (changed && this.view !== undefined) this.updateProjectionMatrix();
12
+ }
13
+ });
14
+
15
+ Object.defineProperty(PerspectiveCamera.prototype, "near", {
16
+ get: function () {
17
+ return this._near;
18
+ },
19
+ set: function (val) {
20
+ const changed = val !== this._near;
21
+ this._near = val;
22
+ if (changed && this.view !== undefined) this.updateProjectionMatrix();
23
+ }
24
+ });
25
+
26
+ Object.defineProperty(PerspectiveCamera.prototype, "far", {
27
+ get: function () {
28
+ return this._far;
29
+ },
30
+ set: function (val) {
31
+ const changed = val !== this._far;
32
+ this._far = val;
33
+ if (changed && this.view !== undefined) this.updateProjectionMatrix();
34
+ }
35
+ });
@@ -16,4 +16,5 @@ export function patchLayers() {
16
16
  if(this[$customVisibilityFlag] === false) return false;
17
17
  return origTest.call(this, layer);
18
18
  };
19
- }
19
+ }
20
+ patchLayers();
@@ -0,0 +1,2 @@
1
+ import "./Layers"
2
+ import "./Camera"