Commit faaad5f0 authored by Tomi6545's avatar Tomi6545
Browse files

update map + save

parent bbb51846
No related merge requests found
Showing with 953 additions and 754 deletions
+953 -754
function downloadScene() {
const placedModels = getAllPlacedModels().map(model => {
const { lat, lng } = threeToLeaflet(model.position.x, model.position.z);
return {
name: model.modelConfig.name,
position: model.position,
rotation: model.rotation,
scale: model.scale,
lat: lat,
lng: lng,
}
});
console.log("Modelle: " + placedModels.length)
if (placedModels.length === 0) {
showInfoDialog("Szene ist leer");
return;
}
if (!geoLocation) {
console.log("Standort nicht geladen");
showInfoDialog("Ihr Standort wurde noch nicht geladen");
return;
}
const data = {
lat: geoLocation.latitude,
lng: geoLocation.longitude,
models: placedModels,
}
const sceneData = JSON.stringify(data, null, 1);
const blob = new Blob([sceneData], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "scene.json";
a.style.display = "none";
document.body.appendChild(a);
a.click();
URL.revokeObjectURL(url);
document.body.removeChild(a);
}
function loadSceneFromFile(loadedData) {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'application/json';
fileInput.addEventListener('change', function(event) {
const file = event.target.files[0];
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) {
try {
const sceneData = JSON.parse(e.target.result);
loadedData(sceneData, fileName);
} catch (error) {
console.error("Fehler beim Verarbeiten der Datei:", error);
}
};
reader.readAsText(file);
});
fileInput.click();
}
\ No newline at end of file
// Variablen
let selectedModel = 'robot';
let selectedPlacedModel = null;
let currentSession = null;
let reticle = null;
let scene, camera;
let geoLocation;
let models = {
bench: {
name: "Bench",
image: "previewImages/bench.PNG",
file: "/assets/models/bench_model/scene.gltf",
scale: { x: 0.1, y: 0.1, z: 0.1 },
minScale: 0.05, // 50% der aktuellen Größe
maxScale: 0.5 // 500% der aktuellen Größe
},
trashbin: {
name: "Trash bin",
image: "previewImages/trash_can.PNG",
file: "/assets/models/trash_model/scene.gltf",
scale: { x: 0.03, y: 0.03, z: 0.03 },
minScale: 0.01, // 50% der aktuellen Größe
maxScale: 0.1 // 500% der aktuellen Größe
},
telephone_box: {
name: "Telephone Box",
image: "previewImages/telephone_box.PNG",
file: "/assets/models/telephone_box_model/scene.gltf",
scale: { x: 0.5, y: 0.5, z: 0.5 },
minScale: 0.05,
maxScale: 1
},
fire_hydrant_model: {
name: "Fire Hydrant",
image: "previewImages/hydrant.PNG",
file: "/assets/models/fire_hydrant_model/scene.gltf",
scale: { x: 0.3, y: 0.3, z: 0.3 },
minScale: 0.1,
maxScale: 1
},
statue: {
name: "Statue",
image: "previewImages/statue.PNG",
file: "/assets/models/statue_model/scene.gltf",
scale: { x: 0.5, y: 0.5, z: 0.5 },
minScale: 0.05,
maxScale: 2
},
fountain: {
name: "Fountain",
image: "previewImages/fountain.PNG",
file: "/assets/models/fountain_model/scene.gltf",
scale: { x: 0.001, y: 0.001, z: 0.001 },
minScale: 0.0005,
maxScale: 0.005
},
lantern: {
name: "Lantern",
image: "previewImages/lantern.jpg",
file: "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/refs/heads/main/Models/Lantern/glTF/Lantern.gltf",
scale: { x: 0.15, y: 0.15, z: 0.15 },
minScale: 0.05,
maxScale: 0.3
}
};
const menus = ['menu-bar', 'add-menu', 'edit-menu', 'options-menu', 'map-window'];
window.onload = () => {
initializeAddMenu();
// Fügt Sound zu allen Buttons hinzu
const buttons = document.querySelectorAll("button, .menu-item");
buttons.forEach(button => {
button.addEventListener("click", playButtonSound);
});
};
function initializeAddMenu() {
const addMenu = document.getElementById('add-menu');
addMenu.innerHTML = Object.entries(models)
.map(
([key, model]) => `
<div class="menu-item" id="${key}-item" onclick="selectModel('${key}')">
<img src="${model.image}" alt="${model.name}" />
</div>
`
)
.join('') +
`
<div class="menu-item" onclick="showMenu('menu-bar')">
<img src="previewImages/back-icon.png" alt="Zurück" />
</div>
`;
}
function showMenu(menuId) {
const isMapWindow = menuId === 'map-window';
if (isMapWindow && !geoLocation) {
console.log("Standort nicht geladen");
showInfoDialog("Ihr Standort wurde noch nicht geladen");
return;
}
menus.forEach(id => {
document.getElementById(id).style.display = id === menuId ? 'flex' : 'none';
});
closeDynamicMenu();
if (menuId === 'menu-bar') clearSelectedModel();
else if (isMapWindow) init_map();
}
function clearSelectedModel() {
if (selectedPlacedModel) {
selectedPlacedModel.traverse((child) => {
if (child.isMesh) {
child.material.emissive.setHex(0x000000); // Markierung entfernen
}
});
selectedPlacedModel = null; // Kein Modell mehr ausgewählt
}
}
function selectModel(modelId) {
const model = models[modelId];
if (model && model.file) {
loadModel(model.file);
showMenu('menu-bar');
}
}
function loadModel(filePath) {
placeModel(filePath, reticle.position, (model) => {
selectedPlacedModel = model;
highlightSelectedModel();
showMenu('edit-menu');
});
}
function placeModel(filePath, position, onPlace = null) {
const modelConfig = Object.values(models).find(model => model.file === filePath);
const loader = new THREE.GLTFLoader();
loader.load(
filePath,
(gltf) => {
const model = gltf.scene;
if (modelConfig && modelConfig.scale) {
model.scale.set(modelConfig.scale.x, modelConfig.scale.y, modelConfig.scale.z);
}
model.position.copy(position);
model.isPlacedModel = true;
model.modelConfig = modelConfig;
scene.add(model);
if (onPlace) {
onPlace(model);
}
},
undefined,
(error) => {
console.error("Fehler beim Laden des Modells:", error);
}
);
}
function highlightSelectedModel() {
if (selectedPlacedModel) {
removeHighlightFromAllModels();
selectedPlacedModel.traverse((child) => {
if (child.isMesh) {
child.material.emissive.setHex(0xff0000); // Rote Hervorhebung
}
});
}
}
function removeHighlightFromAllModels() {
scene.traverse((child) => {
if (child.isMesh && child.material && child.material.emissive) {
child.material.emissive.setHex(0x000000); // Markierung entfernen
}
});
}
function removeHighlightFromSelectedModel() {
if (selectedPlacedModel) {
selectedPlacedModel.traverse((child) => {
if (child.isMesh) child.material.emissive.setHex(0x000000); // Markierung entfernen
});
}
}
function selectModelFromScene(event) {
const mouse = new THREE.Vector2(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1
);
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
// Prüfe Kollisionen mit Objekten in der Szene
const intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
// Finde das Hauptobjekt (Root-Parent), falls Mesh ausgewählt wurde
let selectedObject = intersects[0].object;
while (selectedObject.parent && selectedObject.parent !== scene) {
selectedObject = selectedObject.parent;
}
// Markiere das gesamte Modell als ausgewählt
selectedPlacedModel = selectedObject;
highlightSelectedModel();
showMenu("edit-menu");
}
}
function completeEditing() {
removeHighlightFromSelectedModel();
closeDynamicMenu();
selectedPlacedModel = null;
document.getElementById('edit-menu').style.display = 'none';
document.getElementById('menu-bar').style.display = 'flex';
}
function openRotationMenu() {
if (!selectedPlacedModel) {
showInfoDialog("Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.");
return;
}
// Holen Sie die aktuelle Y-Rotation des Modells (in Grad)
const currentRotation = Math.round(THREE.MathUtils.radToDeg(selectedPlacedModel.rotation.y));
const dynamicMenu = document.getElementById("dynamic-menu");
dynamicMenu.style.display = "flex";
dynamicMenu.innerHTML = `
<h3>Rotation anpassen</h3>
<label>Y-Achse: <span id="current-rotation">${currentRotation}</span>°<input type="range" min="0" max="360" step="10" onchange="updateRotation('y', this.value)"></label>
<button onclick="closeDynamicMenu()">Zurück</button>
`;
}
function updateRotation(axis, value) {
if (selectedPlacedModel) {
const radians = (value / 180) * Math.PI;
selectedPlacedModel.rotation[axis] = radians;
// Update der aktuellen Rotation im Menü
const currentRotationDisplay = document.getElementById("current-rotation");
if (currentRotationDisplay) {
currentRotationDisplay.textContent = value; // Zeige den aktuellen Wert in Grad an
}
}
}
function calculateBoundingBox(object) {
const box = new THREE.Box3().setFromObject(object);
const size = new THREE.Vector3();
box.getSize(size);
return size;
}
function calculateMaxScale(object) {
const boundingBox = calculateBoundingBox(object);
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// Berechne die maximal mögliche Skalierung, um im Viewport zu bleiben
const scaleWidth = viewportWidth / boundingBox.x;
const scaleHeight = viewportHeight / boundingBox.y;
// Wähle den kleineren Wert und reduziere ihn leicht, um sicherzugehen, dass das Objekt nicht über den Rand hinausgeht
const safeScaleFactor = 0.95; // Puffer, um sicherzustellen, dass es nicht zu groß wird
return Math.min(scaleWidth, scaleHeight) * safeScaleFactor;
}
/**
function openScaleMenu() {
if (!selectedPlacedModel) {
showInfoDialog("Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.");
return;
}
// Berechne die maximale Skalierung für das spezifische Objekt
const maxScale = calculateMaxScale(selectedPlacedModel);
// Aktuelle Skalierung des Modells bestimmen
const currentScale = selectedPlacedModel.scale.x;
const dynamicMenu = document.getElementById("dynamic-menu");
dynamicMenu.style.display = "flex";
dynamicMenu.innerHTML = `
<h3>Skalierung anpassen</h3>
<label>Größe: <span id="scale-value">${currentScale.toFixed(2)}</span><input type="range" min="0.01" max="${maxScale.toFixed(2)}" step="0.0001" value="${currentScale}" onchange="updateScale(this.value)"></label>
<button onclick="closeDynamicMenu()">Zurück</button>
`;
} * */
function openScaleMenu() {
if (!selectedPlacedModel) {
showInfoDialog("Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es bearbeiten.");
return;
}
// Berechne die maximale Skalierung für das spezifische Objekt
// const maxScale = calculateMaxScale(selectedPlacedModel);
// Aktuelle Skalierung des Modells bestimmen
const currentScale = selectedPlacedModel.scale.x;
const minScale = selectedPlacedModel.modelConfig.minScale;
const maxScale = selectedPlacedModel.modelConfig.maxScale;
const step = (maxScale - minScale) / 100; // Dynamische Schrittgröße basierend auf Grenzen
console.log("Slider-Werte:", { minScale, maxScale, currentScale });
const dynamicMenu = document.getElementById("dynamic-menu");
dynamicMenu.style.display = "flex";
dynamicMenu.innerHTML = `
<h3>Skalierung anpassen</h3>
<label>Größe: <span id="scale-value">${currentScale.toFixed(2)}</span>
<input type="range" min="${minScale}" max="${maxScale}" step="${step}" value="${currentScale}" onchange="updateScale(this.value)"></label>
<button onclick="closeDynamicMenu()">Zurück</button>
`;
}
function updateScale(value) {
if (selectedPlacedModel) {
const scale = parseFloat(value);
selectedPlacedModel.scale.set(scale, scale, scale);
// Anzeige des aktuellen Wertes aktualisieren
const scaleValueDisplay = document.getElementById("scale-value");
if (scaleValueDisplay) {
scaleValueDisplay.textContent = `${scale.toFixed(2)}`;
}
}
}
function deleteModel() {
if (!selectedPlacedModel) {
showInfoDialog("Kein Modell ausgewählt. Bitte wählen Sie ein Modell aus, bevor Sie es löschen.");
return;
}
// Dialog anzeigen
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';
if (shouldDelete && selectedPlacedModel) {
scene.remove(selectedPlacedModel);
selectedPlacedModel = null;
showMenu('menu-bar');
}
}
function closeDynamicMenu() {
const dynamicMenu = document.getElementById("dynamic-menu");
dynamicMenu.style.display = "none";
}
function getAllPlacedModels() {
return scene.children.filter(child => child.isPlacedModel === true);
}
function refreshMapDialog() {
const mapDialog = document.getElementById('map-dialog');
mapDialog.style.display = 'flex';
}
function closeMapDialog() {
const mapDialog = document.getElementById('map-dialog');
mapDialog.style.display = 'none';
}
async function activateXR(sceneData = null) {
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
const gl = canvas.getContext('webgl', { xrCompatible: true });
const renderer = new THREE.WebGLRenderer({ alpha: true, canvas, context: gl });
renderer.autoClear = false;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera();
camera.matrixAutoUpdate = false;
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(10, 10, 10);
scene.add(light);
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);
});
currentSession = await navigator.xr.requestSession('immersive-ar', {
optionalFeatures: ["dom-overlay"],
domOverlay: { root: document.body },
requiredFeatures: ['hit-test']
});
currentSession.updateRenderState({ baseLayer: new XRWebGLLayer(currentSession, gl) });
const referenceSpace = await currentSession.requestReferenceSpace('local');
const viewerSpace = await currentSession.requestReferenceSpace('viewer');
const hitTestSource = await currentSession.requestHitTestSource({ space: viewerSpace });
document.getElementById('menu-bar').style.display = 'flex';
currentSession.addEventListener("end", () => {
currentSession = null;
menus.forEach(id => {
document.getElementById(id).style.display = 'none';
});
});
canvas.addEventListener("pointerdown", selectModelFromScene);
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(position => {
geoLocation = {
latitude: roundTo(position.coords.latitude, 5),
longitude: roundTo(position.coords.longitude, 5),
};
console.log("GeoLocation: " + JSON.stringify(geoLocation));
if (sceneData) {
sceneData.models.forEach((model) => {
const offSet = leafletToThree(sceneData.lat, sceneData.lng)
if (model.name) {
const filePath = Object.values(models).find(m => model.name === m.name).file;
const { x, z } = leafletToThree(model.lat, model.lng);
const positionVector = new THREE.Vector3(x, model.position.y, z);
placeModel(filePath, positionVector, (placed) => {
if (model.rotation) {
placed.rotation._x = model.rotation._x;
placed.rotation._y = model.rotation._y;
placed.rotation._z = model.rotation._z;
}
if (model.scale) {
placed.scale.x = model.scale.x;
placed.scale.y = model.scale.y;
placed.scale.z = model.scale.z;
}
});
}
});
}
}, function(error) {
console.error("Fehler bei der Geolokalisierung:", JSON.stringify(error));
}, {enableHighAccuracy: true, maximumAge: 2000, timeout: 5000});
}
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);
const hitTestResults = frame.getHitTestResults(hitTestSource);
if (hitTestResults.length > 0) {
const hitPose = hitTestResults[0].getPose(referenceSpace);
reticle.visible = true;
reticle.position.set(hitPose.transform.position.x, hitPose.transform.position.y, hitPose.transform.position.z);
reticle.updateMatrixWorld(true);
}
renderer.render(scene, camera);
}
});
}
function exitAR() {
document.getElementById('confirmation-dialog').style.display = 'flex';
}
function confirmExit(shouldExit) {
if (shouldExit && currentSession) {
currentSession.end();
}
document.getElementById('confirmation-dialog').style.display = 'none';
}
let soundTimeout = false;
function playButtonSound() {
if (!soundTimeout) {
const sound = document.getElementById("button-sound");
sound.currentTime = 0;
sound.play();
soundTimeout = true;
setTimeout(() => {
soundTimeout = false;
}, 200); // Verzögerung von 200ms
}
}
\ No newline at end of file
let init = false;
let mymap;
let userIcon;
let intervalId;
let modeMarkers = []
const earthRadius = 6378137; // Erdradius in Metern (WGS84)
function init_map() { function init_map() {
const mapContainer = document.getElementById('map-container'); const mapContainer = document.getElementById("map-container");
if (mapContainer) { if (mapContainer) {
var mymap = L.map(mapContainer); if (!init) {
var OpenStreetMap_DE = L.tileLayer('https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png', { mymap = L.map(mapContainer);
maxZoom: 30, var OpenStreetMap_DE = L.tileLayer('https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png', {
minZoom: 15, maxZoom: 30,
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' minZoom: 15,
}); attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
// select one basemap from https://leaflet-extras.github.io/leaflet-providers/preview/ });
var CartoDB_Positron = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', { // select one basemap from https://leaflet-extras.github.io/leaflet-providers/preview/
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>', var CartoDB_Positron = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
subdomains: 'abcd', attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>',
maxZoom: 30, subdomains: 'abcd',
minZoom: 15 maxZoom: 30,
minZoom: 15
});
CartoDB_Positron.addTo(mymap);
userIcon = L.icon({
iconUrl: 'previewImages/map-user.png',
iconSize: [32, 32],
iconAnchor: [16, 32],
popupAnchor: [0, -32]
});
mymap.setView([geoLocation.latitude, geoLocation.longitude], 20)
userIcon = L.marker([geoLocation.latitude, geoLocation.longitude], { icon: userIcon }).bindPopup('Ihre Position').addTo(mymap);
init = true;
}
modeMarkers.forEach(marker => {
marker.remove();
}); });
CartoDB_Positron.addTo(mymap); const models = getAllPlacedModels();
models.forEach(model => {
// Marker initialisieren const { lat, lng } = threeToLeaflet(model.position.x, model.position.z);
var userIcon = L.icon({ const modelMarker = L.marker([lat, lng], { icon: L.icon({
iconUrl: 'previewImages/map-user.png', // Dein Männchen-Bild iconUrl: model.modelConfig.image,
iconSize: [32, 32], // Die Größe des Icons iconSize: [16, 16],
iconAnchor: [16, 32], // Der Punkt des Markers, der auf der Position zeigt iconAnchor: [16, 32],
popupAnchor: [0, -32] // Die Position des Popups relativ zum Marker popupAnchor: [0, -32]
})}).addTo(mymap).bindPopup(model.modelConfig?.name || "Unbenannt");
modeMarkers.push(modelMarker);
}); });
intervalId = setInterval(updateMap, 500);
}
}
// Marker initialisieren function updateMap() {
var userMarker = L.marker([0, 0], { icon: userIcon }).bindPopup('Ihre Position').addTo(mymap); if (document.getElementById("map-window").style.display === 'none' && intervalId) {
let initPosition = false; clearInterval(intervalId);
let lastPosition = { lat: 0, lng: 0 }; // Letzte bekannte Position intervalId = null;
const updateThreshold = 1.5; // Schwellenwert für den Abstand (in Metern) return;
}
// Funktion, um die Position zu aktualisieren
function updatePosition(position) {
const userLat = position.coords.latitude;
const userLng = position.coords.longitude;
const currentPosition = L.latLng(userLat, userLng);
const lastKnownPosition = L.latLng(lastPosition.lat, lastPosition.lng);
const distance = currentPosition.distanceTo(lastKnownPosition);
// Nur aktualisieren, wenn der Abstand größer als der Schwellenwert ist
if (distance > updateThreshold || !initPosition) {
if (!initPosition) {
mymap.setView([userLat, userLng], 20); // Zentrische Ansicht beim ersten Mal
initPosition = true;
}
// Marker auf die neue Position setzen
userMarker.setLatLng([userLat, userLng]);
console.log(`Setze Position auf lat=${userLat} lng=${userLng}, Abstand: ${distance}m`);
lastPosition = { lat: userLat, lng: userLng };
}
}
// Die Position alle 5 Sekunden aktualisieren let vec = new THREE.Vector3();
if (navigator.geolocation) { const userPosition = camera.getWorldPosition(vec);
navigator.geolocation.getCurrentPosition(updatePosition); const { lat, lng } = threeToLeaflet(userPosition.x, userPosition.z);
navigator.geolocation.watchPosition(updatePosition, function(error) {
console.error("Fehler bei der Geolokalisierung:", error); if (userIcon) {
}, {enableHighAccuracy: true, maximumAge: 2000,timeout: 5000}); userIcon.setLatLng([lat, lng]);
} else {
console.error("Geolocation wird nicht unterstützt.");
}
} }
} }
function threeToLeaflet(threeX, threeZ) {
if (!geoLocation) {
console.error("GeoLocation ist noch nicht verfügbar");
return;
}
const originLat = geoLocation.latitude;
const originLng = geoLocation.longitude;
const deltaLat = (threeZ / earthRadius) * (180 / Math.PI);
const deltaLng = (threeX / (earthRadius * Math.cos(Math.PI * originLat / 180))) * (180 / Math.PI);
const globalLat = originLat + deltaLat;
const globalLng = originLng + deltaLng;
return { lat: globalLat, lng: globalLng };
}
function leafletToThree(lat, lng) {
if (!geoLocation) {
console.error("GeoLocation ist noch nicht verfügbar");
return;
}
const originLat = geoLocation.latitude;
const originLng = geoLocation.longitude;
const deltaLat = lat - originLat;
const deltaLng = lng - originLng;
const threeZ = deltaLat * (Math.PI / 180) * earthRadius;
const threeX = deltaLng * (Math.PI / 180) * earthRadius * Math.cos(Math.PI * originLat / 180);
return { x: threeX, z: threeZ };
}
function roundTo(n, digits) {
var negative = false;
if (digits === undefined) {
digits = 0;
}
if (n < 0) {
negative = true;
n = n * -1;
}
var multiplicator = Math.pow(10, digits);
n = parseFloat((n * multiplicator).toFixed(11));
n = (Math.round(n) / multiplicator).toFixed(digits);
if (negative) {
n = (n * -1).toFixed(digits);
}
return parseFloat(n);
}
\ No newline at end of file
if (navigator.xr) {
let sceneData;
const startButton = document.createElement('button');
startButton.textContent = 'Start AR';
startButton.style.cssText = "position: fixed; top: 45%; left: 50%; transform: translate(-50%, -50%); padding: 15px; font-size: 18px;";
document.body.appendChild(startButton);
startButton.onclick = () => {
startButton.remove();
loadButton.remove();
activateXR(sceneData);
};
const loadButton = document.createElement('button');
loadButton.textContent = 'Umgebung Laden';
loadButton.style.cssText = "position: fixed; top: 55%; left: 50%; transform: translate(-50%, -50%); padding: 15px; font-size: 18px;";
document.body.appendChild(loadButton);
loadButton.onclick = () => {
loadSceneFromFile((data, fileName) => {
if (data && fileName) {
loadButton.textContent = fileName;
sceneData = data
} else {
loadButton.textContent = 'Umgebung Laden';
}
});
};
} else {
alert('WebXR wird nicht unterstützt.');
}
\ No newline at end of file
This diff is collapsed.
public/previewImages/download-icon.png

