@hello-terrain/three 0.0.0-alpha.12 → 0.0.0-alpha.13
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/dist/index.cjs +151 -233
- package/dist/index.d.cts +43 -50
- package/dist/index.d.mts +43 -50
- package/dist/index.d.ts +43 -50
- package/dist/index.mjs +151 -233
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -937,20 +937,15 @@ function beginUpdate(state, topology, params) {
|
|
|
937
937
|
|
|
938
938
|
function shouldSplit(bounds, level, maxLevel, params) {
|
|
939
939
|
if (level >= maxLevel) return false;
|
|
940
|
-
const mode = params.mode ?? "distance";
|
|
941
940
|
const cx = bounds.cx;
|
|
942
941
|
const cy = bounds.cy;
|
|
943
942
|
const cz = bounds.cz;
|
|
944
943
|
const distSq = cx * cx + cy * cy + cz * cz;
|
|
945
944
|
const safeDistSq = distSq > 1e-12 ? distSq : 1e-12;
|
|
946
|
-
if (mode === "screen") {
|
|
947
|
-
const proj = params.projectionFactor
|
|
948
|
-
const target = params.targetPixels
|
|
949
|
-
if (proj <= 0 || target <= 0)
|
|
950
|
-
const f2 = params.distanceFactor ?? 2;
|
|
951
|
-
const threshold2 = bounds.r * f2;
|
|
952
|
-
return safeDistSq < threshold2 * threshold2;
|
|
953
|
-
}
|
|
945
|
+
if (params.mode === "screen") {
|
|
946
|
+
const proj = params.projectionFactor;
|
|
947
|
+
const target = params.targetPixels;
|
|
948
|
+
if (proj <= 0 || target <= 0) return false;
|
|
954
949
|
const left = bounds.r * bounds.r * proj * proj;
|
|
955
950
|
const right = safeDistSq * target * target;
|
|
956
951
|
return left > right;
|
|
@@ -984,7 +979,7 @@ function refineLeaves(state, topology, params, outLeaves) {
|
|
|
984
979
|
let elevationRange;
|
|
985
980
|
if (params.tileElevationRange) {
|
|
986
981
|
const range = state.scratchElevationRange;
|
|
987
|
-
if (params.tileElevationRange(
|
|
982
|
+
if (params.tileElevationRange(tile, range)) {
|
|
988
983
|
elevationRange = range;
|
|
989
984
|
}
|
|
990
985
|
}
|
|
@@ -1087,19 +1082,9 @@ function balance2to1(state, topology, params, leaves) {
|
|
|
1087
1082
|
}
|
|
1088
1083
|
|
|
1089
1084
|
function update(state, topology, params, outLeaves) {
|
|
1090
|
-
const cam = params.cameraOrigin;
|
|
1091
|
-
const elevation = params.elevationAtCameraXZ ?? 0;
|
|
1092
|
-
const origX = cam.x;
|
|
1093
|
-
const origY = cam.y;
|
|
1094
|
-
const origZ = cam.z;
|
|
1095
|
-
topology.projection.cpu.cameraSurfaceOffset(cam, elevation);
|
|
1096
1085
|
beginUpdate(state, topology, params);
|
|
1097
1086
|
const leaves = refineLeaves(state, topology, params, outLeaves);
|
|
1098
|
-
|
|
1099
|
-
cam.x = origX;
|
|
1100
|
-
cam.y = origY;
|
|
1101
|
-
cam.z = origZ;
|
|
1102
|
-
return result;
|
|
1087
|
+
return balance2to1(state, topology, params, leaves);
|
|
1103
1088
|
}
|
|
1104
1089
|
|
|
1105
1090
|
const scratchTile = { space: 0, level: 0, x: 0, y: 0 };
|
|
@@ -1500,26 +1485,6 @@ function cpuRaycast(query, ray, config, options) {
|
|
|
1500
1485
|
distance: ray.origin.distanceTo(point)
|
|
1501
1486
|
};
|
|
1502
1487
|
}
|
|
1503
|
-
function cpuRaycastBoundsOnly(ray, config, options) {
|
|
1504
|
-
const bounds = getTerrainBounds(config);
|
|
1505
|
-
const planeY = (config.minY + config.maxY) * 0.5;
|
|
1506
|
-
const dirY = ray.direction.y;
|
|
1507
|
-
if (Math.abs(dirY) < 1e-8) return null;
|
|
1508
|
-
const t = (planeY - ray.origin.y) / dirY;
|
|
1509
|
-
if (t < 0) return null;
|
|
1510
|
-
const maxDistance = options?.maxDistance ?? Number.POSITIVE_INFINITY;
|
|
1511
|
-
if (t > maxDistance) return null;
|
|
1512
|
-
const point = new three.Vector3();
|
|
1513
|
-
ray.at(t, point);
|
|
1514
|
-
if (point.x < bounds.minX || point.x > bounds.maxX || point.z < bounds.minZ || point.z > bounds.maxZ) {
|
|
1515
|
-
return null;
|
|
1516
|
-
}
|
|
1517
|
-
return {
|
|
1518
|
-
position: point,
|
|
1519
|
-
normal: new three.Vector3(0, 1, 0),
|
|
1520
|
-
distance: ray.origin.distanceTo(point)
|
|
1521
|
-
};
|
|
1522
|
-
}
|
|
1523
1488
|
function intersectRaySphere(ray, cx, cy, cz, radius) {
|
|
1524
1489
|
const ox = ray.origin.x - cx;
|
|
1525
1490
|
const oy = ray.origin.y - cy;
|
|
@@ -1585,22 +1550,6 @@ function cubeSphereRaycast(query, ray, params, options) {
|
|
|
1585
1550
|
distance: ray.origin.distanceTo(sample.position)
|
|
1586
1551
|
};
|
|
1587
1552
|
}
|
|
1588
|
-
function cubeSphereRaycastBoundsOnly(ray, params, options) {
|
|
1589
|
-
const shell = intersectRaySphere(ray, params.centerX, params.centerY, params.centerZ, params.radius);
|
|
1590
|
-
if (!shell) return null;
|
|
1591
|
-
const maxDistance = options?.maxDistance ?? Number.POSITIVE_INFINITY;
|
|
1592
|
-
const t = shell.t0 >= 0 ? shell.t0 : shell.t1;
|
|
1593
|
-
if (t < 0 || t > maxDistance) return null;
|
|
1594
|
-
const point = new three.Vector3();
|
|
1595
|
-
ray.at(t, point);
|
|
1596
|
-
const normal = new three.Vector3(
|
|
1597
|
-
point.x - params.centerX,
|
|
1598
|
-
point.y - params.centerY,
|
|
1599
|
-
point.z - params.centerZ
|
|
1600
|
-
).normalize();
|
|
1601
|
-
if (params.invert) normal.negate();
|
|
1602
|
-
return { position: point, normal, distance: ray.origin.distanceTo(point) };
|
|
1603
|
-
}
|
|
1604
1553
|
function torusSignedDistance(query, params, px, py, pz, scratchPoint, scratchParams) {
|
|
1605
1554
|
positionToTorusParams(
|
|
1606
1555
|
px,
|
|
@@ -1655,28 +1604,6 @@ function torusRaycast(query, ray, params, options) {
|
|
|
1655
1604
|
distance: ray.origin.distanceTo(sample.position)
|
|
1656
1605
|
};
|
|
1657
1606
|
}
|
|
1658
|
-
function torusRaycastBoundsOnly(ray, params, options) {
|
|
1659
|
-
const shell = intersectRaySphere(
|
|
1660
|
-
ray,
|
|
1661
|
-
params.centerX,
|
|
1662
|
-
params.centerY,
|
|
1663
|
-
params.centerZ,
|
|
1664
|
-
params.outerRadius
|
|
1665
|
-
);
|
|
1666
|
-
if (!shell) return null;
|
|
1667
|
-
const maxDistance = options?.maxDistance ?? Number.POSITIVE_INFINITY;
|
|
1668
|
-
const t = shell.t0 >= 0 ? shell.t0 : shell.t1;
|
|
1669
|
-
if (t < 0 || t > maxDistance) return null;
|
|
1670
|
-
const point = new three.Vector3();
|
|
1671
|
-
ray.at(t, point);
|
|
1672
|
-
const normal = new three.Vector3(
|
|
1673
|
-
point.x - params.centerX,
|
|
1674
|
-
point.y - params.centerY,
|
|
1675
|
-
point.z - params.centerZ
|
|
1676
|
-
).normalize();
|
|
1677
|
-
if (params.invert) normal.negate();
|
|
1678
|
-
return { position: point, normal, distance: ray.origin.distanceTo(point) };
|
|
1679
|
-
}
|
|
1680
1607
|
|
|
1681
1608
|
function createTerrainQuery(cache) {
|
|
1682
1609
|
return {
|
|
@@ -1802,9 +1729,6 @@ function createFlatProjection() {
|
|
|
1802
1729
|
}
|
|
1803
1730
|
},
|
|
1804
1731
|
cpu: {
|
|
1805
|
-
cameraSurfaceOffset(cam, elevation) {
|
|
1806
|
-
cam.y -= elevation;
|
|
1807
|
-
},
|
|
1808
1732
|
createSurfaceOps() {
|
|
1809
1733
|
return null;
|
|
1810
1734
|
},
|
|
@@ -1813,26 +1737,44 @@ function createFlatProjection() {
|
|
|
1813
1737
|
},
|
|
1814
1738
|
raycast(ctx) {
|
|
1815
1739
|
const { ray, options, terrainQuery, config } = ctx;
|
|
1816
|
-
if (terrainQuery)
|
|
1817
|
-
|
|
1818
|
-
if (precise) return precise;
|
|
1819
|
-
}
|
|
1820
|
-
const coarse = cpuRaycastBoundsOnly(ray, config, options);
|
|
1821
|
-
if (coarse && terrainQuery) {
|
|
1822
|
-
const sample = terrainQuery.sampleTerrain(coarse.position.x, coarse.position.z);
|
|
1823
|
-
if (sample.valid) {
|
|
1824
|
-
coarse.position.y = sample.elevation;
|
|
1825
|
-
coarse.normal.copy(sample.normal);
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
return coarse;
|
|
1740
|
+
if (!terrainQuery) return null;
|
|
1741
|
+
return cpuRaycast(terrainQuery, ray, config, options);
|
|
1829
1742
|
}
|
|
1830
1743
|
}
|
|
1831
1744
|
};
|
|
1832
1745
|
}
|
|
1833
1746
|
|
|
1747
|
+
function boundingSphereFromPoints(px, py, pz, count, cameraOrigin, out) {
|
|
1748
|
+
let sumX = 0;
|
|
1749
|
+
let sumY = 0;
|
|
1750
|
+
let sumZ = 0;
|
|
1751
|
+
for (let i = 0; i < count; i++) {
|
|
1752
|
+
sumX += px[i];
|
|
1753
|
+
sumY += py[i];
|
|
1754
|
+
sumZ += pz[i];
|
|
1755
|
+
}
|
|
1756
|
+
const cX = sumX / count;
|
|
1757
|
+
const cY = sumY / count;
|
|
1758
|
+
const cZ = sumZ / count;
|
|
1759
|
+
let maxDistSq = 0;
|
|
1760
|
+
for (let i = 0; i < count; i++) {
|
|
1761
|
+
const dx = px[i] - cX;
|
|
1762
|
+
const dy = py[i] - cY;
|
|
1763
|
+
const dz = pz[i] - cZ;
|
|
1764
|
+
const dSq = dx * dx + dy * dy + dz * dz;
|
|
1765
|
+
if (dSq > maxDistSq) maxDistSq = dSq;
|
|
1766
|
+
}
|
|
1767
|
+
out.cx = cX - cameraOrigin.x;
|
|
1768
|
+
out.cy = cY - cameraOrigin.y;
|
|
1769
|
+
out.cz = cZ - cameraOrigin.z;
|
|
1770
|
+
out.r = Math.sqrt(maxDistSq);
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1834
1773
|
function createFlatTopology(cfg) {
|
|
1835
1774
|
const halfRoot = 0.5 * cfg.rootSize;
|
|
1775
|
+
const px = new Float64Array(8);
|
|
1776
|
+
const py = new Float64Array(8);
|
|
1777
|
+
const pz = new Float64Array(8);
|
|
1836
1778
|
const topology = {
|
|
1837
1779
|
spaceCount: 1,
|
|
1838
1780
|
maxRootCount: 1,
|
|
@@ -1872,15 +1814,26 @@ function createFlatTopology(cfg) {
|
|
|
1872
1814
|
const size = cfg.rootSize * scale;
|
|
1873
1815
|
const minX = cfg.origin.x + (tile.x * size - halfRoot);
|
|
1874
1816
|
const minZ = cfg.origin.z + (tile.y * size - halfRoot);
|
|
1875
|
-
const
|
|
1876
|
-
const
|
|
1877
|
-
const
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1817
|
+
const maxX = minX + size;
|
|
1818
|
+
const maxZ = minZ + size;
|
|
1819
|
+
const yLo = cfg.origin.y + (elevationRange ? elevationRange.min : 0);
|
|
1820
|
+
const yHi = elevationRange ? cfg.origin.y + elevationRange.max : 0;
|
|
1821
|
+
let pointCount = 0;
|
|
1822
|
+
for (let i = 0; i < 4; i++) {
|
|
1823
|
+
const cornerX = (i & 1) === 0 ? minX : maxX;
|
|
1824
|
+
const cornerZ = i < 2 ? minZ : maxZ;
|
|
1825
|
+
px[pointCount] = cornerX;
|
|
1826
|
+
py[pointCount] = yLo;
|
|
1827
|
+
pz[pointCount] = cornerZ;
|
|
1828
|
+
pointCount += 1;
|
|
1829
|
+
if (elevationRange) {
|
|
1830
|
+
px[pointCount] = cornerX;
|
|
1831
|
+
py[pointCount] = yHi;
|
|
1832
|
+
pz[pointCount] = cornerZ;
|
|
1833
|
+
pointCount += 1;
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
boundingSphereFromPoints(px, py, pz, pointCount, cameraOrigin, out);
|
|
1884
1837
|
},
|
|
1885
1838
|
rootTiles(_cameraOrigin, out) {
|
|
1886
1839
|
const root = out[0];
|
|
@@ -1898,6 +1851,9 @@ function createInfiniteFlatTopology(cfg) {
|
|
|
1898
1851
|
const halfRoot = 0.5 * cfg.rootSize;
|
|
1899
1852
|
const rootGridRadius = Math.max(0, Math.floor(cfg.rootGridRadius ?? 1));
|
|
1900
1853
|
const rootWidth = rootGridRadius * 2 + 1;
|
|
1854
|
+
const px = new Float64Array(8);
|
|
1855
|
+
const py = new Float64Array(8);
|
|
1856
|
+
const pz = new Float64Array(8);
|
|
1901
1857
|
return {
|
|
1902
1858
|
spaceCount: 1,
|
|
1903
1859
|
maxRootCount: rootWidth * rootWidth,
|
|
@@ -1931,15 +1887,26 @@ function createInfiniteFlatTopology(cfg) {
|
|
|
1931
1887
|
const size = cfg.rootSize * scale;
|
|
1932
1888
|
const minX = cfg.origin.x + (tile.x * size - halfRoot);
|
|
1933
1889
|
const minZ = cfg.origin.z + (tile.y * size - halfRoot);
|
|
1934
|
-
const
|
|
1935
|
-
const
|
|
1936
|
-
const
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1890
|
+
const maxX = minX + size;
|
|
1891
|
+
const maxZ = minZ + size;
|
|
1892
|
+
const yLo = cfg.origin.y + (elevationRange ? elevationRange.min : 0);
|
|
1893
|
+
const yHi = elevationRange ? cfg.origin.y + elevationRange.max : 0;
|
|
1894
|
+
let pointCount = 0;
|
|
1895
|
+
for (let i = 0; i < 4; i++) {
|
|
1896
|
+
const cornerX = (i & 1) === 0 ? minX : maxX;
|
|
1897
|
+
const cornerZ = i < 2 ? minZ : maxZ;
|
|
1898
|
+
px[pointCount] = cornerX;
|
|
1899
|
+
py[pointCount] = yLo;
|
|
1900
|
+
pz[pointCount] = cornerZ;
|
|
1901
|
+
pointCount += 1;
|
|
1902
|
+
if (elevationRange) {
|
|
1903
|
+
px[pointCount] = cornerX;
|
|
1904
|
+
py[pointCount] = yHi;
|
|
1905
|
+
pz[pointCount] = cornerZ;
|
|
1906
|
+
pointCount += 1;
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
boundingSphereFromPoints(px, py, pz, pointCount, cameraOrigin, out);
|
|
1943
1910
|
},
|
|
1944
1911
|
rootTiles(cameraOrigin, out) {
|
|
1945
1912
|
const camRootX = Math.floor((cameraOrigin.x - cfg.origin.x + halfRoot) / cfg.rootSize);
|
|
@@ -2475,7 +2442,6 @@ function createCubeSphereProjection(config) {
|
|
|
2475
2442
|
const nx = dx / len;
|
|
2476
2443
|
const ny = dy / len;
|
|
2477
2444
|
const nz = dz / len;
|
|
2478
|
-
const dirSign = invert ? -1 : 1;
|
|
2479
2445
|
dirScratch[0] = nx;
|
|
2480
2446
|
dirScratch[1] = ny;
|
|
2481
2447
|
dirScratch[2] = nz;
|
|
@@ -2484,9 +2450,9 @@ function createCubeSphereProjection(config) {
|
|
|
2484
2450
|
out.space = face;
|
|
2485
2451
|
out.u = uvScratch[0];
|
|
2486
2452
|
out.v = uvScratch[1];
|
|
2487
|
-
out.dirX = nx
|
|
2488
|
-
out.dirY = ny
|
|
2489
|
-
out.dirZ = nz
|
|
2453
|
+
out.dirX = nx;
|
|
2454
|
+
out.dirY = ny;
|
|
2455
|
+
out.dirZ = nz;
|
|
2490
2456
|
return true;
|
|
2491
2457
|
},
|
|
2492
2458
|
surfacePosition(key, elevation, outVec) {
|
|
@@ -2519,7 +2485,8 @@ function createCubeSphereProjection(config) {
|
|
|
2519
2485
|
let nx = tuy * tvz - tuz * tvy;
|
|
2520
2486
|
let ny = tuz * tvx - tux * tvz;
|
|
2521
2487
|
let nz = tux * tvy - tuy * tvx;
|
|
2522
|
-
|
|
2488
|
+
const outwardSign = invert ? -1 : 1;
|
|
2489
|
+
if ((nx * key.dirX + ny * key.dirY + nz * key.dirZ) * outwardSign < 0) {
|
|
2523
2490
|
nx = -nx;
|
|
2524
2491
|
ny = -ny;
|
|
2525
2492
|
nz = -nz;
|
|
@@ -2573,19 +2540,6 @@ function createCubeSphereProjection(config) {
|
|
|
2573
2540
|
augmentSampler: augmentCubeSphereSampler
|
|
2574
2541
|
},
|
|
2575
2542
|
cpu: {
|
|
2576
|
-
cameraSurfaceOffset(cam, elevation) {
|
|
2577
|
-
const dx = cam.x - center.x;
|
|
2578
|
-
const dy = cam.y - center.y;
|
|
2579
|
-
const dz = cam.z - center.z;
|
|
2580
|
-
const len = Math.hypot(dx, dy, dz);
|
|
2581
|
-
if (len > 1e-12) {
|
|
2582
|
-
const sign = invert ? 1 : -1;
|
|
2583
|
-
const inv = sign * elevation / len;
|
|
2584
|
-
cam.x += dx * inv;
|
|
2585
|
-
cam.y += dy * inv;
|
|
2586
|
-
cam.z += dz * inv;
|
|
2587
|
-
}
|
|
2588
|
-
},
|
|
2589
2543
|
createSurfaceOps() {
|
|
2590
2544
|
return surfaceOps;
|
|
2591
2545
|
},
|
|
@@ -2596,6 +2550,7 @@ function createCubeSphereProjection(config) {
|
|
|
2596
2550
|
return { query, surfaceQuery, sphereQuery };
|
|
2597
2551
|
},
|
|
2598
2552
|
raycast(ctx) {
|
|
2553
|
+
if (!ctx.sphereQuery) return null;
|
|
2599
2554
|
const range = ctx.terrainQuery?.getGlobalElevationRange();
|
|
2600
2555
|
const dispMax = range ? Math.max(0, range.max - center.y) : radius * 0.1;
|
|
2601
2556
|
const outerPadding = invert ? 0 : dispMax + RAYCAST_PADDING$1;
|
|
@@ -2607,11 +2562,7 @@ function createCubeSphereProjection(config) {
|
|
|
2607
2562
|
maxRadius: radius + outerPadding,
|
|
2608
2563
|
invert
|
|
2609
2564
|
};
|
|
2610
|
-
|
|
2611
|
-
const precise = cubeSphereRaycast(ctx.sphereQuery, ctx.ray, params, ctx.options);
|
|
2612
|
-
if (precise) return precise;
|
|
2613
|
-
}
|
|
2614
|
-
return cubeSphereRaycastBoundsOnly(ctx.ray, params, ctx.options);
|
|
2565
|
+
return cubeSphereRaycast(ctx.sphereQuery, ctx.ray, params, ctx.options);
|
|
2615
2566
|
}
|
|
2616
2567
|
}
|
|
2617
2568
|
};
|
|
@@ -2702,8 +2653,6 @@ function createCubeSphereTopology(cfg) {
|
|
|
2702
2653
|
spaceCount: 6,
|
|
2703
2654
|
maxRootCount: 6,
|
|
2704
2655
|
projection: createCubeSphereProjection({ radius, center, invert: cfg.invert }),
|
|
2705
|
-
radius,
|
|
2706
|
-
center,
|
|
2707
2656
|
neighborSameLevel(tile, dir, out) {
|
|
2708
2657
|
const level = tile.level;
|
|
2709
2658
|
const n = 1 << level;
|
|
@@ -2740,48 +2689,29 @@ function createCubeSphereTopology(cfg) {
|
|
|
2740
2689
|
const u1 = (tile.x + 1) / n;
|
|
2741
2690
|
const v0 = tile.y / n;
|
|
2742
2691
|
const v1 = (tile.y + 1) / n;
|
|
2743
|
-
const
|
|
2744
|
-
const
|
|
2745
|
-
const disps = elevationRange ? [elevationRange.min, elevationRange.max] : [0];
|
|
2692
|
+
const shellLo = radius + (elevationRange ? elevationRange.min : 0);
|
|
2693
|
+
const shellHi = elevationRange ? radius + elevationRange.max : 0;
|
|
2746
2694
|
let pointCount = 0;
|
|
2747
|
-
let sumX = 0;
|
|
2748
|
-
let sumY = 0;
|
|
2749
|
-
let sumZ = 0;
|
|
2750
2695
|
for (let i = 0; i < 4; i++) {
|
|
2751
|
-
|
|
2696
|
+
const u = (i & 1) === 0 ? u0 : u1;
|
|
2697
|
+
const v = i < 2 ? v0 : v1;
|
|
2698
|
+
faceUVToCube(tile.space, u, v, cube);
|
|
2752
2699
|
const len = Math.hypot(cube[0], cube[1], cube[2]);
|
|
2753
2700
|
const dirX = cube[0] / len;
|
|
2754
2701
|
const dirY = cube[1] / len;
|
|
2755
2702
|
const dirZ = cube[2] / len;
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
px[pointCount] =
|
|
2762
|
-
py[pointCount] =
|
|
2763
|
-
pz[pointCount] =
|
|
2764
|
-
sumX += sx;
|
|
2765
|
-
sumY += sy;
|
|
2766
|
-
sumZ += sz;
|
|
2703
|
+
px[pointCount] = center.x + dirX * shellLo;
|
|
2704
|
+
py[pointCount] = center.y + dirY * shellLo;
|
|
2705
|
+
pz[pointCount] = center.z + dirZ * shellLo;
|
|
2706
|
+
pointCount += 1;
|
|
2707
|
+
if (elevationRange) {
|
|
2708
|
+
px[pointCount] = center.x + dirX * shellHi;
|
|
2709
|
+
py[pointCount] = center.y + dirY * shellHi;
|
|
2710
|
+
pz[pointCount] = center.z + dirZ * shellHi;
|
|
2767
2711
|
pointCount += 1;
|
|
2768
2712
|
}
|
|
2769
2713
|
}
|
|
2770
|
-
|
|
2771
|
-
const cY = sumY / pointCount;
|
|
2772
|
-
const cZ = sumZ / pointCount;
|
|
2773
|
-
let maxDistSq = 0;
|
|
2774
|
-
for (let i = 0; i < pointCount; i++) {
|
|
2775
|
-
const dx = px[i] - cX;
|
|
2776
|
-
const dy = py[i] - cY;
|
|
2777
|
-
const dz = pz[i] - cZ;
|
|
2778
|
-
const dSq = dx * dx + dy * dy + dz * dz;
|
|
2779
|
-
if (dSq > maxDistSq) maxDistSq = dSq;
|
|
2780
|
-
}
|
|
2781
|
-
out.cx = cX - cameraOrigin.x;
|
|
2782
|
-
out.cy = cY - cameraOrigin.y;
|
|
2783
|
-
out.cz = cZ - cameraOrigin.z;
|
|
2784
|
-
out.r = Math.sqrt(maxDistSq);
|
|
2714
|
+
boundingSphereFromPoints(px, py, pz, pointCount, cameraOrigin, out);
|
|
2785
2715
|
},
|
|
2786
2716
|
rootTiles(_cameraOrigin, out) {
|
|
2787
2717
|
for (let s = 0; s < 6; s++) {
|
|
@@ -2952,13 +2882,6 @@ function createTorusProjection(config) {
|
|
|
2952
2882
|
}
|
|
2953
2883
|
},
|
|
2954
2884
|
cpu: {
|
|
2955
|
-
cameraSurfaceOffset(cam, elevation) {
|
|
2956
|
-
positionToTorusParams(cam.x, cam.y, cam.z, majorRadius, center, params);
|
|
2957
|
-
torusOutwardNormal$1(params.u, params.v, normalScratch, invert);
|
|
2958
|
-
cam.x -= normalScratch[0] * elevation;
|
|
2959
|
-
cam.y -= normalScratch[1] * elevation;
|
|
2960
|
-
cam.z -= normalScratch[2] * elevation;
|
|
2961
|
-
},
|
|
2962
2885
|
createSurfaceOps() {
|
|
2963
2886
|
return surfaceOps;
|
|
2964
2887
|
},
|
|
@@ -2968,6 +2891,7 @@ function createTorusProjection(config) {
|
|
|
2968
2891
|
return { query, surfaceQuery, sphereQuery: null };
|
|
2969
2892
|
},
|
|
2970
2893
|
raycast(ctx) {
|
|
2894
|
+
if (!ctx.surfaceQuery) return null;
|
|
2971
2895
|
const range = ctx.terrainQuery?.getGlobalElevationRange();
|
|
2972
2896
|
const dispMax = range ? Math.max(0, range.max - ctx.config.originY) : minorRadius * 0.5;
|
|
2973
2897
|
const outerPadding = invert ? 0 : dispMax + RAYCAST_PADDING;
|
|
@@ -2980,11 +2904,7 @@ function createTorusProjection(config) {
|
|
|
2980
2904
|
outerRadius: majorRadius + minorRadius + outerPadding,
|
|
2981
2905
|
invert
|
|
2982
2906
|
};
|
|
2983
|
-
|
|
2984
|
-
const precise = torusRaycast(ctx.surfaceQuery, ctx.ray, raycastParams, ctx.options);
|
|
2985
|
-
if (precise) return precise;
|
|
2986
|
-
}
|
|
2987
|
-
return torusRaycastBoundsOnly(ctx.ray, raycastParams, ctx.options);
|
|
2907
|
+
return torusRaycast(ctx.surfaceQuery, ctx.ray, raycastParams, ctx.options);
|
|
2988
2908
|
}
|
|
2989
2909
|
}
|
|
2990
2910
|
};
|
|
@@ -3017,8 +2937,6 @@ function createTorusTopology(cfg) {
|
|
|
3017
2937
|
baseU,
|
|
3018
2938
|
baseV
|
|
3019
2939
|
}),
|
|
3020
|
-
radius: majorRadius + minorRadius,
|
|
3021
|
-
center,
|
|
3022
2940
|
neighborSameLevel(tile, dir, out) {
|
|
3023
2941
|
const { nU, nV } = levelResolution(tile.level);
|
|
3024
2942
|
let nx = tile.x;
|
|
@@ -3049,42 +2967,28 @@ function createTorusTopology(cfg) {
|
|
|
3049
2967
|
const v0 = tile.y / nV;
|
|
3050
2968
|
const stepU = 1 / nU;
|
|
3051
2969
|
const stepV = 1 / nV;
|
|
3052
|
-
const
|
|
2970
|
+
const dispLo = elevationRange ? elevationRange.min : 0;
|
|
2971
|
+
const dispHi = elevationRange ? elevationRange.max : 0;
|
|
3053
2972
|
let pointCount = 0;
|
|
3054
|
-
let sumX = 0;
|
|
3055
|
-
let sumY = 0;
|
|
3056
|
-
let sumZ = 0;
|
|
3057
2973
|
for (let sj = 0; sj <= 2; sj++) {
|
|
3058
2974
|
for (let si = 0; si <= 2; si++) {
|
|
3059
2975
|
const u = u0 + si * stepU / 2;
|
|
3060
2976
|
const v = v0 + sj * stepV / 2;
|
|
3061
|
-
|
|
3062
|
-
|
|
2977
|
+
torusUVToPoint(u, v, majorRadius, minorRadius, dispLo, center, corner, invert);
|
|
2978
|
+
px[pointCount] = corner[0];
|
|
2979
|
+
py[pointCount] = corner[1];
|
|
2980
|
+
pz[pointCount] = corner[2];
|
|
2981
|
+
pointCount += 1;
|
|
2982
|
+
if (elevationRange) {
|
|
2983
|
+
torusUVToPoint(u, v, majorRadius, minorRadius, dispHi, center, corner, invert);
|
|
3063
2984
|
px[pointCount] = corner[0];
|
|
3064
2985
|
py[pointCount] = corner[1];
|
|
3065
2986
|
pz[pointCount] = corner[2];
|
|
3066
|
-
sumX += corner[0];
|
|
3067
|
-
sumY += corner[1];
|
|
3068
|
-
sumZ += corner[2];
|
|
3069
2987
|
pointCount += 1;
|
|
3070
2988
|
}
|
|
3071
2989
|
}
|
|
3072
2990
|
}
|
|
3073
|
-
|
|
3074
|
-
const cY = sumY / pointCount;
|
|
3075
|
-
const cZ = sumZ / pointCount;
|
|
3076
|
-
let maxDistSq = 0;
|
|
3077
|
-
for (let i = 0; i < pointCount; i++) {
|
|
3078
|
-
const dx = px[i] - cX;
|
|
3079
|
-
const dy = py[i] - cY;
|
|
3080
|
-
const dz = pz[i] - cZ;
|
|
3081
|
-
const dSq = dx * dx + dy * dy + dz * dz;
|
|
3082
|
-
if (dSq > maxDistSq) maxDistSq = dSq;
|
|
3083
|
-
}
|
|
3084
|
-
out.cx = cX - cameraOrigin.x;
|
|
3085
|
-
out.cy = cY - cameraOrigin.y;
|
|
3086
|
-
out.cz = cZ - cameraOrigin.z;
|
|
3087
|
-
out.r = Math.sqrt(maxDistSq);
|
|
2991
|
+
boundingSphereFromPoints(px, py, pz, pointCount, cameraOrigin, out);
|
|
3088
2992
|
},
|
|
3089
2993
|
rootTiles(_cameraOrigin, out) {
|
|
3090
2994
|
let count = 0;
|
|
@@ -3648,6 +3552,9 @@ function createCpuTerrainCache(maxNodes, initialConfig, surfaceOps) {
|
|
|
3648
3552
|
get hasSurface() {
|
|
3649
3553
|
return surfaceOps !== null;
|
|
3650
3554
|
},
|
|
3555
|
+
setSurfaceOps(nextSurfaceOps) {
|
|
3556
|
+
surfaceOps = nextSurfaceOps;
|
|
3557
|
+
},
|
|
3651
3558
|
updateConfig(nextConfig) {
|
|
3652
3559
|
config = nextConfig;
|
|
3653
3560
|
shape.edgeVertexCount = config.innerTileSegments + 3;
|
|
@@ -3799,7 +3706,7 @@ function createCpuTerrainCache(maxNodes, initialConfig, surfaceOps) {
|
|
|
3799
3706
|
}
|
|
3800
3707
|
|
|
3801
3708
|
const WGSIZE = 64;
|
|
3802
|
-
function buildReductionKernel(elevationFieldNode, boundsNode, verticesPerNode) {
|
|
3709
|
+
function buildReductionKernel(elevationFieldNode, boundsNode, verticesPerNode, edgeVertexCount) {
|
|
3803
3710
|
const elemsPerThread = Math.ceil(verticesPerNode / WGSIZE);
|
|
3804
3711
|
return tsl.Fn(() => {
|
|
3805
3712
|
const sharedMin = tsl.workgroupArray("float", WGSIZE);
|
|
@@ -3811,10 +3718,17 @@ function buildReductionKernel(elevationFieldNode, boundsNode, verticesPerNode) {
|
|
|
3811
3718
|
const end = tsl.min(start.add(tsl.int(elemsPerThread)), tsl.int(verticesPerNode));
|
|
3812
3719
|
const localMin = tsl.float(1e10).toVar("localMin");
|
|
3813
3720
|
const localMax = tsl.float(-1e10).toVar("localMax");
|
|
3721
|
+
const edge = tsl.int(edgeVertexCount);
|
|
3722
|
+
const lastEdge = tsl.int(edgeVertexCount - 1);
|
|
3814
3723
|
tsl.Loop({ start, end, type: "int", condition: "<" }, ({ i }) => {
|
|
3815
|
-
const
|
|
3816
|
-
|
|
3817
|
-
|
|
3724
|
+
const ix = tsl.int(i).mod(edge);
|
|
3725
|
+
const iy = tsl.int(i).div(edge);
|
|
3726
|
+
const isSkirt = ix.equal(tsl.int(0)).or(ix.equal(lastEdge)).or(iy.equal(tsl.int(0))).or(iy.equal(lastEdge));
|
|
3727
|
+
tsl.If(isSkirt.not(), () => {
|
|
3728
|
+
const h = elevationFieldNode.element(baseOffset.add(i));
|
|
3729
|
+
localMin.assign(tsl.min(localMin, h));
|
|
3730
|
+
localMax.assign(tsl.max(localMax, h));
|
|
3731
|
+
});
|
|
3818
3732
|
});
|
|
3819
3733
|
sharedMin.element(tid).assign(localMin);
|
|
3820
3734
|
sharedMax.element(tid).assign(localMax);
|
|
@@ -3844,7 +3758,12 @@ const tileBoundsContextTask = work.task((get, work) => {
|
|
|
3844
3758
|
"tileBounds"
|
|
3845
3759
|
);
|
|
3846
3760
|
const verticesPerNode = edgeVertexCount * edgeVertexCount;
|
|
3847
|
-
const kernel = buildReductionKernel(
|
|
3761
|
+
const kernel = buildReductionKernel(
|
|
3762
|
+
elevationFieldContext.node,
|
|
3763
|
+
node,
|
|
3764
|
+
verticesPerNode,
|
|
3765
|
+
edgeVertexCount
|
|
3766
|
+
);
|
|
3848
3767
|
return { data, attribute, node, kernel };
|
|
3849
3768
|
});
|
|
3850
3769
|
}).displayName("tileBoundsContextTask");
|
|
@@ -3874,6 +3793,7 @@ const terrainQueryTask = work.task((get, work) => {
|
|
|
3874
3793
|
const projection = topologyValue.projection;
|
|
3875
3794
|
return work((prev) => {
|
|
3876
3795
|
const shapeKey = `${maxNodesValue}:${innerTileSegmentsValue}:${projection.kind}`;
|
|
3796
|
+
const resolvedRadius = projection.radius ?? radiusValue;
|
|
3877
3797
|
const configValues = {
|
|
3878
3798
|
rootSize: rootSizeValue,
|
|
3879
3799
|
originX: originValue.x,
|
|
@@ -3882,7 +3802,7 @@ const terrainQueryTask = work.task((get, work) => {
|
|
|
3882
3802
|
innerTileSegments: innerTileSegmentsValue,
|
|
3883
3803
|
elevationScale: elevationScaleValue,
|
|
3884
3804
|
maxLevel: maxLevelValue,
|
|
3885
|
-
radius:
|
|
3805
|
+
radius: resolvedRadius,
|
|
3886
3806
|
baseU: projection.baseResolution?.u ?? 1,
|
|
3887
3807
|
baseV: projection.baseResolution?.v ?? 1
|
|
3888
3808
|
};
|
|
@@ -3897,9 +3817,15 @@ const terrainQueryTask = work.task((get, work) => {
|
|
|
3897
3817
|
query = runtime.query;
|
|
3898
3818
|
surfaceQuery = runtime.surfaceQuery;
|
|
3899
3819
|
sphereQuery = runtime.sphereQuery;
|
|
3820
|
+
} else if (prev?.projection !== projection) {
|
|
3821
|
+
cache.setSurfaceOps(projection.cpu.createSurfaceOps());
|
|
3822
|
+
const runtime = projection.cpu.createRuntimeQueries(cache);
|
|
3823
|
+
query = runtime.query;
|
|
3824
|
+
surfaceQuery = runtime.surfaceQuery;
|
|
3825
|
+
sphereQuery = runtime.sphereQuery;
|
|
3900
3826
|
}
|
|
3901
3827
|
cache.updateConfig(configValues);
|
|
3902
|
-
return { cache, query, surfaceQuery, sphereQuery, shapeKey };
|
|
3828
|
+
return { cache, query, surfaceQuery, sphereQuery, shapeKey, projection };
|
|
3903
3829
|
});
|
|
3904
3830
|
}).displayName("terrainQueryTask");
|
|
3905
3831
|
const terrainReadbackTask = work.task(
|
|
@@ -3946,27 +3872,19 @@ const quadtreeConfigTask = work.task((get, work) => {
|
|
|
3946
3872
|
const quadtreeUpdateTask = work.task((get, work) => {
|
|
3947
3873
|
const quadtreeConfig = get(quadtreeConfigTask);
|
|
3948
3874
|
const quadtreeUpdateConfig = get(quadtreeUpdate);
|
|
3949
|
-
const {
|
|
3875
|
+
const { cache } = get(terrainQueryTask);
|
|
3950
3876
|
const elevationScaleValue = get(elevationScale);
|
|
3951
3877
|
let outLeaves = void 0;
|
|
3952
|
-
const cameraPosition = new three.Vector3();
|
|
3953
3878
|
const elevationRangeScratch = { min: 0, max: 0 };
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
cameraPosition.set(cam.x, cam.y, cam.z);
|
|
3958
|
-
quadtreeUpdateConfig.elevationAtCameraXZ = surfaceQuery.getElevationByPosition(cameraPosition) ?? 0;
|
|
3959
|
-
} else {
|
|
3960
|
-
quadtreeUpdateConfig.elevationAtCameraXZ = terrainQuery.getElevation(cam.x, cam.z) ?? 0;
|
|
3879
|
+
quadtreeUpdateConfig.tileElevationRange = (tile, out) => {
|
|
3880
|
+
if (!cache.getTileElevationRange(tile.space, tile.level, tile.x, tile.y, elevationRangeScratch)) {
|
|
3881
|
+
return false;
|
|
3961
3882
|
}
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
out.max = elevationRangeScratch.max * elevationScaleValue;
|
|
3968
|
-
return true;
|
|
3969
|
-
};
|
|
3883
|
+
out.min = elevationRangeScratch.min * elevationScaleValue;
|
|
3884
|
+
out.max = elevationRangeScratch.max * elevationScaleValue;
|
|
3885
|
+
return true;
|
|
3886
|
+
};
|
|
3887
|
+
return work(() => {
|
|
3970
3888
|
outLeaves = update(
|
|
3971
3889
|
quadtreeConfig.state,
|
|
3972
3890
|
quadtreeConfig.topology,
|