sync verbessert

This commit is contained in:
2026-06-08 21:19:53 +02:00
parent 98075fb1ea
commit e2a248671d
3 changed files with 132 additions and 23 deletions
+2 -2
View File
@@ -62,8 +62,8 @@
</div>
<p class="sideBarText">Farbdarstellung</p>
<div class="buttonRow">
<button class="colorButtons active">RGB</button>
<button class="colorButtons">höhencodiert</button>
<button class="colorButtons active" id="rgb">RGB</button>
<button class="colorButtons" id="height">höhencodiert</button>
</div>
<p class="sideBarText" id="pointSliderValue"></p>
<input id="pointSlider" type="range" min="500000" max="10000000" value="3000000">
+125 -20
View File
@@ -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 => {
});
});
});
*/
+5 -1
View File
@@ -374,4 +374,8 @@ position: absolute;
),
rgba(6, 45, 135, 0.92);
color: #ffffff;
}
}
#potree_render_area {
pointer-events: none;
}