6.84 KB

body {
margin: 0;
font-family: Arial, sans-serif;
background-color: #f0f0f0;
color: #333;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-image: url('https://www.hft-stuttgart.de/fileadmin/Dateien/Hochschule/-_R_Juergen_Pollak_HFT_18.04.18-0091.jpg');
background-size: cover;
background-position: center;
}
.container {
text-align: center;
background-color: rgba(255, 255, 255, 0.9);
padding: 50px;
border-radius: 10px;
color: #333;
max-width: 80%;
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.2);
}
h1 {
font-size: 3em;
margin-bottom: 20px;
color: #444;
}
p {
font-size: 1.2em;
margin-bottom: 30px;
}
#add-menu {
overflow-y: auto;
}
#menu-bar,
.menu-placeholder {
position: absolute;
bottom: 0;
width: 100%;
height: 80px;
display: flex;
justify-content: space-around;
align-items: center;
background: #121212;
padding: 10px;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.8);
color: white;
z-index: 10;
border-top: 1px solid #2a2a2a;
}
.menu-item {
background: #2c2c2c;
border-radius: 20px;
padding: 8px;
transition: background-color 0.3s, transform 0.2s;
}
.menu-item:hover {
background-color: #3a3a3a;
transform: scale(1.1);
}
.menu-item img {
width: 50px;
height: 50px;
}
.menu-placeholder .menu-item img {
width: 50px;
height: 50px;
}
button {
background-color: #4CAF50;
color: white;
font-size: 1em;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease-in-out, transform 0.2s;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin: 10px;
}
button:hover {
background-color: #45a049;
}
button:active {
background-color: #387a39;
}
/* Confirmation Dialog */
#confirmation-dialog,
#delete-confirmation-dialog,
#info-dialog {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 20;
}
.dialog-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
}
.dialog-box {
position: relative;
background: #2c2c2c;
padding: 20px;
border-radius: 10px;
text-align: center;
color: white;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.8);
}
.dialog-box p {
margin-bottom: 20px;
font-size: 1.2em;
color: #f0f0f0;
}
.dialog-box button {
margin: 5px;
padding: 10px 20px;
font-size: 16px;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.2s;
}
.dialog-box button:first-child {
background-color: #e74c3c;
color: white;
}
.dialog-box button:first-child:hover {
background-color: #c0392b;
transform: scale(1.05);
}
.dialog-box button:last-child {
background-color: #3498db;
color: white;
}
.dialog-box button:last-child:hover {
background-color: #2980b9;
transform: scale(1.05);
}
#dynamic-menu {
position: absolute;
bottom: 80px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
background: #1e1e1e;
box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.8);
color: white;
width: 100vw;
height: 200px;
z-index: 20;
overflow-y: auto;
border-radius: 8px;
}
#dynamic-menu input[type="range"] {
width: 100%;
margin: 10px auto;
background: #3a3a3a;
border-radius: 5px;
}
#dynamic-menu input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
background: #fff;
border: 2px solid #555;
border-radius: 50%;
cursor: pointer;
}
\ No newline at end of file
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