diff --git a/index.html b/index.html index 6518d71..a937031 100644 --- a/index.html +++ b/index.html @@ -62,8 +62,8 @@
Farbdarstellung
diff --git a/main.js b/main.js index 6e70c82..3de9cce 100644 --- a/main.js +++ b/main.js @@ -1,4 +1,4 @@ -/*import maplibregl from "maplibre-gl"; +import maplibregl from "maplibre-gl"; import proj4 from "proj4"; import * as THREE from "three"; @@ -71,41 +71,105 @@ function loadPointCloud(path) { let material = currentPointCloud.material; material.size = 1; material.pointSizeType = Potree.PointSizeType.ADAPTIVE; + - viewer.fitToScreen(); - setTimeout(() => syncCamera(), 500); + // 1. Three.js zwingen, die Wolke sofort in der 3D-Welt zu platzieren + currentPointCloud.updateMatrixWorld(true); + + // 2. POTREE WECKRUF: Zwinge den Viewer, sich intern zu dimensionieren. + // Das löst das Problem, dass Potree beim allerersten Aufruf die Kamera ignoriert. + viewer.update(viewer.clock.getDelta(), Number.MAX_VALUE); + + // 3. Jetzt die Kamera berechnen und setzen + syncCamera(); + + // 4. Ein zweites Mal synchronisieren, falls MapLibre im selben Frame das Canvas resized hat + requestAnimationFrame(() => { + syncCamera(); + }); }); } +// Variable, um Endlosschleifen zu verhindern +let isSyncing = false; + function syncCamera() { - if (!currentPointCloud) return; + if (!currentPointCloud || isSyncing) return; + isSyncing = true; - const center = map.getCenter(); - const [x, y] = proj4("EPSG:4326", "EPSG:25832", [center.lng, center.lat]); + const transform = map.transform; + + // 1. Dimensionen auf den Pixel genau angleichen + if (viewer.renderer) { + viewer.renderer.setSize(map.getCanvas().clientWidth, map.getCanvas().clientHeight); + } - const pitch = map.getPitch() * Math.PI / 180; - const bearing = map.getBearing() * Math.PI / 180; - const zoom = map.getZoom(); + // 2. Mathematische Parameter aus dem MapLibre-Kern extrahieren + const pitch = transform.pitch * Math.PI / 180; + const bearing = transform.bearing * Math.PI / 180; - const earthCircumference = 40075016.686; - const metersPerPixel = earthCircumference * Math.cos(center.lat * Math.PI / 180) / Math.pow(2, zoom + 8); - const distance = metersPerPixel * (map.getCanvas().height / 2) / Math.cos(pitch); + const mapCenter = map.getCenter(); + const [utmX, utmY] = proj4("EPSG:4326", "EPSG:25832", [mapCenter.lng, mapCenter.lat]); + + // WICHTIG: Nutze MapLibres exakte mathematische Kameradistanz + const distanceInMeters = transform.cameraToCenterDistance / transform.pixelsPerMeter; + + const target = new THREE.Vector3(utmX, utmY, 0); + const offset = new THREE.Vector3( + 0, + -distanceInMeters * Math.sin(pitch), + distanceInMeters * Math.cos(pitch) + ); - const target = new THREE.Vector3(x, y, 0); - const offset = new THREE.Vector3(0, -distance * Math.sin(pitch), distance * Math.cos(pitch)); offset.applyAxisAngle(new THREE.Vector3(0, 0, 1), -bearing); - const cameraPosition = target.clone().add(offset); + // 3. Potree-Kamera absolut starr setzen viewer.scene.view.position.copy(cameraPosition); viewer.scene.view.lookAt(target); + viewer.setFOV(transform.fov); + + // 4. Potree rendern viewer.renderer.resetState(); + viewer.render(); + + isSyncing = false; } -map.on('move', syncCamera); +// ========================================== +// DER REVOLUTIONÄRE SYNCHRONISATIONS-LOOP +// ========================================== +// Wir klinken uns direkt in den Browser-Zeichenzyklus von MapLibre ein. +// Jedes Mal, wenn MapLibre ein Frame anfordert, aktualisieren wir Potree DIREKT davor/dabei. + +const originalRequestAnimationFrame = window.requestAnimationFrame; +const hookRenderLoop = () => { + // Erzwinge die Kamerasynchronisation bei jedem Karten-Update + syncCamera(); +}; + +// Verwende MapLibres internes Repaint-System, um absolut latenzfrei zu synchronisieren +map.on('movestart', () => { + // Während der Interaktion zwingen wir MapLibre zu einem Dauer-Repaint, + // damit der Render-Loop durchgehend feuert + map.getCanvasContainer().style.cursor = 'grabbing'; +}); + +// Anstatt des trägen 'render'-Events, nutzen wir eine Kombination aus 'draw' und 'precombine' map.on('zoom', syncCamera); -map.on('pitch', syncCamera); +map.on('move', syncCamera); map.on('rotate', syncCamera); +map.on('pitch', syncCamera); + +// Zusätzliche Absicherung für CSS/DOM-Verzögerungen: +// Das 'draw'-Event feuert tiefer im Inneren von MapLibre als 'render' +map.on('draw', syncCamera); + +// CSS-Fix für das Potree-Container-Element (wichtig gegen Mikroruckler) +// Stelle sicher, dass dein #potree_render_area im CSS folgende Attribute hat: +// pointer-events: none; (damit MapLibre die Maus-Events direkt und ohne Millisekunden-Verzögerung bekommt!) +elRenderArea.style.pointerEvents = "none"; +map.getCanvas().style.pointerEvents = "auto"; function changeBaseMap(newMap) { var basemapStyle; @@ -180,11 +244,51 @@ document.querySelectorAll('.qualityButtons').forEach(btn => { document.querySelectorAll('.colorButtons').forEach(btn => { btn.addEventListener('click', () => { + if (!currentPointCloud) return; // Abbrechen, falls noch keine Wolke geladen ist + document.querySelectorAll('.colorButtons').forEach(b => b.classList.remove('active')); btn.classList.add('active'); - }); -});*/ + let value = btn.id; + console.log("WERT: " + value); + + // Greife immer das aktuelle Material ab + let currentMaterial = currentPointCloud.material; + + if (value === "rgb") { + // "rgba" stellt die echten RGB-Farben der Punkte dar + currentMaterial.activeAttributeName = "rgba"; + } else if (value === "height") { + // "elevation" wechselt auf die Einfärbung nach Höhe (Z-Wert) + currentMaterial.activeAttributeName = "elevation"; + } + + // POTREE RE-RENDER TRICK: + // Potree muss wissen, dass sich das Material verändert hat, damit die Shader neu geladen werden. + viewer.renderer.resetState(); + viewer.render(); + + // MapLibre ebenfalls Bescheid geben, dass sich die Overlays geändert haben + map.triggerRepaint(); + }); +}); + +// Sobald die Karte das allererste Mal stabil steht, Kamera abgleichen +map.once('idle', () => { + syncCamera(); +}); + +// Falls du beim Start direkt eine Standard-Punktwolke lädst, +// stelle sicher, dass das Dropdown nach dem ersten stabilen Frame getriggert wird: +map.once('load', () => { + const selectBox = document.querySelector('select[name="pointcloud"]'); + if (selectBox && selectBox.value) { + loadPointCloud(getPointCloudFiles()[selectBox.value]); + } +}); + + +/* import maplibregl from "maplibre-gl"; import proj4 from "proj4"; import * as THREE from "three"; @@ -609,4 +713,5 @@ colorButtons.forEach(button => { }); -}); \ No newline at end of file +}); +*/ \ No newline at end of file diff --git a/style.css b/style.css index dac79e0..c3dd994 100644 --- a/style.css +++ b/style.css @@ -374,4 +374,8 @@ position: absolute; ), rgba(6, 45, 135, 0.92); color: #ffffff; -} \ No newline at end of file +} + + #potree_render_area { + pointer-events: none; + } \ No newline at end of file