feat: birch

This commit is contained in:
Dan Finch 2026-08-07 13:01:32 +02:00
parent 16f205babe
commit a4490f7a20
11 changed files with 110 additions and 24 deletions

View file

@ -35,6 +35,9 @@ export type Chunk = {
maxZ: number
grass: Mesh
bark: Mesh
/** Birch trunks (white bark texture) -- separate mesh since a draw is one mesh +
* one texture, and silver birch bark can't share the brown oak/spruce bark. */
birchBark: Mesh
leaf: Mesh
needle: Mesh
rock: Mesh
@ -42,8 +45,9 @@ export type Chunk = {
flowers: Mesh
/** Cheap low-poly impostors of the same trees/boulders, drawn instead of the
* full meshes once the chunk is past `config.lodDistance` (renderScene). Same
* textures as bark/leaf/needle/rock. Bushes/flowers have no far version. */
* textures as bark/birch/leaf/needle/rock. Bushes/flowers have no far version. */
barkFar: Mesh
birchBarkFar: Mesh
leafFar: Mesh
needleFar: Mesh
rockFar: Mesh
@ -231,21 +235,27 @@ function buildChunks(trees: Tree[], boulders: Boulder[], bushes: Bush[], flowers
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()
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)) {
const foliage = tree.kind === "oak" ? leaf : needle
const foliageFar = tree.kind === "oak" ? leafFar : needleFar
Tree.build(tree, bark, foliage)
Tree.build(tree, barkFar, foliageFar, "impostor")
// 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")
}
}
for (const boulder of boulders) {
@ -265,11 +275,11 @@ function buildChunks(trees: Tree[], boulders: Boulder[], bushes: Bush[], flowers
Flower.build(flower, flowerMesh)
}
}
const b = bounds([grass, bark, leaf, needle, rock, flowerMesh])
const b = bounds([grass, bark, birchBark, leaf, needle, rock, flowerMesh])
if (b === null) {
continue
}
chunks.push({ ...b, grass, bark, leaf, needle, rock, flowers: flowerMesh, barkFar, leafFar, needleFar, rockFar })
chunks.push({ ...b, grass, bark, birchBark, leaf, needle, rock, flowers: flowerMesh, barkFar, birchBarkFar, leafFar, needleFar, rockFar })
}
}
return chunks
@ -320,7 +330,8 @@ function placeTrees(colliders: Aabb[]): Tree[] {
if (Math.max(Math.abs(x), Math.abs(z)) < TERRAIN.inner + 3) {
continue
}
const kind = rand() < 0.5 ? "oak" : "spruce"
const roll = rand()
const kind = roll < 0.4 ? "oak" : roll < 0.72 ? "spruce" : "birch"
const growth = 0.08 + rand() * 0.92
const position = { x, y: Terrain.height(TERRAIN, x, z), z }
trees.push({ kind, position, growth, seed: (rand() * 0xFFFFFFFF) | 0 })