use std::collections::HashMap;
use bitreader::{BitReader, BitReaderError};
use bytemuck::cast_slice;
use glam::U16Vec2;
use log::warn;
use metaverse_messages::udp::environment::layer_data::LayerData;
use metaverse_messages::utils::render_data::RenderObject;
use twox_hash::XxHash32;
use crate::{
error::PatchError,
generate_triangles::generate_mesh_with_indices,
layer_handler::{
bits_to_big_endian, decompress_patch, read_bits, PatchData, TerrainHeader, END_OF_PATCHES,
},
};
const PATCHES_PER_EDGE: u16 = 16;
#[derive(Debug, Clone)]
pub struct Land {
pub terrain_header: TerrainHeader,
pub heightmap: Vec<f32>,
}
impl Land {
pub fn generate_mesh(
self,
retry_queue: &mut HashMap<U16Vec2, Self>,
total_patches: &HashMap<U16Vec2, Self>,
) -> Option<(RenderObject, U16Vec2)> {
let location = self.terrain_header.location;
let north_xy = U16Vec2 {
x: location.x,
y: location.y.saturating_sub(1),
};
let east_xy = U16Vec2 {
x: location.x + 1,
y: location.y,
};
let top_corner_xy = U16Vec2 {
x: location.x + 1,
y: location.y.saturating_sub(1),
};
let north = total_patches.get(&north_xy).cloned();
let east = total_patches.get(&east_xy).cloned();
let corner = total_patches.get(&top_corner_xy).cloned();
if let (Some(north_layer), Some(east_layer), Some(top_corner)) = (north, east, corner) {
let object = generate_mesh_with_indices(
total_patches.get(&location).unwrap(),
&north_layer,
&east_layer,
&top_corner,
);
retry_queue.remove(&location);
Some((object, self.terrain_header.location))
} else {
retry_queue.insert(location, self);
None
}
}
}
impl PatchData for Land {
fn from_packet(packet: &LayerData, extended: bool) -> Result<Vec<Self>, PatchError> {
let mut patches = Vec::new();
let mut reader = BitReader::new(&packet.layer_content);
loop {
let mut terrain_header = match TerrainHeader::from_bytes(&mut reader, extended) {
Ok(mut header) => {
header.stride = packet.stride;
header.patch_size = packet.patch_size;
header
}
Err(e) => {
return Err(PatchError {
message: format!("Failed to create header: {}", e).to_string(),
});
}
};
if !extended
&& (terrain_header.location.x >= PATCHES_PER_EDGE
|| terrain_header.location.y >= PATCHES_PER_EDGE)
{
warn!("Invalid LayerData packet {:?}", terrain_header);
}
if terrain_header.quantized_world_bits == END_OF_PATCHES {
break;
}
let patch = parse_heightmap(&mut reader, &terrain_header)?;
let hash = XxHash32::oneshot(1234, cast_slice(&patch));
terrain_header.filename = format!(
"{}_{}_{}",
&terrain_header.location.x, &terrain_header.location.y, hash
);
let heightmap = decompress_patch(&terrain_header, &patch);
patches.push(Land {
terrain_header,
heightmap,
});
}
Ok(patches)
}
}
pub fn parse_heightmap(
reader: &mut BitReader,
terrain_header: &TerrainHeader,
) -> Result<Vec<f32>, BitReaderError> {
let patch_size = terrain_header.patch_size as usize;
let total = patch_size * patch_size;
let mut patch: Vec<f32> = vec![0.0; total];
for i in 0..total {
if reader.read_bool()? {
if reader.read_bool()? {
let is_negative = reader.read_bool()?;
let bits = read_bits(reader, terrain_header.world_bits)?;
let magnitude = bits_to_big_endian(&bits, 8) as f32;
patch[i] = if is_negative { -magnitude } else { magnitude };
} else {
for j in patch.iter_mut().take(total).skip(i) {
*j = 0.0;
}
break;
}
} else {
patch[i] = 0.0;
continue;
}
}
Ok(patch)
}