sync verbessert
This commit is contained in:
+2
-2
@@ -62,8 +62,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="sideBarText">Farbdarstellung</p>
|
<p class="sideBarText">Farbdarstellung</p>
|
||||||
<div class="buttonRow">
|
<div class="buttonRow">
|
||||||
<button class="colorButtons active">RGB</button>
|
<button class="colorButtons active" id="rgb">RGB</button>
|
||||||
<button class="colorButtons">höhencodiert</button>
|
<button class="colorButtons" id="height">höhencodiert</button>
|
||||||
</div>
|
</div>
|
||||||
<p class="sideBarText" id="pointSliderValue"></p>
|
<p class="sideBarText" id="pointSliderValue"></p>
|
||||||
<input id="pointSlider" type="range" min="500000" max="10000000" value="3000000">
|
<input id="pointSlider" type="range" min="500000" max="10000000" value="3000000">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/*import maplibregl from "maplibre-gl";
|
import maplibregl from "maplibre-gl";
|
||||||
import proj4 from "proj4";
|
import proj4 from "proj4";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
|
|
||||||
@@ -72,40 +72,104 @@ function loadPointCloud(path) {
|
|||||||
material.size = 1;
|
material.size = 1;
|
||||||
material.pointSizeType = Potree.PointSizeType.ADAPTIVE;
|
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() {
|
function syncCamera() {
|
||||||
if (!currentPointCloud) return;
|
if (!currentPointCloud || isSyncing) return;
|
||||||
|
isSyncing = true;
|
||||||
|
|
||||||
const center = map.getCenter();
|
const transform = map.transform;
|
||||||
const [x, y] = proj4("EPSG:4326", "EPSG:25832", [center.lng, center.lat]);
|
|
||||||
|
|
||||||
const pitch = map.getPitch() * Math.PI / 180;
|
// 1. Dimensionen auf den Pixel genau angleichen
|
||||||
const bearing = map.getBearing() * Math.PI / 180;
|
if (viewer.renderer) {
|
||||||
const zoom = map.getZoom();
|
viewer.renderer.setSize(map.getCanvas().clientWidth, map.getCanvas().clientHeight);
|
||||||
|
}
|
||||||
|
|
||||||
const earthCircumference = 40075016.686;
|
// 2. Mathematische Parameter aus dem MapLibre-Kern extrahieren
|
||||||
const metersPerPixel = earthCircumference * Math.cos(center.lat * Math.PI / 180) / Math.pow(2, zoom + 8);
|
const pitch = transform.pitch * Math.PI / 180;
|
||||||
const distance = metersPerPixel * (map.getCanvas().height / 2) / Math.cos(pitch);
|
const bearing = transform.bearing * Math.PI / 180;
|
||||||
|
|
||||||
|
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);
|
offset.applyAxisAngle(new THREE.Vector3(0, 0, 1), -bearing);
|
||||||
|
|
||||||
const cameraPosition = target.clone().add(offset);
|
const cameraPosition = target.clone().add(offset);
|
||||||
|
|
||||||
|
// 3. Potree-Kamera absolut starr setzen
|
||||||
viewer.scene.view.position.copy(cameraPosition);
|
viewer.scene.view.position.copy(cameraPosition);
|
||||||
viewer.scene.view.lookAt(target);
|
viewer.scene.view.lookAt(target);
|
||||||
|
viewer.setFOV(transform.fov);
|
||||||
|
|
||||||
|
// 4. Potree rendern
|
||||||
viewer.renderer.resetState();
|
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('zoom', syncCamera);
|
||||||
map.on('pitch', syncCamera);
|
map.on('move', syncCamera);
|
||||||
map.on('rotate', 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) {
|
function changeBaseMap(newMap) {
|
||||||
var basemapStyle;
|
var basemapStyle;
|
||||||
@@ -180,11 +244,51 @@ document.querySelectorAll('.qualityButtons').forEach(btn => {
|
|||||||
|
|
||||||
document.querySelectorAll('.colorButtons').forEach(btn => {
|
document.querySelectorAll('.colorButtons').forEach(btn => {
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener('click', () => {
|
||||||
|
if (!currentPointCloud) return; // Abbrechen, falls noch keine Wolke geladen ist
|
||||||
|
|
||||||
document.querySelectorAll('.colorButtons').forEach(b => b.classList.remove('active'));
|
document.querySelectorAll('.colorButtons').forEach(b => b.classList.remove('active'));
|
||||||
btn.classList.add('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 maplibregl from "maplibre-gl";
|
||||||
import proj4 from "proj4";
|
import proj4 from "proj4";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
@@ -610,3 +714,4 @@ colorButtons.forEach(button => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
Reference in New Issue
Block a user