@preference-sl/pref-viewer 2.11.0-beta.11 → 2.11.0-beta.13

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/package.json CHANGED
@@ -1,56 +1,65 @@
1
1
  {
2
- "name": "@preference-sl/pref-viewer",
3
- "version": "2.11.0-beta.11",
4
- "description": "Web Component to preview GLTF models with Babylon.js",
5
- "author": "Alex Moreno Palacio <amoreno@preference.es>",
6
- "scripts": {
7
- "copy-css": "cpx \"src/css/**/*\" dist/css/",
8
- "release": "standard-version --releaseCommitMessageFormat \"chore(release): {{currentTag}} [skip ci]\"",
9
- "release:beta": "standard-version --prerelease beta --releaseCommitMessageFormat \"chore(release): {{currentTag}} [skip ci]\"",
10
- "build": "npm run copy-css && esbuild src/index.js --bundle --platform=node --format=esm --outfile=dist/bundle.js --sourcemap",
11
- "start": "npm run build && http-server -c-1 . -p 8080",
12
- "test": "vitest"
13
- },
14
- "repository": {
15
- "type": "git",
16
- "url": "git+https://bitbucket.org/preferencesl/pref-viewer.git"
17
- },
18
- "publishConfig": {
19
- "access": "public"
20
- },
21
- "license": "MIT",
22
- "type": "module",
23
- "main": "src/index.js",
24
- "types": "index.d.ts",
25
- "exports": {
26
- ".": {
27
- "import": "./src/index.js",
28
- "require": "./src/index.js"
2
+ "name": "@preference-sl/pref-viewer",
3
+ "version": "2.11.0-beta.13",
4
+ "description": "Web Component to preview GLTF models with Babylon.js",
5
+ "author": "Alex Moreno Palacio <amoreno@preference.es>",
6
+ "scripts": {
7
+ "build:prefweb": "webpack --config webpack.config.cjs --mode production",
8
+ "build:prefweb:dev": "webpack --config webpack.config.cjs --mode development",
9
+ "release": "standard-version --releaseCommitMessageFormat \"chore(release): {{currentTag}} [skip ci]\"",
10
+ "release:beta": "standard-version --prerelease beta --releaseCommitMessageFormat \"chore(release): {{currentTag}} [skip ci]\"",
11
+ "build": "esbuild src/index.js --bundle --platform=node --format=esm --outfile=dist/bundle.js --sourcemap",
12
+ "start": "npm run build && http-server -c-1 . -p 8080",
13
+ "test": "vitest"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://bitbucket.org/preferencesl/pref-viewer.git"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "license": "MIT",
23
+ "type": "module",
24
+ "main": "src/index.js",
25
+ "types": "index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "import": "./src/index.js",
29
+ "require": "./src/index.js"
30
+ }
31
+ },
32
+ "sideEffects": false,
33
+ "files": [
34
+ "src",
35
+ "index.d.ts"
36
+ ],
37
+ "dependencies": {
38
+ "@babylonjs/core": "^8.39.0",
39
+ "@babylonjs/gui": "^8.39.0",
40
+ "@babylonjs/loaders": "^8.39.0",
41
+ "@babylonjs/serializers": "^8.39.0",
42
+ "@panzoom/panzoom": "^4.6.0",
43
+ "babylonjs-gltf2interface": "^8.39.0",
44
+ "buffer": "^6.0.3",
45
+ "idb": "^8.0.3",
46
+ "is-svg": "^6.1.0",
47
+ "jszip": "^3.10.1",
48
+ "stream": "^0.0.3",
49
+ "string_decoder": "^1.3.0"
50
+ },
51
+ "devDependencies": {
52
+ "@babel/core": "^7.22.0",
53
+ "@babel/preset-env": "^7.22.0",
54
+ "babel-loader": "^9.2.1",
55
+ "clean-webpack-plugin": "^4.0.0",
56
+ "esbuild": "^0.25.10",
57
+ "http-server": "^14.1.1",
58
+ "jsdom": "^26.1.0",
59
+ "standard-version": "^9.5.0",
60
+ "terser-webpack-plugin": "^5.3.6",
61
+ "vitest": "^3.2.3",
62
+ "webpack": "^5.88.2",
63
+ "webpack-cli": "^5.1.4"
29
64
  }
30
- },
31
- "sideEffects": false,
32
- "files": [
33
- "src",
34
- "src/css",
35
- "index.d.ts"
36
- ],
37
- "dependencies": {
38
- "@babylonjs/core": "^8.36.1",
39
- "@babylonjs/gui": "^8.36.1",
40
- "@babylonjs/loaders": "^8.36.1",
41
- "@babylonjs/serializers": "^8.36.1",
42
- "@panzoom/panzoom": "^4.6.0",
43
- "babylonjs-gltf2interface": "^8.36.1",
44
- "idb": "^8.0.3",
45
- "is-svg": "^6.1.0",
46
- "jszip": "^3.10.1"
47
- },
48
- "devDependencies": {
49
- "cpx": "^1.5.0",
50
- "esbuild": "^0.25.10",
51
- "http-server": "^14.1.1",
52
- "jsdom": "^26.1.0",
53
- "standard-version": "^9.5.0",
54
- "vitest": "^3.2.3"
55
- }
56
65
  }
