From 8b0ebdbe526a0b6a84679db81d62f21905f76fcf Mon Sep 17 00:00:00 2001 From: Aigars Zeiza Date: Wed, 23 Jul 2025 17:05:11 +0300 Subject: [PATCH 1/5] flight mechanics refactor --- src/camera/Camera.ts | 134 ++++++++- src/camera/PlanetCamera.ts | 282 ++++-------------- src/control/CompassButton.ts | 14 +- .../elevationProfile/ElevationProfileScene.ts | 4 +- src/scene/Planet.ts | 68 +---- 5 files changed, 206 insertions(+), 296 deletions(-) diff --git a/src/camera/Camera.ts b/src/camera/Camera.ts index a0add68a..15572a15 100644 --- a/src/camera/Camera.ts +++ b/src/camera/Camera.ts @@ -13,8 +13,16 @@ import {Vec4} from "../math/Vec4"; import {Sphere} from "../bv/Sphere"; import {Quat} from "../math/Quat"; import {DEGREES_DOUBLE, RADIANS, RADIANS_HALF} from "../math"; +import { Easing, EasingFunction } from "../utils/easing"; +import { LonLat } from "../LonLat"; -export type CameraEvents = ["viewchange", "moveend"]; +export type CameraEvents = [ + "viewchange", + "moveend", + "flystart", + "flyend", + "flystop", +]; const EVENT_NAMES: CameraEvents = [ /** @@ -27,7 +35,25 @@ const EVENT_NAMES: CameraEvents = [ * Camera is stopped. * @event og.Camera#moveend */ - "moveend" + "moveend", + + /** + * Triggered before camera flight. + * @event og.Camera#flystart + */ + "flystart", + + /** + * Triggered when camera finished flight. + * @event og.Camera#flyend + */ + "flyend", + + /** + * Triggered when flight was stopped. + * @event og.Camera#flystop + */ + "flystop", ]; export interface ICameraParams { @@ -40,6 +66,35 @@ export interface ICameraParams { height?: number; } +export interface IFlyCartesianParams extends IFlyBaseParams { + look?: Vec3 | LonLat; + up?: Vec3; +} + +export interface IFlyBaseParams { + duration?: number; + ease?: EasingFunction; + completeCallback?: Function; + startCallback?: Function; + frameCallback?: Function; +} + +export const DEFAULT_FLIGHT_DURATION = 800; +export const DEFAULT_EASING = Easing.CubicInOut; + +type CameraFrame = { + eye: Vec3; + n: Vec3; + u: Vec3; + v: Vec3; +} + +type CameraFlight = { + fly: (progress: number) => CameraFrame; + duration: number; + startedAt: number; +} + const getHorizontalViewAngleByFov = (fov: number, aspect: number) => DEGREES_DOUBLE * Math.atan(Math.tan(RADIANS_HALF * fov) * aspect); /** @@ -172,6 +227,12 @@ class Camera { public _height: number; + protected _flight: CameraFlight | null; + protected _completeCallback: Function | null; + protected _frameCallback: Function | null; + + protected _flying: boolean; + // public dirForwardNED: Vec3; // public dirUpNED: Vec3; // public dirRightNED: Vec3; @@ -213,6 +274,11 @@ class Camera { this._peye = this.eye.clone(); this.isMoving = false; + this._flight = null; + this._completeCallback = null; + this._frameCallback = null; + this._flying = false; + this._tanViewAngle_hrad = 0.0; this._tanViewAngle_hradOneByHeight = 0.0; @@ -272,6 +338,70 @@ class Camera { return this.__id; } + /** + * Flies to the cartesian coordinates. + * @public + * @param {Vec3} [cartesian] - Finish cartesian coordinates. + * @param {IFlyCartesianParams} [params] - Flight parameters + */ + flyCartesian(cartesian: Vec3, params?: IFlyCartesianParams): void { + } + + /** + * Breaks the flight. + * @public + */ + stopFlying() { + if (!this._flying) { + return; + } + this._flying = false; + this._flight = null; + this._frameCallback = null; + this.events.dispatch(this.events.flystop, this); + } + + /** + * Prepare camera to the frame. Used in render node frame function. + * @public + */ + public checkFly() { + if (this._flying && this._flight !== null) { + let progress = Math.min((Date.now() - this._flight.startedAt) / this._flight.duration, 1); + + const frame = this._flight.fly(progress); + this.eye = frame.eye; + this._r = frame.u; + this._u = frame.v; + this._b = frame.n; + this._f.set(-this._b.x, -this._b.y, -this._b.z); + + if (this._frameCallback) { + this._frameCallback(); + } + + this.update(); + + if (progress >= 1) { + this.stopFlying(); + if (this._completeCallback) { + this.events.dispatch(this.events.flyend, this); + this._completeCallback(); + this._completeCallback = null; + } + } + } + } + + /** + * Returns camera is flying. + * @public + * @returns {boolean} + */ + isFlying() { + return this._flying; + } + public checkMoveEnd() { let r = this._r, u = this._u, diff --git a/src/camera/PlanetCamera.ts b/src/camera/PlanetCamera.ts index f623ab7f..9d7b6daa 100644 --- a/src/camera/PlanetCamera.ts +++ b/src/camera/PlanetCamera.ts @@ -1,6 +1,6 @@ import * as mercator from "../mercator"; import * as math from "../math"; -import {Camera, CameraEvents, type ICameraParams} from "./Camera"; +import {Camera, DEFAULT_EASING, DEFAULT_FLIGHT_DURATION, type IFlyCartesianParams, type ICameraParams} from "./Camera"; import {Key} from "../Lock"; import {LonLat} from "../LonLat"; import {Mat4} from "../math/Mat4"; @@ -11,54 +11,18 @@ import {Vec3} from "../math/Vec3"; import {Extent} from "../Extent"; import {Segment} from "../segment/Segment"; import {RADIANS} from "../math"; -import { EventsHandler } from "../Events"; -import { Easing, EasingFunction } from "../utils/easing"; export interface IPlanetCameraParams extends ICameraParams { minAltitude?: number; maxAltitude?: number; } -export type PlanetCameraEventsList = [ - "flystart", - "flyend", - "flystop", -]; - -const PLANET_CAMERA_EVENTS: PlanetCameraEventsList = [ - /** - * Triggered before camera flight. - * @event og.PlanetCamera#flystart - */ - "flystart", - - /** - * Triggered when camera finished flight. - * @event og.PlanetCamera#flyend - */ - "flyend", - - /** - * Triggered when flight was stopped. - * @event og.PlanetCamera#flystop - */ - "flystop", -]; - -export const DEFAULT_FLIGHT_DURATION = 800; -export const DEFAULT_EASING = Easing.CubicInOut; - -type CameraFrame = { - eye: Vec3; - n: Vec3; - u: Vec3; - v: Vec3; +export interface IPlanetFlyCartesianParams extends IFlyCartesianParams { + amplitude?: number; } -type CameraFlight = { - fly: (progress: number) => CameraFrame; - duration: number; - startedAt: number; +export interface IPlanetFlyDistanceParams extends IPlanetFlyCartesianParams { + distance?: number; } /** @@ -78,9 +42,9 @@ type CameraFlight = { * @param {Vec3} [options.up] - Camera eye position. Default (0,1,0) * @fires og.Camera#viewchange * @fires og.Camera#moveend - * @fires og.PlanetCamera#flystart - * @fires og.PlanetCamera#flyend - * @fires og.PlanetCamera#flystop + * @fires og.Camera#flystart + * @fires og.Camera#flyend + * @fires og.Camera#flystop */ class PlanetCamera extends Camera { /** @@ -90,8 +54,6 @@ class PlanetCamera extends Camera { */ public planet: Planet; - public override events: EventsHandler & EventsHandler; - /** * Minimal altitude that camera can reach over the terrain. * @public @@ -145,11 +107,7 @@ class PlanetCamera extends Camera { public slope: number; protected _keyLock: Key; - protected _flight: CameraFlight | null; - protected _completeCallback: Function | null; - protected _frameCallback: Function | null; - protected _flying: boolean; protected _checkTerrainCollision: boolean; public eyeNorm: Vec3; @@ -161,9 +119,6 @@ class PlanetCamera extends Camera { } ); - //@ts-ignore - this.events = this.events.registerNames(PLANET_CAMERA_EVENTS); - this.planet = planet; this.minAltitude = options.minAltitude || 1; @@ -378,36 +333,15 @@ class PlanetCamera extends Camera { * @public * @param {Extent} extent - Current extent. * @param {number} [height] - Destination height. - * @param {Vec3} [up] - Camera UP in the end of flying. Default - (0,1,0) - * @param {Number} [ampl] - Altitude amplitude factor. - * @param {Number} [duration] - Animation duration - * @param {EasingFunction} [ease] - Animation easing - * @param {Function} [completeCallback] - Callback that calls after flying when flying is finished. - * @param {Function} [startCallback] - Callback that calls before the flying begins. - * @param {Function} [frameCallback] - Each frame callback + * @param {IPlanetFlyCartesianParams} [params] - Flight parameters */ public flyExtent( extent: Extent, height?: number | null, - up?: Vec3 | null, - ampl?: number | null, - duration: number = DEFAULT_FLIGHT_DURATION, - ease: EasingFunction = DEFAULT_EASING, - completeCallback?: Function | null, - startCallback?: Function | null, - frameCallback?: Function | null + params: IPlanetFlyCartesianParams = {} ) { - this.flyCartesian( - this.getExtentPosition(extent, height), - Vec3.ZERO, - up, - ampl == null ? 1 : ampl, - duration, - ease, - completeCallback, - startCallback, - frameCallback - ); + params.look = Vec3.ZERO; + this.flyCartesian(this.getExtentPosition(extent, height), params); } public override viewDistance(cartesian: Vec3, distance: number = 10000.0) { @@ -424,15 +358,24 @@ class PlanetCamera extends Camera { this.update(); } + /** + * Flies to the geo coordinates. + * @public + * @param {LonLat} lonlat - Finish coordinates. + * @param {IPlanetFlyCartesianParams} [params] - Flight parameters + */ + flyLonLat( + lonlat: LonLat, + params: IPlanetFlyCartesianParams = {} + ) { + let _lonLat = new LonLat(lonlat.lon, lonlat.lat, lonlat.height || this._lonLat.height); + this.flyCartesian(this.planet.ellipsoid.lonLatToCartesian(_lonLat), params); + } + public flyDistance( cartesian: Vec3, distance: number = 10000.0, - ampl: number = 0.0, - duration: number = DEFAULT_FLIGHT_DURATION, - ease: EasingFunction = DEFAULT_EASING, - completeCallback?: Function, - startCallback?: Function, - frameCallback?: Function + params: IPlanetFlyCartesianParams = {}, ) { let p0 = this.eye.add(this.getForward().scaleTo(distance)); let _rot = Quat.getRotationBetweenVectors(p0.getNormal(), cartesian.getNormal()); @@ -442,63 +385,35 @@ class PlanetCamera extends Camera { } else { let newPos = cartesian.add(_rot.mulVec3(this.getBackward()).scale(distance)), newUp = _rot.mulVec3(this.getUp()); - this.flyCartesian( - newPos, - cartesian, - newUp, - ampl, - duration, - ease, - completeCallback, - startCallback, - frameCallback - ); + params.look = cartesian; + params.up = newUp; + this.flyCartesian(newPos, params); } } - /** - * Flies to the cartesian coordinates. - * @public - * @param {Vec3} cartesian - Finish cartesian coordinates. - * @param {Vec3} [look] - Camera LOOK in the end of flying. Default - (0,0,0) - * @param {Vec3} [up] - Camera UP vector in the end of flying. Default - (0,1,0) - * @param {Number} [ampl=1.0] - Altitude amplitude factor. - * @param {Number} [duration] - Animation duration - * @param {EasingFunction} [ease] - Animation easing - * @param {Function} [completeCallback] - Callback that calls after flying when flying is finished. - * @param {Function} [startCallback] - Callback that calls before the flying begins. - * @param {Function} [frameCallback] - Each frame callback - */ - flyCartesian( - cartesian: Vec3, - look: Vec3 | LonLat | null = Vec3.ZERO, - up: Vec3 | null = Vec3.NORTH, - ampl: number = 1.0, - duration: number = DEFAULT_FLIGHT_DURATION, - ease: EasingFunction = DEFAULT_EASING, - completeCallback: Function | null = () => { - }, - startCallback: Function | null = () => { - }, - frameCallback: Function | null = () => { - } - ) { - + override flyCartesian(cartesian: Vec3, params: IPlanetFlyCartesianParams = {}): void { this.stopFlying(); + this.planet.layerLock.lock(this._keyLock); + this.planet.terrainLock.lock(this._keyLock); + this.planet.normalMapCreator.lock(this._keyLock); + params.amplitude = params.amplitude || 1.0; + params.look = params.look || Vec3.ZERO; + params.up = params.up || Vec3.NORTH; + params.duration = params.duration || DEFAULT_FLIGHT_DURATION; + const ease = params.ease || DEFAULT_EASING; - look = look || Vec3.ZERO; - up = up || Vec3.NORTH; + this._completeCallback = params.completeCallback || (() => { + }); - this._completeCallback = completeCallback; + this._frameCallback = params.frameCallback || (() => { + }); - this._frameCallback = frameCallback; - - if (startCallback) { - startCallback.call(this); + if (params.startCallback) { + params.startCallback.call(this); } - if (look instanceof LonLat) { - look = this.planet.ellipsoid.lonLatToCartesian(look); + if (params.look instanceof LonLat) { + params.look = this.planet.ellipsoid.lonLatToCartesian(params.look); } let ground_a = this.eye.clone(); @@ -507,11 +422,11 @@ class PlanetCamera extends Camera { n_a = this._b; let lonlat_b = this.planet.ellipsoid.cartesianToLonLat(cartesian); - let up_b = up; + let up_b = params.up; let ground_b = this.planet.ellipsoid.lonLatToCartesian( new LonLat(lonlat_b.lon, lonlat_b.lat, 0) ); - let n_b = Vec3.sub(cartesian, look as Vec3); + let n_b = Vec3.sub(cartesian, params.look as Vec3); let u_b = up_b.cross(n_b); n_b.normalize(); u_b.normalize(); @@ -520,7 +435,7 @@ class PlanetCamera extends Camera { let an = ground_a.getNormal(); let bn = ground_b.getNormal(); let anbn = 1.0 - an.dot(bn); - let hM_a = ampl * math.SQRT_HALF * Math.sqrt(anbn > 0.0 ? anbn : 0.0); + let hM_a = params.amplitude * math.SQRT_HALF * Math.sqrt(anbn > 0.0 ? anbn : 0.0); let maxHeight = 6639613; let currMaxHeight = Math.max(this._lonLat.height, lonlat_b.height); @@ -561,76 +476,21 @@ class PlanetCamera extends Camera { v: v }; }, - duration, + duration: params.duration, startedAt: Date.now() } this._flying = true; this.events.dispatch(this.events.flystart, this); } - /** - * Flies to the geo coordinates. - * @public - * @param {LonLat} lonlat - Finish coordinates. - * @param {Vec3 | LonLat} [look] - Camera LOOK in the end of flying. Default - (0,0,0) - * @param {Vec3} [up] - Camera UP vector in the end of flying. Default - (0,1,0) - * @param {number} [ampl] - Altitude amplitude factor. - * @param {Number} [duration] - Animation duration - * @param {EasingFunction} [ease] - Animation easing - * @param {Function} [completeCallback] - Callback that calls after flying when flying is finished. - * @param {Function} [startCallback] - Callback that calls befor the flying begins. - * @param {Function} [frameCallback] - each frame callback - */ - flyLonLat( - lonlat: LonLat, - look?: Vec3 | LonLat, - up?: Vec3, - ampl?: number, - duration: number = DEFAULT_FLIGHT_DURATION, - ease: EasingFunction = DEFAULT_EASING, - completeCallback?: Function, - startCallback?: Function, - frameCallback?: Function - ) { - let _lonLat = new LonLat(lonlat.lon, lonlat.lat, lonlat.height || this._lonLat.height); - this.flyCartesian( - this.planet.ellipsoid.lonLatToCartesian(_lonLat), - look, - up, - ampl, - duration, - ease, - completeCallback, - startCallback, - frameCallback - ); - } - - /** - * Breaks the flight. - * @public - */ - stopFlying() { + override stopFlying(): void { if (!this._flying) { return; } this.planet.layerLock.free(this._keyLock); this.planet.terrainLock.free(this._keyLock); this.planet.normalMapCreator.free(this._keyLock); - - this._flying = false; - this._flight = null; - this._frameCallback = null; - this.events.dispatch(this.events.flystop, this); - } - - /** - * Returns camera is flying. - * @public - * @returns {boolean} - */ - isFlying() { - return this._flying; + super.stopFlying(); } /** @@ -714,42 +574,6 @@ class PlanetCamera extends Camera { } } - /** - * Prepare camera to the frame. Used in render node frame function. - * @public - */ - public checkFly() { - if (this._flying && this._flight !== null) { - let progress = Math.min((Date.now() - this._flight.startedAt) / this._flight.duration, 1); - - this.planet.layerLock.lock(this._keyLock); - this.planet.terrainLock.lock(this._keyLock); - this.planet.normalMapCreator.lock(this._keyLock); - - const frame = this._flight.fly(progress); - this.eye = frame.eye; - this._r = frame.u; - this._u = frame.v; - this._b = frame.n; - this._f.set(-this._b.x, -this._b.y, -this._b.z); - - if (this._frameCallback) { - this._frameCallback(); - } - - this.update(); - - if (progress >= 1) { - this.stopFlying(); - if (this._completeCallback) { - this.events.dispatch(this.events.flyend, this); - this._completeCallback(); - this._completeCallback = null; - } - } - } - } - public checkTerrainCollision() { this._terrainAltitude = this._lonLat.height; if (this._insideSegment && this._insideSegment.planet) { diff --git a/src/control/CompassButton.ts b/src/control/CompassButton.ts index 2ef24962..398ced3f 100644 --- a/src/control/CompassButton.ts +++ b/src/control/CompassButton.ts @@ -116,15 +116,11 @@ export class CompassButton extends Control { if (c) { planet.flyCartesian( c.normal().scaleTo(c.length() + c.distance(planet.camera.eye)), - null, - null, - 0, - undefined, - undefined, - null, - null, - () => { - planet.camera.look(c!); + { + amplitude: 0, + completeCallback: () => { + planet.camera.look(c!); + } } ); } else { diff --git a/src/control/elevationProfile/ElevationProfileScene.ts b/src/control/elevationProfile/ElevationProfileScene.ts index f30e4598..d861932f 100644 --- a/src/control/elevationProfile/ElevationProfileScene.ts +++ b/src/control/elevationProfile/ElevationProfileScene.ts @@ -198,7 +198,9 @@ class ElevationProfileScene extends RenderNode { if (ll.height > maxHeight) maxHeight = ll.height; } - this._planet!.camera.flyExtent(new Extent(new LonLat(minLon, minLat), new LonLat(maxLon, maxLat)), maxHeight, null, 0); + this._planet!.camera.flyExtent(new Extent(new LonLat(minLon, minLat), new LonLat(maxLon, maxLat)), maxHeight, { + amplitude: 0 + }); } } diff --git a/src/scene/Planet.ts b/src/scene/Planet.ts index b11d47ba..2e891a45 100644 --- a/src/scene/Planet.ts +++ b/src/scene/Planet.ts @@ -25,7 +25,7 @@ import {LonLat} from "../LonLat"; import {Node} from "../quadTree/Node"; import {NormalMapCreator} from "../utils/NormalMapCreator"; import {PlainSegmentWorker} from "../utils/PlainSegmentWorker"; -import {DEFAULT_EASING, DEFAULT_FLIGHT_DURATION, PlanetCamera} from "../camera/PlanetCamera"; +import {IPlanetFlyCartesianParams, PlanetCamera} from "../camera/PlanetCamera"; import {Quat} from "../math/Quat"; import {QuadTreeStrategy} from "../quadTree/QuadTreeStrategy"; import {Ray} from "../math/Ray"; @@ -42,7 +42,8 @@ import type {WebGLBufferExt, WebGLTextureExt, IDefaultTextureParams} from "../we import {Program} from "../webgl/Program"; import {Segment} from "../segment/Segment"; import type {AtmosphereParameters} from "../shaders/atmos/atmos"; -import { Easing, EasingFunction } from "../utils/easing"; +import { EasingFunction } from "../utils/easing"; +import { DEFAULT_EASING, DEFAULT_FLIGHT_DURATION } from "../camera/Camera"; export interface IPlanetParams { name?: string; @@ -2126,80 +2127,37 @@ export class Planet extends RenderNode { * @public * @param {Extent} extent - Geographical extent. * @param {Number} [height] - Height on the end of the flight route. - * @param {Vec3} [up] - Camera UP vector on the end of a flying. - * @param {Number} [ampl] - Altitude amplitude factor. - * @param {Number} [duration] - Animation duration - * @param {EasingFunction} [ease] - Animation easing - * @param {Function} [startCallback] - Callback that calls before the flying begins. - * @param {Function} [completeCallback] - Callback that calls after flying when flying is finished. - * @param {Function} [frameCallback] - Each frame callback + * @param {IPlanetFlyCartesianParams} params - Flight parameters. */ public flyExtent( extent: Extent, height?: number, - up?: Vec3, - ampl?: number, - duration: number = DEFAULT_FLIGHT_DURATION, - ease: EasingFunction = DEFAULT_EASING, - completeCallback?: Function, - startCallback?: Function, - frameCallback?: Function + params: IPlanetFlyCartesianParams = {} ) { - this.camera.flyExtent(extent, height, up, ampl, duration, ease, completeCallback, startCallback, frameCallback); + this.camera.flyExtent(extent, height, params); } /** * Fly camera to the point. * @public - * @param {Vec3} cartesian - Point coordinates. - * @param {Vec3} [look] - Camera "look at" point. - * @param {Vec3} [up] - Camera UP vector on the end of a flying. - * @param {Number} [ampl] - Altitude amplitude factor. - * @param {Number} [duration] - Animation duration - * @param {EasingFunction} [ease] - Animation easing - * @param {Function} [completeCallback] - Call the function in the end of flight - * @param {Function} [startCallback] - Call the function in the beginning - * @param {Function} [frameCallback] - Each frame callback + * @param {Vec3} cartesian - Fly cartesian coordinates. + * @param {IPlanetFlyCartesianParams} params - Flight parameters. */ - public flyCartesian( - cartesian: Vec3, - look?: Vec3 | null, - up?: Vec3 | null, - ampl?: number, - duration: number = DEFAULT_FLIGHT_DURATION, - ease: EasingFunction = DEFAULT_EASING, - completeCallback?: Function | null, - startCallback?: Function | null, - frameCallback?: Function | null, - ) { - this.camera.flyCartesian(cartesian, look, up, ampl, duration, ease, completeCallback, startCallback, frameCallback); + public flyCartesian(cartesian: Vec3, params?: IPlanetFlyCartesianParams) { + this.camera.flyCartesian(cartesian, params); } /** * Fly camera to the geodetic position. * @public * @param {LonLat} lonlat - Fly geographical coordinates. - * @param {Vec3 | LonLat} [look] - Camera viewpoint in the end of the flight. - * @param {Vec3} [up] - Camera UP vector on the end of a flying. - * @param {Number} [ampl] - Altitude amplitude factor. - * @param {Number} [duration] - Animation duration - * @param {EasingFunction} [ease] - Animation easing - * @param {Function} [completeCallback] - Call the function in the end of flight - * @param {Function} [startCallback] - Call the function in the beginning - * @param {Function} [frameCallback] - Each frame callback + * @param {IPlanetFlyCartesianParams} params - Flight parameters. */ public flyLonLat( lonlat: LonLat, - look?: Vec3 | LonLat, - up?: Vec3, - ampl?: number, - duration: number = DEFAULT_FLIGHT_DURATION, - ease: EasingFunction = DEFAULT_EASING, - completeCallback?: Function, - startCallback?: Function, - frameCallback?: Function + params: IPlanetFlyCartesianParams = {} ) { - this.camera.flyLonLat(lonlat, look, up, ampl, duration, ease, completeCallback, startCallback, frameCallback); + this.camera.flyLonLat(lonlat, params); } /** From bfede35fcce41c28f12b580004fede0a70a1d3d6 Mon Sep 17 00:00:00 2001 From: Michael Gevlich Date: Thu, 24 Jul 2025 15:59:17 +0400 Subject: [PATCH 2/5] v0.26.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9fca65f5..00e7fa48 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@openglobus/og", - "version": "0.26.3", + "version": "0.26.4", "description": "[openglobus](https://www.openglobus.org/) is a javascript/typescript library designed to display interactive 3d maps and planets with map tiles, imagery and vector data, markers, and 3D objects. It uses the WebGL technology, open source, and completely free.", "main": "lib/og.es.js", "types": "lib/index.d.ts", From 2cb6a7990e5f75f524f9f04cf200180418534b65 Mon Sep 17 00:00:00 2001 From: Aigars Zeiza Date: Fri, 25 Jul 2025 16:17:16 +0300 Subject: [PATCH 3/5] camera fly traectory without planet --- sandbox/modelLoad/dracoLoader.js | 18 ++- src/camera/Camera.ts | 195 +++++++++++++++++++++++-------- src/renderer/Renderer.ts | 2 + src/scene/Planet.ts | 1 - 4 files changed, 166 insertions(+), 50 deletions(-) diff --git a/sandbox/modelLoad/dracoLoader.js b/sandbox/modelLoad/dracoLoader.js index d629327d..f2fe01d1 100644 --- a/sandbox/modelLoad/dracoLoader.js +++ b/sandbox/modelLoad/dracoLoader.js @@ -29,6 +29,7 @@ import { EntityCollection, scene, Gltf, + Easing } from "../../lib/og.es.js"; let renderer = new Renderer("frame", { @@ -50,7 +51,22 @@ class MyScene extends RenderNode { collection.addTo(this); - this.renderer.activeCamera.set(new Vec3(20, 21, 23), new Vec3(10, 0, 0)); + const cameraPositions = [ + [new Vec3(20, 21, 23), new Vec3(0, 2, 2), Vec3.UP, Easing.ElasticOut], + [new Vec3(40, 10, 15), new Vec3(10, 0, 0), Vec3.LEFT, Easing.ElasticOut], + [new Vec3(10, 30, 45), new Vec3(10, 0, 0), Vec3.DOWN, Easing.CubicInOut], + [new Vec3(40, 10, 15), new Vec3(10, 10, 0), Vec3.RIGHT, Easing.BackInOut], + ]; + let i = 0; + setInterval(() => { + this.renderer.activeCamera.flyCartesian(cameraPositions[i][0], { + look: cameraPositions[i][1], + up: cameraPositions[i][2], + ease: cameraPositions[i][3], + }); + i = (i + 1) % cameraPositions.length; + }, 1500); + console.log(this.renderer.activeCamera); DracoDecoderModule().then((decoderModule) => { Gltf.connectDracoDecoderModule(decoderModule); diff --git a/src/camera/Camera.ts b/src/camera/Camera.ts index 15572a15..096ccd5d 100644 --- a/src/camera/Camera.ts +++ b/src/camera/Camera.ts @@ -1,28 +1,23 @@ import * as math from "../math"; -import {type EventsHandler, createEvents} from "../Events"; -import {Frustum} from "./Frustum"; -import {Mat3} from "../math/Mat3"; -import type {NumberArray9} from "../math/Mat3"; -import {Mat4} from "../math/Mat4"; -import type {NumberArray16} from "../math/Mat4"; -import {Renderer} from "../renderer/Renderer"; -import {Vec2} from "../math/Vec2"; -import type {NumberArray2} from "../math/Vec2"; -import {Vec3} from "../math/Vec3"; -import {Vec4} from "../math/Vec4"; -import {Sphere} from "../bv/Sphere"; -import {Quat} from "../math/Quat"; -import {DEGREES_DOUBLE, RADIANS, RADIANS_HALF} from "../math"; +import { type EventsHandler, createEvents } from "../Events"; +import { Frustum } from "./Frustum"; +import { Mat3 } from "../math/Mat3"; +import type { NumberArray9 } from "../math/Mat3"; +import { Mat4 } from "../math/Mat4"; +import type { NumberArray16 } from "../math/Mat4"; +import { Renderer } from "../renderer/Renderer"; +import { Vec2 } from "../math/Vec2"; +import type { NumberArray2 } from "../math/Vec2"; +import { Vec3 } from "../math/Vec3"; +import { Vec4 } from "../math/Vec4"; +import { Sphere } from "../bv/Sphere"; +import { Quat } from "../math/Quat"; +import { DEGREES_DOUBLE, RADIANS, RADIANS_HALF } from "../math"; import { Easing, EasingFunction } from "../utils/easing"; import { LonLat } from "../LonLat"; +import { Ray } from "../math/Ray"; -export type CameraEvents = [ - "viewchange", - "moveend", - "flystart", - "flyend", - "flystop", -]; +export type CameraEvents = ["viewchange", "moveend", "flystart", "flyend", "flystop"]; const EVENT_NAMES: CameraEvents = [ /** @@ -53,7 +48,7 @@ const EVENT_NAMES: CameraEvents = [ * Triggered when flight was stopped. * @event og.Camera#flystop */ - "flystop", + "flystop" ]; export interface ICameraParams { @@ -61,7 +56,7 @@ export interface ICameraParams { viewAngle?: number; look?: Vec3; up?: Vec3; - frustums?: NumberArray2[] + frustums?: NumberArray2[]; width?: number; height?: number; } @@ -87,15 +82,16 @@ type CameraFrame = { n: Vec3; u: Vec3; v: Vec3; -} +}; type CameraFlight = { fly: (progress: number) => CameraFrame; duration: number; startedAt: number; -} +}; -const getHorizontalViewAngleByFov = (fov: number, aspect: number) => DEGREES_DOUBLE * Math.atan(Math.tan(RADIANS_HALF * fov) * aspect); +const getHorizontalViewAngleByFov = (fov: number, aspect: number) => + DEGREES_DOUBLE * Math.atan(Math.tan(RADIANS_HALF * fov) * aspect); /** * Camera class. @@ -114,7 +110,6 @@ const getHorizontalViewAngleByFov = (fov: number, aspect: number) => DEGREES_DOU * @fires EventsHandler#moveend */ class Camera { - static __counter__: number = 0; protected __id: number; @@ -238,7 +233,6 @@ class Camera { // public dirRightNED: Vec3; constructor(options: ICameraParams = {}) { - this.__id = Camera.__counter__++; this.events = createEvents(EVENT_NAMES, this); @@ -292,7 +286,7 @@ class Camera { let fr = new Frustum({ fov: this._viewAngle, - aspect: this.getAspectRatio(),//this._aspect, + aspect: this.getAspectRatio(), //this._aspect, near: fi[0], far: fi[1] }); @@ -300,7 +294,11 @@ class Camera { fr.cameraFrustumIndex = this.frustums.length; this.frustums.push(fr); //this.frustumColors.push.apply(this.frustumColors, fr._pickingColorU); - this.frustumColors.push(fr._pickingColorU[0], fr._pickingColorU[1], fr._pickingColorU[2]); + this.frustumColors.push( + fr._pickingColorU[0], + fr._pickingColorU[1], + fr._pickingColorU[2] + ); } } else { let near = 0.1, @@ -315,7 +313,11 @@ class Camera { fr.cameraFrustumIndex = this.frustums.length; this.frustums.push(fr); - this.frustumColors.push(fr._pickingColorU[0], fr._pickingColorU[1], fr._pickingColorU[2]); + this.frustumColors.push( + fr._pickingColorU[0], + fr._pickingColorU[1], + fr._pickingColorU[2] + ); } this.FARTHEST_FRUSTUM_INDEX = this.frustums.length - 1; @@ -344,7 +346,64 @@ class Camera { * @param {Vec3} [cartesian] - Finish cartesian coordinates. * @param {IFlyCartesianParams} [params] - Flight parameters */ - flyCartesian(cartesian: Vec3, params?: IFlyCartesianParams): void { + flyCartesian(cartesian: Vec3, params: IFlyCartesianParams = {}): void { + this.stopFlying(); + params.look = params.look || Vec3.ZERO; + params.up = params.up || Vec3.UP; + params.duration = params.duration || DEFAULT_FLIGHT_DURATION; + const ease = params.ease || DEFAULT_EASING; + + this._completeCallback = params.completeCallback || (() => { + }); + + this._frameCallback = params.frameCallback || (() => { + }); + + if (params.startCallback) { + params.startCallback.call(this); + } + + let ground_a = this.eye.clone(); + + let v_a = this._u, + n_a = this._b; + + let up_b = params.up; + let ground_b = cartesian.clone(); + let n_b = Vec3.sub(cartesian, params.look as Vec3); + let u_b = up_b.cross(n_b); + n_b.normalize(); + u_b.normalize(); + let v_b = n_b.cross(u_b); + + this._flight = { + fly: (progress: number) => { + let t = ease(progress); + let d = 1 - t; + // camera path and orientations calculation + let g_i = ground_a.smerp(ground_b, d); + let eye_i = g_i; + let up_i = v_a.smerp(v_b, d); + let look_i = Vec3.add(eye_i, n_a.smerp(n_b, d).negateTo()); + + let n = new Vec3(eye_i.x - look_i.x, eye_i.y - look_i.y, eye_i.z - look_i.z); + let u = up_i.cross(n); + n.normalize(); + u.normalize(); + + let v = n.cross(u); + return { + eye: eye_i, + n: n, + u: u, + v: v + }; + }, + duration: params.duration, + startedAt: Date.now() + } + this._flying = true; + this.events.dispatch(this.events.flystart, this); } /** @@ -367,7 +426,10 @@ class Camera { */ public checkFly() { if (this._flying && this._flight !== null) { - let progress = Math.min((Date.now() - this._flight.startedAt) / this._flight.duration, 1); + let progress = Math.min( + (Date.now() - this._flight.startedAt) / this._flight.duration, + 1 + ); const frame = this._flight.fly(progress); this.eye = frame.eye; @@ -441,7 +503,6 @@ class Camera { * @param {Vec3} [options.up] - Camera eye position. Default (0,1,0) */ protected _init(options: ICameraParams) { - this._setProj(this._viewAngle, this.getAspectRatio()); this.set( @@ -489,17 +550,41 @@ class Camera { Vec3.doubleToTwoFloat32Array(eye, this.eyeHigh, this.eyeLow); this._viewMatrix.set([ - u.x, v.x, n.x, 0.0, - u.y, v.y, n.y, 0.0, - u.z, v.z, n.z, 0.0, - -eye.dot(u), -eye.dot(v), -eye.dot(n), 1.0 + u.x, + v.x, + n.x, + 0.0, + u.y, + v.y, + n.y, + 0.0, + u.z, + v.z, + n.z, + 0.0, + -eye.dot(u), + -eye.dot(v), + -eye.dot(n), + 1.0 ]); this._viewMatrixRTE.set([ - u.x, v.x, n.x, 0.0, - u.y, v.y, n.y, 0.0, - u.z, v.z, n.z, 0.0, - 0, 0, 0, 1.0 + u.x, + v.x, + n.x, + 0.0, + u.y, + v.y, + n.y, + 0.0, + u.z, + v.z, + n.z, + 0.0, + 0, + 0, + 0, + 1.0 ]); // do not clean up, someday it will be using @@ -570,7 +655,12 @@ class Camera { protected _updateViewportParameters() { this._tanViewAngle_hrad = Math.tan(this._viewAngle * math.RADIANS_HALF); this._tanViewAngle_hradOneByHeight = this._tanViewAngle_hrad * (1.0 / this._height); - this._projSizeConst = Math.min(this._width < 512 ? 512 : this._width, this._height < 512 ? 512 : this._height) / (this._viewAngle * RADIANS); + this._projSizeConst = + Math.min( + this._width < 512 ? 512 : this._width, + this._height < 512 ? 512 : this._height + ) / + (this._viewAngle * RADIANS); } /** @@ -795,8 +885,12 @@ class Camera { let px = (x - w) / w, py = -(y - h) / h; - let world1 = this.frustums[0].inverseProjectionViewMatrix.mulVec4(new Vec4(px, py, -1.0, 1.0)).affinity(), - world2 = this.frustums[0].inverseProjectionViewMatrix.mulVec4(new Vec4(px, py, 0.0, 1.0)).affinity(); + let world1 = this.frustums[0].inverseProjectionViewMatrix + .mulVec4(new Vec4(px, py, -1.0, 1.0)) + .affinity(), + world2 = this.frustums[0].inverseProjectionViewMatrix + .mulVec4(new Vec4(px, py, 0.0, 1.0)) + .affinity(); return world2.subA(world1).toVec3().normalize(); } @@ -833,7 +927,12 @@ class Camera { * @param {Vec3} [center] - Point that the camera rotates around * @param {Vec3} [up] - Camera up vector */ - public rotateAround(angle: number, isArc: boolean = false, center: Vec3 = Vec3.ZERO, up: Vec3 = Vec3.UP) { + public rotateAround( + angle: number, + isArc: boolean = false, + center: Vec3 = Vec3.ZERO, + up: Vec3 = Vec3.UP + ) { up = isArc ? this._u : up; let rot = Mat4.getRotation(angle, up); let trm = Mat4.getRotationAroundPoint(angle, center, up); @@ -897,7 +996,7 @@ class Camera { public setCurrentFrustum(k: number) { this.currentFrustumIndex = k; - this.frustumColorIndex = (k + 1) * 10.0 / 255.0; + this.frustumColorIndex = ((k + 1) * 10.0) / 255.0; this.isFirstPass = k === this.FARTHEST_FRUSTUM_INDEX; } @@ -986,4 +1085,4 @@ class Camera { } } -export {Camera}; +export { Camera }; diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 962f4b42..d5cae4f3 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -1008,6 +1008,8 @@ class Renderer { e.dispatch(e.draw, this); + this.activeCamera!.checkFly(); + let frustums = this.activeCamera!.frustums; let pointerEvent = e.pointerEvent(); diff --git a/src/scene/Planet.ts b/src/scene/Planet.ts index 2e891a45..b4ecd869 100644 --- a/src/scene/Planet.ts +++ b/src/scene/Planet.ts @@ -1379,7 +1379,6 @@ export class Planet extends RenderNode { this._distBeforeMemClear += this._prevCamEye.distance(cam.eye); this._prevCamEye.copy(cam.eye); - cam.checkFly(); // free memory if (this._createdNodesCount > this._maxNodes && this._distBeforeMemClear > this._minDistanceBeforeMemClear) { From 87824a3377cd75d552324062cb4666445625f8cf Mon Sep 17 00:00:00 2001 From: Aigars Zeiza Date: Fri, 25 Jul 2025 16:39:40 +0300 Subject: [PATCH 4/5] formatting --- src/camera/Camera.ts | 112 +++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/src/camera/Camera.ts b/src/camera/Camera.ts index 096ccd5d..17cb552a 100644 --- a/src/camera/Camera.ts +++ b/src/camera/Camera.ts @@ -348,62 +348,62 @@ class Camera { */ flyCartesian(cartesian: Vec3, params: IFlyCartesianParams = {}): void { this.stopFlying(); - params.look = params.look || Vec3.ZERO; - params.up = params.up || Vec3.UP; - params.duration = params.duration || DEFAULT_FLIGHT_DURATION; - const ease = params.ease || DEFAULT_EASING; - - this._completeCallback = params.completeCallback || (() => { - }); - - this._frameCallback = params.frameCallback || (() => { - }); - - if (params.startCallback) { - params.startCallback.call(this); - } - - let ground_a = this.eye.clone(); - - let v_a = this._u, - n_a = this._b; - - let up_b = params.up; - let ground_b = cartesian.clone(); - let n_b = Vec3.sub(cartesian, params.look as Vec3); - let u_b = up_b.cross(n_b); - n_b.normalize(); - u_b.normalize(); - let v_b = n_b.cross(u_b); - - this._flight = { - fly: (progress: number) => { - let t = ease(progress); - let d = 1 - t; - // camera path and orientations calculation - let g_i = ground_a.smerp(ground_b, d); - let eye_i = g_i; - let up_i = v_a.smerp(v_b, d); - let look_i = Vec3.add(eye_i, n_a.smerp(n_b, d).negateTo()); - - let n = new Vec3(eye_i.x - look_i.x, eye_i.y - look_i.y, eye_i.z - look_i.z); - let u = up_i.cross(n); - n.normalize(); - u.normalize(); - - let v = n.cross(u); - return { - eye: eye_i, - n: n, - u: u, - v: v - }; - }, - duration: params.duration, - startedAt: Date.now() - } - this._flying = true; - this.events.dispatch(this.events.flystart, this); + params.look = params.look || Vec3.ZERO; + params.up = params.up || Vec3.UP; + params.duration = params.duration || DEFAULT_FLIGHT_DURATION; + const ease = params.ease || DEFAULT_EASING; + + this._completeCallback = params.completeCallback || (() => { + }); + + this._frameCallback = params.frameCallback || (() => { + }); + + if (params.startCallback) { + params.startCallback.call(this); + } + + let ground_a = this.eye.clone(); + + let v_a = this._u, + n_a = this._b; + + let up_b = params.up; + let ground_b = cartesian.clone(); + let n_b = Vec3.sub(cartesian, params.look as Vec3); + let u_b = up_b.cross(n_b); + n_b.normalize(); + u_b.normalize(); + let v_b = n_b.cross(u_b); + + this._flight = { + fly: (progress: number) => { + let t = ease(progress); + let d = 1 - t; + // camera path and orientations calculation + let g_i = ground_a.smerp(ground_b, d); + let eye_i = g_i; + let up_i = v_a.smerp(v_b, d); + let look_i = Vec3.add(eye_i, n_a.smerp(n_b, d).negateTo()); + + let n = new Vec3(eye_i.x - look_i.x, eye_i.y - look_i.y, eye_i.z - look_i.z); + let u = up_i.cross(n); + n.normalize(); + u.normalize(); + + let v = n.cross(u); + return { + eye: eye_i, + n: n, + u: u, + v: v + }; + }, + duration: params.duration, + startedAt: Date.now() + } + this._flying = true; + this.events.dispatch(this.events.flystart, this); } /** From 914e20e458fca6f50d6ef0286e33675985eef863 Mon Sep 17 00:00:00 2001 From: Michael Gevlich Date: Sat, 26 Jul 2025 14:28:46 +0400 Subject: [PATCH 5/5] v0.26.5 Camera fly refactoring --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 00e7fa48..5edcd2fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@openglobus/og", - "version": "0.26.4", + "version": "0.26.5", "description": "[openglobus](https://www.openglobus.org/) is a javascript/typescript library designed to display interactive 3d maps and planets with map tiles, imagery and vector data, markers, and 3D objects. It uses the WebGL technology, open source, and completely free.", "main": "lib/og.es.js", "types": "lib/index.d.ts",