@shopware-ag/dive 1.16.14 → 1.16.15-beta.0

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 (44) hide show
  1. package/package.json +1 -1
  2. package/src/ar/AR.ts +164 -0
  3. package/src/ar/arquicklook/ARQuickLook.ts +42 -0
  4. package/src/ar/webxr/WebXR.ts +176 -0
  5. package/src/ar/webxr/controller/WebXRController.ts +334 -0
  6. package/src/ar/webxr/crosshair/WebXRCrosshair.ts +35 -0
  7. package/src/ar/webxr/origin/WebXROrigin.ts +191 -0
  8. package/src/ar/webxr/overlay/Overlay.ts +50 -0
  9. package/src/ar/webxr/raycaster/WebXRRaycaster.ts +131 -0
  10. package/src/ar/webxr/raycaster/ar/WebXRRaycasterAR.ts +102 -0
  11. package/src/ar/webxr/raycaster/three/WebXRRaycasterTHREE.ts +49 -0
  12. package/src/ar/webxr/touchscreencontrols/WebXRTouchscreenControls.ts +356 -0
  13. package/src/axiscamera/AxisCamera.ts +4 -4
  14. package/src/axiscamera/__test__/AxisCamera.test.ts +4 -0
  15. package/src/com/Communication.ts +17 -0
  16. package/src/com/__test__/Communication.test.ts +1 -1
  17. package/src/com/actions/index.ts +2 -0
  18. package/src/dive.ts +51 -9
  19. package/src/events/EventExecutor.ts +35 -0
  20. package/src/helper/findSceneRecursive/findSceneRecursive.ts +2 -2
  21. package/src/info/Info.ts +37 -1
  22. package/src/info/__test__/Info.test.ts +45 -5
  23. package/src/mediacreator/MediaCreator.ts +4 -4
  24. package/src/mediacreator/__test__/MediaCreator.test.ts +7 -2
  25. package/src/renderer/Renderer.ts +21 -11
  26. package/src/renderer/__test__/Renderer.test.ts +19 -1
  27. package/src/scene/Scene.ts +35 -12
  28. package/src/scene/__test__/Scene.test.ts +39 -5
  29. package/src/scene/root/Root.ts +1 -0
  30. package/src/scene/xrroot/XRRoot.ts +56 -0
  31. package/src/scene/xrroot/xrlightroot/XRLightRoot.ts +80 -0
  32. package/src/toolbox/BaseTool.ts +9 -3
  33. package/src/toolbox/Toolbox.ts +1 -1
  34. package/src/toolbox/__test__/Toolbox.test.ts +1 -1
  35. package/src/toolbox/select/SelectTool.ts +1 -1
  36. package/src/toolbox/select/__test__/SelectTool.test.ts +1 -1
  37. package/src/toolbox/transform/TransformTool.ts +4 -4
  38. package/src/toolbox/transform/__test__/TransformTool.test.ts +2 -4
  39. package/build/dive.cjs +0 -3280
  40. package/build/dive.cjs.map +0 -1
  41. package/build/dive.d.cts +0 -1083
  42. package/build/dive.d.ts +0 -1083
  43. package/build/dive.js +0 -3298
  44. package/build/dive.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shopware-ag/dive",
3
- "version": "1.16.14",
3
+ "version": "1.16.15-beta.0",
4
4
  "description": "Shopware Spatial Framework",
5
5
  "type": "module",
6
6
  "main": "./build/dive.cjs",