@@ -1,19 +1,27 @@
1
- import { Color3, HighlightLayer, Mesh, PickingInfo, PointerEventTypes, Scene } from "@babylonjs/core";
1
+ import { Color3, HighlightLayer, Mesh, PickingInfo, Scene } from "@babylonjs/core";
2
2
  import { AdvancedDynamicTexture } from "@babylonjs/gui";
3
- import { OpeningAnimation } from "./babylonjs-animation-opening.js";
3
+ import { PrefViewerColors } from "./styles.js";
4
+ import OpeningAnimation from "./babylonjs-animation-opening.js";
4
5
 
5
6
  /**
6
- * BabylonJSAnimationController - Manages animation playback and interactive highlighting for model containers in Babylon.js scenes.
7
+ * BabylonJSAnimationController - Manages animation playback, highlighting, and interactive controls for animated nodes in Babylon.js scenes.
7
8
  *
8
- * Responsibilities:
9
+ * Summary:
10
+ * This class detects, groups, and manages opening/closing animations for scene nodes, provides interactive highlighting of animated nodes and their meshes, and displays a GUI menu for animation control. It is designed for integration with product configurators and interactive 3D applications using Babylon.js.
11
+ *
12
+ * Key features:
9
13
  * - Detects and groups opening/closing animations in the scene.
10
14
  * - Tracks animated transformation nodes and their relationships to meshes.
11
- * - Highlights animated nodes and their child meshes when hovered.
12
- * - Displays and disposes the animation control menu (GUI) for animated nodes.
13
- * - Provides API for interaction and animation control.
15
+ * - Highlights animated nodes and their child meshes on pointer hover.
16
+ * - Displays and disposes the animation control menu (Babylon.js GUI) for animated nodes.
17
+ * - Provides public API for highlighting, showing the animation menu, and disposing resources.
18
+ * - Cleans up all resources and observers to prevent memory leaks.
14
19
  *
15
20
  * Public Methods:
16
21
  * - dispose(): Disposes all resources managed by the animation controller.
22
+ * - hightlightMeshes(pickingInfo): Highlights meshes that are children of an animated node when hovered.
23
+ * - hideMenu(): Hides and disposes the animation control menu (Babylon.js GUI) if it exists.
24
+ * - showMenu(pickingInfo): Displays the animation control menu for the animated node under the pointer.
17
25
  *
18
26
  * @class
19
27
  */
