diff --git a/package-lock.json b/package-lock.json
index f1d6e04..b84ead5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,9 @@
"dependencies": {
"@aws-amplify/cli-extensibility-helper": "^3.0.1",
"@aws-amplify/ui-react": "^5.3.3",
+ "@babylonjs/core": "^7.34.4",
+ "@babylonjs/loaders": "^7.34.4",
+ "@babylonjs/materials": "^7.37.0",
"@blueprintjs/core": "^4.17.8",
"@blueprintjs/icons": "^4.14.5",
"@emotion/react": "^11.10.6",
@@ -30,6 +33,7 @@
"assign-deep": "^1.0.1",
"aws-amplify": "^5.0.23",
"aws-amplify-react": "^5.1.9",
+ "babylonjs": "^7.34.4",
"bootstrap": "^5.2.3",
"caniuse-lite": "^1.0.30001487",
"cypress-file-upload": "^5.0.8",
@@ -50,6 +54,7 @@
"query-string": "^8.1.0",
"react": "^18.2.0",
"react-3d-viewer": "^1.0.12",
+ "react-babylonjs": "^3.2.1",
"react-csv": "^2.2.2",
"react-dom": "^18.2.0",
"react-focus-lock": "^2.9.4",
@@ -12785,6 +12790,41 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babylonjs/core": {
+ "version": "7.34.4",
+ "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-7.34.4.tgz",
+ "integrity": "sha512-RwAEixWrTPmxN8WCtBav/fNveejRK5f9Cmc88qMiE3jc0HegVS+WarDS5yldMeIQKSuzOVaRfZo1xEY3CTmmFA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@babylonjs/gui": {
+ "version": "7.34.4",
+ "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-7.34.4.tgz",
+ "integrity": "sha512-yADpmCMSAaLS7vGBtViB8HyDfkBkJ4aMZPspwhtjM5iVqNKkkrQ2cvtVHI69nAO5NLwwOOSlYgHDDhTaAGMWPA==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "peerDependencies": {
+ "@babylonjs/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babylonjs/loaders": {
+ "version": "7.34.4",
+ "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-7.34.4.tgz",
+ "integrity": "sha512-CZfwtJXOZf5JrABbzxZ9s/5Ig8WZfS3XmYP69fAHoxcVedUNopctimM6KhjJYipeknbm1FUKMJ8TBBT/Tv8lig==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "@babylonjs/core": "^7.0.0",
+ "babylonjs-gltf2interface": "^7.0.0"
+ }
+ },
+ "node_modules/@babylonjs/materials": {
+ "version": "7.37.0",
+ "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-7.37.0.tgz",
+ "integrity": "sha512-a8vmj2+2moiaV6pU9IfAa1CMz56ydE5hk4LhvZ4kLpfWCH+lrcApzZKKskBTFIAKBZ1NJzwsKEh99ELiDlJ1NA==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "@babylonjs/core": "^7.0.0"
+ }
+ },
"node_modules/@bcoe/v8-coverage": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
@@ -21371,6 +21411,20 @@
"babel-plugin-transform-react-remove-prop-types": "^0.4.24"
}
},
+ "node_modules/babylonjs": {
+ "version": "7.34.4",
+ "resolved": "https://registry.npmjs.org/babylonjs/-/babylonjs-7.34.4.tgz",
+ "integrity": "sha512-ejssY7KbbLrSYtxf+2MdoPezpMh+cCtyf/ZMutdNNYlvFdIS+QUWaNad3GSRABHeBrRvthQtCSsYZx0+wp/+Sw==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/babylonjs-gltf2interface": {
+ "version": "7.34.4",
+ "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-7.34.4.tgz",
+ "integrity": "sha512-9K+gfJZ3Vn9AYVk8zrJeXPIzghCChDPH85sI+22VKhsnnyUfZs5lbO+muShOTrlBiOkCQQDyL2S8vOt5D1OIYg==",
+ "license": "Apache-2.0",
+ "peer": true
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -38888,6 +38942,29 @@
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
+ "node_modules/react-babylonjs": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/react-babylonjs/-/react-babylonjs-3.2.1.tgz",
+ "integrity": "sha512-nre7ArbP5mKmJK4VZ1Qs2qG+e1t/YlurmaE0O/lZUexwudOnbL17BzkHJl2jpu0h9m7otoqVdeKw9A6bDtsV5Q==",
+ "license": "MIT",
+ "dependencies": {
+ "react-reconciler": "^0.27.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "@babylonjs/core": "5.x || 6.x || 7.x",
+ "@babylonjs/gui": "5.x || 6.x || 7.x",
+ "react": ">=18",
+ "react-dom": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/react-beautiful-dnd": {
"version": "13.1.1",
"resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz",
@@ -39442,6 +39519,31 @@
"react-dom": "^16 || ^17 || ^18"
}
},
+ "node_modules/react-reconciler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz",
+ "integrity": "sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.21.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0"
+ }
+ },
+ "node_modules/react-reconciler/node_modules/scheduler": {
+ "version": "0.21.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
+ "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
"node_modules/react-redux": {
"version": "7.2.9",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz",
@@ -58171,6 +58273,30 @@
"to-fast-properties": "^2.0.0"
}
},
+ "@babylonjs/core": {
+ "version": "7.34.4",
+ "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-7.34.4.tgz",
+ "integrity": "sha512-RwAEixWrTPmxN8WCtBav/fNveejRK5f9Cmc88qMiE3jc0HegVS+WarDS5yldMeIQKSuzOVaRfZo1xEY3CTmmFA=="
+ },
+ "@babylonjs/gui": {
+ "version": "7.34.4",
+ "resolved": "https://registry.npmjs.org/@babylonjs/gui/-/gui-7.34.4.tgz",
+ "integrity": "sha512-yADpmCMSAaLS7vGBtViB8HyDfkBkJ4aMZPspwhtjM5iVqNKkkrQ2cvtVHI69nAO5NLwwOOSlYgHDDhTaAGMWPA==",
+ "peer": true,
+ "requires": {}
+ },
+ "@babylonjs/loaders": {
+ "version": "7.34.4",
+ "resolved": "https://registry.npmjs.org/@babylonjs/loaders/-/loaders-7.34.4.tgz",
+ "integrity": "sha512-CZfwtJXOZf5JrABbzxZ9s/5Ig8WZfS3XmYP69fAHoxcVedUNopctimM6KhjJYipeknbm1FUKMJ8TBBT/Tv8lig==",
+ "requires": {}
+ },
+ "@babylonjs/materials": {
+ "version": "7.37.0",
+ "resolved": "https://registry.npmjs.org/@babylonjs/materials/-/materials-7.37.0.tgz",
+ "integrity": "sha512-a8vmj2+2moiaV6pU9IfAa1CMz56ydE5hk4LhvZ4kLpfWCH+lrcApzZKKskBTFIAKBZ1NJzwsKEh99ELiDlJ1NA==",
+ "requires": {}
+ },
"@bcoe/v8-coverage": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
@@ -64766,6 +64892,17 @@
"babel-plugin-transform-react-remove-prop-types": "^0.4.24"
}
},
+ "babylonjs": {
+ "version": "7.34.4",
+ "resolved": "https://registry.npmjs.org/babylonjs/-/babylonjs-7.34.4.tgz",
+ "integrity": "sha512-ejssY7KbbLrSYtxf+2MdoPezpMh+cCtyf/ZMutdNNYlvFdIS+QUWaNad3GSRABHeBrRvthQtCSsYZx0+wp/+Sw=="
+ },
+ "babylonjs-gltf2interface": {
+ "version": "7.34.4",
+ "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-7.34.4.tgz",
+ "integrity": "sha512-9K+gfJZ3Vn9AYVk8zrJeXPIzghCChDPH85sI+22VKhsnnyUfZs5lbO+muShOTrlBiOkCQQDyL2S8vOt5D1OIYg==",
+ "peer": true
+ },
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -77795,6 +77932,14 @@
}
}
},
+ "react-babylonjs": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/react-babylonjs/-/react-babylonjs-3.2.1.tgz",
+ "integrity": "sha512-nre7ArbP5mKmJK4VZ1Qs2qG+e1t/YlurmaE0O/lZUexwudOnbL17BzkHJl2jpu0h9m7otoqVdeKw9A6bDtsV5Q==",
+ "requires": {
+ "react-reconciler": "^0.27.0"
+ }
+ },
"react-beautiful-dnd": {
"version": "13.1.1",
"resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz",
@@ -78218,6 +78363,25 @@
"quill": "^1.3.7"
}
},
+ "react-reconciler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz",
+ "integrity": "sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.21.0"
+ },
+ "dependencies": {
+ "scheduler": {
+ "version": "0.21.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
+ "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
+ "requires": {
+ "loose-envify": "^1.1.0"
+ }
+ }
+ }
+ },
"react-redux": {
"version": "7.2.9",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz",
diff --git a/package.json b/package.json
index e692c57..e17c278 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,9 @@
"dependencies": {
"@aws-amplify/cli-extensibility-helper": "^3.0.1",
"@aws-amplify/ui-react": "^5.3.3",
+ "@babylonjs/core": "^7.34.4",
+ "@babylonjs/loaders": "^7.34.4",
+ "@babylonjs/materials": "^7.37.0",
"@blueprintjs/core": "^4.17.8",
"@blueprintjs/icons": "^4.14.5",
"@emotion/react": "^11.10.6",
@@ -25,6 +28,7 @@
"assign-deep": "^1.0.1",
"aws-amplify": "^5.0.23",
"aws-amplify-react": "^5.1.9",
+ "babylonjs": "^7.34.4",
"bootstrap": "^5.2.3",
"caniuse-lite": "^1.0.30001487",
"cypress-file-upload": "^5.0.8",
@@ -45,6 +49,7 @@
"query-string": "^8.1.0",
"react": "^18.2.0",
"react-3d-viewer": "^1.0.12",
+ "react-babylonjs": "^3.2.1",
"react-csv": "^2.2.2",
"react-dom": "^18.2.0",
"react-focus-lock": "^2.9.4",
diff --git a/src/components/BabylonElement.jsx b/src/components/BabylonElement.jsx
new file mode 100644
index 0000000..81b581e
--- /dev/null
+++ b/src/components/BabylonElement.jsx
@@ -0,0 +1,147 @@
+import React, { useEffect } from "react";
+import { registerBuiltInLoaders } from "@babylonjs/loaders/dynamic";
+import * as BABYLON from "@babylonjs/core";
+import { GridMaterial } from "@babylonjs/materials/";
+import "../css/_3dViewer.scss";
+
+const BabylonElement = (props) => {
+ const disableScroll = () => {
+ [document.body, document.html].forEach((el) => {
+ el?.classList.add("no-scroll");
+ });
+ };
+
+ const enableScroll = () => {
+ [document.body, document.html].forEach((el) => {
+ el?.classList.remove("no-scroll");
+ });
+ };
+
+ const addListeners = (canvas, engine) => {
+ canvas.addEventListener("mouseover", disableScroll);
+ canvas.addEventListener("mouseout", enableScroll);
+
+ window.addEventListener("resize", () => {
+ engine.resize();
+ });
+ };
+
+ const removeListeners = (canvas, engine) => {
+ canvas.removeEventListener("mouseover", disableScroll);
+ canvas.removeEventListener("mouseout", enableScroll);
+
+ window.removeEventListener("resize", () => {
+ engine.resize();
+ });
+ };
+
+ const createScene = async (canvas, engine, modelURL) => {
+ const AXES_LENGTH = 10;
+ const scene = new BABYLON.Scene(engine);
+ scene.environmentTexture = BABYLON.CubeTexture.CreateFromPrefilteredData(
+ props.env,
+ scene
+ );
+ const ground = BABYLON.MeshBuilder.CreateGround(
+ "ground",
+ { width: AXES_LENGTH, height: AXES_LENGTH, updatable: false },
+ scene
+ );
+ const grid = new GridMaterial("grid", scene);
+ grid.backFaceCulling = false;
+ grid.mainColor = BABYLON.Color3.White();
+ grid.lineColor = BABYLON.Color3.White();
+ grid.opacity = 0.25;
+
+ ground.material = grid;
+ ground.alwaysSelectAsActiveMesh = true;
+ ground.isPickable = false;
+
+ // scene.clearColor = new BABYLON.Color4(0,0,0,1);
+ const model = await loadModel(scene, modelURL);
+ const modelDimensions = model.ellipsoid;
+ const modelMaxSize = Math.max(
+ modelDimensions._x,
+ modelDimensions._y,
+ modelDimensions._z
+ );
+
+ model.position = new BABYLON.Vector3(0, modelDimensions._y, 0);
+ const camera = new BABYLON.ArcRotateCamera(
+ "camera",
+ 0,
+ modelDimensions._y,
+ modelMaxSize * 2,
+ new BABYLON.Vector3(0, modelDimensions._y, 0),
+ scene
+ );
+ camera.setPosition(
+ new BABYLON.Vector3(0, modelDimensions._y, modelMaxSize * 2)
+ );
+ camera.setTarget(
+ new BABYLON.Vector3(0, modelDimensions._y, modelMaxSize * 2)
+ );
+ camera.wheelPrecision = 100;
+ camera.lowerRadiusLimit = modelMaxSize;
+ camera.upperRadiusLimit = modelMaxSize * 10;
+ camera.attachControl(canvas, true);
+ camera.minZ = 0.1;
+
+ engine.runRenderLoop(function () {
+ camera.setTarget(model.position);
+ scene.render();
+ });
+ };
+
+ const createCanvas = (canvasWrapper) => {
+ const canvas = document.createElement("canvas");
+ canvas.style.width = "100%";
+ canvas.style.height = "100%";
+ canvas.id = "three-d-canvas";
+ canvasWrapper.innerHTML = "";
+ canvasWrapper.appendChild(canvas);
+
+ return canvas;
+ };
+
+ const loadModel = async (scene, url) => {
+ const filename = url.split("/").pop();
+ const path = url.replace(filename, "");
+ const response = await BABYLON.SceneLoader.ImportMeshAsync(
+ null,
+ path,
+ filename,
+ scene
+ );
+ const model = response.meshes[0];
+
+ return model;
+ };
+
+ useEffect(() => {
+ registerBuiltInLoaders();
+ const canvasWrapper = document.getElementById("canvas-wrapper");
+ const canvas = createCanvas(canvasWrapper);
+ const engine = new BABYLON.Engine(canvas, true);
+
+ createScene(canvas, engine, props.model);
+
+ addListeners(canvas, engine);
+
+ return () => {
+ removeListeners(canvas, engine);
+ enableScroll();
+ engine.dispose();
+ };
+ }, [props.model]);
+
+ return (
+