@navaramap/three-api 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -0
- package/README.md +3 -0
- package/dist/ellipsoidGeodesic.d.ts +54 -0
- package/dist/index.d.ts +106 -0
- package/dist/index.js +180 -0
- package/dist/index.umd.cjs +1 -0
- package/dist/intersection.d.ts +30 -0
- package/dist/rte.d.ts +60 -0
- package/package.json +43 -0
- package/src/ellipsoidGeodesic.ts +113 -0
- package/src/index.ts +303 -0
- package/src/intersection.ts +163 -0
- package/src/rte.ts +114 -0
- package/src/vite-env.d.ts +1 -0
package/LICENSE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { LatLngHeight } from '@navaramap/core';
|
|
2
|
+
/**
|
|
3
|
+
* Wrapper class for geodesic calculations on an ellipsoid surface.
|
|
4
|
+
* Precomputes common variables for optimization when instantiated.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* const geodesic = new EllipsoidGeodesic(
|
|
9
|
+
* { lat: 0.5, lng: 2.0, height: 0 },
|
|
10
|
+
* { lat: 0.6, lng: 2.1, height: 0 }
|
|
11
|
+
* );
|
|
12
|
+
*
|
|
13
|
+
* console.log(geodesic.distance);
|
|
14
|
+
* const points = geodesic.interpolatePoints(1000);
|
|
15
|
+
* const midpoint = geodesic.interpolateDistance(geodesic.distance / 2);
|
|
16
|
+
*
|
|
17
|
+
* geodesic.dispose(); // Free WASM memory when done
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare class EllipsoidGeodesic {
|
|
21
|
+
private _raw;
|
|
22
|
+
/**
|
|
23
|
+
* Create a new geodesic between two points on the ellipsoid.
|
|
24
|
+
* @param start - Start point in geodetic coordinates (lat/lng in radians)
|
|
25
|
+
* @param end - End point in geodetic coordinates (lat/lng in radians)
|
|
26
|
+
*/
|
|
27
|
+
constructor(start: LatLngHeight, end: LatLngHeight);
|
|
28
|
+
/** Distance in meters between start and end points */
|
|
29
|
+
get distance(): number;
|
|
30
|
+
/** Heading at the start point in radians */
|
|
31
|
+
get startHeading(): number;
|
|
32
|
+
/** Heading at the end point in radians */
|
|
33
|
+
get endHeading(): number;
|
|
34
|
+
/** Start point in geodetic coordinates */
|
|
35
|
+
get start(): LatLngHeight;
|
|
36
|
+
/** End point in geodetic coordinates */
|
|
37
|
+
get end(): LatLngHeight;
|
|
38
|
+
/**
|
|
39
|
+
* Interpolate points along the geodesic path.
|
|
40
|
+
* @param granularity - Distance between interpolated points in meters (optional)
|
|
41
|
+
* @returns Array of interpolated points in geodetic coordinates
|
|
42
|
+
*/
|
|
43
|
+
interpolatePoints(granularity?: number): LatLngHeight[];
|
|
44
|
+
/**
|
|
45
|
+
* Interpolate a point at a specific distance along the geodesic path.
|
|
46
|
+
* @param distance - Distance from start point in meters
|
|
47
|
+
* @returns Interpolated point in geodetic coordinates
|
|
48
|
+
*/
|
|
49
|
+
interpolateDistance(distance: number): LatLngHeight;
|
|
50
|
+
/**
|
|
51
|
+
* Free WASM memory. Call this when the geodesic is no longer needed.
|
|
52
|
+
*/
|
|
53
|
+
dispose(): void;
|
|
54
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { LatLngHeight, Window as WindowObject } from '@navaramap/core';
|
|
2
|
+
import { Vector3, Vector2, Matrix4, PerspectiveCamera } from 'three';
|
|
3
|
+
export type { LatLngHeight, LatLng } from '@navaramap/core';
|
|
4
|
+
export type { Window, Transform, CameraFrustum, Plane, Ray, Vec2, Vec3, } from '@navaramap/core';
|
|
5
|
+
export * from './intersection';
|
|
6
|
+
export * from './rte';
|
|
7
|
+
export * from './ellipsoidGeodesic';
|
|
8
|
+
/**
|
|
9
|
+
* Initializes the Navara API WASM module. Must be called before using other API functions.
|
|
10
|
+
*/
|
|
11
|
+
export declare function initNavaraApi(): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Converts geodetic coordinates (longitude, latitude, height) to a Cartesian Vector3 in ECEF coordinates.
|
|
14
|
+
* @param lle - Geodetic coordinates (lng in radians, lat in radians, height in meters)
|
|
15
|
+
* @returns Cartesian Vector3 in Earth-Centered Earth-Fixed (ECEF) coordinates
|
|
16
|
+
*/
|
|
17
|
+
export declare function geodeticToVector3(lle: LatLngHeight): Vector3;
|
|
18
|
+
/**
|
|
19
|
+
* Converts a Cartesian Vector3 in ECEF coordinates to geodetic coordinates.
|
|
20
|
+
* @param xyz - Cartesian Vector3 in Earth-Centered Earth-Fixed (ECEF) coordinates
|
|
21
|
+
* @returns Geodetic coordinates (lng in radians, lat in radians, height in meters)
|
|
22
|
+
*/
|
|
23
|
+
export declare function vector3ToGeodetic(xyz: Vector3): LatLngHeight;
|
|
24
|
+
/**
|
|
25
|
+
* Converts an angle from degrees to radians.
|
|
26
|
+
* @param degree - Angle in degrees
|
|
27
|
+
* @returns Angle in radians
|
|
28
|
+
*/
|
|
29
|
+
export declare function degreeToRadian(degree: number): number;
|
|
30
|
+
/**
|
|
31
|
+
* Converts an angle from radians to degrees.
|
|
32
|
+
* @param radian - Angle in radians
|
|
33
|
+
* @returns Angle in degrees
|
|
34
|
+
*/
|
|
35
|
+
export declare function radianToDegree(radian: number): number;
|
|
36
|
+
/**
|
|
37
|
+
* Converts screen coordinates to world coordinates by raycasting against the WGS84 ellipsoid.
|
|
38
|
+
* @param window - Window configuration with width, height, and pixelRatio
|
|
39
|
+
* @param camera - Three.js PerspectiveCamera
|
|
40
|
+
* @param vec2 - Screen coordinates in CSS pixels (same as MouseEvent clientX/clientY)
|
|
41
|
+
* @returns World position Vector3 in ECEF coordinates, or undefined if no intersection with ellipsoid
|
|
42
|
+
*/
|
|
43
|
+
export declare function convertScreenToWorld(windowObject: WindowObject, camera: PerspectiveCamera, vec2: Vector2): Vector3 | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Computes the surface normal vector at a geodetic position on the WGS84 ellipsoid.
|
|
46
|
+
* @param lle - Geodetic coordinates (lng in radians, lat in radians, height in meters)
|
|
47
|
+
* @returns Unit normal vector pointing outward from the ellipsoid surface
|
|
48
|
+
*/
|
|
49
|
+
export declare function geodeticSurfaceNormal(lle: LatLngHeight): Vector3;
|
|
50
|
+
/**
|
|
51
|
+
* Creates a local East-North-Up (ENU) reference frame transformation matrix at a position.
|
|
52
|
+
* @param origin - Origin position in ECEF coordinates
|
|
53
|
+
* @returns 4x4 transformation matrix from ENU to ECEF
|
|
54
|
+
*/
|
|
55
|
+
export declare function eastNorthUpToFixedFrame(origin: Vector3): Matrix4;
|
|
56
|
+
/**
|
|
57
|
+
* Creates a local North-East-Down (NED) reference frame transformation matrix at a position.
|
|
58
|
+
* @param origin - Origin position in ECEF coordinates
|
|
59
|
+
* @returns 4x4 transformation matrix from NED to ECEF
|
|
60
|
+
*/
|
|
61
|
+
export declare function northEastDownToFixedFrame(origin: Vector3): Matrix4;
|
|
62
|
+
/**
|
|
63
|
+
* Creates a local North-Up-East (NUE) reference frame transformation matrix at a position.
|
|
64
|
+
* @param origin - Origin position in ECEF coordinates
|
|
65
|
+
* @returns 4x4 transformation matrix from NUE to ECEF
|
|
66
|
+
*/
|
|
67
|
+
export declare function northUpEastToFixedFrame(origin: Vector3): Matrix4;
|
|
68
|
+
/**
|
|
69
|
+
* Creates a local North-West-Up (NWU) reference frame transformation matrix at a position.
|
|
70
|
+
* @param origin - Origin position in ECEF coordinates
|
|
71
|
+
* @returns 4x4 transformation matrix from NWU to ECEF
|
|
72
|
+
*/
|
|
73
|
+
export declare function northWestUpToFixedFrame(origin: Vector3): Matrix4;
|
|
74
|
+
/**
|
|
75
|
+
* Converts world coordinates to screen coordinates.
|
|
76
|
+
* @param window - Window configuration with width, height, and pixelRatio
|
|
77
|
+
* @param camera - Three.js PerspectiveCamera
|
|
78
|
+
* @param worldPos - World position Vector3 in ECEF coordinates
|
|
79
|
+
* @returns Screen coordinates in CSS pixels, or undefined if behind camera
|
|
80
|
+
*/
|
|
81
|
+
export declare function convertWorldToScreen(windowObject: WindowObject, camera: PerspectiveCamera, worldPos: Vector3): Vector2 | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* Returns the WGS84 ellipsoid semi-major axis (equatorial radius) in meters.
|
|
84
|
+
* @returns Semi-major axis (approximately 6,378,137 meters)
|
|
85
|
+
*/
|
|
86
|
+
export declare function getWGS84SemiMajorAxis(): number;
|
|
87
|
+
/**
|
|
88
|
+
* Returns the WGS84 ellipsoid semi-minor axis (polar radius) in meters.
|
|
89
|
+
* @returns Semi-minor axis (approximately 6,356,752 meters)
|
|
90
|
+
*/
|
|
91
|
+
export declare function getWGS84SemiMinorAxis(): number;
|
|
92
|
+
/**
|
|
93
|
+
* Returns the WGS84 ellipsoid first eccentricity squared.
|
|
94
|
+
* @returns Eccentricity squared value
|
|
95
|
+
*/
|
|
96
|
+
export declare function getWGS84EccentricitySquared(): number;
|
|
97
|
+
/**
|
|
98
|
+
* Returns the WGS84 ellipsoid flattening ratio.
|
|
99
|
+
* @returns Flattening value (approximately 1/298.257)
|
|
100
|
+
*/
|
|
101
|
+
export declare function getWGS84Flattening(): number;
|
|
102
|
+
/**
|
|
103
|
+
* Returns the WGS84 ellipsoid first eccentricity.
|
|
104
|
+
* @returns Eccentricity value
|
|
105
|
+
*/
|
|
106
|
+
export declare function getWGS84Eccentricity(): number;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import e, { CameraFrustum as t, EllipsoidGeodesic as n, LLE as r, Ray as i, Transform as a, Vec2 as o, Vec3 as s, Window as c, angleToDegree as l, angleToRadian as u, eastNorthUpToFixedFrame as ee, encodePosition as d, geodeticSurfaceNormal as f, geodeticToXyz as p, getHeightFromEllipsoid as m, getPickRay as h, getPlaneFromPointNormal as g, getRayPlaneIntersection as _, getWGS84Eccentricity as v, getWGS84EccentricitySquared as y, getWGS84Flattening as b, getWGS84SemiMajorAxis as x, getWGS84SemiMinorAxis as S, northEastDownToFixedFrame as C, northUpEastToFixedFrame as w, northWestUpToFixedFrame as T, screenToWorld as E, worldToScreen as D, xyzToGeodetic as O } from "@navaramap/engine-api";
|
|
2
|
+
import { Matrix4 as k, Plane as A, Ray as j, Vector2 as M, Vector3 as N } from "three";
|
|
3
|
+
//#region src/intersection.ts
|
|
4
|
+
function P(e, t) {
|
|
5
|
+
let n = g(new s(e.x, e.y, e.z), new s(t.x, t.y, t.z)), r = new A(new N(n.normal.x, n.normal.y, n.normal.z), -n.distance);
|
|
6
|
+
return n.free(), r;
|
|
7
|
+
}
|
|
8
|
+
function F(e, n, r) {
|
|
9
|
+
let i = new c(e.width, e.height, e.pixelRatio);
|
|
10
|
+
i.width *= i.pixelRatio, i.height *= i.pixelRatio;
|
|
11
|
+
let s = h(i, new a(n.position.x, n.position.y, n.position.z, n.quaternion.x, n.quaternion.y, n.quaternion.z, n.quaternion.w, n.scale.x, n.scale.y, n.scale.z), new t(n.near, n.far, u(n.fov), n.aspect), new o(r.x, r.y)), l = new j(new N(s.origin.x, s.origin.y, s.origin.z), new N(s.direction.x, s.direction.y, s.direction.z));
|
|
12
|
+
return s.free(), l;
|
|
13
|
+
}
|
|
14
|
+
function I(e, t) {
|
|
15
|
+
let n = _(new i(new s(e.origin.x, e.origin.y, e.origin.z), new s(e.direction.x, e.direction.y, e.direction.z)), g(new s(t.normal.x * -t.constant, t.normal.y * -t.constant, t.normal.z * -t.constant), new s(t.normal.x, t.normal.y, t.normal.z)));
|
|
16
|
+
if (n) {
|
|
17
|
+
let e = new N(n.x, n.y, n.z);
|
|
18
|
+
return n.free(), e;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function te(e) {
|
|
22
|
+
return m(new s(e.x, e.y, e.z));
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/rte.ts
|
|
26
|
+
var L = Object.freeze({ value: 1 });
|
|
27
|
+
function R(e, t = new N(), n = new N()) {
|
|
28
|
+
let r = d(e.x, e.y, e.z);
|
|
29
|
+
return t.set(r.high.x, r.high.y, r.high.z), n.set(r.low.x, r.low.y, r.low.z), r.free(), {
|
|
30
|
+
high: t,
|
|
31
|
+
low: n
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
var z = (e, t, n = new k()) => {
|
|
35
|
+
n.multiplyMatrices(t, e);
|
|
36
|
+
let r = n.elements;
|
|
37
|
+
return r[12] = 0, r[13] = 0, r[14] = 0, n;
|
|
38
|
+
};
|
|
39
|
+
function B(e, t, n = new N(), r = new k()) {
|
|
40
|
+
r.multiplyMatrices(e, t), n.setFromMatrixPosition(r);
|
|
41
|
+
let i = r.elements;
|
|
42
|
+
return i[12] = 0, i[13] = 0, i[14] = 0, {
|
|
43
|
+
position: n,
|
|
44
|
+
rotationScale: r
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
var V = new k(), H = new N(), U = (e, t) => {
|
|
48
|
+
V.copy(t).invert(), H.copy(e).applyMatrix4(V);
|
|
49
|
+
let n = d(H.x, H.y, H.z), r = n.high, i = n.low, a = {
|
|
50
|
+
high: new N(r.x, r.y, r.z),
|
|
51
|
+
low: new N(i.x, i.y, i.z)
|
|
52
|
+
};
|
|
53
|
+
return n.free(), a;
|
|
54
|
+
}, W = class {
|
|
55
|
+
_raw;
|
|
56
|
+
constructor(e, t) {
|
|
57
|
+
this._raw = new n(new r(e.lat, e.lng, e.height), new r(t.lat, t.lng, t.height));
|
|
58
|
+
}
|
|
59
|
+
get distance() {
|
|
60
|
+
return this._raw.distance;
|
|
61
|
+
}
|
|
62
|
+
get startHeading() {
|
|
63
|
+
return this._raw.start_heading;
|
|
64
|
+
}
|
|
65
|
+
get endHeading() {
|
|
66
|
+
return this._raw.end_heading;
|
|
67
|
+
}
|
|
68
|
+
get start() {
|
|
69
|
+
let e = this._raw.start;
|
|
70
|
+
return {
|
|
71
|
+
lat: e.lat,
|
|
72
|
+
lng: e.lng,
|
|
73
|
+
height: e.height
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
get end() {
|
|
77
|
+
let e = this._raw.end;
|
|
78
|
+
return {
|
|
79
|
+
lat: e.lat,
|
|
80
|
+
lng: e.lng,
|
|
81
|
+
height: e.height
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
interpolatePoints(e) {
|
|
85
|
+
let t = this._raw.interpolateGeodeticPoints(e ?? null), n = [];
|
|
86
|
+
for (let e of t) n.push({
|
|
87
|
+
lat: e.lat,
|
|
88
|
+
lng: e.lng,
|
|
89
|
+
height: e.height
|
|
90
|
+
}), e.free();
|
|
91
|
+
return n;
|
|
92
|
+
}
|
|
93
|
+
interpolateDistance(e) {
|
|
94
|
+
let t = this._raw.interpolateDistance(e), n = {
|
|
95
|
+
lat: t.lat,
|
|
96
|
+
lng: t.lng,
|
|
97
|
+
height: t.height
|
|
98
|
+
};
|
|
99
|
+
return t.free(), n;
|
|
100
|
+
}
|
|
101
|
+
dispose() {
|
|
102
|
+
this._raw.free();
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/index.ts
|
|
107
|
+
async function G() {
|
|
108
|
+
await e();
|
|
109
|
+
}
|
|
110
|
+
function K(e) {
|
|
111
|
+
let t = p(new r(e.lat, e.lng, e.height)), n = new N(t.x, t.y, t.z);
|
|
112
|
+
return t.free(), n;
|
|
113
|
+
}
|
|
114
|
+
function q(e) {
|
|
115
|
+
let t = O(new s(e.x, e.y, e.z)), n = {
|
|
116
|
+
lat: t.lat,
|
|
117
|
+
lng: t.lng,
|
|
118
|
+
height: t.height
|
|
119
|
+
};
|
|
120
|
+
return t.free(), n;
|
|
121
|
+
}
|
|
122
|
+
function J(e) {
|
|
123
|
+
return u(e);
|
|
124
|
+
}
|
|
125
|
+
function Y(e) {
|
|
126
|
+
return l(e);
|
|
127
|
+
}
|
|
128
|
+
function X(e, n, r) {
|
|
129
|
+
let i = new c(e.width, e.height, e.pixelRatio);
|
|
130
|
+
i.width *= i.pixelRatio, i.height *= i.pixelRatio;
|
|
131
|
+
let s = E(i, new a(n.position.x, n.position.y, n.position.z, n.quaternion.x, n.quaternion.y, n.quaternion.z, n.quaternion.w, n.scale.x, n.scale.y, n.scale.z), new t(n.near, n.far, u(n.fov), n.aspect), new o(r.x, r.y));
|
|
132
|
+
if (!s) return;
|
|
133
|
+
let l = new N(s.x, s.y, s.z);
|
|
134
|
+
return s.free(), l;
|
|
135
|
+
}
|
|
136
|
+
function Z(e) {
|
|
137
|
+
let t = f(new r(e.lat, e.lng, e.height)), n = new N(t.x, t.y, t.z);
|
|
138
|
+
return t.free(), n;
|
|
139
|
+
}
|
|
140
|
+
function Q(e) {
|
|
141
|
+
let t = ee(new s(e.x, e.y, e.z));
|
|
142
|
+
return new k().fromArray(t);
|
|
143
|
+
}
|
|
144
|
+
function ne(e) {
|
|
145
|
+
let t = C(new s(e.x, e.y, e.z));
|
|
146
|
+
return new k().fromArray(t);
|
|
147
|
+
}
|
|
148
|
+
function re(e) {
|
|
149
|
+
let t = w(new s(e.x, e.y, e.z));
|
|
150
|
+
return new k().fromArray(t);
|
|
151
|
+
}
|
|
152
|
+
function ie(e) {
|
|
153
|
+
let t = T(new s(e.x, e.y, e.z));
|
|
154
|
+
return new k().fromArray(t);
|
|
155
|
+
}
|
|
156
|
+
function ae(e, n, r) {
|
|
157
|
+
let i = new c(e.width, e.height, e.pixelRatio);
|
|
158
|
+
i.width *= i.pixelRatio, i.height *= i.pixelRatio;
|
|
159
|
+
let o = D(i, new a(n.position.x, n.position.y, n.position.z, n.quaternion.x, n.quaternion.y, n.quaternion.z, n.quaternion.w, n.scale.x, n.scale.y, n.scale.z), new t(n.near, n.far, u(n.fov), n.aspect), new s(r.x, r.y, r.z));
|
|
160
|
+
if (!o) return;
|
|
161
|
+
let l = new M(o.x, o.y);
|
|
162
|
+
return o.free(), l;
|
|
163
|
+
}
|
|
164
|
+
function oe() {
|
|
165
|
+
return x();
|
|
166
|
+
}
|
|
167
|
+
function $() {
|
|
168
|
+
return S();
|
|
169
|
+
}
|
|
170
|
+
function se() {
|
|
171
|
+
return y();
|
|
172
|
+
}
|
|
173
|
+
function ce() {
|
|
174
|
+
return b();
|
|
175
|
+
}
|
|
176
|
+
function le() {
|
|
177
|
+
return v();
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
180
|
+
export { W as EllipsoidGeodesic, L as RTE_ONE_UNIFORM, U as calcCameraPosition, z as calcModelMatrixRTE, B as composeWorldMatrixForRTE, X as convertScreenToWorld, ae as convertWorldToScreen, J as degreeToRadian, Q as eastNorthUpToFixedFrame, R as encodePositionRTE, Z as geodeticSurfaceNormal, K as geodeticToVector3, te as getHeightFromEllipsoid, F as getPickRay, P as getPlaneFromPointNormal, I as getRayPlaneIntersection, le as getWGS84Eccentricity, se as getWGS84EccentricitySquared, ce as getWGS84Flattening, oe as getWGS84SemiMajorAxis, $ as getWGS84SemiMinorAxis, G as initNavaraApi, ne as northEastDownToFixedFrame, re as northUpEastToFixedFrame, ie as northWestUpToFixedFrame, Y as radianToDegree, q as vector3ToGeodetic };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("@navaramap/engine-api"),require("three")):typeof define==`function`&&define.amd?define([`exports`,`@navaramap/engine-api`,`three`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.NavaraThreeApi={},e._navaramap_engine_api,e.three))})(this,function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var r=Object.create,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,s=Object.getPrototypeOf,c=Object.prototype.hasOwnProperty,l=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var s=o(t),l=0,u=s.length,d;l<u;l++)d=s[l],!c.call(e,d)&&d!==n&&i(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(r=a(t,d))||r.enumerable});return e};t=((e,t,n)=>(n=e==null?{}:r(s(e)),l(t||!e||!e.__esModule?i(n,`default`,{value:e,enumerable:!0}):n,e)))(t,1);function u(e,r){let i=(0,t.getPlaneFromPointNormal)(new t.Vec3(e.x,e.y,e.z),new t.Vec3(r.x,r.y,r.z)),a=new n.Plane(new n.Vector3(i.normal.x,i.normal.y,i.normal.z),-i.distance);return i.free(),a}function d(e,r,i){let a=new t.Window(e.width,e.height,e.pixelRatio);a.width*=a.pixelRatio,a.height*=a.pixelRatio;let o=(0,t.getPickRay)(a,new t.Transform(r.position.x,r.position.y,r.position.z,r.quaternion.x,r.quaternion.y,r.quaternion.z,r.quaternion.w,r.scale.x,r.scale.y,r.scale.z),new t.CameraFrustum(r.near,r.far,(0,t.angleToRadian)(r.fov),r.aspect),new t.Vec2(i.x,i.y)),s=new n.Ray(new n.Vector3(o.origin.x,o.origin.y,o.origin.z),new n.Vector3(o.direction.x,o.direction.y,o.direction.z));return o.free(),s}function f(e,r){let i=(0,t.getRayPlaneIntersection)(new t.Ray(new t.Vec3(e.origin.x,e.origin.y,e.origin.z),new t.Vec3(e.direction.x,e.direction.y,e.direction.z)),(0,t.getPlaneFromPointNormal)(new t.Vec3(r.normal.x*-r.constant,r.normal.y*-r.constant,r.normal.z*-r.constant),new t.Vec3(r.normal.x,r.normal.y,r.normal.z)));if(i){let e=new n.Vector3(i.x,i.y,i.z);return i.free(),e}}function p(e){return(0,t.getHeightFromEllipsoid)(new t.Vec3(e.x,e.y,e.z))}var m=Object.freeze({value:1});function h(e,r=new n.Vector3,i=new n.Vector3){let a=(0,t.encodePosition)(e.x,e.y,e.z);return r.set(a.high.x,a.high.y,a.high.z),i.set(a.low.x,a.low.y,a.low.z),a.free(),{high:r,low:i}}var g=(e,t,r=new n.Matrix4)=>{r.multiplyMatrices(t,e);let i=r.elements;return i[12]=0,i[13]=0,i[14]=0,r};function _(e,t,r=new n.Vector3,i=new n.Matrix4){i.multiplyMatrices(e,t),r.setFromMatrixPosition(i);let a=i.elements;return a[12]=0,a[13]=0,a[14]=0,{position:r,rotationScale:i}}var v=new n.Matrix4,y=new n.Vector3,b=(e,r)=>{v.copy(r).invert(),y.copy(e).applyMatrix4(v);let i=(0,t.encodePosition)(y.x,y.y,y.z),a=i.high,o=i.low,s={high:new n.Vector3(a.x,a.y,a.z),low:new n.Vector3(o.x,o.y,o.z)};return i.free(),s},x=class{_raw;constructor(e,n){this._raw=new t.EllipsoidGeodesic(new t.LLE(e.lat,e.lng,e.height),new t.LLE(n.lat,n.lng,n.height))}get distance(){return this._raw.distance}get startHeading(){return this._raw.start_heading}get endHeading(){return this._raw.end_heading}get start(){let e=this._raw.start;return{lat:e.lat,lng:e.lng,height:e.height}}get end(){let e=this._raw.end;return{lat:e.lat,lng:e.lng,height:e.height}}interpolatePoints(e){let t=this._raw.interpolateGeodeticPoints(e??null),n=[];for(let e of t)n.push({lat:e.lat,lng:e.lng,height:e.height}),e.free();return n}interpolateDistance(e){let t=this._raw.interpolateDistance(e),n={lat:t.lat,lng:t.lng,height:t.height};return t.free(),n}dispose(){this._raw.free()}};async function S(){await(0,t.default)()}function C(e){let r=(0,t.geodeticToXyz)(new t.LLE(e.lat,e.lng,e.height)),i=new n.Vector3(r.x,r.y,r.z);return r.free(),i}function w(e){let n=(0,t.xyzToGeodetic)(new t.Vec3(e.x,e.y,e.z)),r={lat:n.lat,lng:n.lng,height:n.height};return n.free(),r}function T(e){return(0,t.angleToRadian)(e)}function E(e){return(0,t.angleToDegree)(e)}function D(e,r,i){let a=new t.Window(e.width,e.height,e.pixelRatio);a.width*=a.pixelRatio,a.height*=a.pixelRatio;let o=(0,t.screenToWorld)(a,new t.Transform(r.position.x,r.position.y,r.position.z,r.quaternion.x,r.quaternion.y,r.quaternion.z,r.quaternion.w,r.scale.x,r.scale.y,r.scale.z),new t.CameraFrustum(r.near,r.far,(0,t.angleToRadian)(r.fov),r.aspect),new t.Vec2(i.x,i.y));if(!o)return;let s=new n.Vector3(o.x,o.y,o.z);return o.free(),s}function O(e){let r=(0,t.geodeticSurfaceNormal)(new t.LLE(e.lat,e.lng,e.height)),i=new n.Vector3(r.x,r.y,r.z);return r.free(),i}function k(e){let r=(0,t.eastNorthUpToFixedFrame)(new t.Vec3(e.x,e.y,e.z));return new n.Matrix4().fromArray(r)}function A(e){let r=(0,t.northEastDownToFixedFrame)(new t.Vec3(e.x,e.y,e.z));return new n.Matrix4().fromArray(r)}function j(e){let r=(0,t.northUpEastToFixedFrame)(new t.Vec3(e.x,e.y,e.z));return new n.Matrix4().fromArray(r)}function M(e){let r=(0,t.northWestUpToFixedFrame)(new t.Vec3(e.x,e.y,e.z));return new n.Matrix4().fromArray(r)}function N(e,r,i){let a=new t.Window(e.width,e.height,e.pixelRatio);a.width*=a.pixelRatio,a.height*=a.pixelRatio;let o=(0,t.worldToScreen)(a,new t.Transform(r.position.x,r.position.y,r.position.z,r.quaternion.x,r.quaternion.y,r.quaternion.z,r.quaternion.w,r.scale.x,r.scale.y,r.scale.z),new t.CameraFrustum(r.near,r.far,(0,t.angleToRadian)(r.fov),r.aspect),new t.Vec3(i.x,i.y,i.z));if(!o)return;let s=new n.Vector2(o.x,o.y);return o.free(),s}function P(){return(0,t.getWGS84SemiMajorAxis)()}function F(){return(0,t.getWGS84SemiMinorAxis)()}function I(){return(0,t.getWGS84EccentricitySquared)()}function L(){return(0,t.getWGS84Flattening)()}function R(){return(0,t.getWGS84Eccentricity)()}e.EllipsoidGeodesic=x,e.RTE_ONE_UNIFORM=m,e.calcCameraPosition=b,e.calcModelMatrixRTE=g,e.composeWorldMatrixForRTE=_,e.convertScreenToWorld=D,e.convertWorldToScreen=N,e.degreeToRadian=T,e.eastNorthUpToFixedFrame=k,e.encodePositionRTE=h,e.geodeticSurfaceNormal=O,e.geodeticToVector3=C,e.getHeightFromEllipsoid=p,e.getPickRay=d,e.getPlaneFromPointNormal=u,e.getRayPlaneIntersection=f,e.getWGS84Eccentricity=R,e.getWGS84EccentricitySquared=I,e.getWGS84Flattening=L,e.getWGS84SemiMajorAxis=P,e.getWGS84SemiMinorAxis=F,e.initNavaraApi=S,e.northEastDownToFixedFrame=A,e.northUpEastToFixedFrame=j,e.northWestUpToFixedFrame=M,e.radianToDegree=E,e.vector3ToGeodetic=w});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Window as WindowObject } from '@navaramap/core';
|
|
2
|
+
import { Vector3, Vector2, PerspectiveCamera, Ray as ThreeRay, Plane as ThreePlane } from 'three';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a plane from a point and a normal vector.
|
|
5
|
+
* @param point - A point on the plane
|
|
6
|
+
* @param normal - The normal vector of the plane
|
|
7
|
+
* @returns A Plane object defined by the point and normal
|
|
8
|
+
*/
|
|
9
|
+
export declare function getPlaneFromPointNormal(point: Vector3, normal: Vector3): ThreePlane;
|
|
10
|
+
/**
|
|
11
|
+
* Creates a picking ray from screen coordinates for raycasting.
|
|
12
|
+
* @param window - Window configuration with width, height, and pixelRatio
|
|
13
|
+
* @param camera - Three.js PerspectiveCamera
|
|
14
|
+
* @param vec2 - Screen coordinates in CSS pixels (same as MouseEvent clientX/clientY)
|
|
15
|
+
* @returns A Ray starting from the camera through the screen point
|
|
16
|
+
*/
|
|
17
|
+
export declare function getPickRay(windowObject: WindowObject, camera: PerspectiveCamera, vec2: Vector2): ThreeRay;
|
|
18
|
+
/**
|
|
19
|
+
* Computes the intersection point between a ray and a plane.
|
|
20
|
+
* @param ray - The ray to test
|
|
21
|
+
* @param plane - The plane to intersect with
|
|
22
|
+
* @returns Intersection point Vector3 or undefined if no intersection
|
|
23
|
+
*/
|
|
24
|
+
export declare function getRayPlaneIntersection(ray: ThreeRay, plane: ThreePlane): Vector3 | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Computes the height of a point above the WGS84 ellipsoid surface.
|
|
27
|
+
* @param point - Position in ECEF coordinates
|
|
28
|
+
* @returns Height above the ellipsoid in meters
|
|
29
|
+
*/
|
|
30
|
+
export declare function getHeightFromEllipsoid(point: Vector3): number;
|
package/dist/rte.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Matrix4, Vector3 } from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* Shared `u_rteOne` uniform for RTE vertex shaders. Always 1.0.
|
|
4
|
+
*
|
|
5
|
+
* Multiplying the high-order difference by a uniform the shader compiler
|
|
6
|
+
* cannot constant-fold blocks fast-math reassociation of
|
|
7
|
+
* `(high - camHigh) + (low - camLow)` into `(high + low) - (camHigh + camLow)`,
|
|
8
|
+
* which would collapse the math to absolute-ECEF f32 precision and make
|
|
9
|
+
* geometry freeze/snap in ~0.25-0.5 m steps as the camera moves
|
|
10
|
+
* (observed on ANGLE Metal). One frozen object is shared by every material.
|
|
11
|
+
*/
|
|
12
|
+
export declare const RTE_ONE_UNIFORM: {
|
|
13
|
+
readonly value: number;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Encodes a position into high and low precision Vector3 components for RTE (Relative-To-Eye) rendering.
|
|
17
|
+
* This enables GPU double precision emulation by splitting a position into two float components.
|
|
18
|
+
* @param original - Position to encode
|
|
19
|
+
* @param resultHigh - Vector3 to store the high precision component (reused to avoid GC)
|
|
20
|
+
* @param resultLow - Vector3 to store the low precision component (reused to avoid GC)
|
|
21
|
+
*/
|
|
22
|
+
export declare function encodePositionRTE(original: Vector3, resultHigh?: Vector3, resultLow?: Vector3): {
|
|
23
|
+
high: Vector3;
|
|
24
|
+
low: Vector3;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Calculates the model-view matrix for Relative-To-Eye (RTE) rendering, which improves precision for distant objects.
|
|
28
|
+
* @param objectMatrixWorld - The object's world transformation matrix
|
|
29
|
+
* @param matrixWorldInverse - The camera's inverse world matrix
|
|
30
|
+
* @param result - Optional matrix to store the result (creates new if omitted)
|
|
31
|
+
* @returns Model-view matrix with translation zeroed for RTE rendering
|
|
32
|
+
*/
|
|
33
|
+
export declare const calcModelMatrixRTE: (objectMatrixWorld: Matrix4, matrixWorldInverse: Matrix4, result?: Matrix4) => Matrix4;
|
|
34
|
+
/**
|
|
35
|
+
* Composes a frame matrix with a local transform, then splits the result into
|
|
36
|
+
* a translation Vector3 and a rotation/scale-only Matrix4 for RTE rendering.
|
|
37
|
+
*
|
|
38
|
+
* The translation is intended to be encoded as high/low RTE uniforms for GPU
|
|
39
|
+
* precision, while the rotation/scale matrix is assigned to the mesh's
|
|
40
|
+
* matrixWorld so it becomes the shader's modelMatrix uniform.
|
|
41
|
+
*
|
|
42
|
+
* @param frameMatrix - The frame transformation matrix (e.g. NUE-to-ECEF)
|
|
43
|
+
* @param localMatrix - The local T*R*S transform to compose within the frame
|
|
44
|
+
* @param resultPosition - Vector3 to store the extracted translation (reused to avoid GC)
|
|
45
|
+
* @param resultRotationScale - Matrix4 to store the translation-zeroed matrix (reused to avoid GC)
|
|
46
|
+
*/
|
|
47
|
+
export declare function composeWorldMatrixForRTE(frameMatrix: Matrix4, localMatrix: Matrix4, resultPosition?: Vector3, resultRotationScale?: Matrix4): {
|
|
48
|
+
position: Vector3;
|
|
49
|
+
rotationScale: Matrix4;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Calculates the camera position relative to an object with high/low precision encoding for RTE rendering.
|
|
53
|
+
* @param cameraPosition - The camera's world position
|
|
54
|
+
* @param modelMatrixWorld - The object's world transformation matrix
|
|
55
|
+
* @returns Object with high and low precision Vector3 components for GPU double precision emulation
|
|
56
|
+
*/
|
|
57
|
+
export declare const calcCameraPosition: (cameraPosition: Vector3, modelMatrixWorld: Matrix4) => {
|
|
58
|
+
high: Vector3;
|
|
59
|
+
low: Vector3;
|
|
60
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@navaramap/three-api",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"main": "dist/index.umd.cjs",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"src",
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT OR Apache-2.0",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/reearth/navara.git",
|
|
16
|
+
"directory": "web/navara_three_api"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"three": ">=0.183.0"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@navaramap/core": "0.0.0",
|
|
26
|
+
"@navaramap/engine-api": "0.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/three": "0.185.1",
|
|
30
|
+
"three": "0.185.1"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"type-check": "tsc && tsc -p tsconfig.node.json",
|
|
34
|
+
"build": "pnpm type-check && vite build --watch=false",
|
|
35
|
+
"build:dev": "vite build --mode development",
|
|
36
|
+
"build:dev:watch": "pnpm build:dev --watch",
|
|
37
|
+
"test": "vitest --passWithNoTests --watch=false",
|
|
38
|
+
"lint": "eslint . --fix",
|
|
39
|
+
"lint:check": "eslint .",
|
|
40
|
+
"format": "prettier --write --ignore-path ../../.gitignore --ignore-path ../../.prettierignore .",
|
|
41
|
+
"format:check": "prettier --check --ignore-path ../../.gitignore --ignore-path ../../.prettierignore ."
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { LatLngHeight } from "@navaramap/core";
|
|
2
|
+
import {
|
|
3
|
+
EllipsoidGeodesic as EllipsoidGeodesicImpl,
|
|
4
|
+
LLE,
|
|
5
|
+
} from "@navaramap/engine-api";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Wrapper class for geodesic calculations on an ellipsoid surface.
|
|
9
|
+
* Precomputes common variables for optimization when instantiated.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const geodesic = new EllipsoidGeodesic(
|
|
14
|
+
* { lat: 0.5, lng: 2.0, height: 0 },
|
|
15
|
+
* { lat: 0.6, lng: 2.1, height: 0 }
|
|
16
|
+
* );
|
|
17
|
+
*
|
|
18
|
+
* console.log(geodesic.distance);
|
|
19
|
+
* const points = geodesic.interpolatePoints(1000);
|
|
20
|
+
* const midpoint = geodesic.interpolateDistance(geodesic.distance / 2);
|
|
21
|
+
*
|
|
22
|
+
* geodesic.dispose(); // Free WASM memory when done
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export class EllipsoidGeodesic {
|
|
26
|
+
private _raw: EllipsoidGeodesicImpl;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create a new geodesic between two points on the ellipsoid.
|
|
30
|
+
* @param start - Start point in geodetic coordinates (lat/lng in radians)
|
|
31
|
+
* @param end - End point in geodetic coordinates (lat/lng in radians)
|
|
32
|
+
*/
|
|
33
|
+
constructor(start: LatLngHeight, end: LatLngHeight) {
|
|
34
|
+
this._raw = new EllipsoidGeodesicImpl(
|
|
35
|
+
new LLE(start.lat, start.lng, start.height),
|
|
36
|
+
new LLE(end.lat, end.lng, end.height),
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Distance in meters between start and end points */
|
|
41
|
+
get distance(): number {
|
|
42
|
+
return this._raw.distance;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Heading at the start point in radians */
|
|
46
|
+
get startHeading(): number {
|
|
47
|
+
return this._raw.start_heading;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Heading at the end point in radians */
|
|
51
|
+
get endHeading(): number {
|
|
52
|
+
return this._raw.end_heading;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Start point in geodetic coordinates */
|
|
56
|
+
get start(): LatLngHeight {
|
|
57
|
+
const lle = this._raw.start;
|
|
58
|
+
return { lat: lle.lat, lng: lle.lng, height: lle.height };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** End point in geodetic coordinates */
|
|
62
|
+
get end(): LatLngHeight {
|
|
63
|
+
const lle = this._raw.end;
|
|
64
|
+
return { lat: lle.lat, lng: lle.lng, height: lle.height };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Interpolate points along the geodesic path.
|
|
69
|
+
* @param granularity - Distance between interpolated points in meters (optional)
|
|
70
|
+
* @returns Array of interpolated points in geodetic coordinates
|
|
71
|
+
*/
|
|
72
|
+
interpolatePoints(granularity?: number): LatLngHeight[] {
|
|
73
|
+
const wasmPoints = this._raw.interpolateGeodeticPoints(granularity ?? null);
|
|
74
|
+
|
|
75
|
+
const results: LatLngHeight[] = [];
|
|
76
|
+
for (const point of wasmPoints) {
|
|
77
|
+
results.push({
|
|
78
|
+
lat: point.lat,
|
|
79
|
+
lng: point.lng,
|
|
80
|
+
height: point.height,
|
|
81
|
+
});
|
|
82
|
+
point.free();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return results;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Interpolate a point at a specific distance along the geodesic path.
|
|
90
|
+
* @param distance - Distance from start point in meters
|
|
91
|
+
* @returns Interpolated point in geodetic coordinates
|
|
92
|
+
*/
|
|
93
|
+
interpolateDistance(distance: number): LatLngHeight {
|
|
94
|
+
const wasmPoint = this._raw.interpolateDistance(distance);
|
|
95
|
+
|
|
96
|
+
const result: LatLngHeight = {
|
|
97
|
+
lat: wasmPoint.lat,
|
|
98
|
+
lng: wasmPoint.lng,
|
|
99
|
+
height: wasmPoint.height,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
wasmPoint.free();
|
|
103
|
+
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Free WASM memory. Call this when the geodesic is no longer needed.
|
|
109
|
+
*/
|
|
110
|
+
dispose(): void {
|
|
111
|
+
this._raw.free();
|
|
112
|
+
}
|
|
113
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import type { LatLngHeight, Window as WindowObject } from "@navaramap/core";
|
|
2
|
+
import initApi, {
|
|
3
|
+
Window,
|
|
4
|
+
LLE,
|
|
5
|
+
Vec3,
|
|
6
|
+
Vec2,
|
|
7
|
+
Transform,
|
|
8
|
+
CameraFrustum,
|
|
9
|
+
geodeticToXyz,
|
|
10
|
+
xyzToGeodetic,
|
|
11
|
+
angleToRadian,
|
|
12
|
+
angleToDegree,
|
|
13
|
+
screenToWorld,
|
|
14
|
+
worldToScreen,
|
|
15
|
+
geodeticSurfaceNormal as nvGeodeticSurfaceNormal,
|
|
16
|
+
eastNorthUpToFixedFrame as nvEastNorthUpToFixedFrame,
|
|
17
|
+
northEastDownToFixedFrame as nvNorthEastDownToFixedFrame,
|
|
18
|
+
northUpEastToFixedFrame as nvNorthUpEastToFixedFrame,
|
|
19
|
+
northWestUpToFixedFrame as nvNorthWestUpToFixedFrame,
|
|
20
|
+
getWGS84SemiMajorAxis as nvGetWGS84SemiMajorAxis,
|
|
21
|
+
getWGS84SemiMinorAxis as nvGetWGS84SemiMinorAxis,
|
|
22
|
+
getWGS84EccentricitySquared as nvGetWGS84EccentricitySquared,
|
|
23
|
+
getWGS84Flattening as nvGetWGS84Flattening,
|
|
24
|
+
getWGS84Eccentricity as nvGetWGS84Eccentricity,
|
|
25
|
+
} from "@navaramap/engine-api";
|
|
26
|
+
import { Vector3, Vector2, Matrix4, PerspectiveCamera } from "three";
|
|
27
|
+
|
|
28
|
+
export type { LatLngHeight, LatLng } from "@navaramap/core";
|
|
29
|
+
export type {
|
|
30
|
+
Window,
|
|
31
|
+
Transform,
|
|
32
|
+
CameraFrustum,
|
|
33
|
+
Plane,
|
|
34
|
+
Ray,
|
|
35
|
+
Vec2,
|
|
36
|
+
Vec3,
|
|
37
|
+
} from "@navaramap/core";
|
|
38
|
+
|
|
39
|
+
export * from "./intersection";
|
|
40
|
+
export * from "./rte";
|
|
41
|
+
export * from "./ellipsoidGeodesic";
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Initializes the Navara API WASM module. Must be called before using other API functions.
|
|
45
|
+
*/
|
|
46
|
+
export async function initNavaraApi() {
|
|
47
|
+
await initApi();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Converts geodetic coordinates (longitude, latitude, height) to a Cartesian Vector3 in ECEF coordinates.
|
|
52
|
+
* @param lle - Geodetic coordinates (lng in radians, lat in radians, height in meters)
|
|
53
|
+
* @returns Cartesian Vector3 in Earth-Centered Earth-Fixed (ECEF) coordinates
|
|
54
|
+
*/
|
|
55
|
+
export function geodeticToVector3(lle: LatLngHeight): Vector3 {
|
|
56
|
+
const pos = geodeticToXyz(new LLE(lle.lat, lle.lng, lle.height));
|
|
57
|
+
const result = new Vector3(pos.x, pos.y, pos.z);
|
|
58
|
+
pos.free();
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Converts a Cartesian Vector3 in ECEF coordinates to geodetic coordinates.
|
|
64
|
+
* @param xyz - Cartesian Vector3 in Earth-Centered Earth-Fixed (ECEF) coordinates
|
|
65
|
+
* @returns Geodetic coordinates (lng in radians, lat in radians, height in meters)
|
|
66
|
+
*/
|
|
67
|
+
export function vector3ToGeodetic(xyz: Vector3): LatLngHeight {
|
|
68
|
+
const vec3 = new Vec3(xyz.x, xyz.y, xyz.z);
|
|
69
|
+
const lle = xyzToGeodetic(vec3);
|
|
70
|
+
const result = { lat: lle.lat, lng: lle.lng, height: lle.height };
|
|
71
|
+
lle.free();
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Converts an angle from degrees to radians.
|
|
77
|
+
* @param degree - Angle in degrees
|
|
78
|
+
* @returns Angle in radians
|
|
79
|
+
*/
|
|
80
|
+
export function degreeToRadian(degree: number): number {
|
|
81
|
+
return angleToRadian(degree);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Converts an angle from radians to degrees.
|
|
86
|
+
* @param radian - Angle in radians
|
|
87
|
+
* @returns Angle in degrees
|
|
88
|
+
*/
|
|
89
|
+
export function radianToDegree(radian: number): number {
|
|
90
|
+
return angleToDegree(radian);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Converts screen coordinates to world coordinates by raycasting against the WGS84 ellipsoid.
|
|
95
|
+
* @param window - Window configuration with width, height, and pixelRatio
|
|
96
|
+
* @param camera - Three.js PerspectiveCamera
|
|
97
|
+
* @param vec2 - Screen coordinates in CSS pixels (same as MouseEvent clientX/clientY)
|
|
98
|
+
* @returns World position Vector3 in ECEF coordinates, or undefined if no intersection with ellipsoid
|
|
99
|
+
*/
|
|
100
|
+
export function convertScreenToWorld(
|
|
101
|
+
windowObject: WindowObject,
|
|
102
|
+
camera: PerspectiveCamera,
|
|
103
|
+
vec2: Vector2,
|
|
104
|
+
): Vector3 | undefined {
|
|
105
|
+
const window = new Window(
|
|
106
|
+
windowObject.width,
|
|
107
|
+
windowObject.height,
|
|
108
|
+
windowObject.pixelRatio,
|
|
109
|
+
);
|
|
110
|
+
window.width = window.width * window.pixelRatio;
|
|
111
|
+
window.height = window.height * window.pixelRatio;
|
|
112
|
+
|
|
113
|
+
const transform = new Transform(
|
|
114
|
+
camera.position.x,
|
|
115
|
+
camera.position.y,
|
|
116
|
+
camera.position.z,
|
|
117
|
+
camera.quaternion.x,
|
|
118
|
+
camera.quaternion.y,
|
|
119
|
+
camera.quaternion.z,
|
|
120
|
+
camera.quaternion.w,
|
|
121
|
+
camera.scale.x,
|
|
122
|
+
camera.scale.y,
|
|
123
|
+
camera.scale.z,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const frustum = new CameraFrustum(
|
|
127
|
+
camera.near,
|
|
128
|
+
camera.far,
|
|
129
|
+
angleToRadian(camera.fov),
|
|
130
|
+
camera.aspect,
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const pos = screenToWorld(
|
|
134
|
+
window,
|
|
135
|
+
transform,
|
|
136
|
+
frustum,
|
|
137
|
+
new Vec2(vec2.x, vec2.y),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
if (!pos) {
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
const result = new Vector3(pos.x, pos.y, pos.z);
|
|
144
|
+
pos.free();
|
|
145
|
+
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Computes the surface normal vector at a geodetic position on the WGS84 ellipsoid.
|
|
151
|
+
* @param lle - Geodetic coordinates (lng in radians, lat in radians, height in meters)
|
|
152
|
+
* @returns Unit normal vector pointing outward from the ellipsoid surface
|
|
153
|
+
*/
|
|
154
|
+
export function geodeticSurfaceNormal(lle: LatLngHeight): Vector3 {
|
|
155
|
+
const pos = nvGeodeticSurfaceNormal(new LLE(lle.lat, lle.lng, lle.height));
|
|
156
|
+
const result = new Vector3(pos.x, pos.y, pos.z);
|
|
157
|
+
pos.free();
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Creates a local East-North-Up (ENU) reference frame transformation matrix at a position.
|
|
163
|
+
* @param origin - Origin position in ECEF coordinates
|
|
164
|
+
* @returns 4x4 transformation matrix from ENU to ECEF
|
|
165
|
+
*/
|
|
166
|
+
export function eastNorthUpToFixedFrame(origin: Vector3): Matrix4 {
|
|
167
|
+
const vec3 = new Vec3(origin.x, origin.y, origin.z);
|
|
168
|
+
const arr = nvEastNorthUpToFixedFrame(vec3);
|
|
169
|
+
const matrix = new Matrix4().fromArray(arr);
|
|
170
|
+
return matrix;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Creates a local North-East-Down (NED) reference frame transformation matrix at a position.
|
|
175
|
+
* @param origin - Origin position in ECEF coordinates
|
|
176
|
+
* @returns 4x4 transformation matrix from NED to ECEF
|
|
177
|
+
*/
|
|
178
|
+
export function northEastDownToFixedFrame(origin: Vector3): Matrix4 {
|
|
179
|
+
const vec3 = new Vec3(origin.x, origin.y, origin.z);
|
|
180
|
+
const arr = nvNorthEastDownToFixedFrame(vec3);
|
|
181
|
+
const matrix = new Matrix4().fromArray(arr);
|
|
182
|
+
return matrix;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Creates a local North-Up-East (NUE) reference frame transformation matrix at a position.
|
|
187
|
+
* @param origin - Origin position in ECEF coordinates
|
|
188
|
+
* @returns 4x4 transformation matrix from NUE to ECEF
|
|
189
|
+
*/
|
|
190
|
+
export function northUpEastToFixedFrame(origin: Vector3): Matrix4 {
|
|
191
|
+
const vec3 = new Vec3(origin.x, origin.y, origin.z);
|
|
192
|
+
const arr = nvNorthUpEastToFixedFrame(vec3);
|
|
193
|
+
const matrix = new Matrix4().fromArray(arr);
|
|
194
|
+
return matrix;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Creates a local North-West-Up (NWU) reference frame transformation matrix at a position.
|
|
199
|
+
* @param origin - Origin position in ECEF coordinates
|
|
200
|
+
* @returns 4x4 transformation matrix from NWU to ECEF
|
|
201
|
+
*/
|
|
202
|
+
export function northWestUpToFixedFrame(origin: Vector3): Matrix4 {
|
|
203
|
+
const vec3 = new Vec3(origin.x, origin.y, origin.z);
|
|
204
|
+
const arr = nvNorthWestUpToFixedFrame(vec3);
|
|
205
|
+
const matrix = new Matrix4().fromArray(arr);
|
|
206
|
+
return matrix;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Converts world coordinates to screen coordinates.
|
|
211
|
+
* @param window - Window configuration with width, height, and pixelRatio
|
|
212
|
+
* @param camera - Three.js PerspectiveCamera
|
|
213
|
+
* @param worldPos - World position Vector3 in ECEF coordinates
|
|
214
|
+
* @returns Screen coordinates in CSS pixels, or undefined if behind camera
|
|
215
|
+
*/
|
|
216
|
+
export function convertWorldToScreen(
|
|
217
|
+
windowObject: WindowObject,
|
|
218
|
+
camera: PerspectiveCamera,
|
|
219
|
+
worldPos: Vector3,
|
|
220
|
+
): Vector2 | undefined {
|
|
221
|
+
const window = new Window(
|
|
222
|
+
windowObject.width,
|
|
223
|
+
windowObject.height,
|
|
224
|
+
windowObject.pixelRatio,
|
|
225
|
+
);
|
|
226
|
+
window.width = window.width * window.pixelRatio;
|
|
227
|
+
window.height = window.height * window.pixelRatio;
|
|
228
|
+
|
|
229
|
+
const transform = new Transform(
|
|
230
|
+
camera.position.x,
|
|
231
|
+
camera.position.y,
|
|
232
|
+
camera.position.z,
|
|
233
|
+
camera.quaternion.x,
|
|
234
|
+
camera.quaternion.y,
|
|
235
|
+
camera.quaternion.z,
|
|
236
|
+
camera.quaternion.w,
|
|
237
|
+
camera.scale.x,
|
|
238
|
+
camera.scale.y,
|
|
239
|
+
camera.scale.z,
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
const frustum = new CameraFrustum(
|
|
243
|
+
camera.near,
|
|
244
|
+
camera.far,
|
|
245
|
+
angleToRadian(camera.fov),
|
|
246
|
+
camera.aspect,
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
const screenPos = worldToScreen(
|
|
250
|
+
window,
|
|
251
|
+
transform,
|
|
252
|
+
frustum,
|
|
253
|
+
new Vec3(worldPos.x, worldPos.y, worldPos.z),
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
if (!screenPos) {
|
|
257
|
+
return undefined;
|
|
258
|
+
}
|
|
259
|
+
const result = new Vector2(screenPos.x, screenPos.y);
|
|
260
|
+
screenPos.free();
|
|
261
|
+
|
|
262
|
+
return result;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Returns the WGS84 ellipsoid semi-major axis (equatorial radius) in meters.
|
|
267
|
+
* @returns Semi-major axis (approximately 6,378,137 meters)
|
|
268
|
+
*/
|
|
269
|
+
export function getWGS84SemiMajorAxis(): number {
|
|
270
|
+
return nvGetWGS84SemiMajorAxis();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Returns the WGS84 ellipsoid semi-minor axis (polar radius) in meters.
|
|
275
|
+
* @returns Semi-minor axis (approximately 6,356,752 meters)
|
|
276
|
+
*/
|
|
277
|
+
export function getWGS84SemiMinorAxis(): number {
|
|
278
|
+
return nvGetWGS84SemiMinorAxis();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Returns the WGS84 ellipsoid first eccentricity squared.
|
|
283
|
+
* @returns Eccentricity squared value
|
|
284
|
+
*/
|
|
285
|
+
export function getWGS84EccentricitySquared(): number {
|
|
286
|
+
return nvGetWGS84EccentricitySquared();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Returns the WGS84 ellipsoid flattening ratio.
|
|
291
|
+
* @returns Flattening value (approximately 1/298.257)
|
|
292
|
+
*/
|
|
293
|
+
export function getWGS84Flattening(): number {
|
|
294
|
+
return nvGetWGS84Flattening();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Returns the WGS84 ellipsoid first eccentricity.
|
|
299
|
+
* @returns Eccentricity value
|
|
300
|
+
*/
|
|
301
|
+
export function getWGS84Eccentricity(): number {
|
|
302
|
+
return nvGetWGS84Eccentricity();
|
|
303
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type { Window as WindowObject } from "@navaramap/core";
|
|
2
|
+
import {
|
|
3
|
+
angleToRadian,
|
|
4
|
+
getPlaneFromPointNormal as nvGetPlaneFromPointNormal,
|
|
5
|
+
getPickRay as nvGetPickRay,
|
|
6
|
+
getRayPlaneIntersection as nvGetRayPlaneIntersection,
|
|
7
|
+
getHeightFromEllipsoid as nvGetHeightFromEllipsoid,
|
|
8
|
+
Vec3,
|
|
9
|
+
Vec2,
|
|
10
|
+
Window,
|
|
11
|
+
Transform,
|
|
12
|
+
CameraFrustum,
|
|
13
|
+
Ray as WASMRay,
|
|
14
|
+
} from "@navaramap/engine-api";
|
|
15
|
+
import {
|
|
16
|
+
Vector3,
|
|
17
|
+
Vector2,
|
|
18
|
+
PerspectiveCamera,
|
|
19
|
+
Ray as ThreeRay,
|
|
20
|
+
Plane as ThreePlane,
|
|
21
|
+
} from "three";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates a plane from a point and a normal vector.
|
|
25
|
+
* @param point - A point on the plane
|
|
26
|
+
* @param normal - The normal vector of the plane
|
|
27
|
+
* @returns A Plane object defined by the point and normal
|
|
28
|
+
*/
|
|
29
|
+
export function getPlaneFromPointNormal(
|
|
30
|
+
point: Vector3,
|
|
31
|
+
normal: Vector3,
|
|
32
|
+
): ThreePlane {
|
|
33
|
+
const pointVec3 = new Vec3(point.x, point.y, point.z);
|
|
34
|
+
const normalVec3 = new Vec3(normal.x, normal.y, normal.z);
|
|
35
|
+
|
|
36
|
+
const wasmPlane = nvGetPlaneFromPointNormal(pointVec3, normalVec3);
|
|
37
|
+
|
|
38
|
+
// Convert WASM Plane to Three.js Plane
|
|
39
|
+
const threeNormal = new Vector3(
|
|
40
|
+
wasmPlane.normal.x,
|
|
41
|
+
wasmPlane.normal.y,
|
|
42
|
+
wasmPlane.normal.z,
|
|
43
|
+
);
|
|
44
|
+
// Three.js Plane uses constant = -distance (dot product of normal and point on plane)
|
|
45
|
+
const threePlane = new ThreePlane(threeNormal, -wasmPlane.distance);
|
|
46
|
+
|
|
47
|
+
wasmPlane.free();
|
|
48
|
+
|
|
49
|
+
return threePlane;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Creates a picking ray from screen coordinates for raycasting.
|
|
54
|
+
* @param window - Window configuration with width, height, and pixelRatio
|
|
55
|
+
* @param camera - Three.js PerspectiveCamera
|
|
56
|
+
* @param vec2 - Screen coordinates in CSS pixels (same as MouseEvent clientX/clientY)
|
|
57
|
+
* @returns A Ray starting from the camera through the screen point
|
|
58
|
+
*/
|
|
59
|
+
export function getPickRay(
|
|
60
|
+
windowObject: WindowObject,
|
|
61
|
+
camera: PerspectiveCamera,
|
|
62
|
+
vec2: Vector2,
|
|
63
|
+
): ThreeRay {
|
|
64
|
+
const window = new Window(
|
|
65
|
+
windowObject.width,
|
|
66
|
+
windowObject.height,
|
|
67
|
+
windowObject.pixelRatio,
|
|
68
|
+
);
|
|
69
|
+
window.width = window.width * window.pixelRatio;
|
|
70
|
+
window.height = window.height * window.pixelRatio;
|
|
71
|
+
|
|
72
|
+
const transform = new Transform(
|
|
73
|
+
camera.position.x,
|
|
74
|
+
camera.position.y,
|
|
75
|
+
camera.position.z,
|
|
76
|
+
camera.quaternion.x,
|
|
77
|
+
camera.quaternion.y,
|
|
78
|
+
camera.quaternion.z,
|
|
79
|
+
camera.quaternion.w,
|
|
80
|
+
camera.scale.x,
|
|
81
|
+
camera.scale.y,
|
|
82
|
+
camera.scale.z,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const frustum = new CameraFrustum(
|
|
86
|
+
camera.near,
|
|
87
|
+
camera.far,
|
|
88
|
+
angleToRadian(camera.fov),
|
|
89
|
+
camera.aspect,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const wasmRay = nvGetPickRay(
|
|
93
|
+
window,
|
|
94
|
+
transform,
|
|
95
|
+
frustum,
|
|
96
|
+
new Vec2(vec2.x, vec2.y),
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Convert WASM Ray to Three.js Ray
|
|
100
|
+
const origin = new Vector3(
|
|
101
|
+
wasmRay.origin.x,
|
|
102
|
+
wasmRay.origin.y,
|
|
103
|
+
wasmRay.origin.z,
|
|
104
|
+
);
|
|
105
|
+
const direction = new Vector3(
|
|
106
|
+
wasmRay.direction.x,
|
|
107
|
+
wasmRay.direction.y,
|
|
108
|
+
wasmRay.direction.z,
|
|
109
|
+
);
|
|
110
|
+
const threeRay = new ThreeRay(origin, direction);
|
|
111
|
+
|
|
112
|
+
wasmRay.free();
|
|
113
|
+
|
|
114
|
+
return threeRay;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Computes the intersection point between a ray and a plane.
|
|
119
|
+
* @param ray - The ray to test
|
|
120
|
+
* @param plane - The plane to intersect with
|
|
121
|
+
* @returns Intersection point Vector3 or undefined if no intersection
|
|
122
|
+
*/
|
|
123
|
+
export function getRayPlaneIntersection(
|
|
124
|
+
ray: ThreeRay,
|
|
125
|
+
plane: ThreePlane,
|
|
126
|
+
): Vector3 | undefined {
|
|
127
|
+
// Convert Three.js Ray to WASM Ray
|
|
128
|
+
const wasmRay = new WASMRay(
|
|
129
|
+
new Vec3(ray.origin.x, ray.origin.y, ray.origin.z),
|
|
130
|
+
new Vec3(ray.direction.x, ray.direction.y, ray.direction.z),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// Convert Three.js Plane to WASM Plane
|
|
134
|
+
// Three.js uses constant = -distance, so we need to negate it
|
|
135
|
+
const wasmPlane = nvGetPlaneFromPointNormal(
|
|
136
|
+
new Vec3(
|
|
137
|
+
plane.normal.x * -plane.constant,
|
|
138
|
+
plane.normal.y * -plane.constant,
|
|
139
|
+
plane.normal.z * -plane.constant,
|
|
140
|
+
),
|
|
141
|
+
new Vec3(plane.normal.x, plane.normal.y, plane.normal.z),
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const intersection = nvGetRayPlaneIntersection(wasmRay, wasmPlane);
|
|
145
|
+
|
|
146
|
+
if (intersection) {
|
|
147
|
+
const result = new Vector3(intersection.x, intersection.y, intersection.z);
|
|
148
|
+
intersection.free();
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Computes the height of a point above the WGS84 ellipsoid surface.
|
|
156
|
+
* @param point - Position in ECEF coordinates
|
|
157
|
+
* @returns Height above the ellipsoid in meters
|
|
158
|
+
*/
|
|
159
|
+
export function getHeightFromEllipsoid(point: Vector3): number {
|
|
160
|
+
const vec3 = new Vec3(point.x, point.y, point.z);
|
|
161
|
+
const result = nvGetHeightFromEllipsoid(vec3);
|
|
162
|
+
return result;
|
|
163
|
+
}
|
package/src/rte.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { encodePosition } from "@navaramap/engine-api";
|
|
2
|
+
import { Matrix4, Vector3 } from "three";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Shared `u_rteOne` uniform for RTE vertex shaders. Always 1.0.
|
|
6
|
+
*
|
|
7
|
+
* Multiplying the high-order difference by a uniform the shader compiler
|
|
8
|
+
* cannot constant-fold blocks fast-math reassociation of
|
|
9
|
+
* `(high - camHigh) + (low - camLow)` into `(high + low) - (camHigh + camLow)`,
|
|
10
|
+
* which would collapse the math to absolute-ECEF f32 precision and make
|
|
11
|
+
* geometry freeze/snap in ~0.25-0.5 m steps as the camera moves
|
|
12
|
+
* (observed on ANGLE Metal). One frozen object is shared by every material.
|
|
13
|
+
*/
|
|
14
|
+
export const RTE_ONE_UNIFORM: { readonly value: number } = Object.freeze({
|
|
15
|
+
value: 1.0,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Encodes a position into high and low precision Vector3 components for RTE (Relative-To-Eye) rendering.
|
|
20
|
+
* This enables GPU double precision emulation by splitting a position into two float components.
|
|
21
|
+
* @param original - Position to encode
|
|
22
|
+
* @param resultHigh - Vector3 to store the high precision component (reused to avoid GC)
|
|
23
|
+
* @param resultLow - Vector3 to store the low precision component (reused to avoid GC)
|
|
24
|
+
*/
|
|
25
|
+
export function encodePositionRTE(
|
|
26
|
+
original: Vector3,
|
|
27
|
+
resultHigh = new Vector3(),
|
|
28
|
+
resultLow = new Vector3(),
|
|
29
|
+
): { high: Vector3; low: Vector3 } {
|
|
30
|
+
const encoded = encodePosition(original.x, original.y, original.z);
|
|
31
|
+
resultHigh.set(encoded.high.x, encoded.high.y, encoded.high.z);
|
|
32
|
+
resultLow.set(encoded.low.x, encoded.low.y, encoded.low.z);
|
|
33
|
+
encoded.free();
|
|
34
|
+
return { high: resultHigh, low: resultLow };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Calculates the model-view matrix for Relative-To-Eye (RTE) rendering, which improves precision for distant objects.
|
|
39
|
+
* @param objectMatrixWorld - The object's world transformation matrix
|
|
40
|
+
* @param matrixWorldInverse - The camera's inverse world matrix
|
|
41
|
+
* @param result - Optional matrix to store the result (creates new if omitted)
|
|
42
|
+
* @returns Model-view matrix with translation zeroed for RTE rendering
|
|
43
|
+
*/
|
|
44
|
+
export const calcModelMatrixRTE = (
|
|
45
|
+
objectMatrixWorld: Matrix4,
|
|
46
|
+
matrixWorldInverse: Matrix4,
|
|
47
|
+
result = new Matrix4(),
|
|
48
|
+
) => {
|
|
49
|
+
result.multiplyMatrices(matrixWorldInverse, objectMatrixWorld);
|
|
50
|
+
const e = result.elements;
|
|
51
|
+
e[12] = 0.0;
|
|
52
|
+
e[13] = 0.0;
|
|
53
|
+
e[14] = 0.0; // zero translation
|
|
54
|
+
return result;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Composes a frame matrix with a local transform, then splits the result into
|
|
59
|
+
* a translation Vector3 and a rotation/scale-only Matrix4 for RTE rendering.
|
|
60
|
+
*
|
|
61
|
+
* The translation is intended to be encoded as high/low RTE uniforms for GPU
|
|
62
|
+
* precision, while the rotation/scale matrix is assigned to the mesh's
|
|
63
|
+
* matrixWorld so it becomes the shader's modelMatrix uniform.
|
|
64
|
+
*
|
|
65
|
+
* @param frameMatrix - The frame transformation matrix (e.g. NUE-to-ECEF)
|
|
66
|
+
* @param localMatrix - The local T*R*S transform to compose within the frame
|
|
67
|
+
* @param resultPosition - Vector3 to store the extracted translation (reused to avoid GC)
|
|
68
|
+
* @param resultRotationScale - Matrix4 to store the translation-zeroed matrix (reused to avoid GC)
|
|
69
|
+
*/
|
|
70
|
+
export function composeWorldMatrixForRTE(
|
|
71
|
+
frameMatrix: Matrix4,
|
|
72
|
+
localMatrix: Matrix4,
|
|
73
|
+
resultPosition = new Vector3(),
|
|
74
|
+
resultRotationScale = new Matrix4(),
|
|
75
|
+
): { position: Vector3; rotationScale: Matrix4 } {
|
|
76
|
+
resultRotationScale.multiplyMatrices(frameMatrix, localMatrix);
|
|
77
|
+
resultPosition.setFromMatrixPosition(resultRotationScale);
|
|
78
|
+
const e = resultRotationScale.elements;
|
|
79
|
+
e[12] = 0;
|
|
80
|
+
e[13] = 0;
|
|
81
|
+
e[14] = 0;
|
|
82
|
+
return { position: resultPosition, rotationScale: resultRotationScale };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const INVERSE_MODEL_MATRIX = new Matrix4();
|
|
86
|
+
const CAMERA_MODEL_POSITION = new Vector3();
|
|
87
|
+
/**
|
|
88
|
+
* Calculates the camera position relative to an object with high/low precision encoding for RTE rendering.
|
|
89
|
+
* @param cameraPosition - The camera's world position
|
|
90
|
+
* @param modelMatrixWorld - The object's world transformation matrix
|
|
91
|
+
* @returns Object with high and low precision Vector3 components for GPU double precision emulation
|
|
92
|
+
*/
|
|
93
|
+
export const calcCameraPosition = (
|
|
94
|
+
cameraPosition: Vector3,
|
|
95
|
+
modelMatrixWorld: Matrix4,
|
|
96
|
+
) => {
|
|
97
|
+
INVERSE_MODEL_MATRIX.copy(modelMatrixWorld).invert();
|
|
98
|
+
CAMERA_MODEL_POSITION.copy(cameraPosition).applyMatrix4(INVERSE_MODEL_MATRIX);
|
|
99
|
+
const encoded = encodePosition(
|
|
100
|
+
CAMERA_MODEL_POSITION.x,
|
|
101
|
+
CAMERA_MODEL_POSITION.y,
|
|
102
|
+
CAMERA_MODEL_POSITION.z,
|
|
103
|
+
);
|
|
104
|
+
const high = encoded.high;
|
|
105
|
+
const low = encoded.low;
|
|
106
|
+
const result = {
|
|
107
|
+
high: new Vector3(high.x, high.y, high.z),
|
|
108
|
+
low: new Vector3(low.x, low.y, low.z),
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
encoded.free();
|
|
112
|
+
|
|
113
|
+
return result;
|
|
114
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|