package/src/ar/AR.ts ADDED
@@ -0,0 +1,164 @@
1
+ import { DIVEInfo, WebXRUnsupportedReason } from '../info/Info';
2
+ import { DIVEARQuickLook } from './arquicklook/ARQuickLook';
3
+ import { DIVEWebXR } from './webxr/WebXR';
4
+ import { type DIVEScene } from '../scene/Scene';
5
+ import { type DIVERenderer } from '../renderer/Renderer';
6
+ import DIVEOrbitControls from '../controls/OrbitControls';
7
+
8
+ export class DIVEAR {
9
+ private _renderer: DIVERenderer;
10
+ private _scene: DIVEScene;
11
+ private _controller: DIVEOrbitControls;
12
+
13
+ private arPlacement: string = 'floor';
14
+ private arScale: string = 'auto';
15
+
16
+ constructor(
17
+ renderer: DIVERenderer,
18
+ scene: DIVEScene,
19
+ controller: DIVEOrbitControls,
20
+ ) {
21
+ this._renderer = renderer;
22
+ this._scene = scene;
23
+ this._controller = controller;
24
+ }
25
+
26
+ public async Launch(): Promise<void> {
27
+ const system = DIVEInfo.GetSystem();
28
+
29
+ if (system === 'iOS') {
30
+ const support = DIVEInfo.GetSupportsARQuickLook();
31
+ if (!support) {
32
+ console.log('ARQuickLook not supported');
33
+ return Promise.reject();
34
+ }
35
+
36
+ console.log('Launching AR on iOS');
37
+
38
+ // Launch ARQuickLook
39
+ await DIVEARQuickLook.Launch(this._scene);
40
+ return Promise.resolve();
41
+ }
42
+
43
+ if (system === 'Android') {
44
+ this.openSceneViewer();
45
+ return;
46
+
47
+ const support = await DIVEInfo.GetSupportsWebXR();
48
+ if (!support) {
49
+ console.log(
50
+ 'WebXR not supported. Reason: ' +
51
+ WebXRUnsupportedReason[
52
+ DIVEInfo.GetWebXRUnsupportedReason()!
53
+ ],
54
+ );
55
+ return Promise.reject();
56
+ }
57
+
58
+ console.log('Launching AR on Android');
59
+ // Launch WebXR
60
+ await DIVEWebXR.Launch(
61
+ this._renderer,
62
+ this._scene,
63
+ this._controller,
64
+ );
65
+ return Promise.resolve();
66
+ }
67
+
68
+ console.log(
69
+ 'AR not supported. Not a mobile system. (System is ' + system + ')',
70
+ );
71
+ }
72
+
73
+ private openSceneViewer(): void {
74
+ const src = this.createSceneViewerSrc();
75
+ const anchor = document.createElement('a');
76
+ const noArViewerSigil = '#model-viewer-no-ar-fallback';
77
+ // let isSceneViewerBlocked = false;
78
+
79
+ const location = self.location.toString();
80
+ const locationUrl = new URL(location);
81
+ const modelUrl = new URL(src, location);
82
+ if (modelUrl.hash) modelUrl.hash = '';
83
+ const params = new URLSearchParams(modelUrl.search);
84
+
85
+ locationUrl.hash = noArViewerSigil;
86
+
87
+ // modelUrl can contain title/link/sound etc.
88
+ params.set('mode', 'ar_preferred');
89
+ if (!params.has('disable_occlusion')) {
90
+ params.set('disable_occlusion', 'true');
91
+ }
92
+ if (this.arScale === 'fixed') {
93
+ params.set('resizable', 'false');
94
+ }
95
+ if (this.arPlacement === 'wall') {
96
+ params.set('enable_vertical_placement', 'true');
97
+ }
98
+ if (params.has('sound')) {
99
+ const soundUrl = new URL(params.get('sound')!, location);
100
+ params.set('sound', soundUrl.toString());
101
+ }
102
+ if (params.has('link')) {
103
+ const linkUrl = new URL(params.get('link')!, location);
104
+ params.set('link', linkUrl.toString());
105
+ }
106
+
107
+ console.log('modelUrl.toString()', modelUrl.toString());
108
+ console.log(
109
+ 'encodeURIComponent(modelUrl.toString())',
110
+ encodeURIComponent(modelUrl.toString()),
111
+ );
112
+
113
+ const intent = `intent://arvr.google.com/scene-viewer/1.2?${
114
+ params.toString() + '&file=' + modelUrl.toString()
115
+ }#Intent;scheme=https;package=com.google.android.googlequicksearchbox;action=android.intent.action.VIEW;S.browser_fallback_url=${encodeURIComponent(
116
+ locationUrl.toString(),
117
+ )};end;`;
118
+ // intent =
119
+ // 'intent://arvr.google.com/scene-viewer/1.0?file=https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Avocado/glTF/Avocado.gltf#Intent;scheme=https;package=com.google.android.googlequicksearchbox;action=android.intent.action.VIEW;S.browser_fallback_url=https://developers.google.com/ar;end;';
120
+ console.log({ intent });
121
+
122
+ const undoHashChange = (): void => {
123
+ if (self.location.hash === noArViewerSigil) {
124
+ // isSceneViewerBlocked = true;
125
+ // The new history will be the current URL with a new hash.
126
+ // Go back one step so that we reset to the expected URL.
127
+ // NOTE(cdata): this should not invoke any browser-level navigation
128
+ // because hash-only changes modify the URL in-place without
129
+ // navigating:
130
+ self.history.back();
131
+ console.warn(
132
+ 'Error while trying to present in AR with Scene Viewer',
133
+ );
134
+ console.warn('Falling back to next ar-mode');
135
+ // this[$selectARMode]();
136
+ // Would be nice to activateAR() here, but webXR fails due to not
137
+ // seeing a user activation.
138
+ }
139
+ };
140
+
141
+ self.addEventListener('hashchange', undoHashChange, { once: true });
142
+
143
+ anchor.setAttribute('href', intent);
144
+ console.log('Attempting to present in AR with Scene Viewer...');
145
+ anchor.click();
146
+ }
147
+
148
+ private createSceneViewerSrc(): string {
149
+ let uri: string | null = null;
150
+
151
+ this._scene.traverse((object) => {
152
+ if (uri) return;
153
+ if (object.userData.uri) {
154
+ uri = object.userData.uri;
155
+ }
156
+ });
157
+
158
+ if (!uri) {
159
+ throw new Error('No model found in scene');
160
+ }
161
+
162
+ return uri;
163
+ }
164
+ }
@@ -0,0 +1,42 @@
1
+ import { Object3D } from 'three';
2
+ import { type DIVEScene } from '../../scene/Scene';
3
+ import { USDZExporter } from 'three/examples/jsm/exporters/USDZExporter';
4
+
5
+ export class DIVEARQuickLook {
6
+ private static _usdzExporter: USDZExporter = new USDZExporter();
7
+
8
+ public static Launch(scene: DIVEScene): Promise<void> {
9
+ // create node to build usdz from
10
+ const quickLookScene = new Object3D();
11
+
12
+ // extract models from scene
13
+ quickLookScene.add(...this.extractModels(scene));
14
+
15
+ // launch ARQuickLook
16
+ return this.launchARFromNode(quickLookScene);
17
+ }
18
+
19
+ private static extractModels(scene: DIVEScene): Object3D[] {
20
+ // extract models
21
+ return scene.Root.children;
22
+ }
23
+
24
+ private static launchARFromNode(node: Object3D): Promise<void> {
25
+ // bundle USDZ
26
+ return this._usdzExporter
27
+ .parse(node, { quickLookCompatible: true })
28
+ .then((usdz: Uint8Array) => {
29
+ // create blob
30
+ const blob = new Blob([usdz], { type: 'model/vnd.usdz+zip' });
31
+ const url = URL.createObjectURL(blob);
32
+
33
+ // launch ARQuickLook
34
+ const a = document.createElement('a');
35
+ a.innerHTML = '<picture></picture>'; // This is actually needed so the viewer opens instantly
36
+ a.rel = 'ar';
37
+ a.href = url;
38
+ a.download = 'scene.usdz';
39
+ a.click();
40
+ });
41
+ }
42
+ }
@@ -0,0 +1,176 @@
1
+ import { Vector3 } from 'three';
2
+ import DIVEOrbitControls from '../../controls/OrbitControls';
3
+ import { type DIVERenderer } from '../../renderer/Renderer';
4
+ import { type DIVEScene } from '../../scene/Scene';
5
+ import { Overlay } from './overlay/Overlay';
6
+ import { DIVEWebXRController } from './controller/WebXRController';
7
+
8
+ export class DIVEWebXR {
9
+ // general members
10
+ private static _renderer: DIVERenderer;
11
+ private static _scene: DIVEScene;
12
+ private static _controller: DIVEOrbitControls;
13
+
14
+ // camera reset members
15
+ private static _cameraPosition: Vector3;
16
+ private static _cameraTarget: Vector3;
17
+
18
+ // render loop members
19
+ private static _renderCallbackId: string | null = null;
20
+
21
+ // setup members
22
+ private static _session: XRSession | null = null;
23
+ private static _referenceSpaceType: XRReferenceSpaceType = 'local';
24
+ private static _overlay: Overlay | null = null;
25
+ private static _options = {
26
+ requiredFeatures: [
27
+ 'local',
28
+ 'hit-test',
29
+ ],
30
+ optionalFeatures: [
31
+ 'light-estimation',
32
+ 'local-floor',
33
+ 'dom-overlay',
34
+ 'depth-sensing',
35
+ ],
36
+ depthSensing: {
37
+ usagePreference: ['gpu-optimized'],
38
+ dataFormatPreference: [],
39
+ },
40
+ domOverlay: { root: {} as HTMLElement },
41
+ };
42
+
43
+ private static _xrController: DIVEWebXRController | null = null;
44
+
45
+ public static async Launch(
46
+ renderer: DIVERenderer,
47
+ scene: DIVEScene,
48
+ controller: DIVEOrbitControls,
49
+ ): Promise<void> {
50
+ this._renderer = renderer;
51
+ this._scene = scene;
52
+ this._controller = controller;
53
+
54
+ // setting camera reset values
55
+ this._cameraPosition = this._controller.object.position.clone();
56
+ this._cameraTarget = this._controller.target.clone();
57
+
58
+ if (!navigator.xr) {
59
+ console.error('WebXR not supported');
60
+ return Promise.reject();
61
+ }
62
+
63
+ // setup current instance
64
+ this._renderer.xr.enabled = true;
65
+ this._scene.InitXR(renderer);
66
+
67
+ // creating overlay
68
+ if (!DIVEWebXR._overlay) {
69
+ const overlay = new Overlay();
70
+ DIVEWebXR._overlay = overlay;
71
+ }
72
+ DIVEWebXR._options.domOverlay = { root: DIVEWebXR._overlay.Element };
73
+
74
+ // request session
75
+ const session = await navigator.xr.requestSession(
76
+ 'immersive-ar',
77
+ this._options,
78
+ );
79
+ session.addEventListener('end', () => {
80
+ this._onSessionEnded();
81
+ });
82
+
83
+ // build up session
84
+ renderer.xr.setReferenceSpaceType(this._referenceSpaceType);
85
+ await renderer.xr.setSession(session);
86
+ DIVEWebXR._overlay.Element.style.display = '';
87
+ this._session = session;
88
+
89
+ // add end session event listener
90
+ DIVEWebXR._overlay.CloseButton.addEventListener('click', () =>
91
+ this.End(),
92
+ );
93
+
94
+ // start session
95
+ await this._onSessionStarted();
96
+
97
+ return Promise.resolve();
98
+ }
99
+
100
+ public static Update(_time: DOMHighResTimeStamp, frame: XRFrame): void {
101
+ if (!this._session) return;
102
+
103
+ if (this._xrController) {
104
+ this._xrController.Update(frame);
105
+ }
106
+ }
107
+
108
+ public static End(): void {
109
+ if (!this._session) return;
110
+ this._session.end();
111
+ }
112
+
113
+ private static async _onSessionStarted(): Promise<void> {
114
+ if (!this._session) return;
115
+
116
+ // add update callback to render loop
117
+ this._renderCallbackId = this._renderer.AddPreRenderCallback(
118
+ (time: DOMHighResTimeStamp, frame: XRFrame) => {
119
+ this.Update(time, frame);
120
+ },
121
+ );
122
+
123
+ this._xrController = new DIVEWebXRController(
124
+ this._session,
125
+ this._renderer,
126
+ this._scene,
127
+ );
128
+ await this._xrController.Init().catch(() => {
129
+ this.End();
130
+ });
131
+
132
+ return Promise.resolve();
133
+ }
134
+
135
+ private static _onSessionEnded(): void {
136
+ if (!this._session) return;
137
+
138
+ if (this._xrController) {
139
+ this._xrController.Dispose();
140
+ }
141
+
142
+ // remove Update() callback
143
+ if (this._renderCallbackId) {
144
+ this._renderer.RemovePreRenderCallback(this._renderCallbackId);
145
+ this._renderCallbackId = null;
146
+ }
147
+
148
+ // disable XR on renderer to restore canvas rendering
149
+ this._renderer.xr.enabled = false;
150
+
151
+ // resize renderer
152
+ const canvasWrapper = this._renderer.domElement.parentElement;
153
+ if (canvasWrapper) {
154
+ const { clientWidth, clientHeight } = canvasWrapper;
155
+ this._renderer.OnResize(clientWidth, clientHeight);
156
+
157
+ // resize camera
158
+ this._controller.object.OnResize(clientWidth, clientHeight);
159
+ }
160
+
161
+ // reset camera
162
+ this._controller.object.position.copy(this._cameraPosition);
163
+ this._controller.target.copy(this._cameraTarget);
164
+
165
+ // reset camera values
166
+ this._cameraPosition.set(0, 0, 0);
167
+ this._cameraTarget.set(0, 0, 0);
168
+
169
+ // dispose xr scene
170
+ this._scene.DisposeXR();
171
+
172
+ this._session.removeEventListener('end', this._onSessionEnded);
173
+ DIVEWebXR._overlay!.Element.style.display = 'none';
174
+ this._session = null;
175
+ }
176
+ }