@needle-tools/engine 4.14.0 → 4.15.0-next.f391a30
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 +8 -0
- package/components.needle.json +1 -1
- package/dist/{gltf-progressive-BttGBXw6.umd.cjs → gltf-progressive-CMwJPwEt.umd.cjs} +1 -1
- package/dist/{gltf-progressive-Bm_6aEi4.js → gltf-progressive-CTlvpS3A.js} +1 -1
- package/dist/{gltf-progressive-T5WKTux5.min.js → gltf-progressive-DYL3SLVb.min.js} +1 -1
- package/dist/materialx-4jJLLe9Q.js +4174 -0
- package/dist/materialx-Bt9FHwco.min.js +158 -0
- package/dist/materialx-NDD0y4JY.umd.cjs +158 -0
- package/dist/{needle-engine.bundle-COL2Bar3.umd.cjs → needle-engine.bundle-C1BFRZDF.umd.cjs} +150 -140
- package/dist/{needle-engine.bundle-Z_gAD7Kg.js → needle-engine.bundle-DB4kLWO_.js} +6651 -6400
- package/dist/{needle-engine.bundle-NolzHLqO.min.js → needle-engine.bundle-DsTdfmeb.min.js} +151 -141
- package/dist/needle-engine.d.ts +345 -88
- package/dist/needle-engine.js +322 -322
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-06AXuvdv.min.js → postprocessing-BN-f4viE.min.js} +1 -1
- package/dist/{postprocessing-CPDcA21P.umd.cjs → postprocessing-DYmYOVm4.umd.cjs} +1 -1
- package/dist/{postprocessing-CI2x8Cln.js → postprocessing-De9ZpJrk.js} +1 -1
- package/dist/{three-examples-BMmNgNCN.umd.cjs → three-examples-BHqRVpO_.umd.cjs} +12 -12
- package/dist/{three-examples-CMYCd5nH.js → three-examples-C0ZCCA_K.js} +182 -192
- package/dist/{three-examples-CQl1fFZp.min.js → three-examples-DmTY8tGr.min.js} +14 -14
- package/lib/engine/api.d.ts +0 -2
- package/lib/engine/api.js +0 -2
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/debug/debug.js +1 -1
- package/lib/engine/debug/debug.js.map +1 -1
- package/lib/engine/debug/debug_spatial_console.js +1 -1
- package/lib/engine/debug/debug_spatial_console.js.map +1 -1
- package/lib/engine/engine_accessibility.d.ts +77 -0
- package/lib/engine/engine_accessibility.js +162 -0
- package/lib/engine/engine_accessibility.js.map +1 -0
- package/lib/engine/engine_context.d.ts +2 -0
- package/lib/engine/engine_context.js +8 -1
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_create_objects.js +1 -1
- package/lib/engine/engine_create_objects.js.map +1 -1
- package/lib/engine/engine_gizmos.js +1 -1
- package/lib/engine/engine_gizmos.js.map +1 -1
- package/lib/engine/engine_license.js +7 -2
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_materialpropertyblock.d.ts +90 -4
- package/lib/engine/engine_materialpropertyblock.js +97 -7
- package/lib/engine/engine_materialpropertyblock.js.map +1 -1
- package/lib/engine/engine_math.d.ts +34 -1
- package/lib/engine/engine_math.js +34 -1
- package/lib/engine/engine_math.js.map +1 -1
- package/lib/engine/engine_networking.js +1 -1
- package/lib/engine/engine_networking.js.map +1 -1
- package/lib/engine/engine_types.d.ts +2 -0
- package/lib/engine/engine_types.js +2 -0
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine/engine_utils.js +2 -2
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/export/gltf/EXT_mesh_gpu_instancing_exporter.js.map +1 -0
- package/lib/engine/export/gltf/index.js +1 -1
- package/lib/engine/export/gltf/index.js.map +1 -1
- package/lib/engine/webcomponents/icons.js +3 -0
- package/lib/engine/webcomponents/icons.js.map +1 -1
- package/lib/engine/webcomponents/logo-element.d.ts +7 -3
- package/lib/engine/webcomponents/logo-element.js +21 -1
- package/lib/engine/webcomponents/logo-element.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +10 -7
- package/lib/engine/webcomponents/needle menu/needle-menu.js +14 -4
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-button.d.ts +37 -11
- package/lib/engine/webcomponents/needle-button.js +42 -11
- package/lib/engine/webcomponents/needle-button.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.ar-overlay.js +10 -1
- package/lib/engine/webcomponents/needle-engine.ar-overlay.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +13 -2
- package/lib/engine/webcomponents/needle-engine.js +23 -3
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine-components/Component.d.ts +1 -2
- package/lib/engine-components/Component.js +1 -3
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/DragControls.d.ts +1 -0
- package/lib/engine-components/DragControls.js +21 -0
- package/lib/engine-components/DragControls.js.map +1 -1
- package/lib/engine-components/NeedleMenu.d.ts +2 -0
- package/lib/engine-components/NeedleMenu.js +2 -0
- package/lib/engine-components/NeedleMenu.js.map +1 -1
- package/lib/engine-components/Networking.d.ts +28 -3
- package/lib/engine-components/Networking.js +28 -3
- package/lib/engine-components/Networking.js.map +1 -1
- package/lib/engine-components/ReflectionProbe.d.ts +25 -2
- package/lib/engine-components/ReflectionProbe.js +46 -2
- package/lib/engine-components/ReflectionProbe.js.map +1 -1
- package/lib/engine-components/Skybox.js +4 -2
- package/lib/engine-components/Skybox.js.map +1 -1
- package/lib/engine-components/export/gltf/GltfExport.js +1 -1
- package/lib/engine-components/export/gltf/GltfExport.js.map +1 -1
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +2 -2
- package/lib/engine-components/export/usdz/USDZExporter.js +1 -1
- package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.d.ts +15 -0
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +77 -0
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/behavior/PhysicsExtension.js +2 -2
- package/lib/engine-components/export/usdz/extensions/behavior/PhysicsExtension.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +1 -1
- package/lib/engine-components/ui/Button.d.ts +1 -0
- package/lib/engine-components/ui/Button.js +11 -0
- package/lib/engine-components/ui/Button.js.map +1 -1
- package/lib/engine-components/ui/Text.d.ts +1 -0
- package/lib/engine-components/ui/Text.js +11 -0
- package/lib/engine-components/ui/Text.js.map +1 -1
- package/package.json +18 -14
- package/plugins/common/buildinfo.js +46 -10
- package/plugins/common/files.js +2 -1
- package/plugins/common/license.js +144 -69
- package/plugins/common/logger.js +172 -11
- package/plugins/common/needle-engine-skill.md +175 -0
- package/plugins/common/worker.js +5 -4
- package/plugins/types/userconfig.d.ts +40 -2
- package/plugins/vite/ai.js +71 -0
- package/plugins/vite/alias.js +6 -5
- package/plugins/vite/asap.js +6 -5
- package/plugins/vite/build-pipeline.js +224 -41
- package/plugins/vite/buildinfo.js +66 -6
- package/plugins/vite/copyfiles.js +41 -12
- package/plugins/vite/custom-element-data.js +26 -16
- package/plugins/vite/defines.js +8 -5
- package/plugins/vite/dependencies.js +16 -10
- package/plugins/vite/dependency-watcher.js +35 -7
- package/plugins/vite/drop-client.js +7 -5
- package/plugins/vite/drop.js +16 -14
- package/plugins/vite/editor-connection.js +18 -16
- package/plugins/vite/imports-logger.js +12 -2
- package/plugins/vite/index.js +8 -3
- package/plugins/vite/local-files-analysis.js +789 -0
- package/plugins/vite/local-files-core.js +992 -0
- package/plugins/vite/local-files-internals.js +28 -0
- package/plugins/vite/local-files-types.d.ts +111 -0
- package/plugins/vite/local-files-utils.js +359 -0
- package/plugins/vite/local-files.js +2 -441
- package/plugins/vite/logger.client.js +45 -35
- package/plugins/vite/logger.js +6 -3
- package/plugins/vite/logging.js +129 -0
- package/plugins/vite/meta.js +18 -4
- package/plugins/vite/needle-app.js +4 -3
- package/plugins/vite/peer.js +2 -1
- package/plugins/vite/pwa.js +33 -17
- package/plugins/vite/reload.js +24 -2
- package/src/engine/api.ts +0 -3
- package/src/engine/debug/debug.ts +1 -1
- package/src/engine/debug/debug_spatial_console.ts +5 -1
- package/src/engine/engine_accessibility.ts +198 -0
- package/src/engine/engine_context.ts +10 -1
- package/src/engine/engine_create_objects.ts +1 -1
- package/src/engine/engine_gizmos.ts +9 -5
- package/src/engine/engine_license.ts +7 -2
- package/src/engine/engine_materialpropertyblock.ts +102 -11
- package/src/engine/engine_math.ts +34 -1
- package/src/engine/engine_networking.ts +1 -1
- package/src/engine/engine_types.ts +5 -0
- package/src/engine/engine_utils.ts +2 -2
- package/src/engine/export/gltf/index.ts +1 -1
- package/src/engine/webcomponents/icons.ts +3 -0
- package/src/engine/webcomponents/logo-element.ts +24 -4
- package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +6 -2
- package/src/engine/webcomponents/needle menu/needle-menu.ts +23 -11
- package/src/engine/webcomponents/needle-button.ts +44 -13
- package/src/engine/webcomponents/needle-engine.ar-overlay.ts +13 -2
- package/src/engine/webcomponents/needle-engine.ts +31 -8
- package/src/engine-components/Component.ts +2 -5
- package/src/engine-components/DragControls.ts +29 -4
- package/src/engine-components/NeedleMenu.ts +5 -3
- package/src/engine-components/Networking.ts +29 -4
- package/src/engine-components/ReflectionProbe.ts +52 -9
- package/src/engine-components/Skybox.ts +4 -2
- package/src/engine-components/export/gltf/GltfExport.ts +1 -1
- package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +2 -2
- package/src/engine-components/export/usdz/USDZExporter.ts +1 -1
- package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +108 -32
- package/src/engine-components/export/usdz/extensions/behavior/PhysicsExtension.ts +2 -2
- package/src/engine-components/ui/Button.ts +12 -0
- package/src/engine-components/ui/Text.ts +13 -0
- package/dist/materialx-CJyQZtjt.min.js +0 -90
- package/dist/materialx-DMs1E08Z.js +0 -4636
- package/dist/materialx-DaKKOoVk.umd.cjs +0 -90
- package/lib/engine/engine_test_utils.d.ts +0 -39
- package/lib/engine/engine_test_utils.js +0 -84
- package/lib/engine/engine_test_utils.js.map +0 -1
- package/lib/include/three/EXT_mesh_gpu_instancing_exporter.js.map +0 -1
- package/src/engine/engine_test_utils.ts +0 -109
- package/src/include/draco/draco_decoder.js +0 -34
- package/src/include/draco/draco_decoder.wasm +0 -0
- package/src/include/draco/draco_wasm_wrapper.js +0 -117
- package/src/include/ktx2/basis_transcoder.js +0 -19
- package/src/include/ktx2/basis_transcoder.wasm +0 -0
- package/src/include/needle/arial-msdf.json +0 -1472
- package/src/include/needle/arial.png +0 -0
- package/src/include/needle/poweredbyneedle.webp +0 -0
- /package/lib/{include/three → engine/export/gltf}/EXT_mesh_gpu_instancing_exporter.d.ts +0 -0
- /package/lib/{include/three → engine/export/gltf}/EXT_mesh_gpu_instancing_exporter.js +0 -0
- /package/src/{include/three → engine/export/gltf}/EXT_mesh_gpu_instancing_exporter.js +0 -0
|
@@ -302,7 +302,7 @@ export class NeedleSpatialMenu {
|
|
|
302
302
|
// logoObject.position.y = 1;
|
|
303
303
|
// this._context.scene.add(logoObject);
|
|
304
304
|
const textureLoader = new TextureLoader();
|
|
305
|
-
textureLoader.load("
|
|
305
|
+
textureLoader.load("https://cdn.needle.tools/static/branding/poweredbyneedle.webp", (texture) => {
|
|
306
306
|
if (texture) {
|
|
307
307
|
onClick.allowModifyUI = false;
|
|
308
308
|
firstLabel.removeFromParent();
|
|
@@ -343,7 +343,11 @@ export class NeedleSpatialMenu {
|
|
|
343
343
|
|
|
344
344
|
if (!fontFamily) {
|
|
345
345
|
fontFamily = ThreeMeshUI.FontLibrary.addFontFamily(this.familyName);
|
|
346
|
-
const normal = fontFamily.addVariant(
|
|
346
|
+
const normal = fontFamily.addVariant(
|
|
347
|
+
"normal",
|
|
348
|
+
"normal",
|
|
349
|
+
"https://cdn.needle.tools/static/fonts/msdf/arial/arial-msdf.json",
|
|
350
|
+
"https://cdn.needle.tools/static/fonts/msdf/arial/arial.png") as any as ThreeMeshUI.FontVariant;
|
|
347
351
|
/** @ts-ignore */
|
|
348
352
|
normal?.addEventListener('ready', () => {
|
|
349
353
|
this.markDirty();
|
|
@@ -129,6 +129,7 @@ export class NeedleMenu {
|
|
|
129
129
|
|
|
130
130
|
constructor(context: Context) {
|
|
131
131
|
this._menu = NeedleMenuElement.getOrCreate(context.domElement, context);
|
|
132
|
+
this._menu.ensureInitialized();
|
|
132
133
|
this._context = context;
|
|
133
134
|
this._spatialMenu = new NeedleSpatialMenu(context, this._menu);
|
|
134
135
|
window.addEventListener("message", this.onPostMessage);
|
|
@@ -309,8 +310,7 @@ export class NeedleMenu {
|
|
|
309
310
|
export class NeedleMenuElement extends HTMLElement {
|
|
310
311
|
|
|
311
312
|
static create() {
|
|
312
|
-
|
|
313
|
-
return document.createElement(elementName, { is: elementName });
|
|
313
|
+
return document.createElement(elementName);
|
|
314
314
|
}
|
|
315
315
|
|
|
316
316
|
static getOrCreate(domElement: HTMLElement, context: Context) {
|
|
@@ -337,9 +337,13 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
337
337
|
|
|
338
338
|
private _domElement: HTMLElement | null = null;
|
|
339
339
|
private _context: Context | null = null;
|
|
340
|
+
private _didInitialize = false;
|
|
340
341
|
|
|
341
342
|
constructor() {
|
|
342
343
|
super();
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private initializeDom() {
|
|
343
347
|
|
|
344
348
|
const template = document.createElement('template');
|
|
345
349
|
// TODO: make host full size again and move the buttons to a wrapper so that we can later easily open e.g. foldouts/dropdowns / use the whole canvas space
|
|
@@ -795,7 +799,7 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
795
799
|
|
|
796
800
|
|
|
797
801
|
let context = this._context;
|
|
798
|
-
// we need to assign it in the timeout because the reference is set *after* the
|
|
802
|
+
// we need to assign it in the timeout because the reference is set *after* the element is initialized
|
|
799
803
|
setTimeout(() => context = this._context);
|
|
800
804
|
|
|
801
805
|
// watch changes
|
|
@@ -864,13 +868,21 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
864
868
|
}
|
|
865
869
|
}
|
|
866
870
|
|
|
871
|
+
ensureInitialized() {
|
|
872
|
+
if (!this._didInitialize) {
|
|
873
|
+
this._didInitialize = true;
|
|
874
|
+
this.initializeDom();
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
867
878
|
private _sizeChangeInterval;
|
|
868
879
|
|
|
869
880
|
connectedCallback() {
|
|
881
|
+
this.ensureInitialized();
|
|
870
882
|
window.addEventListener("resize", this.handleSizeChange);
|
|
871
883
|
this.handleMenuVisible();
|
|
872
884
|
this._sizeChangeInterval = setInterval(() => this.handleSizeChange(undefined, false), 5000);
|
|
873
|
-
// the dom element is set after
|
|
885
|
+
// the dom element is set after initialization runs
|
|
874
886
|
setTimeout(() => {
|
|
875
887
|
this._domElement?.addEventListener("resize", this.handleSizeChange);
|
|
876
888
|
this._domElement?.addEventListener("click", this.#onClick);
|
|
@@ -958,19 +970,19 @@ export class NeedleMenuElement extends HTMLElement {
|
|
|
958
970
|
|
|
959
971
|
// private _root: ShadowRoot | null = null;
|
|
960
972
|
/** @private root container element inside shadow DOM */
|
|
961
|
-
private
|
|
973
|
+
private root!: HTMLDivElement;
|
|
962
974
|
/** @private wraps the whole content (internal layout) */
|
|
963
|
-
private
|
|
975
|
+
private wrapper!: HTMLDivElement;
|
|
964
976
|
/** @private contains the buttons and dynamic elements */
|
|
965
|
-
private
|
|
977
|
+
private options!: HTMLDivElement;
|
|
966
978
|
/** @private contains options visible when in compact mode */
|
|
967
|
-
private
|
|
979
|
+
private optionsCompactMode!: HTMLDivElement;
|
|
968
980
|
/** @private contains the needle-logo html element */
|
|
969
|
-
private
|
|
981
|
+
private logoContainer!: HTMLDivElement;
|
|
970
982
|
/** @private compact menu button element */
|
|
971
|
-
private
|
|
983
|
+
private compactMenuButton!: HTMLButtonElement;
|
|
972
984
|
/** @private foldout container used in compact mode */
|
|
973
|
-
private
|
|
985
|
+
private foldout!: HTMLDivElement;
|
|
974
986
|
|
|
975
987
|
|
|
976
988
|
private readonly trackedElements: WeakSet<Node> = new WeakSet();
|
|
@@ -13,32 +13,58 @@ const htmlTagName = "needle-button";
|
|
|
13
13
|
const isDev = isDevEnvironment();
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* [<needle-button>](https://engine.needle.tools/docs/api/NeedleButtonElement) is a web component for easily adding AR, VR, Quicklook, or QR code buttons to your website without writing JavaScript code.
|
|
17
|
+
*
|
|
18
|
+
* The button automatically handles session management and displays appropriate UI based on device capabilities.
|
|
19
|
+
* It comes with default styling (glassmorphism design) but can be fully customized with CSS.
|
|
20
|
+
*
|
|
21
|
+
* **Supported button types:**
|
|
22
|
+
* - `ar` - WebXR AR session button
|
|
23
|
+
* - `vr` - WebXR VR session button
|
|
24
|
+
* - `quicklook` - Apple AR Quick Look button (iOS only)
|
|
25
|
+
* - `qrcode` - QR code sharing button
|
|
26
|
+
*
|
|
27
|
+
* @example Basic AR/VR buttons
|
|
18
28
|
* ```html
|
|
29
|
+
* <needle-engine src="scene.glb"></needle-engine>
|
|
19
30
|
* <needle-button ar></needle-button>
|
|
20
31
|
* <needle-button vr></needle-button>
|
|
21
32
|
* <needle-button quicklook></needle-button>
|
|
22
33
|
* ```
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
34
|
+
*
|
|
35
|
+
* @example Custom button labels
|
|
25
36
|
* ```html
|
|
26
|
-
* <needle-button ar>Start AR</needle-button>
|
|
27
|
-
* <needle-button vr>
|
|
37
|
+
* <needle-button ar>Start AR Experience</needle-button>
|
|
38
|
+
* <needle-button vr>Enter VR Mode</needle-button>
|
|
28
39
|
* <needle-button quicklook>View in AR</needle-button>
|
|
29
40
|
* ```
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
41
|
+
*
|
|
42
|
+
* @example Custom styling
|
|
32
43
|
* ```html
|
|
33
|
-
* <!-- You can either style the element directly or use a CSS stylesheet -->
|
|
34
44
|
* <style>
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
45
|
+
* needle-button {
|
|
46
|
+
* background-color: #ff6b6b;
|
|
47
|
+
* color: white;
|
|
48
|
+
* border-radius: 8px;
|
|
49
|
+
* padding: 1rem 2rem;
|
|
50
|
+
* }
|
|
51
|
+
* needle-button:hover {
|
|
52
|
+
* background-color: #ff5252;
|
|
53
|
+
* }
|
|
39
54
|
* </style>
|
|
40
55
|
* <needle-button ar>Start AR</needle-button>
|
|
41
56
|
* ```
|
|
57
|
+
*
|
|
58
|
+
* @example Unstyled button (for complete custom styling)
|
|
59
|
+
* ```html
|
|
60
|
+
* <needle-button ar unstyled>
|
|
61
|
+
* <span class="my-icon">🥽</span>
|
|
62
|
+
* Launch AR
|
|
63
|
+
* </needle-button>
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @see {@link NeedleEngineWebComponent} for the main <needle-engine> element
|
|
67
|
+
* @see {@link NeedleMenu} for the built-in menu component that can display similar buttons
|
|
42
68
|
*/
|
|
43
69
|
export class NeedleButtonElement extends HTMLElement {
|
|
44
70
|
|
|
@@ -73,18 +99,22 @@ export class NeedleButtonElement extends HTMLElement {
|
|
|
73
99
|
if (this.getAttribute("ar") != null) {
|
|
74
100
|
this.#webxrfactory ??= new WebXRButtonFactory()
|
|
75
101
|
this.#button = this.#webxrfactory.createARButton();
|
|
102
|
+
this.setAttribute("aria-label", "Enter augmented reality mode");
|
|
76
103
|
}
|
|
77
104
|
else if (this.getAttribute("vr") != null) {
|
|
78
105
|
this.#webxrfactory ??= new WebXRButtonFactory()
|
|
79
106
|
this.#button = this.#webxrfactory.createVRButton();
|
|
107
|
+
this.setAttribute("aria-label", "Enter virtual reality mode");
|
|
80
108
|
}
|
|
81
109
|
else if (this.getAttribute("quicklook") != null) {
|
|
82
110
|
this.#webxrfactory ??= new WebXRButtonFactory()
|
|
83
111
|
this.#button = this.#webxrfactory.createQuicklookButton();
|
|
112
|
+
this.setAttribute("aria-label", "View in AR with Apple Quick Look");
|
|
84
113
|
}
|
|
85
114
|
else if (this.getAttribute("qrcode") != null) {
|
|
86
115
|
this.#buttonfactory ??= new ButtonsFactory();
|
|
87
116
|
this.#button = this.#buttonfactory.createQRCode({ anchorElement: this });
|
|
117
|
+
this.setAttribute("aria-label", "Share application with QR code");
|
|
88
118
|
}
|
|
89
119
|
else {
|
|
90
120
|
if (isDev) {
|
|
@@ -93,6 +123,7 @@ export class NeedleButtonElement extends HTMLElement {
|
|
|
93
123
|
else {
|
|
94
124
|
console.debug("No button type specified for <needle-button>. Use either ar, vr or quicklook attribute.")
|
|
95
125
|
}
|
|
126
|
+
this.setAttribute("aria-label", "Needle Button with no specified type");
|
|
96
127
|
return;
|
|
97
128
|
}
|
|
98
129
|
|
|
@@ -121,7 +121,7 @@ export class AROverlayHandler {
|
|
|
121
121
|
|
|
122
122
|
const quitARSlot = document.createElement("slot");
|
|
123
123
|
quitARSlot.style.display = "contents";
|
|
124
|
-
quitARSlot.style.padding = "10px";
|
|
124
|
+
quitARSlot.style.padding = "10px";
|
|
125
125
|
quitARSlot.setAttribute("name", "quit-ar");
|
|
126
126
|
this.appendElement(quitARSlot, element);
|
|
127
127
|
this._createdAROnlyElements.push(quitARSlot);
|
|
@@ -131,7 +131,16 @@ export class AROverlayHandler {
|
|
|
131
131
|
// No default quit button in the top right corner in app clips
|
|
132
132
|
// we provide one via the native UI
|
|
133
133
|
if (DeviceUtilities.isNeedleAppClip()) {
|
|
134
|
-
|
|
134
|
+
|
|
135
|
+
// quitARSlot.style.display = "none";
|
|
136
|
+
globalThis["NEEDLE_ENGINE_APPCLIP_DISABLE_MENU"] = true;
|
|
137
|
+
|
|
138
|
+
// respect the UI bar at the top of the screen and add some padding to the quit button container
|
|
139
|
+
// @TODO: this should be done in CSS in one place and not here and in debug overlay
|
|
140
|
+
const meta = document.querySelector('meta[name="viewport"]');
|
|
141
|
+
if (meta && !meta.getAttribute("content")?.includes("viewport-fit=")) {
|
|
142
|
+
meta.setAttribute("content", meta.getAttribute("content") + ",viewport-fit=cover");
|
|
143
|
+
}
|
|
135
144
|
}
|
|
136
145
|
|
|
137
146
|
// We want to search the document if there's a quit-ar button
|
|
@@ -155,6 +164,8 @@ export class AROverlayHandler {
|
|
|
155
164
|
right: 0;
|
|
156
165
|
z-index: 600;
|
|
157
166
|
pointer-events: all;
|
|
167
|
+
padding-top: env(safe-area-inset-top, 0px);
|
|
168
|
+
padding-right: calc(env(safe-area-inset-right, 0px) + 10px);
|
|
158
169
|
`;
|
|
159
170
|
this.appendElement(fixedButtonContainer, quitARSlot);
|
|
160
171
|
|
|
@@ -87,17 +87,25 @@ const observedAttributes = [
|
|
|
87
87
|
|
|
88
88
|
// https://developers.google.com/web/fundamentals/web-components/customelements
|
|
89
89
|
|
|
90
|
-
/**
|
|
91
|
-
* The `<needle-engine>` web component. See {@link NeedleEngineAttributes} attributes for supported attributes
|
|
92
|
-
* The web component creates and manages a Needle Engine context, which is responsible for rendering a 3D scene using threejs.
|
|
93
|
-
* The context is created when the `src` attribute is set, and disposed when the element is removed from the DOM. You can prevent cleanup by setting the `keep-alive` attribute to `true`.
|
|
90
|
+
/**
|
|
91
|
+
* The `<needle-engine>` web component. See {@link NeedleEngineAttributes} attributes for supported attributes
|
|
92
|
+
* The web component creates and manages a Needle Engine context, which is responsible for rendering a 3D scene using threejs.
|
|
93
|
+
* The context is created when the `src` attribute is set, and disposed when the element is removed from the DOM. You can prevent cleanup by setting the `keep-alive` attribute to `true`.
|
|
94
94
|
* The context is accessible from the `<needle-engine>` element: `document.querySelector("needle-engine").context`.
|
|
95
95
|
* See {@link https://engine.needle.tools/docs/reference/needle-engine-attributes}
|
|
96
96
|
*
|
|
97
|
-
* @example
|
|
97
|
+
* @example Basic usage
|
|
98
|
+
* ```html
|
|
98
99
|
* <needle-engine src="https://example.com/scene.glb"></needle-engine>
|
|
99
|
-
*
|
|
100
|
+
* ```
|
|
101
|
+
*
|
|
102
|
+
* @example With camera controls disabled
|
|
103
|
+
* ```html
|
|
100
104
|
* <needle-engine src="https://example.com/scene.glb" camera-controls="false"></needle-engine>
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* @see {@link NeedleButtonElement} for adding AR/VR/Quicklook buttons via <needle-button>
|
|
108
|
+
* @see {@link NeedleMenu} for the built-in menu configuration component
|
|
101
109
|
*/
|
|
102
110
|
export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngineComponent {
|
|
103
111
|
|
|
@@ -160,23 +168,36 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
|
|
|
160
168
|
*/
|
|
161
169
|
public get context() { return this._context; }
|
|
162
170
|
|
|
163
|
-
private _context
|
|
171
|
+
private _context!: Context;
|
|
164
172
|
private _overlay_ar: AROverlayHandler;
|
|
165
173
|
private _loadingProgress01: number = 0;
|
|
166
174
|
private _loadingView?: ILoadingViewHandler;
|
|
167
175
|
private _previousSrc: string | null | string[] = null;
|
|
168
176
|
/** @private set to true after <needle-engine> did load completely at least once. Set to false when < to false when <needle-engine> is removed from the document removed from the document */
|
|
169
177
|
private _didFullyLoad: boolean = false;
|
|
178
|
+
private _didInitialize = false;
|
|
170
179
|
|
|
171
180
|
constructor() {
|
|
172
181
|
super();
|
|
173
182
|
this._overlay_ar = new AROverlayHandler();
|
|
174
183
|
// TODO: do we want to rename this event?
|
|
175
184
|
this.addEventListener("ready", this.onReady);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private ensureInitialized() {
|
|
188
|
+
if (!this._didInitialize) {
|
|
189
|
+
this._didInitialize = true;
|
|
190
|
+
this.initializeDom();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private initializeDom() {
|
|
176
195
|
|
|
177
196
|
ensureFonts();
|
|
178
197
|
|
|
179
|
-
this.attachShadow({ mode: 'open' });
|
|
198
|
+
this.attachShadow({ mode: 'open', delegatesFocus: true });
|
|
199
|
+
this.setAttribute("role", "application");
|
|
200
|
+
this.setAttribute("aria-label", "Needle Engine 3D scene");
|
|
180
201
|
const template = document.createElement('template');
|
|
181
202
|
// #region CSS
|
|
182
203
|
template.innerHTML = `<style>
|
|
@@ -271,6 +292,7 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
|
|
|
271
292
|
* @internal
|
|
272
293
|
*/
|
|
273
294
|
async connectedCallback() {
|
|
295
|
+
this.ensureInitialized();
|
|
274
296
|
if (debug) {
|
|
275
297
|
console.log("<needle-engine> connected");
|
|
276
298
|
}
|
|
@@ -282,6 +304,7 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
|
|
|
282
304
|
if (this.getAttribute("tabindex") === null || this.getAttribute("tabindex") === undefined)
|
|
283
305
|
this.setAttribute("tabindex", "0");
|
|
284
306
|
|
|
307
|
+
|
|
285
308
|
this.addEventListener("xr-session-started", this.onXRSessionStarted);
|
|
286
309
|
this.onSetupDesktop();
|
|
287
310
|
|
|
@@ -10,7 +10,7 @@ import * as main from "../engine/engine_mainloop_utils.js";
|
|
|
10
10
|
import { syncDestroy, syncInstantiate, SyncInstantiateOptions } from "../engine/engine_networking_instantiate.js";
|
|
11
11
|
import { Context, FrameEvent } from "../engine/engine_setup.js";
|
|
12
12
|
import * as threeutils from "../engine/engine_three_utils.js";
|
|
13
|
-
import type
|
|
13
|
+
import { $componentName, type Collision, type ComponentInit, type Constructor, type ConstructorConcrete, type GuidsMap, type ICollider, type IComponent, type IGameObject, type SourceIdentifier } from "../engine/engine_types.js";
|
|
14
14
|
import { TypeStore } from "../engine/engine_typestore.js";
|
|
15
15
|
import type { INeedleXRSessionEventReceiver, NeedleXRControllerEventArgs, NeedleXREventArgs } from "../engine/engine_xr.js";
|
|
16
16
|
import { type IPointerEventHandler, PointerEventData } from "./ui/PointerEvents.js";
|
|
@@ -49,7 +49,7 @@ export abstract class GameObject extends Object3D implements Object3D, IGameObje
|
|
|
49
49
|
abstract activeSelf: boolean;
|
|
50
50
|
|
|
51
51
|
/** @deprecated Use {@link addComponent} instead */
|
|
52
|
-
// eslint-disable-next-line
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
53
53
|
abstract addNewComponent<T extends IComponent>(type: ConstructorConcrete<T>, init?: ComponentInit<T>): T;
|
|
54
54
|
|
|
55
55
|
/**
|
|
@@ -364,7 +364,6 @@ export abstract class GameObject extends Object3D implements Object3D, IGameObje
|
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
/** @deprecated use `addComponent` */
|
|
367
|
-
// eslint-disable-next-line deprecation/deprecation
|
|
368
367
|
public static addNewComponent<T extends IComponent>(go: IGameObject | Object3D, type: T | ConstructorConcrete<T>, init?: ComponentInit<T>, callAwake: boolean = true): T {
|
|
369
368
|
return addComponent(go, type, init, { callAwake });
|
|
370
369
|
}
|
|
@@ -542,8 +541,6 @@ export abstract class GameObject extends Object3D implements Object3D, IGameObje
|
|
|
542
541
|
}
|
|
543
542
|
}
|
|
544
543
|
|
|
545
|
-
// DO NOT CHANGE THE SYMBOL NAME
|
|
546
|
-
const $componentName = Symbol("component-name");
|
|
547
544
|
|
|
548
545
|
/**
|
|
549
546
|
* Needle Engine component's are the main building blocks of the Needle Engine.
|
|
@@ -193,7 +193,7 @@ export class DragControls extends Behaviour implements IPointerEventHandler {
|
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
|
-
|
|
196
|
+
|
|
197
197
|
private _rigidbody: Rigidbody | null = null;
|
|
198
198
|
|
|
199
199
|
// future:
|
|
@@ -235,12 +235,22 @@ export class DragControls extends Behaviour implements IPointerEventHandler {
|
|
|
235
235
|
/** @internal */
|
|
236
236
|
onEnable(): void {
|
|
237
237
|
DragControls._instances.push(this);
|
|
238
|
+
this.context.accessibility.updateElement(this, {
|
|
239
|
+
role: "button",
|
|
240
|
+
label: "Drag " + (this.gameObject.name || "object"),
|
|
241
|
+
hidden: false,
|
|
242
|
+
});
|
|
238
243
|
}
|
|
239
244
|
/** @internal */
|
|
240
245
|
onDisable(): void {
|
|
246
|
+
this.context.accessibility.updateElement(this, { hidden: true });
|
|
241
247
|
DragControls._instances = DragControls._instances.filter(i => i !== this);
|
|
242
248
|
}
|
|
243
249
|
|
|
250
|
+
onDestroy(): void {
|
|
251
|
+
this.context.accessibility.removeElement(this);
|
|
252
|
+
}
|
|
253
|
+
|
|
244
254
|
/**
|
|
245
255
|
* Checks if editing is allowed for the current networking connection.
|
|
246
256
|
* @param _obj Optional object to check edit permissions for
|
|
@@ -268,6 +278,8 @@ export class DragControls extends Behaviour implements IPointerEventHandler {
|
|
|
268
278
|
if (!dc || dc !== this) return;
|
|
269
279
|
DragControls.lastHovered = evt.object;
|
|
270
280
|
this.context.domElement.style.cursor = 'pointer';
|
|
281
|
+
|
|
282
|
+
this.context.accessibility.hover(this, `Draggable ${evt.object?.name}`);
|
|
271
283
|
}
|
|
272
284
|
|
|
273
285
|
/**
|
|
@@ -339,6 +351,14 @@ export class DragControls extends Behaviour implements IPointerEventHandler {
|
|
|
339
351
|
}
|
|
340
352
|
|
|
341
353
|
args.use();
|
|
354
|
+
|
|
355
|
+
this.context.accessibility.updateElement(this, {
|
|
356
|
+
role: "button",
|
|
357
|
+
label: "Dragging " + (this.gameObject.name || "object"),
|
|
358
|
+
hidden: false,
|
|
359
|
+
busy: true,
|
|
360
|
+
});
|
|
361
|
+
this.context.accessibility.focus(this);
|
|
342
362
|
}
|
|
343
363
|
}
|
|
344
364
|
|
|
@@ -375,6 +395,11 @@ export class DragControls extends Behaviour implements IPointerEventHandler {
|
|
|
375
395
|
}
|
|
376
396
|
args.use();
|
|
377
397
|
}
|
|
398
|
+
|
|
399
|
+
this.context.accessibility.unfocus(this);
|
|
400
|
+
this.context.accessibility.updateElement(this, {
|
|
401
|
+
busy: false,
|
|
402
|
+
});
|
|
378
403
|
}
|
|
379
404
|
|
|
380
405
|
/**
|
|
@@ -779,12 +804,12 @@ class DragPointerHandler implements IDragHandler {
|
|
|
779
804
|
* Used for determining if enough motion has occurred to start a drag.
|
|
780
805
|
*/
|
|
781
806
|
getTotalMovement(): Vector3 { return this._totalMovement; }
|
|
782
|
-
|
|
807
|
+
|
|
783
808
|
/**
|
|
784
809
|
* Returns the object that follows the pointer during dragging operations.
|
|
785
810
|
*/
|
|
786
811
|
get followObject(): GameObject { return this._followObject; }
|
|
787
|
-
|
|
812
|
+
|
|
788
813
|
/**
|
|
789
814
|
* Returns the point where the pointer initially hit the object in local space.
|
|
790
815
|
*/
|
|
@@ -1377,7 +1402,7 @@ class LegacyDragVisualsHelper {
|
|
|
1377
1402
|
|
|
1378
1403
|
/** Controls whether visual helpers like lines and markers are displayed */
|
|
1379
1404
|
showGizmo: boolean = true;
|
|
1380
|
-
|
|
1405
|
+
|
|
1381
1406
|
/** When true, drag plane alignment changes based on view angle */
|
|
1382
1407
|
useViewAngle: boolean = true;
|
|
1383
1408
|
|
|
@@ -9,8 +9,8 @@ import type { Voip } from './Voip.js';
|
|
|
9
9
|
/**
|
|
10
10
|
* [NeedleMenu](https://engine.needle.tools/docs/api/NeedleMenu) provides configuration for the built-in UI menu.
|
|
11
11
|
* The menu renders as HTML overlay in browser mode and automatically
|
|
12
|
-
* switches to a 3D spatial menu in VR/AR.
|
|
13
|
-
*
|
|
12
|
+
* switches to a 3D spatial menu in VR/AR.
|
|
13
|
+
*
|
|
14
14
|
* 
|
|
15
15
|
*
|
|
16
16
|
* **Features:**
|
|
@@ -18,7 +18,7 @@ import type { Voip } from './Voip.js';
|
|
|
18
18
|
* - Audio mute/unmute button
|
|
19
19
|
* - QR code sharing (desktop only)
|
|
20
20
|
* - Spatial menu in XR (appears when looking up)
|
|
21
|
-
* - Custom positioning (top/bottom)
|
|
21
|
+
* - Custom positioning (top/bottom)
|
|
22
22
|
*
|
|
23
23
|
* **Programmatic access:**
|
|
24
24
|
* Access the menu API via `this.context.menu` to add custom buttons,
|
|
@@ -36,6 +36,8 @@ import type { Voip } from './Voip.js';
|
|
|
36
36
|
* @category User Interface
|
|
37
37
|
* @group Components
|
|
38
38
|
* @see {@link Context.menu} for programmatic menu control
|
|
39
|
+
* @see {@link NeedleButtonElement} for standalone <needle-button> web component
|
|
40
|
+
* @see {@link NeedleEngineWebComponent} for the main <needle-engine> element
|
|
39
41
|
* @see {@link Voip} adds a microphone button to the menu
|
|
40
42
|
* @see {@link ScreenCapture} adds a screen sharing button
|
|
41
43
|
**/
|
|
@@ -7,10 +7,34 @@ import { Behaviour } from "./Component.js";
|
|
|
7
7
|
const debug = getParam("debugnet");
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Provides configuration
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Provides websocket URL configuration for the built-in networking system.
|
|
11
|
+
* Add this component to override the default networking backend URL used by {@link NetworkConnection} (`this.context.connection`).
|
|
12
|
+
*
|
|
13
|
+
* The component registers itself as a URL provider on `awake()`. When the networking system connects,
|
|
14
|
+
* it queries this provider for the websocket URL to use instead of the default Needle networking backend.
|
|
15
|
+
*
|
|
16
|
+
* **URL resolution order:**
|
|
17
|
+
* 1. If `urlParameterName` is set and the corresponding URL parameter exists in the browser URL, that value is used
|
|
18
|
+
* 2. If running on a local network and `localhost` is set, the `localhost` URL is used
|
|
19
|
+
* 3. Otherwise, the `url` field is used
|
|
20
|
+
*
|
|
21
|
+
* Without this component, the default backend URL `wss://networking-2.needle.tools/socket` is used.
|
|
22
|
+
*
|
|
23
|
+
* **Note:** This component only configures the websocket URL. To actually join a networked room,
|
|
24
|
+
* use a `SyncedRoom` component or call `this.context.connection.joinRoom("room-name")` directly.
|
|
25
|
+
*
|
|
26
|
+
* @example Overriding the URL via browser parameter
|
|
27
|
+
* ```ts
|
|
28
|
+
* // With urlParameterName="server", visiting:
|
|
29
|
+
* // https://myapp.com/?server=wss://my-server.com/socket
|
|
30
|
+
* // will connect to that server instead
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @see {@link NetworkConnection} for the main networking API (`this.context.connection`)
|
|
34
|
+
* @see {@link SyncedRoom} for automatic room joining
|
|
35
|
+
* @see {@link OwnershipModel} for networked object ownership
|
|
36
|
+
* @see {@link RoomEvents} for room lifecycle events
|
|
37
|
+
* @link https://engine.needle.tools/docs/how-to-guides/networking/
|
|
14
38
|
* @summary Networking configuration
|
|
15
39
|
* @category Networking
|
|
16
40
|
* @group Components
|
|
@@ -20,6 +44,7 @@ export class Networking extends Behaviour implements INetworkingWebsocketUrlProv
|
|
|
20
44
|
/**
|
|
21
45
|
* The websocket URL to connect to for networking functionality.
|
|
22
46
|
* Can be a complete URL or a relative path that will be resolved against the current origin.
|
|
47
|
+
* @default null
|
|
23
48
|
*/
|
|
24
49
|
@serializable()
|
|
25
50
|
url: string | null = null;
|