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