@aethervtt/shard-terrain 0.1.0 → 0.2.0
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/biomes.d.ts +1 -0
- package/dist/biomes.js +3 -0
- package/dist/chunk.d.ts +0 -44
- package/dist/chunk.js +2 -127
- package/dist/colliders.d.ts +16 -1
- package/dist/colliders.js +39 -24
- package/dist/components.d.ts +5 -0
- package/dist/components.js +7 -2
- package/dist/cube-sphere.d.ts +50 -0
- package/dist/cube-sphere.js +224 -0
- package/dist/grid-mesh.d.ts +49 -0
- package/dist/grid-mesh.js +208 -0
- package/dist/heightfield/bake.d.ts +120 -0
- package/dist/heightfield/bake.js +494 -0
- package/dist/heightfield/colliders.d.ts +39 -0
- package/dist/heightfield/colliders.js +152 -0
- package/dist/heightfield/component.d.ts +14 -0
- package/dist/heightfield/component.js +59 -0
- package/dist/heightfield/deflate.d.ts +11 -0
- package/dist/heightfield/deflate.js +484 -0
- package/dist/heightfield/heightmap.d.ts +27 -0
- package/dist/heightfield/heightmap.js +160 -0
- package/dist/heightfield/kernel.d.ts +277 -0
- package/dist/heightfield/kernel.js +1033 -0
- package/dist/heightfield/material.d.ts +13 -0
- package/dist/heightfield/material.js +394 -0
- package/dist/heightfield/methods.d.ts +104 -0
- package/dist/heightfield/methods.js +218 -0
- package/dist/heightfield/pack.d.ts +59 -0
- package/dist/heightfield/pack.js +127 -0
- package/dist/heightfield/pages.d.ts +91 -0
- package/dist/heightfield/pages.js +244 -0
- package/dist/heightfield/project.d.ts +40 -0
- package/dist/heightfield/project.js +55 -0
- package/dist/heightfield/queries.d.ts +48 -0
- package/dist/heightfield/queries.js +150 -0
- package/dist/heightfield/render.d.ts +108 -0
- package/dist/heightfield/render.js +727 -0
- package/dist/heightfield/root-grid.d.ts +38 -0
- package/dist/heightfield/root-grid.js +109 -0
- package/dist/heightfield/runtime.d.ts +105 -0
- package/dist/heightfield/runtime.js +312 -0
- package/dist/heightfield/source-asset.d.ts +39 -0
- package/dist/heightfield/source-asset.js +372 -0
- package/dist/heightfield/source.d.ts +151 -0
- package/dist/heightfield/source.js +402 -0
- package/dist/heightfield/stack.d.ts +29 -0
- package/dist/heightfield/stack.js +158 -0
- package/dist/heightfield/system.d.ts +22 -0
- package/dist/heightfield/system.js +165 -0
- package/dist/heightfield/testing.d.ts +317 -0
- package/dist/heightfield/testing.js +441 -0
- package/dist/heightfield/worker.js +47 -0
- package/dist/heights.d.ts +8 -3
- package/dist/heights.js +8 -4
- package/dist/index.d.ts +24 -7
- package/dist/index.js +158 -8
- package/dist/lod.d.ts +27 -0
- package/dist/lod.js +23 -1
- package/dist/material.js +2 -1
- package/dist/methods.js +64 -16
- package/dist/overlays.d.ts +2 -0
- package/dist/overlays.js +48 -6
- package/dist/planet.d.ts +2 -7
- package/dist/planet.js +8 -18
- package/dist/plugin.d.ts +10 -3
- package/dist/plugin.js +48 -8
- package/dist/quadtree.d.ts +61 -39
- package/dist/quadtree.js +44 -208
- package/dist/render.d.ts +7 -1
- package/dist/render.js +8 -6
- package/dist/test-planet.d.ts +39 -0
- package/dist/test-planet.js +40 -4
- package/dist/testing.d.ts +2 -0
- package/dist/testing.js +42 -2
- package/package.json +16 -16
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { defineSystem } from "@aethervtt/shard-core";
|
|
2
|
+
import { RigidBody } from "@aethervtt/shard-physics";
|
|
3
|
+
import {
|
|
4
|
+
addRenderFeatures,
|
|
5
|
+
GpuAssetsResource,
|
|
6
|
+
Graph,
|
|
7
|
+
RenderPhase,
|
|
8
|
+
Shaders
|
|
9
|
+
} from "@aethervtt/shard-render";
|
|
10
|
+
import { GlobalTransform } from "@aethervtt/shard-transform";
|
|
11
|
+
import { anchorQueries } from "../colliders.js";
|
|
12
|
+
import { TerrainAnchor, TerrainBudget } from "../components.js";
|
|
13
|
+
import { TerrainWorld } from "../heights.js";
|
|
14
|
+
import { updateHeightfieldColliders } from "./colliders.js";
|
|
15
|
+
import { Terrain } from "./component.js";
|
|
16
|
+
import { HEIGHTFIELD_SHADERS } from "./material.js";
|
|
17
|
+
import { enqueueResident } from "./render.js";
|
|
18
|
+
import { HeightfieldRuntime } from "./runtime.js";
|
|
19
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20
|
+
const p = new Float64Array(3);
|
|
21
|
+
function gatherHeightfieldAnchors(_world, rt, q) {
|
|
22
|
+
const s = rt.settings;
|
|
23
|
+
const layout = rt.layout;
|
|
24
|
+
seen.clear();
|
|
25
|
+
rt.anchors = 0;
|
|
26
|
+
rt.anchorEntity.length = 0;
|
|
27
|
+
const top = rt.hi;
|
|
28
|
+
const add = (entity, m, row, radius) => {
|
|
29
|
+
if (seen.has(entity)) return;
|
|
30
|
+
seen.add(entity);
|
|
31
|
+
rt.frame.pointToPlanet(m[row * 12 + 3], m[row * 12 + 7], m[row * 12 + 11], p);
|
|
32
|
+
if (p[0] < -radius || p[2] < -radius || p[0] > layout.sizeX + radius || p[2] > layout.sizeZ + radius)
|
|
33
|
+
return;
|
|
34
|
+
if (p[1] - top > radius) return;
|
|
35
|
+
const i = rt.anchors++;
|
|
36
|
+
if (rt.anchorPos.length < rt.anchors * 3) {
|
|
37
|
+
const pos = new Float64Array(rt.anchors * 6);
|
|
38
|
+
pos.set(rt.anchorPos);
|
|
39
|
+
rt.anchorPos = pos;
|
|
40
|
+
const r = new Float64Array(rt.anchors * 2);
|
|
41
|
+
r.set(rt.anchorRadius);
|
|
42
|
+
rt.anchorRadius = r;
|
|
43
|
+
}
|
|
44
|
+
rt.anchorPos[i * 3] = p[0];
|
|
45
|
+
rt.anchorPos[i * 3 + 1] = p[1];
|
|
46
|
+
rt.anchorPos[i * 3 + 2] = p[2];
|
|
47
|
+
rt.anchorRadius[i] = radius;
|
|
48
|
+
rt.anchorEntity.push(entity);
|
|
49
|
+
};
|
|
50
|
+
for (const table of q.anchors.tables) {
|
|
51
|
+
const enabled = table.column(TerrainAnchor, "enabled");
|
|
52
|
+
const radius = table.column(TerrainAnchor, "radius");
|
|
53
|
+
const m = table.column(GlobalTransform, "matrix");
|
|
54
|
+
for (let row = 0; row < table.count; row++) {
|
|
55
|
+
const entity = table.entities[row];
|
|
56
|
+
if (!enabled[row]) {
|
|
57
|
+
seen.add(entity);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
add(entity, m, row, radius[row] > 0 ? radius[row] : s.colliderRadius);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for (const table of q.agents.tables) {
|
|
64
|
+
const m = table.column(GlobalTransform, "matrix");
|
|
65
|
+
for (let row = 0; row < table.count; row++) add(table.entities[row], m, row, s.colliderRadius);
|
|
66
|
+
}
|
|
67
|
+
for (const table of q.characters.tables) {
|
|
68
|
+
const m = table.column(GlobalTransform, "matrix");
|
|
69
|
+
for (let row = 0; row < table.count; row++) add(table.entities[row], m, row, s.colliderRadius);
|
|
70
|
+
}
|
|
71
|
+
for (const table of q.bodies.tables) {
|
|
72
|
+
const kind = table.column(RigidBody, "kind");
|
|
73
|
+
const m = table.column(GlobalTransform, "matrix");
|
|
74
|
+
for (let row = 0; row < table.count; row++) {
|
|
75
|
+
if (kind[row] !== 0) continue;
|
|
76
|
+
add(table.entities[row], m, row, s.colliderRadius);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const heightfieldUpdates = [];
|
|
81
|
+
const updateHeightfields = defineSystem({
|
|
82
|
+
name: "terrain/heightfields",
|
|
83
|
+
description: "Resolves each Terrain\u2019s source, bakes its packs when missing or stale (on first use), loads its coarse levels, and keeps heightfield collider tiles (fixed bodies) around characters, dynamic bodies, NavAgents and TerrainAnchors.",
|
|
84
|
+
setup: (world) => ({
|
|
85
|
+
terrains: world.query({ with: [Terrain] }),
|
|
86
|
+
anchors: anchorQueries(world)
|
|
87
|
+
}),
|
|
88
|
+
run: (s, world) => {
|
|
89
|
+
const state = world.resource(TerrainWorld);
|
|
90
|
+
const frame = state.frame;
|
|
91
|
+
for (const table of s.terrains.tables) {
|
|
92
|
+
for (let row = 0; row < table.count; row++) {
|
|
93
|
+
const entity = table.entities[row];
|
|
94
|
+
if (!state.heightfields.has(entity))
|
|
95
|
+
state.heightfields.set(entity, new HeightfieldRuntime(entity));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const env = { fs: state.fs, workers: state.workers };
|
|
99
|
+
const colliderCache = world.resource(TerrainBudget).colliderCache;
|
|
100
|
+
for (const rt of state.heightfields.values()) {
|
|
101
|
+
if (!world.isAlive(rt.entity)) continue;
|
|
102
|
+
rt.refresh(world, env);
|
|
103
|
+
rt.frame.update(world, rt.entity);
|
|
104
|
+
if (!rt.ready) continue;
|
|
105
|
+
gatherHeightfieldAnchors(world, rt, s.anchors);
|
|
106
|
+
updateHeightfieldColliders(world, rt, frame, colliderCache);
|
|
107
|
+
for (const update of heightfieldUpdates) update(world, rt, frame);
|
|
108
|
+
const r = rt.parts.get("render");
|
|
109
|
+
if (r && rt.streaming && r.residentVersion !== rt.version) {
|
|
110
|
+
r.residentVersion = rt.version;
|
|
111
|
+
enqueueResident(rt, r);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
const ownBytes = /* @__PURE__ */ new Map();
|
|
117
|
+
function writeUniforms(world, gpu) {
|
|
118
|
+
const state = world.tryResource(TerrainWorld);
|
|
119
|
+
const assets = world.tryResource(GpuAssetsResource);
|
|
120
|
+
if (!state || !assets) return;
|
|
121
|
+
for (const rt of state.heightfields.values()) {
|
|
122
|
+
const r = rt.parts.get("render");
|
|
123
|
+
if (!r) continue;
|
|
124
|
+
const material = r.material;
|
|
125
|
+
const gm = assets.materials.get(material);
|
|
126
|
+
const layout = material.type.layout;
|
|
127
|
+
if (!gm?.ownBuffer || !layout || gm.version !== material.version) continue;
|
|
128
|
+
let view = ownBytes.get(layout.size);
|
|
129
|
+
if (!view) {
|
|
130
|
+
view = new DataView(new ArrayBuffer(layout.size));
|
|
131
|
+
ownBytes.set(layout.size, view);
|
|
132
|
+
}
|
|
133
|
+
layout.write(view, 0, material.value);
|
|
134
|
+
gpu.device.queue.writeBuffer(gm.ownBuffer, 0, view.buffer, 0, layout.size);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function registerHeightfield(app) {
|
|
138
|
+
const graph = app.world.tryResource(Graph);
|
|
139
|
+
if (!graph) return;
|
|
140
|
+
for (const [path, source] of Object.entries(HEIGHTFIELD_SHADERS))
|
|
141
|
+
app.world.resource(Shaders).register(path, source, "@aethervtt/shard-terrain");
|
|
142
|
+
addRenderFeatures(app.world, {
|
|
143
|
+
name: "terrain/heightfield",
|
|
144
|
+
description: "Heightfield terrain: one grid mesh drawn per streamed page, heights fetched in the vertex stage.",
|
|
145
|
+
nodes: ["terrain/heightfield"],
|
|
146
|
+
baseline: {
|
|
147
|
+
strategy: "The same shader: vertex texture fetch of the RGBA8 page pool and the chunk table (WebGL2 texelFetch), pages uploaded with writeTexture. No compute: leaf normals come from the inflate worker."
|
|
148
|
+
},
|
|
149
|
+
components: [Terrain]
|
|
150
|
+
});
|
|
151
|
+
graph.addNode("terrain/heightfield", {
|
|
152
|
+
kind: "raw",
|
|
153
|
+
phase: RenderPhase.Setup,
|
|
154
|
+
sideEffects: true,
|
|
155
|
+
run(ctx) {
|
|
156
|
+
writeUniforms(ctx.world, ctx.gpu);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
export {
|
|
161
|
+
gatherHeightfieldAnchors,
|
|
162
|
+
heightfieldUpdates,
|
|
163
|
+
registerHeightfield,
|
|
164
|
+
updateHeightfields
|
|
165
|
+
};
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers (not a test) for heightfield terrain (spec 0071): an app with a terrain baked in
|
|
3
|
+
* memory from a source object, a camera placed in the terrain's frame, a loop that runs frames until
|
|
4
|
+
* streaming settles, and hole detection against the sky color.
|
|
5
|
+
*/
|
|
6
|
+
import { type Entity, type World } from '@aethervtt/shard-core';
|
|
7
|
+
import type { GpuContext } from '@aethervtt/shard-gpu';
|
|
8
|
+
import { NoiseGraph } from '@aethervtt/shard-noise';
|
|
9
|
+
import type { PlatformFileSystem, Workers } from '@aethervtt/shard-platform';
|
|
10
|
+
import { App, type Plugin } from '@aethervtt/shard-runtime';
|
|
11
|
+
import type { HeightfieldRender } from './render.js';
|
|
12
|
+
import type { HeightfieldRuntime } from './runtime.js';
|
|
13
|
+
import { TerrainSourceAsset } from './source-asset.js';
|
|
14
|
+
export interface HeightfieldOptions {
|
|
15
|
+
/** A `*.terrain.json` object; asset paths name entries of `noise` and `heightmaps`. */
|
|
16
|
+
source: unknown;
|
|
17
|
+
noise?: Record<string, NoiseGraph>;
|
|
18
|
+
heightmaps?: Record<string, {
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
data: Float32Array;
|
|
22
|
+
}>;
|
|
23
|
+
/** Blocks per side override (tests: small blocks). */
|
|
24
|
+
block?: number;
|
|
25
|
+
workers?: Workers;
|
|
26
|
+
width?: number;
|
|
27
|
+
heightPx?: number;
|
|
28
|
+
fovY?: number;
|
|
29
|
+
clearColor?: [number, number, number, number];
|
|
30
|
+
physics?: boolean;
|
|
31
|
+
skirts?: boolean;
|
|
32
|
+
errorPixels?: number;
|
|
33
|
+
vertexPixels?: number;
|
|
34
|
+
extra?: Plugin[];
|
|
35
|
+
pages?: number;
|
|
36
|
+
pagesPerFrame?: number;
|
|
37
|
+
/** Where the terrain's corner sits in the world (its Transform). */
|
|
38
|
+
at?: [number, number, number];
|
|
39
|
+
/** Bake into (and read packs from) this file service's `.shard/cache/terrain`; memory without. */
|
|
40
|
+
fs?: PlatformFileSystem;
|
|
41
|
+
/** The source asset's guid (its cache directory), default `mem:…`. */
|
|
42
|
+
name?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface HeightfieldApp {
|
|
45
|
+
app: App;
|
|
46
|
+
world: World;
|
|
47
|
+
terrain: Entity;
|
|
48
|
+
camera: Entity;
|
|
49
|
+
sun: Entity;
|
|
50
|
+
view: string;
|
|
51
|
+
runtime(): HeightfieldRuntime;
|
|
52
|
+
render(): HeightfieldRender | undefined;
|
|
53
|
+
}
|
|
54
|
+
/** A terrain source asset made in memory, its assets in the world's stores. */
|
|
55
|
+
export declare function sourceAsset(world: World, o: HeightfieldOptions): TerrainSourceAsset;
|
|
56
|
+
export declare function heightfieldApp(gpu: GpuContext | undefined, o: HeightfieldOptions): Promise<HeightfieldApp>;
|
|
57
|
+
/** Puts the camera at `eye` (terrain frame) looking at `target`, up +Y. */
|
|
58
|
+
export declare function lookAt(p: HeightfieldApp, eye: ArrayLike<number>, target: ArrayLike<number>): void;
|
|
59
|
+
/** Waits until the terrain is streaming (its bake done and coarse levels loaded). */
|
|
60
|
+
export declare function untilStreaming(p: HeightfieldApp, maxMs?: number): Promise<void>;
|
|
61
|
+
/** Runs frames (awaiting pipelines and reads) until nothing is requested, read or uploading. */
|
|
62
|
+
export declare function settleHeightfield(p: HeightfieldApp, maxFrames?: number): Promise<number>;
|
|
63
|
+
/** Renders one more frame and captures the camera's view. */
|
|
64
|
+
export declare function capture(p: HeightfieldApp): Promise<import("@aethervtt/shard-render").CapturedImage>;
|
|
65
|
+
/**
|
|
66
|
+
* Sky-colored pixels (the magenta clear color) whose ray from the camera reaches the plane of the
|
|
67
|
+
* lowest possible ground (`lowest`) inside the terrain's rectangle (less `margin`): the ground is
|
|
68
|
+
* above that plane everywhere, so the ray must have hit it.
|
|
69
|
+
*/
|
|
70
|
+
export declare function holes(data: Uint8Array, W: number, H: number, fov: number, eye: ArrayLike<number>, right: number[], up: number[], forward: number[], lowest: number, rect: [number, number, number, number]): number;
|
|
71
|
+
/** 0071's 2 km test terrain at 0.5 m: hills, a valley image, and a road flattened through them. */
|
|
72
|
+
export declare function valleySource(size?: number, spacing?: number): {
|
|
73
|
+
source: {
|
|
74
|
+
size: number[];
|
|
75
|
+
spacing: number;
|
|
76
|
+
heightRange: number[];
|
|
77
|
+
seed: number;
|
|
78
|
+
splines: {
|
|
79
|
+
road: {
|
|
80
|
+
points: (string | number)[][];
|
|
81
|
+
width: number;
|
|
82
|
+
falloff: number;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
height: ({
|
|
86
|
+
noise: {
|
|
87
|
+
path: string;
|
|
88
|
+
};
|
|
89
|
+
scale: number;
|
|
90
|
+
image?: undefined;
|
|
91
|
+
at?: undefined;
|
|
92
|
+
size?: undefined;
|
|
93
|
+
rotation?: undefined;
|
|
94
|
+
range?: undefined;
|
|
95
|
+
blend?: undefined;
|
|
96
|
+
falloff?: undefined;
|
|
97
|
+
spline?: undefined;
|
|
98
|
+
mode?: undefined;
|
|
99
|
+
} | {
|
|
100
|
+
noise?: undefined;
|
|
101
|
+
scale?: undefined;
|
|
102
|
+
image: {
|
|
103
|
+
path: string;
|
|
104
|
+
};
|
|
105
|
+
at: number[];
|
|
106
|
+
size: number[];
|
|
107
|
+
rotation: number;
|
|
108
|
+
range: number[];
|
|
109
|
+
blend: string;
|
|
110
|
+
falloff: number;
|
|
111
|
+
spline?: undefined;
|
|
112
|
+
mode?: undefined;
|
|
113
|
+
} | {
|
|
114
|
+
noise?: undefined;
|
|
115
|
+
scale?: undefined;
|
|
116
|
+
image?: undefined;
|
|
117
|
+
at?: undefined;
|
|
118
|
+
size?: undefined;
|
|
119
|
+
rotation?: undefined;
|
|
120
|
+
range?: undefined;
|
|
121
|
+
blend?: undefined;
|
|
122
|
+
falloff?: undefined;
|
|
123
|
+
spline: string;
|
|
124
|
+
mode: string;
|
|
125
|
+
})[];
|
|
126
|
+
layers: ({
|
|
127
|
+
name: string;
|
|
128
|
+
triplanar?: undefined;
|
|
129
|
+
} | {
|
|
130
|
+
name: string;
|
|
131
|
+
triplanar: boolean;
|
|
132
|
+
})[];
|
|
133
|
+
paint: ({
|
|
134
|
+
blend?: undefined;
|
|
135
|
+
spline?: undefined;
|
|
136
|
+
layer: string;
|
|
137
|
+
slope?: undefined;
|
|
138
|
+
} | {
|
|
139
|
+
spline?: undefined;
|
|
140
|
+
layer: string;
|
|
141
|
+
slope: number[];
|
|
142
|
+
blend: number;
|
|
143
|
+
} | {
|
|
144
|
+
slope?: undefined;
|
|
145
|
+
layer: string;
|
|
146
|
+
spline: string;
|
|
147
|
+
blend: number;
|
|
148
|
+
})[];
|
|
149
|
+
};
|
|
150
|
+
heightmaps: {
|
|
151
|
+
valley: {
|
|
152
|
+
width: number;
|
|
153
|
+
height: number;
|
|
154
|
+
data: Float32Array<ArrayBuffer>;
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
/** The hills graph of the valley terrain (the playground's #heightfield page uses it too). */
|
|
159
|
+
export declare const VALLEY_HILLS: {
|
|
160
|
+
output: string;
|
|
161
|
+
nodes: {
|
|
162
|
+
base: {
|
|
163
|
+
fbm: {
|
|
164
|
+
source: string;
|
|
165
|
+
octaves: number;
|
|
166
|
+
frequency: number;
|
|
167
|
+
seed: number;
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
ridges: {
|
|
171
|
+
ridged: {
|
|
172
|
+
source: string;
|
|
173
|
+
octaves: number;
|
|
174
|
+
frequency: number;
|
|
175
|
+
seed: number;
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
h: {
|
|
179
|
+
add: (string | {
|
|
180
|
+
multiply: (string | number)[];
|
|
181
|
+
})[];
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
/** What was drawn in one frame: rendered nodes (by key) and the morph camera. */
|
|
186
|
+
export interface DrawnFrame {
|
|
187
|
+
nodes: Map<number, {
|
|
188
|
+
depth: number;
|
|
189
|
+
x: number;
|
|
190
|
+
z: number;
|
|
191
|
+
locks: number[];
|
|
192
|
+
mask: number;
|
|
193
|
+
fade: number;
|
|
194
|
+
anchored: boolean;
|
|
195
|
+
}>;
|
|
196
|
+
/** Camera position in the terrain's frame. */
|
|
197
|
+
camera: number[];
|
|
198
|
+
/** pixelsPerRadian / (errorPixels × lodBias): a depth's split distance per metre of error. */
|
|
199
|
+
splitScale: number;
|
|
200
|
+
errors: Float32Array;
|
|
201
|
+
}
|
|
202
|
+
export declare function snapshotDrawn(p: HeightfieldApp): DrawnFrame;
|
|
203
|
+
/**
|
|
204
|
+
* The drawn ground's height at terrain point (x, z) in a frame, as the vertex stage puts it: the
|
|
205
|
+
* rendered node there, its page's heights morphed toward the parent level (odd vertices to their
|
|
206
|
+
* even neighbors' average, odd-odd along the anti-diagonal) by distance, edge locks, quadrants and
|
|
207
|
+
* fade, then the anti-diagonal triangle under the point. Undefined where nothing drawn covers it
|
|
208
|
+
* or its page isn't in the CPU cache. `edge` skips points within a cell of a stitched edge (its
|
|
209
|
+
* triangles are zipped, not quads).
|
|
210
|
+
*/
|
|
211
|
+
export declare function drawnHeightAt(rt: HeightfieldRuntime, f: DrawnFrame, x: number, z: number, skipStitched?: boolean): number | undefined;
|
|
212
|
+
export interface HeightfieldWalk {
|
|
213
|
+
/** FNV-1a over the characters' final terrain-frame positions (f64 bytes) and the tile count. */
|
|
214
|
+
checksum: string;
|
|
215
|
+
walked: number[];
|
|
216
|
+
/** The lowest the feet got under the collider surface (m, negative is below). */
|
|
217
|
+
lowest: number;
|
|
218
|
+
tiles: number;
|
|
219
|
+
ms: number;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Drops `characters` capsules at deterministic points on the valley terrain and walks each for
|
|
223
|
+
* `seconds` headless (no GPU, colliders only). Node (packages/terrain tests) and Chrome (the
|
|
224
|
+
* playground's #heightfield page) must print the same checksum: the bake, the collider tiles and
|
|
225
|
+
* physics are deterministic.
|
|
226
|
+
*/
|
|
227
|
+
export declare function heightfieldWalk(characters?: number, seconds?: number, workers?: Workers): Promise<HeightfieldWalk>;
|
|
228
|
+
/**
|
|
229
|
+
* 0071's 16 km terrain at 1 m (benches only: about 600 MB of packs): the valley terrain's layers
|
|
230
|
+
* spread over 16 km, with a 12 km road.
|
|
231
|
+
*/
|
|
232
|
+
export declare function openWorldSource(): {
|
|
233
|
+
heightmaps: {
|
|
234
|
+
valley: {
|
|
235
|
+
width: number;
|
|
236
|
+
height: number;
|
|
237
|
+
data: Float32Array<ArrayBuffer>;
|
|
238
|
+
};
|
|
239
|
+
};
|
|
240
|
+
source: {
|
|
241
|
+
size: number[];
|
|
242
|
+
spacing: number;
|
|
243
|
+
seed: number;
|
|
244
|
+
splines: {
|
|
245
|
+
road: {
|
|
246
|
+
points: (string | number)[][];
|
|
247
|
+
width: number;
|
|
248
|
+
falloff: number;
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
height: ({
|
|
252
|
+
noise: {
|
|
253
|
+
path: string;
|
|
254
|
+
};
|
|
255
|
+
scale: number;
|
|
256
|
+
image?: undefined;
|
|
257
|
+
at?: undefined;
|
|
258
|
+
size?: undefined;
|
|
259
|
+
rotation?: undefined;
|
|
260
|
+
range?: undefined;
|
|
261
|
+
blend?: undefined;
|
|
262
|
+
falloff?: undefined;
|
|
263
|
+
spline?: undefined;
|
|
264
|
+
mode?: undefined;
|
|
265
|
+
} | {
|
|
266
|
+
noise?: undefined;
|
|
267
|
+
scale?: undefined;
|
|
268
|
+
image: {
|
|
269
|
+
path: string;
|
|
270
|
+
};
|
|
271
|
+
at: number[];
|
|
272
|
+
size: number[];
|
|
273
|
+
rotation: number;
|
|
274
|
+
range: number[];
|
|
275
|
+
blend: string;
|
|
276
|
+
falloff: number;
|
|
277
|
+
spline?: undefined;
|
|
278
|
+
mode?: undefined;
|
|
279
|
+
} | {
|
|
280
|
+
noise?: undefined;
|
|
281
|
+
scale?: undefined;
|
|
282
|
+
image?: undefined;
|
|
283
|
+
at?: undefined;
|
|
284
|
+
size?: undefined;
|
|
285
|
+
rotation?: undefined;
|
|
286
|
+
range?: undefined;
|
|
287
|
+
blend?: undefined;
|
|
288
|
+
falloff?: undefined;
|
|
289
|
+
spline: string;
|
|
290
|
+
mode: string;
|
|
291
|
+
})[];
|
|
292
|
+
layers: ({
|
|
293
|
+
name: string;
|
|
294
|
+
triplanar?: undefined;
|
|
295
|
+
} | {
|
|
296
|
+
name: string;
|
|
297
|
+
triplanar: boolean;
|
|
298
|
+
})[];
|
|
299
|
+
paint: ({
|
|
300
|
+
blend?: undefined;
|
|
301
|
+
spline?: undefined;
|
|
302
|
+
layer: string;
|
|
303
|
+
slope?: undefined;
|
|
304
|
+
} | {
|
|
305
|
+
spline?: undefined;
|
|
306
|
+
layer: string;
|
|
307
|
+
slope: number[];
|
|
308
|
+
blend: number;
|
|
309
|
+
} | {
|
|
310
|
+
slope?: undefined;
|
|
311
|
+
layer: string;
|
|
312
|
+
spline: string;
|
|
313
|
+
blend: number;
|
|
314
|
+
})[];
|
|
315
|
+
heightRange: number[];
|
|
316
|
+
};
|
|
317
|
+
};
|