Commit 78b89f59 authored by Alfakhori's avatar Alfakhori
Browse files

v0.3.0

parent 18535aa9
Pipeline #12492 passed with stage
in 28 seconds
# Gengenbach AR migration plan
## Audit baseline (2026-10-06)
Audited `D:\OneDrive\0_Projects\Gengenbach\Gengenbach\public` before application edits.
- The active app consists of `index.html`, `style.css`, and five classic scripts: `ar_start.js`, `ar_main.js`, `ar_overviewmap.js`, `ar_download.js`, and `ar_debug_console.js`.
- The tracked `public/new/` directory is deleted in the current working tree. `public/BK-Working/` is untracked and its seven files are byte-identical to the current active HTML/CSS/scripts. Both states are pre-existing user work and must remain untouched.
- There is no package manifest or bundler in the repository. The GitLab Pages config publishes `public/`; keep browser-native modules and no build system.
- Current runtime: Three.js r126 global scripts plus global `THREE.GLTFLoader`; Leaflet CSS/JS from unpinned unpkg URLs; OSM DE tiles; model URLs point to the existing HFT GitLab raw URLs; the reticle is loaded from the WebXR samples URL.
- `assets/models/` has seven GLTF model directories plus a separate street-lamp GLB, textures, plans, UI images/icons, and button audio. Each of the seven catalog GLTFs contains one skin and one animation; use `SkeletonUtils.clone()` for independent model copies. The catalog exposes seven named models (bench, trash bin, lantern, telephone box, hydrant, statue, fountain). Preserve the unlisted asset too.
- There are two predefined scene JSON files. Both use top-level `lat`, `lng`, and `models`; each model stores `name`, `position`, `rotation`, `scale`, `lat`, and `lng`. The importer identifies models by display name and rebuilds X/Z from geographic coordinates relative to the current GPS position.
- Existing behavior includes the start screen, upload and predefined-scene selection, AR start/exit/reload, hit testing/reticle, model placement/cache/selection/highlight/delete, rotation/scale/move controls, map with OSM tiles, model markers and georeferenced plan overlay, 500 ms map updates, scene download/upload, geolocation, button sounds, and the console/error/rejection debug panel.
- Coordinate conversion uses Earth radius 6,378,137 m and the current local tangent-plane equations. Preserve the equations, origin behavior, names, URLs, model defaults, HTML controls, styling, and scene schema.
- Current public inline/dynamic-handler API: `showMenu`, `selectModel`, `openRotationMenu`, `openScaleMenu`, `openMoveMenu`, `deleteModel`, `completeEditing`, `exitAR`, `downloadScene`, `confirmDelete`, `confirmExit`, `closeInfoDialog`, `closeDynamicMenu`, `updateRotation`, `updateScale`, `updateMoveDelta`, and `moveModelDynamic`. Export and attach only these required functions to `window`.
- Existing XR is a manual WebGL path: an XR-compatible canvas/context, explicit `XRWebGLLayer`, manual single-view camera matrices, and a session-owned `requestAnimationFrame` loop. The UI requests `immersive-ar`, requires `hit-test`, and requests `dom-overlay`. A modern renderer migration must replace these pieces together; retaining the old layer/matrix loop alongside `renderer.xr` would create two incompatible owners. Keep the features and hit-test source, while assigning rendering/camera/frame-loop ownership to Three.js WebXRManager.
- The current miss path leaves the reticle at its previous visible pose. Hide it when a frame has no hit, as required by the supplied migration acceptance criteria.
## Dependency outline
`index.html` loads Leaflet and the app entry point. The app entry imports the debug console, AR controller, start screen, map, and scene I/O. `ar_start.js` calls AR start and scene-file loading. `ar_main.js` owns AR/model/UI state and calls the map. The map reads the AR scene/camera/geolocation and serves coordinate conversion to the AR and scene-I/O code. Scene download reads placed models and map conversions. Keep Leaflet as its existing global dependency, with one Three.js import map shared by every module.
## Migration steps and checkpoints
1. Save this audit/plan before code edits; keep the existing user worktree changes and backup intact.
2. Establish a browser-native ES module entry and one import map pinned to Three.js r186 (`0.186.0`, the latest published stable release found during this audit); import addons with `three/addons/`. Keep Leaflet and its UI unchanged.
3. Convert the active app scripts to modules with explicit imports/exports. Preserve current function behavior and HTML; expose only the listed inline/dynamic handler API on `window`. Load the debug console before app initialization.
4. Replace global GLTFLoader usage with one imported shared loader and preserve model cache and external model/reticle URLs. All seven current catalog assets are skinned and animated, so clone each instance with `SkeletonUtils.clone()`.
5. Migrate XR as one coherent change: `renderer.xr.enabled`, `renderer.xr.setSession(session)`, one `renderer.setAnimationLoop`, frame-scoped hit-test processing from the supplied XRFrame, and Three.js-managed XR camera/rendering. Keep immersive AR, required hit-test, optional DOM overlay, viewer/local reference spaces, reticle placement, pointer selection, session end behavior, and DOM overlay.
6. Wire module dependencies for the start screen, map, geolocation, dialogs, debug console, and scene load/download; keep existing coordinate calculations and serialized JSON fields/values.
7. After each major phase, run targeted static checks for syntax/import resolution, old Three.js references, unresolved HTML/dynamic handler names, duplicate script/loop registration, asset/URL/schema preservation, and coordinate formula changes. Compare the feature/API inventory from this audit against the result.
8. Optimize only after functional parity: reuse frame objects, retain the 500 ms map cadence, and avoid unvalidated rendering changes.
9. Report checks separately from device-only behavior. A real mobile WebXR browser/device is required to claim camera passthrough, hit testing, actual placement/manipulation, Leaflet runtime, download/upload, and session exit verified.
## Guardrails
- Do not edit `public/new/` or `public/BK-Working/`, stage/commit them, or remove either working-tree state.
- Keep asset paths, model URLs, UI, map, equations, and JSON scene structure.
- If implementation requires changing a discovered behavior or schema, stop and explain it before doing so.
- Do not claim mobile WebXR acceptance criteria verified without an available device/browser run.
## Static checkpoint results
- The six active JS files pass Node syntax checks and local import/export resolution checks.
- The local module graph is acyclic. The app has one module entry and one Three.js import map; the active app path contains one `renderer.setAnimationLoop` and no explicit `XRWebGLLayer`, session-owned animation loop, or manual camera matrix overwrite.
- All 17 HTML/dynamically generated function-handler names resolve through the compatibility bridge. `moveDelta` is exposed through a read-only `window` accessor only because generated movement-button handlers refer to it.
- Both predefined JSON scenes parse and retain the expected fields. Original named functions, HTML IDs/inline handlers, model URLs/catalog, and coordinate equations remain present.
- The existing `BK-Working/` snapshot still matches the committed classic-script baseline. Existing `public/new/` deletions and the untracked backup were not changed.
- No real mobile WebXR device/browser was available for runtime validation. Camera passthrough, hit-test behavior, placement/manipulation, map behavior on-device, downloads/uploads, and XR exit remain to be checked manually.
import "./ar_debug_console.js";
import { initializeMainUI, installLegacyUI } from "./ar_main.js";
import { downloadScene } from "./ar_download.js";
import { showStartScreen } from "./ar_start.js";
if (navigator.xr) {
showStartScreen();
} else {
alert("WebXR wird nicht unterstützt.");
}
installLegacyUI(downloadScene);
initializeMainUI();
function downloadScene() {
import { getAllPlacedModels, geoLocation, showInfoDialog } from "./ar_main.js";
import { threeToLeaflet } from "./ar_overviewmap.js";
export function downloadScene() {
const placedModels = getAllPlacedModels().map(model => {
const { lat, lng } = threeToLeaflet(model.position.x, model.position.z);
return {
......@@ -44,7 +47,7 @@ function downloadScene() {
document.body.removeChild(a);
}
function loadSceneFromFile(loadedData) {
export function loadSceneFromFile(loadedData) {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'application/json';
......
import * as THREE from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { clone as cloneSkinned } from "three/addons/utils/SkeletonUtils.js";
import {
centerOnPlan,
configureMap,
init_map,
leafletToThree,
roundTo,
stopMapUpdates,
} from "./ar_overviewmap.js";
/* ========================= */
/* GLOBALE VARIABLEN */
/* SHARED AR STATE */
/* ========================= */
let selectedPlacedModel = null;
let currentSession = null;
let reticle = null;
let scene, camera;
let geoLocation;
export let selectedPlacedModel = null;
export let currentSession = null;
export let reticle = null;
export let scene;
export let camera;
export 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 THREE.GLTFLoader();
const gltfLoader = new GLTFLoader();
const menus = [
'menu-bar',
......@@ -35,7 +45,7 @@ menus.forEach(id => {
/* MODELLE */
/* ========================= */
let models = {
export let models = {
bench: {
name: "Bench",
......@@ -106,8 +116,12 @@ let models = {
/* INITIALISIERUNG */
/* ========================= */
window.onload = () => {
export function initializeMainUI() {
configureMap({
getCamera: () => camera,
getGeoLocation: () => geoLocation,
getPlacedModels: getAllPlacedModels,
});
initializeAddMenu();
// Allen Buttons den Sound hinzufügen
......@@ -121,7 +135,7 @@ window.onload = () => {
playButtonSound
);
});
};
}
function initializeAddMenu() {
......@@ -165,7 +179,7 @@ function initializeAddMenu() {
/* MENÜ-STEUERUNG */
/* ========================= */
function showMenu(menuId) {
export function showMenu(menuId) {
const isMapWindow =
menuId === 'map-window';
......@@ -256,18 +270,7 @@ function closeMap() {
}
// Stop the map update interval
if (
typeof intervalId !== 'undefined'
) {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
}
stopMapUpdates();
// Clear selected model
......@@ -275,7 +278,7 @@ function closeMap() {
}
function closeDynamicMenu() {
export function closeDynamicMenu() {
const dynamicMenu =
document.getElementById(
......@@ -290,7 +293,7 @@ function closeDynamicMenu() {
}
function showInfoDialog(message) {
export function showInfoDialog(message) {
const infoDialog =
document.getElementById(
......@@ -310,7 +313,7 @@ function showInfoDialog(message) {
}
function closeInfoDialog() {
export function closeInfoDialog() {
const infoDialog =
document.getElementById(
......@@ -326,7 +329,7 @@ function closeInfoDialog() {
/* MODELL-HANDLING */
/* ========================= */
function getAllPlacedModels() {
export function getAllPlacedModels() {
return scene.children.filter(
child =>
......@@ -376,7 +379,7 @@ function placeModel(
modelCache.get(filePath);
const model =
cachedModel.clone(true);
cloneSkinned(cachedModel);
if (
......@@ -425,7 +428,7 @@ function placeModel(
);
const model =
gltf.scene.clone(true);
cloneSkinned(gltf.scene);
if (
......@@ -473,7 +476,7 @@ function placeModel(
}
function selectModel(modelId) {
export function selectModel(modelId) {
const model =
models[modelId];
......@@ -659,7 +662,7 @@ function removeHighlightFromAllModels() {
}
function deleteModel() {
export function deleteModel() {
if (!selectedPlacedModel) {
......@@ -682,7 +685,7 @@ function deleteModel() {
}
function confirmDelete(
export function confirmDelete(
shouldDelete
) {
......@@ -715,7 +718,7 @@ function confirmDelete(
}
function completeEditing() {
export function completeEditing() {
removeHighlightFromSelectedModel();
......@@ -740,7 +743,7 @@ function completeEditing() {
/* BEARBEITUNGS-MENÜS */
/* ========================= */
function openRotationMenu() {
export function openRotationMenu() {
if (!selectedPlacedModel) {
......@@ -796,7 +799,7 @@ function openRotationMenu() {
}
function updateRotation(
export function updateRotation(
axis,
value
) {
......@@ -827,7 +830,7 @@ function updateRotation(
}
function openScaleMenu() {
export function openScaleMenu() {
if (!selectedPlacedModel) {
......@@ -904,7 +907,7 @@ function openScaleMenu() {
}
function updateScale(
export function updateScale(
percentValue,
minScale,
maxScale
......@@ -943,7 +946,7 @@ function updateScale(
let moveDelta = 0.1;
function openMoveMenu() {
export function openMoveMenu() {
if (!selectedPlacedModel) {
......@@ -1045,7 +1048,7 @@ function openMoveMenu() {
}
function updateMoveDelta(
export function updateMoveDelta(
value
) {
......@@ -1067,7 +1070,7 @@ function updateMoveDelta(
}
function moveModelDynamic(
export function moveModelDynamic(
axis,
value
) {
......@@ -1112,172 +1115,127 @@ function closeMapDialog() {
/* AR-HANDLING */
/* ========================= */
async function activateXR(sceneData = null) {
export 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,
antialias: true,
canvas,
context: gl,
});
renderer.autoClear = false;
scene = new THREE.Scene();
document.body.appendChild(canvas);
camera = new THREE.PerspectiveCamera();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.xr.enabled = true;
renderer.xr.setReferenceSpaceType("local");
camera.matrixAutoUpdate = false;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(
70,
window.innerWidth / window.innerHeight,
0.01,
20,
);
// ========================================================
// 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 THREE.GLTFLoader();
loader.load(
gltfLoader.load(
"https://immersive-web.github.io/webxr-samples/media/gltf/reticle/reticle.gltf",
(gltf) => {
reticle = gltf.scene;
reticle.visible = false;
scene.add(reticle);
},
);
// ========================================================
// Start WebXR AR session
// ========================================================
currentSession = await navigator.xr.requestSession("immersive-ar", {
optionalFeatures: ["dom-overlay"],
domOverlay: {
root: document.body,
undefined,
(error) => {
console.error("[GLTF] Failed to load reticle:", error);
},
);
requiredFeatures: ["hit-test"],
});
try {
currentSession = await navigator.xr.requestSession("immersive-ar", {
requiredFeatures: ["hit-test"],
optionalFeatures: ["dom-overlay"],
domOverlay: {
root: document.body,
},
});
currentSession.updateRenderState({
baseLayer: new XRWebGLLayer(currentSession, gl),
});
await renderer.xr.setSession(currentSession);
} catch (error) {
console.error("[XR] Failed to start session:", error);
renderer.dispose();
canvas.remove();
currentSession = null;
showInfoDialog("Die AR-Session konnte nicht gestartet werden.");
return;
}
const referenceSpace = await currentSession.requestReferenceSpace("local");
const viewerSpace = await currentSession.requestReferenceSpace("viewer");
const hitTestSource = await currentSession.requestHitTestSource({
space: viewerSpace,
});
// ========================================================
// Show main menu
// ========================================================
document.getElementById("menu-bar").style.display = "flex";
// ========================================================
// AR session ended
// Reload the complete website
// ========================================================
currentSession.addEventListener("end", () => {
console.log("AR session ended");
// Completely reload the website.
// This resets the AR canvas, menus, scene, map,
// variables, and all other application state.
window.location.reload();
});
// ========================================================
// Select objects in AR
// ========================================================
canvas.addEventListener("pointerdown", selectModelFromScene);
// ========================================================
// Geolocation
// ========================================================
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));
// =================================================
// Load saved scene models
// =================================================
if (sceneData) {
sceneData.models.forEach((model) => {
if (model.name) {
const modelDefinition = Object.values(models).find(
(m) => model.name === m.name,
(candidate) => model.name === candidate.name,
);
if (!modelDefinition) {
console.error("Modell nicht gefunden:", model.name);
console.error("[Scene] Model not found:", model.name);
return;
}
const filePath = modelDefinition.file;
const { x, z } = leafletToThree(model.lat, model.lng);
const positionVector = new THREE.Vector3(x, model.position.y, z);
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;
}
});
......@@ -1285,14 +1243,9 @@ async function activateXR(sceneData = null) {
});
}
},
(error) => {
console.error(
"Fehler bei der Geolokalisierung:",
JSON.stringify(error),
);
console.error("[Geo] Failed to get location:", error);
},
{
enableHighAccuracy: true,
maximumAge: 2000,
......@@ -1300,73 +1253,46 @@ async function activateXR(sceneData = null) {
},
);
}
// ========================================================
// XR render loop
// ========================================================
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);
renderer.setAnimationLoop((time, frame) => {
if (frame) {
const pose = frame.getViewerPose(referenceSpace);
camera.matrix.fromArray(view.transform.matrix);
if (pose) {
const hitTestResults = frame.getHitTestResults(hitTestSource);
camera.projectionMatrix.fromArray(view.projectionMatrix);
if (hitTestResults.length > 0) {
const hitPose = hitTestResults[0].getPose(referenceSpace);
camera.updateMatrixWorld(true);
// =================================================
// Hit testing
// =================================================
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);
if (reticle && hitPose) {
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;
}
} else if (reticle) {
reticle.visible = false;
}
// =================================================
// Render AR scene
// =================================================
renderer.render(scene, camera);
}
renderer.render(scene, camera);
});
}
/* ========================= */
/* AR EXIT */
/* ========================= */
function exitAR() {
export function exitAR() {
document.getElementById("confirmation-dialog").style.display = "flex";
}
function confirmExit(shouldExit) {
export function confirmExit(shouldExit) {
if (shouldExit && currentSession) {
currentSession.end();
}
......@@ -1374,6 +1300,35 @@ function confirmExit(shouldExit) {
document.getElementById("confirmation-dialog").style.display = "none";
}
export function installLegacyUI(downloadScene) {
Object.assign(window, {
showMenu,
selectModel,
showInfoDialog,
openRotationMenu,
openScaleMenu,
openMoveMenu,
deleteModel,
completeEditing,
exitAR,
downloadScene,
confirmDelete,
confirmExit,
closeInfoDialog,
closeDynamicMenu,
updateRotation,
updateScale,
updateMoveDelta,
moveModelDynamic,
});
Object.defineProperty(window, "moveDelta", {
configurable: true,
get: () => moveDelta,
});
}
/* ========================= */
/* BENUTZERINTERAKTIONEN */
/* ========================= */
......@@ -1394,4 +1349,4 @@ function playButtonSound() {
soundTimeout = false;
}, 200);
}
}
\ No newline at end of file
}
import * as THREE from "three";
let init = false;
let mymap;
let userIcon;
let intervalId;
let modeMarkers = [];
let getCamera = () => null;
let getGeoLocation = () => null;
let getPlacedModels = () => [];
// PERFORMANCE: Reuse one Vector3 instead of allocating a new
// Vector3 every time updateMap() runs.
......@@ -42,12 +47,19 @@ const planBounds = [
const earthRadius = 6378137;
export function configureMap(dependencies) {
getCamera = dependencies.getCamera;
getGeoLocation = dependencies.getGeoLocation;
getPlacedModels = dependencies.getPlacedModels;
}
// ============================================================
// Initialize map
// ============================================================
function init_map() {
export function init_map() {
const mapContainer = document.getElementById("map-container");
const geoLocation = getGeoLocation();
if (mapContainer) {
if (!init) {
......@@ -98,7 +110,7 @@ function init_map() {
// Add currently placed models to map
// ========================================================
const models = getAllPlacedModels();
const models = getPlacedModels();
models.forEach((model) => {
const { lat, lng } = threeToLeaflet(model.position.x, model.position.z);
......@@ -142,11 +154,18 @@ function init_map() {
}
}
export function stopMapUpdates() {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
}
// ============================================================
// Center map on proposed plan
// ============================================================
function centerOnPlan() {
export function centerOnPlan() {
if (!mymap) {
console.error("Map ist noch nicht initialisiert.");
......@@ -164,7 +183,7 @@ function centerOnPlan() {
// Add georeferenced proposed plan
// ============================================================
function addPlanOverlay() {
export function addPlanOverlay() {
if (!mymap) {
console.error("Map ist noch nicht initialisiert.");
......@@ -193,7 +212,7 @@ function addPlanOverlay() {
// Update map
// ============================================================
function updateMap() {
export function updateMap() {
// The interval is normally stopped by closeMap().
// This guard also handles cases where the map is hidden
// by another UI action.
......@@ -205,6 +224,8 @@ function updateMap() {
return;
}
const camera = getCamera();
if (!camera || !userIcon) {
return;
}
......@@ -234,7 +255,9 @@ function updateMap() {
// Convert Three.js coordinates to Leaflet coordinates
// ============================================================
function threeToLeaflet(threeX, threeZ) {
export function threeToLeaflet(threeX, threeZ) {
const geoLocation = getGeoLocation();
if (!geoLocation) {
console.error("GeoLocation ist noch nicht verfügbar");
......@@ -265,7 +288,9 @@ function threeToLeaflet(threeX, threeZ) {
// Convert Leaflet coordinates to Three.js coordinates
// ============================================================
function leafletToThree(lat, lng) {
export function leafletToThree(lat, lng) {
const geoLocation = getGeoLocation();
if (!geoLocation) {
console.error("GeoLocation ist noch nicht verfügbar");
......@@ -298,7 +323,7 @@ function leafletToThree(lat, lng) {
// Round number
// ============================================================
function roundTo(n, digits) {
export function roundTo(n, digits) {
var negative = false;
if (digits === undefined) {
......
import { activateXR } from "./ar_main.js";
import { loadSceneFromFile } from "./ar_download.js";
/* ========================= */
/* DEBUGGING UND START */
/* ========================= */
......@@ -17,7 +20,7 @@ let predefinedButton2 = null;
/* PREDEFINED SCENE */
/* ========================= */
async function loadPredefinedScene(filePath, fileName) {
export async function loadPredefinedScene(filePath, fileName) {
try {
const response = await fetch(filePath);
......@@ -44,7 +47,7 @@ async function loadPredefinedScene(filePath, fileName) {
/* Create the Start Screen */
/* ============================================================ */
function showStartScreen() {
export function showStartScreen() {
// Prevent duplicate start screens
if (
startScreen ||
......@@ -262,12 +265,3 @@ function showStartScreen() {
};
}
/* ============================================================ */
/* INITIAL START */
/* ============================================================ */
if (navigator.xr) {
showStartScreen();
} else {
alert("WebXR wird nicht unterstützt.");
}
......@@ -9,8 +9,14 @@
<link rel="stylesheet" href="style.css">
<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>
<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>
</head>
<body>
......@@ -299,7 +305,7 @@
============================================================ -->
<div id="app-version">
v0.2.0
v0.3.0
</div>
......@@ -307,21 +313,13 @@
JAVASCRIPT
============================================================ -->
<script src="ar_start.js"></script>
<script src="ar_main.js"></script>
<script src="ar_debug_console.js"></script>
<link
rel="stylesheet"
href="https://unpkg.com/leaflet/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<script src="ar_overviewmap.js"></script>
<script src="ar_download.js"></script>
<script type="module" src="ar_app.js"></script>
<!-- ============================================================
......@@ -336,4 +334,4 @@
</body>
</html>
\ No newline at end of file
</html>
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