UNPKG

3d-tiles-renderer

Version:

https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification

465 lines (464 loc) 21 kB
import { e as ot, d as Tt } from "./TilesRendererBase-BGxy2Uih.js"; import { MathUtils as D, Spherical as mt, Vector3 as u, Matrix4 as R, Sphere as Et, Ray as yt, Euler as St, Box3 as dt, Plane as wt, TextureUtils as _t } from "three"; import { estimateBytesUsed as zt } from "three/addons/utils/BufferGeometryUtils.js"; const F = /* @__PURE__ */ new mt(), et = /* @__PURE__ */ new u(), Ct = {}; function xt(c) { const { x: t, y: i, z: o } = c; c.x = o, c.y = t, c.z = i; } function Ft(c) { const { x: t, y: i, z: o } = c; c.z = t, c.x = i, c.y = o; } function gt(c) { return -(c - Math.PI / 2); } function J(c) { return -c + Math.PI / 2; } function Rt(c, t, i = {}) { return F.theta = t, F.phi = J(c), et.setFromSpherical(F), F.setFromVector3(et), i.lat = gt(F.phi), i.lon = F.theta, i; } function st(c, t = "E", i = "W") { const o = c < 0 ? i : t; c = Math.abs(c); const s = ~~c, e = (c - s) * 60, r = ~~e, l = ~~((e - r) * 60); return `${s}° ${r}' ${l}" ${o}`; } function bt(c, t, i = !1) { const o = Rt(c, t, Ct); let s, e; return i ? (s = `${(D.RAD2DEG * o.lat).toFixed(4)}°`, e = `${(D.RAD2DEG * o.lon).toFixed(4)}°`) : (s = st(D.RAD2DEG * o.lat, "N", "S"), e = st(D.RAD2DEG * o.lon, "E", "W")), `${s} ${e}`; } const Ot = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, latitudeToSphericalPhi: J, sphericalPhiToLatitude: gt, swapToGeoFrame: xt, swapToThreeFrame: Ft, toLatLonString: bt }, Symbol.toStringTag, { value: "Module" })), nt = /* @__PURE__ */ new mt(), _ = /* @__PURE__ */ new u(), y = /* @__PURE__ */ new u(), Y = /* @__PURE__ */ new u(), P = /* @__PURE__ */ new R(), S = /* @__PURE__ */ new R(), V = /* @__PURE__ */ new Et(), g = /* @__PURE__ */ new St(), rt = /* @__PURE__ */ new u(), at = /* @__PURE__ */ new u(), ct = /* @__PURE__ */ new u(), A = /* @__PURE__ */ new u(), q = /* @__PURE__ */ new yt(), vt = 1e-12, Nt = 0.1, At = 0, lt = 1, L = 2; class ft { constructor(t = 1, i = 1, o = 1) { this.name = "", this.radius = new u(t, i, o); } /** * Returns the point where the given ray intersects the ellipsoid surface, or null if no * intersection exists. Writes the result into `target`. * @param {Ray} ray * @param {Vector3} target * @returns {Vector3|null} */ intersectRay(t, i) { return P.makeScale(...this.radius).invert(), V.center.set(0, 0, 0), V.radius = 1, q.copy(t).applyMatrix4(P), q.intersectSphere(V, i) ? (P.makeScale(...this.radius), i.applyMatrix4(P), i) : null; } /** * Returns a Matrix4 representing the East-North-Up (ENU) frame at the given geographic * position: X points east, Y points north, Z points up. Writes the result into `target`. * @param {number} lat Latitude in radians. * @param {number} lon Longitude in radians. * @param {number} height Height above the ellipsoid surface in meters. * @param {Matrix4} target * @returns {Matrix4} */ getEastNorthUpFrame(t, i, o, s) { return o.isMatrix4 && (s = o, o = 0, console.warn('Ellipsoid: The signature for "getEastNorthUpFrame" has changed.')), this.getEastNorthUpAxes(t, i, rt, at, ct), this.getCartographicToPosition(t, i, o, A), s.makeBasis(rt, at, ct).setPosition(A); } /** * Returns a Matrix4 representing the ENU frame at the given position, rotated by the given * azimuth, elevation, and roll. Equivalent to `getObjectFrame` with `ENU_FRAME`. * @param {number} lat Latitude in radians. * @param {number} lon Longitude in radians. * @param {number} height Height above the ellipsoid surface in meters. * @param {number} az Azimuth in radians, measured from true north towards east. * @param {number} el Elevation in radians, measured from the horizon upward. * @param {number} roll Roll in radians around the north axis. * @param {Matrix4} target * @returns {Matrix4} */ getOrientedEastNorthUpFrame(t, i, o, s, e, r, a) { return this.getObjectFrame(t, i, o, s, e, r, a, At); } /** * Returns a Matrix4 representing a frame at the given geographic position, rotated by the * given azimuth, elevation, and roll, and adjusted to match the three.js `frame` convention. * `OBJECT_FRAME` orients with "+Y" up and "+Z" forward; `CAMERA_FRAME` orients with "+Y" up * and "-Z" forward; `ENU_FRAME` returns the raw ENU-relative rotation. * @param {number} lat Latitude in radians. * @param {number} lon Longitude in radians. * @param {number} height Height above the ellipsoid surface in meters. * @param {number} az Azimuth in radians, measured from true north towards east. * @param {number} el Elevation in radians, measured from the horizon upward. * @param {number} roll Roll in radians around the north axis. * @param {Matrix4} target * @param {Frames} [frame=OBJECT_FRAME] * @returns {Matrix4} */ getObjectFrame(t, i, o, s, e, r, a, l = L) { return this.getEastNorthUpFrame(t, i, o, P), g.set(e, r, -s, "ZXY"), a.makeRotationFromEuler(g).premultiply(P), l === lt ? (g.set(Math.PI / 2, 0, 0, "XYZ"), S.makeRotationFromEuler(g), a.multiply(S)) : l === L && (g.set(-Math.PI / 2, 0, Math.PI, "XYZ"), S.makeRotationFromEuler(g), a.multiply(S)), a; } /** * Extracts geographic position and orientation (lat, lon, height, azimuth, elevation, roll) * from the given object/camera frame matrix. The inverse of `getObjectFrame`. Writes the * result into `target` and returns it. * @param {Matrix4} matrix * @param {Object} target * @param {Frames} [frame=OBJECT_FRAME] * @returns {{ lat: number, lon: number, height: number, azimuth: number, elevation: number, roll: number }} */ getCartographicFromObjectFrame(t, i, o = L) { return o === lt ? (g.set(-Math.PI / 2, 0, 0, "XYZ"), S.makeRotationFromEuler(g).premultiply(t)) : o === L ? (g.set(-Math.PI / 2, 0, Math.PI, "XYZ"), S.makeRotationFromEuler(g).premultiply(t)) : S.copy(t), A.setFromMatrixPosition(S), this.getPositionToCartographic(A, i), this.getEastNorthUpFrame(i.lat, i.lon, 0, P).invert(), S.premultiply(P), g.setFromRotationMatrix(S, "ZXY"), i.azimuth = -g.z, i.elevation = g.x, i.roll = g.y, i; } /** * Fills in the east, north, and up unit vectors for the ENU frame at the given latitude and * longitude. Optionally writes the surface position into `point`. * @param {number} lat Latitude in radians. * @param {number} lon Longitude in radians. * @param {Vector3} vecEast * @param {Vector3} vecNorth * @param {Vector3} vecUp * @param {Vector3} [point] */ getEastNorthUpAxes(t, i, o, s, e, r = A) { this.getCartographicToPosition(t, i, 0, r), this.getCartographicToNormal(t, i, e), o.set(-r.y, r.x, 0).normalize(), s.crossVectors(e, o).normalize(); } /** * Converts geographic coordinates to a 3D Cartesian position on the ellipsoid surface * (plus the given height offset). Writes the result into `target` and returns it. * @param {number} lat Latitude in radians. * @param {number} lon Longitude in radians. * @param {number} height Height above the ellipsoid surface in meters. * @param {Vector3} target * @returns {Vector3} */ getCartographicToPosition(t, i, o, s) { this.getCartographicToNormal(t, i, _); const e = this.radius; y.copy(_), y.x *= e.x ** 2, y.y *= e.y ** 2, y.z *= e.z ** 2; const r = Math.sqrt(_.dot(y)); return y.divideScalar(r), s.copy(y).addScaledVector(_, o); } /** * Converts a 3D Cartesian position to geographic coordinates (lat, lon, height). Writes the * result into `target` and returns it. * @param {Vector3} pos * @param {Object} target * @returns {{ lat: number, lon: number, height: number }} */ getPositionToCartographic(t, i) { this.getPositionToSurfacePoint(t, y), this.getPositionToNormal(y, _); const o = Y.subVectors(t, y); return i.lon = Math.atan2(_.y, _.x), i.lat = Math.asin(_.z), i.height = Math.sign(o.dot(t)) * o.length(), i; } /** * Returns the surface normal of the ellipsoid at the given latitude and longitude. Writes the * result into `target` and returns it. * @param {number} lat Latitude in radians. * @param {number} lon Longitude in radians. * @param {Vector3} target * @returns {Vector3} */ getCartographicToNormal(t, i, o) { return nt.set(1, J(t), i), o.setFromSpherical(nt).normalize(), xt(o), o; } /** * Returns the surface normal of the ellipsoid at the given 3D Cartesian position. Writes the * result into `target` and returns it. * @param {Vector3} pos * @param {Vector3} target * @returns {Vector3} */ getPositionToNormal(t, i) { const o = this.radius; return i.copy(t), i.x /= o.x ** 2, i.y /= o.y ** 2, i.z /= o.z ** 2, i.normalize(), i; } /** * Projects the given 3D position onto the ellipsoid surface along the geodetic normal. * Returns null if the position is at or near the center. Writes the result into `target`. * @param {Vector3} pos * @param {Vector3} target * @returns {Vector3|null} */ getPositionToSurfacePoint(t, i) { const o = this.radius, s = 1 / o.x ** 2, e = 1 / o.y ** 2, r = 1 / o.z ** 2, a = t.x * t.x * s, l = t.y * t.y * e, h = t.z * t.z * r, p = a + l + h, w = Math.sqrt(1 / p), M = y.copy(t).multiplyScalar(w); if (p < Nt) return isFinite(w) ? i.copy(M) : null; const E = Y.set( M.x * s * 2, M.y * e * 2, M.z * r * 2 ); let m = (1 - w) * t.length() / (0.5 * E.length()), x = 0, $, K, b, v, N, k, j, X, Q, tt, it; do { m -= x, b = 1 / (1 + m * s), v = 1 / (1 + m * e), N = 1 / (1 + m * r), k = b * b, j = v * v, X = N * N, Q = k * b, tt = j * v, it = X * N, $ = a * k + l * j + h * X - 1, K = a * Q * s + l * tt * e + h * it * r; const Pt = -2 * K; x = $ / Pt; } while (Math.abs($) > vt); return i.set( t.x * b, t.y * v, t.z * N ); } /** * Returns the geometric distance to the horizon from the given latitude and elevation above * the ellipsoid surface. * @param {number} latitude Latitude in degrees. * @param {number} elevation Height above the ellipsoid surface in meters. * @returns {number} */ calculateHorizonDistance(t, i) { const o = this.calculateEffectiveRadius(t); return Math.sqrt(2 * o * i + i ** 2); } /** * Returns the prime vertical radius of curvature (distance from the center of the ellipsoid * to the surface along the normal) at the given latitude. * @param {number} latitude Latitude in degrees. * @returns {number} */ calculateEffectiveRadius(t) { const i = this.radius.x, s = 1 - this.radius.z ** 2 / i ** 2, e = t * D.DEG2RAD, r = Math.sin(e) ** 2; return i / Math.sqrt(1 - s * r); } /** * Returns the height of the given 3D position above (or below) the ellipsoid surface. * @param {Vector3} pos * @returns {number} */ getPositionElevation(t) { this.getPositionToSurfacePoint(t, y); const i = Y.subVectors(t, y); return Math.sign(i.dot(t)) * i.length(); } /** * Returns an estimate of the closest point on the ellipsoid surface to the given ray. * Returns the exact surface intersection point if the ray intersects the ellipsoid. * @param {Ray} ray * @param {Vector3} target * @returns {Vector3} */ closestPointToRayEstimate(t, i) { return this.intersectRay(t, i) ? i : (P.makeScale(...this.radius).invert(), q.copy(t).applyMatrix4(P), y.set(0, 0, 0), q.closestPointToPoint(y, i).normalize(), P.makeScale(...this.radius), i.applyMatrix4(P)); } /** * Copies the radius from the given ellipsoid into this one. * @param {Ellipsoid} source * @returns {this} */ copy(t) { return this.radius.copy(t.radius), this; } /** * Returns a new Ellipsoid with the same radius as this one. * @returns {Ellipsoid} */ clone() { return new this.constructor().copy(this); } } const Bt = new ft(ot, ot, Tt); Bt.name = "WGS84 Earth"; const U = /* @__PURE__ */ new u(), O = /* @__PURE__ */ new u(), f = /* @__PURE__ */ new u(), G = /* @__PURE__ */ new yt(); class Gt { constructor(t = new dt(), i = new R()) { this.box = t.clone(), this.transform = i.clone(), this.inverseTransform = new R(), this.points = new Array(8).fill().map(() => new u()), this.planes = new Array(6).fill().map(() => new wt()); } copy(t) { return this.box.copy(t.box), this.transform.copy(t.transform), this.update(), this; } clone() { return new this.constructor().copy(this); } /** * Clamps the given point within the bounds of this OBB * @param {Vector3} point * @param {Vector3} result * @returns {Vector3} */ clampPoint(t, i) { return i.copy(t).applyMatrix4(this.inverseTransform).clamp(this.box.min, this.box.max).applyMatrix4(this.transform); } /** * Returns the distance from any edge of this OBB to the specified point. * If the point lies inside of this box, the distance will be 0. * @param {Vector3} point * @returns {number} */ distanceToPoint(t) { return this.clampPoint(t, f).distanceTo(t); } containsPoint(t) { return f.copy(t).applyMatrix4(this.inverseTransform), this.box.containsPoint(f); } // returns boolean indicating whether the ray has intersected the obb intersectsRay(t) { return G.copy(t).applyMatrix4(this.inverseTransform), G.intersectsBox(this.box); } // Sets "target" equal to the intersection point. // Returns "null" if no intersection found. intersectRay(t, i) { return G.copy(t).applyMatrix4(this.inverseTransform), G.intersectBox(this.box, i) ? (i.applyMatrix4(this.transform), i) : null; } update() { const { points: t, inverseTransform: i, transform: o, box: s } = this; i.copy(o).invert(); const { min: e, max: r } = s; let a = 0; for (let l = -1; l <= 1; l += 2) for (let h = -1; h <= 1; h += 2) for (let p = -1; p <= 1; p += 2) t[a].set( l < 0 ? e.x : r.x, h < 0 ? e.y : r.y, p < 0 ? e.z : r.z ).applyMatrix4(o), a++; this.updatePlanes(); } updatePlanes() { U.copy(this.box.min).applyMatrix4(this.transform), O.copy(this.box.max).applyMatrix4(this.transform), f.set(0, 0, 1).transformDirection(this.transform), this.planes[0].setFromNormalAndCoplanarPoint(f, U), this.planes[1].setFromNormalAndCoplanarPoint(f, O).negate(), f.set(0, 1, 0).transformDirection(this.transform), this.planes[2].setFromNormalAndCoplanarPoint(f, U), this.planes[3].setFromNormalAndCoplanarPoint(f, O).negate(), f.set(1, 0, 0).transformDirection(this.transform), this.planes[4].setFromNormalAndCoplanarPoint(f, U), this.planes[5].setFromNormalAndCoplanarPoint(f, O).negate(); } intersectsSphere(t) { return this.clampPoint(t.center, f), f.distanceToSquared(t.center) <= t.radius * t.radius; } intersectsFrustum(t) { return this._intersectsPlaneShape(t.planes, t.points); } intersectsOBB(t) { return this._intersectsPlaneShape(t.planes, t.points); } // takes a series of 6 planes that define and enclosed shape and the 8 points that lie at the corners // of that shape to determine whether the OBB is intersected with. _intersectsPlaneShape(t, i) { const o = this.points, s = this.planes; for (let e = 0; e < 6; e++) { const r = t[e]; let a = -1 / 0; for (let l = 0; l < 8; l++) { const h = o[l], p = r.distanceToPoint(h); a = a < p ? p : a; } if (a < 0) return !1; } for (let e = 0; e < 6; e++) { const r = s[e]; let a = -1 / 0; for (let l = 0; l < 8; l++) { const h = i[l], p = r.distanceToPoint(h); a = a < p ? p : a; } if (a < 0) return !1; } return !0; } } const W = 1e-13, I = Math.PI, Z = I / 2, B = /* @__PURE__ */ new u(), C = /* @__PURE__ */ new u(), T = /* @__PURE__ */ new u(), n = /* @__PURE__ */ new u(), d = /* @__PURE__ */ new R(), Dt = /* @__PURE__ */ new dt(), ht = /* @__PURE__ */ new R(); function z(c, t) { t.radius = Math.max(t.radius, c.distanceToSquared(t.center)); } function pt(c) { return c.x !== c.y; } class $t extends ft { constructor(t = 1, i = 1, o = 1, s = -Z, e = Z, r = 0, a = 2 * I, l = 0, h = 0) { super(t, i, o), this.latStart = s, this.latEnd = e, this.lonStart = r, this.lonEnd = a, this.heightStart = l, this.heightEnd = h; } /** * Computes an oriented bounding box for this region. Writes the box extents into `box` and * the orientation frame into `matrix`. * @param {Box3} box * @param {Matrix4} matrix */ getBoundingBox(t, i) { pt(this.radius) && console.warn("EllipsoidRegion: Triaxial ellipsoids are not supported."); const { latStart: o, latEnd: s, lonStart: e, lonEnd: r, heightStart: a, heightEnd: l } = this, h = (o + s) * 0.5, p = (e + r) * 0.5, w = o > 0, M = s < 0; let E; w ? E = o : M ? E = s : E = 0; const { min: m, max: x } = t; m.setScalar(1 / 0), x.setScalar(-1 / 0), r - e <= I ? (this.getCartographicToNormal(h, p, T), C.set(0, 0, 1), B.crossVectors(C, T).normalize(), C.crossVectors(T, B).normalize(), i.makeBasis(B, C, T), d.copy(i).invert(), this.getCartographicToPosition(E, e, l, n).applyMatrix4(d), x.x = Math.abs(n.x), m.x = -x.x, this.getCartographicToPosition(s, e, l, n).applyMatrix4(d), x.y = n.y, this.getCartographicToPosition(s, p, l, n).applyMatrix4(d), x.y = Math.max(n.y, x.y), this.getCartographicToPosition(o, e, l, n).applyMatrix4(d), m.y = n.y, this.getCartographicToPosition(o, p, l, n).applyMatrix4(d), m.y = Math.min(n.y, m.y), this.getCartographicToPosition(h, p, l, n).applyMatrix4(d), x.z = n.z, this.getCartographicToPosition(o, e, a, n).applyMatrix4(d), m.z = n.z, this.getCartographicToPosition(s, e, a, n).applyMatrix4(d), m.z = Math.min(n.z, m.z)) : (this.getCartographicToPosition(E, p, l, T), T.z = 0, T.length() < 1e-10 ? T.set(1, 0, 0) : T.normalize(), C.set(0, 0, 1), B.crossVectors(T, C).normalize(), i.makeBasis(B, C, T), d.copy(i).invert(), this.getCartographicToPosition(E, p + Z, l, n).applyMatrix4(d), x.x = Math.abs(n.x), m.x = -x.x, this.getCartographicToPosition(s, 0, M ? a : l, n).applyMatrix4(d), x.y = n.y, this.getCartographicToPosition(o, 0, w ? a : l, n).applyMatrix4(d), m.y = n.y, this.getCartographicToPosition(E, p, l, n).applyMatrix4(d), x.z = n.z, this.getCartographicToPosition(E, r, l, n).applyMatrix4(d), m.z = n.z), t.getCenter(n), t.min.sub(n).multiplyScalar(1 + W), t.max.sub(n).multiplyScalar(1 + W), n.applyMatrix4(i), i.setPosition(n); } /** * Computes a bounding sphere for this region. Writes the result into `sphere`. * @param {Sphere} sphere */ getBoundingSphere(t) { pt(this.radius) && console.warn("EllipsoidRegion: Triaxial ellipsoids are not supported."), this.getBoundingBox(Dt, ht), t.center.setFromMatrixPosition(ht), t.radius = 0; const { latStart: i, latEnd: o, lonStart: s, lonEnd: e, heightStart: r, heightEnd: a } = this, l = (i + o) * 0.5, h = (s + e) * 0.5, p = i > 0, w = o < 0; let M; p ? M = i : w ? M = o : M = 0, this.getCartographicToPosition(M, s, a, n), z(n, t), this.getCartographicToPosition(o, s, a, n), z(n, t), this.getCartographicToPosition(o, h, a, n), z(n, t), this.getCartographicToPosition(i, s, a, n), z(n, t), this.getCartographicToPosition(i, h, a, n), z(n, t), this.getCartographicToPosition(l, h, a, n), z(n, t), this.getCartographicToPosition(i, s, r, n), z(n, t), e - s > I && (this.getCartographicToPosition(M, h + I, a, n), z(n, t)), t.radius = Math.sqrt(t.radius) * (1 + W); } } const H = 0; function ut(c, t, i, o) { try { return _t.getByteLength(c, t, i, o); } catch { return H; } } function Mt(c) { var r, a; if (!c) return 0; if (c.isExternalTexture) return ((r = c.userData) == null ? void 0 : r.byteLength) ?? H; const { format: t, type: i, image: o, mipmaps: s } = c; if (c.isCompressedTexture && Array.isArray(s) && s.length > 0) { let l = 0; for (const h of s) (a = h == null ? void 0 : h.data) != null && a.byteLength ? l += h.data.byteLength : l += ut(h.width, h.height, t, i); return l; } if (!o) return H; let e = ut(o.width, o.height, t, i); return e *= c.generateMipmaps ? 4 / 3 : 1, e; } function It(c) { const t = /* @__PURE__ */ new Set(); let i = 0; return c.traverse((o) => { if (o.geometry && !t.has(o.geometry) && (i += zt(o.geometry), t.add(o.geometry)), o.material) { const s = o.material; for (const e in s) { const r = s[e]; r && r.isTexture && !t.has(r) && (i += Mt(r), t.add(r)); } } }), i; } const kt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, estimateBytesUsed: It, getTextureByteLength: Mt }, Symbol.toStringTag, { value: "Module" })); export { lt as C, At as E, Ot as G, kt as M, Gt as O, Bt as W, ft as a, $t as b, L as c, It as e, Mt as g }; //# sourceMappingURL=MemoryUtils-ZzXvtRjr.js.map