@@ -21,7 +29,7 @@ export default class BabylonJSAnimationController {
21
29
  #scene = null;
22
30
  #animatedNodes = [];
23
31
  #highlightLayer = null;
24
- #highlightColor = new Color3(0, 1, 0); // Color para resaltar los elementos animados (Verde)
32
+ #highlightColor = Color3.FromHexString(PrefViewerColors.primary);
25
33
  #advancedDynamicTexture = null;
26
34
  #openingAnimations = [];
27
35
 
@@ -32,7 +40,6 @@ export default class BabylonJSAnimationController {
32
40
  constructor(scene) {
33
41
  this.#scene = scene;
34
42
  this.#initializeAnimations();
35
- this.#setupPointerObservers();
36
43
  }
37
44
 
38
45
  /**
@@ -122,12 +129,38 @@ export default class BabylonJSAnimationController {
122
129
  return nodeId;
123
130
  };
124
131
 
132
+ /**
133
+ * ---------------------------
134
+ * Public methods
135
+ * ---------------------------
136
+ */
137
+
138
+ /**
139
+ * Disposes all resources managed by the animation controller.
140
+ * Cleans up the highlight layer, GUI texture, and internal animation/node lists.
141
+ * Should be called when the controller is no longer needed to prevent memory leaks.
142
+ * @public
143
+ */
144
+ dispose() {
145
+ if (this.#highlightLayer) {
146
+ this.#highlightLayer.dispose();
147
+ this.#highlightLayer = null;
148
+ }
149
+ this.hideMenu();
150
+ this.#animatedNodes = [];
151
+ this.#openingAnimations = [];
152
+ const observer = this.#scene.onPointerObservable._observers.find((observer) => observer.callback.name.includes("#onAnimationPointerObservable"));
153
+ if (observer) {
154
+ this.#scene.onPointerObservable.remove(observer);
155
+ }
156
+ }
157
+
125
158
  /**
126
159
  * Highlights meshes that are children of an animated node when hovered.
127
- * @private
160
+ * @public
128
161
  * @param {PickingInfo} pickingInfo - Raycast info from pointer position.
129
162
  */
130
- #hightlightMeshesForAnimation(pickingInfo) {
163
+ hightlightMeshes(pickingInfo) {
131
164
  if (!this.#highlightLayer) {
132
165
  this.#highlightLayer = new HighlightLayer("hl_animations", this.#scene);
133
166
  }
@@ -150,52 +183,32 @@ export default class BabylonJSAnimationController {
150
183
  }
151
184
 
152
185
  /**
153
- * Sets up pointer observers to highlight animated nodes on hover and show the animation menu on click.
154
- * @private
155
- */
156
- #setupPointerObservers() {
157
- if (this.#openingAnimations.length === 0) {
158
- return;
159
- }
160
- this.#scene.onPointerObservable.add(this.#onAnimationPointerObservable.bind(this));
161
- }
162
-
163
- /**
164
- * Handles pointer events in the Babylon.js scene for animation interaction.
165
- * On pointer move, highlights meshes belonging to animated nodes under the cursor.
166
- * On pointer up (click), disposes any existing GUI and displays the animation control menu for the selected node.
167
- *
168
- * @private
169
- * @param {PointerInfo} pointerInfo - The pointer event information from Babylon.js.
186
+ * Hides and disposes the animation control menu (Babylon.js GUI) if it exists.
187
+ * @public
188
+ * @returns {void}
170
189
  */
171
- #onAnimationPointerObservable(pointerInfo) {
172
- if (pointerInfo.type === PointerEventTypes.POINTERMOVE) {
173
- const pickingInfo = this.#scene.pick(pointerInfo.event.clientX, pointerInfo.event.clientY);
174
- this.#hightlightMeshesForAnimation(pickingInfo);
175
- }
176
- if (pointerInfo.type === PointerEventTypes.POINTERUP) {
177
- // Remove any previously created Babylon GUI
178
- if (this.#advancedDynamicTexture) {
179
- this.#advancedDynamicTexture.dispose();
180
- this.#advancedDynamicTexture = null;
181
- }
182
- const pickingInfo = this.#scene.pick(pointerInfo.event.clientX, pointerInfo.event.clientY);
183
- this.#showMenu(pickingInfo);
190
+ hideMenu() {
191
+ // Remove any previously created Babylon GUI
192
+ if (this.#advancedDynamicTexture) {
193
+ this.#advancedDynamicTexture.dispose();
194
+ this.#advancedDynamicTexture = null;
184
195
  }
185
196
  }
186
197
 
187
198
  /**
188
199
  * Displays the animation control menu for the animated node under the pointer.
189
- * @private
200
+ * @public
190
201
  * @param {PickingInfo} pickingInfo - Raycast info from pointer position.
191
202
  * @description
192
203
  * Creates the GUI if needed and invokes OpeningAnimation.showControls.
193
204
  */
194
- #showMenu(pickingInfo) {
205
+ showMenu(pickingInfo) {
195
206
  if (!pickingInfo?.hit && !pickingInfo?.pickedMesh) {
196
207
  return;
197
208
  }
198
209
 
210
+ this.hideMenu();
211
+
199
212
  const nodeId = this.#getNodeAnimatedByMesh(pickingInfo.pickedMesh);
200
213
  if (!nodeId) {
201
214
  return;
@@ -205,31 +218,8 @@ export default class BabylonJSAnimationController {
205
218
  return;
206
219
  }
207
220
  if (!this.#advancedDynamicTexture) {
208
- this.#advancedDynamicTexture = AdvancedDynamicTexture.CreateFullscreenUI("UI_Animation");
221
+ this.#advancedDynamicTexture = AdvancedDynamicTexture.CreateFullscreenUI("UI_Animation", true, this.#scene);
209
222
  }
210
223
  openingAnimation.showControls(this.#advancedDynamicTexture);
211
224
  }
212
-
213
- /**
214
- * Disposes all resources managed by the animation controller.
215
- * Cleans up the highlight layer, GUI texture, and internal animation/node lists.
216
- * Should be called when the controller is no longer needed to prevent memory leaks.
217
- * @public
218
- */
219
- dispose() {
220
- if (this.#highlightLayer) {
221
- this.#highlightLayer.dispose();
222
- this.#highlightLayer = null;
223
- }
224
- if (this.#advancedDynamicTexture) {
225
- this.#advancedDynamicTexture.dispose();
226
- this.#advancedDynamicTexture = null;
227
- }
228
- this.#animatedNodes = [];
229
- this.#openingAnimations = [];
230
- const observer = this.#scene.onPointerObservable._observers.find((observer) => observer.callback.name.includes("#onAnimationPointerObservable"));
231
- if (observer) {
232
- this.#scene.onPointerObservable.remove(observer);
233
- }
234
- }
235
225
  }
@@ -1,5 +1,6 @@
1
1
  import { AdvancedDynamicTexture, Button, Control, Image, Slider, StackPanel } from "@babylonjs/gui";
2
- import { OpeningAnimation } from "./babylonjs-animation-opening.js";
2
+ import OpeningAnimation from "./babylonjs-animation-opening.js";
3
+ import { PrefViewerColors } from "./styles.js";
3
4
 
4
5
  /**
5
6
  * OpeningAnimationMenu - Manages and renders the animation control menu for opening/closing animations in a Babylon.js scene.
@@ -42,7 +43,7 @@ import { OpeningAnimation } from "./babylonjs-animation-opening.js";
42
43
  * menu.animationProgress = 0.5;
43
44
  * menu.animationLoop = true;
44
45
  */
45
- export class OpeningAnimationMenu {
46
+ export default class OpeningAnimationMenu {
46
47
  #animationState = OpeningAnimation.states.closed;
47
48
  #animationProgress = 0;
48
49
  #animationLoop = false;
@@ -57,7 +58,7 @@ export class OpeningAnimationMenu {
57
58
  // Style properties
58
59
  #buttonSize = 28;
59
60
  #buttonLoopPaddingLeft = 3;
60
- #colorActive = "#6BA53A";
61
+ #colorActive = PrefViewerColors.primary;
61
62
  #colorEnabled = "#333333";
62
63
  #colorDisabled = "#777777";
63
64
  #colorIcon = "#FFFFFF";
@@ -1,5 +1,5 @@
1
1
  import { AdvancedDynamicTexture } from "@babylonjs/gui";
2
- import { OpeningAnimationMenu } from "./babylonjs-animation-opening-menu.js";
2
+ import OpeningAnimationMenu from "./babylonjs-animation-opening-menu.js";
3
3
 
4
4
  /**
5
5
  * OpeningAnimation - Manages open/close animations for a model part (e.g., a door) in a Babylon.js scene.
@@ -48,7 +48,7 @@ import { OpeningAnimationMenu } from "./babylonjs-animation-opening-menu.js";
48
48
  * anim.showControls(adt);
49
49
  * anim.hideControls();
50
50
  */
51
- export class OpeningAnimation {
51
+ export default class OpeningAnimation {
52
52
  static states = {
53
53
  paused: 0,
54
54
  closed: 1,
@@ -138,11 +138,15 @@ export class OpeningAnimation {
138
138
  #goToOpened(useLoop = false) {
139
139
  this.#lastPausedFrame = this.#endFrame;
140
140
 
141
- if (this.#openAnimation._isStarted && !this.#openAnimation._isPaused) {
142
- this.#openAnimation.pause();
141
+ if (!this.#openAnimation._isStarted) {
142
+ this.#openAnimation.start();
143
143
  }
144
+ this.#openAnimation.pause();
144
145
  this.#openAnimation.goToFrame(this.#endFrame);
145
146
 
147
+ if (!this.#closeAnimation._isStarted) {
148
+ this.#closeAnimation.start();
149
+ }
146
150
  this.#closeAnimation.pause();
147
151
  this.#closeAnimation.goToFrame(this.#startFrame);
148
152
 
@@ -162,11 +166,15 @@ export class OpeningAnimation {
162
166
  #goToClosed(useLoop = false) {
163
167
  this.#lastPausedFrame = this.#startFrame;
164
168
 
165
- if (this.#closeAnimation._isStarted && !this.#closeAnimation._isPaused) {
166
- this.#closeAnimation.pause();
169
+ if (!this.#closeAnimation._isStarted) {
170
+ this.#closeAnimation.start();
167
171
  }
172
+ this.#closeAnimation.pause();
168
173
  this.#closeAnimation.goToFrame(this.#endFrame - this.#startFrame);
169
174
 
175
+ if (!this.#openAnimation._isStarted) {
176
+ this.#openAnimation.start();
177
+ }
170
178
  this.#openAnimation.pause();
171
179
  this.#openAnimation.goToFrame(this.#startFrame);
172
180
 
@@ -1,7 +1,7 @@
1
- import { ArcRotateCamera, AssetContainer, Camera, Color4, DirectionalLight, Engine, HDRCubeTexture, HemisphericLight, IblShadowsRenderPipeline, LoadAssetContainerAsync, MeshBuilder, PointLight, Scene, ShadowGenerator, Tools, Vector3, WebXRDefaultExperience, WebXRFeatureName, WebXRSessionManager } from "@babylonjs/core";
2
- import { DracoCompression } from "@babylonjs/core/Meshes/Compression/dracoCompression";
1
+ import { ArcRotateCamera, AssetContainer, Camera, Color4, DirectionalLight, Engine, HDRCubeTexture, HemisphericLight, IblShadowsRenderPipeline, LoadAssetContainerAsync, MeshBuilder, PointerEventTypes, PointLight, Scene, ShadowGenerator, Tools, Vector3, WebXRDefaultExperience, WebXRFeatureName, WebXRSessionManager } from "@babylonjs/core";
2
+ import { DracoCompression } from "@babylonjs/core/Meshes/Compression/dracoCompression.js";
3
3
  import "@babylonjs/loaders";
4
- import "@babylonjs/loaders/glTF/2.0/Extensions/KHR_draco_mesh_compression";
4
+ import "@babylonjs/loaders/glTF/2.0/Extensions/KHR_draco_mesh_compression.js";
5
5
  import { USDZExportAsync, GLTF2Export } from "@babylonjs/serializers";
6
6
 
7
7
  import GLTFResolver from "./gltf-resolver.js";
@@ -45,19 +45,37 @@ import BabylonJSAnimationController from "./babylonjs-animation-controller.js";
45
45
  * - #renderLoop(): Babylon.js render loop callback.
46
46
  * - #addStylesToARButton(): Styles AR button.
47
47
  * - #createXRExperience(): Initializes WebXR AR experience.
48
- * - #createCamera(), #createLights(), #initializeEnvironmentTexture(), #initializeIBLShadows(), #initializeShadows(): Scene setup.
49
- * - #setMaxSimultaneousLights(): Updates max simultaneous lights for materials.
50
- * - #enableInteraction(), #disableInteraction(): Canvas interaction handlers.
51
- * - #disposeEngine(): Disposes engine and resources.
52
- * - #onMouseWheel(event), #onKeyUp(event): Canvas event handlers.
53
- * - #setOptionsMaterial(), #setOptions_Materials(), #setOptions_Camera(): Applies material/camera options.
54
- * - #findContainerByName(), #addContainer(), #removeContainer(), #replaceContainer(): Container management.
55
- * - #getPrefViewer3DComponent(), #getPrefViewerComponent(): Custom element references.
56
- * - #updateVisibilityAttributeInComponents(): Updates parent visibility attributes.
57
- * - #setVisibilityOfWallAndFloorInModel(): Controls wall/floor mesh visibility.
58
- * - #stopRender(), #startRender(): Render loop control.
59
- * - #loadAssetContainer(), #loadContainers(): Asset loading.
60
- * - #downloadZip(): Generates and downloads a ZIP file.
48
+ * - #createCamera(): Creates and configures the main camera.
49
+ * - #createLights(): Creates and configures scene lights and shadows.
50
+ * - #initializeEnvironmentTexture(): Loads and sets the HDR environment texture.
51
+ * - #initializeIBLShadows(): Sets up IBL shadow pipeline and assigns meshes/materials.
52
+ * - #initializeShadows(): Sets up standard or IBL shadows for meshes.
53
+ * - #setMaxSimultaneousLights(): Updates max simultaneous lights for all materials.
54
+ * - #onPointerObservable(info): Handles pointer events and dispatches to pointer/mouse handlers.
55
+ * - #onPointerUp(event, pickInfo): Handles pointer up events (e.g., right-click for animation menu).
56
+ * - #onPointerMove(event, pickInfo): Handles pointer move events (e.g., mesh highlighting).
57
+ * - #onMouseWheel(event, pickInfo): Handles mouse wheel events for camera zoom.
58
+ * - #onKeyUp(event): Handles keyup events for download dialog and shortcuts.
59
+ * - #enableInteraction(): Adds canvas and scene interaction event listeners.
60
+ * - #disableInteraction(): Removes canvas and scene interaction event listeners.
61
+ * - #disposeEngine(): Disposes engine and releases all resources.
62
+ * - #setOptionsMaterial(optionMaterial): Applies a material option to relevant meshes.
63
+ * - #setOptions_Materials(): Applies all material options from configuration.
64
+ * - #setOptions_Camera(): Applies camera options from configuration.
65
+ * - #findContainerByName(name): Finds a container by its name.
66
+ * - #addContainer(container, updateVisibility): Adds a container to the scene and updates visibility.
67
+ * - #removeContainer(container, updateVisibility): Removes a container from the scene and updates visibility.
68
+ * - #replaceContainer(container, newAssetContainer): Replaces a container in the scene.
69
+ * - #getPrefViewer3DComponent(): Caches and retrieves the parent PREF-VIEWER-3D element.
70
+ * - #getPrefViewerComponent(): Caches and retrieves the parent PREF-VIEWER element.
71
+ * - #updateVisibilityAttributeInComponents(name, isVisible): Updates parent visibility attributes.
72
+ * - #setVisibilityOfWallAndFloorInModel(show): Controls wall/floor mesh visibility.
73
+ * - #stopRender(): Stops the Babylon.js render loop.
74
+ * - #startRender(): Starts the Babylon.js render loop.
75
+ * - #loadAssetContainer(container): Loads an asset container asynchronously.
76
+ * - #loadContainers(): Loads all asset containers and adds them to the scene.
77
+ * - #addDateToName(name): Appends the current date/time to a name string.
78
+ * - #downloadZip(files, name, comment, addDateInName): Generates and downloads a ZIP file.
61
79
  * - #openDownloadDialog(): Opens the modal download dialog.
62
80
  *
63
81
  * Notes:
@@ -401,13 +419,34 @@ export default class BabylonJSController {
401
419
  }
402
420
 
403
421
  /**
404
- * Sets up interaction handlers for the Babylon.js canvas.
422
+ * Handles pointer events observed on the Babylon.js scene.
423
+ * @private
424
+ * @param {PointerInfo} info - The pointer event information from Babylon.js.
425
+ * @returns {void}
426
+ */
427
+ #onPointerObservable(info) {
428
+ const pickInfo = this.#scene.pick(this.#scene.pointerX, this.#scene.pointerY);
429
+ if (info.type === PointerEventTypes.POINTERUP) {
430
+ this.#onPointerUp(info.event, pickInfo);
431
+ } else if (info.type === PointerEventTypes.POINTERMOVE) {
432
+ this.#onPointerMove(info.event, pickInfo);
433
+ } else if (info.type === PointerEventTypes.POINTERWHEEL) {
434
+ this.#onMouseWheel(info.event, pickInfo);
435
+ }
436
+ }
437
+
438
+ /**
439
+ * Sets up interaction handlers for the Babylon.js canvas and scene.
405
440
  * @private
406
441
  * @returns {void}
407
442
  */
408
443
  #enableInteraction() {
409
- this.#canvas.addEventListener("wheel", this.#onMouseWheel.bind(this));
410
- this.#canvas.addEventListener("keyup", this.#onKeyUp.bind(this));
444
+ if (this.#canvas) {
445
+ this.#canvas.addEventListener("keyup", this.#onKeyUp.bind(this));
446
+ }
447
+ if (this.#scene) {
448
+ this.#scene.onPointerObservable.add(this.#onPointerObservable.bind(this));
449
+ }
411
450
  }
412
451
 
413
452
  /**
@@ -416,8 +455,12 @@ export default class BabylonJSController {
416
455
  * @returns {void}
417
456
  */
418
457
  #disableInteraction() {
419
- this.#canvas.removeEventListener("wheel", this.#onMouseWheel.bind(this));
420
- this.#canvas.removeEventListener("keyup", this.#onKeyUp.bind(this));
458
+ if (this.#canvas) {
459
+ this.#canvas.removeEventListener("keyup", this.#onKeyUp.bind(this));
460
+ }
461
+ if (this.#scene !== null) {
462
+ this.#scene.onPointerObservable.removeCallback(this.#onPointerObservable.bind(this));
463
+ }
421
464
  }
422
465
 
423
466
  /**
@@ -436,18 +479,37 @@ export default class BabylonJSController {
436
479
  this.#XRExperience = null;
437
480
  }
438
481
 
482
+ /**
483
+ * Handles keyup events on the Babylon.js canvas for triggering model and scene downloads.
484
+ * @private
485
+ * @param {KeyboardEvent} event - The keyup event.
486
+ * @returns {void}
487
+ */
488
+ #onKeyUp(event) {
489
+ // CTRL + ALT + letter
490
+ if (event.ctrlKey && event.altKey && event.key !== undefined) {
491
+ switch (event.key.toLowerCase()) {
492
+ case "d":
493
+ this.#openDownloadDialog();
494
+ break;
495
+ default:
496
+ break;
497
+ }
498
+ }
499
+ }
500
+
439
501
  /**
440
502
  * Handles mouse wheel events on the Babylon.js canvas for zooming the camera.
441
503
  * @private
442
504
  * @param {WheelEvent} event - The mouse wheel event.
443
- * @returns {void|false}
505
+ * @param {Object} pickInfo - The result of the scene pick operation (not used in this method).
506
+ * @returns {void|false} Returns false if there is no active camera; otherwise, void.
444
507
  */
445
- #onMouseWheel(event) {
446
- if (!this.#scene || !this.#camera) {
508
+ #onMouseWheel(event, pickInfo) {
509
+ if (!this.#scene?.activeCamera) {
447
510
  return false;
448
511
  }
449
- //const pick = this.#scene.pick(this.#scene.pointerX, this.#scene.pointerY);
450
- //this.#camera.target = pick.hit ? pick.pickedPoint.clone() : this.#camera.target;
512
+ //this.#scene.activeCamera.target = pickInfo.hit ? pickInfo.pickedPoint.clone() : this.#scene.activeCamera.target;
451
513
  if (!this.#scene.activeCamera.metadata?.locked) {
452
514
  this.#scene.activeCamera.inertialRadiusOffset -= event.deltaY * this.#scene.activeCamera.wheelPrecision * 0.001;
453
515
  }
@@ -455,24 +517,35 @@ export default class BabylonJSController {
455
517
  }
456
518
 
457
519
  /**
458
- * Handles keyup events on the Babylon.js canvas for triggering model and scene downloads.
520
+ * Handles pointer up events on the Babylon.js scene.
459
521
  * @private
460
- * @param {KeyboardEvent} event - The keyup event.
522
+ * @param {PointerEvent} event - The pointer up event.
523
+ * @param {PickInfo} pickInfo - The result of the scene pick operation.
461
524
  * @returns {void}
462
525
  */
463
- #onKeyUp(event) {
464
- // CTRL + ALT + letter
465
- if (event.ctrlKey && event.altKey && event.key !== undefined) {
466
- switch (event.key.toLowerCase()) {
467
- case "d":
468
- this.#openDownloadDialog();
469
- break;
470
- default:
471
- break;
526
+ #onPointerUp(event, pickInfo) {
527
+ if (this.#babylonJSAnimationController) {
528
+ this.#babylonJSAnimationController.hideMenu();
529
+ // Right click for showing animation menu
530
+ if (event.button === 2) {
531
+ this.#babylonJSAnimationController.showMenu(pickInfo);
472
532
  }
473
533
  }
474
534
  }
475
535
 
536
+ /**
537
+ * Handles pointer move events on the Babylon.js scene.
538
+ * @private
539
+ * @param {PointerEvent} event - The pointer move event.
540
+ * @param {PickInfo} pickInfo - The result of the scene pick operation.
541
+ * @returns {void}
542
+ */
543
+ #onPointerMove(event, pickInfo) {
544
+ if (this.#babylonJSAnimationController) {
545
+ this.#babylonJSAnimationController.hightlightMeshes(pickInfo);
546
+ }
547
+ }
548
+
476
549
  /**
477
550
  * Applies material options from the configuration to the relevant meshes.
478
551
  * @private