@takram/three-geospatial 0.0.1-alpha.1
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/README.md +15 -0
- package/build/index.cjs +43 -0
- package/build/index.js +932 -0
- package/build/r3f.cjs +1 -0
- package/build/r3f.js +38 -0
- package/build/shared.cjs +1 -0
- package/build/shared.js +198 -0
- package/package.json +42 -0
- package/src/ArrayBufferLoader.ts +35 -0
- package/src/DataLoader.ts +114 -0
- package/src/Ellipsoid.ts +128 -0
- package/src/EllipsoidGeometry.ts +107 -0
- package/src/Geodetic.ts +160 -0
- package/src/PointOfView.ts +169 -0
- package/src/Rectangle.ts +97 -0
- package/src/TileCoordinate.test.ts +38 -0
- package/src/TileCoordinate.ts +112 -0
- package/src/TilingScheme.test.ts +63 -0
- package/src/TilingScheme.ts +76 -0
- package/src/TypedArrayLoader.ts +53 -0
- package/src/assertions.ts +13 -0
- package/src/bufferGeometry.ts +62 -0
- package/src/helpers/projectOnEllipsoidSurface.ts +72 -0
- package/src/index.ts +25 -0
- package/src/math.ts +41 -0
- package/src/r3f/EastNorthUpFrame.tsx +52 -0
- package/src/r3f/EllipsoidMesh.tsx +36 -0
- package/src/r3f/index.ts +2 -0
- package/src/shaders/depth.glsl +15 -0
- package/src/shaders/packing.glsl +20 -0
- package/src/shaders/transform.glsl +12 -0
- package/src/typedArray.ts +76 -0
- package/src/types.ts +54 -0
- package/types/ArrayBufferLoader.d.ts +5 -0
- package/types/DataLoader.d.ts +67 -0
- package/types/Ellipsoid.d.ts +18 -0
- package/types/EllipsoidGeometry.d.ts +13 -0
- package/types/Geodetic.d.ts +37 -0
- package/types/PointOfView.d.ts +20 -0
- package/types/Rectangle.d.ts +27 -0
- package/types/TileCoordinate.d.ts +21 -0
- package/types/TilingScheme.d.ts +21 -0
- package/types/TypedArrayLoader.d.ts +16 -0
- package/types/assertions.d.ts +4 -0
- package/types/bufferGeometry.d.ts +5 -0
- package/types/helpers/projectOnEllipsoidSurface.d.ts +6 -0
- package/types/index.d.ts +18 -0
- package/types/math.d.ts +13 -0
- package/types/r3f/EastNorthUpFrame.d.ts +15 -0
- package/types/r3f/EllipsoidMesh.d.ts +13 -0
- package/types/r3f/index.d.ts +2 -0
- package/types/typedArray.d.ts +10 -0
- package/types/types.d.ts +22 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
import { Vector2 } from 'three'
|
2
|
+
|
3
|
+
import { type GeodeticLike } from './Geodetic'
|
4
|
+
import { Rectangle, type RectangleLike } from './Rectangle'
|
5
|
+
import { TileCoordinate, type TileCoordinateLike } from './TileCoordinate'
|
6
|
+
|
7
|
+
const vectorScratch = /*#__PURE__*/ new Vector2()
|
8
|
+
|
9
|
+
export interface TilingSchemeLike {
|
10
|
+
readonly width: number
|
11
|
+
readonly height: number
|
12
|
+
readonly rectangle: RectangleLike
|
13
|
+
}
|
14
|
+
|
15
|
+
// TODO: Support slippyMap and EPSG:3857
|
16
|
+
export class TilingScheme {
|
17
|
+
constructor(
|
18
|
+
public width = 2,
|
19
|
+
public height = 1,
|
20
|
+
public rectangle = Rectangle.MAX
|
21
|
+
) {}
|
22
|
+
|
23
|
+
clone(): TilingScheme {
|
24
|
+
return new TilingScheme(this.width, this.height, this.rectangle.clone())
|
25
|
+
}
|
26
|
+
|
27
|
+
copy(other: TilingSchemeLike): this {
|
28
|
+
this.width = other.width
|
29
|
+
this.height = other.height
|
30
|
+
this.rectangle.copy(other.rectangle)
|
31
|
+
return this
|
32
|
+
}
|
33
|
+
|
34
|
+
getSize(z: number, result = new Vector2()): Vector2 {
|
35
|
+
return result.set(this.width << z, this.height << z)
|
36
|
+
}
|
37
|
+
|
38
|
+
// Reference: https://github.com/CesiumGS/cesium/blob/1.122/packages/engine/Source/Core/GeographicTilingScheme.js#L210
|
39
|
+
getTile(
|
40
|
+
geodetic: GeodeticLike,
|
41
|
+
z: number,
|
42
|
+
result = new TileCoordinate()
|
43
|
+
): TileCoordinate {
|
44
|
+
const size = this.getSize(z, vectorScratch)
|
45
|
+
const width = this.rectangle.width / size.x
|
46
|
+
const height = this.rectangle.height / size.y
|
47
|
+
let longitude = geodetic.longitude
|
48
|
+
if (this.rectangle.east < this.rectangle.west) {
|
49
|
+
longitude += Math.PI * 2
|
50
|
+
}
|
51
|
+
let x = Math.floor((longitude - this.rectangle.west) / width)
|
52
|
+
if (x >= size.x) {
|
53
|
+
x = size.x - 1
|
54
|
+
}
|
55
|
+
let y = Math.floor((geodetic.latitude - this.rectangle.south) / height)
|
56
|
+
if (y >= size.y) {
|
57
|
+
y = size.y - 1
|
58
|
+
}
|
59
|
+
result.x = x
|
60
|
+
result.y = y
|
61
|
+
result.z = z
|
62
|
+
return result
|
63
|
+
}
|
64
|
+
|
65
|
+
// Reference: https://github.com/CesiumGS/cesium/blob/1.122/packages/engine/Source/Core/GeographicTilingScheme.js#L169
|
66
|
+
getRectangle(tile: TileCoordinateLike, result = new Rectangle()): Rectangle {
|
67
|
+
const size = this.getSize(tile.z, vectorScratch)
|
68
|
+
const width = this.rectangle.width / size.x
|
69
|
+
const height = this.rectangle.height / size.y
|
70
|
+
result.west = tile.x * width + this.rectangle.west
|
71
|
+
result.east = (tile.x + 1) * width + this.rectangle.west
|
72
|
+
result.north = this.rectangle.north - (size.y - tile.y - 1) * height
|
73
|
+
result.south = this.rectangle.north - (size.y - tile.y) * height
|
74
|
+
return result
|
75
|
+
}
|
76
|
+
}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import { Loader, type TypedArray } from 'three'
|
2
|
+
|
3
|
+
import { ArrayBufferLoader } from './ArrayBufferLoader'
|
4
|
+
import {
|
5
|
+
parseFloat32Array,
|
6
|
+
parseInt16Array,
|
7
|
+
parseUint16Array
|
8
|
+
} from './typedArray'
|
9
|
+
|
10
|
+
export abstract class TypedArrayLoader<T extends TypedArray> extends Loader<T> {
|
11
|
+
abstract parseTypedArray(buffer: ArrayBuffer): T
|
12
|
+
|
13
|
+
override load(
|
14
|
+
url: string,
|
15
|
+
onLoad: (data: T) => void,
|
16
|
+
onProgress?: (event: ProgressEvent) => void,
|
17
|
+
onError?: (error: unknown) => void
|
18
|
+
): void {
|
19
|
+
const loader = new ArrayBufferLoader(this.manager)
|
20
|
+
loader.setRequestHeader(this.requestHeader)
|
21
|
+
loader.setPath(this.path)
|
22
|
+
loader.setWithCredentials(this.withCredentials)
|
23
|
+
loader.load(
|
24
|
+
url,
|
25
|
+
arrayBuffer => {
|
26
|
+
try {
|
27
|
+
onLoad(this.parseTypedArray(arrayBuffer))
|
28
|
+
} catch (error) {
|
29
|
+
if (onError != null) {
|
30
|
+
onError(error)
|
31
|
+
} else {
|
32
|
+
console.error(error)
|
33
|
+
}
|
34
|
+
this.manager.itemError(url)
|
35
|
+
}
|
36
|
+
},
|
37
|
+
onProgress,
|
38
|
+
onError
|
39
|
+
)
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
export class Int16ArrayLoader extends TypedArrayLoader<Int16Array> {
|
44
|
+
readonly parseTypedArray = parseInt16Array
|
45
|
+
}
|
46
|
+
|
47
|
+
export class Uint16ArrayLoader extends TypedArrayLoader<Uint16Array> {
|
48
|
+
readonly parseTypedArray = parseUint16Array
|
49
|
+
}
|
50
|
+
|
51
|
+
export class Float32ArrayLoader extends TypedArrayLoader<Float32Array> {
|
52
|
+
readonly parseTypedArray = parseFloat32Array
|
53
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
export function assertType<T>(value: unknown): asserts value is T {}
|
2
|
+
|
3
|
+
export function isNotNullish<T>(value: T | null | undefined): value is T {
|
4
|
+
return value != null
|
5
|
+
}
|
6
|
+
|
7
|
+
export function isNotUndefined<T>(value: T | undefined): value is T {
|
8
|
+
return value !== undefined
|
9
|
+
}
|
10
|
+
|
11
|
+
export function isNotFalse<T>(value: T | false): value is T {
|
12
|
+
return value !== false
|
13
|
+
}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import { pick } from 'lodash-es'
|
2
|
+
import { Box3, BufferAttribute, BufferGeometry, Sphere, Vector3 } from 'three'
|
3
|
+
|
4
|
+
import { isNotNullish } from './assertions'
|
5
|
+
|
6
|
+
export type BufferGeometryLike = Pick<
|
7
|
+
BufferGeometry,
|
8
|
+
'attributes' | 'index' | 'boundingBox' | 'boundingSphere'
|
9
|
+
>
|
10
|
+
|
11
|
+
export function toBufferGeometryLike(
|
12
|
+
geometry: BufferGeometry
|
13
|
+
): [BufferGeometryLike, ArrayBuffer[]] {
|
14
|
+
return [
|
15
|
+
pick(geometry, ['attributes', 'index', 'boundingBox', 'boundingSphere']),
|
16
|
+
[
|
17
|
+
...Object.values(geometry.attributes).map(
|
18
|
+
attribute => attribute.array.buffer
|
19
|
+
),
|
20
|
+
geometry.index?.array.buffer
|
21
|
+
].filter(isNotNullish)
|
22
|
+
]
|
23
|
+
}
|
24
|
+
|
25
|
+
export function fromBufferGeometryLike(
|
26
|
+
input: BufferGeometryLike,
|
27
|
+
result = new BufferGeometry()
|
28
|
+
): BufferGeometry {
|
29
|
+
for (const [name, attribute] of Object.entries(input.attributes)) {
|
30
|
+
result.setAttribute(
|
31
|
+
name,
|
32
|
+
new BufferAttribute(
|
33
|
+
attribute.array,
|
34
|
+
attribute.itemSize,
|
35
|
+
attribute.normalized
|
36
|
+
)
|
37
|
+
)
|
38
|
+
}
|
39
|
+
result.index =
|
40
|
+
input.index != null
|
41
|
+
? new BufferAttribute(
|
42
|
+
input.index.array,
|
43
|
+
input.index.itemSize,
|
44
|
+
input.index.normalized
|
45
|
+
)
|
46
|
+
: null
|
47
|
+
if (input.boundingBox != null) {
|
48
|
+
const { min, max } = input.boundingBox
|
49
|
+
result.boundingBox = new Box3(
|
50
|
+
new Vector3(min.x, min.y, min.z),
|
51
|
+
new Vector3(max.x, max.y, max.z)
|
52
|
+
)
|
53
|
+
}
|
54
|
+
if (input.boundingSphere != null) {
|
55
|
+
const { center, radius } = input.boundingSphere
|
56
|
+
result.boundingSphere = new Sphere(
|
57
|
+
new Vector3(center.x, center.y, center.z),
|
58
|
+
radius
|
59
|
+
)
|
60
|
+
}
|
61
|
+
return result
|
62
|
+
}
|
@@ -0,0 +1,72 @@
|
|
1
|
+
import { Vector3 } from 'three'
|
2
|
+
|
3
|
+
const vectorScratch = /*#__PURE__*/ new Vector3()
|
4
|
+
|
5
|
+
// See: https://en.wikipedia.org/wiki/Geographic_coordinate_conversion
|
6
|
+
// Reference: https://github.com/CesiumGS/cesium/blob/1.122/packages/engine/Source/Core/scaleToGeodeticSurface.js
|
7
|
+
|
8
|
+
export interface ProjectOnEllipsoidSurfaceOptions {
|
9
|
+
centerTolerance?: number
|
10
|
+
}
|
11
|
+
|
12
|
+
export function projectOnEllipsoidSurface(
|
13
|
+
position: Vector3,
|
14
|
+
reciprocalRadiiSquared: Vector3,
|
15
|
+
result = new Vector3(),
|
16
|
+
options?: ProjectOnEllipsoidSurfaceOptions
|
17
|
+
): Vector3 | undefined {
|
18
|
+
const { x, y, z } = position
|
19
|
+
const rx = reciprocalRadiiSquared.x
|
20
|
+
const ry = reciprocalRadiiSquared.y
|
21
|
+
const rz = reciprocalRadiiSquared.z
|
22
|
+
const x2 = x * x * rx
|
23
|
+
const y2 = y * y * ry
|
24
|
+
const z2 = z * z * rz
|
25
|
+
|
26
|
+
// Compute the squared ellipsoid norm.
|
27
|
+
const normSquared = x2 + y2 + z2
|
28
|
+
const ratio = Math.sqrt(1 / normSquared)
|
29
|
+
|
30
|
+
// When very close to center or at center.
|
31
|
+
if (!Number.isFinite(ratio)) {
|
32
|
+
return undefined
|
33
|
+
}
|
34
|
+
|
35
|
+
// As an initial approximation, assume that the radial intersection is the
|
36
|
+
// projection point.
|
37
|
+
const intersection = vectorScratch.copy(position).multiplyScalar(ratio)
|
38
|
+
if (normSquared < (options?.centerTolerance ?? 0.1)) {
|
39
|
+
return result.copy(intersection)
|
40
|
+
}
|
41
|
+
|
42
|
+
// Use the gradient at the intersection point in place of the true unit
|
43
|
+
// normal. The difference in magnitude will be absorbed in the multiplier.
|
44
|
+
const gradient = intersection
|
45
|
+
.multiply(reciprocalRadiiSquared)
|
46
|
+
.multiplyScalar(2)
|
47
|
+
|
48
|
+
// Compute the initial guess at the normal vector multiplier.
|
49
|
+
let lambda = ((1 - ratio) * position.length()) / (gradient.length() / 2)
|
50
|
+
|
51
|
+
let correction = 0
|
52
|
+
let sx: number
|
53
|
+
let sy: number
|
54
|
+
let sz: number
|
55
|
+
let error: number
|
56
|
+
do {
|
57
|
+
lambda -= correction
|
58
|
+
sx = 1 / (1 + lambda * rx)
|
59
|
+
sy = 1 / (1 + lambda * ry)
|
60
|
+
sz = 1 / (1 + lambda * rz)
|
61
|
+
const sx2 = sx * sx
|
62
|
+
const sy2 = sy * sy
|
63
|
+
const sz2 = sz * sz
|
64
|
+
const sx3 = sx2 * sx
|
65
|
+
const sy3 = sy2 * sy
|
66
|
+
const sz3 = sz2 * sz
|
67
|
+
error = x2 * sx2 + y2 * sy2 + z2 * sz2 - 1
|
68
|
+
correction = error / ((x2 * sx3 * rx + y2 * sy3 * ry + z2 * sz3 * rz) * -2)
|
69
|
+
} while (Math.abs(error) > 1e-12)
|
70
|
+
|
71
|
+
return result.set(x * sx, y * sy, z * sz)
|
72
|
+
}
|
package/src/index.ts
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
/// <reference types="vite-plugin-glsl/ext" />
|
2
|
+
|
3
|
+
import depth from './shaders/depth.glsl'
|
4
|
+
import packing from './shaders/packing.glsl'
|
5
|
+
import transform from './shaders/transform.glsl'
|
6
|
+
|
7
|
+
export const depthShader: string = depth
|
8
|
+
export const packingShader: string = packing
|
9
|
+
export const transformShader: string = transform
|
10
|
+
|
11
|
+
export * from './ArrayBufferLoader'
|
12
|
+
export * from './assertions'
|
13
|
+
export * from './bufferGeometry'
|
14
|
+
export * from './DataLoader'
|
15
|
+
export * from './Ellipsoid'
|
16
|
+
export * from './EllipsoidGeometry'
|
17
|
+
export * from './Geodetic'
|
18
|
+
export * from './math'
|
19
|
+
export * from './PointOfView'
|
20
|
+
export * from './Rectangle'
|
21
|
+
export * from './TileCoordinate'
|
22
|
+
export * from './TilingScheme'
|
23
|
+
export * from './typedArray'
|
24
|
+
export * from './TypedArrayLoader'
|
25
|
+
export * from './types'
|
package/src/math.ts
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
import { MathUtils } from 'three'
|
2
|
+
|
3
|
+
export const clamp = MathUtils.clamp
|
4
|
+
export const euclideanModulo = MathUtils.euclideanModulo
|
5
|
+
export const inverseLerp = MathUtils.inverseLerp
|
6
|
+
export const lerp = MathUtils.lerp
|
7
|
+
export const radians = MathUtils.degToRad
|
8
|
+
export const degrees = MathUtils.radToDeg
|
9
|
+
export const isPowerOfTwo = MathUtils.isPowerOfTwo
|
10
|
+
export const ceilPowerOfTwo = MathUtils.ceilPowerOfTwo
|
11
|
+
export const floorPowerOfTwo = MathUtils.floorPowerOfTwo
|
12
|
+
export const normalize = MathUtils.normalize
|
13
|
+
|
14
|
+
// Prefer glsl's argument order which differs from that of MathUtils.
|
15
|
+
export function smoothstep(min: number, max: number, x: number): number {
|
16
|
+
if (x <= min) {
|
17
|
+
return 0
|
18
|
+
}
|
19
|
+
if (x >= max) {
|
20
|
+
return 1
|
21
|
+
}
|
22
|
+
x = (x - min) / (max - min)
|
23
|
+
return x * x * (3 - 2 * x)
|
24
|
+
}
|
25
|
+
|
26
|
+
export function saturate(x: number): number {
|
27
|
+
return Math.min(Math.max(x, 0), 1)
|
28
|
+
}
|
29
|
+
|
30
|
+
export function closeTo(
|
31
|
+
a: number,
|
32
|
+
b: number,
|
33
|
+
relativeEpsilon: number,
|
34
|
+
absoluteEpsilon = relativeEpsilon
|
35
|
+
): boolean {
|
36
|
+
const diff = Math.abs(a - b)
|
37
|
+
return (
|
38
|
+
diff <= absoluteEpsilon ||
|
39
|
+
diff <= relativeEpsilon * Math.max(Math.abs(a), Math.abs(b))
|
40
|
+
)
|
41
|
+
}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import { forwardRef, useEffect, useMemo, type ReactNode } from 'react'
|
2
|
+
import { Group, Matrix4, Vector3 } from 'three'
|
3
|
+
import { type SetOptional } from 'type-fest'
|
4
|
+
|
5
|
+
import { Ellipsoid } from '../Ellipsoid'
|
6
|
+
import { Geodetic, type GeodeticLike } from '../Geodetic'
|
7
|
+
|
8
|
+
const matrixScratch = /*#__PURE__*/ new Matrix4()
|
9
|
+
const geodeticScratch = /*#__PURE__*/ new Geodetic()
|
10
|
+
const vectorScratch = /*#__PURE__*/ new Vector3()
|
11
|
+
|
12
|
+
class EastNorthUpFrameGroup extends Group {
|
13
|
+
set(
|
14
|
+
longitude: number,
|
15
|
+
latitude: number,
|
16
|
+
height: number,
|
17
|
+
ellipsoid = Ellipsoid.WGS84
|
18
|
+
): void {
|
19
|
+
// TODO: Support nesting
|
20
|
+
const position = geodeticScratch
|
21
|
+
.set(longitude, latitude, height)
|
22
|
+
.toECEF(vectorScratch)
|
23
|
+
const matrix = ellipsoid.getEastNorthUpFrame(position, matrixScratch)
|
24
|
+
matrix.decompose(this.position, this.quaternion, this.scale)
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
export interface EastNorthUpFrameProps
|
29
|
+
extends SetOptional<GeodeticLike, 'height'> {
|
30
|
+
ellipsoid?: Ellipsoid
|
31
|
+
children?: ReactNode
|
32
|
+
}
|
33
|
+
|
34
|
+
export const EastNorthUpFrame = /*#__PURE__*/ forwardRef<
|
35
|
+
EastNorthUpFrameGroup,
|
36
|
+
EastNorthUpFrameProps
|
37
|
+
>(function EastNorthUpFrame(
|
38
|
+
{ longitude, latitude, height = 0, ellipsoid = Ellipsoid.WGS84, children },
|
39
|
+
forwardedRef
|
40
|
+
) {
|
41
|
+
const group = useMemo(() => new EastNorthUpFrameGroup(), [])
|
42
|
+
|
43
|
+
useEffect(() => {
|
44
|
+
group.set(longitude, latitude, height, ellipsoid)
|
45
|
+
}, [longitude, latitude, height, ellipsoid, group])
|
46
|
+
|
47
|
+
return (
|
48
|
+
<primitive ref={forwardedRef} object={group}>
|
49
|
+
{children}
|
50
|
+
</primitive>
|
51
|
+
)
|
52
|
+
})
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import {
|
2
|
+
extend,
|
3
|
+
type BufferGeometryNode,
|
4
|
+
type MeshProps
|
5
|
+
} from '@react-three/fiber'
|
6
|
+
import { forwardRef, useRef } from 'react'
|
7
|
+
import { mergeRefs } from 'react-merge-refs'
|
8
|
+
import { type Mesh } from 'three'
|
9
|
+
|
10
|
+
import { EllipsoidGeometry } from '../EllipsoidGeometry'
|
11
|
+
|
12
|
+
declare module '@react-three/fiber' {
|
13
|
+
interface ThreeElements {
|
14
|
+
ellipsoidGeometry: BufferGeometryNode<
|
15
|
+
EllipsoidGeometry,
|
16
|
+
typeof EllipsoidGeometry
|
17
|
+
>
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
export interface EllipsoidMeshProps extends Omit<MeshProps, 'args'> {
|
22
|
+
args?: ConstructorParameters<typeof EllipsoidGeometry>
|
23
|
+
}
|
24
|
+
|
25
|
+
export const EllipsoidMesh = /*#__PURE__*/ forwardRef<Mesh, EllipsoidMeshProps>(
|
26
|
+
function Ellipsoid({ args, children, ...props }, forwardedRef) {
|
27
|
+
const ref = useRef<Mesh | null>(null)
|
28
|
+
extend({ EllipsoidGeometry })
|
29
|
+
return (
|
30
|
+
<mesh ref={mergeRefs([ref, forwardedRef])} {...props}>
|
31
|
+
<ellipsoidGeometry args={args} />
|
32
|
+
{children}
|
33
|
+
</mesh>
|
34
|
+
)
|
35
|
+
}
|
36
|
+
)
|
package/src/r3f/index.ts
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
float reverseLogDepth(const float depth, const float near, const float far) {
|
2
|
+
#ifdef USE_LOGDEPTHBUF
|
3
|
+
float d = pow(2.0, depth * log2(far + 1.0)) - 1.0;
|
4
|
+
float a = far / (far - near);
|
5
|
+
float b = far * near / (near - far);
|
6
|
+
return a + b / d;
|
7
|
+
#else
|
8
|
+
return depth;
|
9
|
+
#endif // USE_LOGDEPTHBUF
|
10
|
+
}
|
11
|
+
|
12
|
+
float linearizeDepth(const float depth, const float near, const float far) {
|
13
|
+
float ndc = depth * 2.0 - 1.0;
|
14
|
+
return 2.0 * near * far / (far + near - ndc * (far - near));
|
15
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
// Reference: https://jcgt.org/published/0003/02/01/paper.pdf
|
2
|
+
|
3
|
+
vec2 signNotZero(vec2 v) {
|
4
|
+
return vec2(v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0);
|
5
|
+
}
|
6
|
+
|
7
|
+
vec2 packNormalToVec2(vec3 v) {
|
8
|
+
vec2 p = v.xy * (1.0 / (abs(v.x) + abs(v.y) + abs(v.z)));
|
9
|
+
return v.z <= 0.0
|
10
|
+
? (1.0 - abs(p.yx)) * signNotZero(p)
|
11
|
+
: p;
|
12
|
+
}
|
13
|
+
|
14
|
+
vec3 unpackVec2ToNormal(vec2 e) {
|
15
|
+
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
|
16
|
+
if (v.z < 0.0) {
|
17
|
+
v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy);
|
18
|
+
}
|
19
|
+
return normalize(v);
|
20
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
vec3 screenToView(
|
2
|
+
const vec2 uv,
|
3
|
+
const float depth,
|
4
|
+
const float viewZ,
|
5
|
+
const mat4 projectionMatrix,
|
6
|
+
const mat4 inverseProjectionMatrix
|
7
|
+
) {
|
8
|
+
vec4 clip = vec4(vec3(uv, depth) * 2.0 - 1.0, 1.0);
|
9
|
+
float clipW = projectionMatrix[2][3] * viewZ + projectionMatrix[3][3];
|
10
|
+
clip *= clipW;
|
11
|
+
return (inverseProjectionMatrix * clip).xyz;
|
12
|
+
}
|
@@ -0,0 +1,76 @@
|
|
1
|
+
export type TypedArray =
|
2
|
+
| Int8Array
|
3
|
+
| Uint8Array
|
4
|
+
| Uint8ClampedArray
|
5
|
+
| Int16Array
|
6
|
+
| Uint16Array
|
7
|
+
| Int32Array
|
8
|
+
| Uint32Array
|
9
|
+
| Float32Array
|
10
|
+
| Float64Array
|
11
|
+
|
12
|
+
export type TypedArrayConstructor =
|
13
|
+
| Int8ArrayConstructor
|
14
|
+
| Uint8ArrayConstructor
|
15
|
+
| Uint8ClampedArrayConstructor
|
16
|
+
| Int16ArrayConstructor
|
17
|
+
| Uint16ArrayConstructor
|
18
|
+
| Int32ArrayConstructor
|
19
|
+
| Uint32ArrayConstructor
|
20
|
+
| Float32ArrayConstructor
|
21
|
+
| Float64ArrayConstructor
|
22
|
+
|
23
|
+
type GetValue = keyof {
|
24
|
+
[K in keyof DataView as DataView[K] extends (byteOffset: number) => number
|
25
|
+
? K
|
26
|
+
: never]: DataView[K]
|
27
|
+
}
|
28
|
+
|
29
|
+
export function parseTypedArray<
|
30
|
+
T extends TypedArrayConstructor,
|
31
|
+
K extends GetValue
|
32
|
+
>(
|
33
|
+
buffer: ArrayBuffer,
|
34
|
+
TypedArray: T,
|
35
|
+
getValue: K,
|
36
|
+
littleEndian?: boolean
|
37
|
+
): InstanceType<T>
|
38
|
+
|
39
|
+
export function parseTypedArray<K extends GetValue>(
|
40
|
+
buffer: ArrayBuffer,
|
41
|
+
TypedArray: TypedArrayConstructor,
|
42
|
+
getValue: K,
|
43
|
+
littleEndian = true
|
44
|
+
): TypedArray {
|
45
|
+
const data = new DataView(buffer)
|
46
|
+
const array = new TypedArray(data.byteLength / TypedArray.BYTES_PER_ELEMENT)
|
47
|
+
for (
|
48
|
+
let index = 0, byteIndex = 0;
|
49
|
+
index < array.length;
|
50
|
+
++index, byteIndex += TypedArray.BYTES_PER_ELEMENT
|
51
|
+
) {
|
52
|
+
array[index] = data[getValue](byteIndex, littleEndian)
|
53
|
+
}
|
54
|
+
return array
|
55
|
+
}
|
56
|
+
|
57
|
+
export function parseInt16Array(
|
58
|
+
buffer: ArrayBuffer,
|
59
|
+
littleEndian?: boolean
|
60
|
+
): Int16Array {
|
61
|
+
return parseTypedArray(buffer, Int16Array, 'getInt16', littleEndian)
|
62
|
+
}
|
63
|
+
|
64
|
+
export function parseUint16Array(
|
65
|
+
buffer: ArrayBuffer,
|
66
|
+
littleEndian?: boolean
|
67
|
+
): Uint16Array {
|
68
|
+
return parseTypedArray(buffer, Uint16Array, 'getUint16', littleEndian)
|
69
|
+
}
|
70
|
+
|
71
|
+
export function parseFloat32Array(
|
72
|
+
buffer: ArrayBuffer,
|
73
|
+
littleEndian?: boolean
|
74
|
+
): Float32Array {
|
75
|
+
return parseTypedArray(buffer, Float32Array, 'getFloat32', littleEndian)
|
76
|
+
}
|
package/src/types.ts
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
import {
|
2
|
+
type Matrix2,
|
3
|
+
type Matrix3,
|
4
|
+
type Matrix4,
|
5
|
+
type Vector2,
|
6
|
+
type Vector3,
|
7
|
+
type Vector4
|
8
|
+
} from 'three'
|
9
|
+
import { type ReadonlyTuple } from 'type-fest'
|
10
|
+
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
12
|
+
export type Callable = (...args: any) => any
|
13
|
+
|
14
|
+
export type ReadonlyTuple2<T = number> = ReadonlyTuple<T, 2>
|
15
|
+
export type ReadonlyTuple3<T = number> = ReadonlyTuple<T, 3>
|
16
|
+
export type ReadonlyTuple4<T = number> = ReadonlyTuple<T, 4>
|
17
|
+
|
18
|
+
// Non-readonly version of ReadonlyTuple. This is not type-safe because mutable
|
19
|
+
// methods still exist in the type.
|
20
|
+
// See https://github.com/sindresorhus/type-fest/blob/main/source/readonly-tuple.d.ts
|
21
|
+
type BuildTupleHelper<
|
22
|
+
Element,
|
23
|
+
Length extends number,
|
24
|
+
Rest extends Element[]
|
25
|
+
> = Rest['length'] extends Length
|
26
|
+
? [...Rest]
|
27
|
+
: BuildTupleHelper<Element, Length, [Element, ...Rest]>
|
28
|
+
|
29
|
+
export type Tuple<T, Length extends number> = number extends Length
|
30
|
+
? readonly T[]
|
31
|
+
: BuildTupleHelper<T, Length, []>
|
32
|
+
|
33
|
+
export type Tuple2<T = number> = BuildTupleHelper<T, 2, []>
|
34
|
+
export type Tuple3<T = number> = BuildTupleHelper<T, 3, []>
|
35
|
+
export type Tuple4<T = number> = BuildTupleHelper<T, 4, []>
|
36
|
+
|
37
|
+
// Suppose return type of the mutable methods of classes like Vector3 is `this`.
|
38
|
+
// TODO: How can we specify `this` as a constraint?
|
39
|
+
type ReadonlyThreeInstance<T> = Readonly<{
|
40
|
+
[K in keyof T as T[K] extends Callable
|
41
|
+
? ReturnType<T[K]> extends T
|
42
|
+
? K extends 'clone'
|
43
|
+
? K
|
44
|
+
: never
|
45
|
+
: K
|
46
|
+
: K]: T[K]
|
47
|
+
}>
|
48
|
+
|
49
|
+
export type ReadonlyVector2 = ReadonlyThreeInstance<Vector2>
|
50
|
+
export type ReadonlyVector3 = ReadonlyThreeInstance<Vector3>
|
51
|
+
export type ReadonlyVector4 = ReadonlyThreeInstance<Vector4>
|
52
|
+
export type ReadonlyMatrix2 = ReadonlyThreeInstance<Matrix2>
|
53
|
+
export type ReadonlyMatrix3 = ReadonlyThreeInstance<Matrix3>
|
54
|
+
export type ReadonlyMatrix4 = ReadonlyThreeInstance<Matrix4>
|