import "@maptiler/sdk/style.css"; import { LngLat, Map, MapStyle, config, math } from "@maptiler/sdk"; import { addPerformanceStats, setupMapTilerApiKey } from "./demo-utils"; import { AltitudeReference, Layer3D } from "../../src"; import { Mesh, MeshStandardMaterial, OctahedronGeometry, Vector3 } from "three"; const center: [number, number] = [2.3492790851936776, 48.85417501375531]; setupMapTilerApiKey({ config }); addPerformanceStats(); async function main() { const map = new Map({ container: document.getElementById("map") as HTMLElement, style: MapStyle.SATELLITE.DEFAULT, center: center, maxPitch: 85, terrainControl: true, terrain: true, maptilerLogo: true, projectionControl: true, customAttribution: "Low Poly Biplane model by ElectrikGoat0395", zoom: 12.5, bearing: 0, pitch: 65, }); await map.onReadyAsync(); const layer3D = new Layer3D("layer3d"); map.addLayer(layer3D); const LNGLAT_OFFSET = 0.15; const PLANE_BASE_ALT = 2200; layer3D.addPointLight("point-light", { intensity: 20, color: "#ffffff", lngLat: center.map(num => num + 0.0001) as [number, number], altitude: 2000, altitudeReference: AltitudeReference.GROUND, }); const biplaneOne = await layer3D.addMeshFromURL("biplaneOne", "models/biplane/scene.gltf", { lngLat: center, heading: 0, scale: 100, altitude: PLANE_BASE_ALT, altitudeReference: AltitudeReference.GROUND, transform: { rotation: { y: Math.PI / 2, }, } }); layer3D.cloneMesh("biplaneOne", "biplaneTwo"); const biplaneTwo = layer3D.getItem3D("biplaneTwo")!; let progress = 0; let speed = 0.001; const startLngLat = LngLat.convert(center.map(num => num - LNGLAT_OFFSET) as [number, number]); const endLngLat = LngLat.convert(center.map(num => num + LNGLAT_OFFSET) as [number, number]); function updateBiPlaneOne() { const position = lerpLngLat(startLngLat, endLngLat, progress); const nextPosition = lerpLngLat(startLngLat, endLngLat, progress + 0.01); const currentAltitude = 2000 * Math.sin(progress * 8) + PLANE_BASE_ALT; const nextAltitude = 2000 * Math.sin(progress * 8 + 0.1) + PLANE_BASE_ALT; const pitchInRadians = getPitch(currentAltitude, nextAltitude, position.distanceTo(nextPosition)); const pitch = radiansToDegrees(pitchInRadians); biplaneOne.setPitch(pitch); biplaneOne.setAltitude(currentAltitude); biplaneOne.setLngLat(position); biplaneOne.setHeading(radiansToDegrees(getHeading(startLngLat, endLngLat))); biplaneOne.setRoll(radiansToDegrees(50 * progress)); } function updateBiPlaneTwo() { const position = orbitCenter(center, progress, 0.05); const nextPosition = orbitCenter(center, progress + 0.01, 0.05); biplaneTwo.setLngLat(position); biplaneTwo.setHeading(getHeading(position, nextPosition) * 180 / Math.PI); biplaneTwo.setRoll(45); const currentAltitude = 2000 * Math.sin(progress * Math.PI * 2) + PLANE_BASE_ALT; const nextAltitude = 2000 * Math.sin(progress * Math.PI * 2 + 0.1) + PLANE_BASE_ALT; const pitchInRadians = getPitch(currentAltitude, nextAltitude, position.distanceTo(nextPosition)); const pitch = pitchInRadians * 180 / Math.PI; biplaneTwo.setPitch(pitch); biplaneTwo.setAltitude(currentAltitude); } function loop() { requestAnimationFrame(loop); updateBiPlaneOne(); updateBiPlaneTwo(); progress += 0.001; if (progress > 1) { progress = 0; } } loop(); } function orbitCenter(center: [number, number], progress: number, radius: number): LngLat { return new LngLat( center[0] + radius * Math.cos(progress * 2 * Math.PI), center[1] + radius * Math.sin(progress * 2 * Math.PI), ); } function lerpLngLat(startLngLat: LngLat, endLngLat: LngLat, progress: number): LngLat { return new LngLat( startLngLat.lng + (endLngLat.lng - startLngLat.lng) * progress, startLngLat.lat + (endLngLat.lat - startLngLat.lat) * progress, ); } function getPitch(startAltitude: number, endAltitude: number, distance: number): number { return Math.asin((endAltitude - startAltitude) / distance); } function getHeading(startLngLat: LngLat, endLngLat: LngLat): number { return Math.atan2(endLngLat.lng - startLngLat.lng, endLngLat.lat - startLngLat.lat); } function radiansToDegrees(radians: number): number { return radians * 180 / Math.PI; } main();