1use crate::math_functions::{Aabb, Vec3, VEC3_ZERO};
9
10pub const HEIGHT_FIELD_HOLE: u8 = 0xFF;
12
13pub const HEIGHT_FIELD_VERSION: u64 = 0x8B18CBD138A6BC84;
15
16pub const HEIGHT_FIELD_DATA_SIZE: usize = 88;
18
19pub const CONCAVE_EDGE1: i32 = 0x01;
21pub const CONCAVE_EDGE2: i32 = 0x02;
22pub const CONCAVE_EDGE3: i32 = 0x04;
23pub const INVERSE_CONCAVE_EDGE1: i32 = 0x10;
24pub const INVERSE_CONCAVE_EDGE2: i32 = 0x20;
25pub const INVERSE_CONCAVE_EDGE3: i32 = 0x40;
26
27#[derive(Debug, Clone)]
29pub struct HeightFieldDef {
30 pub heights: Vec<f32>,
32 pub material_indices: Vec<u8>,
35 pub scale: Vec3,
37 pub count_x: i32,
39 pub count_z: i32,
41 pub global_minimum_height: f32,
43 pub global_maximum_height: f32,
45 pub clockwise_winding: bool,
47}
48
49impl Default for HeightFieldDef {
50 fn default() -> Self {
51 Self {
52 heights: Vec::new(),
53 material_indices: Vec::new(),
54 scale: VEC3_ZERO,
55 count_x: 0,
56 count_z: 0,
57 global_minimum_height: 0.0,
58 global_maximum_height: 0.0,
59 clockwise_winding: false,
60 }
61 }
62}
63
64#[derive(Debug, Clone)]
70pub struct HeightFieldData {
71 pub version: u64,
72 pub byte_count: i32,
73 pub hash: u32,
74 pub aabb: Aabb,
75 pub min_height: f32,
76 pub max_height: f32,
77 pub height_scale: f32,
78 pub scale: Vec3,
79 pub column_count: i32,
80 pub row_count: i32,
81 pub heights_offset: i32,
82 pub material_offset: i32,
83 pub flags_offset: i32,
84 pub clockwise: bool,
85 pub padding: [u8; 3],
86 pub compressed_heights: Vec<u16>,
87 pub material_indices: Vec<u8>,
88 pub flags: Vec<u8>,
89}
90
91impl Default for HeightFieldData {
92 fn default() -> Self {
93 Self {
94 version: HEIGHT_FIELD_VERSION,
95 byte_count: 0,
96 hash: 0,
97 aabb: Aabb::default(),
98 min_height: 0.0,
99 max_height: 0.0,
100 height_scale: 0.0,
101 scale: VEC3_ZERO,
102 column_count: 0,
103 row_count: 0,
104 heights_offset: 0,
105 material_offset: 0,
106 flags_offset: 0,
107 clockwise: false,
108 padding: [0; 3],
109 compressed_heights: Vec::new(),
110 material_indices: Vec::new(),
111 flags: Vec::new(),
112 }
113 }
114}
115
116pub fn get_height_field_compressed_heights(hf: &HeightFieldData) -> &[u16] {
118 &hf.compressed_heights
119}
120
121pub fn get_height_field_material_indices(hf: &HeightFieldData) -> &[u8] {
123 &hf.material_indices
124}
125
126pub fn get_height_field_flags(hf: &HeightFieldData) -> &[u8] {
128 &hf.flags
129}
130
131pub fn get_height_field_triangle_count(hf: &HeightFieldData) -> i32 {
133 2 * (hf.column_count - 1) * (hf.row_count - 1)
134}
135
136fn write_u64_le(buf: &mut Vec<u8>, v: u64) {
137 buf.extend_from_slice(&v.to_le_bytes());
138}
139
140fn read_u64_le(buf: &[u8], off: usize) -> u64 {
141 u64::from_le_bytes(buf[off..off + 8].try_into().unwrap())
142}
143
144fn read_u32_le(buf: &[u8], off: usize) -> u32 {
145 u32::from_le_bytes(buf[off..off + 4].try_into().unwrap())
146}
147
148fn read_i32_le(buf: &[u8], off: usize) -> i32 {
149 read_u32_le(buf, off) as i32
150}
151
152fn read_u16_le(buf: &[u8], off: usize) -> u16 {
153 u16::from_le_bytes(buf[off..off + 2].try_into().unwrap())
154}
155
156fn read_f32_le(buf: &[u8], off: usize) -> f32 {
157 f32::from_le_bytes(buf[off..off + 4].try_into().unwrap())
158}
159
160fn read_vec3_at(buf: &[u8], off: usize) -> Vec3 {
161 Vec3 {
162 x: read_f32_le(buf, off),
163 y: read_f32_le(buf, off + 4),
164 z: read_f32_le(buf, off + 8),
165 }
166}
167
168fn read_aabb_at(buf: &[u8], off: usize) -> Aabb {
169 Aabb {
170 lower_bound: read_vec3_at(buf, off),
171 upper_bound: read_vec3_at(buf, off + 12),
172 }
173}
174
175pub fn convert_bytes_to_height_field(bytes: &[u8]) -> Option<HeightFieldData> {
177 if bytes.len() < HEIGHT_FIELD_DATA_SIZE {
178 return None;
179 }
180 let version = read_u64_le(bytes, 0);
181 if version != HEIGHT_FIELD_VERSION {
182 return None;
183 }
184 let byte_count = read_i32_le(bytes, 8);
185 if byte_count < HEIGHT_FIELD_DATA_SIZE as i32 || bytes.len() != byte_count as usize {
186 return None;
187 }
188 let hash = read_u32_le(bytes, 12);
189 let aabb = read_aabb_at(bytes, 16);
190 let min_height = read_f32_le(bytes, 40);
191 let max_height = read_f32_le(bytes, 44);
192 let height_scale = read_f32_le(bytes, 48);
193 let scale = read_vec3_at(bytes, 52);
194 let column_count = read_i32_le(bytes, 64);
195 let row_count = read_i32_le(bytes, 68);
196 let heights_offset = read_i32_le(bytes, 72);
197 let material_offset = read_i32_le(bytes, 76);
198 let flags_offset = read_i32_le(bytes, 80);
199 let clockwise = bytes[84] != 0;
200 let padding = [bytes[85], bytes[86], bytes[87]];
201
202 if column_count < 0 || row_count < 0 {
203 return None;
204 }
205 let height_count = (column_count as usize).checked_mul(row_count as usize)?;
206 let cell_count =
207 ((column_count - 1).max(0) as usize).checked_mul((row_count - 1).max(0) as usize)?;
208
209 let hoff = heights_offset as usize;
210 if hoff + height_count * 2 > bytes.len() {
211 return None;
212 }
213 let mut compressed_heights = Vec::with_capacity(height_count);
214 for i in 0..height_count {
215 compressed_heights.push(read_u16_le(bytes, hoff + i * 2));
216 }
217
218 let moff = material_offset as usize;
219 if moff + cell_count > bytes.len() {
220 return None;
221 }
222 let material_indices = bytes[moff..moff + cell_count].to_vec();
223
224 let foff = flags_offset as usize;
225 let flag_count = cell_count * 2; if foff + flag_count > bytes.len() {
227 return None;
228 }
229 let flags = bytes[foff..foff + flag_count].to_vec();
230
231 Some(HeightFieldData {
232 version,
233 byte_count,
234 hash,
235 aabb,
236 min_height,
237 max_height,
238 height_scale,
239 scale,
240 column_count,
241 row_count,
242 heights_offset,
243 material_offset,
244 flags_offset,
245 clockwise,
246 padding,
247 compressed_heights,
248 material_indices,
249 flags,
250 })
251}
252
253fn write_u32_le(buf: &mut Vec<u8>, v: u32) {
254 buf.extend_from_slice(&v.to_le_bytes());
255}
256
257fn write_i32_le(buf: &mut Vec<u8>, v: i32) {
258 buf.extend_from_slice(&v.to_le_bytes());
259}
260
261fn write_f32_le(buf: &mut Vec<u8>, v: f32) {
262 buf.extend_from_slice(&v.to_le_bytes());
263}
264
265fn write_vec3(buf: &mut Vec<u8>, v: Vec3) {
266 write_f32_le(buf, v.x);
267 write_f32_le(buf, v.y);
268 write_f32_le(buf, v.z);
269}
270
271fn write_aabb(buf: &mut Vec<u8>, a: Aabb) {
272 write_vec3(buf, a.lower_bound);
273 write_vec3(buf, a.upper_bound);
274}
275
276fn pad_to(buf: &mut Vec<u8>, len: usize) {
277 if buf.len() < len {
278 buf.resize(len, 0);
279 }
280}
281
282fn write_header(buf: &mut Vec<u8>, h: &HeightFieldData, hash_override: Option<u32>) {
283 write_u64_le(buf, h.version);
284 write_i32_le(buf, h.byte_count);
285 write_u32_le(buf, hash_override.unwrap_or(h.hash));
286 write_aabb(buf, h.aabb);
287 write_f32_le(buf, h.min_height);
288 write_f32_le(buf, h.max_height);
289 write_f32_le(buf, h.height_scale);
290 write_vec3(buf, h.scale);
291 write_i32_le(buf, h.column_count);
292 write_i32_le(buf, h.row_count);
293 write_i32_le(buf, h.heights_offset);
294 write_i32_le(buf, h.material_offset);
295 write_i32_le(buf, h.flags_offset);
296 buf.push(u8::from(h.clockwise));
297 buf.extend_from_slice(&h.padding);
298 debug_assert_eq!(buf.len(), HEIGHT_FIELD_DATA_SIZE);
299}
300
301impl HeightFieldData {
302 pub fn from_bytes(bytes: &[u8]) -> Option<HeightFieldData> {
304 convert_bytes_to_height_field(bytes)
305 }
306
307 pub fn to_bytes(&self) -> Vec<u8> {
309 self.to_bytes_with_hash(self.hash)
310 }
311
312 pub fn to_bytes_with_hash(&self, hash: u32) -> Vec<u8> {
314 let mut buf = Vec::with_capacity(self.byte_count as usize);
315 write_header(&mut buf, self, Some(hash));
316 pad_to(&mut buf, self.heights_offset as usize);
317 for h in &self.compressed_heights {
318 buf.extend_from_slice(&h.to_le_bytes());
319 }
320 pad_to(&mut buf, self.material_offset as usize);
321 buf.extend_from_slice(&self.material_indices);
322 pad_to(&mut buf, self.flags_offset as usize);
323 buf.extend_from_slice(&self.flags);
324 pad_to(&mut buf, self.byte_count as usize);
325 debug_assert_eq!(buf.len(), self.byte_count as usize);
326 buf
327 }
328}