banished_terrain/mesh.rs
1//! The terrain mesh that the renderer draws.
2//!
3//! The map is split into 32×32-cell tiles, each with its own 33×33 vertices; all tiles share one
4//! index list. Vertices are 16 bytes and match the terrain vertex shader: `i16 x, y` (cell
5//! coordinates centred on the map), `i16 height` (the saved `grid × 256` value), `i16 0xff`, `i16
6//! u, v` (map position × 16384, sampling the surface image), and `u8 normal xyz` (`(n + 1) ×
7//! 127.5`) plus a zero byte.
8//!
9//! Original: `FUN_1400b16b0` (the mesh), `FUN_1400b1d50` (a vertex), `FUN_1400b3740` (a normal).
10
11// MapComponent +0xc8
12/// The number of cells on each side of a tile.
13pub const TILE_CELLS: usize = 32;
14// MapComponent +0xcc
15/// The number of vertices on each side of a tile.
16pub const TILE_VERTS: usize = TILE_CELLS + 1;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
19#[repr(C)]
20/// One vertex of the terrain mesh, 16 bytes, as the terrain shader reads it.
21pub struct TerrainVertex {
22 /// `x`, `y` (cells from the centre of the terrain), the height (the saved value, height ×
23 /// 256), and `0xff`.
24 pub pos: [i16; 4],
25 /// The position on the map × 16384, to sample the surface image.
26 pub uv: [i16; 2],
27 /// The normal as bytes, `(n + 1) × 127.5`, and a zero byte.
28 pub normal: [u8; 4],
29}
30
31/// The terrain mesh: tiles of [`TILE_CELLS`] × [`TILE_CELLS`] cells, each with its own vertices,
32/// and one index list that every tile uses.
33pub struct TerrainMesh {
34 // MapComponent +0xc0
35 /// The terrain's size in cells, `2n`.
36 pub size: usize,
37 /// The number of tiles on each side.
38 pub tiles_per_side: usize,
39 /// The vertices: `tiles_per_side²` blocks of `TILE_VERTS²` vertices, tile by tile, row by row.
40 pub vertices: Vec<TerrainVertex>,
41 /// Indices for one tile (two triangles per cell).
42 pub indices: Vec<u16>,
43}
44
45/// The heights of a map as a save stores them, for queries and for the mesh: `width²` values, row
46/// by row (see [`Generator::heights`](crate::Generator::heights)).
47pub struct HeightGrid<'a> {
48 /// The number of samples on each side, `2n + 1`.
49 pub width: usize,
50 /// The heights, height × 256.
51 pub heights: &'a [i16],
52}
53
54impl HeightGrid<'_> {
55 fn at(&self, x: i32, y: i32) -> i32 {
56 let hi = self.width as i32 - 1;
57 self.heights[(y.clamp(0, hi) as usize) * self.width + x.clamp(0, hi) as usize] as i32
58 }
59
60 /// Normal from integer central differences.
61 ///
62 /// Original: `FUN_1400b3740`.
63 pub fn normal(&self, x: i32, y: i32) -> [f32; 3] {
64 let scale = -1.0 / 256.0;
65 let v = [
66 (self.at(x + 1, y) - self.at(x - 1, y)) as f32 * scale,
67 (self.at(x, y + 1) - self.at(x, y - 1)) as f32 * scale,
68 2.0,
69 ];
70 let len = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
71 let inv = 1.0 / len;
72 [v[0] * inv, v[1] * inv, v[2] * inv]
73 }
74
75 /// The vertex at grid point `(x, y)` of a terrain of `size` cells.
76 ///
77 /// Original: `FUN_1400b1d50`.
78 pub fn vertex(&self, x: i32, y: i32, size: i32) -> TerrainVertex {
79 let half = (size >> 1) as i16;
80 let n = self.normal(x, y);
81 let byte = |v: f32| ((v + 1.0) * 127.5) as i64 as u8;
82 TerrainVertex {
83 pos: [x as i16 - half, y as i16 - half, self.at(x, y) as i16, 0xff],
84 uv: [
85 ((x as f32 / size as f32) * 16384.0) as i32 as i16,
86 ((y as f32 / size as f32) * 16384.0) as i32 as i16,
87 ],
88 normal: [byte(n[0]), byte(n[1]), byte(n[2]), 0],
89 }
90 }
91}
92
93impl TerrainMesh {
94 /// The mesh of a terrain of `size` cells.
95 pub fn build(grid: &HeightGrid, size: usize) -> TerrainMesh {
96 let tiles = size / TILE_CELLS;
97 let mut vertices = Vec::with_capacity(tiles * tiles * TILE_VERTS * TILE_VERTS);
98 for ty in 0..tiles {
99 for tx in 0..tiles {
100 for y in 0..TILE_VERTS {
101 for x in 0..TILE_VERTS {
102 vertices.push(grid.vertex(
103 (tx * TILE_CELLS + x) as i32,
104 (ty * TILE_CELLS + y) as i32,
105 size as i32,
106 ));
107 }
108 }
109 }
110 }
111 TerrainMesh {
112 size,
113 tiles_per_side: tiles,
114 vertices,
115 indices: tile_indices(),
116 }
117 }
118
119 /// The vertices as bytes, ready for a GPU vertex buffer.
120 pub fn vertex_bytes(&self) -> Vec<u8> {
121 let mut out = Vec::with_capacity(self.vertices.len() * 16);
122 for v in &self.vertices {
123 for p in v.pos.iter().chain(&v.uv) {
124 out.extend_from_slice(&p.to_le_bytes());
125 }
126 out.extend_from_slice(&v.normal);
127 }
128 out
129 }
130}
131
132/// Two triangles per cell; the diagonal alternates with `(x + y) & 1`.
133pub fn tile_indices() -> Vec<u16> {
134 let w = TILE_VERTS as u16;
135 let mut out = Vec::with_capacity(TILE_CELLS * TILE_CELLS * 6);
136 for y in 0..TILE_CELLS as u16 {
137 for x in 0..TILE_CELLS as u16 {
138 let a = y * w + x;
139 let b = a + 1;
140 let c = (y + 1) * w + x;
141 if (x + y) & 1 == 0 {
142 out.extend_from_slice(&[a, c, b, b, c, c + 1]);
143 } else {
144 out.extend_from_slice(&[a, c + 1, b, a, c, c + 1]);
145 }
146 }
147 }
148 out
149}