Commit af3bbe7d authored by Alfakhori's avatar Alfakhori
Browse files

return to 126

parent ae2a0557
Pipeline #12486 passed with stage
in 26 seconds
function downloadScene() {
const placedModels = window.getAllPlacedModels().map((model) => {
const { lat, lng } = window.threeToLeaflet(
model.position.x,
model.position.z,
);
const placedModels = getAllPlacedModels().map(model => {
const { lat, lng } = threeToLeaflet(model.position.x, model.position.z);
return {
name: model.modelConfig.name,
position: model.position,
......@@ -11,10 +8,10 @@ function downloadScene() {
scale: model.scale,
lat: lat,
lng: lng,
};
}
});
console.log("Modelle: " + placedModels.length);
console.log("Modelle: " + placedModels.length)
if (placedModels.length === 0) {
showInfoDialog("Szene ist leer");
return;
......@@ -30,7 +27,7 @@ function downloadScene() {
lat: geoLocation.latitude,
lng: geoLocation.longitude,
models: placedModels,
};
}
const sceneData = JSON.stringify(data, null, 1);
const blob = new Blob([sceneData], { type: "application/json" });
......@@ -48,20 +45,20 @@ function downloadScene() {
}
function loadSceneFromFile(loadedData) {
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.accept = "application/json";
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'application/json';
fileInput.addEventListener("change", function (event) {
fileInput.addEventListener('change', function(event) {
const file = event.target.files[0];
const fileName = file ? file.name : "Keine Datei ausgewählt";
const fileName = file ? file.name : "Keine Datei ausgewählt"
if (!file) {
console.log("Keine Datei ausgewählt");
return;
}
const reader = new FileReader();
reader.onload = function (e) {
reader.onload = function(e) {
try {
const sceneData = JSON.parse(e.target.result);
loadedData(sceneData, fileName);
......@@ -74,7 +71,3 @@ function loadSceneFromFile(loadedData) {
});
fileInput.click();
}
\ No newline at end of file
/* Expose functions used by the existing HTML and ar_start.js. */
window.downloadScene = downloadScene;
window.loadSceneFromFile = loadSceneFromFile;
import * as THREE from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
/* ========================= */
/* GLOBALE VARIABLEN */
/* ========================= */
......@@ -9,43 +6,44 @@ let selectedPlacedModel = null;
let currentSession = null;
let reticle = null;
let scene, camera;
let xrRenderer = null;
let geoLocation;
const modelCache = new Map();
// PERFORMANCE: Reuse one GLTFLoader instead of creating
// a new loader every time a model is placed.
const gltfLoader = new GLTFLoader();
const gltfLoader = new THREE.GLTFLoader();
const menus = [
"menu-bar",
"add-menu",
"edit-menu",
"options-menu",
"map-window",
'menu-bar',
'add-menu',
'edit-menu',
'options-menu',
'map-window'
];
// PERFORMANCE: Cache menu DOM elements once instead of
// repeatedly calling document.getElementById() in showMenu().
const menuElements = {};
menus.forEach((id) => {
menus.forEach(id => {
menuElements[id] = document.getElementById(id);
});
/* ========================= */
/* MODELLE */
/* ========================= */
let models = {
bench: {
name: "Bench",
image: "assets/previewImages/bench.PNG",
file: "https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/bench_model/scene.gltf",
scale: { x: 0.1, y: 0.1, z: 0.1 },
minScale: 0.05,
maxScale: 0.5,
maxScale: 0.5
},
trashbin: {
......@@ -54,7 +52,7 @@ let models = {
file: "https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/trash_model/scene.gltf",
scale: { x: 0.03, y: 0.03, z: 0.03 },
minScale: 0.01,
maxScale: 0.1,
maxScale: 0.1
},
lantern: {
......@@ -63,7 +61,7 @@ let models = {
file: "https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/park_light_model/scene.gltf",
scale: { x: 0.5, y: 0.5, z: 0.5 },
minScale: 0.2,
maxScale: 5,
maxScale: 5
},
telephone_box: {
......@@ -72,7 +70,7 @@ let models = {
file: "https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/telephone_box_model/scene.gltf",
scale: { x: 0.5, y: 0.5, z: 0.5 },
minScale: 0.05,
maxScale: 1,
maxScale: 1
},
fire_hydrant_model: {
......@@ -81,7 +79,7 @@ let models = {
file: "https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/fire_hydrant_model/scene.gltf",
scale: { x: 0.3, y: 0.3, z: 0.3 },
minScale: 0.1,
maxScale: 1,
maxScale: 1
},
statue: {
......@@ -90,7 +88,7 @@ let models = {
file: "https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/statue_model/scene.gltf",
scale: { x: 0.5, y: 0.5, z: 0.5 },
minScale: 0.05,
maxScale: 2,
maxScale: 2
},
fountain: {
......@@ -99,27 +97,37 @@ let models = {
file: "https://transfer.hft-stuttgart.de/gitlab/geovistoogsi/ar/-/raw/master/public/assets/models/fountain_model/scene.gltf",
scale: { x: 0.001, y: 0.001, z: 0.001 },
minScale: 0.0005,
maxScale: 0.005,
},
maxScale: 0.005
}
};
/* ========================= */
/* INITIALISIERUNG */
/* ========================= */
window.onload = () => {
initializeAddMenu();
// Allen Buttons den Sound hinzufügen
const buttons = document.querySelectorAll("button, .menu-item");
const buttons = document.querySelectorAll(
"button, .menu-item"
);
buttons.forEach((button) => {
button.addEventListener("click", playButtonSound);
buttons.forEach(button => {
button.addEventListener(
"click",
playButtonSound
);
});
};
function initializeAddMenu() {
const addMenu = document.getElementById("add-menu");
const addMenu =
document.getElementById('add-menu');
addMenu.innerHTML =
Object.entries(models)
......@@ -135,9 +143,10 @@ function initializeAddMenu() {
alt="${model.name}"
/>
</div>
`,
`
)
.join("") +
.join('') +
`
<div
class="menu-item"
......@@ -151,183 +160,300 @@ function initializeAddMenu() {
`;
}
/* ========================= */
/* MENÜ-STEUERUNG */
/* ========================= */
function showMenu(menuId) {
const isMapWindow = menuId === "map-window";
const isMapWindow =
menuId === 'map-window';
// Check location before opening map
if (isMapWindow && !geoLocation) {
console.log("Standort nicht geladen");
showInfoDialog("Ihr Standort wurde noch nicht geladen");
showInfoDialog(
"Ihr Standort wurde noch nicht geladen"
);
return;
}
// PERFORMANCE: Use cached menu elements instead of
// repeatedly querying the DOM.
menus.forEach((id) => {
const element = menuElements[id];
menus.forEach(id => {
const element =
menuElements[id];
if (element) {
element.style.display = id === menuId ? "flex" : "none";
element.style.display =
id === menuId
? 'flex'
: 'none';
}
});
closeDynamicMenu();
// Menu-specific actions
if (menuId === "menu-bar") {
if (menuId === 'menu-bar') {
clearSelectedModel();
} else if (isMapWindow) {
// Initialize map
init_map();
// Leaflet needs to know the actual size of the
// visible map container.
setTimeout(() => {
if (typeof centerOnPlan === "function") {
if (
typeof centerOnPlan === 'function'
) {
centerOnPlan();
}
}, 100);
}
}
/* ============================================================ */
/* Close map and return to AR scene */
/* ============================================================ */
function closeMap() {
const mapWindow = menuElements["map-window"];
const mapWindow =
menuElements['map-window'];
if (mapWindow) {
mapWindow.style.display = "none";
mapWindow.style.display =
"none";
}
// Show the main AR menu again
const menuBar = menuElements["menu-bar"];
const menuBar =
menuElements['menu-bar'];
if (menuBar) {
menuBar.style.display = "flex";
menuBar.style.display =
"flex";
}
// Stop the map update interval
if (typeof intervalId !== "undefined") {
if (
typeof intervalId !== 'undefined'
) {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
}
// Clear selected model
clearSelectedModel();
}
function closeDynamicMenu() {
const dynamicMenu = document.getElementById("dynamic-menu");
const dynamicMenu =
document.getElementById(
"dynamic-menu"
);
if (dynamicMenu) {
dynamicMenu.style.display = "none";
dynamicMenu.style.display =
"none";
}
}
function showInfoDialog(message) {
const infoDialog = document.getElementById("info-dialog");
const infoText = document.getElementById("info-text");
const infoDialog =
document.getElementById(
"info-dialog"
);
infoText.textContent = message;
const infoText =
document.getElementById(
"info-text"
);
infoText.textContent =
message;
infoDialog.style.display = "flex";
infoDialog.style.display =
"flex";
}
function closeInfoDialog() {
const infoDialog = document.getElementById("info-dialog");
infoDialog.style.display = "none";
const infoDialog =
document.getElementById(
"info-dialog"
);
infoDialog.style.display =
"none";
}
/* ========================= */
/* MODELL-HANDLING */
/* ========================= */
function getAllPlacedModels() {
return scene.children.filter((child) => child.isPlacedModel === true);
return scene.children.filter(
child =>
child.isPlacedModel === true
);
}
function loadModel(filePath) {
placeModel(filePath, reticle.position, (model) => {
selectedPlacedModel = model;
placeModel(
filePath,
reticle.position,
(model) => {
selectedPlacedModel =
model;
highlightSelectedModel();
showMenu("edit-menu");
});
showMenu('edit-menu');
}
);
}
function placeModel(filePath, position, onPlace = null) {
const modelConfig = Object.values(models).find(
(model) => model.file === filePath,
function placeModel(
filePath,
position,
onPlace = null
) {
const modelConfig =
Object.values(models)
.find(
model =>
model.file === filePath
);
// PERFORMANCE: Reuse the global GLTFLoader.
// Do not create a new loader for every model.
if (modelCache.has(filePath)) {
// Use the already loaded model
const cachedModel = modelCache.get(filePath);
const cachedModel =
modelCache.get(filePath);
const model =
cachedModel.clone(true);
const model = cachedModel.clone(true);
if (
modelConfig &&
modelConfig.scale
) {
if (modelConfig && modelConfig.scale) {
model.scale.set(
modelConfig.scale.x,
modelConfig.scale.y,
modelConfig.scale.z,
modelConfig.scale.z
);
}
model.position.copy(position);
model.isPlacedModel = true;
model.position.copy(
position
);
model.isPlacedModel =
true;
model.modelConfig = modelConfig;
model.modelConfig =
modelConfig;
scene.add(model);
if (onPlace) {
onPlace(model);
}
} else {
// Model has not been loaded yet
gltfLoader.load(
filePath,
(gltf) => {
// Store the original model in the cache
modelCache.set(filePath, gltf.scene);
modelCache.set(
filePath,
gltf.scene
);
const model = gltf.scene.clone(true);
const model =
gltf.scene.clone(true);
if (
modelConfig &&
modelConfig.scale
) {
if (modelConfig && modelConfig.scale) {
model.scale.set(
modelConfig.scale.x,
modelConfig.scale.y,
modelConfig.scale.z,
modelConfig.scale.z
);
}
model.position.copy(position);
model.isPlacedModel = true;
model.position.copy(
position
);
model.isPlacedModel =
true;
model.modelConfig = modelConfig;
model.modelConfig =
modelConfig;
scene.add(model);
if (onPlace) {
onPlace(model);
}
......@@ -336,164 +462,313 @@ function placeModel(filePath, position, onPlace = null) {
undefined,
(error) => {
console.error("Fehler beim Laden des Modells:", error);
},
console.error(
"Fehler beim Laden des Modells:",
error
);
}
);
}
}
function selectModel(modelId) {
const model = models[modelId];
if (model && model.file) {
loadModel(model.file);
const model =
models[modelId];
showMenu("menu-bar");
if (
model &&
model.file
) {
loadModel(
model.file
);
showMenu(
'menu-bar'
);
}
}
function selectModelFromScene(event) {
const mouse = new THREE.Vector2(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1,
const mouse =
new THREE.Vector2(
(event.clientX /
window.innerWidth) *
2 - 1,
-(event.clientY /
window.innerHeight) *
2 + 1
);
const raycaster = new THREE.Raycaster();
const rayCamera =
xrRenderer && xrRenderer.xr.isPresenting
? xrRenderer.xr.getCamera(camera)
: camera;
const raycaster =
new THREE.Raycaster();
raycaster.setFromCamera(mouse, rayCamera);
const intersects = raycaster.intersectObjects(scene.children, true);
raycaster.setFromCamera(
mouse,
camera
);
if (intersects.length > 0) {
let selectedObject = intersects[0].object;
while (selectedObject.parent && selectedObject.parent !== scene) {
selectedObject = selectedObject.parent;
const intersects =
raycaster.intersectObjects(
scene.children,
true
);
if (
intersects.length > 0
) {
let selectedObject =
intersects[0].object;
while (
selectedObject.parent &&
selectedObject.parent !== scene
) {
selectedObject =
selectedObject.parent;
}
// Reticle cannot be selected
if (selectedObject === reticle) {
console.log("Reticle kann nicht ausgewählt werden.");
if (
selectedObject === reticle
) {
console.log(
"Reticle kann nicht ausgewählt werden."
);
return;
}
selectedPlacedModel = selectedObject;
selectedPlacedModel =
selectedObject;
highlightSelectedModel();
showMenu("edit-menu");
showMenu(
"edit-menu"
);
}
}
function clearSelectedModel() {
if (selectedPlacedModel) {
selectedPlacedModel.traverse((child) => {
selectedPlacedModel.traverse(
(child) => {
if (child.isMesh) {
child.material.emissive.setHex(0x000000);
child.material.emissive
.setHex(
0x000000
);
}
});
}
);
selectedPlacedModel = null;
selectedPlacedModel =
null;
}
}
function highlightSelectedModel() {
if (selectedPlacedModel) {
removeHighlightFromAllModels();
selectedPlacedModel.traverse((child) => {
selectedPlacedModel.traverse(
(child) => {
if (child.isMesh) {
child.material.emissive.setHex(0xff0000);
child.material.emissive
.setHex(
0xff0000
);
}
});
}
);
}
}
function removeHighlightFromSelectedModel() {
if (selectedPlacedModel) {
selectedPlacedModel.traverse((child) => {
selectedPlacedModel.traverse(
(child) => {
if (child.isMesh) {
child.material.emissive.setHex(0x000000);
child.material.emissive
.setHex(
0x000000
);
}
});
}
);
}
}
function removeHighlightFromAllModels() {
scene.traverse((child) => {
if (child.isMesh && child.material && child.material.emissive) {
child.material.emissive.setHex(0x000000);
scene.traverse(
(child) => {
if (
child.isMesh &&
child.material &&
child.material.emissive
) {
child.material.emissive
.setHex(
0x000000
);
}
});
}
);
}
function deleteModel() {
if (!selectedPlacedModel) {
console.log(
"Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es löschen.",
"Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es löschen."
);
return;
}
const deleteDialog = document.getElementById("delete-confirmation-dialog");
deleteDialog.style.display = "flex";
const deleteDialog =
document.getElementById(
'delete-confirmation-dialog'
);
deleteDialog.style.display =
'flex';
}
function confirmDelete(shouldDelete) {
const deleteDialog = document.getElementById("delete-confirmation-dialog");
deleteDialog.style.display = "none";
function confirmDelete(
shouldDelete
) {
const deleteDialog =
document.getElementById(
'delete-confirmation-dialog'
);
deleteDialog.style.display =
'none';
if (shouldDelete && selectedPlacedModel) {
scene.remove(selectedPlacedModel);
if (
shouldDelete &&
selectedPlacedModel
) {
scene.remove(
selectedPlacedModel
);
selectedPlacedModel = null;
selectedPlacedModel =
null;
showMenu("menu-bar");
showMenu(
'menu-bar'
);
}
}
function completeEditing() {
removeHighlightFromSelectedModel();
closeDynamicMenu();
selectedPlacedModel = null;
selectedPlacedModel =
null;
document.getElementById("edit-menu").style.display = "none";
document.getElementById(
'edit-menu'
).style.display =
'none';
document.getElementById("menu-bar").style.display = "flex";
document.getElementById(
'menu-bar'
).style.display =
'flex';
}
/* ========================= */
/* BEARBEITUNGS-MENÜS */
/* ========================= */
function openRotationMenu() {
if (!selectedPlacedModel) {
console.log(
"Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.",
"Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten."
);
return;
}
const currentRotation = Math.round(
THREE.MathUtils.radToDeg(selectedPlacedModel.rotation.y),
const currentRotation =
Math.round(
THREE.MathUtils.radToDeg(
selectedPlacedModel.rotation.y
)
);
const dynamicMenu =
document.getElementById(
"dynamic-menu"
);
const dynamicMenu = document.getElementById("dynamic-menu");
dynamicMenu.style.display = "flex";
dynamicMenu.style.display =
"flex";
dynamicMenu.innerHTML = `
......@@ -520,43 +795,83 @@ function openRotationMenu() {
`;
}
function updateRotation(axis, value) {
function updateRotation(
axis,
value
) {
if (selectedPlacedModel) {
const radians = (value / 180) * Math.PI;
selectedPlacedModel.rotation[axis] = radians;
const radians =
(value / 180) *
Math.PI;
selectedPlacedModel.rotation[axis] =
radians;
const currentRotationDisplay =
document.getElementById(
"current-rotation"
);
const currentRotationDisplay = document.getElementById("current-rotation");
if (currentRotationDisplay) {
currentRotationDisplay.textContent = value;
currentRotationDisplay.textContent =
value;
}
}
}
function openScaleMenu() {
if (!selectedPlacedModel) {
console.log(
"Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.",
"Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten."
);
return;
}
const currentScale = selectedPlacedModel.scale.x;
const minScale = selectedPlacedModel.modelConfig.minScale;
const currentScale =
selectedPlacedModel.scale.x;
const minScale =
selectedPlacedModel.modelConfig.minScale;
const maxScale =
selectedPlacedModel.modelConfig.maxScale;
const maxScale = selectedPlacedModel.modelConfig.maxScale;
const step =
(maxScale - minScale) /
100;
const step = (maxScale - minScale) / 100;
const currentScalePercent =
((currentScale - minScale) / (maxScale - minScale)) * 100;
(
(currentScale - minScale) /
(maxScale - minScale)
) * 100;
const dynamicMenu = document.getElementById("dynamic-menu");
dynamicMenu.style.display = "flex";
const dynamicMenu =
document.getElementById(
"dynamic-menu"
);
dynamicMenu.style.display =
"flex";
dynamicMenu.innerHTML = `
......@@ -588,34 +903,67 @@ function openScaleMenu() {
`;
}
function updateScale(percentValue, minScale, maxScale) {
function updateScale(
percentValue,
minScale,
maxScale
) {
if (selectedPlacedModel) {
const scale = minScale + (percentValue / 100) * (maxScale - minScale);
selectedPlacedModel.scale.set(scale, scale, scale);
const scale =
minScale +
(percentValue / 100) *
(maxScale - minScale);
selectedPlacedModel.scale.set(
scale,
scale,
scale
);
const scaleValueDisplay =
document.getElementById(
"scale-value"
);
const scaleValueDisplay = document.getElementById("scale-value");
if (scaleValueDisplay) {
scaleValueDisplay.textContent = `${parseInt(percentValue, 10)}%`;
scaleValueDisplay.textContent =
`${parseInt(percentValue, 10)}%`;
}
}
}
let moveDelta = 0.1;
function openMoveMenu() {
if (!selectedPlacedModel) {
console.log(
"Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.",
"Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten."
);
return;
}
const dynamicMenu = document.getElementById("dynamic-menu");
dynamicMenu.style.display = "flex";
const dynamicMenu =
document.getElementById(
"dynamic-menu"
);
dynamicMenu.style.display =
"flex";
dynamicMenu.innerHTML = `
......@@ -696,38 +1044,70 @@ function openMoveMenu() {
`;
}
function updateMoveDelta(value) {
moveDelta = parseFloat(value);
const moveDeltaDisplay = document.getElementById("move-delta-display");
function updateMoveDelta(
value
) {
moveDelta =
parseFloat(value);
const moveDeltaDisplay =
document.getElementById(
"move-delta-display"
);
if (moveDeltaDisplay) {
moveDeltaDisplay.textContent = moveDelta.toFixed(2);
moveDeltaDisplay.textContent =
moveDelta.toFixed(2);
}
}
function moveModelDynamic(axis, value) {
function moveModelDynamic(
axis,
value
) {
if (selectedPlacedModel) {
selectedPlacedModel.position[axis] += value;
selectedPlacedModel.position[axis] +=
value;
}
}
/* ========================= */
/* KARTENSTEUERUNG */
/* ========================= */
function refreshMapDialog() {
const mapDialog = document.getElementById("map-dialog");
mapDialog.style.display = "flex";
const mapDialog =
document.getElementById(
'map-dialog'
);
mapDialog.style.display =
'flex';
}
function closeMapDialog() {
const mapDialog = document.getElementById("map-dialog");
mapDialog.style.display = "none";
const mapDialog =
document.getElementById(
'map-dialog'
);
mapDialog.style.display =
'none';
}
/* ========================= */
/* AR-HANDLING */
/* ========================= */
......@@ -737,62 +1117,60 @@ async function activateXR(sceneData = null) {
document.body.appendChild(canvas);
// ========================================================
// Three.js WebXR renderer
// ========================================================
const gl = canvas.getContext("webgl", {
xrCompatible: true,
});
const renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true,
canvas,
context: gl,
});
renderer.autoClear = false;
renderer.xr.enabled = true;
xrRenderer = renderer;
// Keep the same reference-space type used by the previous
// manual WebXR implementation.
renderer.xr.setReferenceSpaceType("local");
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera();
camera.matrixAutoUpdate = false;
// ========================================================
// AR lighting
// ========================================================
// Soft overall illumination
const ambientLight = new THREE.AmbientLight(0xffffff, 2.5);
scene.add(ambientLight);
// Sky/ground illumination
const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x888888, 1.5);
scene.add(hemisphereLight);
// Directional light for shadows and form
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5);
directionalLight.position.set(10, 20, 10);
scene.add(directionalLight);
// ========================================================
// Reticle
// ========================================================
const loader = new GLTFLoader();
const loader = new THREE.GLTFLoader();
loader.load(
"https://immersive-web.github.io/webxr-samples/media/gltf/reticle/reticle.gltf",
(gltf) => {
reticle = gltf.scene;
reticle.visible = false;
scene.add(reticle);
},
undefined,
reticle.visible = false;
(error) => {
console.error("Fehler beim Laden des Reticles:", error);
scene.add(reticle);
},
);
......@@ -810,14 +1188,11 @@ async function activateXR(sceneData = null) {
requiredFeatures: ["hit-test"],
});
// Let Three.js manage the XR render state / framebuffer.
await renderer.xr.setSession(currentSession);
currentSession.updateRenderState({
baseLayer: new XRWebGLLayer(currentSession, gl),
});
const referenceSpace = renderer.xr.getReferenceSpace();
if (!referenceSpace) {
throw new Error("XR reference space konnte nicht erstellt werden.");
}
const referenceSpace = await currentSession.requestReferenceSpace("local");
const viewerSpace = await currentSession.requestReferenceSpace("viewer");
......@@ -859,8 +1234,9 @@ async function activateXR(sceneData = null) {
navigator.geolocation.getCurrentPosition(
(position) => {
geoLocation = {
latitude: window.roundTo(position.coords.latitude, 5),
longitude: window.roundTo(position.coords.longitude, 5),
latitude: roundTo(position.coords.latitude, 5),
longitude: roundTo(position.coords.longitude, 5),
};
console.log("GeoLocation: " + JSON.stringify(geoLocation));
......@@ -869,7 +1245,7 @@ async function activateXR(sceneData = null) {
// Load saved scene models
// =================================================
if (sceneData && Array.isArray(sceneData.models)) {
if (sceneData) {
sceneData.models.forEach((model) => {
if (model.name) {
const modelDefinition = Object.values(models).find(
......@@ -884,27 +1260,25 @@ async function activateXR(sceneData = null) {
const filePath = modelDefinition.file;
// ar_overviewmap.js is loaded before user interaction,
// and exposes this conversion function on window.
const coordinates = window.leafletToThree(model.lat, model.lng);
const { x, z } = leafletToThree(model.lat, model.lng);
const positionVector = new THREE.Vector3(
coordinates.x,
model.position.y,
coordinates.z,
);
const positionVector = new THREE.Vector3(x, model.position.y, z);
placeModel(filePath, positionVector, (placed) => {
if (model.rotation) {
placed.rotation.set(
model.rotation._x ?? model.rotation.x ?? 0,
model.rotation._y ?? model.rotation.y ?? 0,
model.rotation._z ?? model.rotation.z ?? 0,
);
placed.rotation._x = model.rotation._x;
placed.rotation._y = model.rotation._y;
placed.rotation._z = model.rotation._z;
}
if (model.scale) {
placed.scale.set(model.scale.x, model.scale.y, model.scale.z);
placed.scale.x = model.scale.x;
placed.scale.y = model.scale.y;
placed.scale.z = model.scale.z;
}
});
}
......@@ -926,45 +1300,61 @@ async function activateXR(sceneData = null) {
},
);
}
// ========================================================
// Modern Three.js XR render loop
// XR render loop
// ========================================================
renderer.setAnimationLoop((time, frame) => {
if (!frame) {
return;
}
currentSession.requestAnimationFrame(function onXRFrame(time, frame) {
currentSession.requestAnimationFrame(onXRFrame);
gl.bindFramebuffer(
gl.FRAMEBUFFER,
currentSession.renderState.baseLayer.framebuffer,
);
const pose = frame.getViewerPose(referenceSpace);
// ======================================================
if (pose) {
const view = pose.views[0];
const viewport = currentSession.renderState.baseLayer.getViewport(view);
renderer.setSize(viewport.width, viewport.height);
camera.matrix.fromArray(view.transform.matrix);
camera.projectionMatrix.fromArray(view.projectionMatrix);
camera.updateMatrixWorld(true);
// =================================================
// Hit testing
// ======================================================
// =================================================
const hitTestResults = frame.getHitTestResults(hitTestSource);
if (hitTestResults.length > 0) {
const hitPose = hitTestResults[0].getPose(referenceSpace);
if (hitPose && reticle) {
reticle.visible = true;
reticle.position.set(
hitPose.transform.position.x,
hitPose.transform.position.y,
hitPose.transform.position.z,
);
reticle.updateMatrixWorld(true);
}
} else if (reticle) {
reticle.visible = false;
}
// ======================================================
// =================================================
// Render AR scene
// ======================================================
// =================================================
renderer.render(scene, camera);
}
});
}
......@@ -1005,36 +1395,3 @@ function playButtonSound() {
}, 200);
}
}
\ No newline at end of file
window.selectModel = selectModel;
window.updateRotation = updateRotation;
window.updateScale = updateScale;
window.updateMoveDelta = updateMoveDelta;
window.moveModelDynamic = moveModelDynamic;
window.closeDynamicMenu = closeDynamicMenu;
window.roundTo = window.roundTo;
/* ============================================================
GLOBAL API BRIDGE
------------------------------------------------------------
ES modules have their own scope. The existing HTML UI uses
inline onclick handlers, so expose the existing application
functions explicitly on window.
============================================================ */
window.showMenu = showMenu;
window.openRotationMenu = openRotationMenu;
window.openScaleMenu = openScaleMenu;
window.openMoveMenu = openMoveMenu;
window.deleteModel = deleteModel;
window.completeEditing = completeEditing;
window.confirmDelete = confirmDelete;
window.exitAR = exitAR;
window.confirmExit = confirmExit;
window.closeInfoDialog = closeInfoDialog;
window.getAllPlacedModels = getAllPlacedModels;
window.activateXR = activateXR;
window.getXRRenderCamera = () =>
xrRenderer && xrRenderer.xr.isPresenting
? xrRenderer.xr.getCamera(camera)
: camera;
import * as THREE from "three";
let init = false;
let mymap;
let userIcon;
......@@ -100,7 +98,7 @@ function init_map() {
// Add currently placed models to map
// ========================================================
const models = window.getAllPlacedModels();
const models = getAllPlacedModels();
models.forEach((model) => {
const { lat, lng } = threeToLeaflet(model.position.x, model.position.z);
......@@ -212,12 +210,7 @@ function updateMap() {
}
// PERFORMANCE: Reuse the same Vector3 object.
const activeCamera =
typeof window.getXRRenderCamera === "function"
? window.getXRRenderCamera()
: camera;
activeCamera.getWorldPosition(cameraPosition);
camera.getWorldPosition(cameraPosition);
// PERFORMANCE: Do not update Leaflet if the user
// has not moved.
......@@ -330,10 +323,3 @@ function roundTo(n, digits) {
return parseFloat(n);
}
/* Expose map functions needed by the other application modules. */
window.init_map = init_map;
window.centerOnPlan = centerOnPlan;
window.threeToLeaflet = threeToLeaflet;
window.leafletToThree = leafletToThree;
window.roundTo = roundTo;
......@@ -79,6 +79,7 @@ function showStartScreen() {
startScreen.appendChild(hftLogo);
/* ==========================================================
TITLE
========================================================== */
......@@ -189,7 +190,7 @@ function showStartScreen() {
predefinedButton2 = null;
// Start AR
window.window.activateXR(sceneData);
activateXR(sceneData);
};
/* ==========================================================
......@@ -197,7 +198,7 @@ function showStartScreen() {
========================================================== */
loadButton.onclick = () => {
window.window.loadSceneFromFile((data, fileName) => {
loadSceneFromFile((data, fileName) => {
if (data && fileName) {
sceneData = data;
sceneFileName = fileName;
......
......@@ -9,15 +9,8 @@
<link rel="stylesheet" href="style.css">
<!-- Three.js (modern ES module setup) -->
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.186.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.186.0/examples/jsm/"
}
}
</script>
<script src="https://unpkg.com/three@0.126.0/build/three.js"></script>
<script src="https://unpkg.com/three@0.126.0/examples/js/loaders/GLTFLoader.js"></script>
</head>
<body>
......@@ -306,7 +299,7 @@
============================================================ -->
<div id="app-version">
v0.1.4
v0.1.3
</div>
......@@ -314,14 +307,11 @@
JAVASCRIPT
============================================================ -->
<!-- Debug console loads first so it can capture module errors/logs -->
<script src="ar_debug_console.js"></script>
<script src="ar_start.js"></script>
<script src="ar_main.js"></script>
<!-- Application modules -->
<script type="module" src="ar_main.js"></script>
<script type="module" src="ar_overviewmap.js"></script>
<script type="module" src="ar_download.js"></script>
<script type="module" src="ar_start.js"></script>
<script src="ar_debug_console.js"></script>
<link
rel="stylesheet"
......@@ -329,7 +319,9 @@
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<script src="ar_overviewmap.js"></script>
<script src="ar_download.js"></script>
<!-- ============================================================
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment