@hello-terrain/three 0.0.0-alpha.8 → 0.0.0-alpha.9
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 +311 -38
- package/dist/index.d.cts +49 -13
- package/dist/index.d.mts +49 -13
- package/dist/index.d.ts +49 -13
- package/dist/index.mjs +309 -40
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -647,25 +647,6 @@ const createElevation = (tile, uniforms, elevationFn) => {
|
|
|
647
647
|
});
|
|
648
648
|
};
|
|
649
649
|
};
|
|
650
|
-
const readElevationFieldAtPositionLocal = (terrainFieldStorage, edgeVertexCount, positionLocal) => tsl.Fn(() => {
|
|
651
|
-
const nodeIndex = tsl.int(tsl.instanceIndex);
|
|
652
|
-
const intEdge = tsl.int(edgeVertexCount);
|
|
653
|
-
const innerSegments = tsl.int(edgeVertexCount).sub(3);
|
|
654
|
-
const fInnerSegments = tsl.float(innerSegments);
|
|
655
|
-
const last = intEdge.sub(tsl.int(1));
|
|
656
|
-
const u = positionLocal.x.add(tsl.float(0.5));
|
|
657
|
-
const v = positionLocal.z.add(tsl.float(0.5));
|
|
658
|
-
const x = u.mul(fInnerSegments).round().toInt().add(tsl.int(1));
|
|
659
|
-
const y = v.mul(fInnerSegments).round().toInt().add(tsl.int(1));
|
|
660
|
-
const xClamped = tsl.min(tsl.max(x, tsl.int(0)), last);
|
|
661
|
-
const yClamped = tsl.min(tsl.max(y, tsl.int(0)), last);
|
|
662
|
-
return loadTerrainFieldElevation(
|
|
663
|
-
terrainFieldStorage,
|
|
664
|
-
xClamped,
|
|
665
|
-
yClamped,
|
|
666
|
-
nodeIndex
|
|
667
|
-
);
|
|
668
|
-
});
|
|
669
650
|
|
|
670
651
|
function createTileCompute(leafStorage, uniforms) {
|
|
671
652
|
const tileLevel = tsl.Fn(([nodeIndex]) => {
|
|
@@ -732,6 +713,10 @@ function createTileCompute(leafStorage, uniforms) {
|
|
|
732
713
|
tileVertexWorldPositionCompute
|
|
733
714
|
};
|
|
734
715
|
}
|
|
716
|
+
function tileLocalToFieldUV(localCoord, innerSegments) {
|
|
717
|
+
const edge = tsl.float(innerSegments).add(tsl.float(3));
|
|
718
|
+
return tsl.float(localCoord).mul(tsl.float(innerSegments)).add(tsl.float(1.5)).div(edge);
|
|
719
|
+
}
|
|
735
720
|
|
|
736
721
|
const rootSize = work.param(256).displayName("rootSize");
|
|
737
722
|
const origin = work.param({
|
|
@@ -848,12 +833,12 @@ function ensureChildren(store, parentId) {
|
|
|
848
833
|
return childBase;
|
|
849
834
|
}
|
|
850
835
|
|
|
851
|
-
function nextPow2(n) {
|
|
836
|
+
function nextPow2$1(n) {
|
|
852
837
|
let x = 1;
|
|
853
838
|
while (x < n) x <<= 1;
|
|
854
839
|
return x;
|
|
855
840
|
}
|
|
856
|
-
function mix32(x) {
|
|
841
|
+
function mix32$1(x) {
|
|
857
842
|
x >>>= 0;
|
|
858
843
|
x ^= x >>> 16;
|
|
859
844
|
x = Math.imul(x, 2146121005) >>> 0;
|
|
@@ -862,12 +847,12 @@ function mix32(x) {
|
|
|
862
847
|
x ^= x >>> 16;
|
|
863
848
|
return x >>> 0;
|
|
864
849
|
}
|
|
865
|
-
function hashKey(space, level, x, y) {
|
|
866
|
-
const h = space & 255 ^ (level & 255) << 8 ^ mix32(x) >>> 0 ^ mix32(y) >>> 0;
|
|
867
|
-
return mix32(h);
|
|
850
|
+
function hashKey$1(space, level, x, y) {
|
|
851
|
+
const h = space & 255 ^ (level & 255) << 8 ^ mix32$1(x) >>> 0 ^ mix32$1(y) >>> 0;
|
|
852
|
+
return mix32$1(h);
|
|
868
853
|
}
|
|
869
854
|
function createSpatialIndex(maxEntries) {
|
|
870
|
-
const size = nextPow2(Math.max(2, maxEntries * 2));
|
|
855
|
+
const size = nextPow2$1(Math.max(2, maxEntries * 2));
|
|
871
856
|
return {
|
|
872
857
|
size,
|
|
873
858
|
mask: size - 1,
|
|
@@ -892,7 +877,7 @@ function insertSpatialIndexRaw(index, space, level, x, y, value) {
|
|
|
892
877
|
const l = level & 255;
|
|
893
878
|
const xx = x >>> 0;
|
|
894
879
|
const yy = y >>> 0;
|
|
895
|
-
let slot = hashKey(s, l, xx, yy) & index.mask;
|
|
880
|
+
let slot = hashKey$1(s, l, xx, yy) & index.mask;
|
|
896
881
|
for (let probes = 0; probes < index.size; probes++) {
|
|
897
882
|
if (index.stamp[slot] !== index.stampGen) {
|
|
898
883
|
index.stamp[slot] = index.stampGen;
|
|
@@ -916,7 +901,7 @@ function lookupSpatialIndexRaw(index, space, level, x, y) {
|
|
|
916
901
|
const l = level & 255;
|
|
917
902
|
const xx = x >>> 0;
|
|
918
903
|
const yy = y >>> 0;
|
|
919
|
-
let slot = hashKey(s, l, xx, yy) & index.mask;
|
|
904
|
+
let slot = hashKey$1(s, l, xx, yy) & index.mask;
|
|
920
905
|
for (let probes = 0; probes < index.size; probes++) {
|
|
921
906
|
if (index.stamp[slot] !== index.stampGen) return U32_EMPTY;
|
|
922
907
|
if (index.keysSpace[slot] === s && index.keysLevel[slot] === l && index.keysX[slot] === xx && index.keysY[slot] === yy) {
|
|
@@ -1677,6 +1662,275 @@ function createComputePipelineTasks(leafStageTask) {
|
|
|
1677
1662
|
return { compile, execute };
|
|
1678
1663
|
}
|
|
1679
1664
|
|
|
1665
|
+
const SLOT_STRIDE = 6;
|
|
1666
|
+
function nextPow2(n) {
|
|
1667
|
+
let x = 1;
|
|
1668
|
+
while (x < n) x <<= 1;
|
|
1669
|
+
return x;
|
|
1670
|
+
}
|
|
1671
|
+
function createGpuSpatialIndex(maxEntries) {
|
|
1672
|
+
const size = nextPow2(Math.max(2, maxEntries * 2));
|
|
1673
|
+
const data = new Uint32Array(size * SLOT_STRIDE);
|
|
1674
|
+
const attribute = new webgpu.StorageBufferAttribute(data, SLOT_STRIDE);
|
|
1675
|
+
const node = tsl.storage(attribute, "u32", 1).toReadOnly().setName("gpuSpatialIndex");
|
|
1676
|
+
const stampGen = tsl.uniform(tsl.uint(1)).setName("uGpuSpatialIndexStampGen");
|
|
1677
|
+
return {
|
|
1678
|
+
data,
|
|
1679
|
+
size,
|
|
1680
|
+
mask: size - 1,
|
|
1681
|
+
stampGen,
|
|
1682
|
+
attribute,
|
|
1683
|
+
node
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
function uploadGpuSpatialIndex(gpuIndex, cpuIndex) {
|
|
1687
|
+
if (gpuIndex.size !== cpuIndex.size) {
|
|
1688
|
+
throw new Error(
|
|
1689
|
+
`Spatial index size mismatch (gpu=${gpuIndex.size}, cpu=${cpuIndex.size}).`
|
|
1690
|
+
);
|
|
1691
|
+
}
|
|
1692
|
+
for (let i = 0; i < cpuIndex.size; i += 1) {
|
|
1693
|
+
const base = i * SLOT_STRIDE;
|
|
1694
|
+
gpuIndex.data[base] = cpuIndex.stamp[i] ?? 0;
|
|
1695
|
+
gpuIndex.data[base + 1] = cpuIndex.keysSpace[i] ?? 0;
|
|
1696
|
+
gpuIndex.data[base + 2] = cpuIndex.keysLevel[i] ?? 0;
|
|
1697
|
+
gpuIndex.data[base + 3] = cpuIndex.keysX[i] ?? 0;
|
|
1698
|
+
gpuIndex.data[base + 4] = cpuIndex.keysY[i] ?? 0;
|
|
1699
|
+
gpuIndex.data[base + 5] = cpuIndex.values[i] ?? 0;
|
|
1700
|
+
}
|
|
1701
|
+
gpuIndex.stampGen.value = cpuIndex.stampGen >>> 0;
|
|
1702
|
+
gpuIndex.attribute.needsUpdate = true;
|
|
1703
|
+
gpuIndex.node.needsUpdate = true;
|
|
1704
|
+
}
|
|
1705
|
+
function readGpuSpatialIndexValue(spatialIndex, slot, fieldOffset) {
|
|
1706
|
+
const offset = tsl.int(slot).mul(tsl.int(SLOT_STRIDE)).add(tsl.int(fieldOffset));
|
|
1707
|
+
return spatialIndex.node.element(offset).toUint();
|
|
1708
|
+
}
|
|
1709
|
+
const mix32 = tsl.Fn(([x]) => {
|
|
1710
|
+
const v = tsl.uint(x).toVar();
|
|
1711
|
+
v.assign(v.bitXor(v.shiftRight(tsl.uint(16))));
|
|
1712
|
+
v.assign(v.mul(tsl.uint(2146121005)));
|
|
1713
|
+
v.assign(v.bitXor(v.shiftRight(tsl.uint(15))));
|
|
1714
|
+
v.assign(v.mul(tsl.uint(2221713035)));
|
|
1715
|
+
v.assign(v.bitXor(v.shiftRight(tsl.uint(16))));
|
|
1716
|
+
return v;
|
|
1717
|
+
});
|
|
1718
|
+
const hashKey = tsl.Fn(([space, level, x, y]) => {
|
|
1719
|
+
const s = tsl.uint(space).bitAnd(tsl.uint(255));
|
|
1720
|
+
const l = tsl.uint(level).bitAnd(tsl.uint(255));
|
|
1721
|
+
const h = s.bitXor(l.shiftLeft(tsl.uint(8))).bitXor(mix32(tsl.uint(x))).bitXor(mix32(tsl.uint(y)));
|
|
1722
|
+
return mix32(h);
|
|
1723
|
+
});
|
|
1724
|
+
const createGpuSpatialLookup = (spatialIndex) => {
|
|
1725
|
+
const slotCount = spatialIndex.size;
|
|
1726
|
+
const mask = tsl.uint(spatialIndex.mask);
|
|
1727
|
+
const stampGen = spatialIndex.stampGen.toUint();
|
|
1728
|
+
const emptyValue = tsl.int(-1);
|
|
1729
|
+
return tsl.Fn(([space, level, x, y]) => {
|
|
1730
|
+
const s = tsl.uint(space).bitAnd(tsl.uint(255));
|
|
1731
|
+
const l = tsl.uint(level).bitAnd(tsl.uint(255));
|
|
1732
|
+
const xx = tsl.uint(x);
|
|
1733
|
+
const yy = tsl.uint(y);
|
|
1734
|
+
const result = emptyValue.toVar();
|
|
1735
|
+
const slot = hashKey(s, l, xx, yy).bitAnd(mask).toVar();
|
|
1736
|
+
const probes = tsl.int(0).toVar();
|
|
1737
|
+
tsl.Loop(slotCount, () => {
|
|
1738
|
+
const stamp = readGpuSpatialIndexValue(spatialIndex, slot, 0);
|
|
1739
|
+
tsl.If(stamp.notEqual(stampGen), () => {
|
|
1740
|
+
tsl.Break();
|
|
1741
|
+
});
|
|
1742
|
+
const ks = readGpuSpatialIndexValue(spatialIndex, slot, 1);
|
|
1743
|
+
const kl = readGpuSpatialIndexValue(spatialIndex, slot, 2);
|
|
1744
|
+
const kx = readGpuSpatialIndexValue(spatialIndex, slot, 3);
|
|
1745
|
+
const ky = readGpuSpatialIndexValue(spatialIndex, slot, 4);
|
|
1746
|
+
tsl.If(
|
|
1747
|
+
ks.equal(s).and(kl.equal(l)).and(kx.equal(xx)).and(ky.equal(yy)),
|
|
1748
|
+
() => {
|
|
1749
|
+
result.assign(tsl.int(readGpuSpatialIndexValue(spatialIndex, slot, 5)));
|
|
1750
|
+
tsl.Break();
|
|
1751
|
+
}
|
|
1752
|
+
);
|
|
1753
|
+
slot.assign(slot.add(tsl.uint(1)).bitAnd(mask));
|
|
1754
|
+
probes.addAssign(1);
|
|
1755
|
+
});
|
|
1756
|
+
return result;
|
|
1757
|
+
});
|
|
1758
|
+
};
|
|
1759
|
+
const createTileIndexFromWorldPosition = (spatialIndex, uniforms, maxLevel) => {
|
|
1760
|
+
const lookup = createGpuSpatialLookup(spatialIndex);
|
|
1761
|
+
const levelCount = Math.max(1, maxLevel + 1);
|
|
1762
|
+
return tsl.Fn(([worldX, worldZ]) => {
|
|
1763
|
+
const rootOrigin = uniforms.uRootOrigin.toVar();
|
|
1764
|
+
const rootSize = uniforms.uRootSize.toVar();
|
|
1765
|
+
const halfRoot = rootSize.mul(tsl.float(0.5));
|
|
1766
|
+
const tileIndex = tsl.int(-1).toVar();
|
|
1767
|
+
const tileU = tsl.float(0).toVar();
|
|
1768
|
+
const tileV = tsl.float(0).toVar();
|
|
1769
|
+
const i = tsl.int(0).toVar();
|
|
1770
|
+
tsl.Loop(levelCount, () => {
|
|
1771
|
+
const level = tsl.int(maxLevel).sub(i).toVar();
|
|
1772
|
+
const scale = tsl.pow(tsl.float(2), level.toFloat());
|
|
1773
|
+
const tileSize = rootSize.div(scale);
|
|
1774
|
+
const tileX = worldX.sub(rootOrigin.x).add(halfRoot).div(tileSize).floor().toInt();
|
|
1775
|
+
const tileY = worldZ.sub(rootOrigin.z).add(halfRoot).div(tileSize).floor().toInt();
|
|
1776
|
+
const maybeIndex = lookup(tsl.int(0), level, tileX, tileY).toVar();
|
|
1777
|
+
tsl.If(maybeIndex.greaterThanEqual(tsl.int(0)), () => {
|
|
1778
|
+
const minX = rootOrigin.x.add(tileX.toFloat().mul(tileSize)).sub(halfRoot);
|
|
1779
|
+
const minZ = rootOrigin.z.add(tileY.toFloat().mul(tileSize)).sub(halfRoot);
|
|
1780
|
+
tileIndex.assign(maybeIndex);
|
|
1781
|
+
tileU.assign(worldX.sub(minX).div(tileSize));
|
|
1782
|
+
tileV.assign(worldZ.sub(minZ).div(tileSize));
|
|
1783
|
+
tsl.Break();
|
|
1784
|
+
});
|
|
1785
|
+
i.addAssign(1);
|
|
1786
|
+
});
|
|
1787
|
+
return tsl.vec3(tileIndex.toFloat(), tileU, tileV);
|
|
1788
|
+
});
|
|
1789
|
+
};
|
|
1790
|
+
|
|
1791
|
+
const gpuSpatialIndexStorageTask = work.task((get, work) => {
|
|
1792
|
+
const maxNodesValue = get(maxNodes);
|
|
1793
|
+
return work(() => createGpuSpatialIndex(maxNodesValue));
|
|
1794
|
+
}).displayName("gpuSpatialIndexStorageTask");
|
|
1795
|
+
const gpuSpatialIndexUploadTask = work.task((get, work) => {
|
|
1796
|
+
const quadtreeConfig = get(quadtreeConfigTask);
|
|
1797
|
+
get(quadtreeUpdateTask);
|
|
1798
|
+
const gpuSpatialIndex = get(gpuSpatialIndexStorageTask);
|
|
1799
|
+
return work(() => {
|
|
1800
|
+
uploadGpuSpatialIndex(gpuSpatialIndex, quadtreeConfig.state.leafIndex);
|
|
1801
|
+
return gpuSpatialIndex;
|
|
1802
|
+
});
|
|
1803
|
+
}).displayName("gpuSpatialIndexUploadTask");
|
|
1804
|
+
|
|
1805
|
+
function createTerrainSampleNode(params) {
|
|
1806
|
+
const tileLookup = createTileIndexFromWorldPosition(
|
|
1807
|
+
params.spatialIndex,
|
|
1808
|
+
params.uniforms,
|
|
1809
|
+
maxLevel.get()
|
|
1810
|
+
);
|
|
1811
|
+
return tsl.Fn(([worldX, worldZ]) => {
|
|
1812
|
+
const tileResult = tileLookup(worldX, worldZ).toVar();
|
|
1813
|
+
const tileIndex = tsl.int(tileResult.x).toVar();
|
|
1814
|
+
const safeTileIndex = tileIndex.max(tsl.int(0)).toVar();
|
|
1815
|
+
const u = tileResult.y.toVar();
|
|
1816
|
+
const v = tileResult.z.toVar();
|
|
1817
|
+
const fieldU = tileLocalToFieldUV(
|
|
1818
|
+
u,
|
|
1819
|
+
params.uniforms.uInnerTileSegments
|
|
1820
|
+
).toVar();
|
|
1821
|
+
const fieldV = tileLocalToFieldUV(
|
|
1822
|
+
v,
|
|
1823
|
+
params.uniforms.uInnerTileSegments
|
|
1824
|
+
).toVar();
|
|
1825
|
+
const found = tileIndex.greaterThanEqual(tsl.int(0)).toVar();
|
|
1826
|
+
const sampled = sampleTerrainField(
|
|
1827
|
+
params.terrainFieldStorage,
|
|
1828
|
+
fieldU,
|
|
1829
|
+
fieldV,
|
|
1830
|
+
safeTileIndex
|
|
1831
|
+
).toVar();
|
|
1832
|
+
const nx = sampled.g.toVar();
|
|
1833
|
+
const nz = sampled.b.toVar();
|
|
1834
|
+
const ny = tsl.float(1).sub(nx.mul(nx)).sub(nz.mul(nz)).max(0).sqrt();
|
|
1835
|
+
const valid = found.select(tsl.float(1), tsl.float(0)).toVar();
|
|
1836
|
+
return tsl.vec4(
|
|
1837
|
+
sampled.r.mul(valid),
|
|
1838
|
+
nx.mul(valid),
|
|
1839
|
+
ny.mul(valid),
|
|
1840
|
+
nz.mul(valid)
|
|
1841
|
+
);
|
|
1842
|
+
});
|
|
1843
|
+
}
|
|
1844
|
+
function createTerrainSampler(params) {
|
|
1845
|
+
const elevationNode = createElevationFunction(params.elevationCallback);
|
|
1846
|
+
const terrainSampleAt = createTerrainSampleNode(params);
|
|
1847
|
+
const evaluateElevationAt = tsl.Fn(([worldX, worldZ]) => {
|
|
1848
|
+
const rootOrigin = params.uniforms.uRootOrigin.toVar();
|
|
1849
|
+
const rootSize = params.uniforms.uRootSize.toVar();
|
|
1850
|
+
const centeredX = worldX.sub(rootOrigin.x);
|
|
1851
|
+
const centeredZ = worldZ.sub(rootOrigin.z);
|
|
1852
|
+
const rootUV = tsl.vec2(
|
|
1853
|
+
centeredX.div(rootSize).add(0.5),
|
|
1854
|
+
centeredZ.div(rootSize).mul(tsl.float(-1)).add(0.5)
|
|
1855
|
+
).toVar();
|
|
1856
|
+
return elevationNode({
|
|
1857
|
+
worldPosition: tsl.vec3(worldX, rootOrigin.y, worldZ),
|
|
1858
|
+
rootSize,
|
|
1859
|
+
rootUV,
|
|
1860
|
+
tileUV: rootUV,
|
|
1861
|
+
tileLevel: tsl.int(0),
|
|
1862
|
+
tileSize: rootSize,
|
|
1863
|
+
tileOriginVec2: tsl.vec2(0, 0),
|
|
1864
|
+
nodeIndex: tsl.int(0)
|
|
1865
|
+
});
|
|
1866
|
+
});
|
|
1867
|
+
const sampleTerrain = tsl.Fn(
|
|
1868
|
+
([worldX, worldZ]) => terrainSampleAt(worldX, worldZ)
|
|
1869
|
+
);
|
|
1870
|
+
const sampleElevation = tsl.Fn(
|
|
1871
|
+
([worldX, worldZ]) => terrainSampleAt(worldX, worldZ).x
|
|
1872
|
+
);
|
|
1873
|
+
const sampleNormal = tsl.Fn(
|
|
1874
|
+
([worldX, worldZ]) => tsl.vec3(
|
|
1875
|
+
terrainSampleAt(worldX, worldZ).y,
|
|
1876
|
+
terrainSampleAt(worldX, worldZ).z,
|
|
1877
|
+
terrainSampleAt(worldX, worldZ).w
|
|
1878
|
+
)
|
|
1879
|
+
);
|
|
1880
|
+
const sampleValidity = tsl.Fn(
|
|
1881
|
+
([worldX, worldZ]) => terrainSampleAt(worldX, worldZ).y.abs().add(terrainSampleAt(worldX, worldZ).z.abs()).add(terrainSampleAt(worldX, worldZ).w.abs()).greaterThan(tsl.float(0)).select(tsl.float(1), tsl.float(0))
|
|
1882
|
+
);
|
|
1883
|
+
const evaluateElevation = tsl.Fn(
|
|
1884
|
+
([worldX, worldZ]) => evaluateElevationAt(worldX, worldZ)
|
|
1885
|
+
);
|
|
1886
|
+
const evaluateNormalNode = tsl.Fn(
|
|
1887
|
+
([worldX, worldZ, epsilon]) => {
|
|
1888
|
+
const eps = epsilon ?? tsl.float(0.1);
|
|
1889
|
+
const elevationScale = params.uniforms.uElevationScale.toVar();
|
|
1890
|
+
const hL = evaluateElevationAt(worldX.sub(eps), worldZ).mul(
|
|
1891
|
+
elevationScale
|
|
1892
|
+
);
|
|
1893
|
+
const hR = evaluateElevationAt(worldX.add(eps), worldZ).mul(
|
|
1894
|
+
elevationScale
|
|
1895
|
+
);
|
|
1896
|
+
const hD = evaluateElevationAt(worldX, worldZ.sub(eps)).mul(
|
|
1897
|
+
elevationScale
|
|
1898
|
+
);
|
|
1899
|
+
const hU = evaluateElevationAt(worldX, worldZ.add(eps)).mul(
|
|
1900
|
+
elevationScale
|
|
1901
|
+
);
|
|
1902
|
+
const inv2eps = tsl.float(0.5).div(eps);
|
|
1903
|
+
const dhdx = hR.sub(hL).mul(inv2eps);
|
|
1904
|
+
const dhdz = hU.sub(hD).mul(inv2eps);
|
|
1905
|
+
return tsl.vec3(dhdx.negate(), tsl.float(1), dhdz.negate()).normalize();
|
|
1906
|
+
}
|
|
1907
|
+
);
|
|
1908
|
+
const evaluateNormal = (worldX, worldZ, epsilon) => evaluateNormalNode(worldX, worldZ, epsilon ?? tsl.float(0.1));
|
|
1909
|
+
return {
|
|
1910
|
+
sampleElevation,
|
|
1911
|
+
sampleNormal,
|
|
1912
|
+
sampleTerrain,
|
|
1913
|
+
sampleValidity,
|
|
1914
|
+
evaluateElevation,
|
|
1915
|
+
evaluateNormal
|
|
1916
|
+
};
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
const createTerrainSamplerTask = work.task((get, work) => {
|
|
1920
|
+
const terrainFieldStorage = get(createTerrainFieldTextureTask);
|
|
1921
|
+
const spatialIndex = get(gpuSpatialIndexStorageTask);
|
|
1922
|
+
const uniforms = get(createUniformsTask);
|
|
1923
|
+
const elevationCallback = get(elevationFn);
|
|
1924
|
+
return work(
|
|
1925
|
+
() => createTerrainSampler({
|
|
1926
|
+
terrainFieldStorage,
|
|
1927
|
+
spatialIndex,
|
|
1928
|
+
uniforms,
|
|
1929
|
+
elevationCallback
|
|
1930
|
+
})
|
|
1931
|
+
);
|
|
1932
|
+
}).displayName("createTerrainSamplerTask");
|
|
1933
|
+
|
|
1680
1934
|
const isSkirtVertex = tsl.Fn(([segments]) => {
|
|
1681
1935
|
const segmentsNode = typeof segments === "number" ? tsl.int(segments) : segments;
|
|
1682
1936
|
const vIndex = tsl.int(tsl.vertexIndex);
|
|
@@ -1720,14 +1974,15 @@ function createTileBaseWorldPosition(leafStorage, terrainUniforms) {
|
|
|
1720
1974
|
}
|
|
1721
1975
|
function createTileElevation(terrainUniforms, terrainFieldStorage) {
|
|
1722
1976
|
if (!terrainFieldStorage) return tsl.float(0);
|
|
1723
|
-
const
|
|
1724
|
-
|
|
1977
|
+
const innerSegs = terrainUniforms.uInnerTileSegments;
|
|
1978
|
+
const u = tileLocalToFieldUV(tsl.positionLocal.x.add(tsl.float(0.5)), innerSegs);
|
|
1979
|
+
const v = tileLocalToFieldUV(tsl.positionLocal.z.add(tsl.float(0.5)), innerSegs);
|
|
1980
|
+
return sampleTerrainFieldElevation(
|
|
1725
1981
|
terrainFieldStorage,
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
);
|
|
1982
|
+
u,
|
|
1983
|
+
v,
|
|
1984
|
+
tsl.int(tsl.instanceIndex)
|
|
1985
|
+
).mul(terrainUniforms.uElevationScale);
|
|
1731
1986
|
}
|
|
1732
1987
|
function createNormalAssignment(terrainUniforms, terrainFieldStorage) {
|
|
1733
1988
|
if (!terrainFieldStorage) return;
|
|
@@ -1736,7 +1991,12 @@ function createNormalAssignment(terrainUniforms, terrainFieldStorage) {
|
|
|
1736
1991
|
const localVertexIndex = tsl.int(tsl.vertexIndex);
|
|
1737
1992
|
const ix = localVertexIndex.mod(edgeVertexCount);
|
|
1738
1993
|
const iy = localVertexIndex.div(edgeVertexCount);
|
|
1739
|
-
const normalXZ = loadTerrainFieldNormal(
|
|
1994
|
+
const normalXZ = loadTerrainFieldNormal(
|
|
1995
|
+
terrainFieldStorage,
|
|
1996
|
+
ix,
|
|
1997
|
+
iy,
|
|
1998
|
+
nodeIndex
|
|
1999
|
+
);
|
|
1740
2000
|
const nx = normalXZ.x;
|
|
1741
2001
|
const nz = normalXZ.y;
|
|
1742
2002
|
const nySq = tsl.float(1).sub(nx.mul(nx)).sub(nz.mul(nz)).max(tsl.float(0));
|
|
@@ -1744,10 +2004,16 @@ function createNormalAssignment(terrainUniforms, terrainFieldStorage) {
|
|
|
1744
2004
|
tsl.normalLocal.assign(tsl.vec3(nx, ny, nz));
|
|
1745
2005
|
}
|
|
1746
2006
|
function createTileWorldPosition(leafStorage, terrainUniforms, terrainFieldStorage) {
|
|
1747
|
-
const baseWorldPosition = createTileBaseWorldPosition(
|
|
2007
|
+
const baseWorldPosition = createTileBaseWorldPosition(
|
|
2008
|
+
leafStorage,
|
|
2009
|
+
terrainUniforms
|
|
2010
|
+
);
|
|
1748
2011
|
return tsl.Fn(() => {
|
|
1749
2012
|
const base = baseWorldPosition();
|
|
1750
|
-
const yElevation = createTileElevation(
|
|
2013
|
+
const yElevation = createTileElevation(
|
|
2014
|
+
terrainUniforms,
|
|
2015
|
+
terrainFieldStorage
|
|
2016
|
+
);
|
|
1751
2017
|
const skirtVertex = isSkirtVertex(terrainUniforms.uInnerTileSegments);
|
|
1752
2018
|
const skirtY = base.y.add(yElevation).sub(terrainUniforms.uSkirtScale.toVar());
|
|
1753
2019
|
const worldY = tsl.select(skirtVertex, skirtY, base.y.add(yElevation));
|
|
@@ -1770,7 +2036,7 @@ const positionNodeTask = work.task((get, work) => {
|
|
|
1770
2036
|
}).displayName("positionNodeTask");
|
|
1771
2037
|
|
|
1772
2038
|
function terrainGraph() {
|
|
1773
|
-
return work.graph().add(instanceIdTask).add(quadtreeConfigTask).add(quadtreeUpdateTask).add(leafStorageTask).add(surfaceTask).add(leafGpuBufferTask).add(createUniformsTask).add(updateUniformsTask).add(positionNodeTask).add(createElevationFieldContextTask).add(tileNodesTask).add(createTerrainFieldTextureTask).add(elevationFieldStageTask).add(terrainFieldStageTask).add(compileComputeTask).add(executeComputeTask);
|
|
2039
|
+
return work.graph().add(instanceIdTask).add(quadtreeConfigTask).add(quadtreeUpdateTask).add(leafStorageTask).add(surfaceTask).add(leafGpuBufferTask).add(gpuSpatialIndexStorageTask).add(gpuSpatialIndexUploadTask).add(createUniformsTask).add(updateUniformsTask).add(positionNodeTask).add(createElevationFieldContextTask).add(tileNodesTask).add(createTerrainFieldTextureTask).add(createTerrainSamplerTask).add(elevationFieldStageTask).add(terrainFieldStageTask).add(compileComputeTask).add(executeComputeTask);
|
|
1774
2040
|
}
|
|
1775
2041
|
const terrainTasks = {
|
|
1776
2042
|
instanceId: instanceIdTask,
|
|
@@ -1779,12 +2045,15 @@ const terrainTasks = {
|
|
|
1779
2045
|
leafStorage: leafStorageTask,
|
|
1780
2046
|
surface: surfaceTask,
|
|
1781
2047
|
leafGpuBuffer: leafGpuBufferTask,
|
|
2048
|
+
gpuSpatialIndexStorage: gpuSpatialIndexStorageTask,
|
|
2049
|
+
gpuSpatialIndexUpload: gpuSpatialIndexUploadTask,
|
|
1782
2050
|
createUniforms: createUniformsTask,
|
|
1783
2051
|
updateUniforms: updateUniformsTask,
|
|
1784
2052
|
positionNode: positionNodeTask,
|
|
1785
2053
|
createElevationFieldContext: createElevationFieldContextTask,
|
|
1786
2054
|
createTileNodes: tileNodesTask,
|
|
1787
2055
|
createTerrainFieldTexture: createTerrainFieldTextureTask,
|
|
2056
|
+
createTerrainSampler: createTerrainSamplerTask,
|
|
1788
2057
|
elevationFieldStage: elevationFieldStageTask,
|
|
1789
2058
|
terrainFieldStage: terrainFieldStageTask,
|
|
1790
2059
|
compileCompute: compileComputeTask,
|
|
@@ -1866,6 +2135,8 @@ exports.createSpatialIndex = createSpatialIndex;
|
|
|
1866
2135
|
exports.createState = createState;
|
|
1867
2136
|
exports.createTerrainFieldStorage = createTerrainFieldStorage;
|
|
1868
2137
|
exports.createTerrainFieldTextureTask = createTerrainFieldTextureTask;
|
|
2138
|
+
exports.createTerrainSampler = createTerrainSampler;
|
|
2139
|
+
exports.createTerrainSamplerTask = createTerrainSamplerTask;
|
|
1869
2140
|
exports.createTerrainUniforms = createTerrainUniforms;
|
|
1870
2141
|
exports.createUniformsTask = createUniformsTask;
|
|
1871
2142
|
exports.deriveNormalZ = deriveNormalZ;
|
|
@@ -1874,6 +2145,8 @@ exports.elevationFn = elevationFn;
|
|
|
1874
2145
|
exports.elevationScale = elevationScale;
|
|
1875
2146
|
exports.executeComputeTask = executeComputeTask;
|
|
1876
2147
|
exports.getDeviceComputeLimits = getDeviceComputeLimits;
|
|
2148
|
+
exports.gpuSpatialIndexStorageTask = gpuSpatialIndexStorageTask;
|
|
2149
|
+
exports.gpuSpatialIndexUploadTask = gpuSpatialIndexUploadTask;
|
|
1877
2150
|
exports.innerTileSegments = innerTileSegments;
|
|
1878
2151
|
exports.instanceIdTask = instanceIdTask;
|
|
1879
2152
|
exports.isSkirtUV = isSkirtUV;
|
package/dist/index.d.cts
CHANGED
|
@@ -458,6 +458,41 @@ declare function createTileCompute(leafStorage: LeafStorageState, uniforms: Terr
|
|
|
458
458
|
tileVertexWorldPositionCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node, number | Node, number | Node]>;
|
|
459
459
|
};
|
|
460
460
|
|
|
461
|
+
interface ElevationParams {
|
|
462
|
+
worldPosition: Node$1;
|
|
463
|
+
rootSize: Node$1;
|
|
464
|
+
rootUV: Node$1;
|
|
465
|
+
tileUV: Node$1;
|
|
466
|
+
tileLevel: Node$1;
|
|
467
|
+
tileSize: Node$1;
|
|
468
|
+
tileOriginVec2: Node$1;
|
|
469
|
+
nodeIndex: Node$1;
|
|
470
|
+
}
|
|
471
|
+
type ElevationCallback = (params: ElevationParams) => Node$1;
|
|
472
|
+
|
|
473
|
+
interface GpuSpatialIndexContext {
|
|
474
|
+
data: Uint32Array<ArrayBuffer>;
|
|
475
|
+
size: number;
|
|
476
|
+
mask: number;
|
|
477
|
+
stampGen: UniformNode<number>;
|
|
478
|
+
attribute: StorageBufferAttribute;
|
|
479
|
+
node: StorageBufferNode;
|
|
480
|
+
}
|
|
481
|
+
interface TerrainSampler {
|
|
482
|
+
sampleElevation: (worldX: Node, worldZ: Node) => Node;
|
|
483
|
+
sampleNormal: (worldX: Node, worldZ: Node) => Node;
|
|
484
|
+
sampleTerrain: (worldX: Node, worldZ: Node) => Node;
|
|
485
|
+
sampleValidity: (worldX: Node, worldZ: Node) => Node;
|
|
486
|
+
evaluateElevation: (worldX: Node, worldZ: Node) => Node;
|
|
487
|
+
evaluateNormal: (worldX: Node, worldZ: Node, epsilon?: Node) => Node;
|
|
488
|
+
}
|
|
489
|
+
interface CreateTerrainSamplerParams {
|
|
490
|
+
terrainFieldStorage: TerrainFieldStorage;
|
|
491
|
+
spatialIndex: GpuSpatialIndexContext;
|
|
492
|
+
uniforms: TerrainUniformsContext;
|
|
493
|
+
elevationCallback: ElevationCallback;
|
|
494
|
+
}
|
|
495
|
+
|
|
461
496
|
interface QuadtreeConfigState {
|
|
462
497
|
state: QuadtreeState;
|
|
463
498
|
surface: Surface;
|
|
@@ -478,12 +513,15 @@ interface TerrainTasks {
|
|
|
478
513
|
surface: TaskRef<Surface>;
|
|
479
514
|
leafStorage: TaskRef<LeafStorageState>;
|
|
480
515
|
leafGpuBuffer: TaskRef<LeafGpuBufferState>;
|
|
516
|
+
gpuSpatialIndexStorage: TaskRef<GpuSpatialIndexContext>;
|
|
517
|
+
gpuSpatialIndexUpload: TaskRef<GpuSpatialIndexContext>;
|
|
481
518
|
createUniforms: TaskRef<TerrainUniformsContext>;
|
|
482
519
|
updateUniforms: TaskRef<TerrainUniformsContext>;
|
|
483
520
|
positionNode: TaskRef<ShaderCallNodeInternal>;
|
|
484
521
|
createElevationFieldContext: TaskRef<ElevationFieldContext>;
|
|
485
522
|
createTileNodes: TaskRef<ReturnType<typeof createTileCompute>>;
|
|
486
523
|
createTerrainFieldTexture: TaskRef<TerrainFieldStorage>;
|
|
524
|
+
createTerrainSampler: TaskRef<TerrainSampler>;
|
|
487
525
|
elevationFieldStage: TaskRef<ComputePipeline>;
|
|
488
526
|
terrainFieldStage: TaskRef<ComputePipeline>;
|
|
489
527
|
compileCompute: TaskRef<{
|
|
@@ -513,6 +551,9 @@ declare const tileNodesTask: _hello_terrain_work.Task<{
|
|
|
513
551
|
*/
|
|
514
552
|
declare const elevationFieldStageTask: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
|
|
515
553
|
|
|
554
|
+
declare const gpuSpatialIndexStorageTask: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
|
|
555
|
+
declare const gpuSpatialIndexUploadTask: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
|
|
556
|
+
|
|
516
557
|
/** Generates a unique instance ID per graph (cached once). */
|
|
517
558
|
declare const instanceIdTask: _hello_terrain_work.Task<`${string}-${string}-${string}-${string}-${string}`, string, unknown>;
|
|
518
559
|
|
|
@@ -529,17 +570,7 @@ declare const createTerrainFieldTextureTask: _hello_terrain_work.Task<any, strin
|
|
|
529
570
|
*/
|
|
530
571
|
declare const terrainFieldStageTask: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
|
|
531
572
|
|
|
532
|
-
|
|
533
|
-
worldPosition: Node$1;
|
|
534
|
-
rootSize: Node$1;
|
|
535
|
-
rootUV: Node$1;
|
|
536
|
-
tileUV: Node$1;
|
|
537
|
-
tileLevel: Node$1;
|
|
538
|
-
tileSize: Node$1;
|
|
539
|
-
tileOriginVec2: Node$1;
|
|
540
|
-
nodeIndex: Node$1;
|
|
541
|
-
}
|
|
542
|
-
type ElevationCallback = (params: ElevationParams) => Node$1;
|
|
573
|
+
declare const createTerrainSamplerTask: _hello_terrain_work.Task<TerrainSampler, string, unknown>;
|
|
543
574
|
|
|
544
575
|
/** Root tile size in world units. */
|
|
545
576
|
declare const rootSize: _hello_terrain_work.ParamRef<number>;
|
|
@@ -644,6 +675,8 @@ declare const terrainTasks: {
|
|
|
644
675
|
attribute: three_webgpu.StorageBufferAttribute;
|
|
645
676
|
node: three_webgpu.StorageBufferNode;
|
|
646
677
|
}, string, unknown>;
|
|
678
|
+
readonly gpuSpatialIndexStorage: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
|
|
679
|
+
readonly gpuSpatialIndexUpload: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
|
|
647
680
|
readonly createUniforms: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
|
|
648
681
|
readonly updateUniforms: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
|
|
649
682
|
readonly positionNode: _hello_terrain_work.Task<three_src_nodes_TSL_js.ShaderCallNodeInternal, string, unknown>;
|
|
@@ -662,6 +695,7 @@ declare const terrainTasks: {
|
|
|
662
695
|
readonly createTerrainFieldTexture: _hello_terrain_work.Task<any, string, {
|
|
663
696
|
renderer: WebGPURenderer;
|
|
664
697
|
}>;
|
|
698
|
+
readonly createTerrainSampler: _hello_terrain_work.Task<TerrainSampler, string, unknown>;
|
|
665
699
|
readonly elevationFieldStage: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
|
|
666
700
|
readonly terrainFieldStage: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
|
|
667
701
|
readonly compileCompute: _hello_terrain_work.Task<{
|
|
@@ -679,6 +713,8 @@ type ComputeDeviceLimits = {
|
|
|
679
713
|
};
|
|
680
714
|
declare function getDeviceComputeLimits(renderer: WebGPURenderer): ComputeDeviceLimits;
|
|
681
715
|
|
|
716
|
+
declare function createTerrainSampler(params: CreateTerrainSamplerParams): TerrainSampler;
|
|
717
|
+
|
|
682
718
|
/**
|
|
683
719
|
* Maps a value or node from texture space [0, 1] to vector space [-1, 1].
|
|
684
720
|
*
|
|
@@ -751,5 +787,5 @@ declare const voronoiCells: three_src_nodes_TSL_js.ShaderNodeFn<[three_tsl.Proxi
|
|
|
751
787
|
uv: Node;
|
|
752
788
|
}>]>;
|
|
753
789
|
|
|
754
|
-
export { ArrayTextureBackend, AtlasBackend, Dir, TerrainGeometry, TerrainMesh, Texture3DBackend, U32_EMPTY, allocLeafSet, allocSeamTable, beginUpdate, blendAngleCorrectedNormals, buildLeafIndex, buildSeams2to1, compileComputeTask, createComputePipelineTasks, createCubeSphereSurface, createElevationFieldContextTask, createFlatSurface, createInfiniteFlatSurface, createSpatialIndex, createState, createTerrainFieldStorage, createTerrainFieldTextureTask, createTerrainUniforms, createUniformsTask, deriveNormalZ, elevationFieldStageTask, elevationFn, elevationScale, executeComputeTask, getDeviceComputeLimits, innerTileSegments, instanceIdTask, isSkirtUV, isSkirtVertex, leafGpuBufferTask, leafStorageTask, loadTerrainField, loadTerrainFieldElevation, loadTerrainFieldNormal, maxLevel, maxNodes, origin, packTerrainFieldSample, positionNodeTask, quadtreeConfigTask, quadtreeUpdate, quadtreeUpdateTask, resetLeafSet, resetSeamTable, rootSize, sampleTerrainField, sampleTerrainFieldElevation, sampleTerrainFieldNormal, skirtScale, storeTerrainField, surface, surfaceTask, terrainFieldFilter, terrainFieldStageTask, terrainGraph, terrainTasks, textureSpaceToVectorSpace, tileNodesTask, update, updateUniformsTask, vElevation, vGlobalVertexIndex, vectorSpaceToTextureSpace, voronoiCells };
|
|
755
|
-
export type { ComputePipeline, ComputeStageCallback, CubeSphereSurfaceConfig, ElevationCallback, ElevationFieldContext, ElevationParams, FlatSurfaceConfig, InfiniteFlatSurfaceConfig, IntNodeInput, LeafGpuBufferState, LeafSet, LeafStorageState, LodMode, QuadtreeConfig, QuadtreeConfigState, QuadtreeState, SeamTable, SpatialIndex, Surface, TerrainFieldStorage, TerrainFieldStorageBackendType, TerrainFieldStorageFormat, TerrainFieldStorageOptions, TerrainGraph, TerrainTasks, TerrainUniformsContext, TerrainUniformsParams, TileBounds, TileId, UpdateParams };
|
|
790
|
+
export { ArrayTextureBackend, AtlasBackend, Dir, TerrainGeometry, TerrainMesh, Texture3DBackend, U32_EMPTY, allocLeafSet, allocSeamTable, beginUpdate, blendAngleCorrectedNormals, buildLeafIndex, buildSeams2to1, compileComputeTask, createComputePipelineTasks, createCubeSphereSurface, createElevationFieldContextTask, createFlatSurface, createInfiniteFlatSurface, createSpatialIndex, createState, createTerrainFieldStorage, createTerrainFieldTextureTask, createTerrainSampler, createTerrainSamplerTask, createTerrainUniforms, createUniformsTask, deriveNormalZ, elevationFieldStageTask, elevationFn, elevationScale, executeComputeTask, getDeviceComputeLimits, gpuSpatialIndexStorageTask, gpuSpatialIndexUploadTask, innerTileSegments, instanceIdTask, isSkirtUV, isSkirtVertex, leafGpuBufferTask, leafStorageTask, loadTerrainField, loadTerrainFieldElevation, loadTerrainFieldNormal, maxLevel, maxNodes, origin, packTerrainFieldSample, positionNodeTask, quadtreeConfigTask, quadtreeUpdate, quadtreeUpdateTask, resetLeafSet, resetSeamTable, rootSize, sampleTerrainField, sampleTerrainFieldElevation, sampleTerrainFieldNormal, skirtScale, storeTerrainField, surface, surfaceTask, terrainFieldFilter, terrainFieldStageTask, terrainGraph, terrainTasks, textureSpaceToVectorSpace, tileNodesTask, update, updateUniformsTask, vElevation, vGlobalVertexIndex, vectorSpaceToTextureSpace, voronoiCells };
|
|
791
|
+
export type { ComputePipeline, ComputeStageCallback, CreateTerrainSamplerParams, CubeSphereSurfaceConfig, ElevationCallback, ElevationFieldContext, ElevationParams, FlatSurfaceConfig, GpuSpatialIndexContext, InfiniteFlatSurfaceConfig, IntNodeInput, LeafGpuBufferState, LeafSet, LeafStorageState, LodMode, QuadtreeConfig, QuadtreeConfigState, QuadtreeState, SeamTable, SpatialIndex, Surface, TerrainFieldStorage, TerrainFieldStorageBackendType, TerrainFieldStorageFormat, TerrainFieldStorageOptions, TerrainGraph, TerrainSampler, TerrainTasks, TerrainUniformsContext, TerrainUniformsParams, TileBounds, TileId, UpdateParams };
|
package/dist/index.d.mts
CHANGED
|
@@ -458,6 +458,41 @@ declare function createTileCompute(leafStorage: LeafStorageState, uniforms: Terr
|
|
|
458
458
|
tileVertexWorldPositionCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node, number | Node, number | Node]>;
|
|
459
459
|
};
|
|
460
460
|
|
|
461
|
+
interface ElevationParams {
|
|
462
|
+
worldPosition: Node$1;
|
|
463
|
+
rootSize: Node$1;
|
|
464
|
+
rootUV: Node$1;
|
|
465
|
+
tileUV: Node$1;
|
|
466
|
+
tileLevel: Node$1;
|
|
467
|
+
tileSize: Node$1;
|
|
468
|
+
tileOriginVec2: Node$1;
|
|
469
|
+
nodeIndex: Node$1;
|
|
470
|
+
}
|
|
471
|
+
type ElevationCallback = (params: ElevationParams) => Node$1;
|
|
472
|
+
|
|
473
|
+
interface GpuSpatialIndexContext {
|
|
474
|
+
data: Uint32Array<ArrayBuffer>;
|
|
475
|
+
size: number;
|
|
476
|
+
mask: number;
|
|
477
|
+
stampGen: UniformNode<number>;
|
|
478
|
+
attribute: StorageBufferAttribute;
|
|
479
|
+
node: StorageBufferNode;
|
|
480
|
+
}
|
|
481
|
+
interface TerrainSampler {
|
|
482
|
+
sampleElevation: (worldX: Node, worldZ: Node) => Node;
|
|
483
|
+
sampleNormal: (worldX: Node, worldZ: Node) => Node;
|
|
484
|
+
sampleTerrain: (worldX: Node, worldZ: Node) => Node;
|
|
485
|
+
sampleValidity: (worldX: Node, worldZ: Node) => Node;
|
|
486
|
+
evaluateElevation: (worldX: Node, worldZ: Node) => Node;
|
|
487
|
+
evaluateNormal: (worldX: Node, worldZ: Node, epsilon?: Node) => Node;
|
|
488
|
+
}
|
|
489
|
+
interface CreateTerrainSamplerParams {
|
|
490
|
+
terrainFieldStorage: TerrainFieldStorage;
|
|
491
|
+
spatialIndex: GpuSpatialIndexContext;
|
|
492
|
+
uniforms: TerrainUniformsContext;
|
|
493
|
+
elevationCallback: ElevationCallback;
|
|
494
|
+
}
|
|
495
|
+
|
|
461
496
|
interface QuadtreeConfigState {
|
|
462
497
|
state: QuadtreeState;
|
|
463
498
|
surface: Surface;
|
|
@@ -478,12 +513,15 @@ interface TerrainTasks {
|
|
|
478
513
|
surface: TaskRef<Surface>;
|
|
479
514
|
leafStorage: TaskRef<LeafStorageState>;
|
|
480
515
|
leafGpuBuffer: TaskRef<LeafGpuBufferState>;
|
|
516
|
+
gpuSpatialIndexStorage: TaskRef<GpuSpatialIndexContext>;
|
|
517
|
+
gpuSpatialIndexUpload: TaskRef<GpuSpatialIndexContext>;
|
|
481
518
|
createUniforms: TaskRef<TerrainUniformsContext>;
|
|
482
519
|
updateUniforms: TaskRef<TerrainUniformsContext>;
|
|
483
520
|
positionNode: TaskRef<ShaderCallNodeInternal>;
|
|
484
521
|
createElevationFieldContext: TaskRef<ElevationFieldContext>;
|
|
485
522
|
createTileNodes: TaskRef<ReturnType<typeof createTileCompute>>;
|
|
486
523
|
createTerrainFieldTexture: TaskRef<TerrainFieldStorage>;
|
|
524
|
+
createTerrainSampler: TaskRef<TerrainSampler>;
|
|
487
525
|
elevationFieldStage: TaskRef<ComputePipeline>;
|
|
488
526
|
terrainFieldStage: TaskRef<ComputePipeline>;
|
|
489
527
|
compileCompute: TaskRef<{
|
|
@@ -513,6 +551,9 @@ declare const tileNodesTask: _hello_terrain_work.Task<{
|
|
|
513
551
|
*/
|
|
514
552
|
declare const elevationFieldStageTask: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
|
|
515
553
|
|
|
554
|
+
declare const gpuSpatialIndexStorageTask: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
|
|
555
|
+
declare const gpuSpatialIndexUploadTask: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
|
|
556
|
+
|
|
516
557
|
/** Generates a unique instance ID per graph (cached once). */
|
|
517
558
|
declare const instanceIdTask: _hello_terrain_work.Task<`${string}-${string}-${string}-${string}-${string}`, string, unknown>;
|
|
518
559
|
|
|
@@ -529,17 +570,7 @@ declare const createTerrainFieldTextureTask: _hello_terrain_work.Task<any, strin
|
|
|
529
570
|
*/
|
|
530
571
|
declare const terrainFieldStageTask: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
|
|
531
572
|
|
|
532
|
-
|
|
533
|
-
worldPosition: Node$1;
|
|
534
|
-
rootSize: Node$1;
|
|
535
|
-
rootUV: Node$1;
|
|
536
|
-
tileUV: Node$1;
|
|
537
|
-
tileLevel: Node$1;
|
|
538
|
-
tileSize: Node$1;
|
|
539
|
-
tileOriginVec2: Node$1;
|
|
540
|
-
nodeIndex: Node$1;
|
|
541
|
-
}
|
|
542
|
-
type ElevationCallback = (params: ElevationParams) => Node$1;
|
|
573
|
+
declare const createTerrainSamplerTask: _hello_terrain_work.Task<TerrainSampler, string, unknown>;
|
|
543
574
|
|
|
544
575
|
/** Root tile size in world units. */
|
|
545
576
|
declare const rootSize: _hello_terrain_work.ParamRef<number>;
|
|
@@ -644,6 +675,8 @@ declare const terrainTasks: {
|
|
|
644
675
|
attribute: three_webgpu.StorageBufferAttribute;
|
|
645
676
|
node: three_webgpu.StorageBufferNode;
|
|
646
677
|
}, string, unknown>;
|
|
678
|
+
readonly gpuSpatialIndexStorage: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
|
|
679
|
+
readonly gpuSpatialIndexUpload: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
|
|
647
680
|
readonly createUniforms: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
|
|
648
681
|
readonly updateUniforms: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
|
|
649
682
|
readonly positionNode: _hello_terrain_work.Task<three_src_nodes_TSL_js.ShaderCallNodeInternal, string, unknown>;
|
|
@@ -662,6 +695,7 @@ declare const terrainTasks: {
|
|
|
662
695
|
readonly createTerrainFieldTexture: _hello_terrain_work.Task<any, string, {
|
|
663
696
|
renderer: WebGPURenderer;
|
|
664
697
|
}>;
|
|
698
|
+
readonly createTerrainSampler: _hello_terrain_work.Task<TerrainSampler, string, unknown>;
|
|
665
699
|
readonly elevationFieldStage: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
|
|
666
700
|
readonly terrainFieldStage: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
|
|
667
701
|
readonly compileCompute: _hello_terrain_work.Task<{
|
|
@@ -679,6 +713,8 @@ type ComputeDeviceLimits = {
|
|
|
679
713
|
};
|
|
680
714
|
declare function getDeviceComputeLimits(renderer: WebGPURenderer): ComputeDeviceLimits;
|
|
681
715
|
|
|
716
|
+
declare function createTerrainSampler(params: CreateTerrainSamplerParams): TerrainSampler;
|
|
717
|
+
|
|
682
718
|
/**
|
|
683
719
|
* Maps a value or node from texture space [0, 1] to vector space [-1, 1].
|
|
684
720
|
*
|
|
@@ -751,5 +787,5 @@ declare const voronoiCells: three_src_nodes_TSL_js.ShaderNodeFn<[three_tsl.Proxi
|
|
|
751
787
|
uv: Node;
|
|
752
788
|
}>]>;
|
|
753
789
|
|
|
754
|
-
export { ArrayTextureBackend, AtlasBackend, Dir, TerrainGeometry, TerrainMesh, Texture3DBackend, U32_EMPTY, allocLeafSet, allocSeamTable, beginUpdate, blendAngleCorrectedNormals, buildLeafIndex, buildSeams2to1, compileComputeTask, createComputePipelineTasks, createCubeSphereSurface, createElevationFieldContextTask, createFlatSurface, createInfiniteFlatSurface, createSpatialIndex, createState, createTerrainFieldStorage, createTerrainFieldTextureTask, createTerrainUniforms, createUniformsTask, deriveNormalZ, elevationFieldStageTask, elevationFn, elevationScale, executeComputeTask, getDeviceComputeLimits, innerTileSegments, instanceIdTask, isSkirtUV, isSkirtVertex, leafGpuBufferTask, leafStorageTask, loadTerrainField, loadTerrainFieldElevation, loadTerrainFieldNormal, maxLevel, maxNodes, origin, packTerrainFieldSample, positionNodeTask, quadtreeConfigTask, quadtreeUpdate, quadtreeUpdateTask, resetLeafSet, resetSeamTable, rootSize, sampleTerrainField, sampleTerrainFieldElevation, sampleTerrainFieldNormal, skirtScale, storeTerrainField, surface, surfaceTask, terrainFieldFilter, terrainFieldStageTask, terrainGraph, terrainTasks, textureSpaceToVectorSpace, tileNodesTask, update, updateUniformsTask, vElevation, vGlobalVertexIndex, vectorSpaceToTextureSpace, voronoiCells };
|
|
755
|
-
export type { ComputePipeline, ComputeStageCallback, CubeSphereSurfaceConfig, ElevationCallback, ElevationFieldContext, ElevationParams, FlatSurfaceConfig, InfiniteFlatSurfaceConfig, IntNodeInput, LeafGpuBufferState, LeafSet, LeafStorageState, LodMode, QuadtreeConfig, QuadtreeConfigState, QuadtreeState, SeamTable, SpatialIndex, Surface, TerrainFieldStorage, TerrainFieldStorageBackendType, TerrainFieldStorageFormat, TerrainFieldStorageOptions, TerrainGraph, TerrainTasks, TerrainUniformsContext, TerrainUniformsParams, TileBounds, TileId, UpdateParams };
|
|
790
|
+
export { ArrayTextureBackend, AtlasBackend, Dir, TerrainGeometry, TerrainMesh, Texture3DBackend, U32_EMPTY, allocLeafSet, allocSeamTable, beginUpdate, blendAngleCorrectedNormals, buildLeafIndex, buildSeams2to1, compileComputeTask, createComputePipelineTasks, createCubeSphereSurface, createElevationFieldContextTask, createFlatSurface, createInfiniteFlatSurface, createSpatialIndex, createState, createTerrainFieldStorage, createTerrainFieldTextureTask, createTerrainSampler, createTerrainSamplerTask, createTerrainUniforms, createUniformsTask, deriveNormalZ, elevationFieldStageTask, elevationFn, elevationScale, executeComputeTask, getDeviceComputeLimits, gpuSpatialIndexStorageTask, gpuSpatialIndexUploadTask, innerTileSegments, instanceIdTask, isSkirtUV, isSkirtVertex, leafGpuBufferTask, leafStorageTask, loadTerrainField, loadTerrainFieldElevation, loadTerrainFieldNormal, maxLevel, maxNodes, origin, packTerrainFieldSample, positionNodeTask, quadtreeConfigTask, quadtreeUpdate, quadtreeUpdateTask, resetLeafSet, resetSeamTable, rootSize, sampleTerrainField, sampleTerrainFieldElevation, sampleTerrainFieldNormal, skirtScale, storeTerrainField, surface, surfaceTask, terrainFieldFilter, terrainFieldStageTask, terrainGraph, terrainTasks, textureSpaceToVectorSpace, tileNodesTask, update, updateUniformsTask, vElevation, vGlobalVertexIndex, vectorSpaceToTextureSpace, voronoiCells };
|
|
791
|
+
export type { ComputePipeline, ComputeStageCallback, CreateTerrainSamplerParams, CubeSphereSurfaceConfig, ElevationCallback, ElevationFieldContext, ElevationParams, FlatSurfaceConfig, GpuSpatialIndexContext, InfiniteFlatSurfaceConfig, IntNodeInput, LeafGpuBufferState, LeafSet, LeafStorageState, LodMode, QuadtreeConfig, QuadtreeConfigState, QuadtreeState, SeamTable, SpatialIndex, Surface, TerrainFieldStorage, TerrainFieldStorageBackendType, TerrainFieldStorageFormat, TerrainFieldStorageOptions, TerrainGraph, TerrainSampler, TerrainTasks, TerrainUniformsContext, TerrainUniformsParams, TileBounds, TileId, UpdateParams };
|
package/dist/index.d.ts
CHANGED
|
@@ -458,6 +458,41 @@ declare function createTileCompute(leafStorage: LeafStorageState, uniforms: Terr
|
|
|
458
458
|
tileVertexWorldPositionCompute: three_src_nodes_TSL_js.ShaderNodeFn<[number | Node, number | Node, number | Node]>;
|
|
459
459
|
};
|
|
460
460
|
|
|
461
|
+
interface ElevationParams {
|
|
462
|
+
worldPosition: Node$1;
|
|
463
|
+
rootSize: Node$1;
|
|
464
|
+
rootUV: Node$1;
|
|
465
|
+
tileUV: Node$1;
|
|
466
|
+
tileLevel: Node$1;
|
|
467
|
+
tileSize: Node$1;
|
|
468
|
+
tileOriginVec2: Node$1;
|
|
469
|
+
nodeIndex: Node$1;
|
|
470
|
+
}
|
|
471
|
+
type ElevationCallback = (params: ElevationParams) => Node$1;
|
|
472
|
+
|
|
473
|
+
interface GpuSpatialIndexContext {
|
|
474
|
+
data: Uint32Array<ArrayBuffer>;
|
|
475
|
+
size: number;
|
|
476
|
+
mask: number;
|
|
477
|
+
stampGen: UniformNode<number>;
|
|
478
|
+
attribute: StorageBufferAttribute;
|
|
479
|
+
node: StorageBufferNode;
|
|
480
|
+
}
|
|
481
|
+
interface TerrainSampler {
|
|
482
|
+
sampleElevation: (worldX: Node, worldZ: Node) => Node;
|
|
483
|
+
sampleNormal: (worldX: Node, worldZ: Node) => Node;
|
|
484
|
+
sampleTerrain: (worldX: Node, worldZ: Node) => Node;
|
|
485
|
+
sampleValidity: (worldX: Node, worldZ: Node) => Node;
|
|
486
|
+
evaluateElevation: (worldX: Node, worldZ: Node) => Node;
|
|
487
|
+
evaluateNormal: (worldX: Node, worldZ: Node, epsilon?: Node) => Node;
|
|
488
|
+
}
|
|
489
|
+
interface CreateTerrainSamplerParams {
|
|
490
|
+
terrainFieldStorage: TerrainFieldStorage;
|
|
491
|
+
spatialIndex: GpuSpatialIndexContext;
|
|
492
|
+
uniforms: TerrainUniformsContext;
|
|
493
|
+
elevationCallback: ElevationCallback;
|
|
494
|
+
}
|
|
495
|
+
|
|
461
496
|
interface QuadtreeConfigState {
|
|
462
497
|
state: QuadtreeState;
|
|
463
498
|
surface: Surface;
|
|
@@ -478,12 +513,15 @@ interface TerrainTasks {
|
|
|
478
513
|
surface: TaskRef<Surface>;
|
|
479
514
|
leafStorage: TaskRef<LeafStorageState>;
|
|
480
515
|
leafGpuBuffer: TaskRef<LeafGpuBufferState>;
|
|
516
|
+
gpuSpatialIndexStorage: TaskRef<GpuSpatialIndexContext>;
|
|
517
|
+
gpuSpatialIndexUpload: TaskRef<GpuSpatialIndexContext>;
|
|
481
518
|
createUniforms: TaskRef<TerrainUniformsContext>;
|
|
482
519
|
updateUniforms: TaskRef<TerrainUniformsContext>;
|
|
483
520
|
positionNode: TaskRef<ShaderCallNodeInternal>;
|
|
484
521
|
createElevationFieldContext: TaskRef<ElevationFieldContext>;
|
|
485
522
|
createTileNodes: TaskRef<ReturnType<typeof createTileCompute>>;
|
|
486
523
|
createTerrainFieldTexture: TaskRef<TerrainFieldStorage>;
|
|
524
|
+
createTerrainSampler: TaskRef<TerrainSampler>;
|
|
487
525
|
elevationFieldStage: TaskRef<ComputePipeline>;
|
|
488
526
|
terrainFieldStage: TaskRef<ComputePipeline>;
|
|
489
527
|
compileCompute: TaskRef<{
|
|
@@ -513,6 +551,9 @@ declare const tileNodesTask: _hello_terrain_work.Task<{
|
|
|
513
551
|
*/
|
|
514
552
|
declare const elevationFieldStageTask: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
|
|
515
553
|
|
|
554
|
+
declare const gpuSpatialIndexStorageTask: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
|
|
555
|
+
declare const gpuSpatialIndexUploadTask: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
|
|
556
|
+
|
|
516
557
|
/** Generates a unique instance ID per graph (cached once). */
|
|
517
558
|
declare const instanceIdTask: _hello_terrain_work.Task<`${string}-${string}-${string}-${string}-${string}`, string, unknown>;
|
|
518
559
|
|
|
@@ -529,17 +570,7 @@ declare const createTerrainFieldTextureTask: _hello_terrain_work.Task<any, strin
|
|
|
529
570
|
*/
|
|
530
571
|
declare const terrainFieldStageTask: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
|
|
531
572
|
|
|
532
|
-
|
|
533
|
-
worldPosition: Node$1;
|
|
534
|
-
rootSize: Node$1;
|
|
535
|
-
rootUV: Node$1;
|
|
536
|
-
tileUV: Node$1;
|
|
537
|
-
tileLevel: Node$1;
|
|
538
|
-
tileSize: Node$1;
|
|
539
|
-
tileOriginVec2: Node$1;
|
|
540
|
-
nodeIndex: Node$1;
|
|
541
|
-
}
|
|
542
|
-
type ElevationCallback = (params: ElevationParams) => Node$1;
|
|
573
|
+
declare const createTerrainSamplerTask: _hello_terrain_work.Task<TerrainSampler, string, unknown>;
|
|
543
574
|
|
|
544
575
|
/** Root tile size in world units. */
|
|
545
576
|
declare const rootSize: _hello_terrain_work.ParamRef<number>;
|
|
@@ -644,6 +675,8 @@ declare const terrainTasks: {
|
|
|
644
675
|
attribute: three_webgpu.StorageBufferAttribute;
|
|
645
676
|
node: three_webgpu.StorageBufferNode;
|
|
646
677
|
}, string, unknown>;
|
|
678
|
+
readonly gpuSpatialIndexStorage: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
|
|
679
|
+
readonly gpuSpatialIndexUpload: _hello_terrain_work.Task<GpuSpatialIndexContext, string, unknown>;
|
|
647
680
|
readonly createUniforms: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
|
|
648
681
|
readonly updateUniforms: _hello_terrain_work.Task<TerrainUniformsContext, string, unknown>;
|
|
649
682
|
readonly positionNode: _hello_terrain_work.Task<three_src_nodes_TSL_js.ShaderCallNodeInternal, string, unknown>;
|
|
@@ -662,6 +695,7 @@ declare const terrainTasks: {
|
|
|
662
695
|
readonly createTerrainFieldTexture: _hello_terrain_work.Task<any, string, {
|
|
663
696
|
renderer: WebGPURenderer;
|
|
664
697
|
}>;
|
|
698
|
+
readonly createTerrainSampler: _hello_terrain_work.Task<TerrainSampler, string, unknown>;
|
|
665
699
|
readonly elevationFieldStage: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
|
|
666
700
|
readonly terrainFieldStage: _hello_terrain_work.Task<ComputePipeline, string, unknown>;
|
|
667
701
|
readonly compileCompute: _hello_terrain_work.Task<{
|
|
@@ -679,6 +713,8 @@ type ComputeDeviceLimits = {
|
|
|
679
713
|
};
|
|
680
714
|
declare function getDeviceComputeLimits(renderer: WebGPURenderer): ComputeDeviceLimits;
|
|
681
715
|
|
|
716
|
+
declare function createTerrainSampler(params: CreateTerrainSamplerParams): TerrainSampler;
|
|
717
|
+
|
|
682
718
|
/**
|
|
683
719
|
* Maps a value or node from texture space [0, 1] to vector space [-1, 1].
|
|
684
720
|
*
|
|
@@ -751,5 +787,5 @@ declare const voronoiCells: three_src_nodes_TSL_js.ShaderNodeFn<[three_tsl.Proxi
|
|
|
751
787
|
uv: Node;
|
|
752
788
|
}>]>;
|
|
753
789
|
|
|
754
|
-
export { ArrayTextureBackend, AtlasBackend, Dir, TerrainGeometry, TerrainMesh, Texture3DBackend, U32_EMPTY, allocLeafSet, allocSeamTable, beginUpdate, blendAngleCorrectedNormals, buildLeafIndex, buildSeams2to1, compileComputeTask, createComputePipelineTasks, createCubeSphereSurface, createElevationFieldContextTask, createFlatSurface, createInfiniteFlatSurface, createSpatialIndex, createState, createTerrainFieldStorage, createTerrainFieldTextureTask, createTerrainUniforms, createUniformsTask, deriveNormalZ, elevationFieldStageTask, elevationFn, elevationScale, executeComputeTask, getDeviceComputeLimits, innerTileSegments, instanceIdTask, isSkirtUV, isSkirtVertex, leafGpuBufferTask, leafStorageTask, loadTerrainField, loadTerrainFieldElevation, loadTerrainFieldNormal, maxLevel, maxNodes, origin, packTerrainFieldSample, positionNodeTask, quadtreeConfigTask, quadtreeUpdate, quadtreeUpdateTask, resetLeafSet, resetSeamTable, rootSize, sampleTerrainField, sampleTerrainFieldElevation, sampleTerrainFieldNormal, skirtScale, storeTerrainField, surface, surfaceTask, terrainFieldFilter, terrainFieldStageTask, terrainGraph, terrainTasks, textureSpaceToVectorSpace, tileNodesTask, update, updateUniformsTask, vElevation, vGlobalVertexIndex, vectorSpaceToTextureSpace, voronoiCells };
|
|
755
|
-
export type { ComputePipeline, ComputeStageCallback, CubeSphereSurfaceConfig, ElevationCallback, ElevationFieldContext, ElevationParams, FlatSurfaceConfig, InfiniteFlatSurfaceConfig, IntNodeInput, LeafGpuBufferState, LeafSet, LeafStorageState, LodMode, QuadtreeConfig, QuadtreeConfigState, QuadtreeState, SeamTable, SpatialIndex, Surface, TerrainFieldStorage, TerrainFieldStorageBackendType, TerrainFieldStorageFormat, TerrainFieldStorageOptions, TerrainGraph, TerrainTasks, TerrainUniformsContext, TerrainUniformsParams, TileBounds, TileId, UpdateParams };
|
|
790
|
+
export { ArrayTextureBackend, AtlasBackend, Dir, TerrainGeometry, TerrainMesh, Texture3DBackend, U32_EMPTY, allocLeafSet, allocSeamTable, beginUpdate, blendAngleCorrectedNormals, buildLeafIndex, buildSeams2to1, compileComputeTask, createComputePipelineTasks, createCubeSphereSurface, createElevationFieldContextTask, createFlatSurface, createInfiniteFlatSurface, createSpatialIndex, createState, createTerrainFieldStorage, createTerrainFieldTextureTask, createTerrainSampler, createTerrainSamplerTask, createTerrainUniforms, createUniformsTask, deriveNormalZ, elevationFieldStageTask, elevationFn, elevationScale, executeComputeTask, getDeviceComputeLimits, gpuSpatialIndexStorageTask, gpuSpatialIndexUploadTask, innerTileSegments, instanceIdTask, isSkirtUV, isSkirtVertex, leafGpuBufferTask, leafStorageTask, loadTerrainField, loadTerrainFieldElevation, loadTerrainFieldNormal, maxLevel, maxNodes, origin, packTerrainFieldSample, positionNodeTask, quadtreeConfigTask, quadtreeUpdate, quadtreeUpdateTask, resetLeafSet, resetSeamTable, rootSize, sampleTerrainField, sampleTerrainFieldElevation, sampleTerrainFieldNormal, skirtScale, storeTerrainField, surface, surfaceTask, terrainFieldFilter, terrainFieldStageTask, terrainGraph, terrainTasks, textureSpaceToVectorSpace, tileNodesTask, update, updateUniformsTask, vElevation, vGlobalVertexIndex, vectorSpaceToTextureSpace, voronoiCells };
|
|
791
|
+
export type { ComputePipeline, ComputeStageCallback, CreateTerrainSamplerParams, CubeSphereSurfaceConfig, ElevationCallback, ElevationFieldContext, ElevationParams, FlatSurfaceConfig, GpuSpatialIndexContext, InfiniteFlatSurfaceConfig, IntNodeInput, LeafGpuBufferState, LeafSet, LeafStorageState, LodMode, QuadtreeConfig, QuadtreeConfigState, QuadtreeState, SeamTable, SpatialIndex, Surface, TerrainFieldStorage, TerrainFieldStorageBackendType, TerrainFieldStorageFormat, TerrainFieldStorageOptions, TerrainGraph, TerrainSampler, TerrainTasks, TerrainUniformsContext, TerrainUniformsParams, TileBounds, TileId, UpdateParams };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BufferGeometry, BufferAttribute, RGBAFormat, ClampToEdgeWrapping, HalfFloatType, FloatType, LinearFilter, NearestFilter, Vector3 as Vector3$1 } from 'three';
|
|
2
2
|
import { MeshStandardNodeMaterial, InstancedMesh, InstancedBufferAttribute, StorageTexture, StorageArrayTexture, StorageBufferAttribute, Vector3 } from 'three/webgpu';
|
|
3
3
|
import { param, task, graph } from '@hello-terrain/work';
|
|
4
|
-
import { uniform, Fn, float, globalId, int, vec2, uint, If, workgroupBarrier, textureStore, uvec3, vec4, texture, ivec2, ivec3, textureLoad,
|
|
4
|
+
import { uniform, Fn, float, globalId, int, vec2, uint, If, workgroupBarrier, textureStore, uvec3, vec4, texture, ivec2, ivec3, textureLoad, pow, vec3, storage, Loop, Break, vertexIndex, uv, select, instanceIndex, positionLocal, normalLocal, remap, dot, varyingProperty, mx_noise_float, mix } from 'three/tsl';
|
|
5
5
|
import { Fn as Fn$1 } from 'three/src/nodes/TSL.js';
|
|
6
6
|
|
|
7
7
|
class TerrainGeometry extends BufferGeometry {
|
|
@@ -645,25 +645,6 @@ const createElevation = (tile, uniforms, elevationFn) => {
|
|
|
645
645
|
});
|
|
646
646
|
};
|
|
647
647
|
};
|
|
648
|
-
const readElevationFieldAtPositionLocal = (terrainFieldStorage, edgeVertexCount, positionLocal) => Fn(() => {
|
|
649
|
-
const nodeIndex = int(instanceIndex);
|
|
650
|
-
const intEdge = int(edgeVertexCount);
|
|
651
|
-
const innerSegments = int(edgeVertexCount).sub(3);
|
|
652
|
-
const fInnerSegments = float(innerSegments);
|
|
653
|
-
const last = intEdge.sub(int(1));
|
|
654
|
-
const u = positionLocal.x.add(float(0.5));
|
|
655
|
-
const v = positionLocal.z.add(float(0.5));
|
|
656
|
-
const x = u.mul(fInnerSegments).round().toInt().add(int(1));
|
|
657
|
-
const y = v.mul(fInnerSegments).round().toInt().add(int(1));
|
|
658
|
-
const xClamped = min(max(x, int(0)), last);
|
|
659
|
-
const yClamped = min(max(y, int(0)), last);
|
|
660
|
-
return loadTerrainFieldElevation(
|
|
661
|
-
terrainFieldStorage,
|
|
662
|
-
xClamped,
|
|
663
|
-
yClamped,
|
|
664
|
-
nodeIndex
|
|
665
|
-
);
|
|
666
|
-
});
|
|
667
648
|
|
|
668
649
|
function createTileCompute(leafStorage, uniforms) {
|
|
669
650
|
const tileLevel = Fn(([nodeIndex]) => {
|
|
@@ -730,6 +711,10 @@ function createTileCompute(leafStorage, uniforms) {
|
|
|
730
711
|
tileVertexWorldPositionCompute
|
|
731
712
|
};
|
|
732
713
|
}
|
|
714
|
+
function tileLocalToFieldUV(localCoord, innerSegments) {
|
|
715
|
+
const edge = float(innerSegments).add(float(3));
|
|
716
|
+
return float(localCoord).mul(float(innerSegments)).add(float(1.5)).div(edge);
|
|
717
|
+
}
|
|
733
718
|
|
|
734
719
|
const rootSize = param(256).displayName("rootSize");
|
|
735
720
|
const origin = param({
|
|
@@ -846,12 +831,12 @@ function ensureChildren(store, parentId) {
|
|
|
846
831
|
return childBase;
|
|
847
832
|
}
|
|
848
833
|
|
|
849
|
-
function nextPow2(n) {
|
|
834
|
+
function nextPow2$1(n) {
|
|
850
835
|
let x = 1;
|
|
851
836
|
while (x < n) x <<= 1;
|
|
852
837
|
return x;
|
|
853
838
|
}
|
|
854
|
-
function mix32(x) {
|
|
839
|
+
function mix32$1(x) {
|
|
855
840
|
x >>>= 0;
|
|
856
841
|
x ^= x >>> 16;
|
|
857
842
|
x = Math.imul(x, 2146121005) >>> 0;
|
|
@@ -860,12 +845,12 @@ function mix32(x) {
|
|
|
860
845
|
x ^= x >>> 16;
|
|
861
846
|
return x >>> 0;
|
|
862
847
|
}
|
|
863
|
-
function hashKey(space, level, x, y) {
|
|
864
|
-
const h = space & 255 ^ (level & 255) << 8 ^ mix32(x) >>> 0 ^ mix32(y) >>> 0;
|
|
865
|
-
return mix32(h);
|
|
848
|
+
function hashKey$1(space, level, x, y) {
|
|
849
|
+
const h = space & 255 ^ (level & 255) << 8 ^ mix32$1(x) >>> 0 ^ mix32$1(y) >>> 0;
|
|
850
|
+
return mix32$1(h);
|
|
866
851
|
}
|
|
867
852
|
function createSpatialIndex(maxEntries) {
|
|
868
|
-
const size = nextPow2(Math.max(2, maxEntries * 2));
|
|
853
|
+
const size = nextPow2$1(Math.max(2, maxEntries * 2));
|
|
869
854
|
return {
|
|
870
855
|
size,
|
|
871
856
|
mask: size - 1,
|
|
@@ -890,7 +875,7 @@ function insertSpatialIndexRaw(index, space, level, x, y, value) {
|
|
|
890
875
|
const l = level & 255;
|
|
891
876
|
const xx = x >>> 0;
|
|
892
877
|
const yy = y >>> 0;
|
|
893
|
-
let slot = hashKey(s, l, xx, yy) & index.mask;
|
|
878
|
+
let slot = hashKey$1(s, l, xx, yy) & index.mask;
|
|
894
879
|
for (let probes = 0; probes < index.size; probes++) {
|
|
895
880
|
if (index.stamp[slot] !== index.stampGen) {
|
|
896
881
|
index.stamp[slot] = index.stampGen;
|
|
@@ -914,7 +899,7 @@ function lookupSpatialIndexRaw(index, space, level, x, y) {
|
|
|
914
899
|
const l = level & 255;
|
|
915
900
|
const xx = x >>> 0;
|
|
916
901
|
const yy = y >>> 0;
|
|
917
|
-
let slot = hashKey(s, l, xx, yy) & index.mask;
|
|
902
|
+
let slot = hashKey$1(s, l, xx, yy) & index.mask;
|
|
918
903
|
for (let probes = 0; probes < index.size; probes++) {
|
|
919
904
|
if (index.stamp[slot] !== index.stampGen) return U32_EMPTY;
|
|
920
905
|
if (index.keysSpace[slot] === s && index.keysLevel[slot] === l && index.keysX[slot] === xx && index.keysY[slot] === yy) {
|
|
@@ -1675,6 +1660,275 @@ function createComputePipelineTasks(leafStageTask) {
|
|
|
1675
1660
|
return { compile, execute };
|
|
1676
1661
|
}
|
|
1677
1662
|
|
|
1663
|
+
const SLOT_STRIDE = 6;
|
|
1664
|
+
function nextPow2(n) {
|
|
1665
|
+
let x = 1;
|
|
1666
|
+
while (x < n) x <<= 1;
|
|
1667
|
+
return x;
|
|
1668
|
+
}
|
|
1669
|
+
function createGpuSpatialIndex(maxEntries) {
|
|
1670
|
+
const size = nextPow2(Math.max(2, maxEntries * 2));
|
|
1671
|
+
const data = new Uint32Array(size * SLOT_STRIDE);
|
|
1672
|
+
const attribute = new StorageBufferAttribute(data, SLOT_STRIDE);
|
|
1673
|
+
const node = storage(attribute, "u32", 1).toReadOnly().setName("gpuSpatialIndex");
|
|
1674
|
+
const stampGen = uniform(uint(1)).setName("uGpuSpatialIndexStampGen");
|
|
1675
|
+
return {
|
|
1676
|
+
data,
|
|
1677
|
+
size,
|
|
1678
|
+
mask: size - 1,
|
|
1679
|
+
stampGen,
|
|
1680
|
+
attribute,
|
|
1681
|
+
node
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
function uploadGpuSpatialIndex(gpuIndex, cpuIndex) {
|
|
1685
|
+
if (gpuIndex.size !== cpuIndex.size) {
|
|
1686
|
+
throw new Error(
|
|
1687
|
+
`Spatial index size mismatch (gpu=${gpuIndex.size}, cpu=${cpuIndex.size}).`
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
1690
|
+
for (let i = 0; i < cpuIndex.size; i += 1) {
|
|
1691
|
+
const base = i * SLOT_STRIDE;
|
|
1692
|
+
gpuIndex.data[base] = cpuIndex.stamp[i] ?? 0;
|
|
1693
|
+
gpuIndex.data[base + 1] = cpuIndex.keysSpace[i] ?? 0;
|
|
1694
|
+
gpuIndex.data[base + 2] = cpuIndex.keysLevel[i] ?? 0;
|
|
1695
|
+
gpuIndex.data[base + 3] = cpuIndex.keysX[i] ?? 0;
|
|
1696
|
+
gpuIndex.data[base + 4] = cpuIndex.keysY[i] ?? 0;
|
|
1697
|
+
gpuIndex.data[base + 5] = cpuIndex.values[i] ?? 0;
|
|
1698
|
+
}
|
|
1699
|
+
gpuIndex.stampGen.value = cpuIndex.stampGen >>> 0;
|
|
1700
|
+
gpuIndex.attribute.needsUpdate = true;
|
|
1701
|
+
gpuIndex.node.needsUpdate = true;
|
|
1702
|
+
}
|
|
1703
|
+
function readGpuSpatialIndexValue(spatialIndex, slot, fieldOffset) {
|
|
1704
|
+
const offset = int(slot).mul(int(SLOT_STRIDE)).add(int(fieldOffset));
|
|
1705
|
+
return spatialIndex.node.element(offset).toUint();
|
|
1706
|
+
}
|
|
1707
|
+
const mix32 = Fn(([x]) => {
|
|
1708
|
+
const v = uint(x).toVar();
|
|
1709
|
+
v.assign(v.bitXor(v.shiftRight(uint(16))));
|
|
1710
|
+
v.assign(v.mul(uint(2146121005)));
|
|
1711
|
+
v.assign(v.bitXor(v.shiftRight(uint(15))));
|
|
1712
|
+
v.assign(v.mul(uint(2221713035)));
|
|
1713
|
+
v.assign(v.bitXor(v.shiftRight(uint(16))));
|
|
1714
|
+
return v;
|
|
1715
|
+
});
|
|
1716
|
+
const hashKey = Fn(([space, level, x, y]) => {
|
|
1717
|
+
const s = uint(space).bitAnd(uint(255));
|
|
1718
|
+
const l = uint(level).bitAnd(uint(255));
|
|
1719
|
+
const h = s.bitXor(l.shiftLeft(uint(8))).bitXor(mix32(uint(x))).bitXor(mix32(uint(y)));
|
|
1720
|
+
return mix32(h);
|
|
1721
|
+
});
|
|
1722
|
+
const createGpuSpatialLookup = (spatialIndex) => {
|
|
1723
|
+
const slotCount = spatialIndex.size;
|
|
1724
|
+
const mask = uint(spatialIndex.mask);
|
|
1725
|
+
const stampGen = spatialIndex.stampGen.toUint();
|
|
1726
|
+
const emptyValue = int(-1);
|
|
1727
|
+
return Fn(([space, level, x, y]) => {
|
|
1728
|
+
const s = uint(space).bitAnd(uint(255));
|
|
1729
|
+
const l = uint(level).bitAnd(uint(255));
|
|
1730
|
+
const xx = uint(x);
|
|
1731
|
+
const yy = uint(y);
|
|
1732
|
+
const result = emptyValue.toVar();
|
|
1733
|
+
const slot = hashKey(s, l, xx, yy).bitAnd(mask).toVar();
|
|
1734
|
+
const probes = int(0).toVar();
|
|
1735
|
+
Loop(slotCount, () => {
|
|
1736
|
+
const stamp = readGpuSpatialIndexValue(spatialIndex, slot, 0);
|
|
1737
|
+
If(stamp.notEqual(stampGen), () => {
|
|
1738
|
+
Break();
|
|
1739
|
+
});
|
|
1740
|
+
const ks = readGpuSpatialIndexValue(spatialIndex, slot, 1);
|
|
1741
|
+
const kl = readGpuSpatialIndexValue(spatialIndex, slot, 2);
|
|
1742
|
+
const kx = readGpuSpatialIndexValue(spatialIndex, slot, 3);
|
|
1743
|
+
const ky = readGpuSpatialIndexValue(spatialIndex, slot, 4);
|
|
1744
|
+
If(
|
|
1745
|
+
ks.equal(s).and(kl.equal(l)).and(kx.equal(xx)).and(ky.equal(yy)),
|
|
1746
|
+
() => {
|
|
1747
|
+
result.assign(int(readGpuSpatialIndexValue(spatialIndex, slot, 5)));
|
|
1748
|
+
Break();
|
|
1749
|
+
}
|
|
1750
|
+
);
|
|
1751
|
+
slot.assign(slot.add(uint(1)).bitAnd(mask));
|
|
1752
|
+
probes.addAssign(1);
|
|
1753
|
+
});
|
|
1754
|
+
return result;
|
|
1755
|
+
});
|
|
1756
|
+
};
|
|
1757
|
+
const createTileIndexFromWorldPosition = (spatialIndex, uniforms, maxLevel) => {
|
|
1758
|
+
const lookup = createGpuSpatialLookup(spatialIndex);
|
|
1759
|
+
const levelCount = Math.max(1, maxLevel + 1);
|
|
1760
|
+
return Fn(([worldX, worldZ]) => {
|
|
1761
|
+
const rootOrigin = uniforms.uRootOrigin.toVar();
|
|
1762
|
+
const rootSize = uniforms.uRootSize.toVar();
|
|
1763
|
+
const halfRoot = rootSize.mul(float(0.5));
|
|
1764
|
+
const tileIndex = int(-1).toVar();
|
|
1765
|
+
const tileU = float(0).toVar();
|
|
1766
|
+
const tileV = float(0).toVar();
|
|
1767
|
+
const i = int(0).toVar();
|
|
1768
|
+
Loop(levelCount, () => {
|
|
1769
|
+
const level = int(maxLevel).sub(i).toVar();
|
|
1770
|
+
const scale = pow(float(2), level.toFloat());
|
|
1771
|
+
const tileSize = rootSize.div(scale);
|
|
1772
|
+
const tileX = worldX.sub(rootOrigin.x).add(halfRoot).div(tileSize).floor().toInt();
|
|
1773
|
+
const tileY = worldZ.sub(rootOrigin.z).add(halfRoot).div(tileSize).floor().toInt();
|
|
1774
|
+
const maybeIndex = lookup(int(0), level, tileX, tileY).toVar();
|
|
1775
|
+
If(maybeIndex.greaterThanEqual(int(0)), () => {
|
|
1776
|
+
const minX = rootOrigin.x.add(tileX.toFloat().mul(tileSize)).sub(halfRoot);
|
|
1777
|
+
const minZ = rootOrigin.z.add(tileY.toFloat().mul(tileSize)).sub(halfRoot);
|
|
1778
|
+
tileIndex.assign(maybeIndex);
|
|
1779
|
+
tileU.assign(worldX.sub(minX).div(tileSize));
|
|
1780
|
+
tileV.assign(worldZ.sub(minZ).div(tileSize));
|
|
1781
|
+
Break();
|
|
1782
|
+
});
|
|
1783
|
+
i.addAssign(1);
|
|
1784
|
+
});
|
|
1785
|
+
return vec3(tileIndex.toFloat(), tileU, tileV);
|
|
1786
|
+
});
|
|
1787
|
+
};
|
|
1788
|
+
|
|
1789
|
+
const gpuSpatialIndexStorageTask = task((get, work) => {
|
|
1790
|
+
const maxNodesValue = get(maxNodes);
|
|
1791
|
+
return work(() => createGpuSpatialIndex(maxNodesValue));
|
|
1792
|
+
}).displayName("gpuSpatialIndexStorageTask");
|
|
1793
|
+
const gpuSpatialIndexUploadTask = task((get, work) => {
|
|
1794
|
+
const quadtreeConfig = get(quadtreeConfigTask);
|
|
1795
|
+
get(quadtreeUpdateTask);
|
|
1796
|
+
const gpuSpatialIndex = get(gpuSpatialIndexStorageTask);
|
|
1797
|
+
return work(() => {
|
|
1798
|
+
uploadGpuSpatialIndex(gpuSpatialIndex, quadtreeConfig.state.leafIndex);
|
|
1799
|
+
return gpuSpatialIndex;
|
|
1800
|
+
});
|
|
1801
|
+
}).displayName("gpuSpatialIndexUploadTask");
|
|
1802
|
+
|
|
1803
|
+
function createTerrainSampleNode(params) {
|
|
1804
|
+
const tileLookup = createTileIndexFromWorldPosition(
|
|
1805
|
+
params.spatialIndex,
|
|
1806
|
+
params.uniforms,
|
|
1807
|
+
maxLevel.get()
|
|
1808
|
+
);
|
|
1809
|
+
return Fn(([worldX, worldZ]) => {
|
|
1810
|
+
const tileResult = tileLookup(worldX, worldZ).toVar();
|
|
1811
|
+
const tileIndex = int(tileResult.x).toVar();
|
|
1812
|
+
const safeTileIndex = tileIndex.max(int(0)).toVar();
|
|
1813
|
+
const u = tileResult.y.toVar();
|
|
1814
|
+
const v = tileResult.z.toVar();
|
|
1815
|
+
const fieldU = tileLocalToFieldUV(
|
|
1816
|
+
u,
|
|
1817
|
+
params.uniforms.uInnerTileSegments
|
|
1818
|
+
).toVar();
|
|
1819
|
+
const fieldV = tileLocalToFieldUV(
|
|
1820
|
+
v,
|
|
1821
|
+
params.uniforms.uInnerTileSegments
|
|
1822
|
+
).toVar();
|
|
1823
|
+
const found = tileIndex.greaterThanEqual(int(0)).toVar();
|
|
1824
|
+
const sampled = sampleTerrainField(
|
|
1825
|
+
params.terrainFieldStorage,
|
|
1826
|
+
fieldU,
|
|
1827
|
+
fieldV,
|
|
1828
|
+
safeTileIndex
|
|
1829
|
+
).toVar();
|
|
1830
|
+
const nx = sampled.g.toVar();
|
|
1831
|
+
const nz = sampled.b.toVar();
|
|
1832
|
+
const ny = float(1).sub(nx.mul(nx)).sub(nz.mul(nz)).max(0).sqrt();
|
|
1833
|
+
const valid = found.select(float(1), float(0)).toVar();
|
|
1834
|
+
return vec4(
|
|
1835
|
+
sampled.r.mul(valid),
|
|
1836
|
+
nx.mul(valid),
|
|
1837
|
+
ny.mul(valid),
|
|
1838
|
+
nz.mul(valid)
|
|
1839
|
+
);
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1842
|
+
function createTerrainSampler(params) {
|
|
1843
|
+
const elevationNode = createElevationFunction(params.elevationCallback);
|
|
1844
|
+
const terrainSampleAt = createTerrainSampleNode(params);
|
|
1845
|
+
const evaluateElevationAt = Fn(([worldX, worldZ]) => {
|
|
1846
|
+
const rootOrigin = params.uniforms.uRootOrigin.toVar();
|
|
1847
|
+
const rootSize = params.uniforms.uRootSize.toVar();
|
|
1848
|
+
const centeredX = worldX.sub(rootOrigin.x);
|
|
1849
|
+
const centeredZ = worldZ.sub(rootOrigin.z);
|
|
1850
|
+
const rootUV = vec2(
|
|
1851
|
+
centeredX.div(rootSize).add(0.5),
|
|
1852
|
+
centeredZ.div(rootSize).mul(float(-1)).add(0.5)
|
|
1853
|
+
).toVar();
|
|
1854
|
+
return elevationNode({
|
|
1855
|
+
worldPosition: vec3(worldX, rootOrigin.y, worldZ),
|
|
1856
|
+
rootSize,
|
|
1857
|
+
rootUV,
|
|
1858
|
+
tileUV: rootUV,
|
|
1859
|
+
tileLevel: int(0),
|
|
1860
|
+
tileSize: rootSize,
|
|
1861
|
+
tileOriginVec2: vec2(0, 0),
|
|
1862
|
+
nodeIndex: int(0)
|
|
1863
|
+
});
|
|
1864
|
+
});
|
|
1865
|
+
const sampleTerrain = Fn(
|
|
1866
|
+
([worldX, worldZ]) => terrainSampleAt(worldX, worldZ)
|
|
1867
|
+
);
|
|
1868
|
+
const sampleElevation = Fn(
|
|
1869
|
+
([worldX, worldZ]) => terrainSampleAt(worldX, worldZ).x
|
|
1870
|
+
);
|
|
1871
|
+
const sampleNormal = Fn(
|
|
1872
|
+
([worldX, worldZ]) => vec3(
|
|
1873
|
+
terrainSampleAt(worldX, worldZ).y,
|
|
1874
|
+
terrainSampleAt(worldX, worldZ).z,
|
|
1875
|
+
terrainSampleAt(worldX, worldZ).w
|
|
1876
|
+
)
|
|
1877
|
+
);
|
|
1878
|
+
const sampleValidity = Fn(
|
|
1879
|
+
([worldX, worldZ]) => terrainSampleAt(worldX, worldZ).y.abs().add(terrainSampleAt(worldX, worldZ).z.abs()).add(terrainSampleAt(worldX, worldZ).w.abs()).greaterThan(float(0)).select(float(1), float(0))
|
|
1880
|
+
);
|
|
1881
|
+
const evaluateElevation = Fn(
|
|
1882
|
+
([worldX, worldZ]) => evaluateElevationAt(worldX, worldZ)
|
|
1883
|
+
);
|
|
1884
|
+
const evaluateNormalNode = Fn(
|
|
1885
|
+
([worldX, worldZ, epsilon]) => {
|
|
1886
|
+
const eps = epsilon ?? float(0.1);
|
|
1887
|
+
const elevationScale = params.uniforms.uElevationScale.toVar();
|
|
1888
|
+
const hL = evaluateElevationAt(worldX.sub(eps), worldZ).mul(
|
|
1889
|
+
elevationScale
|
|
1890
|
+
);
|
|
1891
|
+
const hR = evaluateElevationAt(worldX.add(eps), worldZ).mul(
|
|
1892
|
+
elevationScale
|
|
1893
|
+
);
|
|
1894
|
+
const hD = evaluateElevationAt(worldX, worldZ.sub(eps)).mul(
|
|
1895
|
+
elevationScale
|
|
1896
|
+
);
|
|
1897
|
+
const hU = evaluateElevationAt(worldX, worldZ.add(eps)).mul(
|
|
1898
|
+
elevationScale
|
|
1899
|
+
);
|
|
1900
|
+
const inv2eps = float(0.5).div(eps);
|
|
1901
|
+
const dhdx = hR.sub(hL).mul(inv2eps);
|
|
1902
|
+
const dhdz = hU.sub(hD).mul(inv2eps);
|
|
1903
|
+
return vec3(dhdx.negate(), float(1), dhdz.negate()).normalize();
|
|
1904
|
+
}
|
|
1905
|
+
);
|
|
1906
|
+
const evaluateNormal = (worldX, worldZ, epsilon) => evaluateNormalNode(worldX, worldZ, epsilon ?? float(0.1));
|
|
1907
|
+
return {
|
|
1908
|
+
sampleElevation,
|
|
1909
|
+
sampleNormal,
|
|
1910
|
+
sampleTerrain,
|
|
1911
|
+
sampleValidity,
|
|
1912
|
+
evaluateElevation,
|
|
1913
|
+
evaluateNormal
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
const createTerrainSamplerTask = task((get, work) => {
|
|
1918
|
+
const terrainFieldStorage = get(createTerrainFieldTextureTask);
|
|
1919
|
+
const spatialIndex = get(gpuSpatialIndexStorageTask);
|
|
1920
|
+
const uniforms = get(createUniformsTask);
|
|
1921
|
+
const elevationCallback = get(elevationFn);
|
|
1922
|
+
return work(
|
|
1923
|
+
() => createTerrainSampler({
|
|
1924
|
+
terrainFieldStorage,
|
|
1925
|
+
spatialIndex,
|
|
1926
|
+
uniforms,
|
|
1927
|
+
elevationCallback
|
|
1928
|
+
})
|
|
1929
|
+
);
|
|
1930
|
+
}).displayName("createTerrainSamplerTask");
|
|
1931
|
+
|
|
1678
1932
|
const isSkirtVertex = Fn(([segments]) => {
|
|
1679
1933
|
const segmentsNode = typeof segments === "number" ? int(segments) : segments;
|
|
1680
1934
|
const vIndex = int(vertexIndex);
|
|
@@ -1718,14 +1972,15 @@ function createTileBaseWorldPosition(leafStorage, terrainUniforms) {
|
|
|
1718
1972
|
}
|
|
1719
1973
|
function createTileElevation(terrainUniforms, terrainFieldStorage) {
|
|
1720
1974
|
if (!terrainFieldStorage) return float(0);
|
|
1721
|
-
const
|
|
1722
|
-
|
|
1975
|
+
const innerSegs = terrainUniforms.uInnerTileSegments;
|
|
1976
|
+
const u = tileLocalToFieldUV(positionLocal.x.add(float(0.5)), innerSegs);
|
|
1977
|
+
const v = tileLocalToFieldUV(positionLocal.z.add(float(0.5)), innerSegs);
|
|
1978
|
+
return sampleTerrainFieldElevation(
|
|
1723
1979
|
terrainFieldStorage,
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
);
|
|
1980
|
+
u,
|
|
1981
|
+
v,
|
|
1982
|
+
int(instanceIndex)
|
|
1983
|
+
).mul(terrainUniforms.uElevationScale);
|
|
1729
1984
|
}
|
|
1730
1985
|
function createNormalAssignment(terrainUniforms, terrainFieldStorage) {
|
|
1731
1986
|
if (!terrainFieldStorage) return;
|
|
@@ -1734,7 +1989,12 @@ function createNormalAssignment(terrainUniforms, terrainFieldStorage) {
|
|
|
1734
1989
|
const localVertexIndex = int(vertexIndex);
|
|
1735
1990
|
const ix = localVertexIndex.mod(edgeVertexCount);
|
|
1736
1991
|
const iy = localVertexIndex.div(edgeVertexCount);
|
|
1737
|
-
const normalXZ = loadTerrainFieldNormal(
|
|
1992
|
+
const normalXZ = loadTerrainFieldNormal(
|
|
1993
|
+
terrainFieldStorage,
|
|
1994
|
+
ix,
|
|
1995
|
+
iy,
|
|
1996
|
+
nodeIndex
|
|
1997
|
+
);
|
|
1738
1998
|
const nx = normalXZ.x;
|
|
1739
1999
|
const nz = normalXZ.y;
|
|
1740
2000
|
const nySq = float(1).sub(nx.mul(nx)).sub(nz.mul(nz)).max(float(0));
|
|
@@ -1742,10 +2002,16 @@ function createNormalAssignment(terrainUniforms, terrainFieldStorage) {
|
|
|
1742
2002
|
normalLocal.assign(vec3(nx, ny, nz));
|
|
1743
2003
|
}
|
|
1744
2004
|
function createTileWorldPosition(leafStorage, terrainUniforms, terrainFieldStorage) {
|
|
1745
|
-
const baseWorldPosition = createTileBaseWorldPosition(
|
|
2005
|
+
const baseWorldPosition = createTileBaseWorldPosition(
|
|
2006
|
+
leafStorage,
|
|
2007
|
+
terrainUniforms
|
|
2008
|
+
);
|
|
1746
2009
|
return Fn(() => {
|
|
1747
2010
|
const base = baseWorldPosition();
|
|
1748
|
-
const yElevation = createTileElevation(
|
|
2011
|
+
const yElevation = createTileElevation(
|
|
2012
|
+
terrainUniforms,
|
|
2013
|
+
terrainFieldStorage
|
|
2014
|
+
);
|
|
1749
2015
|
const skirtVertex = isSkirtVertex(terrainUniforms.uInnerTileSegments);
|
|
1750
2016
|
const skirtY = base.y.add(yElevation).sub(terrainUniforms.uSkirtScale.toVar());
|
|
1751
2017
|
const worldY = select(skirtVertex, skirtY, base.y.add(yElevation));
|
|
@@ -1768,7 +2034,7 @@ const positionNodeTask = task((get, work) => {
|
|
|
1768
2034
|
}).displayName("positionNodeTask");
|
|
1769
2035
|
|
|
1770
2036
|
function terrainGraph() {
|
|
1771
|
-
return graph().add(instanceIdTask).add(quadtreeConfigTask).add(quadtreeUpdateTask).add(leafStorageTask).add(surfaceTask).add(leafGpuBufferTask).add(createUniformsTask).add(updateUniformsTask).add(positionNodeTask).add(createElevationFieldContextTask).add(tileNodesTask).add(createTerrainFieldTextureTask).add(elevationFieldStageTask).add(terrainFieldStageTask).add(compileComputeTask).add(executeComputeTask);
|
|
2037
|
+
return graph().add(instanceIdTask).add(quadtreeConfigTask).add(quadtreeUpdateTask).add(leafStorageTask).add(surfaceTask).add(leafGpuBufferTask).add(gpuSpatialIndexStorageTask).add(gpuSpatialIndexUploadTask).add(createUniformsTask).add(updateUniformsTask).add(positionNodeTask).add(createElevationFieldContextTask).add(tileNodesTask).add(createTerrainFieldTextureTask).add(createTerrainSamplerTask).add(elevationFieldStageTask).add(terrainFieldStageTask).add(compileComputeTask).add(executeComputeTask);
|
|
1772
2038
|
}
|
|
1773
2039
|
const terrainTasks = {
|
|
1774
2040
|
instanceId: instanceIdTask,
|
|
@@ -1777,12 +2043,15 @@ const terrainTasks = {
|
|
|
1777
2043
|
leafStorage: leafStorageTask,
|
|
1778
2044
|
surface: surfaceTask,
|
|
1779
2045
|
leafGpuBuffer: leafGpuBufferTask,
|
|
2046
|
+
gpuSpatialIndexStorage: gpuSpatialIndexStorageTask,
|
|
2047
|
+
gpuSpatialIndexUpload: gpuSpatialIndexUploadTask,
|
|
1780
2048
|
createUniforms: createUniformsTask,
|
|
1781
2049
|
updateUniforms: updateUniformsTask,
|
|
1782
2050
|
positionNode: positionNodeTask,
|
|
1783
2051
|
createElevationFieldContext: createElevationFieldContextTask,
|
|
1784
2052
|
createTileNodes: tileNodesTask,
|
|
1785
2053
|
createTerrainFieldTexture: createTerrainFieldTextureTask,
|
|
2054
|
+
createTerrainSampler: createTerrainSamplerTask,
|
|
1786
2055
|
elevationFieldStage: elevationFieldStageTask,
|
|
1787
2056
|
terrainFieldStage: terrainFieldStageTask,
|
|
1788
2057
|
compileCompute: compileComputeTask,
|
|
@@ -1841,4 +2110,4 @@ const voronoiCells = Fn((params) => {
|
|
|
1841
2110
|
return k;
|
|
1842
2111
|
});
|
|
1843
2112
|
|
|
1844
|
-
export { ArrayTextureBackend, AtlasBackend, Dir, TerrainGeometry, TerrainMesh, Texture3DBackend, U32_EMPTY, allocLeafSet, allocSeamTable, beginUpdate, blendAngleCorrectedNormals, buildLeafIndex, buildSeams2to1, compileComputeTask, createComputePipelineTasks, createCubeSphereSurface, createElevationFieldContextTask, createFlatSurface, createInfiniteFlatSurface, createSpatialIndex, createState, createTerrainFieldStorage, createTerrainFieldTextureTask, createTerrainUniforms, createUniformsTask, deriveNormalZ, elevationFieldStageTask, elevationFn, elevationScale, executeComputeTask, getDeviceComputeLimits, innerTileSegments, instanceIdTask, isSkirtUV, isSkirtVertex, leafGpuBufferTask, leafStorageTask, loadTerrainField, loadTerrainFieldElevation, loadTerrainFieldNormal, maxLevel, maxNodes, origin, packTerrainFieldSample, positionNodeTask, quadtreeConfigTask, quadtreeUpdate, quadtreeUpdateTask, resetLeafSet, resetSeamTable, rootSize, sampleTerrainField, sampleTerrainFieldElevation, sampleTerrainFieldNormal, skirtScale, storeTerrainField, surface, surfaceTask, terrainFieldFilter, terrainFieldStageTask, terrainGraph, terrainTasks, textureSpaceToVectorSpace, tileNodesTask, update, updateUniformsTask, vElevation, vGlobalVertexIndex, vectorSpaceToTextureSpace, voronoiCells };
|
|
2113
|
+
export { ArrayTextureBackend, AtlasBackend, Dir, TerrainGeometry, TerrainMesh, Texture3DBackend, U32_EMPTY, allocLeafSet, allocSeamTable, beginUpdate, blendAngleCorrectedNormals, buildLeafIndex, buildSeams2to1, compileComputeTask, createComputePipelineTasks, createCubeSphereSurface, createElevationFieldContextTask, createFlatSurface, createInfiniteFlatSurface, createSpatialIndex, createState, createTerrainFieldStorage, createTerrainFieldTextureTask, createTerrainSampler, createTerrainSamplerTask, createTerrainUniforms, createUniformsTask, deriveNormalZ, elevationFieldStageTask, elevationFn, elevationScale, executeComputeTask, getDeviceComputeLimits, gpuSpatialIndexStorageTask, gpuSpatialIndexUploadTask, innerTileSegments, instanceIdTask, isSkirtUV, isSkirtVertex, leafGpuBufferTask, leafStorageTask, loadTerrainField, loadTerrainFieldElevation, loadTerrainFieldNormal, maxLevel, maxNodes, origin, packTerrainFieldSample, positionNodeTask, quadtreeConfigTask, quadtreeUpdate, quadtreeUpdateTask, resetLeafSet, resetSeamTable, rootSize, sampleTerrainField, sampleTerrainFieldElevation, sampleTerrainFieldNormal, skirtScale, storeTerrainField, surface, surfaceTask, terrainFieldFilter, terrainFieldStageTask, terrainGraph, terrainTasks, textureSpaceToVectorSpace, tileNodesTask, update, updateUniformsTask, vElevation, vGlobalVertexIndex, vectorSpaceToTextureSpace, voronoiCells };
|
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.9",
|
|
10
10
|
"type": "module",
|
|
11
11
|
"main": "./dist/index.mjs",
|
|
12
12
|
"module": "./dist/index.mjs",
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
"mitata": "^1.0.34",
|
|
30
30
|
"unbuild": "^3.5.0",
|
|
31
31
|
"vitest": "^4.0.16",
|
|
32
|
-
"@config/
|
|
32
|
+
"@config/typescript": "0.1.0",
|
|
33
33
|
"@config/oxfmt": "0.1.0",
|
|
34
|
-
"@config/
|
|
34
|
+
"@config/oxlint": "0.1.0"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@hello-terrain/work": "0.
|
|
37
|
+
"@hello-terrain/work": "0.3.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "unbuild",
|