feat: actors stage 2
This commit is contained in:
parent
4869db01e5
commit
581e5892b0
19 changed files with 752 additions and 594 deletions
98
app/level.ts
98
app/level.ts
|
|
@ -55,6 +55,14 @@ type ChunkMaterials = {
|
|||
flower: Material
|
||||
}
|
||||
|
||||
/** A chunk-material key (also the tag props reference, e.g. a tree's `trunk`). */
|
||||
type MatKey = keyof ChunkMaterials
|
||||
|
||||
/** The fixed order draw groups are emitted in (grass first, flowers -- double-sided
|
||||
* -- last), so the per-chunk draw sequence is deterministic and matches the pre-
|
||||
* registry order. Every material key must appear here. */
|
||||
const MAT_ORDER: MatKey[] = ["grass", "rock", "bark", "birch", "leaf", "needle", "flower"]
|
||||
|
||||
/** The playground: a flat-floored room dropped into the center of a big open
|
||||
* landscape. The room (floor/walls/crate) is small and always drawn; the
|
||||
* outdoor world is split into `chunks` that are frustum-culled per frame. */
|
||||
|
|
@ -249,74 +257,43 @@ function buildChunks(m: ChunkMaterials, trees: Tree[], boulders: Boulder[], bush
|
|||
for (let cj = 0; cj < CHUNK_GRID; cj++) {
|
||||
const z0 = -TERRAIN.outer + cj * cell
|
||||
const z1 = z0 + cell
|
||||
const grass = mesh()
|
||||
const bark = mesh()
|
||||
const birchBark = mesh()
|
||||
const leaf = mesh()
|
||||
const needle = mesh()
|
||||
const rock = mesh()
|
||||
const flowerMesh = mesh()
|
||||
const barkFar = mesh()
|
||||
const birchBarkFar = mesh()
|
||||
const leafFar = mesh()
|
||||
const needleFar = mesh()
|
||||
const rockFar = mesh()
|
||||
// Accumulate geometry into one mesh per material key, for the near (full) and
|
||||
// far (impostor) LOD sets. Props declare which material(s) they write, so the
|
||||
// baker never names a texture -- adding a species/material touches no code here.
|
||||
const near = new Map<string, Mesh>()
|
||||
const far = new Map<string, Mesh>()
|
||||
const grass = matMesh(near, "grass")
|
||||
far.set("grass", grass) // the ground is drawn in both LOD sets
|
||||
Terrain.patch(TERRAIN, grass, x0, z0, x1, z1, TERRAIN_SUBDIV, TERRAIN_SUBDIV, GROUND_UV)
|
||||
for (const tree of trees) {
|
||||
if (inCell(tree.position, x0, z0, x1, z1)) {
|
||||
// Birch trunks go to their own white-bark mesh; oak + birch share the oak
|
||||
// leaf foliage, spruce keeps its needles.
|
||||
const trunk = tree.kind === "birch" ? birchBark : bark
|
||||
const trunkFar = tree.kind === "birch" ? birchBarkFar : barkFar
|
||||
const foliage = tree.kind === "spruce" ? needle : leaf
|
||||
const foliageFar = tree.kind === "spruce" ? needleFar : leafFar
|
||||
Tree.build(tree, trunk, foliage)
|
||||
Tree.build(tree, trunkFar, foliageFar, "impostor")
|
||||
const s = Tree.species(tree.kind)
|
||||
Tree.build(tree, matMesh(near, s.trunk), matMesh(near, s.foliage))
|
||||
Tree.build(tree, matMesh(far, s.trunk), matMesh(far, s.foliage), "impostor")
|
||||
}
|
||||
}
|
||||
for (const boulder of boulders) {
|
||||
if (inCell(boulder.position, x0, z0, x1, z1)) {
|
||||
Boulder.build(boulder, rock)
|
||||
Boulder.build(boulder, rockFar, "impostor")
|
||||
Boulder.build(boulder, matMesh(near, "rock"))
|
||||
Boulder.build(boulder, matMesh(far, "rock"), "impostor")
|
||||
}
|
||||
}
|
||||
// Bushes share the near leaf mesh; they just drop out past lodDistance.
|
||||
// Bushes fold into the near leaf mesh; they just drop out past lodDistance.
|
||||
for (const bush of bushes) {
|
||||
if (inCell(bush.position, x0, z0, x1, z1)) {
|
||||
Bush.build(bush, leaf)
|
||||
Bush.build(bush, matMesh(near, "leaf"))
|
||||
}
|
||||
}
|
||||
for (const flower of flowers) {
|
||||
if (inCell(flower.position, x0, z0, x1, z1)) {
|
||||
Flower.build(flower, flowerMesh)
|
||||
Flower.build(flower, matMesh(near, "flower"))
|
||||
}
|
||||
}
|
||||
const b = bounds([grass, bark, birchBark, leaf, needle, rock, flowerMesh])
|
||||
const b = bounds([...near.values()])
|
||||
if (b === null) {
|
||||
continue
|
||||
}
|
||||
// Same draws as before, just described as data. Order is preserved (it
|
||||
// matches the old fixed sequence): grass, then the solid props, then the
|
||||
// double-sided flowers. Empty meshes are pruned so a chunk only carries the
|
||||
// groups it actually has.
|
||||
const near = drawGroups([
|
||||
[grass, m.grass],
|
||||
[rock, m.rock],
|
||||
[bark, m.bark],
|
||||
[birchBark, m.birch],
|
||||
[leaf, m.leaf],
|
||||
[needle, m.needle],
|
||||
[flowerMesh, m.flower],
|
||||
])
|
||||
const far = drawGroups([
|
||||
[grass, m.grass],
|
||||
[rockFar, m.rock],
|
||||
[barkFar, m.bark],
|
||||
[birchBarkFar, m.birch],
|
||||
[leafFar, m.leaf],
|
||||
[needleFar, m.needle],
|
||||
])
|
||||
chunks.push({ ...b, near, far })
|
||||
chunks.push({ ...b, near: toGroups(near, m), far: toGroups(far, m) })
|
||||
}
|
||||
}
|
||||
return chunks
|
||||
|
|
@ -326,13 +303,26 @@ function inCell(p: { x: number; z: number }, x0: number, z0: number, x1: number,
|
|||
return p.x >= x0 && p.x < x1 && p.z >= z0 && p.z < z1
|
||||
}
|
||||
|
||||
/** Pair meshes with their materials into a draw-group list, dropping any mesh
|
||||
* that ended up empty (a cell rarely holds every prop kind). */
|
||||
function drawGroups(pairs: [Mesh, Material][]): DrawGroup[] {
|
||||
/** Lazily get (creating on first use) the accumulation mesh for a material key in a
|
||||
* chunk's near/far map. Props write into these by key, so the baker stays generic. */
|
||||
function matMesh(map: Map<string, Mesh>, key: string): Mesh {
|
||||
let m = map.get(key)
|
||||
if (m === undefined) {
|
||||
m = mesh()
|
||||
map.set(key, m)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
/** Turn a chunk's per-material meshes into a draw-group list, in a fixed material
|
||||
* order (so the draw sequence is deterministic across bakes) and dropping any that
|
||||
* ended up empty (a cell rarely holds every prop kind). */
|
||||
function toGroups(map: Map<string, Mesh>, materials: ChunkMaterials): DrawGroup[] {
|
||||
const out: DrawGroup[] = []
|
||||
for (const [m, material] of pairs) {
|
||||
if (m.indices.length > 0) {
|
||||
out.push({ mesh: m, material })
|
||||
for (const key of MAT_ORDER) {
|
||||
const m = map.get(key)
|
||||
if (m !== undefined && m.indices.length > 0) {
|
||||
out.push({ mesh: m, material: materials[key] })
|
||||
}
|
||||
}
|
||||
return out
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue