@needle-tools/engine 4.4.0-beta → 4.4.0-beta.10
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.
- package/CHANGELOG.md +65 -2
- package/dist/assets/generateMeshBVH.worker-b7788939.js +25 -0
- package/dist/needle-engine.bundle.js +6808 -6645
- package/dist/needle-engine.bundle.light.js +7071 -6908
- package/dist/needle-engine.bundle.light.min.js +169 -139
- package/dist/needle-engine.bundle.light.umd.cjs +173 -143
- package/dist/needle-engine.bundle.min.js +169 -139
- package/dist/needle-engine.bundle.umd.cjs +172 -142
- package/dist/needle-engine.d.ts +0 -9
- package/dist/needle-engine.js +468 -467
- package/dist/needle-engine.light.d.ts +0 -9
- package/dist/needle-engine.light.js +468 -467
- package/dist/needle-engine.light.min.js +1 -1
- package/dist/needle-engine.light.umd.cjs +1 -1
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/postprocessing.js +1673 -1527
- package/dist/postprocessing.light.js +1673 -1527
- package/dist/postprocessing.light.min.js +80 -66
- package/dist/postprocessing.light.umd.cjs +83 -69
- package/dist/postprocessing.min.js +80 -66
- package/dist/postprocessing.umd.cjs +83 -69
- package/dist/vendor.js +5109 -5123
- package/dist/vendor.light.js +5109 -5123
- package/dist/vendor.light.min.js +84 -84
- package/dist/vendor.light.umd.cjs +78 -78
- package/dist/vendor.min.js +84 -84
- package/dist/vendor.umd.cjs +78 -78
- package/lib/engine/codegen/register_types.js +2 -0
- package/lib/engine/codegen/register_types.js.map +1 -1
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_components.js +3 -1
- package/lib/engine/engine_components.js.map +1 -1
- package/lib/engine/engine_context.d.ts +10 -1
- package/lib/engine/engine_context.js +33 -12
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_element.js +12 -10
- package/lib/engine/engine_element.js.map +1 -1
- package/lib/engine/engine_gameobject.js +5 -0
- package/lib/engine/engine_gameobject.js.map +1 -1
- package/lib/engine/engine_license.d.ts +2 -0
- package/lib/engine/engine_license.js +103 -62
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_networking_blob.js +40 -24
- package/lib/engine/engine_networking_blob.js.map +1 -1
- package/lib/engine/engine_physics_rapier.js +10 -9
- package/lib/engine/engine_physics_rapier.js.map +1 -1
- package/lib/engine/engine_serialization_core.js +6 -2
- package/lib/engine/engine_serialization_core.js.map +1 -1
- package/lib/engine/engine_utils_screenshot.js +1 -1
- package/lib/engine/engine_utils_screenshot.js.map +1 -1
- package/lib/engine/js-extensions/RGBAColor.d.ts +1 -0
- package/lib/engine/js-extensions/RGBAColor.js +48 -0
- package/lib/engine/js-extensions/RGBAColor.js.map +1 -1
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +4 -3
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +6 -6
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +4 -0
- package/lib/engine/webcomponents/needle menu/needle-menu.js +13 -2
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/xr/NeedleXRController.js +2 -3
- package/lib/engine/xr/NeedleXRController.js.map +1 -1
- package/lib/engine/xr/NeedleXRSession.js +12 -12
- package/lib/engine/xr/NeedleXRSession.js.map +1 -1
- package/lib/engine-components/AudioSource.js +7 -0
- package/lib/engine-components/AudioSource.js.map +1 -1
- package/lib/engine-components/Camera.js +18 -12
- package/lib/engine-components/Camera.js.map +1 -1
- package/lib/engine-components/CameraUtils.js +8 -14
- package/lib/engine-components/CameraUtils.js.map +1 -1
- package/lib/engine-components/GroundProjection.js +1 -1
- package/lib/engine-components/GroundProjection.js.map +1 -1
- package/lib/engine-components/NeedleMenu.js +1 -1
- package/lib/engine-components/NeedleMenu.js.map +1 -1
- package/lib/engine-components/OrbitControls.js +3 -1
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/ReflectionProbe.d.ts +1 -0
- package/lib/engine-components/ReflectionProbe.js +16 -9
- package/lib/engine-components/ReflectionProbe.js.map +1 -1
- package/lib/engine-components/SceneSwitcher.js +28 -7
- package/lib/engine-components/SceneSwitcher.js.map +1 -1
- package/lib/engine-components/SyncedTransform.d.ts +6 -0
- package/lib/engine-components/SyncedTransform.js +10 -5
- package/lib/engine-components/SyncedTransform.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +1 -0
- package/lib/engine-components/codegen/components.js +1 -0
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/USDZUI.js +2 -1
- package/lib/engine-components/export/usdz/extensions/USDZUI.js.map +1 -1
- package/lib/engine-components/ui/BaseUIComponent.d.ts +0 -1
- package/lib/engine-components/ui/BaseUIComponent.js +1 -1
- package/lib/engine-components/ui/BaseUIComponent.js.map +1 -1
- package/lib/engine-components/ui/EventSystem.js +1 -1
- package/lib/engine-components/ui/EventSystem.js.map +1 -1
- package/lib/engine-components/ui/Graphic.js +1 -0
- package/lib/engine-components/ui/Graphic.js.map +1 -1
- package/lib/engine-components/ui/RaycastUtils.js +1 -1
- package/lib/engine-components/ui/RaycastUtils.js.map +1 -1
- package/lib/engine-components/ui/RectTransform.d.ts +2 -2
- package/lib/engine-components/ui/RectTransform.js +9 -6
- package/lib/engine-components/ui/RectTransform.js.map +1 -1
- package/lib/engine-components/ui/Symbols.d.ts +1 -0
- package/lib/engine-components/ui/Symbols.js +2 -0
- package/lib/engine-components/ui/Symbols.js.map +1 -0
- package/lib/engine-components/ui/Utils.js +1 -1
- package/lib/engine-components/ui/Utils.js.map +1 -1
- package/lib/engine-components/utils/EnvironmentScene.d.ts +1 -1
- package/lib/engine-components/utils/EnvironmentScene.js +1 -1
- package/lib/engine-components/utils/EnvironmentScene.js.map +1 -1
- package/package.json +3 -3
- package/plugins/common/buildinfo.js +1 -0
- package/plugins/common/cloud.js +2 -0
- package/plugins/common/license.js +174 -33
- package/plugins/types/userconfig.d.ts +5 -0
- package/plugins/vite/build-pipeline.js +4 -3
- package/plugins/vite/dependencies.js +17 -7
- package/plugins/vite/facebook-instant-games.js +7 -4
- package/plugins/vite/index.js +1 -1
- package/src/engine/codegen/register_types.ts +2 -0
- package/src/engine/engine_addressables.ts +3 -2
- package/src/engine/engine_components.ts +2 -1
- package/src/engine/engine_context.ts +36 -12
- package/src/engine/engine_element.ts +10 -9
- package/src/engine/engine_gameobject.ts +7 -0
- package/src/engine/engine_license.ts +116 -67
- package/src/engine/engine_networking_blob.ts +47 -26
- package/src/engine/engine_physics_rapier.ts +10 -12
- package/src/engine/engine_serialization_core.ts +6 -1
- package/src/engine/engine_utils_screenshot.ts +1 -1
- package/src/engine/js-extensions/RGBAColor.ts +50 -0
- package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +6 -6
- package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +6 -6
- package/src/engine/webcomponents/needle menu/needle-menu.ts +13 -2
- package/src/engine/xr/NeedleXRController.ts +2 -3
- package/src/engine/xr/NeedleXRSession.ts +12 -12
- package/src/engine-components/AudioSource.ts +8 -3
- package/src/engine-components/Camera.ts +34 -22
- package/src/engine-components/CameraUtils.ts +8 -16
- package/src/engine-components/GroundProjection.ts +1 -1
- package/src/engine-components/NeedleMenu.ts +1 -1
- package/src/engine-components/OrbitControls.ts +2 -1
- package/src/engine-components/ReflectionProbe.ts +15 -8
- package/src/engine-components/SceneSwitcher.ts +28 -11
- package/src/engine-components/SyncedTransform.ts +11 -6
- package/src/engine-components/codegen/components.ts +1 -0
- package/src/engine-components/export/usdz/extensions/USDZUI.ts +2 -2
- package/src/engine-components/ui/BaseUIComponent.ts +1 -1
- package/src/engine-components/ui/EventSystem.ts +1 -1
- package/src/engine-components/ui/Graphic.ts +1 -1
- package/src/engine-components/ui/RaycastUtils.ts +1 -1
- package/src/engine-components/ui/RectTransform.ts +10 -7
- package/src/engine-components/ui/Symbols.ts +2 -0
- package/src/engine-components/ui/Utils.ts +2 -1
- package/src/engine-components/utils/EnvironmentScene.ts +1 -1
|
@@ -408,7 +408,7 @@ export class Context implements IContext {
|
|
|
408
408
|
|
|
409
409
|
get isCreated() { return this._isCreated; }
|
|
410
410
|
|
|
411
|
-
private
|
|
411
|
+
private _needsUpdateSize: boolean = false;
|
|
412
412
|
private _isCreated: boolean = false;
|
|
413
413
|
private _isCreating: boolean = false;
|
|
414
414
|
private _isVisible: boolean = false;
|
|
@@ -446,11 +446,11 @@ export class Context implements IContext {
|
|
|
446
446
|
this.animations = new AnimationsRegistry(this);
|
|
447
447
|
|
|
448
448
|
|
|
449
|
-
const resizeCallback = () => this.
|
|
449
|
+
const resizeCallback = () => this._needsUpdateSize = true;
|
|
450
450
|
window.addEventListener('resize', resizeCallback);
|
|
451
451
|
this._disposeCallbacks.push(() => window.removeEventListener('resize', resizeCallback));
|
|
452
452
|
|
|
453
|
-
const resizeObserver = new ResizeObserver(_ => this.
|
|
453
|
+
const resizeObserver = new ResizeObserver(_ => this._needsUpdateSize = true);
|
|
454
454
|
resizeObserver.observe(this.domElement);
|
|
455
455
|
this._disposeCallbacks.push(() => resizeObserver.disconnect());
|
|
456
456
|
|
|
@@ -519,15 +519,30 @@ export class Context implements IContext {
|
|
|
519
519
|
|
|
520
520
|
|
|
521
521
|
/** will request a renderer size update the next render call (will call updateSize the next update) */
|
|
522
|
-
requestSizeUpdate() { this.
|
|
522
|
+
requestSizeUpdate() { this._needsUpdateSize = true; }
|
|
523
523
|
|
|
524
524
|
/** Clamps the renderer max resolution. If undefined the max resolution is not clamped. Default is undefined */
|
|
525
525
|
maxRenderResolution?: Vec2;
|
|
526
526
|
|
|
527
|
+
/** Control the renderer devicePixelRatio.
|
|
528
|
+
* **Options**
|
|
529
|
+
* - `auto` - Needle Engine automatically sets the pixel ratio to the current window.devicePixelRatio.
|
|
530
|
+
* - `manual` - Needle Engine will not change the renderer pixel ratio. You can set it manually.
|
|
531
|
+
* - `number` - Needle Engine will set the pixel ratio to the given number. The change will be applied to the renderer and the composer (if used) at the end of the current frame.
|
|
532
|
+
*/
|
|
533
|
+
get devicePixelRatio() { return this._devicePixelRatio; }
|
|
534
|
+
set devicePixelRatio(val: "auto" | "manual" | number) {
|
|
535
|
+
if (val !== this._devicePixelRatio) {
|
|
536
|
+
this._devicePixelRatio = val;
|
|
537
|
+
this._needsUpdateSize = true;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
private _devicePixelRatio: "auto" | "manual" | number = "auto";
|
|
541
|
+
|
|
527
542
|
/** update the renderer and canvas size */
|
|
528
543
|
updateSize(force: boolean = false) {
|
|
529
544
|
if (force || (!this.isManagedExternally && this.renderer.xr?.isPresenting === false)) {
|
|
530
|
-
this.
|
|
545
|
+
this._needsUpdateSize = false;
|
|
531
546
|
const scaleFactor = this.resolutionScaleFactor;
|
|
532
547
|
let width = this.domWidth * scaleFactor;
|
|
533
548
|
let height = this.domHeight * scaleFactor;
|
|
@@ -540,15 +555,24 @@ export class Context implements IContext {
|
|
|
540
555
|
const camera = this.mainCamera as PerspectiveCamera;
|
|
541
556
|
this.updateAspect(camera);
|
|
542
557
|
this.renderer.setSize(width, height, true);
|
|
543
|
-
this.renderer.setPixelRatio(window.devicePixelRatio);
|
|
544
558
|
// avoid setting pixel values here since this can cause pingpong updates
|
|
545
559
|
// e.g. when system scale is set to 125%
|
|
546
560
|
// https://github.com/needle-tools/needle-engine-support/issues/69
|
|
547
561
|
this.renderer.domElement.style.width = "100%";
|
|
548
562
|
this.renderer.domElement.style.height = "100%";
|
|
563
|
+
|
|
564
|
+
const devicePixelRatio = typeof this.devicePixelRatio === "number"
|
|
565
|
+
? this.devicePixelRatio
|
|
566
|
+
: this.devicePixelRatio === "auto"
|
|
567
|
+
? window.devicePixelRatio
|
|
568
|
+
: undefined;
|
|
569
|
+
if (devicePixelRatio !== undefined) {
|
|
570
|
+
this.renderer.setPixelRatio(devicePixelRatio);
|
|
571
|
+
}
|
|
572
|
+
|
|
549
573
|
if (this.composer) {
|
|
550
574
|
this.composer.setSize?.call(this.composer, width, height);
|
|
551
|
-
if ("setPixelRatio" in this.composer && typeof this.composer.setPixelRatio === "function")
|
|
575
|
+
if (devicePixelRatio !== undefined && "setPixelRatio" in this.composer && typeof this.composer.setPixelRatio === "function")
|
|
552
576
|
this.composer.setPixelRatio?.call(this.composer, window.devicePixelRatio);
|
|
553
577
|
}
|
|
554
578
|
}
|
|
@@ -1016,7 +1040,7 @@ export class Context implements IContext {
|
|
|
1016
1040
|
// this.composer.setSize(this.domWidth, this.domHeight);
|
|
1017
1041
|
}
|
|
1018
1042
|
|
|
1019
|
-
this.
|
|
1043
|
+
this._needsUpdateSize = true;
|
|
1020
1044
|
|
|
1021
1045
|
if (this._stats) {
|
|
1022
1046
|
this._stats.showPanel(0);
|
|
@@ -1186,11 +1210,11 @@ export class Context implements IContext {
|
|
|
1186
1210
|
if (frame === undefined) frame = null;
|
|
1187
1211
|
if (isDevEnvironment() || debug || looputils.hasNewScripts()) {
|
|
1188
1212
|
try {
|
|
1189
|
-
performance.mark('update.start');
|
|
1213
|
+
//performance.mark('update.start');
|
|
1190
1214
|
this.internalStep(timestamp, frame);
|
|
1191
1215
|
this._renderlooperrors = 0;
|
|
1192
|
-
performance.mark('update.end');
|
|
1193
|
-
performance.measure('NE Frame', 'update.start', 'update.end');
|
|
1216
|
+
//performance.mark('update.end');
|
|
1217
|
+
//performance.measure('NE Frame', 'update.start', 'update.end');
|
|
1194
1218
|
}
|
|
1195
1219
|
catch (err) {
|
|
1196
1220
|
this._renderlooperrors += 1;
|
|
@@ -1361,7 +1385,7 @@ export class Context implements IContext {
|
|
|
1361
1385
|
this.executeCoroutines(FrameEvent.OnBeforeRender);
|
|
1362
1386
|
invokeLifecycleFunctions(this, FrameEvent.OnBeforeRender);
|
|
1363
1387
|
|
|
1364
|
-
if (this.
|
|
1388
|
+
if (this._needsUpdateSize)
|
|
1365
1389
|
this.updateSize();
|
|
1366
1390
|
|
|
1367
1391
|
if (this.pre_render_callbacks) {
|
|
@@ -12,6 +12,7 @@ import { NeedleLoader } from "./engine_scenetools.js";
|
|
|
12
12
|
import { Context, ContextCreateArgs } from "./engine_setup.js";
|
|
13
13
|
import { type INeedleEngineComponent, type LoadedModel } from "./engine_types.js";
|
|
14
14
|
import { getParam } from "./engine_utils.js";
|
|
15
|
+
import { RGBAColor } from "./js-extensions/RGBAColor.js";
|
|
15
16
|
import { ensureFonts } from "./webcomponents/fonts.js";
|
|
16
17
|
|
|
17
18
|
//
|
|
@@ -177,6 +178,9 @@ export class NeedleEngineHTMLElement extends HTMLElement implements INeedleEngin
|
|
|
177
178
|
-webkit-touch-callout: none;
|
|
178
179
|
-webkit-user-drag: none;
|
|
179
180
|
-webkit-user-modify: none;
|
|
181
|
+
|
|
182
|
+
left: 0;
|
|
183
|
+
top: 0;
|
|
180
184
|
}
|
|
181
185
|
:host .content {
|
|
182
186
|
position: absolute;
|
|
@@ -517,7 +521,7 @@ export class NeedleEngineHTMLElement extends HTMLElement implements INeedleEngin
|
|
|
517
521
|
|
|
518
522
|
private applyAttributes() {
|
|
519
523
|
// set tonemapping if configured
|
|
520
|
-
if (this._context
|
|
524
|
+
if (this._context?.renderer) {
|
|
521
525
|
const attribute = this.getAttribute("tonemapping") || this.getAttribute("tone-mapping") as TonemappingAttributeOptions | null | undefined;
|
|
522
526
|
switch (attribute?.toLowerCase()) {
|
|
523
527
|
case "none":
|
|
@@ -555,15 +559,12 @@ export class NeedleEngineHTMLElement extends HTMLElement implements INeedleEngin
|
|
|
555
559
|
}
|
|
556
560
|
|
|
557
561
|
const backgroundColor = this.getAttribute("background-color");
|
|
558
|
-
if (this._context) {
|
|
562
|
+
if (this._context?.renderer) {
|
|
559
563
|
if (typeof backgroundColor === "string" && backgroundColor.length > 0) {
|
|
560
|
-
const
|
|
561
|
-
if (
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
else {
|
|
565
|
-
this._context.scene.background = new Color(backgroundColor);
|
|
566
|
-
}
|
|
564
|
+
const rgbaColor = RGBAColor.fromColorRepresentation(backgroundColor);
|
|
565
|
+
if(debug) console.debug("<needle-engine> background-color changed, str:", backgroundColor, "→", rgbaColor)
|
|
566
|
+
this._context.renderer.setClearColor(rgbaColor, rgbaColor.alpha);
|
|
567
|
+
this.context.scene.background = null;
|
|
567
568
|
}
|
|
568
569
|
}
|
|
569
570
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Bone, Object3D, Quaternion, SkinnedMesh, Vector3 } from "three";
|
|
2
2
|
|
|
3
|
+
import { $shadowDomOwner } from "../engine-components/ui/Symbols.js";
|
|
3
4
|
import { type AssetReference } from "./engine_addressables.js";
|
|
4
5
|
import { __internalNotifyObjectDestroyed as __internalRemoveReferences, disposeObjectResources } from "./engine_assetdatabase.js";
|
|
5
6
|
import { ComponentLifecycleEvents } from "./engine_components_internal.js";
|
|
@@ -380,6 +381,12 @@ function internalInstantiate(
|
|
|
380
381
|
)
|
|
381
382
|
: GameObject | Object3D | null {
|
|
382
383
|
if (!instance) return null;
|
|
384
|
+
|
|
385
|
+
// Don't clone UI shadow objects
|
|
386
|
+
if (instance[$shadowDomOwner]) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
|
|
383
390
|
// prepare, remove things that dont work out of the box
|
|
384
391
|
// e.g. user data we want to manually clone
|
|
385
392
|
// also children throw errors (e.g. recursive toJson with nested meshes)
|
|
@@ -11,7 +11,7 @@ const _licenseCheckResultChangedCallbacks: ((result: boolean) => void)[] = [];
|
|
|
11
11
|
// This is modified by a bundler (e.g. vite)
|
|
12
12
|
// Do not edit manually
|
|
13
13
|
let NEEDLE_ENGINE_LICENSE_TYPE: string = "basic";
|
|
14
|
-
if (debug)
|
|
14
|
+
if (debug)
|
|
15
15
|
console.log("License Type: " + NEEDLE_ENGINE_LICENSE_TYPE)
|
|
16
16
|
|
|
17
17
|
/** @internal */
|
|
@@ -33,15 +33,24 @@ export function hasIndieLicense() {
|
|
|
33
33
|
return false;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/** @internal */
|
|
37
|
+
export function hasEduLicense() {
|
|
38
|
+
switch (NEEDLE_ENGINE_LICENSE_TYPE) {
|
|
39
|
+
case "edu":
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
/** @internal */
|
|
37
46
|
export function hasCommercialLicense() {
|
|
38
|
-
return hasProLicense() || hasIndieLicense();
|
|
47
|
+
return hasProLicense() || hasIndieLicense() || hasEduLicense();
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
|
|
42
51
|
/** @internal */
|
|
43
52
|
export function onLicenseCheckResultChanged(cb: (result: boolean) => void) {
|
|
44
|
-
if (hasProLicense() || hasIndieLicense())
|
|
53
|
+
if (hasProLicense() || hasIndieLicense() || hasEduLicense())
|
|
45
54
|
return cb(true);
|
|
46
55
|
_licenseCheckResultChangedCallbacks.push(cb);
|
|
47
56
|
}
|
|
@@ -85,14 +94,17 @@ async function checkLicense() {
|
|
|
85
94
|
invokeLicenseCheckResultChanged(true);
|
|
86
95
|
}
|
|
87
96
|
else if (res?.status === 403) {
|
|
97
|
+
invokeLicenseCheckResultChanged(false);
|
|
88
98
|
applicationIsForbidden = true;
|
|
89
99
|
applicationForbiddenText = await res.text();
|
|
90
100
|
}
|
|
91
101
|
else {
|
|
102
|
+
invokeLicenseCheckResultChanged(false);
|
|
92
103
|
if (debug) console.log("License check failed with status " + res?.status);
|
|
93
104
|
}
|
|
94
105
|
}
|
|
95
106
|
catch (err) {
|
|
107
|
+
invokeLicenseCheckResultChanged(false);
|
|
96
108
|
if (debug) console.error("License check failed", err);
|
|
97
109
|
}
|
|
98
110
|
}
|
|
@@ -167,7 +179,9 @@ async function handleForbidden(ctx: IContext) {
|
|
|
167
179
|
|
|
168
180
|
async function showLicenseInfo(ctx: IContext) {
|
|
169
181
|
try {
|
|
170
|
-
if (
|
|
182
|
+
if (!hasProLicense() && !hasIndieLicense()) {
|
|
183
|
+
return onNonCommercialVersionDetected(ctx);
|
|
184
|
+
}
|
|
171
185
|
}
|
|
172
186
|
catch (err) {
|
|
173
187
|
if (debug) console.log("License check failed", err)
|
|
@@ -179,85 +193,120 @@ async function showLicenseInfo(ctx: IContext) {
|
|
|
179
193
|
|
|
180
194
|
|
|
181
195
|
async function onNonCommercialVersionDetected(ctx: IContext) {
|
|
196
|
+
|
|
182
197
|
// if the engine loads faster than the license check, we need to capture the ready event here
|
|
183
198
|
let isReady = false;
|
|
184
199
|
ctx.domElement.addEventListener("ready", () => isReady = true);
|
|
185
200
|
|
|
186
201
|
await runtimeLicenseCheckPromise?.catch(() => { });
|
|
187
|
-
|
|
188
|
-
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
if (hasProLicense() || hasIndieLicense()) return;
|
|
205
|
+
if (hasCommercialLicense() === false) logNonCommercialUse();
|
|
189
206
|
|
|
190
207
|
// check if the engine is already ready (meaning has finished loading)
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
208
|
+
if (isReady) {
|
|
209
|
+
insertNonCommercialUseHint(ctx);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
ctx.domElement.addEventListener("ready", () => {
|
|
213
|
+
insertNonCommercialUseHint(ctx);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
199
216
|
}
|
|
200
217
|
|
|
201
218
|
// const licenseElementIdentifier = "needle-license-element";
|
|
202
219
|
// const licenseDuration = 10000;
|
|
203
220
|
// const licenseDelay = 1200;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
221
|
+
function insertNonCommercialUseHint(ctx: IContext) {
|
|
222
|
+
|
|
223
|
+
const style = `
|
|
224
|
+
position: relative;
|
|
225
|
+
display: block;
|
|
226
|
+
background-size: 20px;
|
|
227
|
+
background-position: 10px 5px;
|
|
228
|
+
background-repeat:no-repeat;
|
|
229
|
+
background-image:url('${base64Logo}');
|
|
230
|
+
background-max-size: 40px;
|
|
231
|
+
padding: 10px;
|
|
232
|
+
padding-left: 30px;
|
|
233
|
+
`;
|
|
234
|
+
if (NEEDLE_ENGINE_LICENSE_TYPE === "edu") {
|
|
235
|
+
console.log("%c " + "Supported by Needle for Education – https://needle.tools", style);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
// if the user has a basic license we already show the logo in the menu and log a license message
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const banner = document.createElement("div");
|
|
243
|
+
banner.className = "needle-non-commercial-use";
|
|
244
|
+
banner.innerHTML = "Made with Needle for Education";
|
|
245
|
+
ctx.domElement.shadowRoot?.appendChild(banner);
|
|
246
|
+
let bannerStyle = `
|
|
247
|
+
position: absolute;
|
|
248
|
+
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
249
|
+
font-size: 12px;
|
|
250
|
+
color: rgb(100, 100, 100);
|
|
251
|
+
/*mix-blend-mode: difference;*/
|
|
252
|
+
background-color: transparent;
|
|
253
|
+
z-index: 10000;
|
|
254
|
+
|
|
255
|
+
cursor: pointer;
|
|
256
|
+
user-select: none;
|
|
257
|
+
opacity: 0;
|
|
258
|
+
|
|
259
|
+
bottom: 6px;
|
|
260
|
+
right: 12px;
|
|
261
|
+
transform: translateY(0px);
|
|
262
|
+
transition: all .5s ease-in-out 1s;
|
|
263
|
+
`;
|
|
264
|
+
banner.style.cssText = bannerStyle;
|
|
265
|
+
banner.addEventListener("click", () => { window.open("https://needle.tools", "_blank") });
|
|
266
|
+
let expectedBannerStyle = banner.style.cssText;
|
|
267
|
+
setTimeout(() => {
|
|
268
|
+
bannerStyle = bannerStyle.replace("opacity: 0", "opacity: 1");
|
|
269
|
+
bannerStyle = bannerStyle.replace("transform: translateY(10px)", "transform: translateY(0)");
|
|
270
|
+
banner.style.cssText = bannerStyle;
|
|
271
|
+
expectedBannerStyle = banner.style.cssText;
|
|
272
|
+
}, 100);
|
|
273
|
+
|
|
274
|
+
// ensure the banner is always visible
|
|
275
|
+
const interval = setInterval(() => {
|
|
276
|
+
const parent = ctx.domElement.shadowRoot || ctx.domElement;
|
|
277
|
+
if (banner.parentNode !== parent) {
|
|
278
|
+
parent.appendChild(banner);
|
|
279
|
+
}
|
|
280
|
+
if (expectedBannerStyle != banner.style.cssText) {
|
|
281
|
+
banner.style.cssText = bannerStyle;
|
|
282
|
+
expectedBannerStyle = banner.style.cssText;
|
|
283
|
+
}
|
|
284
|
+
}, 1000);
|
|
285
|
+
|
|
286
|
+
if (hasEduLicense()) {
|
|
287
|
+
const removeDelay = 20_000;
|
|
288
|
+
setTimeout(() => {
|
|
289
|
+
clearInterval(interval);
|
|
290
|
+
banner?.remove();
|
|
291
|
+
// show the logo every x minutes
|
|
292
|
+
const intervalInMinutes = 5;
|
|
293
|
+
setTimeout(() => {
|
|
294
|
+
if (ctx.domElement.parentNode)
|
|
295
|
+
insertNonCommercialUseHint(ctx);
|
|
296
|
+
}, 1000 * 60 * intervalInMinutes)
|
|
297
|
+
}, removeDelay);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
const base64Logo = "data:image/webp;base64,UklGRrABAABXRUJQVlA4WAoAAAAQAAAAHwAAHwAAQUxQSKEAAAARN6CmbSM4WR7vdARON11EBDq3fLiNbVtVzpMCPlKAEzsx0Y/x+Ovuv4dn0EFE/ydAvz6YggXzgh5sVgXM/zOC/4sii7qgGvB5N7hmuQYwkvazWAu1JPW41FXSHq6pnaQWvqYH18Fc0j1hO/BFTtIeSBlJi5w6qIIO7IOrwhFsB2Yxukif0FTRLpXswHR8MxbslKe9VZsn/Ub5C7YFOpqSTABWUDgg6AAAAFAGAJ0BKiAAIAA+7VyoTqmkpCI3+qgBMB2JbACdMt69DwMIQBLhkTO6XwY00UEDK6cNIDnuNibPf0EgAP7Y1myuiQHLDsF/0h5unrGh6WAbv7aegg2ZMd3uRKfT/3SJztcaujYfTvMXspfCTmYcoO6a+vhC3ss4M8uM58t4siiu59I4aOl59e9Sr6xoxYlHf2v+NnBNpJYeJf8jABQAId/PXuBkLEFkiCucgSGEcfhvajql/j3reCGl0M5/9gQWy7ayNPs+wlvIxFnNfSlfuND4CZOCyxOHhRqOmHN4ULHo3tCSrUNvgAA=";
|
|
304
|
+
|
|
255
305
|
let lastLogTime = 0;
|
|
256
306
|
async function logNonCommercialUse(_logo?: string) {
|
|
257
307
|
const now = Date.now();
|
|
258
308
|
if (now - lastLogTime < 2000) return;
|
|
259
309
|
lastLogTime = now;
|
|
260
|
-
const logo = "data:image/webp;base64,UklGRrABAABXRUJQVlA4WAoAAAAQAAAAHwAAHwAAQUxQSKEAAAARN6CmbSM4WR7vdARON11EBDq3fLiNbVtVzpMCPlKAEzsx0Y/x+Ovuv4dn0EFE/ydAvz6YggXzgh5sVgXM/zOC/4sii7qgGvB5N7hmuQYwkvazWAu1JPW41FXSHq6pnaQWvqYH18Fc0j1hO/BFTtIeSBlJi5w6qIIO7IOrwhFsB2Yxukif0FTRLpXswHR8MxbslKe9VZsn/Ub5C7YFOpqSTABWUDgg6AAAAFAGAJ0BKiAAIAA+7VyoTqmkpCI3+qgBMB2JbACdMt69DwMIQBLhkTO6XwY00UEDK6cNIDnuNibPf0EgAP7Y1myuiQHLDsF/0h5unrGh6WAbv7aegg2ZMd3uRKfT/3SJztcaujYfTvMXspfCTmYcoO6a+vhC3ss4M8uM58t4siiu59I4aOl59e9Sr6xoxYlHf2v+NnBNpJYeJf8jABQAId/PXuBkLEFkiCucgSGEcfhvajql/j3reCGl0M5/9gQWy7ayNPs+wlvIxFnNfSlfuND4CZOCyxOHhRqOmHN4ULHo3tCSrUNvgAA=";
|
|
261
310
|
const style = `
|
|
262
311
|
position: relative;
|
|
263
312
|
display: block;
|
|
@@ -265,7 +314,7 @@ async function logNonCommercialUse(_logo?: string) {
|
|
|
265
314
|
background-size: 20px;
|
|
266
315
|
background-position: 10px 5px;
|
|
267
316
|
background-repeat:no-repeat;
|
|
268
|
-
background-image:url('${
|
|
317
|
+
background-image:url('${base64Logo}');
|
|
269
318
|
background-max-size: 40px;
|
|
270
319
|
margin-bottom: 5px;
|
|
271
320
|
margin-top: .3em;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// workaround for this is not a function when deployed
|
|
2
2
|
// see https://github.com/pvorb/node-md5/issues/52
|
|
3
3
|
import md5 from "md5";
|
|
4
|
+
import { FileLoader } from "three";
|
|
4
5
|
|
|
5
6
|
import { showBalloonWarning } from "./debug/index.js";
|
|
6
7
|
import { hasCommercialLicense } from "./engine_license.js";
|
|
@@ -219,35 +220,55 @@ export namespace BlobStorage {
|
|
|
219
220
|
}
|
|
220
221
|
|
|
221
222
|
export async function download(url: string, progressCallback?: (prog: ProgressEvent) => void): Promise<Uint8Array | null> {
|
|
222
|
-
const response = await fetch(url);
|
|
223
|
-
|
|
224
|
-
const reader = response.body?.getReader();
|
|
225
|
-
const contentLength = response.headers.get('Content-Length');
|
|
226
|
-
const total = contentLength ? parseInt(contentLength) : 0;
|
|
227
|
-
|
|
228
|
-
if (!reader) return null;
|
|
229
|
-
|
|
230
|
-
let received: number = 0;
|
|
231
|
-
const chunks: Uint8Array[] = [];
|
|
232
|
-
while (true) {
|
|
233
|
-
const { done, value } = await reader.read();
|
|
234
|
-
if (value) {
|
|
235
|
-
chunks.push(value);
|
|
236
|
-
received += value.length;
|
|
237
|
-
progressCallback?.call(null, new ProgressEvent('progress', { loaded: received, total: total }));
|
|
238
|
-
}
|
|
239
223
|
|
|
240
|
-
|
|
241
|
-
|
|
224
|
+
// Using a FileLoader here instead of manually fetching so we're able to use the three.js cache system
|
|
225
|
+
const loader = new FileLoader();
|
|
226
|
+
loader.setResponseType('arraybuffer');
|
|
227
|
+
// loader.setRequestHeader( this.requestHeader );
|
|
228
|
+
// loader.setWithCredentials( this.withCredentials );
|
|
229
|
+
const res = await loader.loadAsync(url, prog => {
|
|
230
|
+
if (progressCallback) {
|
|
231
|
+
progressCallback.call(null, prog);
|
|
242
232
|
}
|
|
233
|
+
});
|
|
234
|
+
if (!(res instanceof ArrayBuffer)) {
|
|
235
|
+
console.error("Download failed, no arraybuffer returned");
|
|
236
|
+
return null;
|
|
243
237
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
238
|
+
return new Uint8Array(res);
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
// Old solution: this didn't re-use the three.js loading cache. Using a FileLoader the GLTFLoader under the hood is smart enough to NOT start a new download request
|
|
242
|
+
|
|
243
|
+
// const response = await fetch(url);
|
|
244
|
+
|
|
245
|
+
// const reader = response.body?.getReader();
|
|
246
|
+
// const contentLength = response.headers.get('Content-Length');
|
|
247
|
+
// const total = contentLength ? parseInt(contentLength) : 0;
|
|
248
|
+
|
|
249
|
+
// if (!reader) return null;
|
|
250
|
+
|
|
251
|
+
// let received: number = 0;
|
|
252
|
+
// const chunks: Uint8Array[] = [];
|
|
253
|
+
// while (true) {
|
|
254
|
+
// const { done, value } = await reader.read();
|
|
255
|
+
// if (value) {
|
|
256
|
+
// chunks.push(value);
|
|
257
|
+
// received += value.length;
|
|
258
|
+
// progressCallback?.call(null, new ProgressEvent('progress', { loaded: received, total: total }));
|
|
259
|
+
// }
|
|
260
|
+
|
|
261
|
+
// if (done) {
|
|
262
|
+
// break;
|
|
263
|
+
// }
|
|
264
|
+
// }
|
|
265
|
+
// const final = new Uint8Array(received);
|
|
266
|
+
// let position = 0;
|
|
267
|
+
// for (const chunk of chunks) {
|
|
268
|
+
// final.set(chunk, position);
|
|
269
|
+
// position += chunk.length;
|
|
270
|
+
// }
|
|
271
|
+
// return final;
|
|
251
272
|
}
|
|
252
273
|
}
|
|
253
274
|
|
|
@@ -598,17 +598,15 @@ export class RapierPhysics implements IPhysicsEngine {
|
|
|
598
598
|
scale.multiplyScalar(0.5);
|
|
599
599
|
|
|
600
600
|
// prevent negative scale
|
|
601
|
-
if (scale.x < 0)
|
|
602
|
-
|
|
603
|
-
if (scale.
|
|
604
|
-
scale.y = Math.abs(scale.y);
|
|
605
|
-
if (scale.z < 0)
|
|
606
|
-
scale.z = Math.abs(scale.z);
|
|
601
|
+
if (scale.x < 0) scale.x = Math.abs(scale.x);
|
|
602
|
+
if (scale.y < 0) scale.y = Math.abs(scale.y);
|
|
603
|
+
if (scale.z < 0) scale.z = Math.abs(scale.z);
|
|
607
604
|
|
|
608
605
|
// prevent zero scale - seems normals are flipped otherwise
|
|
609
|
-
|
|
610
|
-
if (scale.
|
|
611
|
-
if (scale.
|
|
606
|
+
const minSize = 0.0000001;
|
|
607
|
+
if (scale.x < minSize) scale.x = minSize;
|
|
608
|
+
if (scale.y < minSize) scale.y = minSize;
|
|
609
|
+
if (scale.z < minSize) scale.z = minSize;
|
|
612
610
|
|
|
613
611
|
const desc = MODULES.RAPIER_PHYSICS.MODULE.ColliderDesc.cuboid(scale.x, scale.y, scale.z);
|
|
614
612
|
// const objectLayerMask = collider.gameObject.layers.mask;
|
|
@@ -994,9 +992,9 @@ export class RapierPhysics implements IPhysicsEngine {
|
|
|
994
992
|
const sc = col as IBoxCollider;
|
|
995
993
|
const obj = col.gameObject;
|
|
996
994
|
const scale = getWorldScale(obj, this._tempPosition);
|
|
997
|
-
const newX = sc.size.x * 0.5 * scale.x;
|
|
998
|
-
const newY = sc.size.y * 0.5 * scale.y;
|
|
999
|
-
const newZ = sc.size.z * 0.5 * scale.z;
|
|
995
|
+
const newX = Math.abs(sc.size.x * 0.5 * scale.x);
|
|
996
|
+
const newY = Math.abs(sc.size.y * 0.5 * scale.y);
|
|
997
|
+
const newZ = Math.abs(sc.size.z * 0.5 * scale.z);
|
|
1000
998
|
sizeHasChanged = cuboid.halfExtents.x !== newX || cuboid.halfExtents.y !== newY || cuboid.halfExtents.z !== newZ;
|
|
1001
999
|
cuboid.halfExtents.x = newX;
|
|
1002
1000
|
cuboid.halfExtents.y = newY;
|
|
@@ -517,11 +517,14 @@ function deserializeObjectWithType(data: any, typeOrConstructor: ConstructorConc
|
|
|
517
517
|
// then we dont need to do anything else
|
|
518
518
|
if (!typeIsFunction && currentValue) {
|
|
519
519
|
if (currentValue instanceof Material) return currentValue;
|
|
520
|
-
if (currentValue instanceof Texture) return currentValue;
|
|
521
520
|
if (currentValue instanceof Mesh) return currentValue;
|
|
522
521
|
if (currentValue instanceof BufferGeometry) return currentValue;
|
|
523
522
|
if (currentValue instanceof AnimationClip) return currentValue;
|
|
523
|
+
// We need to call the RenderTexture deserializer so we can not just return here:
|
|
524
|
+
// if (currentValue instanceof Texture) return currentValue;
|
|
525
|
+
// So this has now been moved down below the deserializer code
|
|
524
526
|
}
|
|
527
|
+
|
|
525
528
|
// Removed this line because it prevents assigning serialized values to existing instances for e.g. EventList
|
|
526
529
|
// https://linear.app/needle/issue/NE-5350
|
|
527
530
|
// if (!typeIsFunction && currentValue instanceof type) return currentValue;
|
|
@@ -594,6 +597,8 @@ function deserializeObjectWithType(data: any, typeOrConstructor: ConstructorConc
|
|
|
594
597
|
|
|
595
598
|
// console.log(type.prototype.get("$serializedTypes"));
|
|
596
599
|
|
|
600
|
+
// We check textures AFTER running deserializers because some textures might need to be converted to a RenderTexture
|
|
601
|
+
if (currentValue instanceof Texture) return currentValue;
|
|
597
602
|
|
|
598
603
|
let instance: any = undefined;
|
|
599
604
|
if (data && (data.isMaterial || data.isTexture || data.isObject3D || data instanceof AnimationClip)) {
|
|
@@ -175,7 +175,7 @@ export function screenshot2(opts: ScreenshotOptionsDataUrl | ScreenshotOptionsTe
|
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
const renderer = context.renderer;
|
|
178
|
-
const isXRScreenshot = renderer.xr.enabled;
|
|
178
|
+
const isXRScreenshot = renderer.xr.enabled && renderer.xr.isPresenting;
|
|
179
179
|
|
|
180
180
|
|
|
181
181
|
// Perform XR screenshot in onBeforeRender (after the screenshot we want to render the original camera view)
|
|
@@ -73,4 +73,54 @@ export class RGBAColor extends Color {
|
|
|
73
73
|
this.alpha = array[offset + 3];
|
|
74
74
|
return super.fromArray(array, offset);
|
|
75
75
|
}
|
|
76
|
+
|
|
77
|
+
static fromColorRepresentation(col: ColorRepresentation) {
|
|
78
|
+
|
|
79
|
+
if (typeof col === "string") {
|
|
80
|
+
if (col.trim() === "transparent") {
|
|
81
|
+
return new RGBAColor(0, 0, 0, 0);
|
|
82
|
+
}
|
|
83
|
+
// handle hex colors with alpha
|
|
84
|
+
if (col.startsWith("#") && col.length === 9) {
|
|
85
|
+
const hex = parseInt(col.slice(1, 9), 16);
|
|
86
|
+
const r = (hex >> 24) & 0xff;
|
|
87
|
+
const g = (hex >> 16) & 0xff;
|
|
88
|
+
const b = (hex >> 8) & 0xff;
|
|
89
|
+
const a = (hex >> 0) & 0xff;
|
|
90
|
+
return new RGBAColor(r / 255, g / 255, b / 255, a / 255);
|
|
91
|
+
}
|
|
92
|
+
// handle hex colors
|
|
93
|
+
else if (col.startsWith("#")) {
|
|
94
|
+
const hex = parseInt(col.slice(1), 16);
|
|
95
|
+
const r = (hex >> 16) & 0xff;
|
|
96
|
+
const g = (hex >> 8) & 0xff;
|
|
97
|
+
const b = (hex >> 0) & 0xff;
|
|
98
|
+
return new RGBAColor(r / 255, g / 255, b / 255, 1);
|
|
99
|
+
}
|
|
100
|
+
// handle rgba string
|
|
101
|
+
else if (col.startsWith("rgba")) {
|
|
102
|
+
const rgba = col.slice(5, -1).split(",").map(Number);
|
|
103
|
+
return new RGBAColor(rgba[0] / 255, rgba[1] / 255, rgba[2] / 255, rgba[3]);
|
|
104
|
+
}
|
|
105
|
+
// handle rgb string
|
|
106
|
+
else if (col.startsWith("rgb")) {
|
|
107
|
+
const rgb = col.slice(4, -1).split(",").map(Number);
|
|
108
|
+
return new RGBAColor(rgb[0] / 255, rgb[1] / 255, rgb[2] / 255, 1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else if (Array.isArray(col)) {
|
|
112
|
+
// handle rgba colors
|
|
113
|
+
if (col.length === 4) {
|
|
114
|
+
return new RGBAColor(col[0], col[1], col[2], col[3]);
|
|
115
|
+
}
|
|
116
|
+
// handle rgb colors
|
|
117
|
+
else if (col.length === 3) {
|
|
118
|
+
return new RGBAColor(col[0], col[1], col[2], 1);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
console.error("Invalid color array length. Expected 3 or 4, got " + col.length);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return new RGBAColor(col);
|
|
125
|
+
}
|
|
76
126
|
}
|